@morlay/session-rdb 0.1.9 → 0.1.10-alpha.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 (143) hide show
  1. package/README.md +24 -3
  2. package/dist/artifact.d.mts +118 -3
  3. package/dist/artifact.mjs +5 -3
  4. package/dist/{log-C0q5svm7.d.mts → backend-BKtJ5sO5.d.mts} +189 -291
  5. package/dist/backend-rdb-BDCntVDy.mjs +1341 -0
  6. package/dist/branch-CG1PUlO6.mjs +833 -0
  7. package/dist/deletion-SSX7IqU8.mjs +35 -0
  8. package/dist/deletion.d.mts +1 -3
  9. package/dist/deletion.mjs +2 -2
  10. package/dist/export-CfJQjQfr.mjs +123 -0
  11. package/dist/export.d.mts +1 -3
  12. package/dist/export.mjs +32 -57
  13. package/dist/import.d.mts +1 -3
  14. package/dist/import.mjs +32 -78
  15. package/dist/index-CUD2ZpTA.d.mts +71 -0
  16. package/dist/index.d.mts +20 -2
  17. package/dist/index.mjs +1562 -5
  18. package/dist/open-YoyD_kah.mjs +561 -0
  19. package/dist/organize.d.mts +9 -0
  20. package/dist/organize.mjs +30 -0
  21. package/dist/route-DpfY0zq-.mjs +114 -0
  22. package/dist/storage.d.mts +41 -50
  23. package/dist/storage.mjs +3 -1
  24. package/dist/testing.d.mts +9 -5
  25. package/dist/testing.mjs +27 -8
  26. package/dist/usage-row-BQQM-_t6.mjs +36 -0
  27. package/dist/usage.d.mts +1 -1
  28. package/dist/usage.mjs +8 -42
  29. package/dist/write-guard-CYrY58GM.mjs +174 -0
  30. package/package.json +32 -36
  31. package/src/artifact.ts +4 -1
  32. package/src/backend-rdb.ts +331 -0
  33. package/src/backend.ts +32 -15
  34. package/src/{branch.ts → branch/index.ts} +125 -112
  35. package/src/branch/port.ts +62 -0
  36. package/src/branch/semantics.ts +69 -0
  37. package/src/db/open.ts +180 -0
  38. package/src/db/stats.ts +449 -0
  39. package/src/deletion-error.ts +17 -0
  40. package/src/deletion.ts +19 -72
  41. package/src/entities/index.ts +16 -1
  42. package/src/entities/v3/event-usage.ts +3 -3
  43. package/src/entities/v3/events.ts +3 -3
  44. package/src/entities/v3/index.ts +4 -16
  45. package/src/entities/v3/persistence-state.ts +3 -3
  46. package/src/entities/v3/schema-meta.ts +3 -3
  47. package/src/entities/v3/session-counts.ts +3 -3
  48. package/src/entities/v3/session-events.ts +3 -3
  49. package/src/entities/v3/session-projcache-rows.ts +3 -3
  50. package/src/entities/v3/session-usage.ts +3 -3
  51. package/src/entities/v3/sessions.ts +3 -3
  52. package/src/entities/v3/storage-records.ts +17 -0
  53. package/src/entities/v3/storage-units.ts +3 -3
  54. package/src/{schema.ts → event-shape.ts} +0 -46
  55. package/src/export.ts +32 -78
  56. package/src/handles.ts +349 -0
  57. package/src/http/route.ts +178 -0
  58. package/src/import-storages.ts +5 -2
  59. package/src/import.ts +63 -125
  60. package/src/index.ts +15 -1630
  61. package/src/legacy.ts +3 -1
  62. package/src/live-routing.ts +205 -0
  63. package/src/log/export.ts +84 -0
  64. package/src/{log.ts → log/repair.ts} +1 -272
  65. package/src/log/rows.ts +107 -0
  66. package/src/log/usage-row.ts +64 -0
  67. package/src/migrations.ts +196 -0
  68. package/src/organize.ts +38 -0
  69. package/src/read-path.ts +190 -0
  70. package/src/routes.ts +2 -1
  71. package/src/rows.ts +25 -77
  72. package/src/session-ops.ts +444 -0
  73. package/src/session-query.ts +17 -4
  74. package/src/session-store.ts +675 -0
  75. package/src/stats-fold.ts +44 -0
  76. package/src/storage-takeover/index.ts +2 -5
  77. package/src/storage-takeover/legacy-domain-migration.ts +118 -0
  78. package/src/storage-takeover/projection-cache.ts +139 -77
  79. package/src/storage-takeover/read-model.ts +189 -0
  80. package/src/storage-takeover/repository.ts +238 -425
  81. package/src/storage-takeover/storage-backend.ts +47 -149
  82. package/src/storage-takeover/types.ts +45 -14
  83. package/src/storage-takeover/workspace-documents.ts +65 -0
  84. package/src/storage.ts +4 -2
  85. package/src/testing.ts +21 -1
  86. package/src/usage.ts +11 -65
  87. package/src/version.ts +13 -0
  88. package/src/write-path.ts +286 -0
  89. package/dist/branch-DbFW3rJX.mjs +0 -391
  90. package/dist/deletion-ODZFcbZm.mjs +0 -2218
  91. package/dist/gc.d.mts +0 -6
  92. package/dist/gc.mjs +0 -49
  93. package/dist/log-DYxdjJ-l.mjs +0 -562
  94. package/dist/routes-DGBlBE_I.mjs +0 -10
  95. package/dist/sqlite-DjOnMRh2.mjs +0 -1777
  96. package/drizzle/postgres/20260908072805_v2_initial/migration.sql +0 -58
  97. package/drizzle/postgres/20260908072805_v2_initial/snapshot.json +0 -686
  98. package/drizzle/postgres/20260908072806_v3_drop_original_seq/migration.sql +0 -1
  99. package/drizzle/postgres/20260908072806_v3_drop_original_seq/snapshot.json +0 -673
  100. package/drizzle/postgres/20260910120001_v3_storage_tables/migration.sql +0 -65
  101. package/drizzle/postgres/20260910120001_v3_storage_tables/snapshot.json +0 -1183
  102. package/drizzle/postgres/20260910130001_v3_session_title_backfill/migration.sql +0 -10
  103. package/drizzle/postgres/20260910130001_v3_session_title_backfill/snapshot.json +0 -1183
  104. package/drizzle/postgres/20260918120000_v3_event_usage/migration.sql +0 -14
  105. package/drizzle/postgres/20260918120000_v3_event_usage/snapshot.json +0 -1309
  106. package/drizzle/postgres/20260921075900_v3_agent_preset/migration.sql +0 -1
  107. package/drizzle/postgres/20260922090001_v3_pinned_seq/migration.sql +0 -2
  108. package/drizzle/postgres/20260922093001_v3_last_event_at/migration.sql +0 -10
  109. package/drizzle/postgres/20260923120000_v3_usage_materialized/migration.sql +0 -48
  110. package/drizzle/postgres/20261008103001_v3_schedule_tasks/migration.sql +0 -11
  111. package/drizzle/sqlite/20260908072751_v2_initial/migration.sql +0 -53
  112. package/drizzle/sqlite/20260908072751_v2_initial/snapshot.json +0 -492
  113. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/migration.sql +0 -6
  114. package/drizzle/sqlite/20260908072752_v3_drop_original_seq/snapshot.json +0 -513
  115. package/drizzle/sqlite/20260910120000_v3_storage_tables/migration.sql +0 -66
  116. package/drizzle/sqlite/20260910120000_v3_storage_tables/snapshot.json +0 -905
  117. package/drizzle/sqlite/20260910130000_v3_session_title_backfill/migration.sql +0 -13
  118. package/drizzle/sqlite/20260910130000_v3_session_title_backfill/snapshot.json +0 -905
  119. package/drizzle/sqlite/20260918120000_v3_event_usage/migration.sql +0 -14
  120. package/drizzle/sqlite/20260918120000_v3_event_usage/snapshot.json +0 -1031
  121. package/drizzle/sqlite/20260921075855_v3_agent_preset/migration.sql +0 -7
  122. package/drizzle/sqlite/20260921075855_v3_agent_preset/snapshot.json +0 -1041
  123. package/drizzle/sqlite/20260922090000_v3_pinned_seq/migration.sql +0 -4
  124. package/drizzle/sqlite/20260922093000_v3_last_event_at/migration.sql +0 -13
  125. package/drizzle/sqlite/20260923120000_v3_usage_materialized/migration.sql +0 -48
  126. package/drizzle/sqlite/20261008103000_v3_schedule_tasks/migration.sql +0 -14
  127. package/src/adapters/index.ts +0 -12
  128. package/src/adapters/to-postgres.ts +0 -93
  129. package/src/adapters/to-sqlite.ts +0 -83
  130. package/src/adapters/types.ts +0 -44
  131. package/src/drizzle/postgres-v2.ts +0 -10
  132. package/src/drizzle/postgres-v3.ts +0 -18
  133. package/src/drizzle/sqlite-v2.ts +0 -9
  134. package/src/drizzle/sqlite-v3.ts +0 -18
  135. package/src/entities/v2/index.ts +0 -20
  136. package/src/entities/v2/session-events.ts +0 -10
  137. package/src/entities/v3/schedule-tasks.ts +0 -16
  138. package/src/entities/v3/workspace-sessions.ts +0 -19
  139. package/src/entities/v3/workspace-state.ts +0 -12
  140. package/src/entities/v3/workspaces.ts +0 -14
  141. package/src/gc.ts +0 -77
  142. package/src/postgres.ts +0 -1015
  143. package/src/sqlite.ts +0 -1086
