@rudderjs/database 1.1.0 → 1.2.1

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 (126) hide show
  1. package/README.md +70 -0
  2. package/dist/db.d.ts +21 -3
  3. package/dist/db.d.ts.map +1 -1
  4. package/dist/db.js +27 -5
  5. package/dist/db.js.map +1 -1
  6. package/dist/index.d.ts +14 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +23 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/native/adapter.d.ts +202 -0
  11. package/dist/native/adapter.d.ts.map +1 -0
  12. package/dist/native/adapter.js +440 -0
  13. package/dist/native/adapter.js.map +1 -0
  14. package/dist/native/compiler.d.ts +371 -0
  15. package/dist/native/compiler.d.ts.map +1 -0
  16. package/dist/native/compiler.js +978 -0
  17. package/dist/native/compiler.js.map +1 -0
  18. package/dist/native/dialect-mysql.d.ts +26 -0
  19. package/dist/native/dialect-mysql.d.ts.map +1 -0
  20. package/dist/native/dialect-mysql.js +188 -0
  21. package/dist/native/dialect-mysql.js.map +1 -0
  22. package/dist/native/dialect-pg.d.ts +26 -0
  23. package/dist/native/dialect-pg.d.ts.map +1 -0
  24. package/dist/native/dialect-pg.js +192 -0
  25. package/dist/native/dialect-pg.js.map +1 -0
  26. package/dist/native/dialect.d.ts +255 -0
  27. package/dist/native/dialect.d.ts.map +1 -0
  28. package/dist/native/dialect.js +237 -0
  29. package/dist/native/dialect.js.map +1 -0
  30. package/dist/native/driver.d.ts +37 -0
  31. package/dist/native/driver.d.ts.map +1 -0
  32. package/dist/native/driver.js +19 -0
  33. package/dist/native/driver.js.map +1 -0
  34. package/dist/native/drivers/better-sqlite3.d.ts +56 -0
  35. package/dist/native/drivers/better-sqlite3.d.ts.map +1 -0
  36. package/dist/native/drivers/better-sqlite3.js +171 -0
  37. package/dist/native/drivers/better-sqlite3.js.map +1 -0
  38. package/dist/native/drivers/mysql.d.ts +30 -0
  39. package/dist/native/drivers/mysql.d.ts.map +1 -0
  40. package/dist/native/drivers/mysql.js +176 -0
  41. package/dist/native/drivers/mysql.js.map +1 -0
  42. package/dist/native/drivers/postgres.d.ts +57 -0
  43. package/dist/native/drivers/postgres.d.ts.map +1 -0
  44. package/dist/native/drivers/postgres.js +155 -0
  45. package/dist/native/drivers/postgres.js.map +1 -0
  46. package/dist/native/errors.d.ts +43 -0
  47. package/dist/native/errors.d.ts.map +1 -0
  48. package/dist/native/errors.js +64 -0
  49. package/dist/native/errors.js.map +1 -0
  50. package/dist/native/index.d.ts +27 -0
  51. package/dist/native/index.d.ts.map +1 -0
  52. package/dist/native/index.js +55 -0
  53. package/dist/native/index.js.map +1 -0
  54. package/dist/native/isolation.d.ts +14 -0
  55. package/dist/native/isolation.d.ts.map +1 -0
  56. package/dist/native/isolation.js +37 -0
  57. package/dist/native/isolation.js.map +1 -0
  58. package/dist/native/query-builder.d.ts +303 -0
  59. package/dist/native/query-builder.d.ts.map +1 -0
  60. package/dist/native/query-builder.js +984 -0
  61. package/dist/native/query-builder.js.map +1 -0
  62. package/dist/native/replica-picker.d.ts +22 -0
  63. package/dist/native/replica-picker.d.ts.map +1 -0
  64. package/dist/native/replica-picker.js +65 -0
  65. package/dist/native/replica-picker.js.map +1 -0
  66. package/dist/native/schema/alter-blueprint.d.ts +37 -0
  67. package/dist/native/schema/alter-blueprint.d.ts.map +1 -0
  68. package/dist/native/schema/alter-blueprint.js +56 -0
  69. package/dist/native/schema/alter-blueprint.js.map +1 -0
  70. package/dist/native/schema/blueprint.d.ts +151 -0
  71. package/dist/native/schema/blueprint.d.ts.map +1 -0
  72. package/dist/native/schema/blueprint.js +286 -0
  73. package/dist/native/schema/blueprint.js.map +1 -0
  74. package/dist/native/schema/column.d.ts +168 -0
  75. package/dist/native/schema/column.d.ts.map +1 -0
  76. package/dist/native/schema/column.js +190 -0
  77. package/dist/native/schema/column.js.map +1 -0
  78. package/dist/native/schema/ddl-compiler.d.ts +34 -0
  79. package/dist/native/schema/ddl-compiler.d.ts.map +1 -0
  80. package/dist/native/schema/ddl-compiler.js +352 -0
  81. package/dist/native/schema/ddl-compiler.js.map +1 -0
  82. package/dist/native/schema/inspect.d.ts +67 -0
  83. package/dist/native/schema/inspect.d.ts.map +1 -0
  84. package/dist/native/schema/inspect.js +312 -0
  85. package/dist/native/schema/inspect.js.map +1 -0
  86. package/dist/native/schema/introspect.d.ts +34 -0
  87. package/dist/native/schema/introspect.d.ts.map +1 -0
  88. package/dist/native/schema/introspect.js +101 -0
  89. package/dist/native/schema/introspect.js.map +1 -0
  90. package/dist/native/schema/migration.d.ts +8 -0
  91. package/dist/native/schema/migration.d.ts.map +1 -0
  92. package/dist/native/schema/migration.js +19 -0
  93. package/dist/native/schema/migration.js.map +1 -0
  94. package/dist/native/schema/migrator.d.ts +144 -0
  95. package/dist/native/schema/migrator.d.ts.map +1 -0
  96. package/dist/native/schema/migrator.js +240 -0
  97. package/dist/native/schema/migrator.js.map +1 -0
  98. package/dist/native/schema/rebuild.d.ts +11 -0
  99. package/dist/native/schema/rebuild.d.ts.map +1 -0
  100. package/dist/native/schema/rebuild.js +92 -0
  101. package/dist/native/schema/rebuild.js.map +1 -0
  102. package/dist/native/schema/schema-builder.d.ts +46 -0
  103. package/dist/native/schema/schema-builder.d.ts.map +1 -0
  104. package/dist/native/schema/schema-builder.js +153 -0
  105. package/dist/native/schema/schema-builder.js.map +1 -0
  106. package/dist/native/schema/schema-facade.d.ts +63 -0
  107. package/dist/native/schema/schema-facade.d.ts.map +1 -0
  108. package/dist/native/schema/schema-facade.js +124 -0
  109. package/dist/native/schema/schema-facade.js.map +1 -0
  110. package/dist/native/schema/schema-types.d.ts +27 -0
  111. package/dist/native/schema/schema-types.d.ts.map +1 -0
  112. package/dist/native/schema/schema-types.js +52 -0
  113. package/dist/native/schema/schema-types.js.map +1 -0
  114. package/dist/native/schema/types-generator.d.ts +73 -0
  115. package/dist/native/schema/types-generator.d.ts.map +1 -0
  116. package/dist/native/schema/types-generator.js +181 -0
  117. package/dist/native/schema/types-generator.js.map +1 -0
  118. package/dist/registry-bridge.d.ts +24 -4
  119. package/dist/registry-bridge.d.ts.map +1 -1
  120. package/dist/registry-bridge.js +20 -0
  121. package/dist/registry-bridge.js.map +1 -1
  122. package/dist/sticky.d.ts +22 -0
  123. package/dist/sticky.d.ts.map +1 -0
  124. package/dist/sticky.js +61 -0
  125. package/dist/sticky.js.map +1 -0
  126. package/package.json +32 -2
