@earendil-works/pi-session-backend-sqlite-node 0.84.3 → 0.85.0

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 (106) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +26 -15
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +9 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/sqlite/index.d.ts +3 -4
  7. package/dist/sqlite/index.d.ts.map +1 -1
  8. package/dist/sqlite/index.js +2 -3
  9. package/dist/sqlite/index.js.map +1 -1
  10. package/dist/sqlite/migrations/001_initial.sql +88 -89
  11. package/dist/sqlite/migrations.d.ts +1 -7
  12. package/dist/sqlite/migrations.d.ts.map +1 -1
  13. package/dist/sqlite/migrations.js +3 -35
  14. package/dist/sqlite/migrations.js.map +1 -1
  15. package/dist/sqlite/repo.d.ts +38 -38
  16. package/dist/sqlite/repo.d.ts.map +1 -1
  17. package/dist/sqlite/repo.js +353 -695
  18. package/dist/sqlite/repo.js.map +1 -1
  19. package/dist/sqlite/session/branch-entries.d.ts +6 -0
  20. package/dist/sqlite/session/branch-entries.d.ts.map +1 -0
  21. package/dist/sqlite/session/branch-entries.js +213 -0
  22. package/dist/sqlite/session/branch-entries.js.map +1 -0
  23. package/dist/sqlite/session/entries.d.ts +24 -0
  24. package/dist/sqlite/session/entries.d.ts.map +1 -0
  25. package/dist/sqlite/session/entries.js +126 -0
  26. package/dist/sqlite/session/entries.js.map +1 -0
  27. package/dist/sqlite/session/session-row.d.ts +23 -0
  28. package/dist/sqlite/session/session-row.d.ts.map +1 -0
  29. package/dist/sqlite/session/session-row.js +69 -0
  30. package/dist/sqlite/session/session-row.js.map +1 -0
  31. package/dist/sqlite/session/session-sequences.d.ts +4 -0
  32. package/dist/sqlite/session/session-sequences.d.ts.map +1 -0
  33. package/dist/sqlite/session/session-sequences.js +13 -0
  34. package/dist/sqlite/session/session-sequences.js.map +1 -0
  35. package/dist/sqlite/session/session-stats.d.ts +6 -0
  36. package/dist/sqlite/session/session-stats.d.ts.map +1 -0
  37. package/dist/sqlite/session/session-stats.js +39 -0
  38. package/dist/sqlite/session/session-stats.js.map +1 -0
  39. package/dist/sqlite/session/usage-ledger.d.ts +20 -0
  40. package/dist/sqlite/session/usage-ledger.d.ts.map +1 -0
  41. package/dist/sqlite/session/usage-ledger.js +50 -0
  42. package/dist/sqlite/session/usage-ledger.js.map +1 -0
  43. package/dist/sqlite/session/values.d.ts +23 -0
  44. package/dist/sqlite/session/values.d.ts.map +1 -0
  45. package/dist/sqlite/session/values.js +91 -0
  46. package/dist/sqlite/session/values.js.map +1 -0
  47. package/dist/sqlite/session.d.ts +42 -0
  48. package/dist/sqlite/session.d.ts.map +1 -0
  49. package/dist/sqlite/session.js +163 -0
  50. package/dist/sqlite/session.js.map +1 -0
  51. package/dist/sqlite/storage.d.ts +38 -0
  52. package/dist/sqlite/storage.d.ts.map +1 -0
  53. package/dist/sqlite/storage.js +161 -0
  54. package/dist/sqlite/storage.js.map +1 -0
  55. package/dist/sqlite/types.d.ts +6 -22
  56. package/dist/sqlite/types.d.ts.map +1 -1
  57. package/dist/sqlite/types.js.map +1 -1
  58. package/package.json +5 -4
  59. package/dist/sqlite/branch-cache.d.ts +0 -6
  60. package/dist/sqlite/branch-cache.d.ts.map +0 -1
  61. package/dist/sqlite/branch-cache.js +0 -70
  62. package/dist/sqlite/branch-cache.js.map +0 -1
  63. package/dist/sqlite/search-backend.d.ts +0 -14
  64. package/dist/sqlite/search-backend.d.ts.map +0 -1
  65. package/dist/sqlite/search-backend.js +0 -153
  66. package/dist/sqlite/search-backend.js.map +0 -1
  67. package/dist/sqlite/storage/branch-entries.d.ts +0 -41
  68. package/dist/sqlite/storage/branch-entries.d.ts.map +0 -1
  69. package/dist/sqlite/storage/branch-entries.js +0 -112
  70. package/dist/sqlite/storage/branch-entries.js.map +0 -1
  71. package/dist/sqlite/storage/branch-tips.d.ts +0 -7
  72. package/dist/sqlite/storage/branch-tips.d.ts.map +0 -1
  73. package/dist/sqlite/storage/branch-tips.js +0 -22
  74. package/dist/sqlite/storage/branch-tips.js.map +0 -1
  75. package/dist/sqlite/storage/entries.d.ts +0 -34
  76. package/dist/sqlite/storage/entries.d.ts.map +0 -1
  77. package/dist/sqlite/storage/entries.js +0 -37
  78. package/dist/sqlite/storage/entries.js.map +0 -1
  79. package/dist/sqlite/storage/facts.d.ts +0 -20
  80. package/dist/sqlite/storage/facts.d.ts.map +0 -1
  81. package/dist/sqlite/storage/facts.js +0 -38
  82. package/dist/sqlite/storage/facts.js.map +0 -1
  83. package/dist/sqlite/storage/lanes.d.ts +0 -35
  84. package/dist/sqlite/storage/lanes.d.ts.map +0 -1
  85. package/dist/sqlite/storage/lanes.js +0 -95
  86. package/dist/sqlite/storage/lanes.js.map +0 -1
  87. package/dist/sqlite/storage/records.d.ts +0 -38
  88. package/dist/sqlite/storage/records.d.ts.map +0 -1
  89. package/dist/sqlite/storage/records.js +0 -49
  90. package/dist/sqlite/storage/records.js.map +0 -1
  91. package/dist/sqlite/storage/session-sequences.d.ts +0 -7
  92. package/dist/sqlite/storage/session-sequences.d.ts.map +0 -1
  93. package/dist/sqlite/storage/session-sequences.js +0 -22
  94. package/dist/sqlite/storage/session-sequences.js.map +0 -1
  95. package/dist/sqlite/storage/session-stats.d.ts +0 -17
  96. package/dist/sqlite/storage/session-stats.d.ts.map +0 -1
  97. package/dist/sqlite/storage/session-stats.js +0 -40
  98. package/dist/sqlite/storage/session-stats.js.map +0 -1
  99. package/dist/sqlite/storage/sessions.d.ts +0 -26
  100. package/dist/sqlite/storage/sessions.d.ts.map +0 -1
  101. package/dist/sqlite/storage/sessions.js +0 -99
  102. package/dist/sqlite/storage/sessions.js.map +0 -1
  103. package/dist/sqlite/storage/writer-leases.d.ts +0 -15
  104. package/dist/sqlite/storage/writer-leases.d.ts.map +0 -1
  105. package/dist/sqlite/storage/writer-leases.js +0 -31
  106. package/dist/sqlite/storage/writer-leases.js.map +0 -1
