@earendil-works/pi-session-backend-sqlite-node 0.84.0 → 0.84.2

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +10 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +10 -3
  5. package/dist/index.js.map +1 -1
  6. package/dist/sqlite/branch-cache.d.ts.map +1 -1
  7. package/dist/sqlite/branch-cache.js +12 -13
  8. package/dist/sqlite/branch-cache.js.map +1 -1
  9. package/dist/sqlite/index.d.ts +1 -0
  10. package/dist/sqlite/index.d.ts.map +1 -1
  11. package/dist/sqlite/index.js +1 -0
  12. package/dist/sqlite/index.js.map +1 -1
  13. package/dist/sqlite/migrations/001_initial.sql +9 -10
  14. package/dist/sqlite/migrations.d.ts.map +1 -1
  15. package/dist/sqlite/migrations.js +5 -4
  16. package/dist/sqlite/migrations.js.map +1 -1
  17. package/dist/sqlite/repo.d.ts.map +1 -1
  18. package/dist/sqlite/repo.js +60 -46
  19. package/dist/sqlite/repo.js.map +1 -1
  20. package/dist/sqlite/search-backend.d.ts +7 -2
  21. package/dist/sqlite/search-backend.d.ts.map +1 -1
  22. package/dist/sqlite/search-backend.js +60 -25
  23. package/dist/sqlite/search-backend.js.map +1 -1
  24. package/dist/sqlite/sql.d.ts +19 -0
  25. package/dist/sqlite/sql.d.ts.map +1 -0
  26. package/dist/sqlite/sql.js +58 -0
  27. package/dist/sqlite/sql.js.map +1 -0
  28. package/dist/sqlite/storage/branch-entries.d.ts +8 -2
  29. package/dist/sqlite/storage/branch-entries.d.ts.map +1 -1
  30. package/dist/sqlite/storage/branch-entries.js +87 -61
  31. package/dist/sqlite/storage/branch-entries.js.map +1 -1
  32. package/dist/sqlite/storage/branch-tips.d.ts.map +1 -1
  33. package/dist/sqlite/storage/branch-tips.js +8 -11
  34. package/dist/sqlite/storage/branch-tips.js.map +1 -1
  35. package/dist/sqlite/storage/entries.d.ts +7 -2
  36. package/dist/sqlite/storage/entries.d.ts.map +1 -1
  37. package/dist/sqlite/storage/entries.js +22 -23
  38. package/dist/sqlite/storage/entries.js.map +1 -1
  39. package/dist/sqlite/storage/facts.d.ts +1 -0
  40. package/dist/sqlite/storage/facts.d.ts.map +1 -1
  41. package/dist/sqlite/storage/facts.js +26 -29
  42. package/dist/sqlite/storage/facts.js.map +1 -1
  43. package/dist/sqlite/storage/lanes.d.ts +1 -0
  44. package/dist/sqlite/storage/lanes.d.ts.map +1 -1
  45. package/dist/sqlite/storage/lanes.js +41 -52
  46. package/dist/sqlite/storage/lanes.js.map +1 -1
  47. package/dist/sqlite/storage/records.d.ts +2 -2
  48. package/dist/sqlite/storage/records.d.ts.map +1 -1
  49. package/dist/sqlite/storage/records.js +27 -47
  50. package/dist/sqlite/storage/records.js.map +1 -1
  51. package/dist/sqlite/storage/session-sequences.d.ts.map +1 -1
  52. package/dist/sqlite/storage/session-sequences.js +5 -6
  53. package/dist/sqlite/storage/session-sequences.js.map +1 -1
  54. package/dist/sqlite/storage/session-stats.d.ts.map +1 -1
  55. package/dist/sqlite/storage/session-stats.js +14 -19
  56. package/dist/sqlite/storage/session-stats.js.map +1 -1
  57. package/dist/sqlite/storage/sessions.d.ts +2 -2
  58. package/dist/sqlite/storage/sessions.d.ts.map +1 -1
  59. package/dist/sqlite/storage/sessions.js +38 -41
  60. package/dist/sqlite/storage/sessions.js.map +1 -1
  61. package/dist/sqlite/storage/writer-leases.d.ts.map +1 -1
  62. package/dist/sqlite/storage/writer-leases.js +18 -17
  63. package/dist/sqlite/storage/writer-leases.js.map +1 -1
  64. package/dist/sqlite/types.d.ts +1 -0
  65. package/dist/sqlite/types.d.ts.map +1 -1
  66. package/dist/sqlite/types.js.map +1 -1
  67. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.84.2] - 2026-08-14
