@codelia/storage 0.1.3 → 0.1.12

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.
package/dist/index.cjs CHANGED
@@ -335,6 +335,7 @@ var SessionStateStoreImpl = class {
335
335
  db;
336
336
  schemaInit;
337
337
  onError;
338
+ lastDbUnavailableError;
338
339
  constructor(options = {}) {
339
340
  const paths = options.paths ?? resolveStoragePaths();
340
341
  this.legacyStateDir = resolveLegacyStateDir(paths);
@@ -347,8 +348,15 @@ var SessionStateStoreImpl = class {
347
348
  ]).then(() => {
348
349
  });
349
350
  this.onError = options.onError;
350
- this.db = this.openDatabase();
351
+ this.db = null;
351
352
  this.schemaInit = null;
353
+ this.lastDbUnavailableError = null;
354
+ }
355
+ getOrCreateDb() {
356
+ if (!this.db) {
357
+ this.db = this.openDatabase();
358
+ }
359
+ return this.db;
352
360
  }
353
361
  resolveLegacyPath(sessionId) {
354
362
  return import_node_path3.default.join(this.legacyStateDir, `${sessionId}.json`);
@@ -415,19 +423,31 @@ var SessionStateStoreImpl = class {
415
423
  }
416
424
  async tryGetDb(action, detail) {
417
425
  try {
418
- const db = await this.db;
426
+ const db = await this.getOrCreateDb();
419
427
  if (!this.schemaInit) {
420
428
  this.schemaInit = Promise.resolve().then(() => {
421
429
  this.initDatabaseSchema(db);
422
430
  });
423
431
  }
424
432
  await this.schemaInit;
433
+ this.lastDbUnavailableError = null;
425
434
  return db;
426
435
  } catch (error) {
436
+ this.db = null;
437
+ this.schemaInit = null;
438
+ this.lastDbUnavailableError = error;
427
439
  this.onError?.(error, { action: `${action}.db_unavailable`, detail });
428
440
  return null;
429
441
  }
430
442
  }
443
+ async requireDb(action, detail) {
444
+ const db = await this.tryGetDb(action, detail);
445
+ if (!db) {
446
+ const reason = this.lastDbUnavailableError ? `: ${String(this.lastDbUnavailableError)}` : "";
447
+ throw new Error(`Session index database unavailable${reason}`);
448
+ }
449
+ return db;
450
+ }
431
451
  async loadLegacy(sessionId) {
432
452
  try {
433
453
  const file = await import_node_fs3.promises.readFile(this.resolveLegacyPath(sessionId), "utf8");
@@ -468,7 +488,10 @@ var SessionStateStoreImpl = class {
468
488
  if (!row) return null;
469
489
  let messages;
470
490
  try {
471
- const payload = await import_node_fs3.promises.readFile(this.resolveMessagePath(sessionId), "utf8");
491
+ const payload = await import_node_fs3.promises.readFile(
492
+ this.resolveMessagePath(sessionId),
493
+ "utf8"
494
+ );
472
495
  messages = deserializeMessages(payload);
473
496
  } catch (error) {
474
497
  if (error.code === "ENOENT") {
@@ -486,7 +509,10 @@ var SessionStateStoreImpl = class {
486
509
  meta = parsed;
487
510
  }
488
511
  } catch (error) {
489
- this.onError?.(error, { action: "index.meta.parse", detail: sessionId });
512
+ this.onError?.(error, {
513
+ action: "index.meta.parse",
514
+ detail: sessionId
515
+ });
490
516
  }
491
517
  }
492
518
  return {
@@ -503,7 +529,10 @@ var SessionStateStoreImpl = class {
503
529
  const messagePayload = serializeMessages(
504
530
  Array.isArray(state.messages) ? state.messages : []
505
531
  );
506
- await atomicWriteFile(this.resolveMessagePath(state.session_id), messagePayload);
532
+ await atomicWriteFile(
533
+ this.resolveMessagePath(state.session_id),
534
+ messagePayload
535
+ );
507
536
  const summary = toSummary(state);
508
537
  db.run(
509
538
  `INSERT INTO session_state (
@@ -537,74 +566,36 @@ var SessionStateStoreImpl = class {
537
566
  ]
538
567
  );
539
568
  }
540
- async saveLegacy(state) {
541
- const payload = `${JSON.stringify(state)}
542
- `;
543
- await atomicWriteFile(this.resolveLegacyPath(state.session_id), payload);
544
- }
545
- async hasLegacySnapshot(sessionId) {
569
+ async load(sessionId) {
570
+ const db = await this.requireDb("load", sessionId);
546
571
  try {
547
- const stat = await import_node_fs3.promises.stat(this.resolveLegacyPath(sessionId));
548
- return stat.isFile();
572
+ const indexed = await this.loadFromIndex(sessionId, db);
573
+ if (indexed) return indexed;
549
574
  } catch (error) {
550
- if (error.code === "ENOENT") {
551
- return false;
552
- }
575
+ this.onError?.(error, { action: "load.index", detail: sessionId });
553
576
  throw error;
554
577
  }
555
- }
556
- async saveLegacyIfPresent(state) {
557
- if (!await this.hasLegacySnapshot(state.session_id)) {
558
- return false;
559
- }
560
- await this.saveLegacy(state);
561
- return true;
562
- }
563
- async load(sessionId) {
564
- const db = await this.tryGetDb("load", sessionId);
565
- if (db) {
566
- try {
567
- const indexed = await this.loadFromIndex(sessionId, db);
568
- if (indexed) return indexed;
569
- } catch (error) {
570
- this.onError?.(error, { action: "load.index", detail: sessionId });
571
- throw error;
572
- }
573
- }
574
578
  const legacy = await this.loadLegacy(sessionId);
575
579
  if (!legacy) return null;
576
580
  if (db) {
577
581
  try {
578
582
  await this.saveToIndex(legacy, db);
579
583
  } catch (error) {
580
- this.onError?.(error, { action: "load.migrate_legacy", detail: sessionId });
584
+ this.onError?.(error, {
585
+ action: "load.migrate_legacy",
586
+ detail: sessionId
587
+ });
581
588
  }
582
589
  }
583
590
  return legacy;
584
591
  }
585
592
  async save(state) {
586
593
  await this.ensureDirs;
587
- const db = await this.tryGetDb("save", state.session_id);
588
- if (!db) {
589
- const savedLegacy = await this.saveLegacyIfPresent(state);
590
- if (savedLegacy) return;
591
- throw new Error(
592
- "Session index database unavailable and no legacy snapshot found"
593
- );
594
- }
594
+ const db = await this.requireDb("save", state.session_id);
595
595
  try {
596
596
  await this.saveToIndex(state, db);
597
597
  } catch (error) {
598
598
  this.onError?.(error, { action: "save.index", detail: state.session_id });
599
- try {
600
- const savedLegacy = await this.saveLegacyIfPresent(state);
601
- if (savedLegacy) return;
602
- } catch (legacyError) {
603
- this.onError?.(legacyError, {
604
- action: "save.legacy_fallback",
605
- detail: state.session_id
606
- });
607
- }
608
599
  throw error;
609
600
  }
610
601
  }
@@ -645,20 +636,19 @@ var SessionStateStoreImpl = class {
645
636
  async list() {
646
637
  await this.ensureDirs;
647
638
  const summaries = /* @__PURE__ */ new Map();
648
- const db = await this.tryGetDb("list");
649
- if (db) {
650
- try {
651
- const rows = db.all(
652
- `SELECT session_id, updated_at, run_id, message_count, last_user_message
653
- FROM session_state
654
- ORDER BY updated_at DESC`
655
- );
656
- for (const row of rows) {
657
- summaries.set(row.session_id, fromSummaryRow(row));
658
- }
659
- } catch (error) {
660
- this.onError?.(error, { action: "list.index" });
639
+ const db = await this.requireDb("list");
640
+ try {
641
+ const rows = db.all(
642
+ `SELECT session_id, updated_at, run_id, message_count, last_user_message
643
+ FROM session_state
644
+ ORDER BY updated_at DESC`
645
+ );
646
+ for (const row of rows) {
647
+ summaries.set(row.session_id, fromSummaryRow(row));
661
648
  }
649
+ } catch (error) {
650
+ this.onError?.(error, { action: "list.index" });
651
+ throw error;
662
652
  }
663
653
  const legacy = await this.listLegacySummaries();
664
654
  for (const item of legacy) {
package/dist/index.d.cts CHANGED
@@ -44,21 +44,21 @@ declare class SessionStateStoreImpl implements SessionStateStore {
44
44
  private readonly messagesDir;
45
45
  private readonly stateDbPath;
46
46
  private readonly ensureDirs;
47
- private readonly db;
47
+ private db;
48
48
  private schemaInit;
49
49
  private readonly onError?;
50
+ private lastDbUnavailableError;
50
51
  constructor(options?: SessionStateStoreOptions);
52
+ private getOrCreateDb;
51
53
  private resolveLegacyPath;
52
54
  private resolveMessagePath;
53
55
  private openDatabase;
54
56
  private initDatabaseSchema;
55
57
  private tryGetDb;
58
+ private requireDb;
56
59
  private loadLegacy;
57
60
  private loadFromIndex;
58
61
  private saveToIndex;
59
- private saveLegacy;
60
- private hasLegacySnapshot;
61
- private saveLegacyIfPresent;
62
62
  load(sessionId: string): Promise<SessionState | null>;
63
63
  save(state: SessionState): Promise<void>;
64
64
  private listLegacySummaries;
package/dist/index.d.ts CHANGED
@@ -44,21 +44,21 @@ declare class SessionStateStoreImpl implements SessionStateStore {
44
44
  private readonly messagesDir;
45
45
  private readonly stateDbPath;
46
46
  private readonly ensureDirs;
47
- private readonly db;
47
+ private db;
48
48
  private schemaInit;
49
49
  private readonly onError?;
50
+ private lastDbUnavailableError;
50
51
  constructor(options?: SessionStateStoreOptions);
52
+ private getOrCreateDb;
51
53
  private resolveLegacyPath;
52
54
  private resolveMessagePath;
53
55
  private openDatabase;
54
56
  private initDatabaseSchema;
55
57
  private tryGetDb;
58
+ private requireDb;
56
59
  private loadLegacy;
57
60
  private loadFromIndex;
58
61
  private saveToIndex;
59
- private saveLegacy;
60
- private hasLegacySnapshot;
61
- private saveLegacyIfPresent;
62
62
  load(sessionId: string): Promise<SessionState | null>;
63
63
  save(state: SessionState): Promise<void>;
64
64
  private listLegacySummaries;
package/dist/index.js CHANGED
@@ -292,6 +292,7 @@ var SessionStateStoreImpl = class {
292
292
  db;
293
293
  schemaInit;
294
294
  onError;
295
+ lastDbUnavailableError;
295
296
  constructor(options = {}) {
296
297
  const paths = options.paths ?? resolveStoragePaths();
297
298
  this.legacyStateDir = resolveLegacyStateDir(paths);
@@ -304,8 +305,15 @@ var SessionStateStoreImpl = class {
304
305
  ]).then(() => {
305
306
  });
306
307
  this.onError = options.onError;
307
- this.db = this.openDatabase();
308
+ this.db = null;
308
309
  this.schemaInit = null;
310
+ this.lastDbUnavailableError = null;
311
+ }
312
+ getOrCreateDb() {
313
+ if (!this.db) {
314
+ this.db = this.openDatabase();
315
+ }
316
+ return this.db;
309
317
  }
310
318
  resolveLegacyPath(sessionId) {
311
319
  return path3.join(this.legacyStateDir, `${sessionId}.json`);
@@ -372,19 +380,31 @@ var SessionStateStoreImpl = class {
372
380
  }
373
381
  async tryGetDb(action, detail) {
374
382
  try {
375
- const db = await this.db;
383
+ const db = await this.getOrCreateDb();
376
384
  if (!this.schemaInit) {
377
385
  this.schemaInit = Promise.resolve().then(() => {
378
386
  this.initDatabaseSchema(db);
379
387
  });
380
388
  }
381
389
  await this.schemaInit;
390
+ this.lastDbUnavailableError = null;
382
391
  return db;
383
392
  } catch (error) {
393
+ this.db = null;
394
+ this.schemaInit = null;
395
+ this.lastDbUnavailableError = error;
384
396
  this.onError?.(error, { action: `${action}.db_unavailable`, detail });
385
397
  return null;
386
398
  }
387
399
  }
400
+ async requireDb(action, detail) {
401
+ const db = await this.tryGetDb(action, detail);
402
+ if (!db) {
403
+ const reason = this.lastDbUnavailableError ? `: ${String(this.lastDbUnavailableError)}` : "";
404
+ throw new Error(`Session index database unavailable${reason}`);
405
+ }
406
+ return db;
407
+ }
388
408
  async loadLegacy(sessionId) {
389
409
  try {
390
410
  const file = await fs3.readFile(this.resolveLegacyPath(sessionId), "utf8");
@@ -425,7 +445,10 @@ var SessionStateStoreImpl = class {
425
445
  if (!row) return null;
426
446
  let messages;
427
447
  try {
428
- const payload = await fs3.readFile(this.resolveMessagePath(sessionId), "utf8");
448
+ const payload = await fs3.readFile(
449
+ this.resolveMessagePath(sessionId),
450
+ "utf8"
451
+ );
429
452
  messages = deserializeMessages(payload);
430
453
  } catch (error) {
431
454
  if (error.code === "ENOENT") {
@@ -443,7 +466,10 @@ var SessionStateStoreImpl = class {
443
466
  meta = parsed;
444
467
  }
445
468
  } catch (error) {
446
- this.onError?.(error, { action: "index.meta.parse", detail: sessionId });
469
+ this.onError?.(error, {
470
+ action: "index.meta.parse",
471
+ detail: sessionId
472
+ });
447
473
  }
448
474
  }
449
475
  return {
@@ -460,7 +486,10 @@ var SessionStateStoreImpl = class {
460
486
  const messagePayload = serializeMessages(
461
487
  Array.isArray(state.messages) ? state.messages : []
462
488
  );
463
- await atomicWriteFile(this.resolveMessagePath(state.session_id), messagePayload);
489
+ await atomicWriteFile(
490
+ this.resolveMessagePath(state.session_id),
491
+ messagePayload
492
+ );
464
493
  const summary = toSummary(state);
465
494
  db.run(
466
495
  `INSERT INTO session_state (
@@ -494,74 +523,36 @@ var SessionStateStoreImpl = class {
494
523
  ]
495
524
  );
496
525
  }
497
- async saveLegacy(state) {
498
- const payload = `${JSON.stringify(state)}
499
- `;
500
- await atomicWriteFile(this.resolveLegacyPath(state.session_id), payload);
501
- }
502
- async hasLegacySnapshot(sessionId) {
526
+ async load(sessionId) {
527
+ const db = await this.requireDb("load", sessionId);
503
528
  try {
504
- const stat = await fs3.stat(this.resolveLegacyPath(sessionId));
505
- return stat.isFile();
529
+ const indexed = await this.loadFromIndex(sessionId, db);
530
+ if (indexed) return indexed;
506
531
  } catch (error) {
507
- if (error.code === "ENOENT") {
508
- return false;
509
- }
532
+ this.onError?.(error, { action: "load.index", detail: sessionId });
510
533
  throw error;
511
534
  }
512
- }
513
- async saveLegacyIfPresent(state) {
514
- if (!await this.hasLegacySnapshot(state.session_id)) {
515
- return false;
516
- }
517
- await this.saveLegacy(state);
518
- return true;
519
- }
520
- async load(sessionId) {
521
- const db = await this.tryGetDb("load", sessionId);
522
- if (db) {
523
- try {
524
- const indexed = await this.loadFromIndex(sessionId, db);
525
- if (indexed) return indexed;
526
- } catch (error) {
527
- this.onError?.(error, { action: "load.index", detail: sessionId });
528
- throw error;
529
- }
530
- }
531
535
  const legacy = await this.loadLegacy(sessionId);
532
536
  if (!legacy) return null;
533
537
  if (db) {
534
538
  try {
535
539
  await this.saveToIndex(legacy, db);
536
540
  } catch (error) {
537
- this.onError?.(error, { action: "load.migrate_legacy", detail: sessionId });
541
+ this.onError?.(error, {
542
+ action: "load.migrate_legacy",
543
+ detail: sessionId
544
+ });
538
545
  }
539
546
  }
540
547
  return legacy;
541
548
  }
542
549
  async save(state) {
543
550
  await this.ensureDirs;
544
- const db = await this.tryGetDb("save", state.session_id);
545
- if (!db) {
546
- const savedLegacy = await this.saveLegacyIfPresent(state);
547
- if (savedLegacy) return;
548
- throw new Error(
549
- "Session index database unavailable and no legacy snapshot found"
550
- );
551
- }
551
+ const db = await this.requireDb("save", state.session_id);
552
552
  try {
553
553
  await this.saveToIndex(state, db);
554
554
  } catch (error) {
555
555
  this.onError?.(error, { action: "save.index", detail: state.session_id });
556
- try {
557
- const savedLegacy = await this.saveLegacyIfPresent(state);
558
- if (savedLegacy) return;
559
- } catch (legacyError) {
560
- this.onError?.(legacyError, {
561
- action: "save.legacy_fallback",
562
- detail: state.session_id
563
- });
564
- }
565
556
  throw error;
566
557
  }
567
558
  }
@@ -602,20 +593,19 @@ var SessionStateStoreImpl = class {
602
593
  async list() {
603
594
  await this.ensureDirs;
604
595
  const summaries = /* @__PURE__ */ new Map();
605
- const db = await this.tryGetDb("list");
606
- if (db) {
607
- try {
608
- const rows = db.all(
609
- `SELECT session_id, updated_at, run_id, message_count, last_user_message
610
- FROM session_state
611
- ORDER BY updated_at DESC`
612
- );
613
- for (const row of rows) {
614
- summaries.set(row.session_id, fromSummaryRow(row));
615
- }
616
- } catch (error) {
617
- this.onError?.(error, { action: "list.index" });
596
+ const db = await this.requireDb("list");
597
+ try {
598
+ const rows = db.all(
599
+ `SELECT session_id, updated_at, run_id, message_count, last_user_message
600
+ FROM session_state
601
+ ORDER BY updated_at DESC`
602
+ );
603
+ for (const row of rows) {
604
+ summaries.set(row.session_id, fromSummaryRow(row));
618
605
  }
606
+ } catch (error) {
607
+ this.onError?.(error, { action: "list.index" });
608
+ throw error;
619
609
  }
620
610
  const legacy = await this.listLegacySummaries();
621
611
  for (const item of legacy) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codelia/storage",
3
- "version": "0.1.3",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20 <25"
@@ -23,7 +23,7 @@
23
23
  "typecheck": "tsc --noEmit"
24
24
  },
25
25
  "dependencies": {
26
- "@codelia/core": "0.1.3",
26
+ "@codelia/core": "0.1.12",
27
27
  "better-sqlite3": "^12.6.2"
28
28
  },
29
29
  "publishConfig": {