@morlay/session-rdb 0.0.11 → 0.0.13

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 (56) hide show
  1. package/README.md +0 -16
  2. package/dist/artifact.d.mts +64 -0
  3. package/dist/artifact.mjs +3 -0
  4. package/dist/import-D2vUnUZe.mjs +438 -0
  5. package/dist/import.d.mts +24 -0
  6. package/dist/import.mjs +2 -0
  7. package/dist/index-uUvn6gYp.d.mts +111 -0
  8. package/dist/index.d.mts +3 -0
  9. package/dist/index.mjs +466 -0
  10. package/dist/invariant.d.mts +7 -0
  11. package/dist/invariant.mjs +8 -0
  12. package/dist/magic-string.es-BWtG0AyA.mjs +1017 -0
  13. package/dist/schema-B-8G4lWc.mjs +852 -0
  14. package/dist/schema-CFXZURX7.d.mts +116 -0
  15. package/dist/sqlite-DIXY-dFZ.mjs +356 -0
  16. package/dist/storage.d.mts +44 -0
  17. package/dist/storage.mjs +3 -0
  18. package/dist/testing.d.mts +31 -0
  19. package/dist/testing.mjs +16051 -0
  20. package/package.json +39 -22
  21. package/src/adapters/ddl.ts +77 -0
  22. package/src/adapters/index.ts +4 -0
  23. package/src/adapters/to-postgres.ts +91 -0
  24. package/src/adapters/to-sqlite.ts +79 -0
  25. package/src/artifact.ts +4 -0
  26. package/src/backend.ts +112 -0
  27. package/src/branch.ts +508 -0
  28. package/src/entities/events.ts +27 -0
  29. package/src/entities/index.ts +30 -0
  30. package/src/entities/persistence-state.ts +10 -0
  31. package/src/entities/schema-meta.ts +9 -0
  32. package/src/entities/session-events.ts +29 -0
  33. package/src/entities/sessions.ts +20 -0
  34. package/src/entities/types.ts +48 -0
  35. package/src/import.ts +270 -0
  36. package/src/index.ts +547 -0
  37. package/src/invariant.ts +15 -0
  38. package/src/log.ts +456 -0
  39. package/src/migrate.ts +137 -0
  40. package/src/postgres.ts +369 -0
  41. package/src/schema.ts +234 -0
  42. package/src/sqlite.ts +412 -0
  43. package/src/storage.ts +5 -0
  44. package/src/testing/contract.ts +562 -0
  45. package/src/testing/coordinator-contract.ts +1723 -0
  46. package/src/testing/helpers.ts +20 -0
  47. package/src/testing.ts +12 -0
  48. package/src/write-guard.ts +27 -0
  49. package/lib/index.d.mts +0 -558
  50. package/lib/index.d.mts.map +0 -1
  51. package/lib/index.mjs +0 -2176
  52. package/lib/index.mjs.map +0 -1
  53. package/lib/invariant.d.mts +0 -15
  54. package/lib/invariant.d.mts.map +0 -1
  55. package/lib/invariant.mjs +0 -21
  56. package/lib/invariant.mjs.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,466 @@