@@ -0,0 +1,181 @@
1
+ // ─── Schema → TypeScript types generator (GATE 7-types) ────
2
+ //
3
+ // The headline of the migrations plan: after `migrate`, a model's column types
4
+ // are GENERATED from the live schema, not hand-maintained — so they can't drift.
5
+ // This module is the PURE core: it turns introspected columns (+ a model's
6
+ // declared `casts`) into a `SchemaRegistry` `.d.ts` that augments `@rudderjs/orm`,
7
+ // which `Model<'table'>` then resolves. Mirrors `@rudderjs/vite`'s scanner
8
+ // emitting `pages/__view/registry.d.ts` (same "generated .d.ts augments framework
9
+ // types" pattern).
10
+ //
11
+ // PURE: string building only. The node-only orchestration (introspect every
12
+ // table → write the file) lives in `schema-types.ts`; the runtime DB reads live
13
+ // in `introspect.ts`. Keeping the mapping pure makes the column→TS contract
14
+ // directly unit-testable.
15
+ /**
16
+ * Map a SQLite declared type (`PRAGMA table_info.type`) to a base TS type. The
17
+ * mapping is by affinity rules — SQLite types are fuzzy, so we match on
18
+ * substrings the same way SQLite's own affinity algorithm does (INT→integer,
19
+ * CHAR/CLOB/TEXT→text, BLOB→blob, REAL/FLOA/DOUB→real, else numeric).
20
+ */
21
+ export function sqliteTypeToTs(declared) {
22
+ const t = declared.toUpperCase();
23
+ if (t.includes('INT'))
24
+ return 'number';
25
+ if (t.includes('CHAR') || t.includes('CLOB') || t.includes('TEXT'))
26
+ return 'string';
27
+ if (t.includes('BLOB') || t === '')
28
+ return 'Uint8Array';
29
+ if (t.includes('REAL') || t.includes('FLOA') || t.includes('DOUB'))
30
+ return 'number';
31
+ return 'number'; // NUMERIC / DECIMAL affinity — stored as number
32
+ }
33
+ /**
34
+ * Map a Postgres `information_schema.columns.data_type` to a base TS type. The
35
+ * mapping reflects what the porsager driver actually returns on READ (so the
36
+ * generated type matches runtime), which `casts` then refine:
37
+ * - `int8`/`bigint` → `number` (the pg driver parses OID 20 to a JS number);
38
+ * - `numeric`/`money` → `string` (porsager keeps these as strings for
39
+ * precision safety — a `float`/`decimal` cast refines them to `number`);
40
+ * - `json`/`jsonb` → `unknown` (a `json` cast or a typed `casts` entry refines);
41
+ * - `timestamp*`/`date` → `Date`; `bytea` → `Uint8Array`; the rest → `string`.
42
+ */
43
+ export function pgTypeToTs(declared) {
44
+ const t = declared.toLowerCase();
45
+ switch (t) {
46
+ case 'boolean': return 'boolean';
47
+ case 'smallint':
48
+ case 'integer':
49
+ case 'bigint':
50
+ case 'real':
51
+ case 'double precision': return 'number';
52
+ case 'numeric':
53
+ case 'money': return 'string';
54
+ case 'json':
55
+ case 'jsonb': return 'unknown';
56
+ case 'bytea': return 'Uint8Array';
57
+ case 'date': return 'Date';
58
+ default:
59
+ // varchar/char surface as `character varying`/`character`; timestamps as
60
+ // `timestamp with/without time zone`; times as `time …`. Match by prefix.
61
+ if (t.startsWith('timestamp'))
62
+ return 'Date';
63
+ if (t.startsWith('character') || t.startsWith('time') || t === 'text' || t === 'uuid')
64
+ return 'string';
65
+ return 'unknown';
66
+ }
67
+ }
68
+ /**
69
+ * Map a MySQL `information_schema.columns.data_type` to a base TS type. The
70
+ * `data_type` is the base type WITHOUT length/precision, which `casts` then
71
+ * refine:
72
+ * - integer family (`tinyint`/`smallint`/`mediumint`/`int`/`bigint`) → `number`.
73
+ * Note `tinyint(1)` (MySQL's BOOLEAN alias) surfaces as `tinyint` here → `number`;
74
+ * a declared `boolean` cast refines it to `boolean` (same treatment as pg);
75
+ * - `decimal`/`numeric` → `string` (precision safety, matching pg — a
76
+ * `float`/`decimal` cast refines to `number`);
77
+ * - `float`/`double`/`real` → `number`;
78
+ * - `json` → `unknown` (a `json` cast or typed `casts` entry refines);
79
+ * - `date`/`datetime`/`timestamp` → `Date`; `blob`/`binary` → `Uint8Array`;
80
+ * - char/text families → `string`; everything else → `unknown`.
81
+ */
82
+ export function mysqlTypeToTs(declared) {
83
+ const t = declared.toLowerCase();
84
+ switch (t) {
85
+ case 'tinyint':
86
+ case 'smallint':
87
+ case 'mediumint':
88
+ case 'int':
89
+ case 'integer':
90
+ case 'bigint': return 'number';
91
+ case 'decimal':
92
+ case 'numeric': return 'string';
93
+ case 'float':
94
+ case 'double':
95
+ case 'double precision':
96
+ case 'real': return 'number';
97
+ case 'json': return 'unknown';
98
+ case 'date':
99
+ case 'datetime':
100
+ case 'timestamp': return 'Date';
101
+ default:
102
+ // varchar/char/text families → string; blob/binary families → Uint8Array.
103
+ if (t.includes('char') || t.includes('text') || t === 'enum' || t === 'set')
104
+ return 'string';
105
+ if (t.includes('blob') || t.includes('binary'))
106
+ return 'Uint8Array';
107
+ return 'unknown';
108
+ }
109
+ }
110
+ /**
111
+ * The TS type a declared cast produces on READ (`toJSON`/hydration). Casts
112
+ * OVERRIDE the storage type — a `boolean` cast turns an INTEGER column into
113
+ * `boolean`, a `json` cast turns TEXT into `unknown`, etc. Returns null for
114
+ * casts that don't change the read type (or unknown cast names), so the caller
115
+ * falls back to the storage mapping.
116
+ */
117
+ export function castToTs(cast) {
118
+ switch (cast) {
119
+ case 'boolean': return 'boolean';
120
+ case 'integer': return 'number';
121
+ case 'float': return 'number';
122
+ case 'string': return 'string';
123
+ case 'encrypted': return 'string';
124
+ case 'date': return 'Date';
125
+ case 'datetime': return 'Date';
126
+ case 'json': return 'unknown';
127
+ case 'array': return 'unknown[]';
128
+ case 'encrypted:array': return 'unknown[]';
129
+ case 'collection': return 'unknown[]';
130
+ case 'encrypted:object': return 'Record<string, unknown>';
131
+ default:
132
+ // `vector(...)` and custom CastUsing classes resolve to number[] / unknown;
133
+ // be conservative — unknown name → let storage type win.
134
+ return null;
135
+ }
136
+ }
137
+ /**
138
+ * Resolve one column's TS type: a declared cast wins over the storage mapping,
139
+ * and a nullable column widens with `| null`. The primary key and NOT NULL
140
+ * columns stay non-null. `typeToTs` is the per-dialect storage mapper
141
+ * ({@link sqliteTypeToTs} by default; {@link pgTypeToTs} for Postgres).
142
+ */
143
+ export function resolveColumnType(col, casts, typeToTs = sqliteTypeToTs) {
144
+ const declaredCast = casts[col.name];
145
+ const base = (declaredCast && castToTs(declaredCast)) || typeToTs(col.type);
146
+ // A column is nullable on read when it permits NULL and isn't the PK.
147
+ const nullable = !col.notNull && col.pk === 0;
148
+ return { name: col.name, ts: nullable ? `${base} | null` : base };
149
+ }
150
+ /** Build one table's {@link TableTypes} from its columns + casts, using the
151
+ * given per-dialect storage mapper (defaults to the SQLite mapping). */
152
+ export function buildTableTypes(table, columns, casts = {}, typeToTs = sqliteTypeToTs) {
153
+ return { table, columns: columns.map((c) => resolveColumnType(c, casts, typeToTs)) };
154
+ }
155
+ /**
156
+ * Emit the full `registry.d.ts` contents: a `declare module '@rudderjs/orm'`
157
+ * augmentation extending `SchemaRegistry` with one entry per table. Tables are
158
+ * sorted for deterministic output (stable diffs / git). An empty schema still
159
+ * emits a valid (empty) augmentation so a stale file is always overwritten.
160
+ */
161
+ export function emitRegistryDts(tables) {
162
+ const sorted = [...tables].sort((a, b) => a.table.localeCompare(b.table));
163
+ const entries = sorted.map((t) => {
164
+ const cols = t.columns.map((c) => ` ${quoteKey(c.name)}: ${c.ts}`).join('\n');
165
+ return ` ${quoteKey(t.table)}: {\n${cols}\n }`;
166
+ }).join('\n');
167
+ return (`// AUTO-GENERATED by @rudderjs/orm — do not edit.\n` +
168
+ `// Source: the migrated database schema (run \`rudder schema:types\` or \`migrate\`).\n` +
169
+ `import '@rudderjs/orm'\n\n` +
170
+ `declare module '@rudderjs/orm' {\n` +
171
+ ` interface SchemaRegistry {\n` +
172
+ `${entries}\n` +
173
+ ` }\n` +
174
+ `}\n`);
175
+ }
176
+ /** Quote an object key only when it isn't a plain JS identifier (keeps the
177
+ * common case clean, stays correct for snake_case-with-dashes or odd names). */
178
+ function quoteKey(key) {
179
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
180
+ }
181
+ //# sourceMappingURL=types-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-generator.js","sourceRoot":"","sources":["../../../src/native/schema/types-generator.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,14 @@
1
- import type { OrmAdapter } from '@rudderjs/contracts';
1
+ import type { OrmAdapter, TransactionOptions } from '@rudderjs/contracts';
2
2
  /**
3
3
  * Runs `fn` inside a database transaction, returning its result. Pushed in by
4
4
  * `@rudderjs/orm` (its `transaction()` free function) so `DB.transaction()` reuses
5
5
  * the ORM's `AsyncLocalStorage` transaction scoping — every `Model.*` AND `DB.*`
6
6
  * call inside `fn` joins the *same* open transaction (one connection, not two).
7
7
  * `@rudderjs/database` can't import `@rudderjs/orm`, so the runner is injected the
8
- * same way the adapter resolver is.
8
+ * same way the adapter resolver is. `opts` (e.g. `isolationLevel`) flows through
9
+ * to the adapter untouched.
9
10
  */
