* Add restore_document MCP tool and archived/trashed listing
Closes the delete/restore asymmetry in the MCP server: previously documents
could be archived or trashed via delete_document but never recovered.
- Add restore_document tool to recover archived or trashed documents,
optionally into a different collection.
- Add a status option ("archived" | "trashed") to list_documents so agents
can discover what to restore.
- Extract the documents.restore route logic into a shared documentRestorer
command, used by both the REST endpoint and the MCP tool.
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
* Use type-only import for Document in documentRestorer
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
* Revert archived/trashed status option on list_documents
Keeps the restore_document tool and shared documentRestorer command;
removes the list_documents status filter and its tests.
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
---------
Co-authored-by: Claude <noreply@anthropic.com>
The attachment cleanup loop used findAllInBatches, which advances an
OFFSET each iteration. Because the callback deletes each batch, the
remaining rows shift backwards and the advancing offset skips over them,
leaving attachments that still reference the team. team.destroy() then
failed with attachments_teamId_fkey.
Page from offset 0 until no attachments remain, and remove the now
redundant per-user attachment delete so the loop is the single
authoritative cleanup.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Guard against null collaboratorIds when persisting collaborative
updates; the DB column has no default and can be NULL on older rows.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Unable to link secondary auth provider on custom domain
* doc
* chore: Custom -> Apex transfer token
* Refactor, address security concerns
* Ensure OAuth intent is single-use
* Secure OAuth state actor binding
* Use scrypt for OAuth actor session binding
* fix: Batch document deletes when emptying trash
Splits the final parentDocumentId clear and destroy in documentPermanentDeleter
into chunks of 100 to keep the exclusive lock window on the documents table
short, preventing concurrent web SELECTs from queueing behind a single large
DELETE.
* fix: Skip parentDocumentId clear for documents restored mid-flight
Re-checks deletedAt in the database before clearing parentDocumentId on
children, so a parent restored between the caller's query and now keeps its
children attached.
* Auto-subscribe mentioned users to documnet
* Add tests for mention auto-subscribe and a buildMention factory
* Add tests that prior unsubscribes are respected when mentioned
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Batch mention subscriptions into a single transaction
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* wip
* Remove obsolete snapshots
* simplify
* chore(test): Convert mocks to TypeScript and tighten fetch mock types
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Remove unneccessary patches
* Migrate to msw instead of custom fetch mock
* Address PR review comments
- Split chained vi.useFakeTimers().setSystemTime() into separate calls.
- Switch test setup to dynamic imports so EventEmitter.defaultMaxListeners
assignment runs before module init (static imports were hoisted above it).
- Drop redundant NODE_ENV guard in monkeyPatchSequelizeErrorsForJest; its
sole caller already gates on env.isTest.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Replace lodash with es-toolkit
Migrate all direct lodash imports to es-toolkit/compat for a smaller,
faster, lodash-compatible utility library. Transitive lodash usage from
other packages remains unchanged.
* fix: Restore isPlainObject semantics in CanCan policy
The lodash migration aliased `isObject` to `lodash/isPlainObject` and
the codemod incorrectly mapped the local name to es-toolkit's `isObject`,
which also returns true for arrays and functions. This caused condition
objects in policy definitions to be skipped, breaking authorization
checks across the codebase.
* fix: Restore unicode-aware length counting in validators
es-toolkit/compat's size() returns string.length, while lodash's _.size()
counts unicode code points. Switch to [...value].length to preserve the
previous behavior so multi-byte characters like emoji count as one.
* 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>
* 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>
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>
* Add argument to suppressEmails wehn inviting users
* Skip InviteSent flag when suppressEmail is set
Keeps the resend-invite counter accurate so users.resendInvite can
still deliver the first email when the initial invite was silent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Address code quality findings
* Round 2, quality findings
* fix: Add fallback for MediaQueryList.addEventListener in test env
The jsdom test environment doesn't implement addEventListener on
MediaQueryList. Prefer addEventListener but fall back to the
deprecated addListener when unavailable.
* feat: Map creator/updater IDs to existing users during JSON import
When importing documents from JSON, resolve the original document author
to an internal user by matching on user ID first, then email, falling
back to the importing user. Results are cached to avoid redundant queries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Add negative caching for user resolution during import
Cache misses (not just hits) in resolveUserId so that repeated lookups
for users that don't exist in the target team are served from cache
instead of hitting the database for every document.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: Fix resolveUserId JSDoc to match actual behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Strip comment marks from documents in presentation mode
Move removeMarks to shared ProsemirrorHelper and use it to strip comment
marks before rendering slides. Make server ProsemirrorHelper extend the
shared class to eliminate duplication and remove SharedProsemirrorHelper
imports from server code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Use Set for mark lookup and cloneDeep for browser compat
Use a Set for O(1) mark lookups in removeMarks traversal. Replace
structuredClone with lodash/cloneDeep to support older browsers
that lack the native API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: Add tests for ProsemirrorHelper.removeMarks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds group sync from external authentication providers, allowing team group memberships to be automatically managed based on provider data on sign-in in the future.
* wip
* fix: Remove unused variable in ProsemirrorHelper.extractEmojiFromStart
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test
* DRY
* Restore cleanup
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Use signed urls (ttl configurable) for attachment urls in "copy markdown"
* make signed urls a parameter, not a instance configuration
* feat(fix): copy as markdown: refactor toMarkdown (use signed urls)
* attempt to fix tests failing
* adjust markdown exporting in revisions too
* formatting
---------
Co-authored-by: Tom Moor <tom@getoutline.com>
* Add backlinks support for publicly shared documents
Include backlinks in the documents.info API response for publicly shared documents, filtering to only show backlinks that exist within the shared tree.
Changes:
- Add findSourceDocumentIdsInSharedTree method to Relationship model to find backlinks within allowed document IDs
- Export getAllIdsInSharedTree helper from shareLoader for reuse
- Update presentDocument to accept and include backlinkIds in response
- Modify documents.info endpoint to fetch and include backlinks for public shares
- Add backlinkIds property to Document model and update backlinks getter to use it when available
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* refactor
* refactor
* wip
* tsc
* fix: Signed-out view throw, spacing
* revert
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* perf: Remove findOrCreate in views.create
* findOrCreate for subscriptionCreator
* test
* Remove findOrCreate for document,collection,group memberships
* tsc