1
+ import { i as WriteGuard, r as createTablesSql, t as SqliteBackend } from "./sqlite-DIXY-dFZ.mjs";
2
+ import { A as toJsonlArtifact, C as repairOrphanInboxSplices, D as scanRows, E as rowToMeta, O as sessionConflictRow, _ as buildSeqMap, a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, b as recomputeReplaceProvenance, g as toPostgresSchema, i as SCHEMA_VERSION, k as sessionInsertRow, l as isPersistedEvent, m as postgresTableDefs, n as EPHEMERAL_EVENT_TYPES, o as eventDimensions, r as EVENT_ENCODING, t as DEFAULT_BUSY_TIMEOUT_MS, w as repairSurfaceOps } from "./schema-B-8G4lWc.mjs";
3
+ import { c as SessionBranchRdb, l as SessionBranchRdbProvider, s as registerSessionImport, u as locateTurnEnd } from "./import-D2vUnUZe.mjs";
4
+ import z from "@deepseek-ai/schemastery";
5
+ import { randomUUID } from "node:crypto";
6
+ import { Pool } from "pg";
7
+ import { drizzle } from "drizzle-orm/node-postgres";
8
+ import { PersistenceCoordinator, SessionPersistence, SessionPersistenceRevision } from "@deepseek-ai/dsh-session-persistence";
9
+ import { SessionLogOffset } from "@deepseek-ai/dsh-session";
10
+ import { and, desc, eq, gte, sql } from "drizzle-orm";
11
+ //#region src/postgres.ts
12
+ var PostgresBackend = class PostgresBackend {
13
+ db;
14
+ options;
15
+ kind = "postgres";
16
+ storeIdentity;
17
+ tables;
18
+ constructor(db, options) {
19
+ this.db = db;
20
+ this.options = options;
21
+ this.tables = toPostgresSchema(postgresTableDefs, this.options.schema ?? "public");
22
+ }
23
+ async open() {
24
+ const storeId = await this.db.transaction(async (tx) => {
25
+ const schema = this.options.schema ?? "public";
26
+ const qualifiedMeta = schema === "public" ? "t_schema_meta" : `"${schema}".t_schema_meta`;
27
+ const metaExists = (await tx.execute(sql`SELECT to_regclass(${qualifiedMeta}) IS NOT NULL AS exists`)).rows[0]?.exists === true;
28
+ for (const statement of createTablesSql("postgres", postgresTableDefs, schema)) await tx.execute(sql.raw(statement));
29
+ if (!metaExists) await tx.insert(this.tables["t_schema_meta"]).values([{
30
+ fKey: "schema_version",
31
+ fValue: String(2)
32
+ }, {
33
+ fKey: "application_id",
34
+ fValue: String(SESSION_PERSISTENCE_SQLITE_APPLICATION_ID)
35
+ }]).execute();
36
+ const version = await this.readMeta(tx, "schema_version");
37
+ const applicationId = await this.readMeta(tx, "application_id");
38
+ if (version === void 0 || applicationId === void 0) throw new Error("session database has an unversioned schema or application identity");
39
+ if (Number(version) !== 2) throw new Error(`session database has schema version ${version}, incompatible with this build (2)`);
40
+ if (Number(applicationId) !== 1146308688) throw new Error(`session database has application id ${applicationId}, expected ${SESSION_PERSISTENCE_SQLITE_APPLICATION_ID}`);
41
+ await tx.insert(this.tables["t_persistence_state"]).values({
42
+ fSingleton: 1,
43
+ fStoreId: randomUUID()
44
+ }).onConflictDoNothing().execute();
45
+ const storeId = (await tx.select({ fStoreId: this.tables["t_persistence_state"].fStoreId }).from(this.tables["t_persistence_state"]).where(eq(this.tables["t_persistence_state"].fSingleton, 1)).execute())[0]?.fStoreId;
46
+ if (storeId === void 0 || storeId.length === 0) throw new Error("session database has no valid store identity");
47
+ return storeId;
48
+ });
49
+ this.storeIdentity = `${this.options.identityBase}:store:${storeId}`;
50
+ }
51
+ async close() {
52
+ await this.options.close();
53
+ }
54
+ async getSession(id) {
55
+ return (await this.db.select().from(this.tables["t_sessions"]).where(eq(this.tables["t_sessions"].fSessionId, id)).execute())[0];
56
+ }
57
+ async getSeqMapRows(id) {
58
+ return this.db.select({
59
+ fSequence: this.tables["t_session_events"].fSequence,
60
+ fOriginalSeq: this.tables["t_session_events"].fOriginalSeq
61
+ }).from(this.tables["t_session_events"]).where(eq(this.tables["t_session_events"].fSessionId, id)).execute();
62
+ }
63
+ async getEventRows(id, fromSequence) {
64
+ return (fromSequence === void 0 ? this.eventRows(this.db).where(eq(this.tables["t_session_events"].fSessionId, id)) : this.eventRows(this.db).where(and(eq(this.tables["t_session_events"].fSessionId, id), gte(this.tables["t_session_events"].fSequence, fromSequence)))).orderBy(this.tables["t_session_events"].fSequence).execute();
65
+ }
66
+ async listSessions() {
67
+ return this.db.select().from(this.tables["t_sessions"]).execute();
68
+ }
69
+ async transaction(fn) {
70
+ return this.db.transaction(async (tx) => fn(this.txFor(tx)));
71
+ }
72
+ txFor(tx) {
73
+ return {
74
+ upsertSession: (storage, incarnation) => this.upsertSession(tx, storage, incarnation),
75
+ getHead: (id) => this.getHead(tx, id),
76
+ getSeedLength: (id) => this.getSeedLength(tx, id),
77
+ updateSeedLength: (id, seedLength) => this.updateSeedLength(tx, id, seedLength),
78
+ insertEvents: (events) => this.insertEvents(tx, events),
79
+ insertBridges: (rows) => this.insertBridges(tx, rows),
80
+ updateHead: (id, headEventId, headSequence) => this.updateHead(tx, id, headEventId, headSequence),
81
+ bumpRevision: (id) => this.bumpRevision(tx, id),
82
+ deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(tx, id, fromSequence),
83
+ getPrevBridge: (id, sequence) => this.getPrevBridge(tx, id, sequence),
84
+ getLastBridge: (id) => this.getLastBridge(tx, id)
85
+ };
86
+ }
87
+ async readMeta(exec, key) {
88
+ return (await exec.select({ fValue: this.tables["t_schema_meta"].fValue }).from(this.tables["t_schema_meta"]).where(eq(this.tables["t_schema_meta"].fKey, key)).execute())[0]?.fValue;
89
+ }
90
+ async upsertSession(exec, storage, incarnation) {
91
+ await exec.insert(this.tables["t_sessions"]).values(sessionInsertRow(storage, incarnation)).onConflictDoUpdate({
92
+ target: this.tables["t_sessions"].fSessionId,
93
+ set: sessionConflictRow(storage)
94
+ }).execute();
95
+ }
96
+ async getHead(exec, id) {
97
+ const head = (await exec.select({
98
+ fHeadEventId: this.tables["t_sessions"].fHeadEventId,
99
+ fHeadSequence: this.tables["t_sessions"].fHeadSequence
100
+ }).from(this.tables["t_sessions"]).where(eq(this.tables["t_sessions"].fSessionId, id)).execute())[0];
101
+ /* v8 ignore next -- appendBatch/commitRepair always materialize the row before reading the head */
102
+ if (head === void 0) throw new Error(`session "${id}" has no materialized row`);
103
+ return head;
104
+ }
105
+ async getSeedLength(exec, id) {
106
+ const row = (await exec.select({ fSeedLength: this.tables["t_sessions"].fSeedLength }).from(this.tables["t_sessions"]).where(eq(this.tables["t_sessions"].fSessionId, id)).execute())[0];
107
+ /* v8 ignore next -- rewind always materializes the row before reading the seed length */
108
+ if (row === void 0) throw new Error(`session "${id}" has no materialized row`);
109
+ return row.fSeedLength;
110
+ }
111
+ async updateSeedLength(exec, id, seedLength) {
112
+ await exec.update(this.tables["t_sessions"]).set({ fSeedLength: seedLength }).where(eq(this.tables["t_sessions"].fSessionId, id)).execute();
113
+ }
114
+ static INSERT_BATCH_ROWS = 1e3;
115
+ async insertEvents(exec, events) {
116
+ if (events.length === 0) return;
117
+ for (let i = 0; i < events.length; i += PostgresBackend.INSERT_BATCH_ROWS) await exec.insert(this.tables["t_events"]).values(events.slice(i, i + PostgresBackend.INSERT_BATCH_ROWS).map((event) => ({ ...event }))).execute();
118
+ }
119
+ async insertBridges(exec, rows) {
120
+ if (rows.length === 0) return;
121
+ for (let i = 0; i < rows.length; i += PostgresBackend.INSERT_BATCH_ROWS) await exec.insert(this.tables["t_session_events"]).values(rows.slice(i, i + PostgresBackend.INSERT_BATCH_ROWS).map((row) => ({ ...row }))).execute();
122
+ }
123
+ async updateHead(exec, id, headEventId, headSequence) {
124
+ await exec.update(this.tables["t_sessions"]).set({
125
+ fHeadEventId: headEventId,
126
+ fHeadSequence: headSequence
127
+ }).where(eq(this.tables["t_sessions"].fSessionId, id)).execute();
128
+ }
129
+ async bumpRevision(exec, id) {
130
+ await exec.update(this.tables["t_sessions"]).set({ fRevision: sql`${this.tables["t_sessions"].fRevision} + 1` }).where(eq(this.tables["t_sessions"].fSessionId, id)).execute();
131
+ }
132
+ async deleteBridgeTail(exec, id, fromSequence) {
133
+ await exec.delete(this.tables["t_session_events"]).where(and(eq(this.tables["t_session_events"].fSessionId, id), gte(this.tables["t_session_events"].fSequence, fromSequence))).execute();
134
+ }
135
+ async getPrevBridge(exec, id, sequence) {
136
+ return (await exec.select({
137
+ fEventId: this.tables["t_session_events"].fEventId,
138
+ fSequence: this.tables["t_session_events"].fSequence
139
+ }).from(this.tables["t_session_events"]).where(and(eq(this.tables["t_session_events"].fSessionId, id), eq(this.tables["t_session_events"].fSequence, sequence))).execute())[0];
140
+ }
141
+ async getLastBridge(exec, id) {
142
+ return (await exec.select({
143
+ fEventId: this.tables["t_session_events"].fEventId,
144
+ fSequence: this.tables["t_session_events"].fSequence
145
+ }).from(this.tables["t_session_events"]).where(eq(this.tables["t_session_events"].fSessionId, id)).orderBy(desc(this.tables["t_session_events"].fSequence)).limit(1).execute())[0];
146
+ }
147
+ eventRows(exec) {
148
+ return exec.select({
149
+ fEventId: this.tables["t_session_events"].fEventId,
150
+ fSequence: this.tables["t_session_events"].fSequence,
151
+ fOriginalSeq: this.tables["t_session_events"].fOriginalSeq,
152
+ fType: this.tables["t_events"].fType,
153
+ fKind: this.tables["t_events"].fKind,
154
+ fRole: this.tables["t_events"].fRole,
155
+ fName: this.tables["t_events"].fName,
156
+ fActionId: this.tables["t_events"].fActionId,
157
+ fCreatedAt: this.tables["t_events"].fCreatedAt,
158
+ fData: this.tables["t_events"].fData,
159
+ fSurfaceOp: this.tables["t_session_events"].fSurfaceOp
160
+ }).from(this.tables["t_session_events"]).innerJoin(this.tables["t_events"], eq(this.tables["t_session_events"].fEventId, this.tables["t_events"].fEventId));
161
+ }
162
+ };
163
+ //#endregion
164
+ //#region src/index.ts
165
+ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersistence {
166
+ config;
167
+ static inject = ["sessions", "settings"];
168
+ static Config = z.union([z.object({
169
+ type: z.const("sqlite"),
170
+ path: z.string().required(),
171
+ journalMode: z.union([
172
+ "wal",
173
+ "delete",
174
+ "truncate",
175
+ "persist"
176
+ ]).default("wal"),
177
+ busyTimeout: z.number().step(1).min(0).default(DEFAULT_BUSY_TIMEOUT_MS)
178
+ }), z.object({
179
+ type: z.const("postgres"),
180
+ connectionString: z.string().required(),
181
+ schema: z.string().default("public")
182
+ })]);
183
+ static settingsNs = "session-rdb";
184
+ name = "session-rdb";
185
+ supportsRawArtifacts = true;
186
+ async readRaw(id, signal) {
187
+ signal?.throwIfAborted();
188
+ await this.ready;
189
+ signal?.throwIfAborted();
190
+ const log = await this.readLog(id, {}, signal);
191
+ if (log === void 0) return void 0;
192
+ repairSurfaceOps(log.events);
193
+ recomputeReplaceProvenance(log.events);
194
+ const inheritedEventCount = Math.min(log.inheritedEventCount, log.events.length);
195
+ return {
196
+ meta: log.meta,
197
+ inheritedEventCount: SessionLogOffset(inheritedEventCount),
198
+ filename: "session.jsonl",
199
+ content: toJsonlArtifact(log.meta, inheritedEventCount, log.events)
200
+ };
201
+ }
202
+ backend;
203
+ storeIdentity;
204
+ ready;
205
+ coordinator;
206
+ writeGuard = new WriteGuard();
207
+ reuseEventIds = /* @__PURE__ */ new Map();
208
+ constructor(ctx, config, injectedBackend) {
209
+ let resolved = config;
210
+ const settings = ctx.reflect.get("settings");
211
+ if (settings !== void 0) {
212
+ const scope = settings.register(SessionPersistenceRdb.settingsNs, SessionPersistenceRdb.Config, { base: config });
213
+ resolved = scope.get();
214
+ scope.watch(() => {
215
+ ctx.logger.warn("session-rdb: settings changed; restart to apply the new configuration");
216
+ });
217
+ }
218
+ super(ctx);
219
+ this.config = config;
220
+ this.config = resolved;
221
+ this.backend = injectedBackend ?? createBackend(resolved);
222
+ this.ready = this.init();
223
+ this.coordinator = new PersistenceCoordinator(this.ctx, this);
224
+ new SessionBranchRdb(this.ctx);
225
+ registerSessionImport(this.ctx, this);
226
+ }
227
+ async init() {
228
+ await this.backend.open();
229
+ this.storeIdentity = this.backend.storeIdentity;
230
+ }
231
+ locate(_meta) {}
232
+ create(meta, inheritedEventCount) {
233
+ return this.coordinator.create(meta, inheritedEventCount === void 0 ? void 0 : SessionLogOffset(inheritedEventCount));
234
+ }
235
+ append(id, events) {
236
+ return this.coordinator.append(id, events);
237
+ }
238
+ load(id) {
239
+ return this.coordinator.load(id);
240
+ }
241
+ inspect(id, signal) {
242
+ return this.coordinator.inspect(id, signal);
243
+ }
244
+ readFrom(id, fromSeq, signal) {
245
+ return this.coordinator.readFrom(id, fromSeq, signal);
246
+ }
247
+ borrowSession(id, signal) {
248
+ return this.coordinator.borrowSession(id, signal);
249
+ }
250
+ loadStored(id, signal) {
251
+ return this.readPrefix(id, signal);
252
+ }
253
+ async loadStoredFrom(id, fromSeq, signal) {
254
+ const log = await this.readLog(id, { fromSeq }, signal);
255
+ if (log === void 0) return void 0;
256
+ return {
257
+ meta: log.meta,
258
+ inheritedEventCount: SessionLogOffset(log.inheritedEventCount),
259
+ events: log.events
260
+ };
261
+ }
262
+ async readPrefix(id, signal) {
263
+ const log = await this.readLog(id, {}, signal);
264
+ if (log === void 0) {
265
+ this.writeGuard.confirmHead(id, -1);
266
+ return;
267
+ }
268
+ this.writeGuard.confirmHead(id, log.events.at(-1)?.seq ?? -1);
269
+ recomputeReplaceProvenance(log.events);
270
+ repairOrphanInboxSplices(log.events);
271
+ return {
272
+ meta: log.meta,
273
+ inheritedEventCount: SessionLogOffset(log.inheritedEventCount),
274
+ events: log.events,
275
+ revision: SessionPersistenceRevision(`${this.storeIdentity}:incarnation:${log.incarnation}:revision:${log.revision}`),
276
+ ...log.tornFrom !== void 0 ? { tornMarker: log.tornFrom } : {}
277
+ };
278
+ }
279
+ async readStoredRevision(id, signal) {
280
+ signal?.throwIfAborted();
281
+ await this.ready;
282
+ signal?.throwIfAborted();
283
+ const row = await this.backend.getSession(id);
284
+ if (row === void 0) return void 0;
285
+ return SessionPersistenceRevision(`${this.storeIdentity}:incarnation:${row.fIncarnation}:revision:${row.fRevision}`);
286
+ }
287
+ async readLog(id, options = {}, signal) {
288
+ signal?.throwIfAborted();
289
+ await this.ready;
290
+ signal?.throwIfAborted();
291
+ const row = await this.backend.getSession(id);
292
+ if (row === void 0) return void 0;
293
+ const meta = rowToMeta(row);
294
+ let eventRows;
295
+ let seqMap;
296
+ if (options.fromSeq === void 0) {
297
+ eventRows = await this.backend.getEventRows(id);
298
+ seqMap = buildSeqMap(eventRows);
299
+ } else {
300
+ eventRows = await this.backend.getEventRows(id, options.fromSeq);
301
+ const seqRows = await this.backend.getSeqMapRows(id);
302
+ seqMap = buildSeqMap(seqRows);
303
+ }
304
+ signal?.throwIfAborted();
305
+ const { preserved, tornFrom } = scanRows(eventRows, options.fromSeq ?? 0, seqMap);
306
+ return {
307
+ meta,
308
+ inheritedEventCount: row.fSeedLength ?? 0,
309
+ events: preserved,
310
+ incarnation: row.fIncarnation,
311
+ revision: row.fRevision,
312
+ ...tornFrom !== void 0 ? { tornFrom } : {}
313
+ };
314
+ }
315
+ async appendBatch(storage, events, _isMaterialized) {
316
+ await this.ready;
317
+ const persisted = events.filter(isPersistedEvent);
318
+ if (persisted.length === 0) return;
319
+ const meta = storage.meta;
320
+ const reuse = this.reuseEventIds.get(meta.id);
321
+ if (reuse !== void 0) this.reuseEventIds.delete(meta.id);
322
+ let confirmedHead = -1;
323
+ await this.backend.transaction(async (tx) => {
324
+ await tx.upsertSession(storage, randomUUID());
325
+ const head = await tx.getHead(meta.id);
326
+ this.writeGuard.assertNoConcurrentWriter(meta.id, head.fHeadSequence);
327
+ const { headEventId, headSequence } = await appendEventTail(tx, meta, persisted, {
328
+ parentId: head.fHeadEventId,
329
+ nextSeq: head.fHeadSequence + 1
330
+ }, reuse);
331
+ await tx.updateHead(meta.id, headEventId, headSequence);
332
+ await tx.bumpRevision(meta.id);
333
+ confirmedHead = headSequence;
334
+ });
335
+ this.writeGuard.confirmHead(meta.id, confirmedHead);
336
+ }
337
+ async commitRepair(storage, tornMarker, closers) {
338
+ await this.ready;
339
+ const meta = storage.meta;
340
+ const persistedClosers = closers.filter(isPersistedEvent);
341
+ if (tornMarker === void 0 && persistedClosers.length === 0) return;
342
+ await this.backend.transaction(async (tx) => {
343
+ if (tornMarker !== void 0) {
344
+ await tx.deleteBridgeTail(meta.id, tornMarker);
345
+ const prev = await tx.getPrevBridge(meta.id, tornMarker - 1);
346
+ if (prev === void 0) await tx.updateHead(meta.id, "", -1);
347
+ else await tx.updateHead(meta.id, prev.fEventId, prev.fSequence);
348
+ }
349
+ if (persistedClosers.length > 0) {
350
+ const last = await tx.getLastBridge(meta.id);
351
+ const { headEventId, headSequence } = await appendEventTail(tx, meta, persistedClosers, {
352
+ parentId: last?.fEventId ?? "",
353
+ nextSeq: (last?.fSequence ?? -1) + 1
354
+ });
355
+ await tx.updateHead(meta.id, headEventId, headSequence);
356
+ }
357
+ await tx.bumpRevision(meta.id);
358
+ });
359
+ const row = await this.backend.getSession(meta.id);
360
+ this.writeGuard.confirmHead(meta.id, row?.fHeadSequence ?? -1);
361
+ }
362
+ async list(signal) {
363
+ signal?.throwIfAborted();
364
+ await this.ready;
365
+ signal?.throwIfAborted();
366
+ const rows = await this.backend.listSessions();
367
+ signal?.throwIfAborted();
368
+ return rows.map(rowToMeta);
369
+ }
370
+ async listSnapshots(signal) {
371
+ signal?.throwIfAborted();
372
+ await this.ready;
373
+ signal?.throwIfAborted();
374
+ const rows = await this.backend.listSessions();
375
+ signal?.throwIfAborted();
376
+ return rows.map((row) => ({
377
+ header: rowToMeta(row),
378
+ revision: SessionPersistenceRevision(`${this.storeIdentity}:incarnation:${row.fIncarnation}:revision:${row.fRevision}`),
379
+ inheritedEventCount: row.fSeedLength ?? 0
380
+ }));
381
+ }
382
+ async close() {
383
+ await this.ready;
384
+ await this.backend.close();
385
+ }
386
+ registerReuseEventIds(childId, map) {
387
+ this.reuseEventIds.set(childId, new Map(map));
388
+ }
389
+ internals() {
390
+ return {
391
+ backend: this.backend,
392
+ writeGuard: this.writeGuard,
393
+ create: (meta, inheritedEventCount) => this.create(meta, inheritedEventCount),
394
+ append: (id, events) => this.append(id, events),
395
+ load: (id) => this.load(id),
396
+ inspect: (id, signal) => this.inspect(id, signal),
397
+ readFrom: (id, fromSeq, signal) => this.readFrom(id, fromSeq, signal),
398
+ listSnapshots: (signal) => this.listSnapshots(signal),
399
+ readStoredRevision: (id, signal) => this.readStoredRevision(id, signal),
400
+ registerReuseEventIds: (childId, map) => this.registerReuseEventIds(childId, map)
401
+ };
402
+ }
403
+ };
404
+ function createBackend(config) {
405
+ if (config.type === "sqlite") return new SqliteBackend({
406
+ path: config.path,
407
+ journalMode: config.journalMode ?? "wal",
408
+ busyTimeout: config.busyTimeout ?? 5e3
409
+ });
410
+ const pool = new Pool({ connectionString: config.connectionString });
411
+ pool.on("error", () => {});
412
+ return new PostgresBackend(drizzle({ client: pool }), {
413
+ identityBase: [
414
+ "postgres",
415
+ pool.options.host ?? "localhost",
416
+ String(pool.options.port ?? 5432),
417
+ pool.options.database ?? "",
418
+ config.schema ?? "public"
419
+ ].join(":"),
420
+ schema: config.schema ?? "public",
421
+ close: () => pool.end()
422
+ });
423
+ }
424
+ async function appendEventTail(tx, meta, events, anchor, reuse) {
425
+ let parentId = anchor.parentId;
426
+ let nextSeq = anchor.nextSeq;
427
+ const eventRows = [];
428
+ const bridgeRows = [];
429
+ for (const event of events) {
430
+ const reusedId = reuse?.get(event.seq);
431
+ const eventId = reusedId ?? randomUUID();
432
+ if (reusedId === void 0) {
433
+ const { kind, role, name, actionId } = eventDimensions(event);
434
+ eventRows.push({
435
+ fEventId: eventId,
436
+ fParentId: parentId,
437
+ fType: event.type,
438
+ fKind: kind,
439
+ fRole: role,
440
+ fName: name,
441
+ fActionId: actionId,
442
+ fEncoding: EVENT_ENCODING,
443
+ fData: JSON.stringify(event.data),
444
+ fCreatedAt: event.time
445
+ });
446
+ }
447
+ const surfaceOp = event.surfaceOp === void 0 ? null : JSON.stringify(event.surfaceOp);
448
+ bridgeRows.push({
449
+ fSessionId: meta.id,
450
+ fEventId: eventId,
451
+ fSequence: nextSeq,
452
+ fOriginalSeq: event.seq,
453
+ fSurfaceOp: surfaceOp
454
+ });
455
+ parentId = eventId;
456
+ nextSeq++;
457
+ }
458
+ if (eventRows.length > 0) await tx.insertEvents(eventRows);
459
+ await tx.insertBridges(bridgeRows);
460
+ return {
461
+ headEventId: parentId,
462
+ headSequence: nextSeq - 1
463
+ };
464
+ }
465
+ //#endregion
466
+ export { EPHEMERAL_EVENT_TYPES, SCHEMA_VERSION, SessionBranchRdb, SessionBranchRdbProvider, SessionPersistenceRdb, SessionPersistenceRdb as default, locateTurnEnd };
@@ -0,0 +1,7 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ //#region src/invariant.d.ts
3
+ declare const name = "session-rdb-invariant";
4
+ declare const inject: string[];
5
+ declare const apply: (ctx: Context) => Promise<() => void>;
6
+ //#endregion
7
+ export { apply, inject, name };
@@ -0,0 +1,8 @@
1
+ //#region src/invariant.ts
2
+ const PACKAGE_NAME = "@morlay/session-rdb";
3
+ const name = "session-rdb-invariant";
4
+ const inject = ["invariants"];
5
+ const install = () => {};
6
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
7
+ //#endregion
8
+ export { apply, inject, name };