@@ -1 +0,0 @@
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,99 +0,0 @@
1
- import { assertJsonSerializable, SessionError } from "@earendil-works/pi-agent-core";
2
- import { sql } from "../sql.js";
3
- function parseMetadata(metadata, sessionId) {
4
- if (metadata === null)
5
- return undefined;
6
- let parsed;
7
- try {
8
- parsed = JSON.parse(metadata);
9
- }
10
- catch (error) {
11
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: metadata is not valid JSON`, error instanceof Error ? error : undefined);
12
- }
13
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
14
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: metadata must be an object`);
15
- }
16
- return parsed;
17
- }
18
- export function sessionExists(db, sessionId) {
19
- return !!sql `SELECT 1 AS found FROM sessions WHERE id = ${sessionId}`.get(db);
20
- }
21
- function serializeMetadata(metadata) {
22
- if (metadata === undefined)
23
- return null;
24
- if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
25
- throw new SessionError("invalid_payload", "SQLite session metadata must be an object");
26
- }
27
- assertJsonSerializable(metadata);
28
- return JSON.stringify(metadata);
29
- }
30
- export function insertSessionRow(db, session) {
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);
33
- }
34
- export function readSessionRow(db, 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
- }
50
- export function readSessionRows(db, options = {}) {
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);
67
- }
68
- export function deleteSessionRow(db, sessionId) {
69
- sql `DELETE FROM sessions WHERE id = ${sessionId}`.run(db);
70
- }
71
- function parseSessionName(value, sessionId) {
72
- if (value === null)
73
- return undefined;
74
- let parsed;
75
- try {
76
- parsed = JSON.parse(value);
77
- }
78
- catch (error) {
79
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name is not valid JSON`, error instanceof Error ? error : undefined);
80
- }
81
- if (typeof parsed !== "string") {
82
- throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name must be a string`);
83
- }
84
- return parsed;
85
- }
86
- export function decodeSessionMetadata(row, path) {
87
- const metadata = parseMetadata(row.metadata, row.id);
88
- const name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);
89
- return {
90
- id: row.id,
91
- createdAt: row.created_at,
92
- ...(name === undefined ? {} : { name }),
93
- cwd: row.cwd,
94
- path,
95
- parentSessionId: row.parent_session_id ?? undefined,
96
- metadata,
97
- };
98
- }
99
- //# sourceMappingURL=sessions.js.map
@@ -1 +0,0 @@
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,15 +0,0 @@
1
- import type { SqliteDatabase } from "../types.ts";
2
- export interface WriterLease {
3
- ownerId: string;
4
- fence: number;
5
- expiresAtMs: number;
6
- }
7
- export declare function acquireWriterLease(db: SqliteDatabase, sessionId: string, ownerId: string, now: number, expiresAtMs: number): {
8
- ownerId: string;
9
- fence: number;
10
- expiresAtMs: number;
11
- } | undefined;
12
- export declare function renewWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease, now: number, expiresAtMs: number): boolean;
13
- export declare function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease): void;
14
- export declare function deleteWriterLease(db: SqliteDatabase, sessionId: string): void;
15
- //# sourceMappingURL=writer-leases.d.ts.map
@@ -1 +0,0 @@
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,31 +0,0 @@
1
- import { sql } from "../sql.js";
2
- export function acquireWriterLease(db, sessionId, ownerId, now, expiresAtMs) {
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);
11
- return row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };
12
- }
13
- export function renewWriterLease(db, sessionId, lease, now, expiresAtMs) {
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
- if (result.changes === 1)
21
- lease.expiresAtMs = expiresAtMs;
22
- return result.changes === 1;
23
- }
24
- export function releaseWriterLease(db, sessionId, lease) {
25
- sql `DELETE FROM writer_leases
26
- WHERE session_id = ${sessionId} AND owner_id = ${lease.ownerId} AND fence = ${lease.fence}`.run(db);
27
- }
28
- export function deleteWriterLease(db, sessionId) {
29
- sql `DELETE FROM writer_leases WHERE session_id = ${sessionId}`.run(db);
30
- }
31
- //# sourceMappingURL=writer-leases.js.map
@@ -1 +0,0 @@
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"]}