* chore(test): drop no-op per-test Redis flushall
The afterEach created a fresh ioredis-mock client and flushed it, which
doesn't clear state held by clients elsewhere in the test. Removing the
hook saves a few ms across thousands of test cases.
* Cache Jest transform cache
Drop the dedicated setup job that blocked every other job for ~60s,
extract the install steps into a reusable composite action, drop the
unnecessary bundle-size dependency on types, and switch test-server
sharding to Jest's native --shard flag.
* fix: relative path returned from MCP
* fix: MCP create_attachment uploadUrl and size validation
Make uploadUrl absolute against team.url so MCP clients can resolve it
without a base, tighten the size schema to match the REST endpoint
(int, nonnegative, finite), and stub cookies on the MCP API context so
LocalStorage's CSRF-aware getPresignedPost works for Bearer-authed
MCP requests. Adds tests covering the success path, persistence, size
limits, schema rejections, and read-only scope enforcement.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Update editor generics
* fix: Address PR review on editor generics
- Restore null-guard on Link click handler so anchors aren't inert when no onClickLink is provided
- Mark onClickLink optional in LinkOptions and openLink command to match runtime
- Remove dead `collapsed` option from HeadingOptions
- Make ToggleBlock dictionary optional and restore optional-chained access for server-side schema instantiation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add delete_document and delete_collection MCP tools
Adds MCP tools for deleting (or archiving) documents and collections.
Refactors Document#delete into destroyWithCtx and extracts collection
archive logic into Collection#archiveWithCtx so the same code paths can
be shared between the REST API and MCP entry points.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Wrap MCP delete tools in DB transaction
Ensures delete/archive of documents and collections via MCP is atomic
and that row locks (transaction.LOCK.UPDATE) inside *WithCtx methods
actually apply, matching the pattern used by move_document.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs: Clarify delete_collection MCP tool description
Reflects that collection deletion only soft-deletes non-archived
documents via the BeforeDestroy hook.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: promote no-explicit-any from warn to error and resolve violations
Upgrades the oxlint rule severity and removes all 40 existing
`no-explicit-any` warnings across the codebase. Most call sites gained
proper types (SharedEditor refs, JSONNode/JSONMark for ProseMirror JSON
walking, DocumentsStore, dd-trace `Span` parameter inference, prosemirror
Fragment public API in place of internal `(fragment as any).content`).
A few load-bearing `any` uses were preserved with scoped disable
comments where changing the type would cascade widely (Sequelize JSONB
columns on `Event`, the `withTracing` higher-order function generic,
`Extension.options` consumed by many subclasses, dd-trace's `req`
patching).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces `as any` casts when constructing OAuth2Server Request/Response
with explicit objects containing the fields the library actually
consumes, and switches BaseStorage's manual header spread to a
node-fetch `Headers` instance to avoid the no-misused-spread warning.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add breadcrumb to MCP responses
* test: Update MCP test expectations for new response envelope
Tests were reading the old flat document shape; update them to read
through the new { document, breadcrumb } envelope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf: Batch collection lookups when building breadcrumbs
Add getBreadcrumbsForDocuments helper that loads all referenced
collections in one query (with the user's memberships) and resolves
breadcrumbs from the per-collection cached documentStructure. Use it
in list_documents and move_document, replacing the per-document
Collection.findByPk that produced an N+1 query pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test: Add coverage for getBreadcrumbsForDocuments and parallelize doc + breadcrumb loads
Run presentDocument and getDocumentBreadcrumb concurrently in fetch,
create_document, and update_document so the breadcrumb lookup no
longer adds latency on top of the presenter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: resolve no-explicit-any lint warnings in plugins
Replaces uses of `any` in the plugins directory with concrete types,
`unknown`, or structured type assertions, addressing the remaining
typescript-eslint(no-explicit-any) warnings flagged by oxlint.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: address review feedback in GitLabIssueProvider
Drop trailing semicolon from log string and add early return in
`destroyNamespace` when neither `user_id` nor `full_path` is present
to avoid an unnecessary full-scan transaction.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: should change lastModifiedById when republishing an already published document
* test: Use same-team creator in republish attribution test
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Api keys with global read scope not being saved correctly
* refactor: Hoist global scopes Set to module level
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: centralize sidebar expansion state to eliminate O(N²) tree traversals
Each DocumentLink previously traversed the full collection tree independently
to determine whether to auto-expand (pathToDocument / descendants), which is
O(N) per row and quadratic overall. With thousands of documents this makes
the sidebar unusable.
Replaces per-node expansion state with a single MobX-backed
SidebarExpansionState per tree root. The ObservableSet ensures only the
toggled node re-renders. Alt-click cascade, auto-expand on navigation,
and drag-to-reparent expansion all go through the same centralized state
instead of the per-node SidebarDisclosureContext relay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: move SidebarExpansionContext alongside other sidebar contexts
Rename hooks/useSidebarExpansion.ts to components/SidebarExpansionContext.ts
to match the convention of SidebarContext.ts and SidebarDisclosureContext.ts.
The context is now the default export with hooks as named exports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: scope sidebar expansion to its own tree and restore alt-click cascade
`useSidebarExpansionState` was unconditionally adding the active document
id to every per-tree expansion set, which made `SharedWithMeLink` auto-
expand whenever the user navigated anywhere in the matching sidebar
context. `computeAncestorPath` now includes the target when found and
returns empty when absent, so the hook only expands ids that actually
belong to its tree.
Also restores alt-click cascade for `StarredDocumentLink` and
`SharedWithMeLink`: the parents still broadcast disclosure events but
`DocumentLink` no longer listens, so nested children weren't expanded.
`StarredDocumentLink` now subscribes via `useSidebarDisclosure` (mirroring
`CollectionLinkChildren`), and `SharedWithMeLink` calls
`expansion.expandAll`/`collapseAll` directly on alt-click.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: collapse expanded nodes when children are removed and deduplicate shared expansion provider
Restores the effect that collapses a node in the expansion state when it
no longer has children, preventing the reorder drop logic from treating
leaf nodes as expanded containers. Also removes the redundant
SidebarExpansionContext.Provider from SharedCollectionLink since the
parent SharedSidebar already provides one.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: Suspended users should not be included in cached member count for groups
* fix: Defer CounterCache hook registration until model is initialized
The previous test-only no-op hid a timing bug where setImmediate could
fire before the Sequelize instance had registered the related model,
causing "Model not initialized" failures. Poll until the model is
ready, and unref the pending immediate so it does not keep the event
loop alive in environments where the database is never initialized.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf: Reduce overhead of group member count invalidation
Select only the groupId column with raw queries and de-duplicate before
issuing Redis deletes, avoiding loading full GroupUser rows into memory
when a user belongs to many groups.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: unref Redis healthcheck interval
Don't keep the Node event loop alive solely for the periodic ping; the
event loop should drain on its own when the application is shutting
down or a Jest worker is finishing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor: Centralize counter cache key in RedisPrefixHelper
Avoid duplicating the "count:<Model>:<relation>:<id>" string between
the CounterCache decorator and the User suspension hook by routing
both through a single getCounterCacheKey helper.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Walk to parent transaction when scheduling cache invalidation
Nested savepoints commit independently of their outer transaction, so
afterCommit callbacks attached to the inner transaction may run after
the outer rolls back, or never run at all. Match the pattern used in
Collection, Event, and base/Model and walk to the parent transaction
so the cache invalidation fires after the real outer commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: resolve no-redundant-type-constituents and test/mock no-explicit-any warnings
Clears 36 lint warnings: all 5 no-redundant-type-constituents, 6
no-misused-spread (via narrowing getPartitionWhereClause's return type
to WhereAttributeHash), and 25 no-explicit-any in test/mock files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: resolve no-base-to-string warnings in tests
Convert userProvisioner try/catch error assertions to Jest's
.rejects.toThrow() idiom, and cast webhook test body to string.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: resolve no-explicit-any warnings in cancan and tracing
Tighten types in the cancan policy framework and tracing decorators.
Constructor / generic-function upper bounds keep `any` where TypeScript
variance requires it, scoped to single-line oxlint-disable comments.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Adds missing stable dependencies (e.g. `t`, prop callbacks, store refs,
`setFocusedCommentId`) and removes unnecessary ones across hooks where the
fix is straightforward. For the two MobX-observed `.orderedData` deps in
`History.tsx`, keeps the original deps and silences the false positive
with `eslint-disable-next-line` so the memos still recompute when the
underlying observable arrays change.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Capture jest mock references in local variables instead of asserting
against unbound method references on mocked classes/instances.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Convert rich_text and rich_text_to_plaintext from static methods to
static arrow-function fields so they can be passed as map callbacks
without tripping the unbound-method lint. Neither method accesses
`this`, so behavior is unchanged.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: resolve remaining unbound-method lint warnings
Apply targeted fixes per call pattern: arrow wrappers when passing a
method as a callback, arrow-function class fields when the method
doesn't depend on `this`, and `.bind()` when capturing for later
invocation.
Also replaces the rfc6902 hasOwnProperty re-export with a small wrapper
function so callers don't reference an unbound prototype method.
* chore: memoize history.goBack callbacks
Stable identity prevents Button re-renders and avoids re-subscribing
the global keydown handler in RegisterKeyDown when the parent renders.
* chore: enable typescript/restrict-template-expressions lint rule
Coerce values of unknown type with explicit String() and tighten typing
for template literal expressions across the codebase.
* fix: restore --line-height on Card for fadeOut ::after gradient
* chore: clear mechanical lint warnings
Drops 44 oxlint warnings (559 → 515) by fixing easy mechanical rules
across the codebase: no-useless-escape, no-duplicate-type-constituents,
no-redundant-type-constituents, no-unused-expressions,
no-meaningless-void-operator, require-array-sort-compare, await-thenable.
* chore: drop callback parameter from useCallback deps
The `open` argument is a parameter of the callback, not a closed-over
variable, so it doesn't belong in the deps array.
* chore: promote cleared lint rules to errors
Promotes the rules cleared in this PR from warn to error so future
violations fail the lint:
- no-unused-expressions
- typescript/await-thenable
- typescript/no-duplicate-type-constituents
- typescript/no-meaningless-void-operator
- typescript/require-array-sort-compare
Removes the override that suppressed no-useless-escape on source
files (the global rule is already error) and fixes the 21 escape
violations that this exposed in regex character classes and template
literals.
* chore: address PR review feedback
- usePinnedDocuments: simplify UrlId to plain string instead of the
intersection trick.
- PlantUML embed: move - to end of character class so it's a literal
hyphen rather than a range operator.
- checkboxes: type token params as Token | undefined to match the
actual call sites that pass tokens[index - 2] etc.
* chore: replace explicit any with concrete types in shared
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: address review feedback
- naturalSort: guard non-string field values instead of asserting string
- ProsemirrorHelper: type stored mark attrs as Partial<CommentMark>
- env: revert to Record<string, any>; safer typing requires fixing many consumers
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Reduce no-explicit-any warnings in server directory
Tightens types across test response bodies, decorator signatures, the
TestServer wrapper, base class generics, and presenter Record types.
Where any is genuinely load-bearing (Sequelize model generics,
PropertyDescriptor decorator returns, plugin-registered template
classes, Fix mixin), keeps any with a targeted eslint-disable plus
reason rather than masking the constraint. Cuts server-only
no-explicit-any warnings from 162 to 70.
* fix: groups test asserts on first response instead of second
Caught by Copilot review on the no-explicit-any cleanup. Also fixes
the pre-existing getChangsetSkipped → getChangesetSkipped typo
surfaced while reviewing nearby decorator code.
* fix: Resolve no-floating-promises lint errors
Adds await or void to 10 unhandled promises. Notable fixes: a test
assertion using `.resolves` was never awaited, and a custom emoji
fetch with setState was running during render instead of in an effect.
* chore: Promote no-floating-promises to error
Now that all occurrences are fixed, prevent regressions.
* chore(deps-dev): bump oxlint-tsgolint from 0.1.6 to 0.21.1
Bumps [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) from 0.1.6 to 0.21.1.
- [Release notes](https://github.com/oxc-project/tsgolint/releases)
- [Commits](https://github.com/oxc-project/tsgolint/compare/v0.1.6...v0.21.1)
---
updated-dependencies:
- dependency-name: oxlint-tsgolint
dependency-version: 0.21.1
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* chore: Adjust lint config for newer oxlint-tsgolint
Pin oxlint to 1.50.0 and oxlint-tsgolint to 0.14.2. Older oxlint
can't parse newer tsgolint diagnostic payloads, and tsgolint >=0.15
rejects moduleResolution: "node" — moving off it requires either
"bundler" (currently breaks @hocuspocus@1.1.3 typings, which lack a
types condition in their package.json exports) or "node16"/"nodenext"
(would require explicit .js extensions on every relative import).
Add per-package ignorePatterns since they no longer propagate from
the root config when nested configs are present.
Drop tsconfig baseUrl (typescript-go in tsgolint rejects it) and add
a plugins/* path alias so cross-plugin imports keep resolving. The
babel resolver is switched from babel-plugin-tsconfig-paths-module-
resolver (which required baseUrl) to babel-plugin-module-resolver
with explicit aliases.
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>