10
- export type TransactionRunner = <T>(fn: () => Promise<T>) => Promise<T>;
11
+ export type TransactionRunner = <T>(fn: () => Promise<T>, opts?: TransactionOptions) => Promise<T>;
11
12
  /**
12
13
  * Resolves the adapter for a NAMED connection (`DB.connection('reporting')`),
13
14
  * opening it lazily on first use. Pushed in by `@rudderjs/orm`'s `db-bridge`
@@ -19,7 +20,17 @@ export type TransactionRunner = <T>(fn: () => Promise<T>) => Promise<T>;
19
20
  export type ConnectionResolver = (name: string) => Promise<OrmAdapter>;
20
21
  /** Runs `fn` inside a transaction on a NAMED connection — the named
21
22
  * counterpart of {@link TransactionRunner}, same injection rationale. */
22
- export type NamedTransactionRunner = <T>(name: string, fn: () => Promise<T>) => Promise<T>;
23
+ export type NamedTransactionRunner = <T>(name: string, fn: () => Promise<T>, opts?: TransactionOptions) => Promise<T>;
24
+ /**
25
+ * Queues `fn` to run after the current transaction commits (drops it on
26
+ * rollback; runs immediately when no transaction is open). Pushed in by
27
+ * `@rudderjs/orm` (its `afterCommit()` free function, which owns the
28
+ * transaction-tree bookkeeping) so `DB.afterCommit()` shares the queue with
29
+ * `Model.*` / `transaction()` call sites.
30
+ */
31
+ export type AfterCommitRunner = (fn: () => void | Promise<void>, opts?: {
32
+ connection?: string;
33
+ }) => Promise<void>;
23
34
  /**
24
35
  * Register the function that resolves the active ORM adapter. Called once by
25
36
  * `@rudderjs/orm`'s `db-bridge` module (imported for side effect from each
@@ -66,4 +77,13 @@ export declare function registerNamedTransactionRunner(fn: NamedTransactionRunne
66
77
  /** Resolve the named transaction runner. Throws a clear error when none has
67
78
  * been registered. */