4
+
5
+ ## [0.84.1] - 2026-08-07
6
+
7
+ ### Added
8
+
9
+ - Added the composable, parameterized `sql` template tag for SQLite queries.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed SQLite branch queries to apply filters, cursors, and limits in SQL; bounded log reads; and added covering indexes for session, record, branch, and fact queries ([#7727](https://github.com/earendil-works/pi/pull/7727) by [@cristinaponcela](https://github.com/cristinaponcela)).
4
14
 
5
15
  ## [0.84.0] - 2026-08-06
6
16
 
package/README.md CHANGED
@@ -8,8 +8,15 @@ migrations, materialized views, and optional FTS search.
8
8
  await using repository = new SqliteSessionRepository(options);
9
9
  const search = createSqliteSessionSearch(options);
10
10
  const session = await repository.create({ cwd });
11
- const hits = await search.search({ text: "needle" });
11
+ await session.appendMessage(message);
12
+
13
+ const hits = [];
14
+ for await (const hit of search.search("needle")) hits.push(hit);
12
15
  ```
13
16
 
14
- The repository lazily owns one shared database connection. Search is an independent,
15
- query-only projection over the same canonical database.
17
+ The repository lazily owns one shared database connection. Search is an independent
18
+ service over the same canonical database: repositories do not expose `search()`.
19
+ The FTS table and triggers are created lazily on the first non-blank search; when
20
+ FTS is first created, search performs a one-time rebuild from canonical entries.
21
+ After that, SQLite triggers keep FTS in sync with canonical entry inserts, deletes,
22
+ and payload updates.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAoC,MAAM,mBAAmB,CAAC;AAwFjH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAEvE;AAED,wBAAgB,uBAAuB,IAAI,qBAAqB,CAM/D;AAGD,cAAc,mBAAmB,CAAC","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tthis.db.exec(\"BEGIN IMMEDIATE\");\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tthis.db.exec(\"COMMIT\");\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tthis.db.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAoC,MAAM,mBAAmB,CAAC;AAiGjH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAEvE;AAED,wBAAgB,uBAAuB,IAAI,qBAAqB,CAM/D;AAGD,cAAc,mBAAmB,CAAC","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { sql } from \"./sqlite/sql.ts\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow> {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.iterate(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.iterate(...(params as SQLInputValue[]))\n\t\t) as Iterable<TRow>;\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tsql`BEGIN IMMEDIATE`.exec(this);\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tsql`COMMIT`.exec(this);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tsql`ROLLBACK`.exec(this);\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { DatabaseSync } from "node:sqlite";
2
+ import { sql } from "./sqlite/sql.js";
2
3
  function isNamedParameters(value) {
3
4
  if (value === null || typeof value !== "object")
4
5
  return false;
@@ -36,6 +37,12 @@ class NodeSqliteStatement {
36
37
  ? this.statement.all(first, ...rest)
37
38
  : this.statement.all(...params));
38
39
  }
40
+ iterate(...params) {
41
+ const [first, ...rest] = params;
42
+ return (isNamedParameters(first)
43
+ ? this.statement.iterate(first, ...rest)
44
+ : this.statement.iterate(...params));
45
+ }
39
46
  }
40
47
  class NodeSqliteDatabase {
41
48
  db;
@@ -49,18 +56,18 @@ class NodeSqliteDatabase {
49
56
  return new NodeSqliteStatement(this.db.prepare(sql));
50
57
  }
51
58
  transaction(fn) {
52
- this.db.exec("BEGIN IMMEDIATE");
59
+ sql `BEGIN IMMEDIATE`.exec(this);
53
60
  try {
54
61
  const result = fn();
55
62
  if (isAsyncResult(result)) {
56
63
  throw new TypeError("SQLite transaction callbacks must be synchronous");
57
64
  }
58
- this.db.exec("COMMIT");
65
+ sql `COMMIT`.exec(this);
59
66
  return result;
60
67
  }
61
68
  catch (error) {
62
69
  try {
63
- this.db.exec("ROLLBACK");
70
+ sql `ROLLBACK`.exec(this);
64
71
  }
65
72
  catch {
66
73
  // Ignore rollback errors to rethrow original error.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tthis.db.exec(\"BEGIN IMMEDIATE\");\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tthis.db.exec(\"COMMIT\");\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tthis.db.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SQLInputValue } from \"node:sqlite\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { sql } from \"./sqlite/sql.ts\";\nimport type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteStatement } from \"./sqlite/types.ts\";\n\nfunction isNamedParameters(value: unknown): value is Record<string, SQLInputValue> {\n\tif (value === null || typeof value !== \"object\") return false;\n\tif (Array.isArray(value) || ArrayBuffer.isView(value)) return false;\n\treturn true;\n}\n\nfunction isAsyncResult(value: unknown): boolean {\n\treturn value !== null && (typeof value === \"object\" || typeof value === \"function\") && \"then\" in value;\n}\n\nclass NodeSqliteStatement implements SqliteStatement {\n\tprivate readonly statement: ReturnType<DatabaseSync[\"prepare\"]>;\n\n\tconstructor(statement: ReturnType<DatabaseSync[\"prepare\"]>) {\n\t\tthis.statement = statement;\n\t}\n\n\trun(...params: unknown[]): SqliteRunResult {\n\t\tconst [first, ...rest] = params;\n\t\tconst result = isNamedParameters(first)\n\t\t\t? this.statement.run(first, ...(rest as SQLInputValue[]))\n\t\t\t: this.statement.run(...(params as SQLInputValue[]));\n\t\treturn {\n\t\t\tchanges: Number(result.changes),\n\t\t\tlastInsertRowid: result.lastInsertRowid === undefined ? undefined : Number(result.lastInsertRowid),\n\t\t};\n\t}\n\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.get(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.get(...(params as SQLInputValue[]))\n\t\t) as TRow | undefined;\n\t}\n\n\tall<TRow extends object>(...params: unknown[]): TRow[] {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.all(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.all(...(params as SQLInputValue[]))\n\t\t) as TRow[];\n\t}\n\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow> {\n\t\tconst [first, ...rest] = params;\n\t\treturn (\n\t\t\tisNamedParameters(first)\n\t\t\t\t? this.statement.iterate(first, ...(rest as SQLInputValue[]))\n\t\t\t\t: this.statement.iterate(...(params as SQLInputValue[]))\n\t\t) as Iterable<TRow>;\n\t}\n}\n\nclass NodeSqliteDatabase implements SqliteDatabase {\n\tprivate readonly db: DatabaseSync;\n\n\tconstructor(db: DatabaseSync) {\n\t\tthis.db = db;\n\t}\n\n\texec(sql: string): void {\n\t\tthis.db.exec(sql);\n\t}\n\n\tprepare(sql: string): SqliteStatement {\n\t\treturn new NodeSqliteStatement(this.db.prepare(sql));\n\t}\n\n\ttransaction<T>(fn: () => T): T {\n\t\tsql`BEGIN IMMEDIATE`.exec(this);\n\t\ttry {\n\t\t\tconst result = fn();\n\t\t\tif (isAsyncResult(result)) {\n\t\t\t\tthrow new TypeError(\"SQLite transaction callbacks must be synchronous\");\n\t\t\t}\n\t\t\tsql`COMMIT`.exec(this);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tsql`ROLLBACK`.exec(this);\n\t\t\t} catch {\n\t\t\t\t// Ignore rollback errors to rethrow original error.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n\nexport function wrapNodeSqliteDatabase(db: DatabaseSync): SqliteDatabase {\n\treturn new NodeSqliteDatabase(db);\n}\n\nexport function createNodeSqliteFactory(): SqliteDatabaseFactory {\n\treturn {\n\t\tasync open(path: string): Promise<SqliteDatabase> {\n\t\t\treturn new NodeSqliteDatabase(new DatabaseSync(path));\n\t\t},\n\t};\n}\n\n// Re-export the SQLite session backend and types so this package is a complete node-sqlite backend.\nexport * from \"./sqlite/index.ts\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"branch-cache.d.ts","sourceRoot":"","sources":["../../src/sqlite/branch-cache.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAGtE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAcvE;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAqBtF;AAkBD,wBAAgB,wBAAwB,CACvC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,QAwBvB","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { uuidv7 } from \"@earendil-works/pi-ai\";\nimport {\n\tcopyBranchEntriesThroughSeq,\n\tdeleteBranchEntries,\n\tinsertBranchEntriesForPath,\n\tinsertBranchEntry,\n\treadBranchContainingEntry,\n} from \"./storage/branch-entries.ts\";\n\nimport { deleteBranchTips, insertBranchTip, readBranchTipBranchId, updateBranchTip } from \"./storage/branch-tips.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport function deleteBranchCache(db: SqliteDatabase, sessionId: string) {\n\tdeleteBranchTips(db, sessionId);\n\tdeleteBranchEntries(db, sessionId);\n}\n\nexport function rebuildBranchCache(db: SqliteDatabase, sessionId: string) {\n\tconst tips = db\n\t\t.prepare(\n\t\t\t`SELECT leaf.id\n\t\t\tFROM entries AS leaf\n\t\t\tWHERE leaf.session_id = ?\n\t\t\t\tAND NOT EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id\n\t\t\t\t)\n\t\t\tORDER BY leaf.seq`,\n\t\t)\n\t\t.all<{ id: string }>(sessionId);\n\tdeleteBranchCache(db, sessionId);\n\tfor (const tip of tips) buildCachedBranch(db, sessionId, tip.id);\n}\n\nexport function buildCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tdb.exec(\"SAVEPOINT build_branch_cache\");\n\ttry {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntriesForPath(db, sessionId, branchId, leafId);\n\t\tinsertBranchTip(db, sessionId, leafId, branchId);\n\t\tdb.exec(\"RELEASE SAVEPOINT build_branch_cache\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdb.exec(\"ROLLBACK TO SAVEPOINT build_branch_cache\");\n\t\t\tdb.exec(\"RELEASE SAVEPOINT build_branch_cache\");\n\t\t} catch {\n\t\t\t// Preserve the original build failure.\n\t\t}\n\t\tif (error instanceof SessionError) throw error;\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Failed to build SQLite branch cache at entry ${leafId}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction extendBranch(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tparentId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tif (!updateBranchTip(db, sessionId, branchId, parentId, entryId)) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch tip ${parentId} changed during append`);\n\t}\n}\n\nexport function appendEntryToBranchCache(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n\tparentId: string | null,\n) {\n\tif (parentId === null) {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\t\tinsertBranchTip(db, sessionId, entryId, branchId);\n\t\treturn;\n\t}\n\n\tconst tipBranchId = readBranchTipBranchId(db, sessionId, parentId);\n\tif (tipBranchId !== undefined) {\n\t\textendBranch(db, sessionId, tipBranchId, parentId, entryId, entrySeq, entryType, customType);\n\t\treturn;\n\t}\n\n\tconst source = readBranchContainingEntry(db, sessionId, parentId);\n\tif (!source) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch cache has no branch containing parent entry ${parentId}`);\n\t}\n\n\tconst branchId = uuidv7();\n\tcopyBranchEntriesThroughSeq(db, sessionId, branchId, source.branchId, source.entrySeq);\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tinsertBranchTip(db, sessionId, entryId, branchId);\n}\n"]}
1
+ {"version":3,"file":"branch-cache.d.ts","sourceRoot":"","sources":["../../src/sqlite/branch-cache.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAGtE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAUvE;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAqBtF;AAkBD,wBAAgB,wBAAwB,CACvC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,QAwBvB","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { uuidv7 } from \"@earendil-works/pi-ai\";\nimport { sql } from \"./sql.ts\";\nimport {\n\tcopyBranchEntriesThroughSeq,\n\tdeleteBranchEntries,\n\tinsertBranchEntriesForPath,\n\tinsertBranchEntry,\n\treadBranchContainingEntry,\n} from \"./storage/branch-entries.ts\";\nimport { deleteBranchTips, insertBranchTip, readBranchTipBranchId, updateBranchTip } from \"./storage/branch-tips.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport function deleteBranchCache(db: SqliteDatabase, sessionId: string) {\n\tdeleteBranchTips(db, sessionId);\n\tdeleteBranchEntries(db, sessionId);\n}\n\nexport function rebuildBranchCache(db: SqliteDatabase, sessionId: string) {\n\tconst tips = sql`SELECT leaf.id\n\t\tFROM entries AS leaf\n\t\tWHERE leaf.session_id = ${sessionId}\n\t\t\tAND NOT EXISTS (\n\t\t\t\tSELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id\n\t\t\t)\n\t\tORDER BY leaf.seq`.all<{ id: string }>(db);\n\tdeleteBranchCache(db, sessionId);\n\tfor (const tip of tips) buildCachedBranch(db, sessionId, tip.id);\n}\n\nexport function buildCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tsql`SAVEPOINT build_branch_cache`.exec(db);\n\ttry {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntriesForPath(db, sessionId, branchId, leafId);\n\t\tinsertBranchTip(db, sessionId, leafId, branchId);\n\t\tsql`RELEASE SAVEPOINT build_branch_cache`.exec(db);\n\t} catch (error) {\n\t\ttry {\n\t\t\tsql`ROLLBACK TO SAVEPOINT build_branch_cache`.exec(db);\n\t\t\tsql`RELEASE SAVEPOINT build_branch_cache`.exec(db);\n\t\t} catch {\n\t\t\t// Preserve the original build failure.\n\t\t}\n\t\tif (error instanceof SessionError) throw error;\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Failed to build SQLite branch cache at entry ${leafId}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction extendBranch(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tparentId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tif (!updateBranchTip(db, sessionId, branchId, parentId, entryId)) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch tip ${parentId} changed during append`);\n\t}\n}\n\nexport function appendEntryToBranchCache(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n\tparentId: string | null,\n) {\n\tif (parentId === null) {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\t\tinsertBranchTip(db, sessionId, entryId, branchId);\n\t\treturn;\n\t}\n\n\tconst tipBranchId = readBranchTipBranchId(db, sessionId, parentId);\n\tif (tipBranchId !== undefined) {\n\t\textendBranch(db, sessionId, tipBranchId, parentId, entryId, entrySeq, entryType, customType);\n\t\treturn;\n\t}\n\n\tconst source = readBranchContainingEntry(db, sessionId, parentId);\n\tif (!source) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch cache has no branch containing parent entry ${parentId}`);\n\t}\n\n\tconst branchId = uuidv7();\n\tcopyBranchEntriesThroughSeq(db, sessionId, branchId, source.branchId, source.entrySeq);\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tinsertBranchTip(db, sessionId, entryId, branchId);\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { SessionError } from "@earendil-works/pi-agent-core";
2
2
  import { uuidv7 } from "@earendil-works/pi-ai";
3
+ import { sql } from "./sql.js";
3
4
  import { copyBranchEntriesThroughSeq, deleteBranchEntries, insertBranchEntriesForPath, insertBranchEntry, readBranchContainingEntry, } from "./storage/branch-entries.js";
4
5
  import { deleteBranchTips, insertBranchTip, readBranchTipBranchId, updateBranchTip } from "./storage/branch-tips.js";
5
6
  export function deleteBranchCache(db, sessionId) {
@@ -7,31 +8,29 @@ export function deleteBranchCache(db, sessionId) {
7
8
  deleteBranchEntries(db, sessionId);
8
9
  }
9
10
  export function rebuildBranchCache(db, sessionId) {
10
- const tips = db
11
- .prepare(`SELECT leaf.id
12
- FROM entries AS leaf
13
- WHERE leaf.session_id = ?
14
- AND NOT EXISTS (
15
- SELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id
16
- )
17
- ORDER BY leaf.seq`)
18
- .all(sessionId);
11
+ const tips = sql `SELECT leaf.id
12
+ FROM entries AS leaf
13
+ WHERE leaf.session_id = ${sessionId}
14
+ AND NOT EXISTS (
15
+ SELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id
16
+ )
17
+ ORDER BY leaf.seq`.all(db);
19
18
  deleteBranchCache(db, sessionId);
20
19
  for (const tip of tips)
21
20
  buildCachedBranch(db, sessionId, tip.id);
22
21
  }
23
22
  export function buildCachedBranch(db, sessionId, leafId) {
24
- db.exec("SAVEPOINT build_branch_cache");
23
+ sql `SAVEPOINT build_branch_cache`.exec(db);
25
24
  try {
26
25
  const branchId = uuidv7();
27
26
  insertBranchEntriesForPath(db, sessionId, branchId, leafId);
28
27
  insertBranchTip(db, sessionId, leafId, branchId);
29
- db.exec("RELEASE SAVEPOINT build_branch_cache");
28
+ sql `RELEASE SAVEPOINT build_branch_cache`.exec(db);
30
29
  }
31
30
  catch (error) {
32
31
  try {
33
- db.exec("ROLLBACK TO SAVEPOINT build_branch_cache");
34
- db.exec("RELEASE SAVEPOINT build_branch_cache");
32
+ sql `ROLLBACK TO SAVEPOINT build_branch_cache`.exec(db);
33
+ sql `RELEASE SAVEPOINT build_branch_cache`.exec(db);
35
34
  }
36
35
  catch {
37
36
  // Preserve the original build failure.
@@ -1 +1 @@
1
- {"version":3,"file":"branch-cache.js","sourceRoot":"","sources":["../../src/sqlite/branch-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EACN,2BAA2B,EAC3B,mBAAmB,EACnB,0BAA0B,EAC1B,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGrH,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACxE,gBAAgB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAChC,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;AAAA,CACnC;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACzE,MAAM,IAAI,GAAG,EAAE;SACb,OAAO,CACP;;;;;;qBAMkB,CAClB;SACA,GAAG,CAAiB,SAAS,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACjE;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE,MAAc,EAAE;IACxF,EAAE,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACxC,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC;QAC1B,0BAA0B,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5D,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjD,EAAE,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC;YACJ,EAAE,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;YACpD,EAAE,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACR,uCAAuC;QACxC,CAAC;QACD,IAAI,KAAK,YAAY,YAAY;YAAE,MAAM,KAAK,CAAC;QAC/C,MAAM,IAAI,YAAY,CACrB,SAAS,EACT,gDAAgD,MAAM,EAAE,EACxD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AAAA,CACD;AAED,SAAS,YAAY,CACpB,EAAkB,EAClB,SAAiB,EACjB,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,QAAgB,EAChB,SAAiB,EACjB,UAAyB,EACxB;IACD,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACrF,IAAI,CAAC,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,cAAc,QAAQ,wBAAwB,CAAC,CAAC;IACzF,CAAC;AAAA,CACD;AAED,MAAM,UAAU,wBAAwB,CACvC,EAAkB,EAClB,SAAiB,EACjB,OAAe,EACf,QAAgB,EAChB,SAAiB,EACjB,UAAyB,EACzB,QAAuB,EACtB;IACD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC;QAC1B,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QACrF,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAClD,OAAO;IACR,CAAC;IAED,MAAM,WAAW,GAAG,qBAAqB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACnE,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC/B,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7F,OAAO;IACR,CAAC;IAED,MAAM,MAAM,GAAG,yBAAyB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,sDAAsD,QAAQ,EAAE,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC;IAC1B,2BAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvF,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACrF,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAAA,CAClD","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { uuidv7 } from \"@earendil-works/pi-ai\";\nimport {\n\tcopyBranchEntriesThroughSeq,\n\tdeleteBranchEntries,\n\tinsertBranchEntriesForPath,\n\tinsertBranchEntry,\n\treadBranchContainingEntry,\n} from \"./storage/branch-entries.ts\";\n\nimport { deleteBranchTips, insertBranchTip, readBranchTipBranchId, updateBranchTip } from \"./storage/branch-tips.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport function deleteBranchCache(db: SqliteDatabase, sessionId: string) {\n\tdeleteBranchTips(db, sessionId);\n\tdeleteBranchEntries(db, sessionId);\n}\n\nexport function rebuildBranchCache(db: SqliteDatabase, sessionId: string) {\n\tconst tips = db\n\t\t.prepare(\n\t\t\t`SELECT leaf.id\n\t\t\tFROM entries AS leaf\n\t\t\tWHERE leaf.session_id = ?\n\t\t\t\tAND NOT EXISTS (\n\t\t\t\t\tSELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id\n\t\t\t\t)\n\t\t\tORDER BY leaf.seq`,\n\t\t)\n\t\t.all<{ id: string }>(sessionId);\n\tdeleteBranchCache(db, sessionId);\n\tfor (const tip of tips) buildCachedBranch(db, sessionId, tip.id);\n}\n\nexport function buildCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tdb.exec(\"SAVEPOINT build_branch_cache\");\n\ttry {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntriesForPath(db, sessionId, branchId, leafId);\n\t\tinsertBranchTip(db, sessionId, leafId, branchId);\n\t\tdb.exec(\"RELEASE SAVEPOINT build_branch_cache\");\n\t} catch (error) {\n\t\ttry {\n\t\t\tdb.exec(\"ROLLBACK TO SAVEPOINT build_branch_cache\");\n\t\t\tdb.exec(\"RELEASE SAVEPOINT build_branch_cache\");\n\t\t} catch {\n\t\t\t// Preserve the original build failure.\n\t\t}\n\t\tif (error instanceof SessionError) throw error;\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Failed to build SQLite branch cache at entry ${leafId}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction extendBranch(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tparentId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tif (!updateBranchTip(db, sessionId, branchId, parentId, entryId)) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch tip ${parentId} changed during append`);\n\t}\n}\n\nexport function appendEntryToBranchCache(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n\tparentId: string | null,\n) {\n\tif (parentId === null) {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\t\tinsertBranchTip(db, sessionId, entryId, branchId);\n\t\treturn;\n\t}\n\n\tconst tipBranchId = readBranchTipBranchId(db, sessionId, parentId);\n\tif (tipBranchId !== undefined) {\n\t\textendBranch(db, sessionId, tipBranchId, parentId, entryId, entrySeq, entryType, customType);\n\t\treturn;\n\t}\n\n\tconst source = readBranchContainingEntry(db, sessionId, parentId);\n\tif (!source) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch cache has no branch containing parent entry ${parentId}`);\n\t}\n\n\tconst branchId = uuidv7();\n\tcopyBranchEntriesThroughSeq(db, sessionId, branchId, source.branchId, source.entrySeq);\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tinsertBranchTip(db, sessionId, entryId, branchId);\n}\n"]}
1
+ {"version":3,"file":"branch-cache.js","sourceRoot":"","sources":["../../src/sqlite/branch-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { uuidv7 } from \"@earendil-works/pi-ai\";\nimport { sql } from \"./sql.ts\";\nimport {\n\tcopyBranchEntriesThroughSeq,\n\tdeleteBranchEntries,\n\tinsertBranchEntriesForPath,\n\tinsertBranchEntry,\n\treadBranchContainingEntry,\n} from \"./storage/branch-entries.ts\";\nimport { deleteBranchTips, insertBranchTip, readBranchTipBranchId, updateBranchTip } from \"./storage/branch-tips.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport function deleteBranchCache(db: SqliteDatabase, sessionId: string) {\n\tdeleteBranchTips(db, sessionId);\n\tdeleteBranchEntries(db, sessionId);\n}\n\nexport function rebuildBranchCache(db: SqliteDatabase, sessionId: string) {\n\tconst tips = sql`SELECT leaf.id\n\t\tFROM entries AS leaf\n\t\tWHERE leaf.session_id = ${sessionId}\n\t\t\tAND NOT EXISTS (\n\t\t\t\tSELECT 1 FROM entries AS child WHERE child.session_id = leaf.session_id AND child.parent_id = leaf.id\n\t\t\t)\n\t\tORDER BY leaf.seq`.all<{ id: string }>(db);\n\tdeleteBranchCache(db, sessionId);\n\tfor (const tip of tips) buildCachedBranch(db, sessionId, tip.id);\n}\n\nexport function buildCachedBranch(db: SqliteDatabase, sessionId: string, leafId: string) {\n\tsql`SAVEPOINT build_branch_cache`.exec(db);\n\ttry {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntriesForPath(db, sessionId, branchId, leafId);\n\t\tinsertBranchTip(db, sessionId, leafId, branchId);\n\t\tsql`RELEASE SAVEPOINT build_branch_cache`.exec(db);\n\t} catch (error) {\n\t\ttry {\n\t\t\tsql`ROLLBACK TO SAVEPOINT build_branch_cache`.exec(db);\n\t\t\tsql`RELEASE SAVEPOINT build_branch_cache`.exec(db);\n\t\t} catch {\n\t\t\t// Preserve the original build failure.\n\t\t}\n\t\tif (error instanceof SessionError) throw error;\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Failed to build SQLite branch cache at entry ${leafId}`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction extendBranch(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tbranchId: string,\n\tparentId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n) {\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tif (!updateBranchTip(db, sessionId, branchId, parentId, entryId)) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch tip ${parentId} changed during append`);\n\t}\n}\n\nexport function appendEntryToBranchCache(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tentryId: string,\n\tentrySeq: number,\n\tentryType: string,\n\tcustomType: string | null,\n\tparentId: string | null,\n) {\n\tif (parentId === null) {\n\t\tconst branchId = uuidv7();\n\t\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\t\tinsertBranchTip(db, sessionId, entryId, branchId);\n\t\treturn;\n\t}\n\n\tconst tipBranchId = readBranchTipBranchId(db, sessionId, parentId);\n\tif (tipBranchId !== undefined) {\n\t\textendBranch(db, sessionId, tipBranchId, parentId, entryId, entrySeq, entryType, customType);\n\t\treturn;\n\t}\n\n\tconst source = readBranchContainingEntry(db, sessionId, parentId);\n\tif (!source) {\n\t\tthrow new SessionError(\"invalid_entry\", `Branch cache has no branch containing parent entry ${parentId}`);\n\t}\n\n\tconst branchId = uuidv7();\n\tcopyBranchEntriesThroughSeq(db, sessionId, branchId, source.branchId, source.entrySeq);\n\tinsertBranchEntry(db, sessionId, branchId, entryId, entrySeq, entryType, customType);\n\tinsertBranchTip(db, sessionId, entryId, branchId);\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  export * from "./migrations.ts";
2
2
  export { SqliteSessionRepository, type SqliteSessionRepositoryOptions, type SqliteWriterLeaseOptions, } from "./repo.ts";
3
3
  export * from "./search-backend.ts";
4
+ export * from "./sql.ts";
4
5
  export type { SqliteDatabase, SqliteDatabaseFactory, SqliteRunResult, SqliteSessionCreateOptions, SqliteSessionListOptions, SqliteSessionMetadata, SqliteSessionRepositoryEnv, SqliteStatement, } from "./types.ts";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACN,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,KAAK,wBAAwB,GAC7B,MAAM,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,YAAY,EACX,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,eAAe,GACf,MAAM,YAAY,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport {\n\tSqliteSessionRepository,\n\ttype SqliteSessionRepositoryOptions,\n\ttype SqliteWriterLeaseOptions,\n} from \"./repo.ts\";\nexport * from \"./search-backend.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepositoryEnv,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACN,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,KAAK,wBAAwB,GAC7B,MAAM,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,UAAU,CAAC;AACzB,YAAY,EACX,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,eAAe,GACf,MAAM,YAAY,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport {\n\tSqliteSessionRepository,\n\ttype SqliteSessionRepositoryOptions,\n\ttype SqliteWriterLeaseOptions,\n} from \"./repo.ts\";\nexport * from \"./search-backend.ts\";\nexport * from \"./sql.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepositoryEnv,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from "./migrations.js";
2
2
  export { SqliteSessionRepository, } from "./repo.js";
3
3
  export * from "./search-backend.js";
4
+ export * from "./sql.js";
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACN,uBAAuB,GAGvB,MAAM,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport {\n\tSqliteSessionRepository,\n\ttype SqliteSessionRepositoryOptions,\n\ttype SqliteWriterLeaseOptions,\n} from \"./repo.ts\";\nexport * from \"./search-backend.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepositoryEnv,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACN,uBAAuB,GAGvB,MAAM,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,UAAU,CAAC","sourcesContent":["export * from \"./migrations.ts\";\nexport {\n\tSqliteSessionRepository,\n\ttype SqliteSessionRepositoryOptions,\n\ttype SqliteWriterLeaseOptions,\n} from \"./repo.ts\";\nexport * from \"./search-backend.ts\";\nexport * from \"./sql.ts\";\nexport type {\n\tSqliteDatabase,\n\tSqliteDatabaseFactory,\n\tSqliteRunResult,\n\tSqliteSessionCreateOptions,\n\tSqliteSessionListOptions,\n\tSqliteSessionMetadata,\n\tSqliteSessionRepositoryEnv,\n\tSqliteStatement,\n} from \"./types.ts\";\n"]}
@@ -1,14 +1,13 @@
1
1
  CREATE TABLE IF NOT EXISTS sessions (
2
2
  id TEXT PRIMARY KEY,
3
- created_at TEXT NOT NULL,
3
+ created_at INTEGER NOT NULL,
4
4
  cwd TEXT NOT NULL,
5
5
  parent_session_id TEXT NULL,
6
6
  metadata TEXT NULL
7
7
  ) WITHOUT ROWID;
8
8
 
9
9
  CREATE INDEX IF NOT EXISTS idx_sessions_created_at ON sessions(created_at DESC);
10
- CREATE INDEX IF NOT EXISTS idx_sessions_cwd ON sessions(cwd);
11
- CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
10
+ CREATE INDEX IF NOT EXISTS idx_sessions_cwd_created_at ON sessions(cwd, created_at DESC);
12
11
 
13
12
  CREATE TABLE IF NOT EXISTS entries (
14
13
  session_id TEXT NOT NULL,
@@ -16,13 +15,12 @@ CREATE TABLE IF NOT EXISTS entries (
16
15
  id TEXT NOT NULL,
17
16
  parent_id TEXT NULL,
18
17
  type TEXT NOT NULL,
19
- timestamp TEXT NOT NULL,
18
+ timestamp INTEGER NOT NULL,
20
19
  payload TEXT NOT NULL,
21
20
  PRIMARY KEY (session_id, id),
22
21
  UNIQUE (session_id, seq)
23
22
  );
24
23
 
25
- CREATE INDEX IF NOT EXISTS idx_entries_session_seq ON entries(session_id, seq);
26
24
  CREATE INDEX IF NOT EXISTS idx_entries_session_parent ON entries(session_id, parent_id);
27
25
  CREATE INDEX IF NOT EXISTS idx_entries_session_type_seq ON entries(session_id, type, seq);
28
26
 
@@ -53,7 +51,7 @@ CREATE TABLE IF NOT EXISTS branch_entries (
53
51
  ) WITHOUT ROWID;
54
52
 
55
53
  CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_seq ON branch_entries(session_id, branch_id, entry_seq);
56
- CREATE INDEX IF NOT EXISTS idx_branch_entries_session_entry ON branch_entries(session_id, entry_id);
54
+ CREATE INDEX IF NOT EXISTS idx_branch_entries_session_entry ON branch_entries(session_id, entry_id, branch_id, entry_seq);
57
55
  CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_type_seq ON branch_entries(session_id, branch_id, entry_type, entry_seq);
58
56
  CREATE INDEX IF NOT EXISTS idx_branch_entries_session_branch_custom_seq ON branch_entries(session_id, branch_id, custom_type, entry_seq);
59
57
 
@@ -73,13 +71,15 @@ CREATE TABLE IF NOT EXISTS records (
73
71
  run_id TEXT NULL,
74
72
  type TEXT NOT NULL,
75
73
  op_kind TEXT NULL,
76
- timestamp TEXT NOT NULL,
74
+ timestamp INTEGER NOT NULL,
77
75
  payload TEXT NOT NULL,
78
76
  PRIMARY KEY (session_id, id),
79
77
  UNIQUE (session_id, seq)
80
78
  ) WITHOUT ROWID;
81
79
 
82
- CREATE INDEX IF NOT EXISTS idx_records_session_seq ON records(session_id, seq);
80
+ CREATE INDEX IF NOT EXISTS idx_records_session_lane_seq ON records(session_id, lane, seq);
81
+ CREATE INDEX IF NOT EXISTS idx_records_session_type_seq ON records(session_id, type, seq);
82
+ CREATE INDEX IF NOT EXISTS idx_records_session_type_op_kind_seq ON records(session_id, type, op_kind, seq);
83
83
  CREATE INDEX IF NOT EXISTS idx_records_session_lane_type_seq ON records(session_id, lane, type, seq);
84
84
  CREATE INDEX IF NOT EXISTS idx_records_session_lane_type_op_kind_seq ON records(session_id, lane, type, op_kind, seq);
85
85
  CREATE INDEX IF NOT EXISTS idx_records_session_run_id_seq ON records(session_id, run_id, seq);
@@ -92,7 +92,6 @@ CREATE TABLE IF NOT EXISTS lane_moves (
92
92
  PRIMARY KEY (session_id, seq)
93
93
  ) WITHOUT ROWID;
94
94
 
95
- CREATE INDEX IF NOT EXISTS idx_lane_moves_session_lane_seq ON lane_moves(session_id, lane, seq);
96
95
 
97
96
  CREATE TABLE IF NOT EXISTS facts (
98
97
  session_id TEXT NOT NULL,
@@ -107,8 +106,8 @@ CREATE INDEX IF NOT EXISTS idx_facts_session_kind_key_seq ON facts(session_id, k
107
106
 
108
107
  CREATE TABLE IF NOT EXISTS branch_tips (
109
108
  session_id TEXT NOT NULL,
110
- tip_id TEXT NOT NULL,
111
109
  branch_id TEXT NOT NULL,
110
+ tip_id TEXT NOT NULL,
112
111
  PRIMARY KEY (session_id, tip_id),
113
112
  UNIQUE (session_id, branch_id)
114
113
  ) WITHOUT ROWID;
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAMD,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAQjE;AAWD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBvE","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nfunction ensureMigrationsTable(db: SqliteDatabase): void {\n\tdb.exec(`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = db.prepare(\"SELECT id FROM migrations ORDER BY applied_at, id\").all<{ id: string }>();\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tdb.transaction(() => {\n\t\t\tdb.exec(migration.sql);\n\t\t\tdb.prepare(\"INSERT INTO migrations (id, applied_at) VALUES (?, ?)\").run(\n\t\t\t\tmigration.id,\n\t\t\t\tnew Date().toISOString(),\n\t\t\t);\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAMD,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAQjE;AAWD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvE","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { sql } from \"./sql.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nfunction ensureMigrationsTable(db: SqliteDatabase): void {\n\tsql`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`.exec(db);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = sql`SELECT id FROM migrations ORDER BY applied_at, id`.all<{ id: string }>(db);\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tdb.transaction(() => {\n\t\t\tdb.exec(migration.sql);\n\t\t\tsql`INSERT INTO migrations (id, applied_at) VALUES (${migration.id}, ${new Date().toISOString()})`.run(db);\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { fileURLToPath } from "node:url";
3
+ import { sql } from "./sql.js";
3
4
  async function loadMigrationSql(relativePath) {
4
5
  return readFile(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8");
5
6
  }
@@ -13,24 +14,24 @@ export async function loadMigrations() {
13
14
  ];
14
15
  }
15
16
  function ensureMigrationsTable(db) {
16
- db.exec(`
17
+ sql `
17
18
  CREATE TABLE IF NOT EXISTS migrations (
18
19
  id TEXT PRIMARY KEY,
19
20
  applied_at TEXT NOT NULL
20
21
  );
21
- `);
22
+ `.exec(db);
22
23
  }
23
24
  export async function applyMigrations(db) {
24
25
  ensureMigrationsTable(db);
25
26
  const migrations = await loadMigrations();
26
- const appliedRows = db.prepare("SELECT id FROM migrations ORDER BY applied_at, id").all();
27
+ const appliedRows = sql `SELECT id FROM migrations ORDER BY applied_at, id`.all(db);
27
28
  const applied = new Set(appliedRows.map((row) => row.id));
28
29
  for (const migration of migrations) {
29
30
  if (applied.has(migration.id))
30
31
  continue;
31
32
  db.transaction(() => {
32
33
  db.exec(migration.sql);
33
- db.prepare("INSERT INTO migrations (id, applied_at) VALUES (?, ?)").run(migration.id, new Date().toISOString());
34
+ sql `INSERT INTO migrations (id, applied_at) VALUES (${migration.id}, ${new Date().toISOString()})`.run(db);
34
35
  });
35
36
  applied.add(migration.id);
36
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AASzC,KAAK,UAAU,gBAAgB,CAAC,YAAoB,EAAmB;IACtE,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAAA,CAC/E;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,GAA+B;IAClE,OAAO;QACN;YACC,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,CAAC;YACR,GAAG,EAAE,MAAM,gBAAgB,CAAC,8BAA8B,CAAC;SAC3D;KACD,CAAC;AAAA,CACF;AAED,SAAS,qBAAqB,CAAC,EAAkB,EAAQ;IACxD,EAAE,CAAC,IAAI,CAAC;;;;;CAKR,CAAC,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAkB,EAAiB;IACxE,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,UAAU,GAAG,MAAM,cAAc,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,EAAkB,CAAC;IAC1G,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,SAAS;QACxC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;YACpB,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACvB,EAAE,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAC,GAAG,CACtE,SAAS,CAAC,EAAE,EACZ,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CACxB,CAAC;QAAA,CACF,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;AAAA,CACD","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nfunction ensureMigrationsTable(db: SqliteDatabase): void {\n\tdb.exec(`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = db.prepare(\"SELECT id FROM migrations ORDER BY applied_at, id\").all<{ id: string }>();\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tdb.transaction(() => {\n\t\t\tdb.exec(migration.sql);\n\t\t\tdb.prepare(\"INSERT INTO migrations (id, applied_at) VALUES (?, ?)\").run(\n\t\t\t\tmigration.id,\n\t\t\t\tnew Date().toISOString(),\n\t\t\t);\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/sqlite/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAS/B,KAAK,UAAU,gBAAgB,CAAC,YAAoB,EAAmB;IACtE,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAAA,CAC/E;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,GAA+B;IAClE,OAAO;QACN;YACC,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,CAAC;YACR,GAAG,EAAE,MAAM,gBAAgB,CAAC,8BAA8B,CAAC;SAC3D;KACD,CAAC;AAAA,CACF;AAED,SAAS,qBAAqB,CAAC,EAAkB,EAAQ;IACxD,GAAG,CAAA;;;;;CAKH,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CACV;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAkB,EAAiB;IACxE,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,UAAU,GAAG,MAAM,cAAc,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,GAAG,CAAA,mDAAmD,CAAC,GAAG,CAAiB,EAAE,CAAC,CAAC;IACnG,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,SAAS;QACxC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC;YACpB,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACvB,GAAG,CAAA,mDAAmD,SAAS,CAAC,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAAA,CAC3G,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;AAAA,CACD","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { sql } from \"./sql.ts\";\nimport type { SqliteDatabase } from \"./types.ts\";\n\nexport interface SqliteMigration {\n\tid: string;\n\torder: number;\n\tsql: string;\n}\n\nasync function loadMigrationSql(relativePath: string): Promise<string> {\n\treturn readFile(fileURLToPath(new URL(relativePath, import.meta.url)), \"utf8\");\n}\n\nexport async function loadMigrations(): Promise<SqliteMigration[]> {\n\treturn [\n\t\t{\n\t\t\tid: \"001_initial.sql\",\n\t\t\torder: 1,\n\t\t\tsql: await loadMigrationSql(\"./migrations/001_initial.sql\"),\n\t\t},\n\t];\n}\n\nfunction ensureMigrationsTable(db: SqliteDatabase): void {\n\tsql`\nCREATE TABLE IF NOT EXISTS migrations (\n\tid TEXT PRIMARY KEY,\n\tapplied_at TEXT NOT NULL\n);\n`.exec(db);\n}\n\nexport async function applyMigrations(db: SqliteDatabase): Promise<void> {\n\tensureMigrationsTable(db);\n\tconst migrations = await loadMigrations();\n\tconst appliedRows = sql`SELECT id FROM migrations ORDER BY applied_at, id`.all<{ id: string }>(db);\n\tconst applied = new Set(appliedRows.map((row) => row.id));\n\n\tfor (const migration of migrations) {\n\t\tif (applied.has(migration.id)) continue;\n\t\tdb.transaction(() => {\n\t\t\tdb.exec(migration.sql);\n\t\t\tsql`INSERT INTO migrations (id, applied_at) VALUES (${migration.id}, ${new Date().toISOString()})`.run(db);\n\t\t});\n\t\tapplied.add(migration.id);\n\t}\n}\n"]}