AGENTS.md
Rama main · réplica de lectura del repositorio original.
Outline is a fast, collaborative knowledge base built for teams. It's built with React and TypeScript in both frontend and backend, uses a real-time collaboration engine, and is designed for excellent performance and user experience. The backend is a Koa server with an RPC API and uses PostgreSQL and Redis. The application can be self-hosted or used as a cloud service.
There is a web client which is fully responsive and works on mobile devices.
**Monorepo Structure:**
- **`app/`** - React web application with MobX state management
- **`server/`** - Koa API server with Sequelize ORM and background workers
- **`shared/`** - Shared TypeScript types, utilities, and editor components
- **`plugins/`** - Plugin system for extending functionality
- **`public/`** - Static assets served directly
- **Various config files** - TypeScript, Vite, Vitest, oxfmt, Oxlint configurations
Refer to /docs/ARCHITECTURE.md for detailed architecture documentation.
## Instructions
You're an expert in the following areas:
- TypeScript
- React and React Router
- MobX and MobX-React
- Node.js and Koa
- Sequelize ORM
- PostgreSQL
- Redis
- HTML, CSS and Styled Components
- Prosemirror (rich text editor)
- WebSockets and real-time collaboration
## General Guidelines
- Critical – Do not create new markdown (.md) files.
- Use early returns for readability.
- Leave an empty line above every comment, unless the comment is the first line in a block.
- Emphasize type safety and static analysis.
- Follow consistent oxfmt formatting.
- Do not replace smart quotes ("") or ('') with simple quotes ("").
- Do not add translation strings manually; they will be extracted automatically from the codebase.
- UI strings use US English and full grammar. Do not join two sentences with a comma.
## Dependencies and Upgrading
- Use yarn for all dependency management.
- After updating dependency versions, install to update lockfiles:
```bash
yarn install
```
- When adding a `resolutions` entry to address a security advisory in a transitive dependency, target only the specific vulnerable descriptors using the `name@npm:<range>` syntax rather than overriding the package globally. Inspect `yarn.lock` to find the exact ranges requested by upstream packages and add one entry per vulnerable range, e.g.:
```json
"resolutions": {
"qs@npm:^6.5.2": "^6.14.2",
"qs@npm:^6.11.0": "^6.14.2",
"qs@npm:^6.14.0": "^6.14.2"
}
```
This keeps overrides scoped to the affected dependents and avoids forcing unrelated consumers onto an incompatible version.
## TypeScript Usage
- Use strict mode.
- Avoid "unknown" unless absolutely necessary.
- Never use "any".
- Prefer type definitions; avoid type assertions (as, !).
- Always use curly braces for if statements.
- Avoid # for private properties.
- Prefer interface over type for object shapes.
## Classes & Code Organization
### Class Member Order
1. Public static variables
2. Public static methods
3. Public variables
4. Public methods
5. Protected variables & methods
6. Private variables & methods
### Exports
- Exported members must appear at the top of the file.
- Always use named exports for new components & classes.
- Document ALL public/exported functions with JSDoc.
## React Usage
- Use functional components with hooks.
- Event handlers should be prefixed with "handle", like "handleClick" for onClick.
- Avoid unnecessary re-renders by using React.memo, useMemo, and useCallback appropriately.
- Use descriptive prop types with TypeScript interfaces.
- Do not import React unless it is used directly.
- Use styled-components for component styling.
- Ensure high accessibility (a11y) standards using ARIA roles and semantic HTML.
- In components that stay mounted across navigation (layout, sidebar, command bar), do not use `useLocation`. Use `useLocationSelector` to select only the value you need, or read `history.location` in effects and handlers.
- Give stable props to memoized and `observer` components, especially list rows. Do not create new arrays, objects, or JSX on each render; use shared constants for empty arrays.
- Do not read `ref.current` during render to pass it to props or context. Keep the element in state with a callback ref.
- If the initial value of `useState` reads observables, use a lazy initializer. Otherwise the expression runs, and subscribes, on every render.
## MobX State Management
- Use MobX stores for global state management.
- Keep stores in `app/stores/`.
- Use `observable`, `action`, and `computed` decorators appropriately.
- Prefer computed values over manual calculations in render.
- Keep business logic in stores, not components.
- Do not read an observable during render only for an effect or a per-item check. Use `useComputed` to derive a narrow value (e.g. `isActive`) so only affected components re-render.
- Use `observable.map` rather than a set when observers check membership by key. `ObservableSet.has()` tracks the whole set, so any change re-renders every observer.
- Store lookups called for each row of a list must not scan a computed list such as `orderedData`, as this subscribes the component to every item in the store.
- A context value provided near the app root must not change on navigation. Expose frequently changing store values as getters read on access, not values read during render.
## Database & ORM
- Use Sequelize models in `server/models/`.
- Generate migrations with Sequelize CLI:
```bash
yarn sequelize migration:create --name=add-field-to-table
```
- Run migrations with `yarn db:migrate`.
- Use transactions for multi-table operations.
- Add appropriate indexes for query performance.
- Always handle database errors gracefully.
- When updating Sequelize array or JSON attributes, replace the value instead of mutating it in place; reload the model in tests to verify persistence rather than trusting its in-memory state.
- Schedule optional background work that depends on a transaction from `afterCommit`, and use non-fatal scheduling so a queue failure does not fail the committed operation.
- When changing persisted task input fields, keep readers compatible with queued inputs or migrate them before removing old fields.
## API Design
- RESTful endpoints under `/api/`.
- Authentication endpoints under `/auth/`.
- Use consistent error responses.
- Validate request data using the validation middleware and schemas
- Use presenters to format API responses.
- Keep API routes thin, use model methods for business logic, or commands if logic spans multiple models.
## Authentication & Authorization
- JWT tokens for authentication.
- Policies in `server/policies/` for authorization.
- Use cancan-style ability checks.
- Use authenticated middleware for protected routes.
- Always verify user permissions before data access.
- When linking accounts by provider email, use exact case-insensitive equality within the workspace.
- Set account email and passkey management routes to `AuthenticationType.APP`; API-key authentication must not change login credentials or authenticators.
## Real-time Collaboration
- WebSocket connections for real-time updates.
- Use Y.js for collaborative editing.
- Handle connection state changes gracefully.
- For bulk deletions that drive authorization or WebSocket processing, load affected models in the transaction and delete each with request context so downstream processors receive the expected per-item events.
- Do not persist viewer-specific unfurls in shared document mentions; only store unfurls that map to a stable mention type.
- When persisting pasted attachment URLs, use `signedUrlToAttachmentUrl` to convert this instance’s signed URLs to stable attachment redirects. Cover HTML, Markdown, and single-URL pastes, including pasting over selected text; document nodes and link marks must not retain expiring local URLs, and external URLs must remain unchanged.
- Editor plugins that repair the document (assigning missing IDs, fixing invalid structure) must use `RepairPlugin` from `shared/editor/plugins/RepairPlugin.ts`. Other plugins that change the document in `appendTransaction` must only respond to local changes, guard with `isLocalDocChange` from `shared/editor/lib/multiplayer.ts`. Otherwise every connected client writes the change in response to remote edits and is attributed as an editor.
## Documentation
- All public/exported functions & classes must have JSDoc.
- Include:
- Description
- @param and @return (start lowercase, end with period)
- @throws if applicable
- Add a newline between the description and the @ block.
- Use correct punctuation.
## Testing
- Run tests with Vitest:
```bash
# Run a specific test file (preferred)
yarn test path/to/test.spec.ts
# Run every test (avoid)
yarn test
# Run test suites (avoid)
yarn test:app # All frontend tests
yarn test:server # All backend tests
yarn test:shared # All shared code tests
```
- Write unit tests for utilities and business logic in a collocated .test.ts file.
- Server tests share a module registry between files. A server test that uses `vi.mock` or `vi.resetModules` must start with `// @vitest-isolate true` so it runs isolated.
- Do not create new test directories
- Mock external dependencies appropriately in **mocks** folder.
- Aim for high code coverage but focus on critical paths.
## Code Quality
- Use Oxlint for linting: `yarn lint`
- Format code with oxfmt: `yarn format`
- Check types with TypeScript: `yarn tsc`
- Pre-commit hooks run automatically via Husky.
- Fix linting issues before committing.
## Error Handling
- Use custom error classes in `server/errors.ts`.
- Always catch and handle errors appropriately.
- Log errors with appropriate context.
- Return user-friendly error messages.
- Never expose sensitive information in errors.
## Performance
- Use React.memo for expensive components.
- Implement pagination for large lists.
- Use database indexes effectively.
- Cache expensive computations.
- Use `Redis.ephemeralClient` for request-path caches, rate limits, and locks, including lock extension and release.
- Monitor performance with appropriate tools.
- Lazy load routes and components where appropriate.
## Security
- Sanitize all user input.
- Always use `sanitizeUrl()` when setting `href` or `src` from user-controlled data in ProseMirror `toDOM` methods, regardless of whether it is imported via an alias or a relative path. Unlike React components, `toDOM` writes raw DOM and does not sanitize attribute values.
- Use CSRF protection.
- Use rateLimiter middleware for sensitive endpoints.
- Follow OWASP guidelines.
- Never store sensitive data in plain text.
- Use environment variables for secrets.