68
79
  export declare function resolveNamedTransactionRunner(): NamedTransactionRunner;
80
+ /**
81
+ * Register the function that queues after-commit callbacks. Called by
82
+ * `@rudderjs/orm`'s `db-bridge` module. Idempotent — last registration wins.
83
+ */
84
+ export declare function registerAfterCommitRunner(fn: AfterCommitRunner): void;
85
+ /** Resolve the after-commit runner. Throws a clear error when none has been
86
+ * registered — i.e. `@rudderjs/orm` (and a database provider) wasn't loaded,
87
+ * or the installed `@rudderjs/orm` predates after-commit support. */
88
+ export declare function resolveAfterCommitRunner(): AfterCommitRunner;
69
89
  //# sourceMappingURL=registry-bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-bridge.d.ts","sourceRoot":"","sources":["../src/registry-bridge.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAEvE;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;AAEtE;0EAC0E;AAC1E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAO1F;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,UAAU,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAS3C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,kBAAkB,CAU9D;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,sBAAsB,GAAG,IAAI,CAE/E;AAED;uBACuB;AACvB,wBAAgB,6BAA6B,IAAI,sBAAsB,CAStE"}
1
+ {"version":3,"file":"registry-bridge.d.ts","sourceRoot":"","sources":["../src/registry-bridge.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAEzE;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAElG;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;AAEtE;0EAC0E;AAC1E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAErH;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAQjH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,UAAU,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAS3C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,iBAAiB,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,kBAAkB,CAU9D;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,sBAAsB,GAAG,IAAI,CAE/E;AAED;uBACuB;AACvB,wBAAgB,6BAA6B,IAAI,sBAAsB,CAStE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAErE;AAED;;sEAEsE;AACtE,wBAAgB,wBAAwB,IAAI,iBAAiB,CAS5D"}
@@ -14,6 +14,7 @@ let resolver = null;
14
14
  let txRunner = null;
15
15
  let connectionResolver = null;
16
16
  let namedTxRunner = null;
17
+ let afterCommitRunner = null;
17
18
  /**
18
19
  * Register the function that resolves the active ORM adapter. Called once by
19
20
  * `@rudderjs/orm`'s `db-bridge` module (imported for side effect from each
@@ -97,11 +98,30 @@ export function resolveNamedTransactionRunner() {
97
98
  }
98
99
  return namedTxRunner;
99
100
  }
101
+ /**
102
+ * Register the function that queues after-commit callbacks. Called by
103
+ * `@rudderjs/orm`'s `db-bridge` module. Idempotent — last registration wins.
104
+ */
105
+ export function registerAfterCommitRunner(fn) {
106
+ afterCommitRunner = fn;
107
+ }
108
+ /** Resolve the after-commit runner. Throws a clear error when none has been
109
+ * registered — i.e. `@rudderjs/orm` (and a database provider) wasn't loaded,
110
+ * or the installed `@rudderjs/orm` predates after-commit support. */
111
+ export function resolveAfterCommitRunner() {
112
+ if (!afterCommitRunner) {
113
+ throw new Error('[RudderJS DB] No after-commit runner is available. DB.afterCommit() runs ' +
114
+ 'through @rudderjs/orm — make sure a database provider is registered (and ' +
115
+ '@rudderjs/orm installed) before calling DB.afterCommit().');
116
+ }
117
+ return afterCommitRunner;
118
+ }
100
119
  /** @internal — test-only reset of the registered resolvers + transaction runners. */
101
120
  export function __resetAdapterResolver() {
102
121
  resolver = null;
103
122
  txRunner = null;
104
123
  connectionResolver = null;
105
124
  namedTxRunner = null;
125
+ afterCommitRunner = null;
106
126
  }
107
127
  //# sourceMappingURL=registry-bridge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-bridge.js","sourceRoot":"","sources":["../src/registry-bridge.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,0BAA0B;AAC1B,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,+EAA+E;AA4B/E,IAAI,QAAQ,GAA8B,IAAI,CAAA;AAC9C,IAAI,QAAQ,GAA6B,IAAI,CAAA;AAC7C,IAAI,kBAAkB,GAA8B,IAAI,CAAA;AACxD,IAAI,aAAa,GAAkC,IAAI,CAAA;AAEvD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAoB;IAC1D,QAAQ,GAAG,EAAE,CAAA;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,wEAAwE;YACtE,uEAAuE;YACvE,8DAA8D,CACjE,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,EAAE,CAAA;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAqB;IAC7D,QAAQ,GAAG,EAAE,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,0EAA0E;YACxE,2EAA2E;YAC3E,2DAA2D,CAC9D,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,EAAsB;IAC/D,kBAAkB,GAAG,EAAE,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,yEAAyE;YACvE,0EAA0E;YAC1E,sEAAsE;YACtE,kBAAkB,CACrB,CAAA;IACH,CAAC;IACD,OAAO,kBAAkB,CAAA;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,EAA0B;IACvE,aAAa,GAAG,EAAE,CAAA;AACpB,CAAC;AAED;uBACuB;AACvB,MAAM,UAAU,6BAA6B;IAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,0DAA0D;YACxD,2EAA2E;YAC3E,wEAAwE,CAC3E,CAAA;IACH,CAAC;IACD,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,sBAAsB;IACpC,QAAQ,GAAG,IAAI,CAAA;IACf,QAAQ,GAAG,IAAI,CAAA;IACf,kBAAkB,GAAG,IAAI,CAAA;IACzB,aAAa,GAAG,IAAI,CAAA;AACtB,CAAC"}
1
+ {"version":3,"file":"registry-bridge.js","sourceRoot":"","sources":["../src/registry-bridge.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,0BAA0B;AAC1B,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,+EAA+E;AAsC/E,IAAI,QAAQ,GAA8B,IAAI,CAAA;AAC9C,IAAI,QAAQ,GAA6B,IAAI,CAAA;AAC7C,IAAI,kBAAkB,GAA8B,IAAI,CAAA;AACxD,IAAI,aAAa,GAAkC,IAAI,CAAA;AACvD,IAAI,iBAAiB,GAA6B,IAAI,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAoB;IAC1D,QAAQ,GAAG,EAAE,CAAA;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,wEAAwE;YACtE,uEAAuE;YACvE,8DAA8D,CACjE,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,EAAE,CAAA;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAqB;IAC7D,QAAQ,GAAG,EAAE,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,0EAA0E;YACxE,2EAA2E;YAC3E,2DAA2D,CAC9D,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,EAAsB;IAC/D,kBAAkB,GAAG,EAAE,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,yEAAyE;YACvE,0EAA0E;YAC1E,sEAAsE;YACtE,kBAAkB,CACrB,CAAA;IACH,CAAC;IACD,OAAO,kBAAkB,CAAA;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,EAA0B;IACvE,aAAa,GAAG,EAAE,CAAA;AACpB,CAAC;AAED;uBACuB;AACvB,MAAM,UAAU,6BAA6B;IAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,0DAA0D;YACxD,2EAA2E;YAC3E,wEAAwE,CAC3E,CAAA;IACH,CAAC;IACD,OAAO,aAAa,CAAA;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAqB;IAC7D,iBAAiB,GAAG,EAAE,CAAA;AACxB,CAAC;AAED;;sEAEsE;AACtE,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,2EAA2E;YAC3E,2DAA2D,CAC9D,CAAA;IACH,CAAC;IACD,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,sBAAsB;IACpC,QAAQ,GAAG,IAAI,CAAA;IACf,QAAQ,GAAG,IAAI,CAAA;IACf,kBAAkB,GAAG,IAAI,CAAA;IACzB,aAAa,GAAG,IAAI,CAAA;IACpB,iBAAiB,GAAG,IAAI,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { MiddlewareHandler } from '@rudderjs/contracts';
2
+ /** Run `fn` inside a fresh database context — the unit sticky reads scope to.
3
+ * The middleware wraps each request in one; wrap queue-job/command bodies
4
+ * manually if they need read-your-writes on a sticky connection. */
5
+ export declare function runWithDatabaseContext<T>(fn: () => T): T;
6
+ /** Whether a database context (request scope) is active. */
7
+ export declare function hasDatabaseContext(): boolean;
8
+ /** Record that `connection` performed a write in the current context.
9
+ * No-op outside a context. Called by split adapters' write paths. */
10
+ export declare function markWrote(connection: string): void;
11
+ /** Whether `connection` wrote in the current context — when true (and the
12
+ * connection is `sticky`), reads route to the write connection. Always
13
+ * `false` outside a context. */
14
+ export declare function stickyWrote(connection: string): boolean;
15
+ /**
16
+ * Request middleware entering a database context, so sticky reads scope to
17
+ * the request. Auto-installed on the `web` + `api` groups by the native
18
+ * provider when any configured connection sets `sticky: true` with a `read`
19
+ * pool; harmless to install twice (an inner scope simply shadows the outer).
20
+ */
21
+ export declare function databaseContextMiddleware(): MiddlewareHandler;
22
+ //# sourceMappingURL=sticky.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sticky.d.ts","sourceRoot":"","sources":["../src/sticky.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAa5D;;qEAEqE;AACrE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAExD;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED;sEACsE;AACtE,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAElD;AAED;;iCAEiC;AACjC,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,iBAAiB,CAE7D"}
package/dist/sticky.js ADDED
@@ -0,0 +1,61 @@
1
+ // ─── Sticky-read scope for read/write-split connections ────
2
+ //
3
+ // Laravel parity: with `sticky: true` on a split connection, a read issued
4
+ // AFTER a write *within the same request cycle* routes to the write
5
+ // connection — the just-written row may not have replicated yet. The "request
6
+ // cycle" is an AsyncLocalStorage scope entered per request by
7
+ // {@link databaseContextMiddleware} (the native provider appends it to both
8
+ // the `web` and `api` groups when a sticky split connection is configured).
9
+ //
10
+ // Outside a scope (queue jobs, rudder commands, scripts) the flag is a no-op
11
+ // and reads go to the replicas. That divergence is deliberate: Laravel's
12
+ // per-connection `$recordsModified` flag resets per request because PHP's
13
+ // process dies with the request — a long-lived Node process would otherwise
14
+ // go sticky-forever after its first write.
15
+ //
16
+ // Node-only module (top-level `node:async_hooks` import) — exported via the
17
+ // `@rudderjs/database/sticky` subpath (canonical home since the Phase-2 engine
18
+ // relocation; `@rudderjs/orm/sticky` stays as a re-export shim). NEVER
19
+ // re-export it from a client-bundle-reachable entry.
20
+ import { AsyncLocalStorage } from 'node:async_hooks';
21
+ // Shared via globalThis for the same dual-load reason as the ORM registry:
22
+ // adapter packages, the `@rudderjs/orm/sticky` shim, and a bundled copy of
23
+ // this module must all see ONE scope. The key keeps its historical `orm` name
24
+ // on purpose — renaming it would split the ALS across an upgrade re-boot.
25
+ const STICKY_KEY = '__rudderjs_orm_sticky__';
26
+ const _g = globalThis;
27
+ if (!_g[STICKY_KEY]) {
28
+ _g[STICKY_KEY] = new AsyncLocalStorage();
29
+ }
30
+ const _als = _g[STICKY_KEY];
31
+ /** Run `fn` inside a fresh database context — the unit sticky reads scope to.
32
+ * The middleware wraps each request in one; wrap queue-job/command bodies
33
+ * manually if they need read-your-writes on a sticky connection. */
34
+ export function runWithDatabaseContext(fn) {
35
+ return _als.run(new Set(), fn);
36
+ }
37
+ /** Whether a database context (request scope) is active. */
38
+ export function hasDatabaseContext() {
39
+ return _als.getStore() !== undefined;
40
+ }
41
+ /** Record that `connection` performed a write in the current context.
42
+ * No-op outside a context. Called by split adapters' write paths. */
43
+ export function markWrote(connection) {
44
+ _als.getStore()?.add(connection);
45
+ }
46
+ /** Whether `connection` wrote in the current context — when true (and the
47
+ * connection is `sticky`), reads route to the write connection. Always
48
+ * `false` outside a context. */
49
+ export function stickyWrote(connection) {
50
+ return _als.getStore()?.has(connection) ?? false;
51
+ }
52
+ /**
53
+ * Request middleware entering a database context, so sticky reads scope to
54
+ * the request. Auto-installed on the `web` + `api` groups by the native
55
+ * provider when any configured connection sets `sticky: true` with a `read`
56
+ * pool; harmless to install twice (an inner scope simply shadows the outer).
57
+ */
58
+ export function databaseContextMiddleware() {
59
+ return (_req, _res, next) => runWithDatabaseContext(() => next());
60
+ }
61
+ //# sourceMappingURL=sticky.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sticky.js","sourceRoot":"","sources":["../src/sticky.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,oEAAoE;AACpE,8EAA8E;AAC9E,8DAA8D;AAC9D,4EAA4E;AAC5E,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,4EAA4E;AAC5E,2CAA2C;AAC3C,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,uEAAuE;AACvE,qDAAqD;AAErD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGpD,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,UAAU,GAAG,yBAAyB,CAAA;AAC5C,MAAM,EAAE,GAAG,UAAqC,CAAA;AAChD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;IACpB,EAAE,CAAC,UAAU,CAAC,GAAG,IAAI,iBAAiB,EAAe,CAAA;AACvD,CAAC;AACD,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAmC,CAAA;AAE7D;;qEAEqE;AACrE,MAAM,UAAU,sBAAsB,CAAI,EAAW;IACnD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAA;AAChC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAA;AACtC,CAAC;AAED;sEACsE;AACtE,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,UAAU,CAAC,CAAA;AAClC,CAAC;AAED;;iCAEiC;AACjC,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,KAAK,CAAA;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;AACnE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rudderjs/database",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,13 +21,43 @@
21
21
  ".": {
22
22
  "import": "./dist/index.js",
23
23
  "types": "./dist/index.d.ts"
24
+ },
25
+ "./sticky": {
26
+ "node": "./dist/sticky.js",
27
+ "import": "./dist/sticky.js",
28
+ "default": "./dist/sticky.js",
29
+ "types": "./dist/sticky.d.ts"
30
+ },
31
+ "./native": {
32
+ "node": "./dist/native/index.js",
33
+ "import": "./dist/native/index.js",
34
+ "default": "./dist/native/index.js",
35
+ "types": "./dist/native/index.d.ts"
24
36
  }
25
37
  },
26
38
  "dependencies": {
27
- "@rudderjs/contracts": "^1.10.0"
39
+ "@rudderjs/contracts": "^1.12.0"
40
+ },
41
+ "peerDependencies": {
42
+ "better-sqlite3": "^12.0.0",
43
+ "postgres": "^3.0.0",
44
+ "mysql2": "^3.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "better-sqlite3": {
48
+ "optional": true
49
+ },
50
+ "postgres": {
51
+ "optional": true
52
+ },
53
+ "mysql2": {
54
+ "optional": true
55
+ }
28
56
  },
29
57
  "devDependencies": {
58
+ "@types/better-sqlite3": "^7.0.0",
30
59
  "@types/node": "^20.0.0",
60
+ "better-sqlite3": "^12.0.0",
31
61
  "typescript": "^5.4.0",
32
62
  "tsx": "^4.0.0"
33
63
  },