mirror of
https://github.com/zadam/trilium.git
synced 2025-11-03 11:56:01 +01:00
Compare commits
17 Commits
copilot/ad
...
copilot/in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
056c07591e | ||
|
|
0df15a02ba | ||
|
|
02404a5f5b | ||
|
|
ed146f71c5 | ||
|
|
4492876293 | ||
|
|
51779cf218 | ||
|
|
5536284826 | ||
|
|
e4d74108c6 | ||
|
|
faf67f5da2 | ||
|
|
021d1ba0fb | ||
|
|
ffead56a1d | ||
|
|
b644701983 | ||
|
|
18810bb86f | ||
|
|
4f442551a9 | ||
|
|
a8f565d912 | ||
|
|
0c6a57d3bb | ||
|
|
01deab9c79 |
4
.github/workflows/deploy-docs.yml
vendored
4
.github/workflows/deploy-docs.yml
vendored
@@ -10,6 +10,7 @@ on:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'apps/edit-docs/**'
|
||||
- 'apps/build-docs/**'
|
||||
- 'packages/share-theme/**'
|
||||
|
||||
# Allow manual triggering from Actions tab
|
||||
@@ -23,6 +24,7 @@ on:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'apps/edit-docs/**'
|
||||
- 'apps/build-docs/**'
|
||||
- 'packages/share-theme/**'
|
||||
|
||||
jobs:
|
||||
@@ -60,6 +62,8 @@ jobs:
|
||||
- name: Validate Built Site
|
||||
run: |
|
||||
test -f site/index.html || (echo "ERROR: site/index.html not found" && exit 1)
|
||||
test -f site/developer-guide/index.html || (echo "ERROR: site/developer-guide/index.html not found" && exit 1)
|
||||
echo "✓ User Guide and Developer Guide built successfully"
|
||||
|
||||
- name: Deploy
|
||||
uses: ./.github/actions/deploy-to-cloudflare-pages
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@playwright/test": "1.56.1",
|
||||
"@stylistic/eslint-plugin": "5.5.0",
|
||||
"@types/express": "5.0.5",
|
||||
"@types/node": "24.9.2",
|
||||
"@types/node": "24.10.0",
|
||||
"@types/yargs": "17.0.34",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"eslint": "9.39.0",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"keywords": [],
|
||||
"author": "Elian Doran <contact@eliandoran.me>",
|
||||
"license": "AGPL-3.0-only",
|
||||
"packageManager": "pnpm@10.19.0",
|
||||
"packageManager": "pnpm@10.20.0",
|
||||
"devDependencies": {
|
||||
"@redocly/cli": "2.10.0",
|
||||
"archiver": "7.0.1",
|
||||
|
||||
@@ -14,17 +14,12 @@ import BuildContext from "./context.js";
|
||||
const DOCS_ROOT = "../../../docs";
|
||||
const OUTPUT_DIR = "../../site";
|
||||
|
||||
async function buildDocsInner() {
|
||||
const i18n = await import("@triliumnext/server/src/services/i18n.js");
|
||||
await i18n.initializeTranslations();
|
||||
async function importAndExportDocs(sourcePath: string, outputSubDir: string) {
|
||||
const note = await importData(sourcePath);
|
||||
|
||||
const sqlInit = (await import("../../server/src/services/sql_init.js")).default;
|
||||
await sqlInit.createInitialDatabase(true);
|
||||
|
||||
const note = await importData(join(__dirname, DOCS_ROOT, "User Guide"));
|
||||
|
||||
// Export
|
||||
const zipFilePath = "output.zip";
|
||||
// Use a meaningful name for the temporary zip file
|
||||
const zipName = outputSubDir || "user-guide";
|
||||
const zipFilePath = `output-${zipName}.zip`;
|
||||
try {
|
||||
const { exportToZip } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
|
||||
const branch = note.getParentBranches()[0];
|
||||
@@ -36,12 +31,35 @@ async function buildDocsInner() {
|
||||
const fileOutputStream = fsExtra.createWriteStream(zipFilePath);
|
||||
await exportToZip(taskContext, branch, "share", fileOutputStream);
|
||||
await waitForStreamToFinish(fileOutputStream);
|
||||
await extractZip(zipFilePath, OUTPUT_DIR);
|
||||
|
||||
// Output to root directory if outputSubDir is empty, otherwise to subdirectory
|
||||
const outputPath = outputSubDir ? join(OUTPUT_DIR, outputSubDir) : OUTPUT_DIR;
|
||||
await extractZip(zipFilePath, outputPath);
|
||||
} finally {
|
||||
if (await fsExtra.exists(zipFilePath)) {
|
||||
await fsExtra.rm(zipFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function buildDocsInner() {
|
||||
const i18n = await import("@triliumnext/server/src/services/i18n.js");
|
||||
await i18n.initializeTranslations();
|
||||
|
||||
const sqlInit = (await import("../../server/src/services/sql_init.js")).default;
|
||||
await sqlInit.createInitialDatabase(true);
|
||||
|
||||
// Wait for becca to be loaded before importing data
|
||||
const beccaLoader = await import("../../server/src/becca/becca_loader.js");
|
||||
await beccaLoader.beccaLoaded;
|
||||
|
||||
// Build User Guide
|
||||
console.log("Building User Guide...");
|
||||
await importAndExportDocs(join(__dirname, DOCS_ROOT, "User Guide"), "");
|
||||
|
||||
// Build Developer Guide
|
||||
console.log("Building Developer Guide...");
|
||||
await importAndExportDocs(join(__dirname, DOCS_ROOT, "Developer Guide"), "developer-guide");
|
||||
|
||||
// Copy favicon.
|
||||
await fs.copyFile("../../apps/website/src/assets/favicon.ico", join(OUTPUT_DIR, "favicon.ico"));
|
||||
@@ -51,10 +69,10 @@ async function buildDocsInner() {
|
||||
|
||||
export async function importData(path: string) {
|
||||
const buffer = await createImportZip(path);
|
||||
const importService = (await import("@triliumnext/server/src/services/import/zip.js")).default;
|
||||
const TaskContext = (await import("@triliumnext/server/src/services/task_context.js")).default;
|
||||
const importService = (await import("../../server/src/services/import/zip.js")).default;
|
||||
const TaskContext = (await import("../../server/src/services/task_context.js")).default;
|
||||
const context = new TaskContext("no-progress-reporting", "importNotes", null);
|
||||
const becca = (await import("@triliumnext/server/src/becca/becca.js")).default;
|
||||
const becca = (await import("../../server/src/becca/becca.js")).default;
|
||||
|
||||
const rootNote = becca.getRoot();
|
||||
if (!rootNote) {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"color": "5.0.2",
|
||||
"dayjs": "1.11.19",
|
||||
"dayjs-plugin-utc": "0.1.2",
|
||||
"debounce": "2.2.0",
|
||||
"debounce": "3.0.0",
|
||||
"draggabilly": "3.0.0",
|
||||
"force-graph": "1.51.0",
|
||||
"globals": "16.5.0",
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"cookie-parser": "1.4.7",
|
||||
"csrf-csrf": "3.2.2",
|
||||
"dayjs": "1.11.19",
|
||||
"debounce": "2.2.0",
|
||||
"debounce": "3.0.0",
|
||||
"debug": "4.4.3",
|
||||
"ejs": "3.1.10",
|
||||
"electron": "38.5.0",
|
||||
|
||||
1016
docs/ARCHITECTURE.md
vendored
1016
docs/ARCHITECTURE.md
vendored
File diff suppressed because it is too large
Load Diff
736
docs/DATABASE.md
vendored
736
docs/DATABASE.md
vendored
@@ -1,736 +0,0 @@
|
||||
# Trilium Database Architecture
|
||||
|
||||
> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md) | [Database Schema](Developer%20Guide/Developer%20Guide/Development%20and%20architecture/Database/)
|
||||
|
||||
## Overview
|
||||
|
||||
Trilium uses **SQLite** as its embedded database engine, providing a reliable, file-based storage system that requires no separate database server. The database stores all notes, their relationships, metadata, and configuration.
|
||||
|
||||
## Database File
|
||||
|
||||
**Location:**
|
||||
- Desktop: `~/.local/share/trilium-data/document.db` (Linux/Mac) or `%APPDATA%/trilium-data/document.db` (Windows)
|
||||
- Server: Configured via `TRILIUM_DATA_DIR` environment variable
|
||||
- Docker: Mounted volume at `/home/node/trilium-data/`
|
||||
|
||||
**Characteristics:**
|
||||
- Single-file database
|
||||
- Embedded (no server required)
|
||||
- ACID compliant
|
||||
- Cross-platform
|
||||
- Supports up to 281 TB database size
|
||||
- Efficient for 100k+ notes
|
||||
|
||||
## Database Driver
|
||||
|
||||
**Library:** `better-sqlite3`
|
||||
|
||||
**Why better-sqlite3:**
|
||||
- Native performance (C++ bindings)
|
||||
- Synchronous API (simpler code)
|
||||
- Prepared statements
|
||||
- Transaction support
|
||||
- Type safety
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
// apps/server/src/services/sql.ts
|
||||
import Database from 'better-sqlite3'
|
||||
|
||||
const db = new Database('document.db')
|
||||
const stmt = db.prepare('SELECT * FROM notes WHERE noteId = ?')
|
||||
const note = stmt.get(noteId)
|
||||
```
|
||||
|
||||
## Schema Overview
|
||||
|
||||
Schema location: `apps/server/src/assets/db/schema.sql`
|
||||
|
||||
**Entity Tables:**
|
||||
- `notes` - Core note data
|
||||
- `branches` - Tree relationships
|
||||
- `attributes` - Metadata (labels/relations)
|
||||
- `revisions` - Version history
|
||||
- `attachments` - File attachments
|
||||
- `blobs` - Binary content storage
|
||||
|
||||
**System Tables:**
|
||||
- `options` - Application configuration
|
||||
- `entity_changes` - Change tracking for sync
|
||||
- `recent_notes` - Recently accessed notes
|
||||
- `etapi_tokens` - API authentication tokens
|
||||
- `user_data` - User credentials
|
||||
- `sessions` - Web session storage
|
||||
|
||||
## Entity Tables
|
||||
|
||||
### Notes Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE notes (
|
||||
noteId TEXT NOT NULL PRIMARY KEY,
|
||||
title TEXT NOT NULL DEFAULT "note",
|
||||
isProtected INT NOT NULL DEFAULT 0,
|
||||
type TEXT NOT NULL DEFAULT 'text',
|
||||
mime TEXT NOT NULL DEFAULT 'text/html',
|
||||
blobId TEXT DEFAULT NULL,
|
||||
isDeleted INT NOT NULL DEFAULT 0,
|
||||
deleteId TEXT DEFAULT NULL,
|
||||
dateCreated TEXT NOT NULL,
|
||||
dateModified TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IDX_notes_title ON notes (title);
|
||||
CREATE INDEX IDX_notes_type ON notes (type);
|
||||
CREATE INDEX IDX_notes_dateCreated ON notes (dateCreated);
|
||||
CREATE INDEX IDX_notes_dateModified ON notes (dateModified);
|
||||
CREATE INDEX IDX_notes_utcDateModified ON notes (utcDateModified);
|
||||
CREATE INDEX IDX_notes_blobId ON notes (blobId);
|
||||
```
|
||||
|
||||
**Field Descriptions:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `noteId` | TEXT | Unique identifier (UUID or custom) |
|
||||
| `title` | TEXT | Note title (displayed in tree) |
|
||||
| `isProtected` | INT | 1 if encrypted, 0 if not |
|
||||
| `type` | TEXT | Note type: text, code, file, image, etc. |
|
||||
| `mime` | TEXT | MIME type: text/html, application/json, etc. |
|
||||
| `blobId` | TEXT | Reference to content in blobs table |
|
||||
| `isDeleted` | INT | Soft delete flag |
|
||||
| `deleteId` | TEXT | Unique delete operation ID |
|
||||
| `dateCreated` | TEXT | Creation date (local timezone) |
|
||||
| `dateModified` | TEXT | Last modified (local timezone) |
|
||||
| `utcDateCreated` | TEXT | Creation date (UTC) |
|
||||
| `utcDateModified` | TEXT | Last modified (UTC) |
|
||||
|
||||
**Note Types:**
|
||||
- `text` - Rich text with HTML
|
||||
- `code` - Source code
|
||||
- `file` - Binary file
|
||||
- `image` - Image file
|
||||
- `search` - Saved search
|
||||
- `render` - Custom HTML rendering
|
||||
- `relation-map` - Relationship diagram
|
||||
- `canvas` - Excalidraw drawing
|
||||
- `mermaid` - Mermaid diagram
|
||||
- `book` - Container for documentation
|
||||
- `web-view` - Embedded web page
|
||||
- `mindmap` - Mind map
|
||||
- `geomap` - Geographical map
|
||||
|
||||
### Branches Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE branches (
|
||||
branchId TEXT NOT NULL PRIMARY KEY,
|
||||
noteId TEXT NOT NULL,
|
||||
parentNoteId TEXT NOT NULL,
|
||||
notePosition INTEGER NOT NULL,
|
||||
prefix TEXT,
|
||||
isExpanded INTEGER NOT NULL DEFAULT 0,
|
||||
isDeleted INTEGER NOT NULL DEFAULT 0,
|
||||
deleteId TEXT DEFAULT NULL,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IDX_branches_noteId_parentNoteId ON branches (noteId, parentNoteId);
|
||||
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||
```
|
||||
|
||||
**Field Descriptions:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `branchId` | TEXT | Unique identifier for this branch |
|
||||
| `noteId` | TEXT | Child note ID |
|
||||
| `parentNoteId` | TEXT | Parent note ID |
|
||||
| `notePosition` | INT | Sort order among siblings |
|
||||
| `prefix` | TEXT | Optional prefix text (e.g., "Chapter 1:") |
|
||||
| `isExpanded` | INT | Tree expansion state |
|
||||
| `isDeleted` | INT | Soft delete flag |
|
||||
| `deleteId` | TEXT | Delete operation ID |
|
||||
| `utcDateModified` | TEXT | Last modified (UTC) |
|
||||
|
||||
**Key Concepts:**
|
||||
- **Cloning:** A note can have multiple branches (multiple parents)
|
||||
- **Position:** Siblings ordered by `notePosition`
|
||||
- **Prefix:** Display text before note title in tree
|
||||
- **Soft Delete:** Allows sync before permanent deletion
|
||||
|
||||
### Attributes Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE attributes (
|
||||
attributeId TEXT NOT NULL PRIMARY KEY,
|
||||
noteId TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
value TEXT DEFAULT '' NOT NULL,
|
||||
position INT DEFAULT 0 NOT NULL,
|
||||
utcDateModified TEXT NOT NULL,
|
||||
isDeleted INT NOT NULL,
|
||||
deleteId TEXT DEFAULT NULL,
|
||||
isInheritable INT DEFAULT 0 NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IDX_attributes_name_value ON attributes (name, value);
|
||||
CREATE INDEX IDX_attributes_noteId ON attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_value ON attributes (value);
|
||||
```
|
||||
|
||||
**Field Descriptions:**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `attributeId` | TEXT | Unique identifier |
|
||||
| `noteId` | TEXT | Note this attribute belongs to |
|
||||
| `type` | TEXT | 'label' or 'relation' |
|
||||
| `name` | TEXT | Attribute name |
|
||||
| `value` | TEXT | Attribute value (text for labels, noteId for relations) |
|
||||
| `position` | INT | Display order |
|
||||
| `utcDateModified` | TEXT | Last modified (UTC) |
|
||||
| `isDeleted` | INT | Soft delete flag |
|
||||
| `deleteId` | TEXT | Delete operation ID |
|
||||
| `isInheritable` | INT | Inherited by child notes |
|
||||
|
||||
**Attribute Types:**
|
||||
|
||||
**Labels** (key-value pairs):
|
||||
```sql
|
||||
-- Example: #priority=high
|
||||
INSERT INTO attributes (attributeId, noteId, type, name, value)
|
||||
VALUES ('attr1', 'note123', 'label', 'priority', 'high')
|
||||
```
|
||||
|
||||
**Relations** (links to other notes):
|
||||
```sql
|
||||
-- Example: ~author=[[noteId]]
|
||||
INSERT INTO attributes (attributeId, noteId, type, name, value)
|
||||
VALUES ('attr2', 'note123', 'relation', 'author', 'author-note-id')
|
||||
```
|
||||
|
||||
**Special Attributes:**
|
||||
- `#run=frontendStartup` - Execute script on frontend load
|
||||
- `#run=backendStartup` - Execute script on backend load
|
||||
- `#customWidget` - Custom widget implementation
|
||||
- `#iconClass` - Custom tree icon
|
||||
- `#cssClass` - CSS class for note
|
||||
- `#sorted` - Auto-sort children
|
||||
- `#hideChildrenOverview` - Don't show child list
|
||||
|
||||
### Revisions Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE revisions (
|
||||
revisionId TEXT NOT NULL PRIMARY KEY,
|
||||
noteId TEXT NOT NULL,
|
||||
type TEXT DEFAULT '' NOT NULL,
|
||||
mime TEXT DEFAULT '' NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
isProtected INT NOT NULL DEFAULT 0,
|
||||
blobId TEXT DEFAULT NULL,
|
||||
utcDateLastEdited TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
utcDateModified TEXT NOT NULL,
|
||||
dateLastEdited TEXT NOT NULL,
|
||||
dateCreated TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IDX_revisions_noteId ON revisions (noteId);
|
||||
CREATE INDEX IDX_revisions_utcDateCreated ON revisions (utcDateCreated);
|
||||
CREATE INDEX IDX_revisions_utcDateLastEdited ON revisions (utcDateLastEdited);
|
||||
CREATE INDEX IDX_revisions_blobId ON revisions (blobId);
|
||||
```
|
||||
|
||||
**Revision Strategy:**
|
||||
- Automatic revision created on note modification
|
||||
- Configurable interval (default: daily max)
|
||||
- Stores complete note snapshot
|
||||
- Allows reverting to previous versions
|
||||
- Can be disabled with `#disableVersioning`
|
||||
|
||||
### Attachments Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE attachments (
|
||||
attachmentId TEXT NOT NULL PRIMARY KEY,
|
||||
ownerId TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
mime TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
isProtected INT NOT NULL DEFAULT 0,
|
||||
position INT DEFAULT 0 NOT NULL,
|
||||
blobId TEXT DEFAULT NULL,
|
||||
dateModified TEXT NOT NULL,
|
||||
utcDateModified TEXT NOT NULL,
|
||||
utcDateScheduledForErasureSince TEXT DEFAULT NULL,
|
||||
isDeleted INT NOT NULL,
|
||||
deleteId TEXT DEFAULT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IDX_attachments_ownerId_role ON attachments (ownerId, role);
|
||||
CREATE INDEX IDX_attachments_blobId ON attachments (blobId);
|
||||
```
|
||||
|
||||
**Attachment Roles:**
|
||||
- `file` - Regular file attachment
|
||||
- `image` - Image file
|
||||
- `cover-image` - Note cover image
|
||||
- Custom roles for specific purposes
|
||||
|
||||
### Blobs Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE blobs (
|
||||
blobId TEXT NOT NULL PRIMARY KEY,
|
||||
content TEXT NULL DEFAULT NULL,
|
||||
dateModified TEXT NOT NULL,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**Blob Usage:**
|
||||
- Stores actual content (text or binary)
|
||||
- Referenced by notes, revisions, attachments
|
||||
- Deduplication via hash-based blobId
|
||||
- TEXT type stores both text and binary (base64)
|
||||
|
||||
**Content Types:**
|
||||
- **Text notes:** HTML content
|
||||
- **Code notes:** Plain text source code
|
||||
- **Binary notes:** Base64 encoded data
|
||||
- **Protected notes:** Encrypted content
|
||||
|
||||
## System Tables
|
||||
|
||||
### Options Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE options (
|
||||
name TEXT NOT NULL PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
isSynced INTEGER DEFAULT 0 NOT NULL,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**Key Options:**
|
||||
- `documentId` - Unique installation ID
|
||||
- `dbVersion` - Schema version
|
||||
- `syncVersion` - Sync protocol version
|
||||
- `passwordVerificationHash` - Password verification
|
||||
- `encryptedDataKey` - Encryption key (encrypted)
|
||||
- `theme` - UI theme
|
||||
- Various feature flags and settings
|
||||
|
||||
**Synced Options:**
|
||||
- `isSynced = 1` - Synced across devices
|
||||
- `isSynced = 0` - Local to this installation
|
||||
|
||||
### Entity Changes Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE entity_changes (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
entityName TEXT NOT NULL,
|
||||
entityId TEXT NOT NULL,
|
||||
hash TEXT NOT NULL,
|
||||
isErased INT NOT NULL,
|
||||
changeId TEXT NOT NULL,
|
||||
componentId TEXT NOT NULL,
|
||||
instanceId TEXT NOT NULL,
|
||||
isSynced INTEGER NOT NULL,
|
||||
utcDateChanged TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX IDX_entityChanges_entityName_entityId
|
||||
ON entity_changes (entityName, entityId);
|
||||
CREATE INDEX IDX_entity_changes_changeId ON entity_changes (changeId);
|
||||
```
|
||||
|
||||
**Purpose:** Track all entity modifications for synchronization
|
||||
|
||||
**Entity Types:**
|
||||
- `notes`
|
||||
- `branches`
|
||||
- `attributes`
|
||||
- `revisions`
|
||||
- `attachments`
|
||||
- `options`
|
||||
- `etapi_tokens`
|
||||
|
||||
### Recent Notes Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE recent_notes (
|
||||
noteId TEXT NOT NULL PRIMARY KEY,
|
||||
notePath TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**Purpose:** Track recently accessed notes for quick access
|
||||
|
||||
### Sessions Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE sessions (
|
||||
sid TEXT PRIMARY KEY,
|
||||
sess TEXT NOT NULL,
|
||||
expired TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
**Purpose:** HTTP session storage for web interface
|
||||
|
||||
### User Data Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_data (
|
||||
tmpID INT PRIMARY KEY,
|
||||
username TEXT,
|
||||
email TEXT,
|
||||
userIDEncryptedDataKey TEXT,
|
||||
userIDVerificationHash TEXT,
|
||||
salt TEXT,
|
||||
derivedKey TEXT,
|
||||
isSetup TEXT DEFAULT "false"
|
||||
);
|
||||
```
|
||||
|
||||
**Purpose:** Store user authentication credentials
|
||||
|
||||
### ETAPI Tokens Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE etapi_tokens (
|
||||
etapiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
tokenHash TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
utcDateModified TEXT NOT NULL,
|
||||
isDeleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
```
|
||||
|
||||
**Purpose:** API token authentication for external access
|
||||
|
||||
## Data Relationships
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Notes │
|
||||
└───┬──────────┘
|
||||
│
|
||||
┌───────────┼───────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────┐ ┌──────────┐ ┌───────────┐
|
||||
│Branches│ │Attributes│ │Attachments│
|
||||
└────────┘ └──────────┘ └─────┬─────┘
|
||||
│ │
|
||||
│ │
|
||||
│ ┌──────────┐ │
|
||||
└──────▶│ Blobs │◀────────┘
|
||||
└──────────┘
|
||||
▲
|
||||
│
|
||||
┌────┴─────┐
|
||||
│Revisions │
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
**Relationships:**
|
||||
- Notes ↔ Branches (many-to-many via noteId)
|
||||
- Notes → Attributes (one-to-many)
|
||||
- Notes → Blobs (one-to-one)
|
||||
- Notes → Revisions (one-to-many)
|
||||
- Notes → Attachments (one-to-many)
|
||||
- Attachments → Blobs (one-to-one)
|
||||
- Revisions → Blobs (one-to-one)
|
||||
|
||||
## Database Access Patterns
|
||||
|
||||
### Direct SQL Access
|
||||
|
||||
**Location:** `apps/server/src/services/sql.ts`
|
||||
|
||||
```typescript
|
||||
// Execute query (returns rows)
|
||||
const notes = sql.getRows('SELECT * FROM notes WHERE type = ?', ['text'])
|
||||
|
||||
// Execute query (returns single row)
|
||||
const note = sql.getRow('SELECT * FROM notes WHERE noteId = ?', [noteId])
|
||||
|
||||
// Execute statement (no return)
|
||||
sql.execute('UPDATE notes SET title = ? WHERE noteId = ?', [title, noteId])
|
||||
|
||||
// Insert
|
||||
sql.insert('notes', {
|
||||
noteId: 'new-note-id',
|
||||
title: 'New Note',
|
||||
type: 'text',
|
||||
// ...
|
||||
})
|
||||
|
||||
// Transactions
|
||||
sql.transactional(() => {
|
||||
sql.execute('UPDATE ...')
|
||||
sql.execute('INSERT ...')
|
||||
})
|
||||
```
|
||||
|
||||
### Entity-Based Access (Recommended)
|
||||
|
||||
**Via Becca Cache:**
|
||||
|
||||
```typescript
|
||||
// Get entity from cache
|
||||
const note = becca.getNote(noteId)
|
||||
|
||||
// Modify and save
|
||||
note.title = 'Updated Title'
|
||||
note.save() // Writes to database
|
||||
|
||||
// Create new
|
||||
const newNote = becca.createNote({
|
||||
parentNoteId: 'root',
|
||||
title: 'New Note',
|
||||
type: 'text',
|
||||
content: 'Hello World'
|
||||
})
|
||||
|
||||
// Delete
|
||||
note.markAsDeleted()
|
||||
```
|
||||
|
||||
## Database Migrations
|
||||
|
||||
**Location:** `apps/server/src/migrations/`
|
||||
|
||||
**Migration Files:**
|
||||
- Format: `XXXX_migration_name.sql` or `XXXX_migration_name.js`
|
||||
- Executed in numerical order
|
||||
- Version tracked in `options.dbVersion`
|
||||
|
||||
**SQL Migration Example:**
|
||||
```sql
|
||||
-- 0280_add_new_column.sql
|
||||
ALTER TABLE notes ADD COLUMN newField TEXT DEFAULT NULL;
|
||||
|
||||
UPDATE options SET value = '280' WHERE name = 'dbVersion';
|
||||
```
|
||||
|
||||
**JavaScript Migration Example:**
|
||||
```javascript
|
||||
// 0285_complex_migration.js
|
||||
module.exports = () => {
|
||||
const notes = sql.getRows('SELECT * FROM notes WHERE type = ?', ['old-type'])
|
||||
|
||||
for (const note of notes) {
|
||||
sql.execute('UPDATE notes SET type = ? WHERE noteId = ?',
|
||||
['new-type', note.noteId])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Migration Process:**
|
||||
1. Server checks `dbVersion` on startup
|
||||
2. Compares with latest migration number
|
||||
3. Executes pending migrations in order
|
||||
4. Updates `dbVersion` after each
|
||||
5. Restarts if migrations ran
|
||||
|
||||
## Database Maintenance
|
||||
|
||||
### Backup
|
||||
|
||||
**Full Backup:**
|
||||
```bash
|
||||
# Copy database file
|
||||
cp document.db document.db.backup
|
||||
|
||||
# Or use Trilium's backup feature
|
||||
# Settings → Backup
|
||||
```
|
||||
|
||||
**Automatic Backups:**
|
||||
- Daily backup (configurable)
|
||||
- Stored in `backup/` directory
|
||||
- Retention policy (keep last N backups)
|
||||
|
||||
### Vacuum
|
||||
|
||||
**Purpose:** Reclaim unused space, defragment
|
||||
|
||||
```sql
|
||||
VACUUM;
|
||||
```
|
||||
|
||||
**When to vacuum:**
|
||||
- After deleting many notes
|
||||
- Database file size larger than expected
|
||||
- Performance degradation
|
||||
|
||||
### Integrity Check
|
||||
|
||||
```sql
|
||||
PRAGMA integrity_check;
|
||||
```
|
||||
|
||||
**Result:** "ok" or list of errors
|
||||
|
||||
### Consistency Checks
|
||||
|
||||
**Built-in Consistency Checks:**
|
||||
|
||||
Location: `apps/server/src/services/consistency_checks.ts`
|
||||
|
||||
- Orphaned branches
|
||||
- Missing parent notes
|
||||
- Circular dependencies
|
||||
- Invalid entity references
|
||||
- Blob reference integrity
|
||||
|
||||
**Run Checks:**
|
||||
```typescript
|
||||
// Via API
|
||||
POST /api/consistency-check
|
||||
|
||||
// Or from backend script
|
||||
api.runConsistencyChecks()
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Indexes
|
||||
|
||||
**Existing Indexes:**
|
||||
- `notes.title` - Fast title searches
|
||||
- `notes.type` - Filter by type
|
||||
- `notes.dateCreated/Modified` - Time-based queries
|
||||
- `branches.noteId_parentNoteId` - Tree navigation
|
||||
- `attributes.name_value` - Attribute searches
|
||||
|
||||
**Query Optimization:**
|
||||
```sql
|
||||
-- Use indexed columns in WHERE clause
|
||||
SELECT * FROM notes WHERE type = 'text' -- Uses index
|
||||
|
||||
-- Avoid functions on indexed columns
|
||||
SELECT * FROM notes WHERE LOWER(title) = 'test' -- No index
|
||||
|
||||
-- Better
|
||||
SELECT * FROM notes WHERE title = 'Test' -- Uses index
|
||||
```
|
||||
|
||||
### Connection Settings
|
||||
|
||||
```typescript
|
||||
// apps/server/src/services/sql.ts
|
||||
const db = new Database('document.db', {
|
||||
// Enable WAL mode for better concurrency
|
||||
verbose: console.log
|
||||
})
|
||||
|
||||
db.pragma('journal_mode = WAL')
|
||||
db.pragma('synchronous = NORMAL')
|
||||
db.pragma('cache_size = -64000') // 64MB cache
|
||||
db.pragma('temp_store = MEMORY')
|
||||
```
|
||||
|
||||
**WAL Mode Benefits:**
|
||||
- Better concurrency (readers don't block writers)
|
||||
- Faster commits
|
||||
- More robust
|
||||
|
||||
### Query Performance
|
||||
|
||||
**Use EXPLAIN QUERY PLAN:**
|
||||
```sql
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM notes
|
||||
WHERE type = 'text'
|
||||
AND dateCreated > '2025-01-01'
|
||||
```
|
||||
|
||||
**Analyze slow queries:**
|
||||
- Check index usage
|
||||
- Avoid SELECT *
|
||||
- Use prepared statements
|
||||
- Batch operations in transactions
|
||||
|
||||
## Database Size Management
|
||||
|
||||
**Typical Sizes:**
|
||||
- 1,000 notes: ~5-10 MB
|
||||
- 10,000 notes: ~50-100 MB
|
||||
- 100,000 notes: ~500 MB - 1 GB
|
||||
|
||||
**Size Reduction Strategies:**
|
||||
|
||||
1. **Delete old revisions**
|
||||
2. **Remove large attachments**
|
||||
3. **Vacuum database**
|
||||
4. **Compact blobs**
|
||||
5. **Archive old notes**
|
||||
|
||||
**Blob Deduplication:**
|
||||
- Blobs identified by content hash
|
||||
- Identical content shares one blob
|
||||
- Automatic deduplication on insert
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Protected Notes Encryption
|
||||
|
||||
**Encryption Process:**
|
||||
```typescript
|
||||
// Encrypt blob content
|
||||
const encryptedContent = encrypt(content, dataKey)
|
||||
blob.content = encryptedContent
|
||||
|
||||
// Store encrypted
|
||||
sql.insert('blobs', { blobId, content: encryptedContent })
|
||||
```
|
||||
|
||||
**Encryption Details:**
|
||||
- Algorithm: AES-256-CBC
|
||||
- Key derivation: PBKDF2 (10,000 iterations)
|
||||
- Per-note encryption
|
||||
- Master key encrypted with user password
|
||||
|
||||
### SQL Injection Prevention
|
||||
|
||||
**Always use parameterized queries:**
|
||||
```typescript
|
||||
// GOOD - Safe from SQL injection
|
||||
sql.execute('SELECT * FROM notes WHERE title = ?', [userInput])
|
||||
|
||||
// BAD - Vulnerable to SQL injection
|
||||
sql.execute(`SELECT * FROM notes WHERE title = '${userInput}'`)
|
||||
```
|
||||
|
||||
### Database File Protection
|
||||
|
||||
**File Permissions:**
|
||||
- Owner read/write only
|
||||
- No group/other access
|
||||
- Located in user-specific directory
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Overall architecture
|
||||
- [Database Schema Files](Developer%20Guide/Developer%20Guide/Development%20and%20architecture/Database/)
|
||||
- [Migration Scripts](../apps/server/src/migrations/)
|
||||
155
docs/QUICK_REFERENCE.md
vendored
155
docs/QUICK_REFERENCE.md
vendored
@@ -1,155 +0,0 @@
|
||||
# Trilium Technical Documentation - Quick Reference
|
||||
|
||||
> **Start here:** [TECHNICAL_DOCUMENTATION.md](TECHNICAL_DOCUMENTATION.md) - Complete index of all documentation
|
||||
|
||||
## 📖 Documentation Files
|
||||
|
||||
| Document | Description | Size | Lines |
|
||||
|----------|-------------|------|-------|
|
||||
| [TECHNICAL_DOCUMENTATION.md](TECHNICAL_DOCUMENTATION.md) | Main index and navigation hub | 13KB | 423 |
|
||||
| [ARCHITECTURE.md](ARCHITECTURE.md) | Complete system architecture | 30KB | 1,016 |
|
||||
| [DATABASE.md](DATABASE.md) | Database schema and operations | 19KB | 736 |
|
||||
| [SYNCHRONIZATION.md](SYNCHRONIZATION.md) | Sync protocol and implementation | 14KB | 583 |
|
||||
| [SCRIPTING.md](SCRIPTING.md) | User scripting system guide | 17KB | 734 |
|
||||
| [SECURITY_ARCHITECTURE.md](SECURITY_ARCHITECTURE.md) | Security implementation details | 19KB | 834 |
|
||||
|
||||
**Total:** 112KB of comprehensive documentation across 4,326 lines!
|
||||
|
||||
## 🎯 Quick Access by Role
|
||||
|
||||
### 👤 End Users
|
||||
- **Getting Started:** [User Guide](User%20Guide/User%20Guide/)
|
||||
- **Scripting:** [SCRIPTING.md](SCRIPTING.md)
|
||||
- **Sync Setup:** [SYNCHRONIZATION.md](SYNCHRONIZATION.md)
|
||||
|
||||
### 💻 Developers
|
||||
- **Architecture:** [ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
- **Development Setup:** [Developer Guide](Developer%20Guide/Developer%20Guide/Environment%20Setup.md)
|
||||
- **Database:** [DATABASE.md](DATABASE.md)
|
||||
|
||||
### 🔒 Security Auditors
|
||||
- **Security:** [SECURITY_ARCHITECTURE.md](SECURITY_ARCHITECTURE.md)
|
||||
- **Encryption:** [SECURITY_ARCHITECTURE.md#encryption](SECURITY_ARCHITECTURE.md#encryption)
|
||||
- **Auth:** [SECURITY_ARCHITECTURE.md#authentication](SECURITY_ARCHITECTURE.md#authentication)
|
||||
|
||||
### 🏗️ System Architects
|
||||
- **Overall Design:** [ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
- **Cache System:** [ARCHITECTURE.md#three-layer-cache-system](ARCHITECTURE.md#three-layer-cache-system)
|
||||
- **Entity Model:** [ARCHITECTURE.md#entity-system](ARCHITECTURE.md#entity-system)
|
||||
|
||||
### 🔧 DevOps Engineers
|
||||
- **Server Installation:** [User Guide - Server Installation](User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
|
||||
- **Docker:** [Developer Guide - Docker](Developer%20Guide/Developer%20Guide/Development%20and%20architecture/Docker.md)
|
||||
- **Sync Server:** [SYNCHRONIZATION.md#sync-server-configuration](SYNCHRONIZATION.md#sync-server-configuration)
|
||||
|
||||
### 📊 Database Administrators
|
||||
- **Schema:** [DATABASE.md#database-schema](DATABASE.md#database-schema)
|
||||
- **Maintenance:** [DATABASE.md#database-maintenance](DATABASE.md#database-maintenance)
|
||||
- **Performance:** [DATABASE.md#performance-optimization](DATABASE.md#performance-optimization)
|
||||
|
||||
## 🔍 Quick Topic Finder
|
||||
|
||||
### Core Concepts
|
||||
- **Becca Cache:** [ARCHITECTURE.md#1-becca-backend-cache](ARCHITECTURE.md#1-becca-backend-cache)
|
||||
- **Froca Cache:** [ARCHITECTURE.md#2-froca-frontend-cache](ARCHITECTURE.md#2-froca-frontend-cache)
|
||||
- **Entity System:** [ARCHITECTURE.md#entity-system](ARCHITECTURE.md#entity-system)
|
||||
- **Widget System:** [ARCHITECTURE.md#widget-based-ui](ARCHITECTURE.md#widget-based-ui)
|
||||
|
||||
### Database
|
||||
- **Schema Overview:** [DATABASE.md#schema-overview](DATABASE.md#schema-overview)
|
||||
- **Notes Table:** [DATABASE.md#notes-table](DATABASE.md#notes-table)
|
||||
- **Branches Table:** [DATABASE.md#branches-table](DATABASE.md#branches-table)
|
||||
- **Migrations:** [DATABASE.md#database-migrations](DATABASE.md#database-migrations)
|
||||
|
||||
### Synchronization
|
||||
- **Sync Protocol:** [SYNCHRONIZATION.md#sync-protocol](SYNCHRONIZATION.md#sync-protocol)
|
||||
- **Conflict Resolution:** [SYNCHRONIZATION.md#conflict-resolution](SYNCHRONIZATION.md#conflict-resolution)
|
||||
- **Entity Changes:** [SYNCHRONIZATION.md#entity-changes](SYNCHRONIZATION.md#entity-changes)
|
||||
|
||||
### Scripting
|
||||
- **Frontend Scripts:** [SCRIPTING.md#frontend-scripts](SCRIPTING.md#frontend-scripts)
|
||||
- **Backend Scripts:** [SCRIPTING.md#backend-scripts](SCRIPTING.md#backend-scripts)
|
||||
- **Script Examples:** [SCRIPTING.md#script-examples](SCRIPTING.md#script-examples)
|
||||
- **API Reference:** [SCRIPTING.md#script-api](SCRIPTING.md#script-api)
|
||||
|
||||
### Security
|
||||
- **Authentication:** [SECURITY_ARCHITECTURE.md#authentication](SECURITY_ARCHITECTURE.md#authentication)
|
||||
- **Encryption:** [SECURITY_ARCHITECTURE.md#encryption](SECURITY_ARCHITECTURE.md#encryption)
|
||||
- **Input Sanitization:** [SECURITY_ARCHITECTURE.md#input-sanitization](SECURITY_ARCHITECTURE.md#input-sanitization)
|
||||
- **Best Practices:** [SECURITY_ARCHITECTURE.md#security-best-practices](SECURITY_ARCHITECTURE.md#security-best-practices)
|
||||
|
||||
## 📚 Learning Paths
|
||||
|
||||
### New to Trilium Development
|
||||
1. Read [ARCHITECTURE.md](ARCHITECTURE.md) - System overview
|
||||
2. Setup environment: [Environment Setup](Developer%20Guide/Developer%20Guide/Environment%20Setup.md)
|
||||
3. Explore [DATABASE.md](DATABASE.md) - Understand data model
|
||||
4. Check [Developer Guide](Developer%20Guide/Developer%20Guide/)
|
||||
|
||||
### Want to Create Scripts
|
||||
1. Read [SCRIPTING.md](SCRIPTING.md) - Complete guide
|
||||
2. Check [Script API](Script%20API/) - API reference
|
||||
3. Review examples: [SCRIPTING.md#script-examples](SCRIPTING.md#script-examples)
|
||||
4. Explore [Advanced Showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases)
|
||||
|
||||
### Setting Up Sync
|
||||
1. Understand protocol: [SYNCHRONIZATION.md](SYNCHRONIZATION.md)
|
||||
2. Configure server: [SYNCHRONIZATION.md#sync-server-configuration](SYNCHRONIZATION.md#sync-server-configuration)
|
||||
3. Setup clients: [SYNCHRONIZATION.md#client-setup](SYNCHRONIZATION.md#client-setup)
|
||||
4. Troubleshoot: [SYNCHRONIZATION.md#troubleshooting](SYNCHRONIZATION.md#troubleshooting)
|
||||
|
||||
### Security Review
|
||||
1. Read threat model: [SECURITY_ARCHITECTURE.md#threat-model](SECURITY_ARCHITECTURE.md#threat-model)
|
||||
2. Review authentication: [SECURITY_ARCHITECTURE.md#authentication](SECURITY_ARCHITECTURE.md#authentication)
|
||||
3. Check encryption: [SECURITY_ARCHITECTURE.md#encryption](SECURITY_ARCHITECTURE.md#encryption)
|
||||
4. Verify best practices: [SECURITY_ARCHITECTURE.md#security-best-practices](SECURITY_ARCHITECTURE.md#security-best-practices)
|
||||
|
||||
## 🗺️ Documentation Map
|
||||
|
||||
```
|
||||
docs/
|
||||
├── TECHNICAL_DOCUMENTATION.md ← START HERE (Index)
|
||||
│
|
||||
├── Core Technical Docs
|
||||
│ ├── ARCHITECTURE.md (System design)
|
||||
│ ├── DATABASE.md (Data layer)
|
||||
│ ├── SYNCHRONIZATION.md (Sync system)
|
||||
│ ├── SCRIPTING.md (User scripting)
|
||||
│ └── SECURITY_ARCHITECTURE.md (Security)
|
||||
│
|
||||
├── Developer Guide/
|
||||
│ └── Developer Guide/ (Development setup)
|
||||
│
|
||||
├── User Guide/
|
||||
│ └── User Guide/ (End-user docs)
|
||||
│
|
||||
└── Script API/ (API reference)
|
||||
```
|
||||
|
||||
## 💡 Tips for Reading Documentation
|
||||
|
||||
1. **Start with the index:** [TECHNICAL_DOCUMENTATION.md](TECHNICAL_DOCUMENTATION.md) provides an overview
|
||||
2. **Use search:** Press Ctrl+F / Cmd+F to find specific topics
|
||||
3. **Follow links:** Documents are cross-referenced for easy navigation
|
||||
4. **Code examples:** Most docs include practical code examples
|
||||
5. **See Also sections:** Check bottom of each doc for related resources
|
||||
|
||||
## 🔗 External Resources
|
||||
|
||||
- **Website:** https://triliumnotes.org
|
||||
- **Online Docs:** https://docs.triliumnotes.org
|
||||
- **GitHub:** https://github.com/TriliumNext/Trilium
|
||||
- **Discussions:** https://github.com/TriliumNext/Trilium/discussions
|
||||
- **Matrix Chat:** https://matrix.to/#/#triliumnext:matrix.org
|
||||
|
||||
## 🤝 Contributing to Documentation
|
||||
|
||||
Found an error or want to improve the docs? See:
|
||||
- [Contributing Guide](../README.md#-contribute)
|
||||
- [Documentation Standards](TECHNICAL_DOCUMENTATION.md#documentation-conventions)
|
||||
|
||||
---
|
||||
|
||||
**Version:** 0.99.3
|
||||
**Last Updated:** November 2025
|
||||
**Maintained by:** TriliumNext Team
|
||||
22
docs/README.md
vendored
22
docs/README.md
vendored
@@ -1,17 +1,4 @@
|
||||
# Trilium Notes Documentation
|
||||
|
||||
## 📚 Technical Documentation
|
||||
|
||||
**NEW:** Comprehensive technical and architectural documentation is now available!
|
||||
|
||||
- **[Technical Documentation Index](TECHNICAL_DOCUMENTATION.md)** - Complete index to all technical docs
|
||||
- **[Architecture Overview](ARCHITECTURE.md)** - System design and core patterns
|
||||
- **[Database Architecture](DATABASE.md)** - Complete database documentation
|
||||
- **[Synchronization](SYNCHRONIZATION.md)** - Sync protocol and implementation
|
||||
- **[Scripting System](SCRIPTING.md)** - User scripting guide and API
|
||||
- **[Security Architecture](SECURITY_ARCHITECTURE.md)** - Security implementation details
|
||||
|
||||
## 📖 User Documentation
|
||||
# Trilium Notes
|
||||
|
||||
Please see the [main documentation](index.md) or visit one of our translated versions:
|
||||
|
||||
@@ -22,11 +9,4 @@ Please see the [main documentation](index.md) or visit one of our translated ver
|
||||
- [简体中文](README-ZH_CN.md)
|
||||
- [繁體中文](README-ZH_TW.md)
|
||||
|
||||
## 🔧 Developer Documentation
|
||||
|
||||
- [Developer Guide](Developer%20Guide/Developer%20Guide/) - Development environment and contribution guide
|
||||
- [Script API](Script%20API/) - Complete scripting API reference
|
||||
|
||||
## 🔗 Additional Resources
|
||||
|
||||
For the full application README, please visit our [GitHub repository](https://github.com/triliumnext/trilium).
|
||||
734
docs/SCRIPTING.md
vendored
734
docs/SCRIPTING.md
vendored
@@ -1,734 +0,0 @@
|
||||
# Trilium Scripting System
|
||||
|
||||
> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md) | [Script API Documentation](Script%20API/)
|
||||
|
||||
## Overview
|
||||
|
||||
Trilium features a **powerful scripting system** that allows users to extend and customize the application without modifying source code. Scripts are written in JavaScript and can execute both in the **frontend (browser)** and **backend (Node.js)** contexts.
|
||||
|
||||
## Script Types
|
||||
|
||||
### Frontend Scripts
|
||||
|
||||
**Location:** Attached to notes with `#run=frontendStartup` attribute
|
||||
|
||||
**Execution Context:** Browser environment
|
||||
|
||||
**Access:**
|
||||
- Trilium Frontend API
|
||||
- Browser APIs (DOM, localStorage, etc.)
|
||||
- Froca (frontend cache)
|
||||
- UI widgets
|
||||
- No direct file system access
|
||||
|
||||
**Lifecycle:**
|
||||
- `frontendStartup` - Run once when Trilium loads
|
||||
- `frontendReload` - Run on every note context change
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Attach to note with #run=frontendStartup
|
||||
const api = window.api
|
||||
|
||||
// Add custom button to toolbar
|
||||
api.addButtonToToolbar({
|
||||
title: 'My Button',
|
||||
icon: 'star',
|
||||
action: () => {
|
||||
api.showMessage('Hello from frontend!')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Backend Scripts
|
||||
|
||||
**Location:** Attached to notes with `#run=backendStartup` attribute
|
||||
|
||||
**Execution Context:** Node.js server environment
|
||||
|
||||
**Access:**
|
||||
- Trilium Backend API
|
||||
- Node.js APIs (fs, http, etc.)
|
||||
- Becca (backend cache)
|
||||
- Database (SQL)
|
||||
- External libraries (via require)
|
||||
|
||||
**Lifecycle:**
|
||||
- `backendStartup` - Run once when server starts
|
||||
- Event handlers (custom events)
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Attach to note with #run=backendStartup
|
||||
const api = require('@triliumnext/api')
|
||||
|
||||
// Listen for note creation
|
||||
api.dayjs // Example: access dayjs library
|
||||
|
||||
api.onNoteCreated((note) => {
|
||||
if (note.title.includes('TODO')) {
|
||||
note.setLabel('priority', 'high')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Render Scripts
|
||||
|
||||
**Location:** Attached to notes with `#customWidget` or similar attributes
|
||||
|
||||
**Purpose:** Custom note rendering/widgets
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Custom widget for a note
|
||||
class MyWidget extends api.NoteContextAwareWidget {
|
||||
doRender() {
|
||||
this.$widget = $('<div>')
|
||||
.text('Custom widget content')
|
||||
return this.$widget
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MyWidget
|
||||
```
|
||||
|
||||
## Script API
|
||||
|
||||
### Frontend API
|
||||
|
||||
**Location:** `apps/client/src/services/frontend_script_api.ts`
|
||||
|
||||
**Global Access:** `window.api`
|
||||
|
||||
**Key Methods:**
|
||||
|
||||
```typescript
|
||||
// Note Operations
|
||||
api.getNote(noteId) // Get note object
|
||||
api.getBranch(branchId) // Get branch object
|
||||
api.getActiveNote() // Currently displayed note
|
||||
api.openNote(noteId, activateNote) // Open note in UI
|
||||
|
||||
// UI Operations
|
||||
api.showMessage(message) // Show toast notification
|
||||
api.showDialog() // Show modal dialog
|
||||
api.confirm(message) // Show confirmation dialog
|
||||
api.prompt(message, defaultValue) // Show input prompt
|
||||
|
||||
// Tree Operations
|
||||
api.getTree() // Get note tree structure
|
||||
api.expandTree(noteId) // Expand tree branch
|
||||
api.collapseTree(noteId) // Collapse tree branch
|
||||
|
||||
// Search
|
||||
api.searchForNotes(searchQuery) // Search notes
|
||||
api.searchForNote(searchQuery) // Get single note
|
||||
|
||||
// Navigation
|
||||
api.openTabWithNote(noteId) // Open note in new tab
|
||||
api.closeActiveTab() // Close current tab
|
||||
api.activateNote(noteId) // Switch to note
|
||||
|
||||
// Attributes
|
||||
api.getAttribute(noteId, type, name) // Get attribute
|
||||
api.getAttributes(noteId, type, name) // Get all matching attributes
|
||||
|
||||
// Custom Widgets
|
||||
api.addButtonToToolbar(def) // Add toolbar button
|
||||
api.addCustomWidget(def) // Add custom widget
|
||||
|
||||
// Events
|
||||
api.runOnNoteOpened(callback) // Note opened event
|
||||
api.runOnNoteContentChange(callback) // Content changed event
|
||||
|
||||
// Utilities
|
||||
api.dayjs // Date/time library
|
||||
api.formatDate(date) // Format date
|
||||
api.log(message) // Console log
|
||||
```
|
||||
|
||||
### Backend API
|
||||
|
||||
**Location:** `apps/server/src/services/backend_script_api.ts`
|
||||
|
||||
**Access:** `require('@triliumnext/api')` or global `api`
|
||||
|
||||
**Key Methods:**
|
||||
|
||||
```typescript
|
||||
// Note Operations
|
||||
api.getNote(noteId) // Get note from Becca
|
||||
api.getNoteWithContent(noteId) // Get note with content
|
||||
api.createNote(parentNoteId, title) // Create new note
|
||||
api.deleteNote(noteId) // Delete note
|
||||
|
||||
// Branch Operations
|
||||
api.getBranch(branchId) // Get branch
|
||||
api.createBranch(noteId, parentNoteId) // Create branch (clone)
|
||||
|
||||
// Attribute Operations
|
||||
api.getAttribute(noteId, type, name) // Get attribute
|
||||
api.createAttribute(noteId, type, name, value) // Create attribute
|
||||
|
||||
// Database Access
|
||||
api.sql.getRow(query, params) // Execute SQL query (single row)
|
||||
api.sql.getRows(query, params) // Execute SQL query (multiple rows)
|
||||
api.sql.execute(query, params) // Execute SQL statement
|
||||
|
||||
// Events
|
||||
api.onNoteCreated(callback) // Note created event
|
||||
api.onNoteUpdated(callback) // Note updated event
|
||||
api.onNoteDeleted(callback) // Note deleted event
|
||||
api.onAttributeCreated(callback) // Attribute created event
|
||||
|
||||
// Search
|
||||
api.searchForNotes(searchQuery) // Search notes
|
||||
|
||||
// Date/Time
|
||||
api.dayjs // Date/time library
|
||||
api.now() // Current date/time
|
||||
|
||||
// Logging
|
||||
api.log(message) // Log message
|
||||
api.error(message) // Log error
|
||||
|
||||
// External Communication
|
||||
api.axios // HTTP client library
|
||||
|
||||
// Utilities
|
||||
api.backup.backupNow() // Trigger backup
|
||||
api.export.exportSubtree(noteId) // Export notes
|
||||
```
|
||||
|
||||
## Script Attributes
|
||||
|
||||
### Execute Attributes
|
||||
|
||||
- `#run=frontendStartup` - Execute on frontend startup
|
||||
- `#run=backendStartup` - Execute on backend startup
|
||||
- `#run=hourly` - Execute every hour
|
||||
- `#run=daily` - Execute daily
|
||||
|
||||
### Widget Attributes
|
||||
|
||||
- `#customWidget` - Custom note widget
|
||||
- `#widget` - Standard widget integration
|
||||
|
||||
### Other Attributes
|
||||
|
||||
- `#disableVersioning` - Disable automatic versioning for this note
|
||||
- `#hideChildrenOverview` - Hide children in overview
|
||||
- `#iconClass` - Custom icon for note
|
||||
|
||||
## Entity Classes
|
||||
|
||||
### Frontend Entities
|
||||
|
||||
**FNote** (`apps/client/src/entities/fnote.ts`)
|
||||
|
||||
```typescript
|
||||
class FNote {
|
||||
noteId: string
|
||||
title: string
|
||||
type: string
|
||||
mime: string
|
||||
|
||||
// Relationships
|
||||
getParentNotes(): FNote[]
|
||||
getChildNotes(): FNote[]
|
||||
getBranches(): FBranch[]
|
||||
|
||||
// Attributes
|
||||
getAttribute(type, name): FAttribute
|
||||
getAttributes(type?, name?): FAttribute[]
|
||||
hasLabel(name): boolean
|
||||
getLabelValue(name): string
|
||||
|
||||
// Content
|
||||
getContent(): Promise<string>
|
||||
|
||||
// Navigation
|
||||
open(): void
|
||||
}
|
||||
```
|
||||
|
||||
**FBranch**
|
||||
|
||||
```typescript
|
||||
class FBranch {
|
||||
branchId: string
|
||||
noteId: string
|
||||
parentNoteId: string
|
||||
prefix: string
|
||||
notePosition: number
|
||||
|
||||
getNote(): FNote
|
||||
getParentNote(): FNote
|
||||
}
|
||||
```
|
||||
|
||||
**FAttribute**
|
||||
|
||||
```typescript
|
||||
class FAttribute {
|
||||
attributeId: string
|
||||
noteId: string
|
||||
type: 'label' | 'relation'
|
||||
name: string
|
||||
value: string
|
||||
|
||||
getNote(): FNote
|
||||
getTargetNote(): FNote // For relations
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Entities
|
||||
|
||||
**BNote** (`apps/server/src/becca/entities/bnote.ts`)
|
||||
|
||||
```typescript
|
||||
class BNote {
|
||||
noteId: string
|
||||
title: string
|
||||
type: string
|
||||
mime: string
|
||||
isProtected: boolean
|
||||
|
||||
// Content
|
||||
getContent(): string | Buffer
|
||||
setContent(content: string | Buffer): void
|
||||
|
||||
// Relationships
|
||||
getParentNotes(): BNote[]
|
||||
getChildNotes(): BNote[]
|
||||
getBranches(): BBranch[]
|
||||
|
||||
// Attributes
|
||||
getAttribute(type, name): BAttribute
|
||||
getAttributes(type?, name?): BAttribute[]
|
||||
setLabel(name, value): BAttribute
|
||||
setRelation(name, targetNoteId): BAttribute
|
||||
hasLabel(name): boolean
|
||||
getLabelValue(name): string
|
||||
|
||||
// Operations
|
||||
save(): void
|
||||
markAsDeleted(): void
|
||||
}
|
||||
```
|
||||
|
||||
**BBranch**
|
||||
|
||||
```typescript
|
||||
class BBranch {
|
||||
branchId: string
|
||||
noteId: string
|
||||
parentNoteId: string
|
||||
prefix: string
|
||||
notePosition: number
|
||||
|
||||
getNote(): BNote
|
||||
getParentNote(): BNote
|
||||
save(): void
|
||||
}
|
||||
```
|
||||
|
||||
**BAttribute**
|
||||
|
||||
```typescript
|
||||
class BAttribute {
|
||||
attributeId: string
|
||||
noteId: string
|
||||
type: 'label' | 'relation'
|
||||
name: string
|
||||
value: string
|
||||
|
||||
getNote(): BNote
|
||||
getTargetNote(): BNote // For relations
|
||||
save(): void
|
||||
}
|
||||
```
|
||||
|
||||
## Script Examples
|
||||
|
||||
### Frontend Examples
|
||||
|
||||
**1. Custom Toolbar Button**
|
||||
|
||||
```javascript
|
||||
// #run=frontendStartup
|
||||
api.addButtonToToolbar({
|
||||
title: 'Export to PDF',
|
||||
icon: 'file-export',
|
||||
action: async () => {
|
||||
const note = api.getActiveNote()
|
||||
if (note) {
|
||||
await api.runOnBackend('exportToPdf', [note.noteId])
|
||||
api.showMessage('Export started')
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**2. Auto-Save Reminder**
|
||||
|
||||
```javascript
|
||||
// #run=frontendStartup
|
||||
let saveTimer
|
||||
api.runOnNoteContentChange(() => {
|
||||
clearTimeout(saveTimer)
|
||||
saveTimer = setTimeout(() => {
|
||||
api.showMessage('Remember to save your work!')
|
||||
}, 300000) // 5 minutes
|
||||
})
|
||||
```
|
||||
|
||||
**3. Note Statistics Widget**
|
||||
|
||||
```javascript
|
||||
// #customWidget
|
||||
class StatsWidget extends api.NoteContextAwareWidget {
|
||||
doRender() {
|
||||
this.$widget = $('<div class="stats-widget">')
|
||||
return this.$widget
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
const content = await note.getContent()
|
||||
const words = content.split(/\s+/).length
|
||||
const chars = content.length
|
||||
|
||||
this.$widget.html(`
|
||||
<div>Words: ${words}</div>
|
||||
<div>Characters: ${chars}</div>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StatsWidget
|
||||
```
|
||||
|
||||
### Backend Examples
|
||||
|
||||
**1. Auto-Tagging on Note Creation**
|
||||
|
||||
```javascript
|
||||
// #run=backendStartup
|
||||
api.onNoteCreated((note) => {
|
||||
// Auto-tag TODO notes
|
||||
if (note.title.includes('TODO')) {
|
||||
note.setLabel('type', 'todo')
|
||||
note.setLabel('priority', 'normal')
|
||||
}
|
||||
|
||||
// Auto-tag meeting notes by date
|
||||
if (note.title.match(/Meeting \d{4}-\d{2}-\d{2}/)) {
|
||||
note.setLabel('type', 'meeting')
|
||||
const dateMatch = note.title.match(/(\d{4}-\d{2}-\d{2})/)
|
||||
if (dateMatch) {
|
||||
note.setLabel('date', dateMatch[1])
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**2. Daily Backup Reminder**
|
||||
|
||||
```javascript
|
||||
// #run=daily
|
||||
const todayNote = api.getTodayNote()
|
||||
todayNote.setLabel('backupDone', 'false')
|
||||
|
||||
// Create reminder note
|
||||
api.createNote(todayNote.noteId, '🔔 Backup Reminder', {
|
||||
content: 'Remember to verify today\'s backup!',
|
||||
type: 'text'
|
||||
})
|
||||
```
|
||||
|
||||
**3. External API Integration**
|
||||
|
||||
```javascript
|
||||
// #run=backendStartup
|
||||
api.onNoteCreated(async (note) => {
|
||||
// Sync new notes to external service
|
||||
if (note.hasLabel('sync-external')) {
|
||||
try {
|
||||
await api.axios.post('https://external-api.com/sync', {
|
||||
noteId: note.noteId,
|
||||
title: note.title,
|
||||
content: note.getContent()
|
||||
})
|
||||
note.setLabel('lastSync', api.dayjs().format())
|
||||
} catch (error) {
|
||||
api.log('Sync failed: ' + error.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**4. Database Cleanup**
|
||||
|
||||
```javascript
|
||||
// #run=weekly
|
||||
// Clean up old revisions
|
||||
const cutoffDate = api.dayjs().subtract(90, 'days').format()
|
||||
|
||||
const oldRevisions = api.sql.getRows(`
|
||||
SELECT revisionId FROM revisions
|
||||
WHERE utcDateCreated < ?
|
||||
`, [cutoffDate])
|
||||
|
||||
api.log(`Deleting ${oldRevisions.length} old revisions`)
|
||||
|
||||
for (const row of oldRevisions) {
|
||||
api.sql.execute('DELETE FROM revisions WHERE revisionId = ?', [row.revisionId])
|
||||
}
|
||||
```
|
||||
|
||||
## Script Storage
|
||||
|
||||
**Storage Location:** Scripts are stored as regular notes
|
||||
|
||||
**Identifying Scripts:**
|
||||
- Have `#run` attribute or `#customWidget` attribute
|
||||
- Type is typically `code` with MIME `application/javascript`
|
||||
|
||||
**Script Note Structure:**
|
||||
```
|
||||
📁 Scripts (folder note)
|
||||
├── 📜 Frontend Scripts
|
||||
│ ├── Custom Toolbar Button (#run=frontendStartup)
|
||||
│ └── Statistics Widget (#customWidget)
|
||||
└── 📜 Backend Scripts
|
||||
├── Auto-Tagger (#run=backendStartup)
|
||||
└── Daily Backup (#run=daily)
|
||||
```
|
||||
|
||||
## Script Execution
|
||||
|
||||
### Frontend Execution
|
||||
|
||||
**Timing:**
|
||||
1. Trilium frontend loads
|
||||
2. Froca cache initializes
|
||||
3. Script notes with `#run=frontendStartup` are found
|
||||
4. Scripts execute in dependency order
|
||||
|
||||
**Isolation:**
|
||||
- Each script runs in separate context
|
||||
- Shared `window.api` object
|
||||
- Can access global window object
|
||||
|
||||
### Backend Execution
|
||||
|
||||
**Timing:**
|
||||
1. Server starts
|
||||
2. Becca cache loads
|
||||
3. Script notes with `#run=backendStartup` are found
|
||||
4. Scripts execute in dependency order
|
||||
|
||||
**Isolation:**
|
||||
- Each script is a separate module
|
||||
- Can require Node.js modules
|
||||
- Shared `api` global
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Frontend:**
|
||||
```javascript
|
||||
try {
|
||||
// Script code
|
||||
} catch (error) {
|
||||
api.showError('Script error: ' + error.message)
|
||||
console.error(error)
|
||||
}
|
||||
```
|
||||
|
||||
**Backend:**
|
||||
```javascript
|
||||
try {
|
||||
// Script code
|
||||
} catch (error) {
|
||||
api.log('Script error: ' + error.message)
|
||||
console.error(error)
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Frontend Scripts
|
||||
|
||||
**Risks:**
|
||||
- Can access all notes via Froca
|
||||
- Can manipulate DOM
|
||||
- Can make API calls
|
||||
- Limited by browser security model
|
||||
|
||||
**Mitigations:**
|
||||
- User must trust scripts they add
|
||||
- Scripts run with user privileges
|
||||
- No access to file system
|
||||
|
||||
### Backend Scripts
|
||||
|
||||
**Risks:**
|
||||
- Full Node.js access
|
||||
- Can execute system commands
|
||||
- Can access file system
|
||||
- Can make network requests
|
||||
|
||||
**Mitigations:**
|
||||
- Scripts are user-created (trusted)
|
||||
- Single-user model (no privilege escalation)
|
||||
- Review scripts before adding `#run` attribute
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Review script code** before adding execution attributes
|
||||
2. **Use specific attributes** rather than wildcard searches
|
||||
3. **Avoid eval()** and dynamic code execution
|
||||
4. **Validate inputs** in scripts
|
||||
5. **Handle errors** gracefully
|
||||
6. **Log important actions** for audit trail
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
**1. Cache Results:**
|
||||
```javascript
|
||||
// Bad: Re-query on every call
|
||||
function getConfig() {
|
||||
return api.getNote('config').getContent()
|
||||
}
|
||||
|
||||
// Good: Cache the result
|
||||
let cachedConfig
|
||||
function getConfig() {
|
||||
if (!cachedConfig) {
|
||||
cachedConfig = api.getNote('config').getContent()
|
||||
}
|
||||
return cachedConfig
|
||||
}
|
||||
```
|
||||
|
||||
**2. Use Efficient Queries:**
|
||||
```javascript
|
||||
// Bad: Load all notes and filter
|
||||
const todos = api.searchForNotes('#type=todo')
|
||||
|
||||
// Good: Use specific search
|
||||
const todos = api.searchForNotes('#type=todo #status=pending')
|
||||
```
|
||||
|
||||
**3. Batch Operations:**
|
||||
```javascript
|
||||
// Bad: Save after each change
|
||||
notes.forEach(note => {
|
||||
note.title = 'Updated'
|
||||
note.save()
|
||||
})
|
||||
|
||||
// Good: Batch changes
|
||||
notes.forEach(note => {
|
||||
note.title = 'Updated'
|
||||
})
|
||||
// Save happens in batch
|
||||
```
|
||||
|
||||
**4. Debounce Event Handlers:**
|
||||
```javascript
|
||||
let timeout
|
||||
api.runOnNoteContentChange(() => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
// Process change
|
||||
}, 500)
|
||||
})
|
||||
```
|
||||
|
||||
## Debugging Scripts
|
||||
|
||||
### Frontend Debugging
|
||||
|
||||
**Browser DevTools:**
|
||||
```javascript
|
||||
console.log('Debug info:', data)
|
||||
debugger // Breakpoint
|
||||
```
|
||||
|
||||
**Trilium Log:**
|
||||
```javascript
|
||||
api.log('Script executed')
|
||||
```
|
||||
|
||||
### Backend Debugging
|
||||
|
||||
**Console Output:**
|
||||
```javascript
|
||||
console.log('Backend debug:', data)
|
||||
api.log('Script log message')
|
||||
```
|
||||
|
||||
**Inspect Becca:**
|
||||
```javascript
|
||||
api.log('Note count:', Object.keys(api.becca.notes).length)
|
||||
```
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
### Custom Note Types
|
||||
|
||||
Scripts can implement custom note type handlers:
|
||||
|
||||
```javascript
|
||||
// Register custom type
|
||||
api.registerNoteType({
|
||||
type: 'mytype',
|
||||
mime: 'application/x-mytype',
|
||||
renderNote: (note) => {
|
||||
// Custom rendering
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### External Libraries
|
||||
|
||||
**Frontend:**
|
||||
```javascript
|
||||
// Load external library
|
||||
const myLib = await import('https://cdn.example.com/lib.js')
|
||||
```
|
||||
|
||||
**Backend:**
|
||||
```javascript
|
||||
// Use Node.js require
|
||||
const fs = require('fs')
|
||||
const axios = require('axios')
|
||||
```
|
||||
|
||||
### State Persistence
|
||||
|
||||
**Frontend:**
|
||||
```javascript
|
||||
// Use localStorage
|
||||
localStorage.setItem('myScript:data', JSON.stringify(data))
|
||||
const data = JSON.parse(localStorage.getItem('myScript:data'))
|
||||
```
|
||||
|
||||
**Backend:**
|
||||
```javascript
|
||||
// Store in special note
|
||||
const stateNote = api.getNote('script-state-note')
|
||||
stateNote.setContent(JSON.stringify(data))
|
||||
|
||||
const data = JSON.parse(stateNote.getContent())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [Script API Documentation](Script%20API/) - Complete API reference
|
||||
- [Advanced Showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases) - Example scripts
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Overall architecture
|
||||
834
docs/SECURITY_ARCHITECTURE.md
vendored
834
docs/SECURITY_ARCHITECTURE.md
vendored
@@ -1,834 +0,0 @@
|
||||
# Trilium Security Architecture
|
||||
|
||||
> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md) | [SECURITY.md](../SECURITY.md)
|
||||
|
||||
## Overview
|
||||
|
||||
Trilium implements a **defense-in-depth security model** with multiple layers of protection for user data. The security architecture covers authentication, authorization, encryption, input sanitization, and secure communication.
|
||||
|
||||
## Security Principles
|
||||
|
||||
1. **Data Privacy**: User data is protected at rest and in transit
|
||||
2. **Encryption**: Per-note encryption for sensitive content
|
||||
3. **Authentication**: Multiple authentication methods supported
|
||||
4. **Authorization**: Single-user model with granular protected sessions
|
||||
5. **Input Validation**: All user input sanitized
|
||||
6. **Secure Defaults**: Security features enabled by default
|
||||
7. **Transparency**: Open source allows security audits
|
||||
|
||||
## Threat Model
|
||||
|
||||
### Threats Considered
|
||||
|
||||
1. **Unauthorized Access**
|
||||
- Physical access to device
|
||||
- Network eavesdropping
|
||||
- Stolen credentials
|
||||
- Session hijacking
|
||||
|
||||
2. **Data Exfiltration**
|
||||
- Malicious scripts
|
||||
- XSS attacks
|
||||
- SQL injection
|
||||
- CSRF attacks
|
||||
|
||||
3. **Data Corruption**
|
||||
- Malicious modifications
|
||||
- Database tampering
|
||||
- Sync conflicts
|
||||
|
||||
4. **Privacy Leaks**
|
||||
- Unencrypted backups
|
||||
- Search indexing
|
||||
- Temporary files
|
||||
- Memory dumps
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Nation-state attackers
|
||||
- Zero-day vulnerabilities in dependencies
|
||||
- Hardware vulnerabilities (Spectre, Meltdown)
|
||||
- Physical access with unlimited time
|
||||
- Quantum computing attacks
|
||||
|
||||
## Authentication
|
||||
|
||||
### Password Authentication
|
||||
|
||||
**Implementation:** `apps/server/src/services/password.ts`
|
||||
|
||||
**Password Storage:**
|
||||
```typescript
|
||||
// Password is never stored directly
|
||||
const salt = crypto.randomBytes(32)
|
||||
const derivedKey = crypto.pbkdf2Sync(password, salt, 10000, 32, 'sha256')
|
||||
const verificationHash = crypto.createHash('sha256')
|
||||
.update(derivedKey)
|
||||
.digest('hex')
|
||||
|
||||
// Store only salt and verification hash
|
||||
sql.insert('user_data', {
|
||||
salt: salt.toString('hex'),
|
||||
derivedKey: derivedKey.toString('hex') // Used for encryption
|
||||
})
|
||||
|
||||
sql.insert('options', {
|
||||
name: 'passwordVerificationHash',
|
||||
value: verificationHash
|
||||
})
|
||||
```
|
||||
|
||||
**Password Requirements:**
|
||||
- Minimum length: 4 characters (configurable)
|
||||
- No maximum length
|
||||
- All characters allowed
|
||||
- Can be changed by user
|
||||
|
||||
**Login Process:**
|
||||
```typescript
|
||||
// 1. User submits password
|
||||
POST /api/login/password
|
||||
Body: { password: "user-password" }
|
||||
|
||||
// 2. Server derives key
|
||||
const derivedKey = crypto.pbkdf2Sync(password, salt, 10000, 32, 'sha256')
|
||||
|
||||
// 3. Verify against stored hash
|
||||
const verificationHash = crypto.createHash('sha256')
|
||||
.update(derivedKey)
|
||||
.digest('hex')
|
||||
|
||||
if (verificationHash === storedHash) {
|
||||
// 4. Create session
|
||||
req.session.loggedIn = true
|
||||
req.session.regenerate()
|
||||
}
|
||||
```
|
||||
|
||||
### TOTP (Two-Factor Authentication)
|
||||
|
||||
**Implementation:** `apps/server/src/routes/api/login.ts`
|
||||
|
||||
**Setup Process:**
|
||||
```typescript
|
||||
// 1. Generate secret
|
||||
const secret = speakeasy.generateSecret({
|
||||
name: `Trilium (${username})`,
|
||||
length: 32
|
||||
})
|
||||
|
||||
// 2. Store encrypted secret
|
||||
const encryptedSecret = encrypt(secret.base32, dataKey)
|
||||
sql.insert('options', {
|
||||
name: 'totpSecret',
|
||||
value: encryptedSecret
|
||||
})
|
||||
|
||||
// 3. Generate QR code
|
||||
const qrCodeUrl = secret.otpauth_url
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
```typescript
|
||||
// User submits TOTP token
|
||||
POST /api/login/totp
|
||||
Body: { token: "123456" }
|
||||
|
||||
// Verify token
|
||||
const secret = decrypt(encryptedSecret, dataKey)
|
||||
const verified = speakeasy.totp.verify({
|
||||
secret: secret,
|
||||
encoding: 'base32',
|
||||
token: token,
|
||||
window: 1 // Allow 1 time step tolerance
|
||||
})
|
||||
```
|
||||
|
||||
### OpenID Connect
|
||||
|
||||
**Implementation:** `apps/server/src/routes/api/login.ts`
|
||||
|
||||
**Supported Providers:**
|
||||
- Any OpenID Connect compatible provider
|
||||
- Google, GitHub, Auth0, etc.
|
||||
|
||||
**Flow:**
|
||||
```typescript
|
||||
// 1. Redirect to provider
|
||||
GET /api/login/openid
|
||||
|
||||
// 2. Provider redirects back with code
|
||||
GET /api/login/openid/callback?code=...
|
||||
|
||||
// 3. Exchange code for tokens
|
||||
const tokens = await openidClient.callback(redirectUri, req.query)
|
||||
|
||||
// 4. Verify ID token
|
||||
const claims = tokens.claims()
|
||||
|
||||
// 5. Create session
|
||||
req.session.loggedIn = true
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
**Session Storage:** SQLite database (sessions table)
|
||||
|
||||
**Session Configuration:**
|
||||
```typescript
|
||||
app.use(session({
|
||||
secret: sessionSecret,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
rolling: true,
|
||||
cookie: {
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
|
||||
httpOnly: true,
|
||||
secure: isHttps,
|
||||
sameSite: 'lax'
|
||||
},
|
||||
store: new SqliteStore({
|
||||
db: db,
|
||||
table: 'sessions'
|
||||
})
|
||||
}))
|
||||
```
|
||||
|
||||
**Session Invalidation:**
|
||||
- Automatic timeout after inactivity
|
||||
- Manual logout clears session
|
||||
- Server restart invalidates all sessions (optional)
|
||||
|
||||
## Authorization
|
||||
|
||||
### Single-User Model
|
||||
|
||||
**Desktop:**
|
||||
- Single user (owner of device)
|
||||
- No multi-user support
|
||||
- Full access to all notes
|
||||
|
||||
**Server:**
|
||||
- Single user per installation
|
||||
- Authentication required for all operations
|
||||
- No user roles or permissions
|
||||
|
||||
### Protected Sessions
|
||||
|
||||
**Purpose:** Temporary access to encrypted (protected) notes
|
||||
|
||||
**Implementation:** `apps/server/src/services/protected_session.ts`
|
||||
|
||||
**Workflow:**
|
||||
```typescript
|
||||
// 1. User enters password for protected notes
|
||||
POST /api/protected-session/enter
|
||||
Body: { password: "protected-password" }
|
||||
|
||||
// 2. Derive encryption key
|
||||
const protectedDataKey = deriveKey(password)
|
||||
|
||||
// 3. Verify password (decrypt known encrypted value)
|
||||
const decrypted = decrypt(testValue, protectedDataKey)
|
||||
if (decrypted === expectedValue) {
|
||||
// 4. Store in memory (not in session)
|
||||
protectedSessionHolder.setProtectedDataKey(protectedDataKey)
|
||||
|
||||
// 5. Set timeout
|
||||
setTimeout(() => {
|
||||
protectedSessionHolder.clearProtectedDataKey()
|
||||
}, timeout)
|
||||
}
|
||||
```
|
||||
|
||||
**Protected Session Timeout:**
|
||||
- Default: 10 minutes (configurable)
|
||||
- Extends on activity
|
||||
- Cleared on browser close
|
||||
- Separate from main session
|
||||
|
||||
### API Authorization
|
||||
|
||||
**Internal API:**
|
||||
- Requires authenticated session
|
||||
- CSRF token validation
|
||||
- Same-origin policy
|
||||
|
||||
**ETAPI (External API):**
|
||||
- Token-based authentication
|
||||
- No session required
|
||||
- Rate limiting
|
||||
|
||||
## Encryption
|
||||
|
||||
### Note Encryption
|
||||
|
||||
**Encryption Algorithm:** AES-256-CBC
|
||||
|
||||
**Key Hierarchy:**
|
||||
```
|
||||
User Password
|
||||
↓ (PBKDF2)
|
||||
Data Key (for protected notes)
|
||||
↓ (AES-256)
|
||||
Protected Note Content
|
||||
```
|
||||
|
||||
**Encryption Process:**
|
||||
```typescript
|
||||
// 1. Generate IV (initialization vector)
|
||||
const iv = crypto.randomBytes(16)
|
||||
|
||||
// 2. Encrypt content
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', dataKey, iv)
|
||||
let encrypted = cipher.update(content, 'utf8', 'base64')
|
||||
encrypted += cipher.final('base64')
|
||||
|
||||
// 3. Prepend IV to encrypted content
|
||||
const encryptedBlob = iv.toString('base64') + ':' + encrypted
|
||||
|
||||
// 4. Store in database
|
||||
sql.insert('blobs', {
|
||||
blobId: blobId,
|
||||
content: encryptedBlob
|
||||
})
|
||||
```
|
||||
|
||||
**Decryption Process:**
|
||||
```typescript
|
||||
// 1. Split IV and encrypted content
|
||||
const [ivBase64, encryptedData] = encryptedBlob.split(':')
|
||||
const iv = Buffer.from(ivBase64, 'base64')
|
||||
|
||||
// 2. Decrypt
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', dataKey, iv)
|
||||
let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
|
||||
decrypted += decipher.final('utf8')
|
||||
|
||||
return decrypted
|
||||
```
|
||||
|
||||
**Protected Note Metadata:**
|
||||
- Title is NOT encrypted (for tree display)
|
||||
- Type and MIME are NOT encrypted
|
||||
- Content IS encrypted
|
||||
- Attributes CAN be encrypted (optional)
|
||||
|
||||
### Data Key Management
|
||||
|
||||
**Master Data Key:**
|
||||
```typescript
|
||||
// Generated once during setup
|
||||
const dataKey = crypto.randomBytes(32) // 256 bits
|
||||
|
||||
// Encrypted with derived key from user password
|
||||
const derivedKey = crypto.pbkdf2Sync(password, salt, 10000, 32, 'sha256')
|
||||
const encryptedDataKey = encrypt(dataKey, derivedKey)
|
||||
|
||||
// Stored in database
|
||||
sql.insert('options', {
|
||||
name: 'encryptedDataKey',
|
||||
value: encryptedDataKey.toString('hex')
|
||||
})
|
||||
```
|
||||
|
||||
**Key Rotation:**
|
||||
- Not currently supported
|
||||
- Requires re-encrypting all protected notes
|
||||
- Planned for future version
|
||||
|
||||
### Transport Encryption
|
||||
|
||||
**HTTPS:**
|
||||
- Required for server installations (recommended)
|
||||
- TLS 1.2+ only
|
||||
- Strong cipher suites preferred
|
||||
- Certificate validation enabled
|
||||
|
||||
**Desktop:**
|
||||
- Local communication (no network)
|
||||
- No HTTPS required
|
||||
|
||||
### Backup Encryption
|
||||
|
||||
**Database Backups:**
|
||||
- Protected notes remain encrypted in backup
|
||||
- Backup file should be protected separately
|
||||
- Consider encrypting backup storage location
|
||||
|
||||
## Input Sanitization
|
||||
|
||||
### XSS Prevention
|
||||
|
||||
**HTML Sanitization:**
|
||||
|
||||
Location: `apps/client/src/services/dompurify.ts`
|
||||
|
||||
```typescript
|
||||
import DOMPurify from 'dompurify'
|
||||
|
||||
// Configure DOMPurify
|
||||
DOMPurify.setConfig({
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'div', ...],
|
||||
ALLOWED_ATTR: ['href', 'title', 'class', 'id', ...],
|
||||
ALLOW_DATA_ATTR: false
|
||||
})
|
||||
|
||||
// Sanitize HTML before rendering
|
||||
const cleanHtml = DOMPurify.sanitize(userHtml)
|
||||
```
|
||||
|
||||
**CKEditor Configuration:**
|
||||
```typescript
|
||||
// apps/client/src/widgets/type_widgets/text_type_widget.ts
|
||||
ClassicEditor.create(element, {
|
||||
// Restrict allowed content
|
||||
htmlSupport: {
|
||||
allow: [
|
||||
{ name: /./, attributes: true, classes: true, styles: true }
|
||||
],
|
||||
disallow: [
|
||||
{ name: 'script' },
|
||||
{ name: 'iframe', attributes: /^(?!src$).*/ }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Content Security Policy:**
|
||||
```typescript
|
||||
// apps/server/src/main.ts
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Content-Security-Policy',
|
||||
"default-src 'self'; " +
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
|
||||
"style-src 'self' 'unsafe-inline'; " +
|
||||
"img-src 'self' data: blob:;"
|
||||
)
|
||||
next()
|
||||
})
|
||||
```
|
||||
|
||||
### SQL Injection Prevention
|
||||
|
||||
**Parameterized Queries:**
|
||||
```typescript
|
||||
// GOOD - Safe from SQL injection
|
||||
const notes = sql.getRows(
|
||||
'SELECT * FROM notes WHERE title = ?',
|
||||
[userInput]
|
||||
)
|
||||
|
||||
// BAD - Vulnerable to SQL injection
|
||||
const notes = sql.getRows(
|
||||
`SELECT * FROM notes WHERE title = '${userInput}'`
|
||||
)
|
||||
```
|
||||
|
||||
**ORM Usage:**
|
||||
```typescript
|
||||
// Entity-based access prevents SQL injection
|
||||
const note = becca.getNote(noteId)
|
||||
note.title = userInput // Sanitized by entity
|
||||
note.save() // Parameterized query
|
||||
```
|
||||
|
||||
### CSRF Prevention
|
||||
|
||||
**CSRF Token Validation:**
|
||||
|
||||
Location: `apps/server/src/routes/middleware/csrf.ts`
|
||||
|
||||
```typescript
|
||||
// Generate CSRF token
|
||||
const csrfToken = crypto.randomBytes(32).toString('hex')
|
||||
req.session.csrfToken = csrfToken
|
||||
|
||||
// Validate on state-changing requests
|
||||
app.use((req, res, next) => {
|
||||
if (['POST', 'PUT', 'DELETE'].includes(req.method)) {
|
||||
const token = req.headers['x-csrf-token']
|
||||
if (token !== req.session.csrfToken) {
|
||||
return res.status(403).json({ error: 'CSRF token mismatch' })
|
||||
}
|
||||
}
|
||||
next()
|
||||
})
|
||||
```
|
||||
|
||||
**Client-Side:**
|
||||
```typescript
|
||||
// apps/client/src/services/server.ts
|
||||
const csrfToken = getCsrfToken()
|
||||
|
||||
fetch('/api/notes', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
```
|
||||
|
||||
### File Upload Validation
|
||||
|
||||
**Validation:**
|
||||
```typescript
|
||||
// apps/server/src/routes/api/attachments.ts
|
||||
const allowedMimeTypes = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'application/pdf',
|
||||
// ...
|
||||
]
|
||||
|
||||
if (!allowedMimeTypes.includes(file.mimetype)) {
|
||||
throw new Error('File type not allowed')
|
||||
}
|
||||
|
||||
// Validate file size
|
||||
const maxSize = 100 * 1024 * 1024 // 100 MB
|
||||
if (file.size > maxSize) {
|
||||
throw new Error('File too large')
|
||||
}
|
||||
|
||||
// Sanitize filename
|
||||
const sanitizedFilename = path.basename(file.originalname)
|
||||
.replace(/[^a-z0-9.-]/gi, '_')
|
||||
```
|
||||
|
||||
## Network Security
|
||||
|
||||
### HTTPS Configuration
|
||||
|
||||
**Server Setup:**
|
||||
```typescript
|
||||
// apps/server/src/main.ts
|
||||
const httpsOptions = {
|
||||
key: fs.readFileSync('server.key'),
|
||||
cert: fs.readFileSync('server.cert')
|
||||
}
|
||||
|
||||
https.createServer(httpsOptions, app).listen(443)
|
||||
```
|
||||
|
||||
**Certificate Validation:**
|
||||
- Require valid certificates in production
|
||||
- Self-signed certificates allowed for development
|
||||
- Certificate pinning not implemented
|
||||
|
||||
### Secure Headers
|
||||
|
||||
```typescript
|
||||
// apps/server/src/main.ts
|
||||
app.use((req, res, next) => {
|
||||
// Prevent clickjacking
|
||||
res.setHeader('X-Frame-Options', 'SAMEORIGIN')
|
||||
|
||||
// Prevent MIME sniffing
|
||||
res.setHeader('X-Content-Type-Options', 'nosniff')
|
||||
|
||||
// XSS protection
|
||||
res.setHeader('X-XSS-Protection', '1; mode=block')
|
||||
|
||||
// Referrer policy
|
||||
res.setHeader('Referrer-Policy', 'same-origin')
|
||||
|
||||
// HTTPS upgrade
|
||||
if (req.secure) {
|
||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000')
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
**API Rate Limiting:**
|
||||
```typescript
|
||||
// apps/server/src/routes/middleware/rate_limit.ts
|
||||
const rateLimit = require('express-rate-limit')
|
||||
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 1000, // Limit each IP to 1000 requests per window
|
||||
message: 'Too many requests from this IP'
|
||||
})
|
||||
|
||||
app.use('/api/', apiLimiter)
|
||||
```
|
||||
|
||||
**Login Rate Limiting:**
|
||||
```typescript
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 5, // 5 failed attempts
|
||||
skipSuccessfulRequests: true
|
||||
})
|
||||
|
||||
app.post('/api/login/password', loginLimiter, loginHandler)
|
||||
```
|
||||
|
||||
## Data Security
|
||||
|
||||
### Secure Data Deletion
|
||||
|
||||
**Soft Delete:**
|
||||
```typescript
|
||||
// Mark as deleted (sync first)
|
||||
note.isDeleted = 1
|
||||
note.deleteId = generateUUID()
|
||||
note.save()
|
||||
|
||||
// Entity change tracked for sync
|
||||
addEntityChange('notes', noteId, note)
|
||||
```
|
||||
|
||||
**Hard Delete (Erase):**
|
||||
```typescript
|
||||
// After sync completed
|
||||
sql.execute('DELETE FROM notes WHERE noteId = ?', [noteId])
|
||||
sql.execute('DELETE FROM branches WHERE noteId = ?', [noteId])
|
||||
sql.execute('DELETE FROM attributes WHERE noteId = ?', [noteId])
|
||||
|
||||
// Mark entity change as erased
|
||||
sql.execute('UPDATE entity_changes SET isErased = 1 WHERE entityId = ?', [noteId])
|
||||
```
|
||||
|
||||
**Blob Cleanup:**
|
||||
```typescript
|
||||
// Find orphaned blobs (not referenced by any note/revision/attachment)
|
||||
const orphanedBlobs = sql.getRows(`
|
||||
SELECT blobId FROM blobs
|
||||
WHERE blobId NOT IN (SELECT blobId FROM notes WHERE blobId IS NOT NULL)
|
||||
AND blobId NOT IN (SELECT blobId FROM revisions WHERE blobId IS NOT NULL)
|
||||
AND blobId NOT IN (SELECT blobId FROM attachments WHERE blobId IS NOT NULL)
|
||||
`)
|
||||
|
||||
// Delete orphaned blobs
|
||||
for (const blob of orphanedBlobs) {
|
||||
sql.execute('DELETE FROM blobs WHERE blobId = ?', [blob.blobId])
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Security
|
||||
|
||||
**Protected Data in Memory:**
|
||||
- Protected data keys stored in memory only
|
||||
- Cleared on timeout
|
||||
- Not written to disk
|
||||
- Not in session storage
|
||||
|
||||
**Memory Cleanup:**
|
||||
```typescript
|
||||
// Clear sensitive data
|
||||
const clearSensitiveData = () => {
|
||||
protectedDataKey = null
|
||||
|
||||
// Force garbage collection if available
|
||||
if (global.gc) {
|
||||
global.gc()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Temporary Files
|
||||
|
||||
**Secure Temporary Files:**
|
||||
```typescript
|
||||
const tempDir = os.tmpdir()
|
||||
const tempFile = path.join(tempDir, `trilium-${crypto.randomBytes(16).toString('hex')}`)
|
||||
|
||||
// Write temp file
|
||||
fs.writeFileSync(tempFile, data, { mode: 0o600 }) // Owner read/write only
|
||||
|
||||
// Clean up after use
|
||||
fs.unlinkSync(tempFile)
|
||||
```
|
||||
|
||||
## Dependency Security
|
||||
|
||||
### Vulnerability Scanning
|
||||
|
||||
**Tools:**
|
||||
- `npm audit` - Check for known vulnerabilities
|
||||
- Renovate bot - Automatic dependency updates
|
||||
- GitHub Dependabot alerts
|
||||
|
||||
**Process:**
|
||||
```bash
|
||||
# Check for vulnerabilities
|
||||
npm audit
|
||||
|
||||
# Fix automatically
|
||||
npm audit fix
|
||||
|
||||
# Manual review for breaking changes
|
||||
npm audit fix --force
|
||||
```
|
||||
|
||||
### Dependency Pinning
|
||||
|
||||
**package.json:**
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"express": "4.18.2", // Exact version
|
||||
"better-sqlite3": "^9.2.2" // Compatible versions
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**pnpm Overrides:**
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"lodash@<4.17.21": ">=4.17.21", // Force minimum version
|
||||
"axios@<0.21.2": ">=0.21.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Patch Management
|
||||
|
||||
**pnpm Patches:**
|
||||
```bash
|
||||
# Create patch
|
||||
pnpm patch @ckeditor/ckeditor5
|
||||
|
||||
# Edit files in temporary directory
|
||||
# ...
|
||||
|
||||
# Generate patch file
|
||||
pnpm patch-commit /tmp/ckeditor5-patch
|
||||
|
||||
# Patch applied automatically on install
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### For Users
|
||||
|
||||
1. **Strong Passwords**
|
||||
- Use unique password for Trilium
|
||||
- Enable TOTP 2FA
|
||||
- Protect password manager
|
||||
|
||||
2. **Protected Notes**
|
||||
- Use for sensitive information
|
||||
- Set reasonable session timeout
|
||||
- Don't leave sessions unattended
|
||||
|
||||
3. **Backups**
|
||||
- Regular backups to secure location
|
||||
- Encrypt backup storage
|
||||
- Test backup restoration
|
||||
|
||||
4. **Server Setup**
|
||||
- Use HTTPS only
|
||||
- Keep software updated
|
||||
- Firewall configuration
|
||||
- Use reverse proxy (nginx, Caddy)
|
||||
|
||||
5. **Scripts**
|
||||
- Review scripts before using
|
||||
- Be cautious with external scripts
|
||||
- Understand script permissions
|
||||
|
||||
### For Developers
|
||||
|
||||
1. **Code Review**
|
||||
- Review all security-related changes
|
||||
- Test authentication/authorization changes
|
||||
- Validate input sanitization
|
||||
|
||||
2. **Testing**
|
||||
- Write security tests
|
||||
- Test edge cases
|
||||
- Penetration testing
|
||||
|
||||
3. **Dependencies**
|
||||
- Regular updates
|
||||
- Audit new dependencies
|
||||
- Monitor security advisories
|
||||
|
||||
4. **Secrets**
|
||||
- No secrets in source code
|
||||
- Use environment variables
|
||||
- Secure key generation
|
||||
|
||||
## Security Auditing
|
||||
|
||||
### Logs
|
||||
|
||||
**Security Events Logged:**
|
||||
- Login attempts (success/failure)
|
||||
- Protected session access
|
||||
- Password changes
|
||||
- ETAPI token usage
|
||||
- Failed CSRF validations
|
||||
|
||||
**Log Location:**
|
||||
- Desktop: Console output
|
||||
- Server: Log files or stdout
|
||||
|
||||
### Monitoring
|
||||
|
||||
**Metrics to Monitor:**
|
||||
- Failed login attempts
|
||||
- API error rates
|
||||
- Unusual database changes
|
||||
- Large exports/imports
|
||||
|
||||
## Incident Response
|
||||
|
||||
### Security Issue Reporting
|
||||
|
||||
**Process:**
|
||||
1. Email security@triliumnext.com
|
||||
2. Include vulnerability details
|
||||
3. Provide reproduction steps
|
||||
4. Allow reasonable disclosure time
|
||||
|
||||
**Response:**
|
||||
1. Acknowledge within 48 hours
|
||||
2. Investigate and validate
|
||||
3. Develop fix
|
||||
4. Coordinate disclosure
|
||||
5. Release patch
|
||||
|
||||
### Breach Response
|
||||
|
||||
**If Compromised:**
|
||||
1. Change password immediately
|
||||
2. Review recent activity
|
||||
3. Check for unauthorized changes
|
||||
4. Restore from backup if needed
|
||||
5. Update security settings
|
||||
|
||||
## Future Security Enhancements
|
||||
|
||||
**Planned:**
|
||||
- Hardware security key support (U2F/WebAuthn)
|
||||
- End-to-end encryption for sync
|
||||
- Zero-knowledge architecture option
|
||||
- Encryption key rotation
|
||||
- Audit log enhancements
|
||||
- Per-note access controls
|
||||
|
||||
**Under Consideration:**
|
||||
- Multi-user support with permissions
|
||||
- Blockchain-based sync verification
|
||||
- Homomorphic encryption for search
|
||||
- Quantum-resistant encryption
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [SECURITY.md](../SECURITY.md) - Security policy
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Overall architecture
|
||||
- [Protected Notes Guide](https://triliumnext.github.io/Docs/Wiki/protected-notes)
|
||||
583
docs/SYNCHRONIZATION.md
vendored
583
docs/SYNCHRONIZATION.md
vendored
@@ -1,583 +0,0 @@
|
||||
# Trilium Synchronization Architecture
|
||||
|
||||
> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md) | [User Guide: Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization)
|
||||
|
||||
## Overview
|
||||
|
||||
Trilium implements a sophisticated **bidirectional synchronization system** that allows users to sync their note databases across multiple devices (desktop clients and server instances). The sync protocol is designed to handle:
|
||||
|
||||
- Concurrent modifications across devices
|
||||
- Conflict resolution
|
||||
- Partial sync (only changed entities)
|
||||
- Protected note synchronization
|
||||
- Efficient bandwidth usage
|
||||
|
||||
## Sync Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Desktop 1 │ │ Desktop 2 │
|
||||
│ (Client) │ │ (Client) │
|
||||
└──────┬──────┘ └──────┬──────┘
|
||||
│ │
|
||||
│ WebSocket/HTTP │
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────────────────────────────────────┐
|
||||
│ Sync Server │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ Sync Service │ │
|
||||
│ │ - Entity Change Management │ │
|
||||
│ │ - Conflict Resolution │ │
|
||||
│ │ - Version Tracking │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────┴───────┐ │
|
||||
│ │ Database │ │
|
||||
│ │ (entity_changes)│ │
|
||||
│ └──────────────┘ │
|
||||
└────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Entity Changes
|
||||
|
||||
Every modification to any entity (note, branch, attribute, etc.) creates an **entity change** record:
|
||||
|
||||
```sql
|
||||
entity_changes (
|
||||
id, -- Auto-increment ID
|
||||
entityName, -- 'notes', 'branches', 'attributes', etc.
|
||||
entityId, -- ID of the changed entity
|
||||
hash, -- Content hash for integrity
|
||||
isErased, -- If entity was erased (deleted permanently)
|
||||
changeId, -- Unique change identifier
|
||||
componentId, -- Installation identifier
|
||||
instanceId, -- Process instance identifier
|
||||
isSynced, -- Whether synced to server
|
||||
utcDateChanged -- When change occurred
|
||||
)
|
||||
```
|
||||
|
||||
**Key Properties:**
|
||||
- **changeId**: Globally unique identifier (UUID) for the change
|
||||
- **componentId**: Unique per Trilium installation (persists across restarts)
|
||||
- **instanceId**: Unique per process (changes on restart)
|
||||
- **hash**: SHA-256 hash of entity data for integrity verification
|
||||
|
||||
### Sync Versions
|
||||
|
||||
Each Trilium installation tracks:
|
||||
- **Local sync version**: Highest change ID seen locally
|
||||
- **Server sync version**: Highest change ID on server
|
||||
- **Entity versions**: Last sync version for each entity type
|
||||
|
||||
### Change Tracking
|
||||
|
||||
**When an entity is modified:**
|
||||
|
||||
```typescript
|
||||
// apps/server/src/services/entity_changes.ts
|
||||
function addEntityChange(entityName, entityId, entity) {
|
||||
const hash = calculateHash(entity)
|
||||
const changeId = generateUUID()
|
||||
|
||||
sql.insert('entity_changes', {
|
||||
entityName,
|
||||
entityId,
|
||||
hash,
|
||||
changeId,
|
||||
componentId: config.componentId,
|
||||
instanceId: config.instanceId,
|
||||
isSynced: 0,
|
||||
utcDateChanged: now()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Entity modification triggers:**
|
||||
- Note content update
|
||||
- Note metadata change
|
||||
- Branch creation/deletion/reorder
|
||||
- Attribute addition/removal
|
||||
- Options modification
|
||||
|
||||
## Sync Protocol
|
||||
|
||||
### Sync Handshake
|
||||
|
||||
**Step 1: Client Initiates Sync**
|
||||
|
||||
```typescript
|
||||
// Client sends current sync version
|
||||
POST /api/sync/check
|
||||
{
|
||||
"sourceId": "client-component-id",
|
||||
"maxChangeId": 12345
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Server Responds with Status**
|
||||
|
||||
```typescript
|
||||
// Server checks for changes
|
||||
Response:
|
||||
{
|
||||
"entityChanges": 567, // Changes on server
|
||||
"maxChangeId": 12890, // Server's max change ID
|
||||
"outstandingPushCount": 23 // Client changes not yet synced
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Decision**
|
||||
|
||||
- If `entityChanges > 0`: Pull changes from server
|
||||
- If `outstandingPushCount > 0`: Push changes to server
|
||||
- Both can happen in sequence
|
||||
|
||||
### Pull Sync (Server → Client)
|
||||
|
||||
**Client Requests Changes:**
|
||||
|
||||
```typescript
|
||||
POST /api/sync/pull
|
||||
{
|
||||
"sourceId": "client-component-id",
|
||||
"lastSyncedChangeId": 12345
|
||||
}
|
||||
```
|
||||
|
||||
**Server Responds:**
|
||||
|
||||
```typescript
|
||||
Response:
|
||||
{
|
||||
"notes": [
|
||||
{ noteId: "abc", title: "New Note", ... }
|
||||
],
|
||||
"branches": [...],
|
||||
"attributes": [...],
|
||||
"revisions": [...],
|
||||
"attachments": [...],
|
||||
"entityChanges": [
|
||||
{ entityName: "notes", entityId: "abc", changeId: "...", ... }
|
||||
],
|
||||
"maxChangeId": 12890
|
||||
}
|
||||
```
|
||||
|
||||
**Client Processing:**
|
||||
|
||||
1. Apply entity changes to local database
|
||||
2. Update Froca cache
|
||||
3. Update local sync version
|
||||
4. Trigger UI refresh
|
||||
|
||||
### Push Sync (Client → Server)
|
||||
|
||||
**Client Sends Changes:**
|
||||
|
||||
```typescript
|
||||
POST /api/sync/push
|
||||
{
|
||||
"sourceId": "client-component-id",
|
||||
"entities": [
|
||||
{
|
||||
"entity": {
|
||||
"noteId": "xyz",
|
||||
"title": "Modified Note",
|
||||
...
|
||||
},
|
||||
"entityChange": {
|
||||
"changeId": "change-uuid",
|
||||
"entityName": "notes",
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Server Processing:**
|
||||
|
||||
1. Validate changes
|
||||
2. Check for conflicts
|
||||
3. Apply changes to database
|
||||
4. Update Becca cache
|
||||
5. Mark as synced
|
||||
6. Broadcast to other connected clients via WebSocket
|
||||
|
||||
**Conflict Detection:**
|
||||
|
||||
```typescript
|
||||
// Check if entity was modified on server since client's last sync
|
||||
const serverEntity = becca.getNote(noteId)
|
||||
const serverLastModified = serverEntity.utcDateModified
|
||||
|
||||
if (serverLastModified > clientSyncVersion) {
|
||||
// CONFLICT!
|
||||
resolveConflict(serverEntity, clientEntity)
|
||||
}
|
||||
```
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
### Conflict Types
|
||||
|
||||
**1. Content Conflict**
|
||||
- Both client and server modified same note content
|
||||
- **Resolution**: Last-write-wins based on `utcDateModified`
|
||||
|
||||
**2. Structure Conflict**
|
||||
- Branch moved/deleted on one side, modified on other
|
||||
- **Resolution**: Tombstone records, reconciliation
|
||||
|
||||
**3. Attribute Conflict**
|
||||
- Same attribute modified differently
|
||||
- **Resolution**: Last-write-wins
|
||||
|
||||
### Conflict Resolution Strategy
|
||||
|
||||
**Last-Write-Wins:**
|
||||
```typescript
|
||||
if (clientEntity.utcDateModified > serverEntity.utcDateModified) {
|
||||
// Client wins, apply client changes
|
||||
applyClientChange(clientEntity)
|
||||
} else {
|
||||
// Server wins, reject client change
|
||||
// Client will pull server version on next sync
|
||||
}
|
||||
```
|
||||
|
||||
**Tombstone Records:**
|
||||
- Deleted entities leave tombstone in `entity_changes`
|
||||
- Prevents re-sync of deleted items
|
||||
- `isErased = 1` for permanent deletions
|
||||
|
||||
### Protected Notes Sync
|
||||
|
||||
**Challenge:** Encrypted content can't be synced without password
|
||||
|
||||
**Solution:**
|
||||
|
||||
1. **Protected session required**: User must unlock protected notes
|
||||
2. **Encrypted sync**: Content synced in encrypted form
|
||||
3. **Hash verification**: Integrity checked without decryption
|
||||
4. **Lazy decryption**: Only decrypt when accessed
|
||||
|
||||
**Sync Flow:**
|
||||
|
||||
```typescript
|
||||
// Client side
|
||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
// Skip protected notes if session not active
|
||||
continue
|
||||
}
|
||||
|
||||
// Server side
|
||||
if (note.isProtected) {
|
||||
// Sync encrypted blob
|
||||
// Don't decrypt for sync
|
||||
syncEncryptedBlob(note.blobId)
|
||||
}
|
||||
```
|
||||
|
||||
## Sync States
|
||||
|
||||
### Connection States
|
||||
|
||||
- **Connected**: WebSocket connection active
|
||||
- **Disconnected**: No connection to sync server
|
||||
- **Syncing**: Actively transferring data
|
||||
- **Conflict**: Sync paused due to conflict
|
||||
|
||||
### Entity Sync States
|
||||
|
||||
Each entity can be in:
|
||||
- **Synced**: In sync with server
|
||||
- **Pending**: Local changes not yet pushed
|
||||
- **Conflict**: Conflicting changes detected
|
||||
|
||||
### UI Indicators
|
||||
|
||||
```typescript
|
||||
// apps/client/src/widgets/sync_status.ts
|
||||
class SyncStatusWidget {
|
||||
showSyncStatus() {
|
||||
if (isConnected && allSynced) {
|
||||
showIcon('synced')
|
||||
} else if (isSyncing) {
|
||||
showIcon('syncing-spinner')
|
||||
} else if (hasConflicts) {
|
||||
showIcon('conflict-warning')
|
||||
} else {
|
||||
showIcon('not-synced')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Incremental Sync
|
||||
|
||||
Only entities changed since last sync are transferred:
|
||||
|
||||
```sql
|
||||
SELECT * FROM entity_changes
|
||||
WHERE id > :lastSyncedChangeId
|
||||
ORDER BY id ASC
|
||||
LIMIT 1000
|
||||
```
|
||||
|
||||
### Batch Processing
|
||||
|
||||
Changes sent in batches to reduce round trips:
|
||||
|
||||
```typescript
|
||||
const BATCH_SIZE = 1000
|
||||
const changes = getUnsyncedChanges(BATCH_SIZE)
|
||||
await syncBatch(changes)
|
||||
```
|
||||
|
||||
### Hash-Based Change Detection
|
||||
|
||||
```typescript
|
||||
// Only sync if hash differs
|
||||
const localHash = calculateHash(localEntity)
|
||||
const serverHash = getServerHash(entityId)
|
||||
|
||||
if (localHash !== serverHash) {
|
||||
syncEntity(localEntity)
|
||||
}
|
||||
```
|
||||
|
||||
### Compression
|
||||
|
||||
Large payloads compressed before transmission:
|
||||
|
||||
```typescript
|
||||
// Server sends compressed response
|
||||
res.setHeader('Content-Encoding', 'gzip')
|
||||
res.send(gzip(syncData))
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Network Errors
|
||||
|
||||
**Retry Strategy:**
|
||||
```typescript
|
||||
const RETRY_DELAYS = [1000, 2000, 5000, 10000, 30000]
|
||||
|
||||
async function syncWithRetry(attempt = 0) {
|
||||
try {
|
||||
await performSync()
|
||||
} catch (error) {
|
||||
if (attempt < RETRY_DELAYS.length) {
|
||||
setTimeout(() => {
|
||||
syncWithRetry(attempt + 1)
|
||||
}, RETRY_DELAYS[attempt])
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sync Integrity Checks
|
||||
|
||||
**Hash Verification:**
|
||||
```typescript
|
||||
// Verify entity hash matches
|
||||
const calculatedHash = calculateHash(entity)
|
||||
const receivedHash = entityChange.hash
|
||||
|
||||
if (calculatedHash !== receivedHash) {
|
||||
throw new Error('Hash mismatch - data corruption detected')
|
||||
}
|
||||
```
|
||||
|
||||
**Consistency Checks:**
|
||||
- Orphaned branches detection
|
||||
- Missing parent notes
|
||||
- Invalid entity references
|
||||
- Circular dependencies
|
||||
|
||||
## Sync Server Configuration
|
||||
|
||||
### Server Setup
|
||||
|
||||
**Required Options:**
|
||||
```javascript
|
||||
{
|
||||
"syncServerHost": "https://sync.example.com",
|
||||
"syncServerTimeout": 60000,
|
||||
"syncProxy": "" // Optional HTTP proxy
|
||||
}
|
||||
```
|
||||
|
||||
**Authentication:**
|
||||
- Username/password or
|
||||
- Sync token (generated on server)
|
||||
|
||||
### Client Setup
|
||||
|
||||
**Desktop Client:**
|
||||
```javascript
|
||||
// Settings → Sync
|
||||
{
|
||||
"syncServerHost": "https://sync.example.com",
|
||||
"username": "user@example.com",
|
||||
"password": "********"
|
||||
}
|
||||
```
|
||||
|
||||
**Test Connection:**
|
||||
```typescript
|
||||
POST /api/sync/test
|
||||
Response: { "success": true }
|
||||
```
|
||||
|
||||
## Sync API Endpoints
|
||||
|
||||
Located at: `apps/server/src/routes/api/sync.ts`
|
||||
|
||||
**Endpoints:**
|
||||
|
||||
- `POST /api/sync/check` - Check sync status
|
||||
- `POST /api/sync/pull` - Pull changes from server
|
||||
- `POST /api/sync/push` - Push changes to server
|
||||
- `POST /api/sync/finished` - Mark sync complete
|
||||
- `POST /api/sync/test` - Test connection
|
||||
- `GET /api/sync/stats` - Sync statistics
|
||||
|
||||
## WebSocket Sync Updates
|
||||
|
||||
Real-time sync via WebSocket:
|
||||
|
||||
```typescript
|
||||
// Server broadcasts change to all connected clients
|
||||
ws.broadcast('entity-change', {
|
||||
entityName: 'notes',
|
||||
entityId: 'abc123',
|
||||
changeId: 'change-uuid',
|
||||
sourceId: 'originating-component-id'
|
||||
})
|
||||
|
||||
// Client receives and applies
|
||||
ws.on('entity-change', (data) => {
|
||||
if (data.sourceId !== myComponentId) {
|
||||
froca.processEntityChange(data)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Sync Scheduling
|
||||
|
||||
### Automatic Sync
|
||||
|
||||
**Desktop:**
|
||||
- Sync on startup
|
||||
- Periodic sync (configurable interval, default: 60s)
|
||||
- Sync before shutdown
|
||||
|
||||
**Server:**
|
||||
- Sync on entity modification
|
||||
- WebSocket push to connected clients
|
||||
|
||||
### Manual Sync
|
||||
|
||||
User can trigger:
|
||||
- Full sync
|
||||
- Sync now
|
||||
- Sync specific subtree
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Sync stuck:**
|
||||
```sql
|
||||
-- Reset sync state
|
||||
UPDATE entity_changes SET isSynced = 0;
|
||||
DELETE FROM options WHERE name LIKE 'sync%';
|
||||
```
|
||||
|
||||
**Hash mismatch:**
|
||||
- Data corruption detected
|
||||
- Re-sync from backup
|
||||
- Check database integrity
|
||||
|
||||
**Conflict loop:**
|
||||
- Manual intervention required
|
||||
- Export conflicting notes
|
||||
- Choose winning version
|
||||
- Re-sync
|
||||
|
||||
### Sync Diagnostics
|
||||
|
||||
**Check sync status:**
|
||||
```typescript
|
||||
GET /api/sync/stats
|
||||
Response: {
|
||||
"unsyncedChanges": 0,
|
||||
"lastSyncDate": "2025-11-02T12:00:00Z",
|
||||
"syncVersion": 12890
|
||||
}
|
||||
```
|
||||
|
||||
**Entity change log:**
|
||||
```sql
|
||||
SELECT * FROM entity_changes
|
||||
WHERE isSynced = 0
|
||||
ORDER BY id DESC;
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Encrypted Sync
|
||||
|
||||
- Protected notes synced encrypted
|
||||
- No plain text over network
|
||||
- Server cannot read protected content
|
||||
|
||||
### Authentication
|
||||
|
||||
- Username/password over HTTPS only
|
||||
- Sync tokens for token-based auth
|
||||
- Session cookies with CSRF protection
|
||||
|
||||
### Authorization
|
||||
|
||||
- Users can only sync their own data
|
||||
- No cross-user sync support
|
||||
- Sync server validates ownership
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
**Typical Sync Performance:**
|
||||
- 1000 changes: ~2-5 seconds
|
||||
- 10000 changes: ~20-50 seconds
|
||||
- Initial full sync (100k notes): ~5-10 minutes
|
||||
|
||||
**Factors:**
|
||||
- Network latency
|
||||
- Database size
|
||||
- Number of protected notes
|
||||
- Attachment sizes
|
||||
|
||||
## Future Improvements
|
||||
|
||||
**Planned Enhancements:**
|
||||
- Differential sync (binary diff)
|
||||
- Peer-to-peer sync (no central server)
|
||||
- Multi-server sync
|
||||
- Partial sync (subtree only)
|
||||
- Sync over Tor/I2P
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Overall architecture
|
||||
- [Sync User Guide](https://triliumnext.github.io/Docs/Wiki/synchronization)
|
||||
- [Sync API Source](../apps/server/src/routes/api/sync.ts)
|
||||
423
docs/TECHNICAL_DOCUMENTATION.md
vendored
423
docs/TECHNICAL_DOCUMENTATION.md
vendored
@@ -1,423 +0,0 @@
|
||||
# Trilium Notes - Technical Documentation Index
|
||||
|
||||
Welcome to the comprehensive technical and architectural documentation for Trilium Notes. This index provides quick access to all technical documentation resources.
|
||||
|
||||
## 📚 Core Architecture Documentation
|
||||
|
||||
### [ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
**Main technical architecture document** covering the complete system design.
|
||||
|
||||
**Topics Covered:**
|
||||
- High-level architecture overview
|
||||
- Monorepo structure and organization
|
||||
- Core architecture patterns (Becca, Froca, Shaca)
|
||||
- Entity system and data model
|
||||
- Widget-based UI architecture
|
||||
- Frontend and backend architecture
|
||||
- API architecture (Internal, ETAPI, WebSocket)
|
||||
- Build system and tooling
|
||||
- Testing strategy
|
||||
- Security overview
|
||||
|
||||
**Audience:** Developers, architects, contributors
|
||||
|
||||
---
|
||||
|
||||
### [DATABASE.md](DATABASE.md)
|
||||
**Complete database architecture and schema documentation.**
|
||||
|
||||
**Topics Covered:**
|
||||
- SQLite database structure
|
||||
- Entity tables (notes, branches, attributes, revisions, attachments, blobs)
|
||||
- System tables (options, entity_changes, sessions)
|
||||
- Data relationships and integrity
|
||||
- Database access patterns
|
||||
- Migrations and versioning
|
||||
- Performance optimization
|
||||
- Backup and maintenance
|
||||
- Security considerations
|
||||
|
||||
**Audience:** Backend developers, database administrators
|
||||
|
||||
---
|
||||
|
||||
### [SYNCHRONIZATION.md](SYNCHRONIZATION.md)
|
||||
**Detailed synchronization protocol and implementation.**
|
||||
|
||||
**Topics Covered:**
|
||||
- Sync architecture overview
|
||||
- Entity change tracking
|
||||
- Sync protocol (handshake, pull, push)
|
||||
- Conflict resolution strategies
|
||||
- Protected notes synchronization
|
||||
- Performance optimizations
|
||||
- Error handling and retry logic
|
||||
- Sync server configuration
|
||||
- WebSocket real-time updates
|
||||
- Troubleshooting guide
|
||||
|
||||
**Audience:** Advanced users, sync server administrators, contributors
|
||||
|
||||
---
|
||||
|
||||
### [SCRIPTING.md](SCRIPTING.md)
|
||||
**Comprehensive guide to the Trilium scripting system.**
|
||||
|
||||
**Topics Covered:**
|
||||
- Script types (frontend, backend, render)
|
||||
- Frontend API reference
|
||||
- Backend API reference
|
||||
- Entity classes (FNote, BNote, etc.)
|
||||
- Script examples and patterns
|
||||
- Script storage and execution
|
||||
- Security considerations
|
||||
- Performance optimization
|
||||
- Debugging techniques
|
||||
- Advanced topics
|
||||
|
||||
**Audience:** Power users, script developers, plugin creators
|
||||
|
||||
---
|
||||
|
||||
### [SECURITY_ARCHITECTURE.md](SECURITY_ARCHITECTURE.md)
|
||||
**In-depth security architecture and implementation.**
|
||||
|
||||
**Topics Covered:**
|
||||
- Security principles and threat model
|
||||
- Authentication methods (password, TOTP, OpenID)
|
||||
- Session management
|
||||
- Authorization and protected sessions
|
||||
- Encryption (notes, transport, backups)
|
||||
- Input sanitization (XSS, SQL injection, CSRF)
|
||||
- Network security (HTTPS, headers, rate limiting)
|
||||
- Data security and secure deletion
|
||||
- Dependency security
|
||||
- Security best practices
|
||||
- Incident response
|
||||
|
||||
**Audience:** Security engineers, administrators, auditors
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Developer Documentation
|
||||
|
||||
### [Developer Guide](Developer%20Guide/Developer%20Guide/)
|
||||
Collection of developer-focused documentation for contributing to Trilium.
|
||||
|
||||
**Key Documents:**
|
||||
- [Environment Setup](Developer%20Guide/Developer%20Guide/Environment%20Setup.md) - Setting up development environment
|
||||
- [Project Structure](Developer%20Guide/Developer%20Guide/Project%20Structure.md) - Monorepo organization
|
||||
- [Development and Architecture](Developer%20Guide/Developer%20Guide/Development%20and%20architecture/) - Various development topics
|
||||
|
||||
**Topics Include:**
|
||||
- Local development setup
|
||||
- Building and deployment
|
||||
- Adding new note types
|
||||
- Database schema details
|
||||
- Internationalization
|
||||
- Icons and UI customization
|
||||
- Docker development
|
||||
- Troubleshooting
|
||||
|
||||
**Audience:** Contributors, developers
|
||||
|
||||
---
|
||||
|
||||
## 📖 User Documentation
|
||||
|
||||
### [User Guide](User%20Guide/User%20Guide/)
|
||||
Comprehensive end-user documentation for using Trilium.
|
||||
|
||||
**Key Sections:**
|
||||
- Installation & Setup
|
||||
- Basic Concepts and Features
|
||||
- Note Types
|
||||
- Advanced Usage
|
||||
- Synchronization
|
||||
- Import/Export
|
||||
|
||||
**Audience:** End users, administrators
|
||||
|
||||
---
|
||||
|
||||
### [Script API](Script%20API/)
|
||||
Complete API reference for user scripting.
|
||||
|
||||
**Coverage:**
|
||||
- Frontend API methods
|
||||
- Backend API methods
|
||||
- Entity properties and methods
|
||||
- Event handlers
|
||||
- Utility functions
|
||||
|
||||
**Audience:** Script developers, power users
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start Guides
|
||||
|
||||
### For Users
|
||||
1. [Installation Guide](User%20Guide/User%20Guide/Installation%20&%20Setup/) - Get Trilium running
|
||||
2. [Basic Concepts](User%20Guide/User%20Guide/Basic%20Concepts%20and%20Features/) - Learn the fundamentals
|
||||
3. [Scripting Guide](SCRIPTING.md) - Extend Trilium with scripts
|
||||
|
||||
### For Developers
|
||||
1. [Environment Setup](Developer%20Guide/Developer%20Guide/Environment%20Setup.md) - Setup development environment
|
||||
2. [Architecture Overview](ARCHITECTURE.md) - Understand the system
|
||||
3. [Contributing Guide](../README.md#-contribute) - Start contributing
|
||||
|
||||
### For Administrators
|
||||
1. [Server Installation](User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md) - Deploy Trilium server
|
||||
2. [Synchronization Setup](SYNCHRONIZATION.md) - Configure sync
|
||||
3. [Security Best Practices](SECURITY_ARCHITECTURE.md#security-best-practices) - Secure your installation
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Documentation by Topic
|
||||
|
||||
### Architecture & Design
|
||||
- [Overall Architecture](ARCHITECTURE.md)
|
||||
- [Monorepo Structure](ARCHITECTURE.md#monorepo-structure)
|
||||
- [Three-Layer Cache System](ARCHITECTURE.md#three-layer-cache-system)
|
||||
- [Entity System](ARCHITECTURE.md#entity-system)
|
||||
- [Widget-Based UI](ARCHITECTURE.md#widget-based-ui)
|
||||
|
||||
### Data & Storage
|
||||
- [Database Architecture](DATABASE.md)
|
||||
- [Entity Tables](DATABASE.md#entity-tables)
|
||||
- [Data Relationships](DATABASE.md#data-relationships)
|
||||
- [Blob Storage](DATABASE.md#blobs-table)
|
||||
- [Database Migrations](DATABASE.md#database-migrations)
|
||||
|
||||
### Synchronization
|
||||
- [Sync Architecture](SYNCHRONIZATION.md#sync-architecture)
|
||||
- [Sync Protocol](SYNCHRONIZATION.md#sync-protocol)
|
||||
- [Conflict Resolution](SYNCHRONIZATION.md#conflict-resolution)
|
||||
- [Protected Notes Sync](SYNCHRONIZATION.md#protected-notes-sync)
|
||||
- [WebSocket Sync](SYNCHRONIZATION.md#websocket-sync-updates)
|
||||
|
||||
### Security
|
||||
- [Authentication](SECURITY_ARCHITECTURE.md#authentication)
|
||||
- [Encryption](SECURITY_ARCHITECTURE.md#encryption)
|
||||
- [Input Sanitization](SECURITY_ARCHITECTURE.md#input-sanitization)
|
||||
- [Network Security](SECURITY_ARCHITECTURE.md#network-security)
|
||||
- [Security Best Practices](SECURITY_ARCHITECTURE.md#security-best-practices)
|
||||
|
||||
### Scripting & Extensibility
|
||||
- [Script Types](SCRIPTING.md#script-types)
|
||||
- [Frontend API](SCRIPTING.md#frontend-api)
|
||||
- [Backend API](SCRIPTING.md#backend-api)
|
||||
- [Script Examples](SCRIPTING.md#script-examples)
|
||||
- [Custom Widgets](SCRIPTING.md#render-scripts)
|
||||
|
||||
### Frontend
|
||||
- [Client Architecture](ARCHITECTURE.md#frontend-architecture)
|
||||
- [Widget System](ARCHITECTURE.md#widget-based-ui)
|
||||
- [Event System](ARCHITECTURE.md#event-system)
|
||||
- [Froca Cache](ARCHITECTURE.md#2-froca-frontend-cache)
|
||||
- [UI Components](ARCHITECTURE.md#ui-components)
|
||||
|
||||
### Backend
|
||||
- [Server Architecture](ARCHITECTURE.md#backend-architecture)
|
||||
- [Service Layer](ARCHITECTURE.md#service-layer)
|
||||
- [Route Structure](ARCHITECTURE.md#route-structure)
|
||||
- [Becca Cache](ARCHITECTURE.md#1-becca-backend-cache)
|
||||
- [Middleware](ARCHITECTURE.md#middleware)
|
||||
|
||||
### Build & Deploy
|
||||
- [Build System](ARCHITECTURE.md#build-system)
|
||||
- [Package Manager](ARCHITECTURE.md#package-manager-pnpm)
|
||||
- [Build Tools](ARCHITECTURE.md#build-tools)
|
||||
- [Docker](Developer%20Guide/Developer%20Guide/Development%20and%20architecture/Docker.md)
|
||||
- [Deployment](Developer%20Guide/Developer%20Guide/Building%20and%20deployment/)
|
||||
|
||||
### Testing
|
||||
- [Testing Strategy](ARCHITECTURE.md#testing-strategy)
|
||||
- [Test Organization](ARCHITECTURE.md#test-organization)
|
||||
- [E2E Testing](ARCHITECTURE.md#e2e-testing)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Reference Documentation
|
||||
|
||||
### File Locations
|
||||
```
|
||||
trilium/
|
||||
├── apps/
|
||||
│ ├── client/ # Frontend application
|
||||
│ ├── server/ # Backend server
|
||||
│ ├── desktop/ # Electron app
|
||||
│ └── ...
|
||||
├── packages/
|
||||
│ ├── commons/ # Shared code
|
||||
│ ├── ckeditor5/ # Rich text editor
|
||||
│ └── ...
|
||||
├── docs/
|
||||
│ ├── ARCHITECTURE.md # Main architecture doc
|
||||
│ ├── DATABASE.md # Database documentation
|
||||
│ ├── SYNCHRONIZATION.md # Sync documentation
|
||||
│ ├── SCRIPTING.md # Scripting guide
|
||||
│ ├── SECURITY_ARCHITECTURE.md # Security documentation
|
||||
│ ├── Developer Guide/ # Developer docs
|
||||
│ ├── User Guide/ # User docs
|
||||
│ └── Script API/ # API reference
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Key Source Files
|
||||
- **Backend Entry:** `apps/server/src/main.ts`
|
||||
- **Frontend Entry:** `apps/client/src/desktop.ts` / `apps/client/src/index.ts`
|
||||
- **Becca Cache:** `apps/server/src/becca/becca.ts`
|
||||
- **Froca Cache:** `apps/client/src/services/froca.ts`
|
||||
- **Database Schema:** `apps/server/src/assets/db/schema.sql`
|
||||
- **Backend API:** `apps/server/src/services/backend_script_api.ts`
|
||||
- **Frontend API:** `apps/client/src/services/frontend_script_api.ts`
|
||||
|
||||
### Important Directories
|
||||
- **Entities:** `apps/server/src/becca/entities/`
|
||||
- **Widgets:** `apps/client/src/widgets/`
|
||||
- **Services:** `apps/server/src/services/`
|
||||
- **Routes:** `apps/server/src/routes/`
|
||||
- **Migrations:** `apps/server/src/migrations/`
|
||||
- **Tests:** Various `*.spec.ts` files throughout
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Common Tasks
|
||||
|
||||
### Understanding the Codebase
|
||||
1. Read [ARCHITECTURE.md](ARCHITECTURE.md) for overview
|
||||
2. Explore [Monorepo Structure](ARCHITECTURE.md#monorepo-structure)
|
||||
3. Review [Entity System](ARCHITECTURE.md#entity-system)
|
||||
4. Check [Key Files](ARCHITECTURE.md#key-files-for-understanding-architecture)
|
||||
|
||||
### Adding Features
|
||||
1. Review relevant architecture documentation
|
||||
2. Check [Developer Guide](Developer%20Guide/Developer%20Guide/)
|
||||
3. Follow existing patterns in codebase
|
||||
4. Write tests
|
||||
5. Update documentation
|
||||
|
||||
### Debugging Issues
|
||||
1. Check [Troubleshooting](Developer%20Guide/Developer%20Guide/Troubleshooting/)
|
||||
2. Review [Database](DATABASE.md) for data issues
|
||||
3. Check [Synchronization](SYNCHRONIZATION.md) for sync issues
|
||||
4. Review [Security](SECURITY_ARCHITECTURE.md) for auth issues
|
||||
|
||||
### Performance Optimization
|
||||
1. [Database Performance](DATABASE.md#performance-optimization)
|
||||
2. [Cache Optimization](ARCHITECTURE.md#caching-system)
|
||||
3. [Build Optimization](ARCHITECTURE.md#build-system)
|
||||
4. [Script Performance](SCRIPTING.md#performance-considerations)
|
||||
|
||||
---
|
||||
|
||||
## 🔗 External Resources
|
||||
|
||||
### Official Links
|
||||
- **Website:** https://triliumnotes.org
|
||||
- **Documentation:** https://docs.triliumnotes.org
|
||||
- **GitHub:** https://github.com/TriliumNext/Trilium
|
||||
- **Discussions:** https://github.com/TriliumNext/Trilium/discussions
|
||||
- **Matrix Chat:** https://matrix.to/#/#triliumnext:matrix.org
|
||||
|
||||
### Community Resources
|
||||
- **Awesome Trilium:** https://github.com/Nriver/awesome-trilium
|
||||
- **TriliumRocks:** https://trilium.rocks/
|
||||
- **Wiki:** https://triliumnext.github.io/Docs/Wiki/
|
||||
|
||||
### Related Projects
|
||||
- **TriliumDroid:** https://github.com/FliegendeWurst/TriliumDroid
|
||||
- **Web Clipper:** Included in main repository
|
||||
|
||||
---
|
||||
|
||||
## 📝 Documentation Conventions
|
||||
|
||||
### Document Structure
|
||||
- Overview section
|
||||
- Table of contents
|
||||
- Main content with headings
|
||||
- Code examples where relevant
|
||||
- "See Also" references
|
||||
|
||||
### Code Examples
|
||||
```typescript
|
||||
// TypeScript examples with comments
|
||||
const example = 'value'
|
||||
```
|
||||
|
||||
```sql
|
||||
-- SQL examples with formatting
|
||||
SELECT * FROM notes WHERE noteId = ?
|
||||
```
|
||||
|
||||
### Cross-References
|
||||
- Use relative links: `[text](path/to/file.md)`
|
||||
- Reference sections: `[text](file.md#section)`
|
||||
- External links: Full URLs
|
||||
|
||||
### Maintenance
|
||||
- Review on major releases
|
||||
- Update for architectural changes
|
||||
- Add examples for new features
|
||||
- Keep API references current
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing to Documentation
|
||||
|
||||
### What to Document
|
||||
- New features and APIs
|
||||
- Architecture changes
|
||||
- Migration guides
|
||||
- Performance tips
|
||||
- Security considerations
|
||||
|
||||
### How to Contribute
|
||||
1. Edit markdown files in `docs/`
|
||||
2. Follow existing structure and style
|
||||
3. Include code examples
|
||||
4. Test links and formatting
|
||||
5. Submit pull request
|
||||
|
||||
### Documentation Standards
|
||||
- Clear, concise language
|
||||
- Complete code examples
|
||||
- Proper markdown formatting
|
||||
- Cross-references to related docs
|
||||
- Updated version numbers
|
||||
|
||||
---
|
||||
|
||||
## 📅 Version Information
|
||||
|
||||
- **Documentation Version:** 0.99.3
|
||||
- **Last Updated:** November 2025
|
||||
- **Trilium Version:** 0.99.3+
|
||||
- **Next Review:** When major architectural changes occur
|
||||
|
||||
---
|
||||
|
||||
## 💡 Getting Help
|
||||
|
||||
### For Users
|
||||
- [User Guide](User%20Guide/User%20Guide/)
|
||||
- [GitHub Discussions](https://github.com/TriliumNext/Trilium/discussions)
|
||||
- [Matrix Chat](https://matrix.to/#/#triliumnext:matrix.org)
|
||||
|
||||
### For Developers
|
||||
- [Developer Guide](Developer%20Guide/Developer%20Guide/)
|
||||
- [Architecture Docs](ARCHITECTURE.md)
|
||||
- [GitHub Issues](https://github.com/TriliumNext/Trilium/issues)
|
||||
|
||||
### For Contributors
|
||||
- [Contributing Guidelines](../README.md#-contribute)
|
||||
- [Code of Conduct](../CODE_OF_CONDUCT)
|
||||
- [Developer Setup](Developer%20Guide/Developer%20Guide/Environment%20Setup.md)
|
||||
|
||||
---
|
||||
|
||||
**Maintained by:** TriliumNext Team
|
||||
**License:** AGPL-3.0-only
|
||||
**Repository:** https://github.com/TriliumNext/Trilium
|
||||
@@ -42,7 +42,7 @@
|
||||
"@playwright/test": "1.56.1",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"@types/express": "5.0.5",
|
||||
"@types/node": "24.9.2",
|
||||
"@types/node": "24.10.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"chalk": "5.6.2",
|
||||
@@ -57,7 +57,7 @@
|
||||
"jiti": "2.6.1",
|
||||
"jsonc-eslint-parser": "2.4.1",
|
||||
"react-refresh": "0.18.0",
|
||||
"rollup-plugin-webpack-stats": "2.1.6",
|
||||
"rollup-plugin-webpack-stats": "2.1.7",
|
||||
"tslib": "2.8.1",
|
||||
"tsx": "4.20.6",
|
||||
"typescript": "~5.9.0",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "out-tsc",
|
||||
"typeRoots": [
|
||||
"typings",
|
||||
"node_modules/@types"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "out-tsc",
|
||||
"typeRoots": [
|
||||
"typings",
|
||||
"node_modules/@types"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "out-tsc",
|
||||
"typeRoots": [
|
||||
"typings",
|
||||
"node_modules/@types"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "out-tsc",
|
||||
"typeRoots": [
|
||||
"typings",
|
||||
"node_modules/@types"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "out-tsc",
|
||||
"typeRoots": [
|
||||
"typings",
|
||||
"node_modules/@types"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"moduleResolution": "Node16",
|
||||
"target": "ES2022",
|
||||
"rootDir": "src",
|
||||
"outDir": "out-tsc",
|
||||
"module": "Node16"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
|
||||
306
pnpm-lock.yaml
generated
306
pnpm-lock.yaml
generated
@@ -53,8 +53,8 @@ importers:
|
||||
specifier: 5.0.5
|
||||
version: 5.0.5
|
||||
'@types/node':
|
||||
specifier: 24.9.2
|
||||
version: 24.9.2
|
||||
specifier: 24.10.0
|
||||
version: 24.10.0
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@vitest/browser@3.2.4)(vitest@3.2.4)
|
||||
@@ -98,8 +98,8 @@ importers:
|
||||
specifier: 0.18.0
|
||||
version: 0.18.0
|
||||
rollup-plugin-webpack-stats:
|
||||
specifier: 2.1.6
|
||||
version: 2.1.6(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
specifier: 2.1.7
|
||||
version: 2.1.7(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
tslib:
|
||||
specifier: 2.8.1
|
||||
version: 2.8.1
|
||||
@@ -117,13 +117,13 @@ importers:
|
||||
version: 2.0.1
|
||||
vite:
|
||||
specifier: 7.1.12
|
||||
version: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite-plugin-dts:
|
||||
specifier: ~4.5.0
|
||||
version: 4.5.4(@types/node@24.9.2)(rollup@4.52.0)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 4.5.4(@types/node@24.10.0)(rollup@4.52.0)(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
apps/build-docs:
|
||||
devDependencies:
|
||||
@@ -224,8 +224,8 @@ importers:
|
||||
specifier: 0.1.2
|
||||
version: 0.1.2
|
||||
debounce:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
draggabilly:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
@@ -304,7 +304,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@preact/preset-vite':
|
||||
specifier: 2.10.2
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@types/bootstrap':
|
||||
specifier: 5.2.10
|
||||
version: 5.2.10
|
||||
@@ -337,7 +337,7 @@ importers:
|
||||
version: 0.7.2
|
||||
vite-plugin-static-copy:
|
||||
specifier: 3.1.4
|
||||
version: 3.1.4(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 3.1.4(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
|
||||
apps/db-compare:
|
||||
dependencies:
|
||||
@@ -500,7 +500,7 @@ importers:
|
||||
version: 2.1.3(electron@38.5.0)
|
||||
'@preact/preset-vite':
|
||||
specifier: 2.10.2
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@triliumnext/commons':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/commons
|
||||
@@ -634,8 +634,8 @@ importers:
|
||||
specifier: 1.11.19
|
||||
version: 1.11.19
|
||||
debounce:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
debug:
|
||||
specifier: 4.4.3
|
||||
version: 4.4.3(supports-color@6.0.0)
|
||||
@@ -779,7 +779,7 @@ importers:
|
||||
version: 1.0.1
|
||||
vite:
|
||||
specifier: 7.1.12
|
||||
version: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
ws:
|
||||
specifier: 8.18.3
|
||||
version: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -819,7 +819,7 @@ importers:
|
||||
devDependencies:
|
||||
'@preact/preset-vite':
|
||||
specifier: 2.10.2
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
eslint:
|
||||
specifier: 9.39.0
|
||||
version: 9.39.0(jiti@2.6.1)
|
||||
@@ -834,7 +834,7 @@ importers:
|
||||
version: 0.4.2
|
||||
vite:
|
||||
specifier: 7.1.12
|
||||
version: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
packages/ckeditor5:
|
||||
dependencies:
|
||||
@@ -877,7 +877,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@ckeditor/ckeditor5-package-tools':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~8.46.0
|
||||
version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -886,7 +886,7 @@ importers:
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@vitest/browser':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-istanbul':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
@@ -913,16 +913,16 @@ importers:
|
||||
version: 12.2.0(stylelint@16.25.0(typescript@5.9.3))
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3)
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: 5.9.3
|
||||
version: 5.9.3
|
||||
vite-plugin-svgo:
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.0
|
||||
version: 9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -937,7 +937,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@ckeditor/ckeditor5-package-tools':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~8.46.0
|
||||
version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -946,7 +946,7 @@ importers:
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@vitest/browser':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-istanbul':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
@@ -973,16 +973,16 @@ importers:
|
||||
version: 12.2.0(stylelint@16.25.0(typescript@5.9.3))
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3)
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: 5.9.3
|
||||
version: 5.9.3
|
||||
vite-plugin-svgo:
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.0
|
||||
version: 9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -997,7 +997,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@ckeditor/ckeditor5-package-tools':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~8.46.0
|
||||
version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -1006,7 +1006,7 @@ importers:
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@vitest/browser':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-istanbul':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
@@ -1033,16 +1033,16 @@ importers:
|
||||
version: 12.2.0(stylelint@16.25.0(typescript@5.9.3))
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3)
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: 5.9.3
|
||||
version: 5.9.3
|
||||
vite-plugin-svgo:
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.0
|
||||
version: 9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -1064,7 +1064,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@ckeditor/ckeditor5-package-tools':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~8.46.0
|
||||
version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -1073,7 +1073,7 @@ importers:
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@vitest/browser':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-istanbul':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
@@ -1100,16 +1100,16 @@ importers:
|
||||
version: 12.2.0(stylelint@16.25.0(typescript@5.9.3))
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3)
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: 5.9.3
|
||||
version: 5.9.3
|
||||
vite-plugin-svgo:
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.0
|
||||
version: 9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -1131,7 +1131,7 @@ importers:
|
||||
version: 5.0.0
|
||||
'@ckeditor/ckeditor5-package-tools':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
version: 4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~8.46.0
|
||||
version: 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -1140,7 +1140,7 @@ importers:
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@vitest/browser':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
version: 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/coverage-istanbul':
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
@@ -1167,16 +1167,16 @@ importers:
|
||||
version: 12.2.0(stylelint@16.25.0(typescript@5.9.3))
|
||||
ts-node:
|
||||
specifier: 10.9.2
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3)
|
||||
version: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: 5.9.3
|
||||
version: 5.9.3
|
||||
vite-plugin-svgo:
|
||||
specifier: ~2.0.0
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
version: 2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
webdriverio:
|
||||
specifier: 9.20.0
|
||||
version: 9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
@@ -5480,8 +5480,8 @@ packages:
|
||||
'@types/node@22.18.8':
|
||||
resolution: {integrity: sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==}
|
||||
|
||||
'@types/node@24.9.2':
|
||||
resolution: {integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==}
|
||||
'@types/node@24.10.0':
|
||||
resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
|
||||
|
||||
'@types/parse-json@4.0.2':
|
||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||
@@ -7408,9 +7408,9 @@ packages:
|
||||
de-indent@1.0.2:
|
||||
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||
|
||||
debounce@2.2.0:
|
||||
resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==}
|
||||
engines: {node: '>=18'}
|
||||
debounce@3.0.0:
|
||||
resolution: {integrity: sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
debug@2.6.9:
|
||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||
@@ -12648,8 +12648,8 @@ packages:
|
||||
resolution: {integrity: sha512-EsoOi8moHN6CAYyTZipxDDVTJn0j2nBCWor4wRU45RQ8ER2qREDykXLr3Ulz6hBh6oBKCFTQIjo21i0FXNo/IA==}
|
||||
hasBin: true
|
||||
|
||||
rollup-plugin-stats@1.5.1:
|
||||
resolution: {integrity: sha512-WXx9F3i57DLKkB8mt6Zw3jN9sS8YOqTsfvuOG8RW0D95Wn5KHt2e9POh8sYWODgmTsiKK0Nm54ZjxLnp7yeCDw==}
|
||||
rollup-plugin-stats@1.5.2:
|
||||
resolution: {integrity: sha512-3PtTLkgJ9zDaBITh92sysBxpaIJHSokODV4eo6ivnxfzDZxFPpTPooWHPse/X/Qi9A186Opu+hPycZNPxSgtnA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
rolldown: ^1.0.0-beta.0
|
||||
@@ -12675,8 +12675,8 @@ packages:
|
||||
peerDependencies:
|
||||
rollup: ^3.0.0||^4.0.0
|
||||
|
||||
rollup-plugin-webpack-stats@2.1.6:
|
||||
resolution: {integrity: sha512-njKotmo0lWIbrTKJ5CrIPk9DuDsQziOo73rE3aQIAhecJj5o0ECBbE0vxgMor37o6TQ/IEAK8pDxzs4CqLdIJw==}
|
||||
rollup-plugin-webpack-stats@2.1.7:
|
||||
resolution: {integrity: sha512-hg+lhXu/lOp+k9SR7Xi47+s/YJyYnSSfE7h3iuqwDthaFwxYRw9cztd7HLXrwDgW18CC1L7n9ueZBr/Te2BWUw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
rolldown: ^1.0.0-beta.0
|
||||
@@ -15747,6 +15747,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
'@ckeditor/ckeditor5-watchdog': 47.1.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
@@ -16326,7 +16328,7 @@ snapshots:
|
||||
es-toolkit: 1.39.5
|
||||
protobufjs: 7.5.0
|
||||
|
||||
'@ckeditor/ckeditor5-package-tools@4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)':
|
||||
'@ckeditor/ckeditor5-package-tools@4.1.1(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(bufferutil@4.0.9)(esbuild@0.25.12)(utf-8-validate@6.0.5)':
|
||||
dependencies:
|
||||
'@ckeditor/ckeditor5-dev-translations': 53.2.0(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12)(typescript@5.0.4)(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12))
|
||||
'@ckeditor/ckeditor5-dev-utils': 53.2.0(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12)(typescript@5.0.4)(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12))
|
||||
@@ -16345,7 +16347,7 @@ snapshots:
|
||||
stylelint-config-ckeditor5: 2.0.1(stylelint@16.25.0(typescript@5.9.3))
|
||||
terser-webpack-plugin: 5.3.14(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12)(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12))
|
||||
ts-loader: 9.5.4(typescript@5.0.4)(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12))
|
||||
ts-node: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.0.4)
|
||||
ts-node: 10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.0.4)
|
||||
typescript: 5.0.4
|
||||
upath: 2.0.1
|
||||
webpack: 5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12)
|
||||
@@ -16436,8 +16438,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.1.0
|
||||
'@ckeditor/ckeditor5-utils': 47.1.0
|
||||
ckeditor5: 47.1.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-restricted-editing@47.1.0':
|
||||
dependencies:
|
||||
@@ -17994,26 +17994,26 @@ snapshots:
|
||||
'@inquirer/core': 9.2.1
|
||||
'@inquirer/type': 2.0.0
|
||||
|
||||
'@inquirer/confirm@5.1.19(@types/node@24.9.2)':
|
||||
'@inquirer/confirm@5.1.19(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.3.0(@types/node@24.9.2)
|
||||
'@inquirer/type': 3.0.9(@types/node@24.9.2)
|
||||
'@inquirer/core': 10.3.0(@types/node@24.10.0)
|
||||
'@inquirer/type': 3.0.9(@types/node@24.10.0)
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@inquirer/core@10.3.0(@types/node@24.9.2)':
|
||||
'@inquirer/core@10.3.0(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.1
|
||||
'@inquirer/figures': 1.0.14
|
||||
'@inquirer/type': 3.0.9(@types/node@24.9.2)
|
||||
'@inquirer/type': 3.0.9(@types/node@24.10.0)
|
||||
cli-width: 4.1.0
|
||||
mute-stream: 2.0.0
|
||||
signal-exit: 4.1.0
|
||||
wrap-ansi: 6.2.0
|
||||
yoctocolors-cjs: 2.1.3
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@inquirer/core@9.2.1':
|
||||
@@ -18106,9 +18106,9 @@ snapshots:
|
||||
dependencies:
|
||||
mute-stream: 1.0.0
|
||||
|
||||
'@inquirer/type@3.0.9(@types/node@24.9.2)':
|
||||
'@inquirer/type@3.0.9(@types/node@24.10.0)':
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@isaacs/balanced-match@4.0.1': {}
|
||||
@@ -18540,23 +18540,23 @@ snapshots:
|
||||
dependencies:
|
||||
langium: 3.3.1
|
||||
|
||||
'@microsoft/api-extractor-model@7.30.6(@types/node@24.9.2)':
|
||||
'@microsoft/api-extractor-model@7.30.6(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@microsoft/tsdoc': 0.15.1
|
||||
'@microsoft/tsdoc-config': 0.17.1
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.9.2)
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.10.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
'@microsoft/api-extractor@7.52.8(@types/node@24.9.2)':
|
||||
'@microsoft/api-extractor@7.52.8(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@microsoft/api-extractor-model': 7.30.6(@types/node@24.9.2)
|
||||
'@microsoft/api-extractor-model': 7.30.6(@types/node@24.10.0)
|
||||
'@microsoft/tsdoc': 0.15.1
|
||||
'@microsoft/tsdoc-config': 0.17.1
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.9.2)
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.10.0)
|
||||
'@rushstack/rig-package': 0.5.3
|
||||
'@rushstack/terminal': 0.15.3(@types/node@24.9.2)
|
||||
'@rushstack/ts-command-line': 5.0.1(@types/node@24.9.2)
|
||||
'@rushstack/terminal': 0.15.3(@types/node@24.10.0)
|
||||
'@rushstack/ts-command-line': 5.0.1(@types/node@24.10.0)
|
||||
lodash: 4.17.21
|
||||
minimatch: 3.0.8
|
||||
resolve: 1.22.10
|
||||
@@ -18877,18 +18877,18 @@ snapshots:
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
'@preact/preset-vite@2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
'@preact/preset-vite@2.10.2(@babel/core@7.28.0)(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.0
|
||||
'@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.0)
|
||||
'@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.0)
|
||||
'@prefresh/vite': 2.4.8(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@prefresh/vite': 2.4.8(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.0)
|
||||
debug: 4.4.1
|
||||
picocolors: 1.1.1
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite-prerender-plugin: 0.5.11(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite-prerender-plugin: 0.5.11(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
transitivePeerDependencies:
|
||||
- preact
|
||||
- supports-color
|
||||
@@ -18901,7 +18901,7 @@ snapshots:
|
||||
|
||||
'@prefresh/utils@1.2.1': {}
|
||||
|
||||
'@prefresh/vite@2.4.8(preact@10.27.2)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
'@prefresh/vite@2.4.8(preact@10.27.2)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.0
|
||||
'@prefresh/babel-plugin': 0.5.2
|
||||
@@ -18909,7 +18909,7 @@ snapshots:
|
||||
'@prefresh/utils': 1.2.1
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
preact: 10.27.2
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -19612,7 +19612,7 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.52.0':
|
||||
optional: true
|
||||
|
||||
'@rushstack/node-core-library@5.13.1(@types/node@24.9.2)':
|
||||
'@rushstack/node-core-library@5.13.1(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
ajv: 8.13.0
|
||||
ajv-draft-04: 1.0.0(ajv@8.13.0)
|
||||
@@ -19623,23 +19623,23 @@ snapshots:
|
||||
resolve: 1.22.10
|
||||
semver: 7.5.4
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@rushstack/rig-package@0.5.3':
|
||||
dependencies:
|
||||
resolve: 1.22.10
|
||||
strip-json-comments: 3.1.1
|
||||
|
||||
'@rushstack/terminal@0.15.3(@types/node@24.9.2)':
|
||||
'@rushstack/terminal@0.15.3(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.9.2)
|
||||
'@rushstack/node-core-library': 5.13.1(@types/node@24.10.0)
|
||||
supports-color: 8.1.1
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@rushstack/ts-command-line@5.0.1(@types/node@24.9.2)':
|
||||
'@rushstack/ts-command-line@5.0.1(@types/node@24.10.0)':
|
||||
dependencies:
|
||||
'@rushstack/terminal': 0.15.3(@types/node@24.9.2)
|
||||
'@rushstack/terminal': 0.15.3(@types/node@24.10.0)
|
||||
'@types/argparse': 1.0.38
|
||||
argparse: 1.0.10
|
||||
string-argv: 0.3.2
|
||||
@@ -20315,7 +20315,7 @@ snapshots:
|
||||
|
||||
'@types/appdmg@0.5.5':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@types/archiver@7.0.0':
|
||||
@@ -20333,11 +20333,11 @@ snapshots:
|
||||
'@types/body-parser@1.19.6':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/bonjour@3.5.13':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/bootstrap@5.2.10':
|
||||
dependencies:
|
||||
@@ -20351,7 +20351,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.4
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/chai@5.2.2':
|
||||
@@ -20376,11 +20376,11 @@ snapshots:
|
||||
'@types/connect-history-api-fallback@1.5.4':
|
||||
dependencies:
|
||||
'@types/express-serve-static-core': 5.1.0
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/cookie-parser@1.4.10(@types/express@5.0.5)':
|
||||
dependencies:
|
||||
@@ -20393,7 +20393,7 @@ snapshots:
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/cssnano@5.1.3(postcss@8.5.6)':
|
||||
dependencies:
|
||||
@@ -20552,7 +20552,7 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@5.1.0':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.5
|
||||
@@ -20587,7 +20587,7 @@ snapshots:
|
||||
|
||||
'@types/fs-extra@9.0.13':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@types/geojson-vt@3.2.5':
|
||||
@@ -20599,7 +20599,7 @@ snapshots:
|
||||
'@types/glob@7.2.0':
|
||||
dependencies:
|
||||
'@types/minimatch': 5.1.2
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
@@ -20613,7 +20613,7 @@ snapshots:
|
||||
|
||||
'@types/http-proxy@1.17.16':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/ini@4.1.1': {}
|
||||
|
||||
@@ -20627,11 +20627,11 @@ snapshots:
|
||||
|
||||
'@types/jsonfile@6.1.4':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/keyv@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/leaflet-gpx@1.3.8':
|
||||
dependencies:
|
||||
@@ -20681,11 +20681,11 @@ snapshots:
|
||||
|
||||
'@types/mute-stream@0.0.4':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/node-forge@1.3.14':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/node@16.9.1': {}
|
||||
|
||||
@@ -20717,7 +20717,7 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@24.9.2':
|
||||
'@types/node@24.10.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
@@ -20745,13 +20745,13 @@ snapshots:
|
||||
|
||||
'@types/readdir-glob@1.1.5':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/resolve@1.20.2': {}
|
||||
|
||||
'@types/responselike@1.0.3':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/retry@0.12.2': {}
|
||||
|
||||
@@ -20770,7 +20770,7 @@ snapshots:
|
||||
'@types/send@0.17.5':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/serve-favicon@2.5.7':
|
||||
dependencies:
|
||||
@@ -20783,7 +20783,7 @@ snapshots:
|
||||
'@types/serve-static@1.15.10':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.4
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
'@types/send': 0.17.5
|
||||
|
||||
'@types/serve-static@2.2.0':
|
||||
@@ -20797,7 +20797,7 @@ snapshots:
|
||||
|
||||
'@types/sockjs@0.3.36':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/statuses@2.0.6':
|
||||
optional: true
|
||||
@@ -20812,7 +20812,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/cookiejar': 2.1.5
|
||||
'@types/methods': 1.1.4
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
form-data: 4.0.4
|
||||
|
||||
'@types/supercluster@7.1.3':
|
||||
@@ -20828,7 +20828,7 @@ snapshots:
|
||||
|
||||
'@types/through2@2.0.41':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
|
||||
'@types/tmp@0.2.6': {}
|
||||
|
||||
@@ -20866,7 +20866,7 @@ snapshots:
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
@@ -20995,16 +20995,16 @@ snapshots:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@vitest/browser@3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
|
||||
'@vitest/browser@3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
|
||||
dependencies:
|
||||
'@testing-library/dom': 10.4.0
|
||||
'@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0)
|
||||
'@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@vitest/utils': 3.2.4
|
||||
magic-string: 0.30.18
|
||||
sirv: 3.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
optionalDependencies:
|
||||
playwright: 1.56.1
|
||||
@@ -21027,7 +21027,7 @@ snapshots:
|
||||
magicast: 0.3.5
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -21046,9 +21046,9 @@ snapshots:
|
||||
std-env: 3.9.0
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
optionalDependencies:
|
||||
'@vitest/browser': 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/browser': 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -21060,14 +21060,14 @@ snapshots:
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.4(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
'@vitest/mocker@3.2.4(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.18
|
||||
optionalDependencies:
|
||||
msw: 2.7.5(@types/node@24.9.2)(typescript@5.9.3)
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
msw: 2.7.5(@types/node@24.10.0)(typescript@5.9.3)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
@@ -21098,7 +21098,7 @@ snapshots:
|
||||
sirv: 3.0.1
|
||||
tinyglobby: 0.2.15
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
'@vitest/utils@3.2.4':
|
||||
dependencies:
|
||||
@@ -23206,7 +23206,7 @@ snapshots:
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
|
||||
debounce@2.2.0: {}
|
||||
debounce@3.0.0: {}
|
||||
|
||||
debug@2.6.9:
|
||||
dependencies:
|
||||
@@ -23693,7 +23693,7 @@ snapshots:
|
||||
engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
'@types/cors': 2.8.19
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
@@ -25870,13 +25870,13 @@ snapshots:
|
||||
|
||||
jest-worker@26.6.2:
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
jest-worker@27.5.1:
|
||||
dependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
@@ -27298,12 +27298,12 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3):
|
||||
msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@bundled-es-modules/cookie': 2.0.1
|
||||
'@bundled-es-modules/statuses': 1.0.1
|
||||
'@bundled-es-modules/tough-cookie': 0.1.6
|
||||
'@inquirer/confirm': 5.1.19(@types/node@24.9.2)
|
||||
'@inquirer/confirm': 5.1.19(@types/node@24.10.0)
|
||||
'@mswjs/interceptors': 0.37.6
|
||||
'@open-draft/deferred-promise': 2.2.0
|
||||
'@open-draft/until': 2.1.0
|
||||
@@ -28967,7 +28967,7 @@ snapshots:
|
||||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
long: 5.3.2
|
||||
|
||||
protocol-buffers-schema@3.6.0: {}
|
||||
@@ -29537,11 +29537,11 @@ snapshots:
|
||||
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.29
|
||||
optional: true
|
||||
|
||||
rollup-plugin-stats@1.5.1(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
rollup-plugin-stats@1.5.2(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
optionalDependencies:
|
||||
rolldown: 1.0.0-beta.29
|
||||
rollup: 4.52.0
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
rollup-plugin-styles@4.0.0(rollup@4.40.0):
|
||||
dependencies:
|
||||
@@ -29570,13 +29570,13 @@ snapshots:
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.40.0)
|
||||
rollup: 4.40.0
|
||||
|
||||
rollup-plugin-webpack-stats@2.1.6(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
rollup-plugin-webpack-stats@2.1.7(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
rollup-plugin-stats: 1.5.1(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
rollup-plugin-stats: 1.5.2(rolldown@1.0.0-beta.29)(rollup@4.52.0)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
optionalDependencies:
|
||||
rolldown: 1.0.0-beta.29
|
||||
rollup: 4.52.0
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
rollup@4.40.0:
|
||||
dependencies:
|
||||
@@ -31140,14 +31140,14 @@ snapshots:
|
||||
typescript: 5.0.4
|
||||
webpack: 5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.25.12)
|
||||
|
||||
ts-node@10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.0.4):
|
||||
ts-node@10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.0.4):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.11
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
acorn: 8.15.0
|
||||
acorn-walk: 8.3.4
|
||||
arg: 4.1.3
|
||||
@@ -31160,14 +31160,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.11.29(@swc/helpers@0.5.17)
|
||||
|
||||
ts-node@10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.9.2)(typescript@5.9.3):
|
||||
ts-node@10.9.2(@swc/core@1.11.29(@swc/helpers@0.5.17))(@types/node@24.10.0)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.11
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
acorn: 8.15.0
|
||||
acorn-walk: 8.3.4
|
||||
arg: 4.1.3
|
||||
@@ -31561,13 +31561,13 @@ snapshots:
|
||||
'@types/unist': 3.0.3
|
||||
vfile-message: 4.0.2
|
||||
|
||||
vite-node@3.2.4(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
vite-node@3.2.4(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.3(supports-color@6.0.0)
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@@ -31582,9 +31582,9 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-plugin-dts@4.5.4(@types/node@24.9.2)(rollup@4.52.0)(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
vite-plugin-dts@4.5.4(@types/node@24.10.0)(rollup@4.52.0)(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
'@microsoft/api-extractor': 7.52.8(@types/node@24.9.2)
|
||||
'@microsoft/api-extractor': 7.52.8(@types/node@24.10.0)
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.52.0)
|
||||
'@volar/typescript': 2.4.13
|
||||
'@vue/language-core': 2.2.0(typescript@5.9.3)
|
||||
@@ -31595,27 +31595,27 @@ snapshots:
|
||||
magic-string: 0.30.17
|
||||
typescript: 5.9.3
|
||||
optionalDependencies:
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite-plugin-static-copy@3.1.4(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
vite-plugin-static-copy@3.1.4(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
p-map: 7.0.3
|
||||
picocolors: 1.1.1
|
||||
tinyglobby: 0.2.15
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
vite-plugin-svgo@2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
vite-plugin-svgo@2.0.0(typescript@5.9.3)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
svgo: 3.3.2
|
||||
typescript: 5.9.3
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
vite-prerender-plugin@0.5.11(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
vite-prerender-plugin@0.5.11(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
kolorist: 1.8.0
|
||||
magic-string: 0.30.18
|
||||
@@ -31623,9 +31623,9 @@ snapshots:
|
||||
simple-code-frame: 1.3.0
|
||||
source-map: 0.7.6
|
||||
stack-trace: 1.0.0-pre2
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
dependencies:
|
||||
esbuild: 0.25.11
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -31634,7 +31634,7 @@ snapshots:
|
||||
rollup: 4.52.0
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 24.9.2
|
||||
'@types/node': 24.10.0
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.6.1
|
||||
less: 4.1.3
|
||||
@@ -31645,11 +31645,11 @@ snapshots:
|
||||
tsx: 4.20.6
|
||||
yaml: 2.8.1
|
||||
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.9.2)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.1.3)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@vitest/mocker': 3.2.4(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
@@ -31667,13 +31667,13 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vite-node: 3.2.4(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 24.9.2
|
||||
'@vitest/browser': 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.9.2)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@types/node': 24.10.0
|
||||
'@vitest/browser': 3.2.4(bufferutil@4.0.9)(msw@2.7.5(@types/node@24.10.0)(typescript@5.9.3))(playwright@1.56.1)(utf-8-validate@6.0.5)(vite@7.1.12(@types/node@24.10.0)(jiti@2.6.1)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)(webdriverio@9.20.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))
|
||||
'@vitest/ui': 3.2.4(vitest@3.2.4)
|
||||
happy-dom: 20.0.10
|
||||
jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
|
||||
Reference in New Issue
Block a user