@morlay/session-rdb 0.0.15 → 0.0.16-alpha.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 (65) hide show
  1. package/README.md +11 -13
  2. package/dist/artifact.d.mts +6 -16
  3. package/dist/artifact.mjs +3 -3
  4. package/dist/{import-DFed8DBt.mjs → import-CKrzjXVB.mjs} +73 -83
  5. package/dist/import.d.mts +2 -3
  6. package/dist/import.mjs +2 -2
  7. package/dist/index-CgGDHQSK.d.mts +225 -0
  8. package/dist/index.d.mts +3 -3
  9. package/dist/index.mjs +694 -169
  10. package/dist/log-DTJsxMud.mjs +314 -0
  11. package/dist/{schema-CFXZURX7.d.mts → schema-COK7-wRV.d.mts} +3 -15
  12. package/dist/sqlite-DCy4VTD8.mjs +698 -0
  13. package/dist/storage.d.mts +2 -7
  14. package/dist/storage.mjs +2 -3
  15. package/dist/testing.d.mts +30 -1
  16. package/dist/testing.mjs +676 -1991
  17. package/drizzle/postgres/20260908072805_v2_initial/migration.sql +58 -0
  18. package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +686 -0
  19. package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +1 -0
  20. package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +673 -0
  21. package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +53 -0
  22. package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +492 -0
  23. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +6 -0
  24. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +513 -0
  25. package/package.json +17 -12
  26. package/src/adapters/index.ts +10 -2
  27. package/src/adapters/to-postgres.ts +19 -15
  28. package/src/adapters/to-sqlite.ts +20 -14
  29. package/src/{entities → adapters}/types.ts +7 -8
  30. package/src/backend.ts +0 -7
  31. package/src/branch.ts +29 -110
  32. package/src/drizzle/postgres-v2.ts +11 -0
  33. package/src/drizzle/postgres-v3.ts +11 -0
  34. package/src/drizzle/sqlite-v2.ts +10 -0
  35. package/src/drizzle/sqlite-v3.ts +11 -0
  36. package/src/entities/index.ts +2 -30
  37. package/src/entities/v2/index.ts +20 -0
  38. package/src/entities/v2/session-events.ts +11 -0
  39. package/src/entities/v3/events.ts +27 -0
  40. package/src/entities/v3/index.ts +27 -0
  41. package/src/entities/v3/persistence-state.ts +10 -0
  42. package/src/entities/v3/schema-meta.ts +9 -0
  43. package/src/entities/v3/session-events.ts +26 -0
  44. package/src/entities/v3/sessions.ts +20 -0
  45. package/src/import.ts +65 -57
  46. package/src/index.ts +868 -223
  47. package/src/invariant.ts +0 -2
  48. package/src/legacy.ts +67 -0
  49. package/src/log.ts +41 -87
  50. package/src/postgres.ts +75 -93
  51. package/src/schema.ts +3 -14
  52. package/src/sqlite.ts +59 -46
  53. package/src/testing/contract.ts +460 -375
  54. package/src/testing/coordinator-contract.ts +108 -1374
  55. package/src/testing.ts +1 -6
  56. package/dist/index-uUvn6gYp.d.mts +0 -111
  57. package/dist/schema-vwzwEXNE.mjs +0 -878
  58. package/dist/sqlite-CG_Qcx5C.mjs +0 -356
  59. package/src/adapters/ddl.ts +0 -77
  60. package/src/entities/events.ts +0 -27
  61. package/src/entities/persistence-state.ts +0 -10
  62. package/src/entities/schema-meta.ts +0 -9
  63. package/src/entities/session-events.ts +0 -29
  64. package/src/entities/sessions.ts +0 -20
  65. package/src/migrate.ts +0 -137
