@cyanheads/mcp-ts-core 0.9.15 → 0.9.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/CLAUDE.md +2 -1
- package/README.md +1 -1
- package/changelog/0.9.x/0.9.16.md +11 -0
- package/changelog/0.9.x/0.9.17.md +23 -0
- package/dist/logs/combined.log +8 -0
- package/dist/logs/error.log +4 -0
- package/dist/logs/interactions.log +0 -0
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts +9 -1
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
- package/dist/services/mirror/core/defineMirror.d.ts +62 -0
- package/dist/services/mirror/core/defineMirror.d.ts.map +1 -0
- package/dist/services/mirror/core/defineMirror.js +99 -0
- package/dist/services/mirror/core/defineMirror.js.map +1 -0
- package/dist/services/mirror/core/runner.d.ts +29 -0
- package/dist/services/mirror/core/runner.d.ts.map +1 -0
- package/dist/services/mirror/core/runner.js +95 -0
- package/dist/services/mirror/core/runner.js.map +1 -0
- package/dist/services/mirror/index.d.ts +18 -0
- package/dist/services/mirror/index.d.ts.map +1 -0
- package/dist/services/mirror/index.js +17 -0
- package/dist/services/mirror/index.js.map +1 -0
- package/dist/services/mirror/sqlite/handle.d.ts +48 -0
- package/dist/services/mirror/sqlite/handle.d.ts.map +1 -0
- package/dist/services/mirror/sqlite/handle.js +105 -0
- package/dist/services/mirror/sqlite/handle.js.map +1 -0
- package/dist/services/mirror/sqlite/schema.d.ts +38 -0
- package/dist/services/mirror/sqlite/schema.d.ts.map +1 -0
- package/dist/services/mirror/sqlite/schema.js +124 -0
- package/dist/services/mirror/sqlite/schema.js.map +1 -0
- package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts +27 -0
- package/dist/services/mirror/sqlite/sqliteMirrorStore.d.ts.map +1 -0
- package/dist/services/mirror/sqlite/sqliteMirrorStore.js +286 -0
- package/dist/services/mirror/sqlite/sqliteMirrorStore.js.map +1 -0
- package/dist/services/mirror/types.d.ts +211 -0
- package/dist/services/mirror/types.d.ts.map +1 -0
- package/dist/services/mirror/types.js +9 -0
- package/dist/services/mirror/types.js.map +1 -0
- package/package.json +11 -2
- package/skills/api-mirror/SKILL.md +103 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,4FAA4F;AAC5F,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,IAAI,GAAG,OAAO;IAC7C,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;IACnC,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC7C,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACnF;AAED,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,IAAI,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC5D,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;CAChC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA0CD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,YAAY,CAAC,CAuBvB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Runtime-agnostic SQLite handle for the mirror store. Uses the
|
|
3
|
+
* built-in `bun:sqlite` driver under Bun and the `better-sqlite3` optional peer
|
|
4
|
+
* dependency on Node — both exposed through one synchronous handle interface
|
|
5
|
+
* (the intersection of the two driver APIs).
|
|
6
|
+
*
|
|
7
|
+
* The drivers are loaded via variable-specifier dynamic imports so the
|
|
8
|
+
* framework typechecks and builds without `bun-types` in scope or
|
|
9
|
+
* `better-sqlite3` installed; both resolve at runtime on the matching runtime.
|
|
10
|
+
* @module services/mirror/sqlite/handle
|
|
11
|
+
*/
|
|
12
|
+
import { mkdir } from 'node:fs/promises';
|
|
13
|
+
import { dirname, resolve as resolvePath } from 'node:path';
|
|
14
|
+
import { configurationError, databaseError, McpError } from '../../../types-global/errors.js';
|
|
15
|
+
import { runtimeCaps } from '../../../utils/internal/runtime.js';
|
|
16
|
+
/**
|
|
17
|
+
* Variable-specifier module IDs. Annotating as `string` (not the string
|
|
18
|
+
* literal) stops `tsc` from statically resolving the module, so the framework
|
|
19
|
+
* compiles without `bun-types` or `better-sqlite3` present. Each resolves at
|
|
20
|
+
* runtime only on the runtime that ships it.
|
|
21
|
+
*/
|
|
22
|
+
const BUN_SQLITE_SPECIFIER = 'bun:sqlite';
|
|
23
|
+
const BETTER_SQLITE3_SPECIFIER = 'better-sqlite3';
|
|
24
|
+
/**
|
|
25
|
+
* Open (or create) a SQLite database at `path`, picking the driver for the
|
|
26
|
+
* current runtime. Creates the parent directory, enables WAL, and sets
|
|
27
|
+
* `busy_timeout` so a refresh writer and reader processes coexist without
|
|
28
|
+
* spurious `database is locked` errors.
|
|
29
|
+
*
|
|
30
|
+
* Throws `ConfigurationError` on Node when `better-sqlite3` is not installed,
|
|
31
|
+
* and `DatabaseError` for any other open failure.
|
|
32
|
+
*/
|
|
33
|
+
export async function openSqliteHandle(path, options = {}) {
|
|
34
|
+
await mkdir(dirname(resolvePath(path)), { recursive: true });
|
|
35
|
+
const busyTimeoutMs = options.busyTimeoutMs ?? 5000;
|
|
36
|
+
let handle;
|
|
37
|
+
try {
|
|
38
|
+
handle = runtimeCaps.isBun
|
|
39
|
+
? /* istanbul ignore next -- Bun branch; the test suite runs Vitest workers under Node */
|
|
40
|
+
await openBunHandle(path)
|
|
41
|
+
: await openBetterSqlite3Handle(path);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
// The Node path throws a ConfigurationError when better-sqlite3 is absent —
|
|
45
|
+
// preserve it rather than masking it as a generic open failure.
|
|
46
|
+
if (err instanceof McpError)
|
|
47
|
+
throw err;
|
|
48
|
+
throw databaseError(`Failed to open mirror store at ${path}`, { path }, { cause: err });
|
|
49
|
+
}
|
|
50
|
+
// Connection pragmas. WAL allows one writer + concurrent readers; NORMAL
|
|
51
|
+
// synchronous is the WAL-recommended durability/throughput balance.
|
|
52
|
+
handle.exec(`PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON; PRAGMA busy_timeout = ${busyTimeoutMs};`);
|
|
53
|
+
return handle;
|
|
54
|
+
}
|
|
55
|
+
/* istanbul ignore next -- Bun-only driver; the test suite runs Vitest workers under Node */
|
|
56
|
+
async function openBunHandle(path) {
|
|
57
|
+
const mod = (await import(BUN_SQLITE_SPECIFIER));
|
|
58
|
+
const db = new mod.Database(path, { create: true });
|
|
59
|
+
return {
|
|
60
|
+
close: () => {
|
|
61
|
+
db.close();
|
|
62
|
+
},
|
|
63
|
+
exec: (sql) => {
|
|
64
|
+
db.exec(sql);
|
|
65
|
+
},
|
|
66
|
+
prepare: (sql) => {
|
|
67
|
+
const stmt = db.prepare(sql);
|
|
68
|
+
return {
|
|
69
|
+
all: (...params) => stmt.all(...params),
|
|
70
|
+
get: (...params) => stmt.get(...params),
|
|
71
|
+
run: (...params) => stmt.run(...params),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
transaction: (fn) => db.transaction(fn)(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function openBetterSqlite3Handle(path) {
|
|
78
|
+
let mod;
|
|
79
|
+
try {
|
|
80
|
+
mod = (await import(BETTER_SQLITE3_SPECIFIER));
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
/* istanbul ignore next -- missing-dep path; better-sqlite3 is installed in the test env */
|
|
84
|
+
throw configurationError('Install "better-sqlite3" to use the SQLite mirror store on Node: bun add better-sqlite3', { path }, { cause: err });
|
|
85
|
+
}
|
|
86
|
+
const db = new mod.default(path);
|
|
87
|
+
return {
|
|
88
|
+
close: () => {
|
|
89
|
+
db.close();
|
|
90
|
+
},
|
|
91
|
+
exec: (sql) => {
|
|
92
|
+
db.exec(sql);
|
|
93
|
+
},
|
|
94
|
+
prepare: (sql) => {
|
|
95
|
+
const stmt = db.prepare(sql);
|
|
96
|
+
return {
|
|
97
|
+
all: (...params) => stmt.all(...params),
|
|
98
|
+
get: (...params) => stmt.get(...params),
|
|
99
|
+
run: (...params) => stmt.run(...params),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
transaction: (fn) => db.transaction(fn)(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle.js","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/handle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DDL generation for the SQLite mirror store. Builds the primary
|
|
3
|
+
* table, secondary indexes, an optional FTS5 external-content index with sync
|
|
4
|
+
* triggers, the single-row `mirror_sync_state` table, and the `schema_version`
|
|
5
|
+
* table — all from a declarative column/FTS spec.
|
|
6
|
+
* @module services/mirror/sqlite/schema
|
|
7
|
+
*/
|
|
8
|
+
/** Default FTS5 tokenizer — Unicode-aware, diacritic-stripping, no stemming. */
|
|
9
|
+
export declare const DEFAULT_FTS_TOKENIZER = "unicode61 remove_diacritics 2";
|
|
10
|
+
/** Declarative schema spec consumed by {@link buildSchemaSql}. */
|
|
11
|
+
export interface SchemaSpec {
|
|
12
|
+
/** Column name → SQLite type/declaration (e.g. `'TEXT'`, `'INTEGER NOT NULL'`). */
|
|
13
|
+
columns: Record<string, string>;
|
|
14
|
+
/** Columns to index in FTS5 (subset of `columns`). Omit/empty to skip FTS. */
|
|
15
|
+
fts?: string[];
|
|
16
|
+
/** FTS5 tokenizer directive. Defaults to {@link DEFAULT_FTS_TOKENIZER}. */
|
|
17
|
+
ftsTokenizer?: string;
|
|
18
|
+
/** Secondary indexes over declared columns. */
|
|
19
|
+
indexes?: Array<{
|
|
20
|
+
name?: string;
|
|
21
|
+
columns: string[];
|
|
22
|
+
}>;
|
|
23
|
+
/** Primary-key column — receives `PRIMARY KEY NOT NULL` and drives upsert/tombstone. */
|
|
24
|
+
primaryKey: string;
|
|
25
|
+
/** Primary table name. */
|
|
26
|
+
table: string;
|
|
27
|
+
}
|
|
28
|
+
/** Validate a {@link SchemaSpec} and return its FTS column set (validated). */
|
|
29
|
+
export declare function validateSchemaSpec(spec: SchemaSpec): {
|
|
30
|
+
ftsColumns: string[];
|
|
31
|
+
tokenizer: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Build the full idempotent DDL for a mirror store. Safe to run on every open
|
|
35
|
+
* (`CREATE … IF NOT EXISTS` throughout). Returns one SQL string.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildSchemaSql(spec: SchemaSpec): string;
|
|
38
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAErE,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACtD,wFAAwF;IACxF,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAaD,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CA4ChG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAuCvD"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DDL generation for the SQLite mirror store. Builds the primary
|
|
3
|
+
* table, secondary indexes, an optional FTS5 external-content index with sync
|
|
4
|
+
* triggers, the single-row `mirror_sync_state` table, and the `schema_version`
|
|
5
|
+
* table — all from a declarative column/FTS spec.
|
|
6
|
+
* @module services/mirror/sqlite/schema
|
|
7
|
+
*/
|
|
8
|
+
import { configurationError } from '../../../types-global/errors.js';
|
|
9
|
+
/** Default FTS5 tokenizer — Unicode-aware, diacritic-stripping, no stemming. */
|
|
10
|
+
export const DEFAULT_FTS_TOKENIZER = 'unicode61 remove_diacritics 2';
|
|
11
|
+
const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
12
|
+
function assertIdentifier(value, role) {
|
|
13
|
+
if (!IDENTIFIER_RE.test(value)) {
|
|
14
|
+
throw configurationError(`Invalid mirror ${role} identifier "${value}" — must match ${IDENTIFIER_RE.source}.`, { value, role });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Validate a {@link SchemaSpec} and return its FTS column set (validated). */
|
|
18
|
+
export function validateSchemaSpec(spec) {
|
|
19
|
+
assertIdentifier(spec.table, 'table');
|
|
20
|
+
const columnNames = Object.keys(spec.columns);
|
|
21
|
+
if (columnNames.length === 0) {
|
|
22
|
+
throw configurationError('Mirror schema must declare at least one column.', {
|
|
23
|
+
table: spec.table,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
for (const col of columnNames)
|
|
27
|
+
assertIdentifier(col, 'column');
|
|
28
|
+
if (!(spec.primaryKey in spec.columns)) {
|
|
29
|
+
throw configurationError(`Mirror primaryKey "${spec.primaryKey}" is not among the declared columns.`, { primaryKey: spec.primaryKey, columns: columnNames });
|
|
30
|
+
}
|
|
31
|
+
const ftsColumns = spec.fts ?? [];
|
|
32
|
+
for (const col of ftsColumns) {
|
|
33
|
+
if (!(col in spec.columns)) {
|
|
34
|
+
throw configurationError(`Mirror FTS column "${col}" is not a declared column.`, {
|
|
35
|
+
column: col,
|
|
36
|
+
columns: columnNames,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const tokenizer = spec.ftsTokenizer ?? DEFAULT_FTS_TOKENIZER;
|
|
41
|
+
if (tokenizer.includes('"')) {
|
|
42
|
+
throw configurationError('Mirror ftsTokenizer must not contain a double-quote character.', {
|
|
43
|
+
tokenizer,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
for (const idx of spec.indexes ?? []) {
|
|
47
|
+
for (const col of idx.columns) {
|
|
48
|
+
if (!(col in spec.columns)) {
|
|
49
|
+
throw configurationError(`Mirror index column "${col}" is not a declared column.`, {
|
|
50
|
+
column: col,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { ftsColumns, tokenizer };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build the full idempotent DDL for a mirror store. Safe to run on every open
|
|
59
|
+
* (`CREATE … IF NOT EXISTS` throughout). Returns one SQL string.
|
|
60
|
+
*/
|
|
61
|
+
export function buildSchemaSql(spec) {
|
|
62
|
+
const { ftsColumns, tokenizer } = validateSchemaSpec(spec);
|
|
63
|
+
const t = spec.table;
|
|
64
|
+
const columnDefs = Object.entries(spec.columns).map(([name, decl]) => {
|
|
65
|
+
const base = `${name} ${decl}`.trim();
|
|
66
|
+
return name === spec.primaryKey ? `${name} ${decl} PRIMARY KEY NOT NULL`.trim() : base;
|
|
67
|
+
});
|
|
68
|
+
const parts = [
|
|
69
|
+
`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL);`,
|
|
70
|
+
`CREATE TABLE IF NOT EXISTS ${t} (\n ${columnDefs.join(',\n ')}\n);`,
|
|
71
|
+
];
|
|
72
|
+
for (const idx of spec.indexes ?? []) {
|
|
73
|
+
const name = idx.name ?? `${t}_${idx.columns.join('_')}_idx`;
|
|
74
|
+
assertIdentifier(name, 'index');
|
|
75
|
+
parts.push(`CREATE INDEX IF NOT EXISTS ${name} ON ${t}(${idx.columns.join(', ')});`);
|
|
76
|
+
}
|
|
77
|
+
if (ftsColumns.length > 0) {
|
|
78
|
+
parts.push(buildFtsSql(t, ftsColumns, tokenizer));
|
|
79
|
+
}
|
|
80
|
+
parts.push(`CREATE TABLE IF NOT EXISTS mirror_sync_state (
|
|
81
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
82
|
+
status TEXT NOT NULL,
|
|
83
|
+
cursor TEXT,
|
|
84
|
+
checkpoint TEXT,
|
|
85
|
+
started_at TEXT,
|
|
86
|
+
completed_at TEXT,
|
|
87
|
+
total INTEGER,
|
|
88
|
+
error TEXT
|
|
89
|
+
);`, `INSERT OR IGNORE INTO mirror_sync_state(id, status) VALUES (1, 'pending');`);
|
|
90
|
+
return parts.join('\n\n');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build the FTS5 external-content virtual table and its insert/delete/update
|
|
94
|
+
* sync triggers. External content (`content=<table>`) stores only the index, not
|
|
95
|
+
* a copy of the text; the triggers keep it in lock-step with the base table,
|
|
96
|
+
* using the `('delete', …)` command-row form required for external-content
|
|
97
|
+
* deletes.
|
|
98
|
+
*/
|
|
99
|
+
function buildFtsSql(table, ftsColumns, tokenizer) {
|
|
100
|
+
const cols = ftsColumns.join(', ');
|
|
101
|
+
const newCols = ftsColumns.map((c) => `new.${c}`).join(', ');
|
|
102
|
+
const oldCols = ftsColumns.map((c) => `old.${c}`).join(', ');
|
|
103
|
+
const fts = `${table}_fts`;
|
|
104
|
+
return `CREATE VIRTUAL TABLE IF NOT EXISTS ${fts} USING fts5(
|
|
105
|
+
${ftsColumns.join(',\n ')},
|
|
106
|
+
content='${table}',
|
|
107
|
+
content_rowid='rowid',
|
|
108
|
+
tokenize="${tokenizer}"
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_ai AFTER INSERT ON ${table} BEGIN
|
|
112
|
+
INSERT INTO ${fts}(rowid, ${cols}) VALUES (new.rowid, ${newCols});
|
|
113
|
+
END;
|
|
114
|
+
|
|
115
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_ad AFTER DELETE ON ${table} BEGIN
|
|
116
|
+
INSERT INTO ${fts}(${fts}, rowid, ${cols}) VALUES ('delete', old.rowid, ${oldCols});
|
|
117
|
+
END;
|
|
118
|
+
|
|
119
|
+
CREATE TRIGGER IF NOT EXISTS ${table}_au AFTER UPDATE ON ${table} BEGIN
|
|
120
|
+
INSERT INTO ${fts}(${fts}, rowid, ${cols}) VALUES ('delete', old.rowid, ${oldCols});
|
|
121
|
+
INSERT INTO ${fts}(rowid, ${cols}) VALUES (new.rowid, ${newCols});
|
|
122
|
+
END;`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Embedded-SQLite implementation of {@link MirrorStore}. Owns the
|
|
3
|
+
* primary table, optional FTS5 index, the `mirror_sync_state` row, schema
|
|
4
|
+
* versioning + migration runner, generic flat queries, and the raw-handle
|
|
5
|
+
* escape hatch. Lazy-opens on first use (memoized) and is then backed by
|
|
6
|
+
* synchronous driver calls.
|
|
7
|
+
* @module services/mirror/sqlite/sqliteMirrorStore
|
|
8
|
+
*/
|
|
9
|
+
import type { Migration, MirrorStore } from '../types.js';
|
|
10
|
+
import { type SchemaSpec } from './schema.js';
|
|
11
|
+
/** Configuration for {@link sqliteMirrorStore}. */
|
|
12
|
+
export interface SqliteMirrorStoreSpec extends SchemaSpec {
|
|
13
|
+
/** `PRAGMA busy_timeout` in ms. Default 5000. */
|
|
14
|
+
busyTimeoutMs?: number;
|
|
15
|
+
/** Migrations applied in order when the stored version is lower than `version`. */
|
|
16
|
+
migrations?: Migration[];
|
|
17
|
+
/** Filesystem path to the SQLite database (created if absent). */
|
|
18
|
+
path: string;
|
|
19
|
+
/** Current schema version. Default 1. */
|
|
20
|
+
version?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a SQLite-backed {@link MirrorStore} from a declarative spec. The store
|
|
24
|
+
* is not opened until the first method call.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sqliteMirrorStore(spec: SqliteMirrorStoreSpec): MirrorStore;
|
|
27
|
+
//# sourceMappingURL=sqliteMirrorStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteMirrorStore.d.ts","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/sqliteMirrorStore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAEV,SAAS,EAET,WAAW,EAMZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAkB,KAAK,UAAU,EAAsB,MAAM,aAAa,CAAC;AAElF,mDAAmD;AACnD,MAAM,WAAW,qBAAsB,SAAQ,UAAU;IACvD,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,WAAW,CA0L1E"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Embedded-SQLite implementation of {@link MirrorStore}. Owns the
|
|
3
|
+
* primary table, optional FTS5 index, the `mirror_sync_state` row, schema
|
|
4
|
+
* versioning + migration runner, generic flat queries, and the raw-handle
|
|
5
|
+
* escape hatch. Lazy-opens on first use (memoized) and is then backed by
|
|
6
|
+
* synchronous driver calls.
|
|
7
|
+
* @module services/mirror/sqlite/sqliteMirrorStore
|
|
8
|
+
*/
|
|
9
|
+
import { databaseError, validationError } from '../../../types-global/errors.js';
|
|
10
|
+
import { openSqliteHandle } from './handle.js';
|
|
11
|
+
import { buildSchemaSql, validateSchemaSpec } from './schema.js';
|
|
12
|
+
const SQL_OP = {
|
|
13
|
+
eq: '=',
|
|
14
|
+
ne: '!=',
|
|
15
|
+
gt: '>',
|
|
16
|
+
gte: '>=',
|
|
17
|
+
lt: '<',
|
|
18
|
+
lte: '<=',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Create a SQLite-backed {@link MirrorStore} from a declarative spec. The store
|
|
22
|
+
* is not opened until the first method call.
|
|
23
|
+
*/
|
|
24
|
+
export function sqliteMirrorStore(spec) {
|
|
25
|
+
// Fail fast on a malformed spec at construction time, not first query.
|
|
26
|
+
const { ftsColumns } = validateSchemaSpec(spec);
|
|
27
|
+
const allColumns = Object.keys(spec.columns);
|
|
28
|
+
const ftsTable = `${spec.table}_fts`;
|
|
29
|
+
let opened;
|
|
30
|
+
let opening;
|
|
31
|
+
function open() {
|
|
32
|
+
if (opened)
|
|
33
|
+
return Promise.resolve(opened);
|
|
34
|
+
opening ??= (async () => {
|
|
35
|
+
const handleOptions = {
|
|
36
|
+
...(spec.busyTimeoutMs !== undefined && { busyTimeoutMs: spec.busyTimeoutMs }),
|
|
37
|
+
};
|
|
38
|
+
const handle = await openSqliteHandle(spec.path, handleOptions);
|
|
39
|
+
try {
|
|
40
|
+
handle.exec(buildSchemaSql(spec));
|
|
41
|
+
runMigrations(handle, spec.version ?? 1, spec.migrations ?? []);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
handle.close();
|
|
45
|
+
throw databaseError(`Failed to initialize mirror store at ${spec.path}`, { path: spec.path }, { cause: err });
|
|
46
|
+
}
|
|
47
|
+
opened = { handle, allColumns, hasFts: ftsColumns.length > 0 };
|
|
48
|
+
return opened;
|
|
49
|
+
})();
|
|
50
|
+
return opening;
|
|
51
|
+
}
|
|
52
|
+
function assertColumn(column, role) {
|
|
53
|
+
if (!allColumns.includes(column)) {
|
|
54
|
+
throw validationError(`Unknown mirror ${role} column "${column}".`, {
|
|
55
|
+
column,
|
|
56
|
+
columns: allColumns,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
async applyBatch(records, tombstones) {
|
|
62
|
+
if (records.length === 0 && tombstones.length === 0)
|
|
63
|
+
return;
|
|
64
|
+
const { handle } = await open();
|
|
65
|
+
const cols = allColumns;
|
|
66
|
+
const placeholders = cols.map(() => '?').join(', ');
|
|
67
|
+
const updates = cols
|
|
68
|
+
.filter((c) => c !== spec.primaryKey)
|
|
69
|
+
.map((c) => `${c} = excluded.${c}`)
|
|
70
|
+
.join(', ');
|
|
71
|
+
// No non-PK columns → a bare PK table; ON CONFLICT DO NOTHING keeps upsert idempotent.
|
|
72
|
+
const conflict = updates
|
|
73
|
+
? `ON CONFLICT(${spec.primaryKey}) DO UPDATE SET ${updates}`
|
|
74
|
+
: `ON CONFLICT(${spec.primaryKey}) DO NOTHING`;
|
|
75
|
+
const upsert = handle.prepare(`INSERT INTO ${spec.table}(${cols.join(', ')}) VALUES (${placeholders}) ${conflict}`);
|
|
76
|
+
const remove = handle.prepare(`DELETE FROM ${spec.table} WHERE ${spec.primaryKey} = ?`);
|
|
77
|
+
handle.transaction(() => {
|
|
78
|
+
for (const record of records) {
|
|
79
|
+
upsert.run(...cols.map((c) => record[c] ?? null));
|
|
80
|
+
}
|
|
81
|
+
for (const id of tombstones)
|
|
82
|
+
remove.run(id);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
async query(options) {
|
|
86
|
+
const store = await open();
|
|
87
|
+
if (options.match && !store.hasFts) {
|
|
88
|
+
throw validationError('This mirror has no FTS index; `match` is not supported.', {
|
|
89
|
+
table: spec.table,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
for (const f of options.filters ?? [])
|
|
93
|
+
assertColumn(f.column, 'filter');
|
|
94
|
+
if (options.sort && options.sort !== 'relevance')
|
|
95
|
+
assertColumn(options.sort.column, 'sort');
|
|
96
|
+
const { handle } = store;
|
|
97
|
+
const { clauses: filterClauses, params: filterParams } = buildFilterClauses(options.filters ?? []);
|
|
98
|
+
const relevance = options.sort === 'relevance' && Boolean(options.match);
|
|
99
|
+
// COUNT never needs bm25, so the FTS predicate is always the rowid
|
|
100
|
+
// subquery form — no join, no ordering.
|
|
101
|
+
const countClauses = [...filterClauses];
|
|
102
|
+
const countParams = [...filterParams];
|
|
103
|
+
if (options.match) {
|
|
104
|
+
countClauses.unshift(`${spec.table}.rowid IN (SELECT rowid FROM ${ftsTable} WHERE ${ftsTable} MATCH ?)`);
|
|
105
|
+
countParams.unshift(options.match);
|
|
106
|
+
}
|
|
107
|
+
const countWhere = countClauses.length > 0 ? `WHERE ${countClauses.join(' AND ')}` : '';
|
|
108
|
+
const total = handle
|
|
109
|
+
.prepare(`SELECT COUNT(*) AS n FROM ${spec.table} ${countWhere}`)
|
|
110
|
+
.get(...countParams)?.n ?? 0;
|
|
111
|
+
// Row fetch. For relevance sort, apply MATCH on the JOINed FTS table so
|
|
112
|
+
// `bm25()` is scored in that MATCH's context; the subquery form leaves
|
|
113
|
+
// bm25 with no query to score against. Other sorts keep the subquery form
|
|
114
|
+
// (no join needed). `ORDER BY` columns are table-qualified to avoid a
|
|
115
|
+
// `rowid` ambiguity once the FTS table is joined in.
|
|
116
|
+
const rowClauses = [...filterClauses];
|
|
117
|
+
const rowParams = [...filterParams];
|
|
118
|
+
let join = '';
|
|
119
|
+
if (options.match) {
|
|
120
|
+
if (relevance) {
|
|
121
|
+
join = `JOIN ${ftsTable} ON ${spec.table}.rowid = ${ftsTable}.rowid`;
|
|
122
|
+
rowClauses.unshift(`${ftsTable} MATCH ?`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
rowClauses.unshift(`${spec.table}.rowid IN (SELECT rowid FROM ${ftsTable} WHERE ${ftsTable} MATCH ?)`);
|
|
126
|
+
}
|
|
127
|
+
rowParams.unshift(options.match);
|
|
128
|
+
}
|
|
129
|
+
const rowWhere = rowClauses.length > 0 ? `WHERE ${rowClauses.join(' AND ')}` : '';
|
|
130
|
+
const selectCols = store.allColumns.map((c) => `${spec.table}.${c}`).join(', ');
|
|
131
|
+
const orderBy = relevance
|
|
132
|
+
? `ORDER BY bm25(${ftsTable}) ASC`
|
|
133
|
+
: buildOrderBy(options.sort, spec.table);
|
|
134
|
+
const rows = handle
|
|
135
|
+
.prepare(`SELECT ${selectCols} FROM ${spec.table} ${join} ${rowWhere} ${orderBy} LIMIT ? OFFSET ?`)
|
|
136
|
+
.all(...rowParams, options.limit, options.offset);
|
|
137
|
+
return { rows, total };
|
|
138
|
+
},
|
|
139
|
+
async getByIds(ids) {
|
|
140
|
+
if (ids.length === 0)
|
|
141
|
+
return [];
|
|
142
|
+
const { handle } = await open();
|
|
143
|
+
const placeholders = ids.map(() => '?').join(', ');
|
|
144
|
+
const rows = handle
|
|
145
|
+
.prepare(`SELECT ${allColumns.join(', ')} FROM ${spec.table} WHERE ${spec.primaryKey} IN (${placeholders})`)
|
|
146
|
+
.all(...ids);
|
|
147
|
+
const byId = new Map(rows.map((r) => [r[spec.primaryKey], r]));
|
|
148
|
+
return ids.map((id) => byId.get(id)).filter((r) => r !== undefined);
|
|
149
|
+
},
|
|
150
|
+
async count() {
|
|
151
|
+
const { handle } = await open();
|
|
152
|
+
const row = handle.prepare(`SELECT COUNT(*) AS n FROM ${spec.table}`).get();
|
|
153
|
+
return row?.n ?? 0;
|
|
154
|
+
},
|
|
155
|
+
async readState() {
|
|
156
|
+
const { handle } = await open();
|
|
157
|
+
return readSyncState(handle);
|
|
158
|
+
},
|
|
159
|
+
async writeState(state) {
|
|
160
|
+
const { handle } = await open();
|
|
161
|
+
writeSyncState(handle, state);
|
|
162
|
+
},
|
|
163
|
+
async raw() {
|
|
164
|
+
return (await open()).handle;
|
|
165
|
+
},
|
|
166
|
+
async integrityCheck() {
|
|
167
|
+
const { handle } = await open();
|
|
168
|
+
const integrity = handle.prepare(`PRAGMA integrity_check`).all();
|
|
169
|
+
const quick = handle.prepare(`PRAGMA quick_check`).all();
|
|
170
|
+
const results = [
|
|
171
|
+
...integrity.map((r) => `integrity_check: ${r.integrity_check}`),
|
|
172
|
+
...quick.map((r) => `quick_check: ${r.quick_check}`),
|
|
173
|
+
];
|
|
174
|
+
return { ok: results.every((r) => r.endsWith('ok')), results };
|
|
175
|
+
},
|
|
176
|
+
close() {
|
|
177
|
+
if (opened) {
|
|
178
|
+
opened.handle.close();
|
|
179
|
+
opened = undefined;
|
|
180
|
+
opening = undefined;
|
|
181
|
+
}
|
|
182
|
+
return Promise.resolve();
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Query building
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
function buildFilterClauses(filters) {
|
|
190
|
+
const clauses = [];
|
|
191
|
+
const params = [];
|
|
192
|
+
for (const f of filters) {
|
|
193
|
+
if (f.op === 'in') {
|
|
194
|
+
const values = Array.isArray(f.value) ? f.value : [f.value];
|
|
195
|
+
if (values.length === 0) {
|
|
196
|
+
clauses.push('0'); // empty IN matches nothing
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
clauses.push(`${f.column} IN (${values.map(() => '?').join(', ')})`);
|
|
200
|
+
params.push(...values);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
clauses.push(`${f.column} ${SQL_OP[f.op]} ?`);
|
|
204
|
+
params.push(Array.isArray(f.value) ? (f.value[0] ?? null) : f.value);
|
|
205
|
+
}
|
|
206
|
+
return { clauses, params };
|
|
207
|
+
}
|
|
208
|
+
function buildOrderBy(sort, table) {
|
|
209
|
+
if (sort && sort !== 'relevance') {
|
|
210
|
+
return `ORDER BY ${table}.${sort.column} ${sort.direction === 'asc' ? 'ASC' : 'DESC'}`;
|
|
211
|
+
}
|
|
212
|
+
return `ORDER BY ${table}.rowid ASC`;
|
|
213
|
+
}
|
|
214
|
+
function readSyncState(handle) {
|
|
215
|
+
const row = handle
|
|
216
|
+
.prepare(`SELECT status, cursor, checkpoint, started_at, completed_at, total, error
|
|
217
|
+
FROM mirror_sync_state WHERE id = 1`)
|
|
218
|
+
.get();
|
|
219
|
+
if (!row)
|
|
220
|
+
return { status: 'pending' };
|
|
221
|
+
return {
|
|
222
|
+
status: row.status,
|
|
223
|
+
...(row.cursor != null && { cursor: row.cursor }),
|
|
224
|
+
...(row.checkpoint != null && { checkpoint: row.checkpoint }),
|
|
225
|
+
...(row.started_at != null && { startedAt: row.started_at }),
|
|
226
|
+
...(row.completed_at != null && { completedAt: row.completed_at }),
|
|
227
|
+
...(row.total != null && { total: row.total }),
|
|
228
|
+
...(row.error != null && { error: row.error }),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Persist sync state. `completed_at` and `total` are durable "last successful
|
|
233
|
+
* sync" markers — preserved via COALESCE when a write omits them — so an
|
|
234
|
+
* in-progress or failed refresh on top of a complete mirror keeps the
|
|
235
|
+
* completion marker that readiness keys off. Every other column is
|
|
236
|
+
* current-run progress, overwritten on each write.
|
|
237
|
+
*/
|
|
238
|
+
function writeSyncState(handle, state) {
|
|
239
|
+
handle
|
|
240
|
+
.prepare(`UPDATE mirror_sync_state
|
|
241
|
+
SET status = ?, cursor = ?, checkpoint = ?, started_at = ?,
|
|
242
|
+
completed_at = COALESCE(?, completed_at),
|
|
243
|
+
total = COALESCE(?, total),
|
|
244
|
+
error = ?
|
|
245
|
+
WHERE id = 1`)
|
|
246
|
+
.run(state.status, state.cursor ?? null, state.checkpoint ?? null, state.startedAt ?? null, state.completedAt ?? null, state.total ?? null, state.error ?? null);
|
|
247
|
+
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Schema versioning
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
/**
|
|
252
|
+
* Apply pending migrations. A brand-new database (no `schema_version` row) was
|
|
253
|
+
* just built at the current schema by the DDL, so it is stamped to `target`
|
|
254
|
+
* without running migrations (there is no older data to transform). An existing
|
|
255
|
+
* database at a lower version runs each migration with `stored < version <=
|
|
256
|
+
* target`, in order, stamping the version after each.
|
|
257
|
+
*/
|
|
258
|
+
function runMigrations(handle, target, migrations) {
|
|
259
|
+
const stored = handle.prepare(`SELECT MAX(version) AS version FROM schema_version`).get()
|
|
260
|
+
?.version ?? 0;
|
|
261
|
+
if (stored === 0) {
|
|
262
|
+
stampVersion(handle, target);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (stored >= target)
|
|
266
|
+
return;
|
|
267
|
+
const pending = migrations
|
|
268
|
+
.filter((m) => m.version > stored && m.version <= target)
|
|
269
|
+
.sort((a, b) => a.version - b.version);
|
|
270
|
+
for (const m of pending) {
|
|
271
|
+
handle.transaction(() => {
|
|
272
|
+
m.up(handle);
|
|
273
|
+
});
|
|
274
|
+
stampVersion(handle, m.version);
|
|
275
|
+
}
|
|
276
|
+
stampVersion(handle, target);
|
|
277
|
+
}
|
|
278
|
+
function stampVersion(handle, version) {
|
|
279
|
+
handle.transaction(() => {
|
|
280
|
+
handle.prepare(`DELETE FROM schema_version`).run();
|
|
281
|
+
handle
|
|
282
|
+
.prepare(`INSERT INTO schema_version(version, applied_at) VALUES (?, ?)`)
|
|
283
|
+
.run(version, new Date().toISOString());
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=sqliteMirrorStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteMirrorStore.js","sourceRoot":"","sources":["../../../../src/services/mirror/sqlite/sqliteMirrorStore.ts"],"names":[],"mappings":"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"}
|