196 Commits

Author SHA1 Message Date
Tom Moor ecafd5f32a chore: Update JSON importer to use zip streaming (#12380)
* chore: Update JSON importer to use zip streaming, new importer flow

* chore: Drop teamId from import urlId collision check and remove unused internal-id scaffolding

urlId is globally unique on Document/Collection so the team scope was wrong.
Also removes leftover internal-id generation in JSONAPIImportTask that was
never used in task input/output.

* Restore classes used upstream
2026-05-25 17:03:02 -04:00
Tom Moor 77cee2806c chore: getJWTToken -> getSessionToken (#12371)
* getJWTToken -> getSessionToken

Ensure expiry is included in payload

* Refactor test harness to avoid direct usage of getSessionToken
2026-05-17 16:58:52 -04:00
Tom Moor 4a324784be Refactor MCP tests (#12347) 2026-05-13 20:57:55 -04:00
Tom Moor 9c26535815 Auto-subscribe mentioned users to document (#12235)
* 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>
2026-05-07 21:33:55 -04:00
Tom Moor d02659d325 chore: Remove stale Jest references from docs and comments (#12285)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 21:55:07 -04:00
Tom Moor 091346dfe8 chore: Migrate to vitest (#12272)
* 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>
2026-05-06 21:10:51 -04:00
Tom Moor 0139b91b5d chore: Replace lodash with es-toolkit (#12281)
* 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.
2026-05-06 21:03:47 -04:00
Tom Moor 0df6c4947a chore(test): Performance (#12267)
* 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
2026-05-05 08:26:55 -04:00
Tom Moor 4c8a1c89b2 chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217) 2026-04-29 17:45:02 -04:00
Tom Moor 281b778b2d fix: Suspended users should not be included in cached member count (#12197)
* 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>
2026-04-29 11:24:44 -04:00
Tom Moor 5610df5a26 chore: Reduce no-explicit-any warnings in server directory (#12202)
* 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.
2026-04-28 19:50:45 -04:00
Tom Moor 600108bc43 feat: Document insight rollups (#12086)
* First pass

* Remove popularity changes

* Address review feedback

- Compute retention cutoff in UTC from the database rather than worker-local TZ
- Push partition predicate into rollup source CTEs to avoid full-table scans per partition

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Anchor insight rollups to UTC and include today

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 08:11:15 -04:00
Tom Moor 505082b196 fix: Correctly validate uploaded file size using "local" storage option (#12095)
* fix: Correctly validate uploaded file size using local storage option

* fix: Normalize attachment size from BIGINT before comparison

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:25:46 -04:00
Tom Moor cbb53285a7 fix: Flaky test (#12087)
* fix: Flaky test

* fix: Validation values incorrect
2026-04-16 21:40:08 -04:00
Tom Moor 2a2774a6d0 chore: Update modelcontextprotocol (#12052)
* chore: Update modelcontextprotocol

* fix: Restore native Web API classes after jest-fetch-mock setup

jest-fetch-mock replaces globalThis.Response with a cross-fetch polyfill
that doesn't support Web Streams (ReadableStream bodies become Buffers).
The MCP SDK's @hono/node-server adapter calls response.body.getReader()
which fails with the polyfilled Response. Since dontMock() is already
called, preserving the native classes is the correct behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 08:15:43 -04:00
Tom Moor 1bd6ad830e MCP improvements (#11822)
* fix: Data always included in list_documents response

* Remove resources, add fetch tool
Fix pagination arguments do not accept string

* type -> resource

* Add URL resolving
2026-03-20 09:45:50 -04:00
Copilot 79fc0b90b9 Only include passkeys in auth.config providers when team has registered passkeys (#11748)
* Initial plan

* Only return passkeys auth provider if team has at least one registered passkey

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
2026-03-13 21:46:56 -04:00
Tom Moor 7be893f9a3 Refactor templates (#11027)
closes #8674
2026-02-20 18:53:00 -05:00
Tom Moor 1937043aed feat: MCP Server (#11464)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:14:18 -05:00
Tom Moor 957648a588 feat: OAuth dynamic client registration (#11462)
* feat: DCR first pass

* Add cleanup task, management endpoints

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* wip

* Combine migrations

* Self review

* fix: Guard OAuth policies

* fix: Application access list not updating on deletion

* feat: Add OAUTH_DISABLE_DCR env var to disable dynamic client registration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Validate max length of redirect URIs in DCR schemas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Self review

* Use withCtx methods for correct event creation

* Remove incorrect scopes_supported

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 17:30:19 -05:00
Copilot c382e1233b Convert markdown frontmatter to YAML codeblocks on import (#11420)
* Initial plan

* Add frontmatter to YAML codeblock conversion

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Add edge case tests and fix frontmatter regex, install types

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Address code review feedback - improve template literal readability

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
2026-02-12 18:32:15 -05:00
Tom Moor 00fb4d1af7 chore: Update node style imports (#11277)
- crypto → node:crypto
  - fs → node:fs
  - fs/promises → node:fs/promises
  - path → node:path
  - http → node:http
  - https → node:https
  - stream → node:stream
  - buffer → node:buffer
  - url → node:url
  - os → node:os
  - net → node:net
  - dns → node:dns
  - events → node:events
  - readline → node:readline
  - querystring → node:querystring
  - util → node:util
2026-01-26 20:51:50 -05:00
Tom Moor 57b6e9aca4 feat: Passkey support (#11065)
closes #6930
2026-01-05 19:58:46 -05:00
Tom Moor a3b2615edf chore: Remove future public bucket usage (#10977)
* No longer upload avatars to public bucket

* Public redirect

* tests

* test

* test
2025-12-24 20:24:58 -05:00
Tom Moor bf45e97641 chore: Enforce type import consistency (#10968)
* Update types

* fix circular dep

* type imports

* lint type imports and --fix
2025-12-19 23:07:02 -05:00
Tom Moor b45a096aeb feat: Implement RFC 9700 hardening against refresh token reuse (#10960)
* feat: Implement RFC 9700 hardening against refresh token reuse

* tests

* Update tests with less mocking, hit actual endpoints
2025-12-19 17:52:23 -05:00
Tom Moor a54e66e19a chore: Test improvements (#10945)
* Lazy queues, correctly closing Redis and server

* feedback

* fix: Tests not correctly split across matrix
2025-12-17 23:15:55 -05:00
Tom Moor 6499164187 chore: fix MaxEventListener warning in tests (#10943) 2025-12-17 18:57:56 -05:00
Tom Moor 6f3534c713 feat: Custom emoji reactions (#10805)
* Claude first pass

* Move custom emojis first in search results

* refactor

* fix: Remove extra load emoji call
2025-12-05 18:47:12 -05:00
Tom Moor 94252672f8 feat: Allow PKCE clients to refresh tokens (#10769)
* Add clientType concept

* Add clientType mutations

* tsc

* i18n

* fix: Invalid input handling

* tsc
2025-12-03 18:09:43 -05:00
Alex 76701e35ec fix: replace uuid package with standard module (#10318) 2025-10-10 17:06:51 -04:00
Tom Moor cc8a3d8b5e chore: Still seeing redis connection failures in CI (#9957) 2025-08-17 18:42:20 -04:00
Tom Moor 8fcb629bdf fix: Standardize request filtering between cloud / self-hosted (#9914)
* fix: Add request-filtering-agent to self-hosted environment

* refactor

* Debug logging

* self-review

* Remove unused AbortController

* test

* test

* Address feedback
2025-08-15 07:16:29 -04:00
codegen-sh[bot] c3f93a3e9d Add relationships API endpoints (#9402)
* Migrate Backlink model to generic Relationship model

- Create new Relationship model with type field to support different relationship types
- Add database migration to create relationships table and migrate existing backlinks
- Update Backlink model to delegate to Relationship model for backward compatibility
- Update BacklinksProcessor to use Relationship model with backlink type
- Update API routes to use new Relationship model
- Update test files to use Relationship model
- Maintain backward compatibility through database view and model delegation

Fixes #9366

* Update migration to rename table instead of creating new one

- Rename existing backlinks table to relationships instead of creating new table
- Add type column with default value to existing table
- Update existing rows to have type='backlink'
- Avoid expensive data migration by keeping existing data in place
- Maintain backward compatibility with database view
- Update rollback to reverse table rename and column addition

This approach is much more efficient for large datasets as it avoids copying millions of rows.

* Remove unnecessary UPDATE statement from migration

The UPDATE statement is not needed since defaultValue automatically
applies to existing rows when adding a column with a default value.

Thanks @tommoor for catching this!

* Wrap up migration in transaction

- Wrap all migration operations in a transaction for atomicity
- Add transaction parameter to all queryInterface calls
- Follow the same pattern as other migrations in the codebase
- Ensures all operations succeed or fail together

* Remove Backlink class entirely and use Relationship everywhere

- Delete server/models/Backlink.ts
- Remove Backlink export from server/models/index.ts
- Remove Backlink import and association from Document model
- All functionality now uses Relationship model with RelationshipType.Backlink
- Maintains same API through Relationship model methods
- Cleaner architecture with single relationship model

* Update documents.test.ts to use RelationshipType enum instead of string

- Import RelationshipType from Relationship model
- Replace type: "backlink" with type: RelationshipType.Backlink
- Improves type safety and consistency with enum usage

* Address code review feedback

- Add transaction wrapper to migration down method for safer rollback
- Remove unused findByTypeForUser method from Relationship model
- Method wasn't used and won't work for all relationship types (e.g., user mentions)
- Clean up code structure and improve safety

* Restore imports

* Add relationships API endpoints

- Create relationships API following stars pattern
- Add CRUD operations: create, list, delete
- Include proper validation, authentication, and authorization
- Support filtering by relationship type and document IDs
- Add relationship presenter and policies
- Register routes in main API router

* Remove relationships.create and relationships.delete endpoints

- Keep only relationships.list endpoint as requested
- Remove create and delete schemas from validation
- Update policies to only allow read operations
- Relationships will be managed internally, not via external API

* Add relationships.info endpoint

- Use Document.findByPk for authorization as requested
- Find relationship by ID and verify user has access to related document
- Return relationship details with accessible documents
- Include proper validation schema for UUID parameter

* Update 20250601223331-migrate-backlink-to-relationship.js

* Update Relationship.ts

* wip

* test

* Final tweaks

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2025-08-10 15:24:01 -04:00
Hemachandar 04c3d81b1f chore: Setup missing oxlint configs (#9862)
* shared

* server

* app

* remove vestigial eslintrc files

* update comment directives
2025-08-06 19:54:22 -04:00
Hemachandar d3eb3db7ba feat: Public sharing of collections (#9529)
* shares.info, collections.info, documents.info

* shares.list, shares.create, shares.update

* shares.sitemap

* parity with existing document shared screen

* collection share popover

* parent share and table

* collection scene

* collection link in sidebar

* sidebar and breadcrumb collection link click

* collection link click in editor

* meta

* more meta + 404 page

* map internal link, remove showLastUpdated option

* fix shares.list pagination

* show last updated

* shareLoader tests

* lint

* sidebar context for collection link

* badge in shares table

* fix existing tests

* tsc

* update failing test snapshot

* env

* signed url for collection attachments

* include collection content in SSR for screen readers

* search

* drafts can be shared

* review

* tsc, remove old shared-doc scene

* tweaks

* DRY

* refactor loader

* Remove share/collection urls

* fix: Collection overview should not be editable when viewing shared link and logged in

* Tweak public breadcrumb

* fix: Deleted documents should never be exposed through share

* empty sharedTree array where includeChildDocuments is false

* revert includeChildDocs guard for logical correctness + SSR bug fix

* fix: check document is part of share

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2025-08-03 13:07:39 -04:00
Tom Moor cf2f13193f chore: Fix Redis mock not used consistently in tests (#9716) 2025-07-23 09:38:24 -04:00
Tom Moor 772eb2f1d4 fix: randomstring dep does not work in browser with rolldown-vite (#9624)
* fix: randomstring dep does not work in browser with rolldown-vite

* fix: Last usage of randomstring, docs

* feedback
2025-07-13 09:33:16 -04:00
codegen-sh[bot] 97f8d0f265 Separate Prettier and ESLint according to best practices (#9565)
* Separate Prettier and ESLint according to best practices

- Create standalone .prettierrc configuration file
- Remove eslint-plugin-prettier integration from ESLint config
- Replace with eslint-config-prettier to disable conflicting rules
- Remove eslint-plugin-prettier dependency
- Add dedicated format and format:check scripts
- Update lint-staged to run Prettier and ESLint separately
- Format entire codebase with new Prettier configuration

This follows the recommended approach from Prettier documentation:
https://prettier.io/docs/integrating-with-linters#notes

* Remove test comment

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
2025-07-08 18:01:48 -04:00
Tom Moor 201fbb56eb perf: Add cache for document structure (#9196)
* Normalize Collection.findByPk

* Add caching of documentStructure

* fix: Do not set cache before transaction is flushed

* Mock Redis
2025-05-18 18:45:00 -04:00
Tom Moor 08b7c11461 Normalize Collection.findByPk (#9193) 2025-05-14 00:05:45 -04:00
Tom Moor b9387734c7 perf: Remove documentStructure from default query select (#9141)
* perf: Remove documentStructure from default query select

* test
2025-05-07 07:47:57 -04:00
Tom Moor bed3d1078e fix: More guards against empty text nodes (#9132) 2025-05-04 20:11:02 +00:00
Tom Moor a06671e8ce OAuth provider (#8884)
This PR contains the necessary work to make Outline an OAuth provider including:

- OAuth app registration
- OAuth app management
- Private / public apps (Public in cloud only)
- Full OAuth 2.0 spec compatible authentication flow
- Granular scopes
- User token management screen in settings
- Associated API endpoints for programatic access
2025-05-03 19:40:18 -04:00
codegen-sh[bot] a11ab56117 Cleanup the old Notion importer (#8832)
* Cleanup the old Notion importer

* Fix Notion importer cleanup PR based on feedback

* Restore Notion format references for backward compatibility

* Remove Notion import fixtures

* translations

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2025-04-06 09:04:35 -07:00
Hemachandar f6da244c33 fix: Handle empty text blocks from Notion response (#8785) 2025-03-25 04:31:06 -07:00
Tom Moor a69ef1f3c9 quick: Remove expired temporary AWS keys from fixture data (#8755)
* fix: Remove temporary AWS keys causing false positive alerts

* Previously missed PR feedback

* snap
2025-03-23 19:31:38 +00:00
Hemachandar 6e98568e5b API importer for Notion (#8710) 2025-03-23 12:19:13 -07:00
Tom Moor 533a14369c fix: Do not wait for connections at end of test suites (#8752) 2025-03-22 20:46:36 -07:00
Hemachandar 7a90a909b3 Prevent duplicate emails when user has existing access to a document. (#8263)
* check user has higher access

* membershipId column

* handle document shared email

* fix and cleanup

* tests

* jsdoc

* event changeset

* check collection permission

* change date in migration filename

* review

* rename migration filename to today

* required group, jsdoc
2025-03-04 17:56:44 -08:00