package/src/import.ts CHANGED
@@ -6,10 +6,11 @@ import { parseSessionFormatLogFilename } from "@deepseek-ai/dsh-session-format";
6
6
  import type { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
7
7
  import { balanceRewindPrefix } from "@morlay/session-branch";
8
8
  import { unzip } from "fflate";
9
- import { replaceLiveSessionLog } from "./branch.ts";
9
+ import { replaceLiveSessionLog } from "./branch/index.ts";
10
10
  import { restoreCatalog } from "./legacy.ts";
11
- import type { SessionPersistenceRdb } from "./index.ts";
11
+ import type { SessionPersistenceRdb } from "./session-store.ts";
12
12
  import { SESSION_IMPORT_PATH } from "./routes.ts";
13
+ import { HttpError, mountJsonRoute } from "./http/route.ts";
13
14
 
14
15
  export const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
15
16
 
@@ -210,128 +211,65 @@ export async function persistImport(
210
211
  }
211
212
 
212
213
  export function registerSessionImport(ctx: Context, persistence: SessionPersistenceRdb): void {
213
- ctx.inject(["webServer", "connection"] as const, (webCtx) => {
214
- const webServer = webCtx.webServer as unknown as {
215
- register(route: {
216
- kind: "exact";
217
- path: string;
218
- handler: (
219
- req: import("node:http").IncomingMessage,
220
- res: import("node:http").ServerResponse,
221
- ) => void | Promise<void>;
222
- }): () => void;
223
- };
224
- const connection = webCtx.get("connection") as unknown as {
225
- requestRejection(request: {
226
- headers: import("node:http").IncomingHttpHeaders;
227
- }): number | undefined;
228
- };
229
- return webCtx.effect(
230
- () =>
231
- webServer.register({
232
- kind: "exact",
233
- path: SESSION_IMPORT_PATH,
234
- handler: async (req, res) => {
235
- const rejection = connection.requestRejection(req);
236
- if (rejection !== undefined) {
237
- res.writeHead(rejection);
238
- res.end(rejection === 401 ? "unauthorized" : "forbidden");
239
- return;
240
- }
241
- const chunks: Buffer[] = [];
242
- for await (const chunk of req) chunks.push(chunk as Buffer);
243
- const body = Buffer.concat(chunks);
244
- let envelope: { zip?: unknown; sessionId?: unknown };
245
- try {
246
- envelope = JSON.parse(body.toString("utf8")) as {
247
- zip?: unknown;
248
- sessionId?: unknown;
249
- };
250
- } catch {
251
- res.writeHead(400, { "content-type": "application/json" });
252
- res.end(JSON.stringify({ error: "request body is not JSON" }));
253
- return;
254
- }
255
- if (typeof envelope.zip !== "string" || envelope.zip === "") {
256
- res.writeHead(400, { "content-type": "application/json" });
257
- res.end(JSON.stringify({ error: "missing zip field" }));
258
- return;
259
- }
260
- if (
261
- envelope.sessionId !== undefined &&
262
- (typeof envelope.sessionId !== "string" || envelope.sessionId === "")
263
- ) {
264
- res.writeHead(400, { "content-type": "application/json" });
265
- res.end(JSON.stringify({ error: "sessionId must be a non-empty string" }));
266
- return;
267
- }
268
- let zip: Uint8Array;
269
- try {
270
- zip = Buffer.from(envelope.zip, "base64");
271
- } catch {
272
- res.writeHead(400, { "content-type": "application/json" });
273
- res.end(JSON.stringify({ error: "zip field is not valid base64" }));
274
- return;
275
- }
276
- if (zip.byteLength > MAX_IMPORT_ZIP_BYTES) {
277
- res.writeHead(413, { "content-type": "application/json" });
278
- res.end(JSON.stringify({ error: "imported zip exceeds the size limit" }));
279
- return;
280
- }
281
- let imported: SessionStorageMetadata & { events: SessionEvent[] };
282
- try {
283
- imported = await parseImportZip(zip);
284
- } catch (error: unknown) {
285
- res.writeHead(400, { "content-type": "application/json" });
286
- res.end(
287
- JSON.stringify({
288
- error: error instanceof Error ? error.message : "imported zip is invalid",
289
- }),
290
- );
291
- return;
292
- }
293
- const targetId =
294
- typeof envelope.sessionId === "string"
295
- ? (envelope.sessionId as SessionId)
296
- : undefined;
297
- const branch = webCtx.get("sessionBranch") as unknown as
298
- | {
299
- rewind(id: SessionId, toBoundary: number): Promise<unknown>;
300
- resetLiveDerivedState?(session: Session): void;
301
- }
302
- | undefined;
303
- try {
304
- const sessions = webCtx.get("sessions") as
305
- | { get(id: SessionId): Session | undefined }
306
- | undefined;
307
- const id = await persistImport(
308
- persistence,
309
- branch,
310
- imported,
311
- targetId,
312
- sessions,
313
- (sessionId) => stopAgentLoop(webCtx, sessionId),
314
- );
315
- res.writeHead(200, { "content-type": "application/json" });
316
- res.end(JSON.stringify({ sessionId: id }));
317
- } catch (error: unknown) {
318
- const message = error instanceof Error ? error.message : String(error);
319
- if (targetId !== undefined && /not found/i.test(message)) {
320
- res.writeHead(404, { "content-type": "application/json" });
321
- res.end(JSON.stringify({ error: `session "${targetId}" not found` }));
322
- return;
323
- }
324
- res.writeHead(500, { "content-type": "application/json" });
325
- res.end(
326
- JSON.stringify({
327
- error: error instanceof Error ? error.message : "import failed",
328
- }),
329
- );
330
- return;
331
- }
332
- },
333
- }),
334
- `session-rdb: ${SESSION_IMPORT_PATH} route`,
335
- );
214
+ mountJsonRoute(ctx, {
215
+ path: SESSION_IMPORT_PATH,
216
+ handle: async (body, webCtx) => {
217
+ const envelope = body as { zip?: unknown; sessionId?: unknown };
218
+ if (typeof envelope.zip !== "string" || envelope.zip === "") {
219
+ throw new HttpError(400, "missing zip field");
220
+ }
221
+ if (
222
+ envelope.sessionId !== undefined &&
223
+ (typeof envelope.sessionId !== "string" || envelope.sessionId === "")
224
+ ) {
225
+ throw new HttpError(400, "sessionId must be a non-empty string");
226
+ }
227
+ let zip: Uint8Array;
228
+ try {
229
+ zip = Buffer.from(envelope.zip, "base64");
230
+ } catch {
231
+ throw new HttpError(400, "zip field is not valid base64");
232
+ }
233
+ if (zip.byteLength > MAX_IMPORT_ZIP_BYTES) {
234
+ throw new HttpError(413, "imported zip exceeds the size limit");
235
+ }
236
+ let imported: SessionStorageMetadata & { events: SessionEvent[] };
237
+ try {
238
+ imported = await parseImportZip(zip);
239
+ } catch (error: unknown) {
240
+ throw new HttpError(
241
+ 400,
242
+ error instanceof Error ? error.message : "imported zip is invalid",
243
+ );
244
+ }
245
+ const targetId =
246
+ typeof envelope.sessionId === "string" ? (envelope.sessionId as SessionId) : undefined;
247
+ const branch = webCtx.get("sessionBranch") as unknown as
248
+ | {
249
+ rewind(id: SessionId, toBoundary: number): Promise<unknown>;
250
+ resetLiveDerivedState?(session: Session): void;
251
+ }
252
+ | undefined;
253
+ const sessions = webCtx.get("sessions") as
254
+ | { get(id: SessionId): Session | undefined }
255
+ | undefined;
256
+ try {
257
+ const id = await persistImport(
258
+ persistence,
259
+ branch,
260
+ imported,
261
+ targetId,
262
+ sessions,
263
+ (sessionId) => stopAgentLoop(webCtx, sessionId),
264
+ );
265
+ return { body: { sessionId: id } };
266
+ } catch (error: unknown) {
267
+ const message = error instanceof Error ? error.message : String(error);
268
+ if (targetId !== undefined && /not found/i.test(message)) {
269
+ throw new HttpError(404, `session "${targetId}" not found`);
270
+ }
271
+ throw error;
272
+ }
273
+ },
336
274
  });
337
275
  }