@enbox/dwn-sql-store 0.0.10 → 0.0.11

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 (57) hide show
  1. package/dist/esm/src/data-store-s3.js +12 -21
  2. package/dist/esm/src/data-store-s3.js.map +1 -1
  3. package/dist/esm/src/data-store-sql.js +14 -37
  4. package/dist/esm/src/data-store-sql.js.map +1 -1
  5. package/dist/esm/src/main.js +1 -0
  6. package/dist/esm/src/main.js.map +1 -1
  7. package/dist/esm/src/message-store-sql.js +13 -107
  8. package/dist/esm/src/message-store-sql.js.map +1 -1
  9. package/dist/esm/src/migration-provider.js +36 -0
  10. package/dist/esm/src/migration-provider.js.map +1 -0
  11. package/dist/esm/src/migration-runner.js +26 -88
  12. package/dist/esm/src/migration-runner.js.map +1 -1
  13. package/dist/esm/src/migrations/001-initial-schema.js +4 -5
  14. package/dist/esm/src/migrations/001-initial-schema.js.map +1 -1
  15. package/dist/esm/src/migrations/002-content-addressed-datastore.js +3 -4
  16. package/dist/esm/src/migrations/002-content-addressed-datastore.js.map +1 -1
  17. package/dist/esm/src/migrations/003-add-squash-column.js +3 -4
  18. package/dist/esm/src/migrations/003-add-squash-column.js.map +1 -1
  19. package/dist/esm/src/migrations/index.js +14 -6
  20. package/dist/esm/src/migrations/index.js.map +1 -1
  21. package/dist/esm/src/resumable-task-store-sql.js +14 -21
  22. package/dist/esm/src/resumable-task-store-sql.js.map +1 -1
  23. package/dist/esm/src/state-index-sql.js +17 -58
  24. package/dist/esm/src/state-index-sql.js.map +1 -1
  25. package/dist/types/src/data-store-s3.d.ts.map +1 -1
  26. package/dist/types/src/data-store-sql.d.ts.map +1 -1
  27. package/dist/types/src/main.d.ts +1 -0
  28. package/dist/types/src/main.d.ts.map +1 -1
  29. package/dist/types/src/message-store-sql.d.ts +0 -8
  30. package/dist/types/src/message-store-sql.d.ts.map +1 -1
  31. package/dist/types/src/migration-provider.d.ts +36 -0
  32. package/dist/types/src/migration-provider.d.ts.map +1 -0
  33. package/dist/types/src/migration-runner.d.ts +13 -39
  34. package/dist/types/src/migration-runner.d.ts.map +1 -1
  35. package/dist/types/src/migrations/001-initial-schema.d.ts +3 -3
  36. package/dist/types/src/migrations/001-initial-schema.d.ts.map +1 -1
  37. package/dist/types/src/migrations/002-content-addressed-datastore.d.ts +2 -2
  38. package/dist/types/src/migrations/002-content-addressed-datastore.d.ts.map +1 -1
  39. package/dist/types/src/migrations/003-add-squash-column.d.ts +2 -2
  40. package/dist/types/src/migrations/003-add-squash-column.d.ts.map +1 -1
  41. package/dist/types/src/migrations/index.d.ts +12 -4
  42. package/dist/types/src/migrations/index.d.ts.map +1 -1
  43. package/dist/types/src/resumable-task-store-sql.d.ts.map +1 -1
  44. package/dist/types/src/state-index-sql.d.ts.map +1 -1
  45. package/package.json +2 -2
  46. package/src/data-store-s3.ts +14 -25
  47. package/src/data-store-sql.ts +15 -44
  48. package/src/main.ts +1 -0
  49. package/src/message-store-sql.ts +14 -113
  50. package/src/migration-provider.ts +52 -0
  51. package/src/migration-runner.ts +33 -123
  52. package/src/migrations/001-initial-schema.ts +6 -7
  53. package/src/migrations/002-content-addressed-datastore.ts +5 -6
  54. package/src/migrations/003-add-squash-column.ts +5 -7
  55. package/src/migrations/index.ts +15 -7
  56. package/src/resumable-task-store-sql.ts +16 -25
  57. package/src/state-index-sql.ts +18 -62
@@ -1,15 +1,23 @@
1
- import type { Migration } from '../migration-runner.js';
1
+ import type { DwnMigrationFactory } from '../migration-provider.js';
2
2
 
3
3
  import { migration001InitialSchema } from './001-initial-schema.js';