@@ -0,0 +1,698 @@
1
+ import { l as sessionConflictRow, u as sessionInsertRow } from "./log-DTJsxMud.mjs";
2
+ import { randomUUID } from "node:crypto";
3
+ import { and, eq, gte, sql } from "drizzle-orm";
4
+ import { check, index, integer, sqliteTable, text, unique } from "drizzle-orm/sqlite-core";
5
+ import { bigint, check as check$1, index as index$1, integer as integer$1, pgSchema, pgTable, serial, text as text$1, unique as unique$1 } from "drizzle-orm/pg-core";
6
+ import { readdirSync, statSync } from "node:fs";
7
+ import { mkdir, open } from "node:fs/promises";
8
+ import { dirname, resolve } from "node:path";
9
+ import { DatabaseSync } from "node:sqlite";
10
+ import { drizzle } from "drizzle-orm/node-sqlite";
11
+ import { migrate } from "drizzle-orm/node-sqlite/migrator";
12
+ //#region src/write-guard.ts
13
+ var WriteGuard = class {
14
+ headSeqs = /* @__PURE__ */ new Map();
15
+ confirmHead(id, head) {
16
+ this.headSeqs.set(id, head);
17
+ }
18
+ assertNoConcurrentWriter(id, storedHead) {
19
+ const known = this.headSeqs.get(id);
20
+ if (known === void 0) {
21
+ if (storedHead !== -1) throw new Error(`session "${id}" has a persisted log this instance has not read; another writer may own it — load the session first`);
22
+ return;
23
+ }
24
+ if (known !== storedHead) throw new Error(`session "${id}" was modified by another writer (stored head ${storedHead}, this instance last confirmed head ${known}); concurrent writers on one session are not supported`);
25
+ }
26
+ };
27
+ //#endregion
28
+ //#region src/adapters/types.ts
29
+ function toProperty(name) {
30
+ return name.replace(/_([a-z])/g, (_match, char) => char.toUpperCase());
31
+ }
32
+ //#endregion
33
+ //#region src/adapters/to-sqlite.ts
34
+ function buildColumn$1(name, c, tables) {
35
+ let col;
36
+ switch (c.type) {
37
+ case "text": {
38
+ const built = text(name);
39
+ col = c.primaryKey ? built.primaryKey() : built;
40
+ break;
41
+ }
42
+ case "serial":
43
+ col = integer(name).primaryKey({ autoIncrement: true });
44
+ break;
45
+ case "integer":
46
+ case "bigint": {
47
+ const built = integer(name);
48
+ col = c.primaryKey ? built.primaryKey() : built;
49
+ break;
50
+ }
51
+ }
52
+ if (c.notNull) col = col.notNull();
53
+ if (c.default !== void 0) col = col.default(c.default);
54
+ if (c.unique) col = col.unique();
55
+ if (c.references) {
56
+ const { table, column, onDelete } = c.references;
57
+ col = col.references(() => tables[table][toProperty(column)], { onDelete });
58
+ }
59
+ return col;
60
+ }
61
+ function toSqliteSchema(defs) {
62
+ const tables = {};
63
+ for (const def of defs) {
64
+ const columns = {};
65
+ for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn$1(name, c, tables);
66
+ const extra = (self) => [
67
+ ...Object.entries(def.checks ?? {}).map(([name, c]) => check(name, sql.raw(c.expression))),
68
+ ...Object.entries(def.uniques ?? {}).map(([name, u]) => unique(name).on(...u.columns.map((n) => self[toProperty(n)]))),
69
+ ...Object.entries(def.indexes ?? {}).map(([name, i]) => index(name).on(...i.columns.map((n) => self[toProperty(n)])))
70
+ ];
71
+ tables[def.name] = sqliteTable(def.name, columns, extra);
72
+ }
73
+ return tables;
74
+ }
75
+ //#endregion
76
+ //#region src/adapters/to-postgres.ts
77
+ function buildColumn(name, c, tables) {
78
+ let col;
79
+ switch (c.type) {
80
+ case "text": {
81
+ const built = text$1(name);
82
+ col = c.primaryKey ? built.primaryKey() : built;
83
+ break;
84
+ }
85
+ case "serial":
86
+ col = serial(name).primaryKey();
87
+ break;
88
+ case "integer": {
89
+ const built = integer$1(name);
90
+ col = c.primaryKey ? built.primaryKey() : built;
91
+ break;
92
+ }
93
+ case "bigint": {
94
+ const built = bigint(name, { mode: "number" });
95
+ col = c.primaryKey ? built.primaryKey() : built;
96
+ break;
97
+ }
98
+ }
99
+ if (c.notNull) col = col.notNull();
100
+ if (c.default !== void 0) col = col.default(c.default);
101
+ if (c.unique) col = col.unique();
102
+ if (c.references) {
103
+ const { table, column, onDelete } = c.references;
104
+ col = col.references(() => tables[table][toProperty(column)], { onDelete });
105
+ }
106
+ return col;
107
+ }
108
+ function toPostgresSchema(defs, schemaName = "public") {
109
+ const tables = {};
110
+ const table = schemaName === "public" ? pgTable : pgSchema(schemaName).table;
111
+ for (const def of defs) {
112
+ const columns = {};
113
+ for (const [name, c] of Object.entries(def.columns)) columns[toProperty(name)] = buildColumn(name, c, tables);
114
+ const extra = (self) => [
115
+ ...Object.entries(def.checks ?? {}).map(([name, c]) => check$1(name, sql.raw(c.expression))),
116
+ ...Object.entries(def.uniques ?? {}).map(([name, u]) => unique$1(name).on(...u.columns.map((n) => self[toProperty(n)]))),
117
+ ...Object.entries(def.indexes ?? {}).map(([name, i]) => index$1(name).on(...i.columns.map((n) => self[toProperty(n)])))
118
+ ];
119
+ tables[def.name] = table(def.name, columns, extra);
120
+ }
121
+ return tables;
122
+ }
123
+ //#endregion
124
+ //#region src/entities/v3/persistence-state.ts
125
+ const persistenceState = {
126
+ name: "t_persistence_state",
127
+ columns: {
128
+ f_singleton: {
129
+ type: "integer",
130
+ primaryKey: true
131
+ },
132
+ f_store_id: {
133
+ type: "text",
134
+ notNull: true
135
+ }
136
+ },
137
+ checks: { ck_persistence_state_singleton: { expression: "f_singleton = 1" } }
138
+ };
139
+ //#endregion
140
+ //#region src/entities/v3/schema-meta.ts
141
+ const schemaMeta = {
142
+ name: "t_schema_meta",
143
+ columns: {
144
+ f_key: {
145
+ type: "text",
146
+ primaryKey: true
147
+ },
148
+ f_value: {
149
+ type: "text",
150
+ notNull: true
151
+ }
152
+ }
153
+ };
154
+ //#endregion
155
+ //#region src/entities/v3/sessions.ts
156
+ const sessions = {
157
+ name: "t_sessions",
158
+ columns: {
159
+ f_id: {
160
+ type: "serial",
161
+ primaryKey: true
162
+ },
163
+ f_session_id: {
164
+ type: "text",
165
+ notNull: true,
166
+ unique: true
167
+ },
168
+ f_head_event_id: {
169
+ type: "text",
170
+ notNull: true,
171
+ default: ""
172
+ },
173
+ f_head_sequence: {
174
+ type: "integer",
175
+ notNull: true,
176
+ default: -1
177
+ },
178
+ f_version: {
179
+ type: "integer",
180
+ notNull: true
181
+ },
182
+ f_created_at: {
183
+ type: "bigint",
184
+ notNull: true
185
+ },
186
+ f_cwd: { type: "text" },
187
+ f_parent_session: { type: "text" },
188
+ f_seed_length: { type: "integer" },
189
+ f_origin: { type: "text" },
190
+ f_delegation_depth: { type: "integer" },
191
+ f_incarnation: {
192
+ type: "text",
193
+ notNull: true
194
+ },
195
+ f_revision: {
196
+ type: "integer",
197
+ notNull: true
198
+ }
199
+ }
200
+ };
201
+ //#endregion
202
+ //#region src/entities/v3/events.ts
203
+ const events = {
204
+ name: "t_events",
205
+ columns: {
206
+ f_id: {
207
+ type: "serial",
208
+ primaryKey: true
209
+ },
210
+ f_event_id: {
211
+ type: "text",
212
+ notNull: true,
213
+ unique: true
214
+ },
215
+ f_parent_id: {
216
+ type: "text",
217
+ notNull: true,
218
+ default: ""
219
+ },
220
+ f_type: {
221
+ type: "text",
222
+ notNull: true,
223
+ default: ""
224
+ },
225
+ f_kind: {
226
+ type: "text",
227
+ notNull: true,
228
+ default: ""
229
+ },
230
+ f_role: {
231
+ type: "text",
232
+ notNull: true,
233
+ default: ""
234
+ },
235
+ f_name: {
236
+ type: "text",
237
+ notNull: true,
238
+ default: ""
239
+ },
240
+ f_action_id: {
241
+ type: "text",
242
+ notNull: true,
243
+ default: ""
244
+ },
245
+ f_encoding: {
246
+ type: "text",
247
+ notNull: true,
248
+ default: ""
249
+ },
250
+ f_data: {
251
+ type: "text",
252
+ notNull: true
253
+ },
254
+ f_created_at: {
255
+ type: "bigint",
256
+ notNull: true,
257
+ default: 0
258
+ }
259
+ },
260
+ indexes: {
261
+ idx_events_kind: { columns: ["f_kind"] },
262
+ idx_events_role: { columns: ["f_role"] },
263
+ idx_events_name: { columns: ["f_name"] },
264
+ idx_events_action_id: { columns: ["f_action_id"] }
265
+ }
266
+ };
267
+ //#endregion
268
+ //#region src/entities/v3/session-events.ts
269
+ const sessionEvents$1 = {
270
+ name: "t_session_events",
271
+ columns: {
272
+ f_id: {
273
+ type: "serial",
274
+ primaryKey: true
275
+ },
276
+ f_session_id: {
277
+ type: "text",
278
+ notNull: true,
279
+ references: {
280
+ table: "t_sessions",
281
+ column: "f_session_id",
282
+ onDelete: "cascade"
283
+ }
284
+ },
285
+ f_event_id: {
286
+ type: "text",
287
+ notNull: true,
288
+ references: {
289
+ table: "t_events",
290
+ column: "f_event_id",
291
+ onDelete: "cascade"
292
+ }
293
+ },
294
+ f_sequence: {
295
+ type: "integer",
296
+ notNull: true
297
+ },
298
+ f_surface_op: { type: "text" }
299
+ },
300
+ uniques: { uq_session_events_session_sequence: { columns: ["f_session_id", "f_sequence"] } },
301
+ indexes: { idx_session_events_event_id: { columns: ["f_event_id"] } }
302
+ };
303
+ //#endregion
304
+ //#region src/entities/v3/index.ts
305
+ const sqliteTableDefs = [
306
+ persistenceState,
307
+ schemaMeta,
308
+ sessions,
309
+ events,
310
+ sessionEvents$1
311
+ ];
312
+ const postgresTableDefs = [
313
+ persistenceState,
314
+ schemaMeta,
315
+ sessions,
316
+ events,
317
+ sessionEvents$1
318
+ ];
319
+ ({ ...sessionEvents$1 }), { ...sessionEvents$1.columns };
320
+ //#endregion
321
+ //#region src/schema.ts
322
+ const SCHEMA_VERSION = 3;
323
+ const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 1146308688;
324
+ const EVENT_ENCODING = "json";
325
+ const sqliteTables = toSqliteSchema(sqliteTableDefs);
326
+ const tPersistenceState = sqliteTables["t_persistence_state"];
327
+ const tSchemaMeta = sqliteTables["t_schema_meta"];
328
+ const tSessions = sqliteTables["t_sessions"];
329
+ const tEvents = sqliteTables["t_events"];
330
+ const tSessionEvents = sqliteTables["t_session_events"];
331
+ const DEFAULT_BUSY_TIMEOUT_MS = 5e3;
332
+ function eventKind(event) {
333
+ switch (event.type) {
334
+ case "user/message": return "message";
335
+ case "assistant/message":
336
+ if ((event.data.message?.content)?.some((block) => block?.type === "reasoning")) return "thinking";
337
+ return "message";
338
+ case "turn/start":
339
+ case "turn/end":
340
+ case "step/start":
341
+ case "step/end":
342
+ case "session/end-seed": return "turn";
343
+ case "tool/call":
344
+ case "tool/result":
345
+ case "tool/code-dispatch-start":
346
+ case "tool/code-dispatch": return "tool";
347
+ case "request/header":
348
+ case "request/context": return "request";
349
+ case "model/selection":
350
+ case "permission/preset":
351
+ case "approval/policy":
352
+ case "sandbox/mode":
353
+ case "plan/mode":
354
+ case "agent-preset/selected": return "config";
355
+ case "approval/asked":
356
+ case "approval/decided":
357
+ case "command/run":
358
+ case "command/done":
359
+ case "hook/invoked":
360
+ case "hook/result":
361
+ case "feedback/record": return "audit";
362
+ case "session/title":
363
+ case "session/title-llm-request":
364
+ case "session-log-deepseek/delivery-accepted": return "lifecycle";
365
+ case "agent/inbox/spliced": return "inbox";
366
+ case "compaction/start":
367
+ case "compaction/end":
368
+ case "compaction/summary":
369
+ case "compaction/prune": return "compaction";
370
+ case "llm/retry":
371
+ case "llm/retry-started": return "llm";
372
+ case "subagent/descriptor":
373
+ case "subagent/model-selection-policy": return "subagent";
374
+ case "team/member":
375
+ case "team/task":
376
+ case "team/message/queued":
377
+ case "team/message/delivered": return "team";
378
+ case "tool-workflow/run-start":
379
+ case "tool-workflow/run-end":
380
+ case "tool-workflow/agent-start":
381
+ case "tool-workflow/agent-end": return "workflow";
382
+ case "goal/change": return "goal";
383
+ case "schedule/change": return "schedule";
384
+ case "todo/write": return "todo";
385
+ case "web/deepseek-search-llm-request": return "web";
386
+ default: return "";
387
+ }
388
+ }
389
+ function eventDimensions(event) {
390
+ const kind = eventKind(event);
391
+ const type = event.type;
392
+ const data = event.data;
393
+ switch (type) {
394
+ case "user/message": return {
395
+ kind,
396
+ role: "user",
397
+ name: "",
398
+ actionId: ""
399
+ };
400
+ case "assistant/message": return {
401
+ kind,
402
+ role: "assistant",
403
+ name: "",
404
+ actionId: ""
405
+ };
406
+ case "tool/result": return {
407
+ kind,
408
+ role: "tool",
409
+ name: "",
410
+ actionId: data["message"]?.content?.[0]?.toolCallId ?? ""
411
+ };
412
+ case "tool/call": return {
413
+ kind,
414
+ role: "",
415
+ name: typeof data["name"] === "string" ? data["name"] : "",
416
+ actionId: typeof data["callId"] === "string" ? data["callId"] : ""
417
+ };
418
+ case "tool/code-dispatch-start":
419
+ case "tool/code-dispatch": return {
420
+ kind,
421
+ role: "",
422
+ name: "",
423
+ actionId: typeof data["subCallId"] === "string" ? data["subCallId"] : ""
424
+ };
425
+ case "command/run":
426
+ case "command/done": return {
427
+ kind,
428
+ role: "",
429
+ name: type === "command/run" && typeof data["name"] === "string" ? data["name"] : "",
430
+ actionId: typeof data["commandId"] === "string" ? data["commandId"] : ""
431
+ };
432
+ case "approval/asked":
433
+ case "approval/decided": return {
434
+ kind,
435
+ role: "",
436
+ name: "",
437
+ actionId: typeof data["id"] === "string" ? data["id"] : ""
438
+ };
439
+ case "hook/invoked":
440
+ case "hook/result": return {
441
+ kind,
442
+ role: "",
443
+ name: "",
444
+ actionId: typeof data["handlerId"] === "string" ? data["handlerId"] : ""
445
+ };
446
+ case "llm/retry":
447
+ case "llm/retry-started": return {
448
+ kind,
449
+ role: "",
450
+ name: "",
451
+ actionId: typeof data["retryId"] === "string" ? data["retryId"] : ""
452
+ };
453
+ case "tool-workflow/run-start":
454
+ case "tool-workflow/run-end":
455
+ case "tool-workflow/agent-start":
456
+ case "tool-workflow/agent-end": return {
457
+ kind,
458
+ role: "",
459
+ name: "",
460
+ actionId: typeof data["runId"] === "string" ? data["runId"] : ""
461
+ };
462
+ case "todo/write": return {
463
+ kind,
464
+ role: "",
465
+ name: "todos",
466
+ actionId: ""
467
+ };
468
+ case "subagent/descriptor": return {
469
+ kind,
470
+ role: "",
471
+ name: typeof data["label"] === "string" ? data["label"] : "",
472
+ actionId: ""
473
+ };
474
+ default: return {
475
+ kind,
476
+ role: "",
477
+ name: "",
478
+ actionId: ""
479
+ };
480
+ }
481
+ }
482
+ //#endregion
483
+ //#region src/sqlite.ts
484
+ /** drizzle-kit 生成的迁移目录(随包根 drizzle/ 发布;src/dist 形态经相对 URL 统一解析)。 */
485
+ const sqliteMigrationsDir = new URL("../drizzle/sqlite/", import.meta.url).pathname;
486
+ const sqliteTxQueues = /* @__PURE__ */ new Map();
487
+ function enqueueSqliteTx(path, fn) {
488
+ const run = (sqliteTxQueues.get(path) ?? Promise.resolve()).then(fn);
489
+ sqliteTxQueues.set(path, run.then(() => void 0, () => void 0));
490
+ return run;
491
+ }
492
+ async function createDatabaseFile(path) {
493
+ try {
494
+ await (await open(path, "wx", 384)).close();
495
+ } catch (error) {
496
+ if (error.code !== "EEXIST") throw error;
497
+ }
498
+ }
499
+ function openDatabase(path, journalMode, busyTimeout = DEFAULT_BUSY_TIMEOUT_MS) {
500
+ const db = new DatabaseSync(path);
501
+ try {
502
+ configureDatabase(db, path, journalMode, busyTimeout);
503
+ const dbx = drizzle({ client: db });
504
+ const { user_version: onDisk } = db.prepare("PRAGMA user_version").get();
505
+ if (onDisk === 2 && !hasMigrationsTable(db)) baselineV2(db);
506
+ migrate(dbx, { migrationsFolder: sqliteMigrationsDir });
507
+ dbx.insert(tPersistenceState).values({
508
+ fSingleton: 1,
509
+ fStoreId: randomUUID()
510
+ }).onConflictDoNothing().run();
511
+ if (onDisk === 0 || onDisk === 2) db.exec(`PRAGMA user_version = 3`);
512
+ db.exec(`PRAGMA journal_mode = ${journalMode.toUpperCase()}`);
513
+ return db;
514
+ } catch (error) {
515
+ db.close();
516
+ throw error;
517
+ }
518
+ }
519
+ function hasMigrationsTable(db) {
520
+ return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '__drizzle_migrations'").get() !== void 0;
521
+ }
522
+ /** 标记 v2 baseline 已应用(迁移表 v1 结构:id/hash/created_at/name/applied_at)。 */
523
+ function baselineV2(db) {
524
+ const dir = readdirSync(sqliteMigrationsDir).find((name) => name.endsWith("_v2_initial"));
525
+ if (dir === void 0) throw new Error("missing v2 baseline migration in drizzle/sqlite");
526
+ db.exec(`
527
+ CREATE TABLE __drizzle_migrations (
528
+ id INTEGER PRIMARY KEY,
529
+ hash text NOT NULL,
530
+ created_at numeric,
531
+ name text,
532
+ applied_at TEXT
533
+ )
534
+ `);
535
+ db.prepare("INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at) VALUES (?, ?, ?, ?)").run("baseline", 0, dir, (/* @__PURE__ */ new Date()).toISOString());
536
+ }
537
+ function configureDatabase(db, path, journalMode, busyTimeout) {
538
+ db.exec("PRAGMA foreign_keys = ON");
539
+ db.exec(`PRAGMA busy_timeout = ${busyTimeout}`);
540
+ drizzle({ client: db }).transaction((tx) => {
541
+ const { user_version: onDisk } = tx.get(sql`PRAGMA user_version`);
542
+ const { application_id: applicationId } = tx.get(sql`PRAGMA application_id`);
543
+ const { count: userObjectCount } = tx.get(sql`SELECT COUNT(*) AS count FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*'`);
544
+ if (onDisk === 0 && (applicationId !== 0 || userObjectCount > 0)) throw new Error(`session database at "${path}" has an unversioned schema or application identity`);
545
+ if (onDisk !== 0 && onDisk !== 2 && onDisk !== 3) throw new Error(`session database at "${path}" has schema version ${onDisk}, incompatible with this build (3)`);
546
+ if (onDisk >= 1 && applicationId !== 1146308688) throw new Error(`session database at "${path}" has application id ${applicationId}, expected ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`);
547
+ if (onDisk === 0) {
548
+ tx.run(sql.raw(`PRAGMA application_id = ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`));
549
+ tx.run(sql.raw(`PRAGMA user_version = 3`));
550
+ }
551
+ }, { behavior: "immediate" });
552
+ }
553
+ var SqliteBackend = class SqliteBackend {
554
+ options;
555
+ kind = "sqlite";
556
+ storeIdentity;
557
+ dbPath = "";
558
+ db;
559
+ constructor(options) {
560
+ this.options = options;
561
+ }
562
+ async open() {
563
+ const actual = this.options.path === ":memory:" ? this.options.path : resolve(this.options.path);
564
+ this.dbPath = actual;
565
+ if (actual !== ":memory:") {
566
+ await mkdir(dirname(actual), {
567
+ recursive: true,
568
+ mode: 448
569
+ });
570
+ await createDatabaseFile(actual);
571
+ }
572
+ await enqueueSqliteTx(actual, async () => {
573
+ this.db = drizzle({ client: openDatabase(actual, this.options.journalMode, this.options.busyTimeout) });
574
+ migrate(this.db, { migrationsFolder: sqliteMigrationsDir });
575
+ });
576
+ try {
577
+ const row = this.db.select({ fStoreId: tPersistenceState.fStoreId }).from(tPersistenceState).where(eq(tPersistenceState.fSingleton, 1)).get();
578
+ /* v8 ignore next -- openDatabase inserts the singleton before returning. */
579
+ if (row === void 0) throw new Error(`session database at "${actual}" has no store identity`);
580
+ if (row.fStoreId.length === 0) throw new Error(`session database at "${actual}" has no valid store identity`);
581
+ if (actual !== ":memory:") {
582
+ const identity = statSync(actual, { bigint: true });
583
+ this.storeIdentity = `file:${identity.dev}:${identity.ino}:${identity.birthtimeNs}:store:${row.fStoreId}`;
584
+ } else this.storeIdentity = `memory:store:${row.fStoreId}`;
585
+ } catch (error) {
586
+ this.db.$client.close();
587
+ throw error;
588
+ }
589
+ }
590
+ async close() {
591
+ if (this.db === void 0) return;
592
+ this.db.$client.close();
593
+ }
594
+ async getSession(id) {
595
+ return this.db.select().from(tSessions).where(eq(tSessions.fSessionId, id)).get();
596
+ }
597
+ async getEventRows(id, fromSequence) {
598
+ return (fromSequence === void 0 ? this.eventRows().where(eq(tSessionEvents.fSessionId, id)) : this.eventRows().where(and(eq(tSessionEvents.fSessionId, id), gte(tSessionEvents.fSequence, fromSequence)))).orderBy(tSessionEvents.fSequence).all();
599
+ }
600
+ async listSessions() {
601
+ return this.db.select().from(tSessions).all();
602
+ }
603
+ async transaction(fn) {
604
+ return enqueueSqliteTx(this.dbPath, async () => {
605
+ this.db.$client.exec("BEGIN IMMEDIATE");
606
+ try {
607
+ const result = await fn(this.tx);
608
+ this.db.$client.exec("COMMIT");
609
+ return result;
610
+ } catch (error) {
611
+ /* v8 ignore start */
612
+ try {
613
+ this.db.$client.exec("ROLLBACK");
614
+ } catch {}
615
+ throw error;
616
+ }
617
+ });
618
+ }
619
+ tx = {
620
+ upsertSession: (storage, incarnation) => this.upsertSession(storage, incarnation),
621
+ getHead: (id) => this.getHead(id),
622
+ getSeedLength: (id) => this.getSeedLength(id),
623
+ updateSeedLength: (id, seedLength) => this.updateSeedLength(id, seedLength),
624
+ insertEvents: (events) => this.insertEvents(events),
625
+ insertBridges: (rows) => this.insertBridges(rows),
626
+ updateHead: (id, headEventId, headSequence) => this.updateHead(id, headEventId, headSequence),
627
+ bumpRevision: (id) => this.bumpRevision(id),
628
+ deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(id, fromSequence),
629
+ getPrevBridge: (id, sequence) => this.getPrevBridge(id, sequence)
630
+ };
631
+ async upsertSession(storage, incarnation) {
632
+ this.db.insert(tSessions).values(sessionInsertRow(storage, incarnation)).onConflictDoUpdate({
633
+ target: tSessions.fSessionId,
634
+ set: sessionConflictRow(storage)
635
+ }).run();
636
+ }
637
+ async getHead(id) {
638
+ const head = this.db.select({
639
+ fHeadEventId: tSessions.fHeadEventId,
640
+ fHeadSequence: tSessions.fHeadSequence
641
+ }).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
642
+ /* v8 ignore next -- appendBatch/commitRepair always materialize the row before reading the head */
643
+ if (head === void 0) throw new Error(`session "${id}" has no materialized row`);
644
+ return head;
645
+ }
646
+ async getSeedLength(id) {
647
+ const row = this.db.select({ fSeedLength: tSessions.fSeedLength }).from(tSessions).where(eq(tSessions.fSessionId, id)).get();
648
+ /* v8 ignore next -- rewind always materializes the row before reading the seed length */
649
+ if (row === void 0) throw new Error(`session "${id}" has no materialized row`);
650
+ return row.fSeedLength;
651
+ }
652
+ async updateSeedLength(id, seedLength) {
653
+ this.db.update(tSessions).set({ fSeedLength: seedLength }).where(eq(tSessions.fSessionId, id)).run();
654
+ }
655
+ static INSERT_BATCH_ROWS = 1e3;
656
+ async insertEvents(events) {
657
+ if (events.length === 0) return;
658
+ for (let i = 0; i < events.length; i += SqliteBackend.INSERT_BATCH_ROWS) this.db.insert(tEvents).values(events.slice(i, i + SqliteBackend.INSERT_BATCH_ROWS).map((event) => ({ ...event }))).run();
659
+ }
660
+ async insertBridges(rows) {
661
+ if (rows.length === 0) return;
662
+ for (let i = 0; i < rows.length; i += SqliteBackend.INSERT_BATCH_ROWS) this.db.insert(tSessionEvents).values(rows.slice(i, i + SqliteBackend.INSERT_BATCH_ROWS).map((row) => ({ ...row }))).run();
663
+ }
664
+ async updateHead(id, headEventId, headSequence) {
665
+ this.db.update(tSessions).set({
666
+ fHeadEventId: headEventId,
667
+ fHeadSequence: headSequence
668
+ }).where(eq(tSessions.fSessionId, id)).run();
669
+ }
670
+ async bumpRevision(id) {
671
+ this.db.update(tSessions).set({ fRevision: sql`${tSessions.fRevision} + 1` }).where(eq(tSessions.fSessionId, id)).run();
672
+ }
673
+ async deleteBridgeTail(id, fromSequence) {
674
+ this.db.delete(tSessionEvents).where(and(eq(tSessionEvents.fSessionId, id), gte(tSessionEvents.fSequence, fromSequence))).run();
675
+ }
676
+ async getPrevBridge(id, sequence) {
677
+ return this.db.select({
678
+ fEventId: tSessionEvents.fEventId,
679
+ fSequence: tSessionEvents.fSequence
680
+ }).from(tSessionEvents).where(and(eq(tSessionEvents.fSessionId, id), eq(tSessionEvents.fSequence, sequence))).get();
681
+ }
682
+ eventRows() {
683
+ return this.db.select({
684
+ fEventId: tSessionEvents.fEventId,
685
+ fSequence: tSessionEvents.fSequence,
686
+ fType: tEvents.fType,
687
+ fKind: tEvents.fKind,
688
+ fRole: tEvents.fRole,
689
+ fName: tEvents.fName,
690
+ fActionId: tEvents.fActionId,
691
+ fCreatedAt: tEvents.fCreatedAt,
692
+ fData: tEvents.fData,
693
+ fSurfaceOp: tSessionEvents.fSurfaceOp
694
+ }).from(tSessionEvents).innerJoin(tEvents, eq(tSessionEvents.fEventId, tEvents.fEventId));
695
+ }
696
+ };
697
+ //#endregion
698
+ export { SCHEMA_VERSION as a, eventKind as c, tSchemaMeta as d, tSessionEvents as f, WriteGuard as g, toPostgresSchema as h, EVENT_ENCODING as i, tEvents as l, postgresTableDefs as m, openDatabase as n, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as o, tSessions as p, DEFAULT_BUSY_TIMEOUT_MS as r, eventDimensions as s, SqliteBackend as t, tPersistenceState as u };