@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
@@ -1 +1 @@
1
- {"version":3,"file":"session-sequences.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAO,GAAG,CAAC,EAAE;IAClF,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,CACzG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACtE,MAAM,WAAW,GAAG,EAAE;SACpB,OAAO,CAAC,6DAA6D,CAAC;SACtE,GAAG,CAAuB,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,oCAAoC,SAAS,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,WAAW,CAAC,QAAQ,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAE;IACvF,EAAE,CAAC,OAAO,CAAC,gEAAgE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAAA,CACrG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,GAAW,EAAE;IACnF,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAChF","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function createSequence(db: SqliteDatabase, sessionId: string, nextSeq = 1) {\n\tdb.prepare(\"INSERT INTO session_sequences (session_id, next_seq) VALUES (?, ?)\").run(sessionId, nextSeq);\n}\n\nexport function getNextSequence(db: SqliteDatabase, sessionId: string) {\n\tconst sequenceRow = db\n\t\t.prepare(\"SELECT next_seq FROM session_sequences WHERE session_id = ?\")\n\t\t.get<{ next_seq: number }>(sessionId);\n\tif (!sequenceRow) {\n\t\tthrow new SessionError(\"storage\", `Missing sequence row for session ${sessionId}`);\n\t}\n\treturn sequenceRow.next_seq;\n}\n\nexport function setNextSequence(db: SqliteDatabase, sessionId: string, nextSeq: number) {\n\tdb.prepare(\"UPDATE session_sequences SET next_seq = ? WHERE session_id = ?\").run(nextSeq, sessionId);\n}\n\nexport function advanceSequence(db: SqliteDatabase, sessionId: string, seq: number) {\n\tsetNextSequence(db, sessionId, seq + 1);\n}\n\nexport function deleteSequence(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM session_sequences WHERE session_id = ?\").run(sessionId);\n}\n"]}
1
+ {"version":3,"file":"session-sequences.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGhC,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAO,GAAG,CAAC,EAAE;IAClF,GAAG,CAAA,gEAAgE,SAAS,KAAK,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACpG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACtE,MAAM,WAAW,GAAG,GAAG,CAAA,6DAA6D,SAAS,EAAE,CAAC,GAAG,CAEhG,EAAE,CAAC,CAAC;IACP,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,oCAAoC,SAAS,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,WAAW,CAAC,QAAQ,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAE;IACvF,GAAG,CAAA,2CAA2C,OAAO,uBAAuB,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAChG;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,GAAW,EAAE;IACnF,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAAA,CACxC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACrE,GAAG,CAAA,oDAAoD,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAC3E","sourcesContent":["import { SessionError } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function createSequence(db: SqliteDatabase, sessionId: string, nextSeq = 1) {\n\tsql`INSERT INTO session_sequences (session_id, next_seq) VALUES (${sessionId}, ${nextSeq})`.run(db);\n}\n\nexport function getNextSequence(db: SqliteDatabase, sessionId: string) {\n\tconst sequenceRow = sql`SELECT next_seq FROM session_sequences WHERE session_id = ${sessionId}`.get<{\n\t\tnext_seq: number;\n\t}>(db);\n\tif (!sequenceRow) {\n\t\tthrow new SessionError(\"storage\", `Missing sequence row for session ${sessionId}`);\n\t}\n\treturn sequenceRow.next_seq;\n}\n\nexport function setNextSequence(db: SqliteDatabase, sessionId: string, nextSeq: number) {\n\tsql`UPDATE session_sequences SET next_seq = ${nextSeq} WHERE session_id = ${sessionId}`.run(db);\n}\n\nexport function advanceSequence(db: SqliteDatabase, sessionId: string, seq: number) {\n\tsetNextSequence(db, sessionId, seq + 1);\n}\n\nexport function deleteSequence(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM session_sequences WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-stats.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI,CAMzF;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAgB7E;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAKjF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAYzF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAEvE","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tdb.prepare(\n\t\t`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (?, ?, 0, 0, 0, 0)`,\n\t).run(sessionId, messageCount);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\t\tFROM session_stats\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.get<SessionStatsRow>(sessionId);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = db\n\t\t.prepare(\"UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?\")\n\t\t.run(sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE session_stats\n\t\t\tSET cached_tokens = cached_tokens + ?,\n\t\t\t\tuncached_tokens = uncached_tokens + ?,\n\t\t\t\ttotal_tokens = total_tokens + ?,\n\t\t\t\tcost_total = cost_total + ?\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tdb.prepare(\"DELETE FROM session_stats WHERE session_id = ?\").run(sessionId);\n}\n"]}
1
+ {"version":3,"file":"session-stats.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI,CAIzF;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAY7E;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAKjF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAQzF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAEvE","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tsql`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (${sessionId}, ${messageCount}, 0, 0, 0, 0)`.run(db);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = sql`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\tFROM session_stats\n\t\tWHERE session_id = ${sessionId}`.get<SessionStatsRow>(db);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = sql`UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ${sessionId}`.run(\n\t\tdb,\n\t);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = sql`UPDATE session_stats\n\t\tSET cached_tokens = cached_tokens + ${usage.cacheRead},\n\t\t\tuncached_tokens = uncached_tokens + ${usage.input + usage.cacheWrite},\n\t\t\ttotal_tokens = total_tokens + ${usage.totalTokens},\n\t\t\tcost_total = cost_total + ${usage.cost.total}\n\t\tWHERE session_id = ${sessionId}`.run(db);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tsql`DELETE FROM session_stats WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -1,15 +1,14 @@
1
1
  import { SessionError } from "@earendil-works/pi-agent-core";
2
+ import { sql } from "../sql.js";
2
3
  export function createStats(db, sessionId, messageCount = 0) {
3
- db.prepare(`INSERT INTO session_stats
4
+ sql `INSERT INTO session_stats
4
5
  (session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)
5
- VALUES (?, ?, 0, 0, 0, 0)`).run(sessionId, messageCount);
6
+ VALUES (${sessionId}, ${messageCount}, 0, 0, 0, 0)`.run(db);
6
7
  }
7
8
  export function readStats(db, sessionId) {
8
- const row = db
9
- .prepare(`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total
10
- FROM session_stats
11
- WHERE session_id = ?`)
12
- .get(sessionId);
9
+ const row = sql `SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total
10
+ FROM session_stats
11
+ WHERE session_id = ${sessionId}`.get(db);
13
12
  if (!row)
14
13
  throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
15
14
  return {
@@ -21,25 +20,21 @@ export function readStats(db, sessionId) {
21
20
  };
22
21
  }
23
22
  export function incrementMessageCount(db, sessionId) {
24
- const result = db
25
- .prepare("UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?")
26
- .run(sessionId);
23
+ const result = sql `UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ${sessionId}`.run(db);
27
24
  if (result.changes !== 1)
28
25
  throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
29
26
  }
30
27
  export function addUsageToStats(db, sessionId, usage) {
31
- const result = db
32
- .prepare(`UPDATE session_stats
33
- SET cached_tokens = cached_tokens + ?,
34
- uncached_tokens = uncached_tokens + ?,
35
- total_tokens = total_tokens + ?,
36
- cost_total = cost_total + ?
37
- WHERE session_id = ?`)
38
- .run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);
28
+ const result = sql `UPDATE session_stats
29
+ SET cached_tokens = cached_tokens + ${usage.cacheRead},
30
+ uncached_tokens = uncached_tokens + ${usage.input + usage.cacheWrite},
31
+ total_tokens = total_tokens + ${usage.totalTokens},
32
+ cost_total = cost_total + ${usage.cost.total}
33
+ WHERE session_id = ${sessionId}`.run(db);
39
34
  if (result.changes !== 1)
40
35
  throw new SessionError("storage", `Missing stats row for session ${sessionId}`);
41
36
  }
42
37
  export function deleteStats(db, sessionId) {
43
- db.prepare("DELETE FROM session_stats WHERE session_id = ?").run(sessionId);
38
+ sql `DELETE FROM session_stats WHERE session_id = ${sessionId}`.run(db);
44
39
  }
45
40
  //# sourceMappingURL=session-stats.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-stats.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,+BAA+B,CAAC;AAahF,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAE,YAAY,GAAG,CAAC,EAAQ;IAC1F,EAAE,CAAC,OAAO,CACT;;6BAE2B,CAC3B,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAAA,CAC/B;AAED,MAAM,UAAU,SAAS,CAAC,EAAkB,EAAE,SAAiB,EAAgB;IAC9E,MAAM,GAAG,GAAG,EAAE;SACZ,OAAO,CACP;;wBAEqB,CACrB;SACA,GAAG,CAAkB,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;IAC1F,OAAO;QACN,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,cAAc,EAAE,GAAG,CAAC,eAAe;QACnC,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,SAAS,EAAE,GAAG,CAAC,UAAU;KACzB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IAClF,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CAAC,iFAAiF,CAAC;SAC1F,GAAG,CAAC,SAAS,CAAC,CAAC;IACjB,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAY,EAAQ;IAC1F,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CACP;;;;;wBAKqB,CACrB;SACA,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACvG,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IACxE,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAC5E","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tdb.prepare(\n\t\t`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (?, ?, 0, 0, 0, 0)`,\n\t).run(sessionId, messageCount);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\t\tFROM session_stats\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.get<SessionStatsRow>(sessionId);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = db\n\t\t.prepare(\"UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ?\")\n\t\t.run(sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE session_stats\n\t\t\tSET cached_tokens = cached_tokens + ?,\n\t\t\t\tuncached_tokens = uncached_tokens + ?,\n\t\t\t\ttotal_tokens = total_tokens + ?,\n\t\t\t\tcost_total = cost_total + ?\n\t\t\tWHERE session_id = ?`,\n\t\t)\n\t\t.run(usage.cacheRead, usage.input + usage.cacheWrite, usage.totalTokens, usage.cost.total, sessionId);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tdb.prepare(\"DELETE FROM session_stats WHERE session_id = ?\").run(sessionId);\n}\n"]}
1
+ {"version":3,"file":"session-stats.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,+BAA+B,CAAC;AAEhF,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAYhC,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAE,YAAY,GAAG,CAAC,EAAQ;IAC1F,GAAG,CAAA;;aAES,SAAS,KAAK,YAAY,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAC9D;AAED,MAAM,UAAU,SAAS,CAAC,EAAkB,EAAE,SAAiB,EAAgB;IAC9E,MAAM,GAAG,GAAG,GAAG,CAAA;;uBAEO,SAAS,EAAE,CAAC,GAAG,CAAkB,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;IAC1F,OAAO;QACN,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,cAAc,EAAE,GAAG,CAAC,eAAe;QACnC,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,SAAS,EAAE,GAAG,CAAC,UAAU;KACzB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IAClF,MAAM,MAAM,GAAG,GAAG,CAAA,iFAAiF,SAAS,EAAE,CAAC,GAAG,CACjH,EAAE,CACF,CAAC;IACF,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,eAAe,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAY,EAAQ;IAC1F,MAAM,MAAM,GAAG,GAAG,CAAA;wCACqB,KAAK,CAAC,SAAS;yCACd,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU;mCACpC,KAAK,CAAC,WAAW;+BACrB,KAAK,CAAC,IAAI,CAAC,KAAK;uBACxB,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iCAAiC,SAAS,EAAE,CAAC,CAAC;AAAA,CAC1G;AAED,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IACxE,GAAG,CAAA,gDAAgD,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACvE","sourcesContent":["import { SessionError, type SessionStats } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionStatsRow {\n\tsession_id: string;\n\tmessage_count: number;\n\tcached_tokens: number;\n\tuncached_tokens: number;\n\ttotal_tokens: number;\n\tcost_total: number;\n}\n\nexport function createStats(db: SqliteDatabase, sessionId: string, messageCount = 0): void {\n\tsql`INSERT INTO session_stats\n\t\t\t(session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total)\n\t\t\tVALUES (${sessionId}, ${messageCount}, 0, 0, 0, 0)`.run(db);\n}\n\nexport function readStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = sql`SELECT session_id, message_count, cached_tokens, uncached_tokens, total_tokens, cost_total\n\t\tFROM session_stats\n\t\tWHERE session_id = ${sessionId}`.get<SessionStatsRow>(db);\n\tif (!row) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tcachedTokens: row.cached_tokens,\n\t\tuncachedTokens: row.uncached_tokens,\n\t\ttotalTokens: row.total_tokens,\n\t\tcostTotal: row.cost_total,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tconst result = sql`UPDATE session_stats SET message_count = message_count + 1 WHERE session_id = ${sessionId}`.run(\n\t\tdb,\n\t);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function addUsageToStats(db: SqliteDatabase, sessionId: string, usage: Usage): void {\n\tconst result = sql`UPDATE session_stats\n\t\tSET cached_tokens = cached_tokens + ${usage.cacheRead},\n\t\t\tuncached_tokens = uncached_tokens + ${usage.input + usage.cacheWrite},\n\t\t\ttotal_tokens = total_tokens + ${usage.totalTokens},\n\t\t\tcost_total = cost_total + ${usage.cost.total}\n\t\tWHERE session_id = ${sessionId}`.run(db);\n\tif (result.changes !== 1) throw new SessionError(\"storage\", `Missing stats row for session ${sessionId}`);\n}\n\nexport function deleteStats(db: SqliteDatabase, sessionId: string): void {\n\tsql`DELETE FROM session_stats WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import type { SqliteDatabase, SqliteSessionMetadata } from "../types.ts";
2
2
  export interface SessionRow {
3
3
  id: string;
4
- created_at: string;
4
+ created_at: number;
5
5
  metadata: string | null;
6
6
  cwd: string;
7
7
  parent_session_id: string | null;
@@ -10,7 +10,7 @@ export interface SessionRow {
10
10
  }
11
11
  export interface NewSessionRow {
12
12
  id: string;
13
- createdAt: string;
13
+ createdAt: number;
14
14
  cwd: string;
15
15
  parentSessionId?: string;
16
16
  metadata?: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAElE;AAWD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,QAQ1E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAmBnE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,gBAqBjF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAsBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAY1F","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: string;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: string;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!db.prepare(\"SELECT 1 AS found FROM sessions WHERE id = ?\").get<{ found: number }>(sessionId);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tdb.prepare(\"INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsession.id,\n\t\tsession.createdAt,\n\t\tserializeMetadata(session.metadata),\n\t\tsession.cwd,\n\t\tsession.parentSessionId ?? null,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\tWHERE s.id = ?`,\n\t\t)\n\t\t.get<SessionRow>(sessionId);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? \"\" : \"WHERE s.cwd = ?\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\t${where}\n\t\t\tORDER BY s.created_at DESC`,\n\t\t)\n\t\t.all<SessionRow>(...(options.cwd === undefined ? [] : [options.cwd]));\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM sessions WHERE id = ?\").run(sessionId);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string {\n\tif (value === null) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: Date.parse(row.created_at),\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAElE;AAWD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,QAK1E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAenE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,gBAiBjF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAoBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAY1F","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: number;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: number;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!sql`SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get<{ found: number }>(db);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tsql`INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id)\n\t\tVALUES (${session.id}, ${session.createdAt}, ${serializeMetadata(session.metadata)}, ${session.cwd}, ${session.parentSessionId ?? null})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\tWHERE s.id = ${sessionId}`.get<SessionRow>(db);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? sql`` : sql`WHERE s.cwd = ${options.cwd}`;\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\t${where}\n\t\tORDER BY s.created_at DESC`.all<SessionRow>(db);\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string | undefined {\n\tif (value === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: row.created_at,\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { assertJsonSerializable, SessionError } from "@earendil-works/pi-agent-core";
2
+ import { sql } from "../sql.js";
2
3
  function parseMetadata(metadata, sessionId) {
3
4
  if (metadata === null)
4
5
  return undefined;
@@ -15,7 +16,7 @@ function parseMetadata(metadata, sessionId) {
15
16
  return parsed;
16
17
  }
17
18
  export function sessionExists(db, sessionId) {
18
- return !!db.prepare("SELECT 1 AS found FROM sessions WHERE id = ?").get(sessionId);
19
+ return !!sql `SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get(db);
19
20
  }
20
21
  function serializeMetadata(metadata) {
21
22
  if (metadata === undefined)
@@ -27,53 +28,49 @@ function serializeMetadata(metadata) {
27
28
  return JSON.stringify(metadata);
28
29
  }
29
30
  export function insertSessionRow(db, session) {
30
- db.prepare("INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)").run(session.id, session.createdAt, serializeMetadata(session.metadata), session.cwd, session.parentSessionId ?? null);
31
+ sql `INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id)
32
+ VALUES (${session.id}, ${session.createdAt}, ${serializeMetadata(session.metadata)}, ${session.cwd}, ${session.parentSessionId ?? null})`.run(db);
31
33
  }
32
34
  export function readSessionRow(db, sessionId) {
33
- return db
34
- .prepare(`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
35
- name_fact.seq IS NOT NULL AS has_session_name,
36
- name_fact.value AS session_name
37
- FROM sessions AS s
38
- LEFT JOIN facts AS name_fact
39
- ON name_fact.session_id = s.id
40
- AND name_fact.kind = 'name'
41
- AND name_fact.key IS NULL
42
- AND name_fact.seq = (
43
- SELECT MAX(f.seq)
44
- FROM facts AS f
45
- WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
46
- )
47
- WHERE s.id = ?`)
48
- .get(sessionId);
35
+ return sql `SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
36
+ name_fact.seq IS NOT NULL AS has_session_name,
37
+ name_fact.value AS session_name
38
+ FROM sessions AS s
39
+ LEFT JOIN facts AS name_fact
40
+ ON name_fact.session_id = s.id
41
+ AND name_fact.kind = 'name'
42
+ AND name_fact.key IS NULL
43
+ AND name_fact.seq = (
44
+ SELECT MAX(f.seq)
45
+ FROM facts AS f
46
+ WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
47
+ )
48
+ WHERE s.id = ${sessionId}`.get(db);
49
49
  }
50
50
  export function readSessionRows(db, options = {}) {
51
- const where = options.cwd === undefined ? "" : "WHERE s.cwd = ?";
52
- return db
53
- .prepare(`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
54
- name_fact.seq IS NOT NULL AS has_session_name,
55
- name_fact.value AS session_name
56
- FROM sessions AS s
57
- LEFT JOIN facts AS name_fact
58
- ON name_fact.session_id = s.id
59
- AND name_fact.kind = 'name'
60
- AND name_fact.key IS NULL
61
- AND name_fact.seq = (
62
- SELECT MAX(f.seq)
63
- FROM facts AS f
64
- WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
65
- )
66
- ${where}
67
- ORDER BY s.created_at DESC`)
68
- .all(...(options.cwd === undefined ? [] : [options.cwd]));
51
+ const where = options.cwd === undefined ? sql `` : sql `WHERE s.cwd = ${options.cwd}`;
52
+ return sql `SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
53
+ name_fact.seq IS NOT NULL AS has_session_name,
54
+ name_fact.value AS session_name
55
+ FROM sessions AS s
56
+ LEFT JOIN facts AS name_fact
57
+ ON name_fact.session_id = s.id
58
+ AND name_fact.kind = 'name'
59
+ AND name_fact.key IS NULL
60
+ AND name_fact.seq = (
61
+ SELECT MAX(f.seq)
62
+ FROM facts AS f
63
+ WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
64
+ )
65
+ ${where}
66
+ ORDER BY s.created_at DESC`.all(db);
69
67
  }
70
68
  export function deleteSessionRow(db, sessionId) {
71
- db.prepare("DELETE FROM sessions WHERE id = ?").run(sessionId);
69
+ sql `DELETE FROM sessions WHERE id = ${sessionId}`.run(db);
72
70
  }
73
71
  function parseSessionName(value, sessionId) {
74
- if (value === null) {
75
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name must be a string`);
76
- }
72
+ if (value === null)
73
+ return undefined;
77
74
  let parsed;
78
75
  try {
79
76
  parsed = JSON.parse(value);
@@ -91,7 +88,7 @@ export function decodeSessionMetadata(row, path) {
91
88
  const name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);
92
89
  return {
93
90
  id: row.id,
94
- createdAt: Date.parse(row.created_at),
91
+ createdAt: row.created_at,
95
92
  ...(name === undefined ? {} : { name }),
96
93
  cwd: row.cwd,
97
94
  path,
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: string;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: string;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!db.prepare(\"SELECT 1 AS found FROM sessions WHERE id = ?\").get<{ found: number }>(sessionId);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tdb.prepare(\"INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsession.id,\n\t\tsession.createdAt,\n\t\tserializeMetadata(session.metadata),\n\t\tsession.cwd,\n\t\tsession.parentSessionId ?? null,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\tWHERE s.id = ?`,\n\t\t)\n\t\t.get<SessionRow>(sessionId);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? \"\" : \"WHERE s.cwd = ?\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\t${where}\n\t\t\tORDER BY s.created_at DESC`,\n\t\t)\n\t\t.all<SessionRow>(...(options.cwd === undefined ? [] : [options.cwd]));\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM sessions WHERE id = ?\").run(sessionId);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string {\n\tif (value === null) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: Date.parse(row.created_at),\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: number;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: number;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!sql`SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get<{ found: number }>(db);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tsql`INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id)\n\t\tVALUES (${session.id}, ${session.createdAt}, ${serializeMetadata(session.metadata)}, ${session.cwd}, ${session.parentSessionId ?? null})`.run(\n\t\tdb,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\tWHERE s.id = ${sessionId}`.get<SessionRow>(db);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? sql`` : sql`WHERE s.cwd = ${options.cwd}`;\n\treturn sql`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\tname_fact.value AS session_name\n\t\tFROM sessions AS s\n\t\tLEFT JOIN facts AS name_fact\n\t\t\tON name_fact.session_id = s.id\n\t\t\tAND name_fact.kind = 'name'\n\t\t\tAND name_fact.key IS NULL\n\t\t\tAND name_fact.seq = (\n\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\tFROM facts AS f\n\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t)\n\t\t${where}\n\t\tORDER BY s.created_at DESC`.all<SessionRow>(db);\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string | undefined {\n\tif (value === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: row.created_at,\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"writer-leases.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM;;;;cAenB;AAED,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,WAWnB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,QAM3F;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEtE","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\t\tVALUES (?, ?, 1, ?)\n\t\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\t\towner_id = excluded.owner_id,\n\t\t\t\tfence = writer_leases.fence + 1,\n\t\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\t\tWHERE writer_leases.expires_at_ms <= ?\n\t\t\tRETURNING owner_id, fence, expires_at_ms`,\n\t\t)\n\t\t.get<WriterLeaseRow>(sessionId, ownerId, expiresAtMs, now);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE writer_leases\n\t\t\tSET expires_at_ms = ?\n\t\t\tWHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`,\n\t\t)\n\t\t.run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?\").run(\n\t\tsessionId,\n\t\tlease.ownerId,\n\t\tlease.fence,\n\t);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ?\").run(sessionId);\n}\n"]}
1
+ {"version":3,"file":"writer-leases.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM;;;;cAWnB;AAED,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,WAUnB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,QAG3F;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEtE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = sql`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\tVALUES (${sessionId}, ${ownerId}, 1, ${expiresAtMs})\n\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\towner_id = excluded.owner_id,\n\t\t\tfence = writer_leases.fence + 1,\n\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\tWHERE writer_leases.expires_at_ms <= ${now}\n\t\tRETURNING owner_id, fence, expires_at_ms`.get<WriterLeaseRow>(db);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = sql`UPDATE writer_leases\n\t\tSET expires_at_ms = ${expiresAtMs}\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND owner_id = ${lease.ownerId}\n\t\t\tAND fence = ${lease.fence}\n\t\t\tAND expires_at_ms > ${now}`.run(db);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tsql`DELETE FROM writer_leases\n\t\tWHERE session_id = ${sessionId} AND owner_id = ${lease.ownerId} AND fence = ${lease.fence}`.run(db);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM writer_leases WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -1,30 +1,31 @@
1
+ import { sql } from "../sql.js";
1
2
  export function acquireWriterLease(db, sessionId, ownerId, now, expiresAtMs) {
2
- const row = db
3
- .prepare(`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)
4
- VALUES (?, ?, 1, ?)
5
- ON CONFLICT(session_id) DO UPDATE SET
6
- owner_id = excluded.owner_id,
7
- fence = writer_leases.fence + 1,
8
- expires_at_ms = excluded.expires_at_ms
9
- WHERE writer_leases.expires_at_ms <= ?
10
- RETURNING owner_id, fence, expires_at_ms`)
11
- .get(sessionId, ownerId, expiresAtMs, now);
3
+ const row = sql `INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)
4
+ VALUES (${sessionId}, ${ownerId}, 1, ${expiresAtMs})
5
+ ON CONFLICT(session_id) DO UPDATE SET
6
+ owner_id = excluded.owner_id,
7
+ fence = writer_leases.fence + 1,
8
+ expires_at_ms = excluded.expires_at_ms
9
+ WHERE writer_leases.expires_at_ms <= ${now}
10
+ RETURNING owner_id, fence, expires_at_ms`.get(db);
12
11
  return row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };
13
12
  }
14
13
  export function renewWriterLease(db, sessionId, lease, now, expiresAtMs) {
15
- const result = db
16
- .prepare(`UPDATE writer_leases
17
- SET expires_at_ms = ?
18
- WHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`)
19
- .run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);
14
+ const result = sql `UPDATE writer_leases
15
+ SET expires_at_ms = ${expiresAtMs}
16
+ WHERE session_id = ${sessionId}
17
+ AND owner_id = ${lease.ownerId}
18
+ AND fence = ${lease.fence}
19
+ AND expires_at_ms > ${now}`.run(db);
20
20
  if (result.changes === 1)
21
21
  lease.expiresAtMs = expiresAtMs;
22
22
  return result.changes === 1;
23
23
  }
24
24
  export function releaseWriterLease(db, sessionId, lease) {
25
- db.prepare("DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?").run(sessionId, lease.ownerId, lease.fence);
25
+ sql `DELETE FROM writer_leases
26
+ WHERE session_id = ${sessionId} AND owner_id = ${lease.ownerId} AND fence = ${lease.fence}`.run(db);
26
27
  }
27
28
  export function deleteWriterLease(db, sessionId) {
28
- db.prepare("DELETE FROM writer_leases WHERE session_id = ?").run(sessionId);
29
+ sql `DELETE FROM writer_leases WHERE session_id = ${sessionId}`.run(db);
29
30
  }
30
31
  //# sourceMappingURL=writer-leases.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"writer-leases.js","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,kBAAkB,CACjC,EAAkB,EAClB,SAAiB,EACjB,OAAe,EACf,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,GAAG,GAAG,EAAE;SACZ,OAAO,CACP;;;;;;;4CAOyC,CACzC;SACA,GAAG,CAAiB,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IAC5D,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC;AAAA,CACnH;AAED,MAAM,UAAU,gBAAgB,CAC/B,EAAkB,EAClB,SAAiB,EACjB,KAAkB,EAClB,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CACP;;6EAE0E,CAC1E;SACA,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAkB,EAAE;IAC7F,EAAE,CAAC,OAAO,CAAC,+EAA+E,CAAC,CAAC,GAAG,CAC9F,SAAS,EACT,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,KAAK,CACX,CAAC;AAAA,CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACxE,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAC5E","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\t\tVALUES (?, ?, 1, ?)\n\t\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\t\towner_id = excluded.owner_id,\n\t\t\t\tfence = writer_leases.fence + 1,\n\t\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\t\tWHERE writer_leases.expires_at_ms <= ?\n\t\t\tRETURNING owner_id, fence, expires_at_ms`,\n\t\t)\n\t\t.get<WriterLeaseRow>(sessionId, ownerId, expiresAtMs, now);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE writer_leases\n\t\t\tSET expires_at_ms = ?\n\t\t\tWHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`,\n\t\t)\n\t\t.run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?\").run(\n\t\tsessionId,\n\t\tlease.ownerId,\n\t\tlease.fence,\n\t);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ?\").run(sessionId);\n}\n"]}
1
+ {"version":3,"file":"writer-leases.js","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAehC,MAAM,UAAU,kBAAkB,CACjC,EAAkB,EAClB,SAAiB,EACjB,OAAe,EACf,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,GAAG,GAAG,GAAG,CAAA;YACJ,SAAS,KAAK,OAAO,QAAQ,WAAW;;;;;yCAKX,GAAG;2CACD,CAAC,GAAG,CAAiB,EAAE,CAAC,CAAC;IACnE,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC;AAAA,CACnH;AAED,MAAM,UAAU,gBAAgB,CAC/B,EAAkB,EAClB,SAAiB,EACjB,KAAkB,EAClB,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,MAAM,GAAG,GAAG,CAAA;wBACK,WAAW;uBACZ,SAAS;oBACZ,KAAK,CAAC,OAAO;iBAChB,KAAK,CAAC,KAAK;yBACH,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAkB,EAAE;IAC7F,GAAG,CAAA;uBACmB,SAAS,mBAAmB,KAAK,CAAC,OAAO,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACrG;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACxE,GAAG,CAAA,gDAAgD,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACvE","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = sql`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\tVALUES (${sessionId}, ${ownerId}, 1, ${expiresAtMs})\n\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\towner_id = excluded.owner_id,\n\t\t\tfence = writer_leases.fence + 1,\n\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\tWHERE writer_leases.expires_at_ms <= ${now}\n\t\tRETURNING owner_id, fence, expires_at_ms`.get<WriterLeaseRow>(db);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = sql`UPDATE writer_leases\n\t\tSET expires_at_ms = ${expiresAtMs}\n\t\tWHERE session_id = ${sessionId}\n\t\t\tAND owner_id = ${lease.ownerId}\n\t\t\tAND fence = ${lease.fence}\n\t\t\tAND expires_at_ms > ${now}`.run(db);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tsql`DELETE FROM writer_leases\n\t\tWHERE session_id = ${sessionId} AND owner_id = ${lease.ownerId} AND fence = ${lease.fence}`.run(db);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tsql`DELETE FROM writer_leases WHERE session_id = ${sessionId}`.run(db);\n}\n"]}
@@ -11,6 +11,7 @@ export interface SqliteStatement {
11
11
  run(...params: unknown[]): SqliteRunResult;
12
12
  get<TRow extends object>(...params: unknown[]): TRow | undefined;
13
13
  all<TRow extends object>(...params: unknown[]): TRow[];
14
+ iterate<TRow extends object>(...params: unknown[]): Iterable<TRow>;
14
15
  }
15
16
  /** SQLite database capability used by the SQLite session backend. */
16
17
  export interface SqliteDatabase {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEvG,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC/B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IAC3C,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IACjE,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;CACvD;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,oFAAoF;IACpF,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEvG,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC/B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IAC3C,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IACjE,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IACvD,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,oFAAoF;IACpF,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow>;\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n\titerate<TRow extends object>(...params: unknown[]): Iterable<TRow>;\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-session-backend-sqlite-node",
3
- "version": "0.84.0",
3
+ "version": "0.84.2",
4
4
  "description": "Node sqlite session backend for @earendil-works/pi-agent-core sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,8 +34,8 @@
34
34
  "node": ">=22.19.0"
35
35
  },
36
36
  "dependencies": {
37
- "@earendil-works/pi-ai": "^0.84.0",
38
- "@earendil-works/pi-agent-core": "^0.84.0"
37
+ "@earendil-works/pi-ai": "^0.84.2",
38
+ "@earendil-works/pi-agent-core": "^0.84.2"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@vitest/coverage-v8": "4.1.9",