4
4
  import { migration002ContentAddressedDatastore } from './002-content-addressed-datastore.js';
5
5
  import { migration003AddSquashColumn } from './003-add-squash-column.js';
6
6
 
7
7
  /**
8
- * All migrations in sequential order. The MigrationRunner applies them
9
- * in array order, skipping any that have already been recorded.
8
+ * All DWN store migrations in sequential order.
9
+ *
10
+ * Each entry is a `[name, factory]` tuple where the factory is a
11
+ * {@link DwnMigrationFactory} that receives the dialect and returns a
12
+ * standard Kysely `Migration`. The `DwnMigrationProvider` resolves these
13
+ * into the `Record<string, Migration>` that Kysely's `Migrator` expects.
14
+ *
15
+ * **Ordering contract:** Entries MUST be sorted by name (lexicographic).
16
+ * Kysely's `Migrator` sorts by key as well, but maintaining order here
17
+ * makes the source of truth explicit and human-readable.
10
18
  */
11
- export const allMigrations: Migration[] = [
12
- migration001InitialSchema,
13
- migration002ContentAddressedDatastore,
14
- migration003AddSquashColumn,
19
+ export const allDwnMigrations: ReadonlyArray<readonly [name: string, factory: DwnMigrationFactory]> = [
20
+ ['001-initial-schema', migration001InitialSchema],
21
+ ['002-content-addressed-datastore', migration002ContentAddressedDatastore],
22
+ ['003-add-squash-column', migration003AddSquashColumn],
15
23
  ];
@@ -4,7 +4,7 @@ import type { ManagedResumableTask, ResumableTaskStore } from '@enbox/dwn-sdk-js
4
4
 
5
5
  import { Cid } from '@enbox/dwn-sdk-js';
6
6
  import { executeWithTransaction } from './utils/transaction.js';
7
- import { Kysely } from 'kysely';
7
+ import { Kysely, sql } from 'kysely';
8
8
 
9
9
  export class ResumableTaskStoreSql implements ResumableTaskStore {
10
10
  private static readonly taskTimeoutInSeconds = 60;
@@ -23,31 +23,22 @@ export class ResumableTaskStoreSql implements ResumableTaskStore {
23
23
 
24
24
  this.#db = new Kysely<DwnDatabaseType>({ dialect: this.#dialect });
25
25
 
26
- // if table already exists, there is no more things todo
27
- const tableName = 'resumableTasks';
28
- const tableExists = await this.#dialect.hasTable(this.#db, tableName);
29
- if (tableExists) {
30
- return;
31
- }
32
-
33
- // else create the table and corresponding indexes
34
-
35
- const table = this.#db.schema
36
- .createTable(tableName)
37
- .ifNotExists() // kept to show supported by all dialects in contrast to ifNotExists() below, though not needed due to hasTable() check above
38
- .addColumn('id', 'varchar(255)', (col) => col.primaryKey())
39
- .addColumn('task', 'text')
40
- .addColumn('timeout', 'bigint')
41
- .addColumn('retryCount', 'integer');
42
-
43
- await table.execute();
26
+ // Fail fast if migrations have not been run tables must already exist.
27
+ await this.#assertTablesExist();
28
+ }
44
29
 
45
- await this.#db.schema
46
- .createIndex('index_timeout')
47
- // .ifNotExists() // intentionally kept commented out code to show that it is not supported by all dialects (ie. MySQL)
48
- .on('resumableTasks')
49
- .column('timeout')
50
- .execute();
30
+ /**
31
+ * Verifies that the required tables exist by executing a zero-row SELECT.
32
+ * Throws a clear error directing the caller to run migrations first.
33
+ */
34
+ async #assertTablesExist(): Promise<void> {
35
+ try {
36
+ await sql`SELECT 1 FROM ${sql.table('resumableTasks')} LIMIT 0`.execute(this.#db!);
37
+ } catch {
38
+ throw new Error(
39
+ 'ResumableTaskStoreSql: table \'resumableTasks\' does not exist. Run DWN store migrations before opening stores.'
40
+ );
41
+ }
51
42
  }
52
43
 
53
44
  async close(): Promise<void> {
@@ -16,9 +16,9 @@ import type { KeyValues } from '@enbox/dwn-sdk-js';
16
16
  import type { StateIndex } from '@enbox/dwn-sdk-js';
17
17
 
18
18
  import { initDefaultHashes } from '@enbox/dwn-sdk-js';
19
- import { Kysely } from 'kysely';
20
19
  import { SMTStoreSql } from './smt-store-sql.js';
21
20
  import { SparseMerkleTree } from '@enbox/dwn-sdk-js';
21
+ import { Kysely, sql } from 'kysely';
22
22
 
23
23
  export class StateIndexSql implements StateIndex {
24
24
  #dialect: Dialect;
@@ -51,68 +51,24 @@ export class StateIndexSql implements StateIndex {
51
51
  // Ensure default hashes are initialized for the SMT
52
52
  await initDefaultHashes();
53
53
 
54
- // ─── Create stateIndexNodes table ─────────────────────────────────────
55
- const nodesTableName = 'stateIndexNodes';
56
- const nodesTableExists = await this.#dialect.hasTable(this.#db, nodesTableName);
57
- if (!nodesTableExists) {
58
- await this.#db.schema
59
- .createTable(nodesTableName)
60
- .ifNotExists()
61
- .addColumn('tenant', 'varchar(255)', (col) => col.notNull())
62
- .addColumn('scope', 'varchar(200)', (col) => col.notNull())
63
- .addColumn('nodeHash', 'varchar(64)', (col) => col.notNull())
64
- .addColumn('nodeType', 'varchar(10)', (col) => col.notNull())
65
- .addColumn('leftHash', 'varchar(64)')
66
- .addColumn('rightHash', 'varchar(64)')
67
- .addColumn('leafKeyHash', 'varchar(64)')
68
- .addColumn('leafValueCid', 'varchar(60)')
69
- .execute();
70
-
71
- // Not UNIQUE because the delete-then-insert upsert pattern in SMTStoreSql
72
- // can race under concurrent access, causing duplicate key violations.
73
- await this.#db.schema
74
- .createIndex('index_stateIndexNodes_tenant_scope_nodeHash')
75
- .on(nodesTableName)
76
- .columns(['tenant', 'scope', 'nodeHash'])
77
- .execute();
78
- }
79
-
80
- // ─── Create stateIndexRoots table ─────────────────────────────────────
81
- const rootsTableName = 'stateIndexRoots';
82
- const rootsTableExists = await this.#dialect.hasTable(this.#db, rootsTableName);
83
- if (!rootsTableExists) {
84
- await this.#db.schema
85
- .createTable(rootsTableName)
86
- .ifNotExists()
87
- .addColumn('tenant', 'varchar(255)', (col) => col.notNull())
88
- .addColumn('scope', 'varchar(200)', (col) => col.notNull())
89
- .addColumn('rootHash', 'varchar(64)', (col) => col.notNull())
90
- .execute();
91
-
92
- await this.#db.schema
93
- .createIndex('index_stateIndexRoots_tenant_scope')
94
- .on(rootsTableName)
95
- .columns(['tenant', 'scope'])
96
- .execute();
97
- }
98
-
99
- // ─── Create stateIndexMeta table ──────────────────────────────────────
100
- const metaTableName = 'stateIndexMeta';
101
- const metaTableExists = await this.#dialect.hasTable(this.#db, metaTableName);
102
- if (!metaTableExists) {
103
- await this.#db.schema
104
- .createTable(metaTableName)
105
- .ifNotExists()
106
- .addColumn('tenant', 'varchar(255)', (col) => col.notNull())
107
- .addColumn('messageCid', 'varchar(60)', (col) => col.notNull())
108
- .addColumn('protocol', 'varchar(200)')
109
- .execute();
54
+ // Fail fast if migrations have not been run — tables must already exist.
55
+ await this.#assertTablesExist();
56
+ }
110
57
 
111
- await this.#db.schema
112
- .createIndex('index_stateIndexMeta_tenant_messageCid')
113
- .on(metaTableName)
114
- .columns(['tenant', 'messageCid'])
115
- .execute();
58
+ /**
59
+ * Verifies that the required tables exist by executing a zero-row SELECT.
60
+ * Throws a clear error directing the caller to run migrations first.
61
+ */
62
+ async #assertTablesExist(): Promise<void> {
63
+ const tables = ['stateIndexNodes', 'stateIndexRoots', 'stateIndexMeta'] as const;
64
+ for (const table of tables) {
65
+ try {
66
+ await sql`SELECT 1 FROM ${sql.table(table)} LIMIT 0`.execute(this.#db!);
67
+ } catch {
68
+ throw new Error(
69
+ `StateIndexSql: table '${table}' does not exist. Run DWN store migrations before opening stores.`
70
+ );
71
+ }
116
72
  }
117
73
  }
118
74