@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.
- package/README.md +70 -0
- package/dist/db.d.ts +21 -3
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +27 -5
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/native/adapter.d.ts +202 -0
- package/dist/native/adapter.d.ts.map +1 -0
- package/dist/native/adapter.js +440 -0
- package/dist/native/adapter.js.map +1 -0
- package/dist/native/compiler.d.ts +371 -0
- package/dist/native/compiler.d.ts.map +1 -0
- package/dist/native/compiler.js +978 -0
- package/dist/native/compiler.js.map +1 -0
- package/dist/native/dialect-mysql.d.ts +26 -0
- package/dist/native/dialect-mysql.d.ts.map +1 -0
- package/dist/native/dialect-mysql.js +188 -0
- package/dist/native/dialect-mysql.js.map +1 -0
- package/dist/native/dialect-pg.d.ts +26 -0
- package/dist/native/dialect-pg.d.ts.map +1 -0
- package/dist/native/dialect-pg.js +192 -0
- package/dist/native/dialect-pg.js.map +1 -0
- package/dist/native/dialect.d.ts +255 -0
- package/dist/native/dialect.d.ts.map +1 -0
- package/dist/native/dialect.js +237 -0
- package/dist/native/dialect.js.map +1 -0
- package/dist/native/driver.d.ts +37 -0
- package/dist/native/driver.d.ts.map +1 -0
- package/dist/native/driver.js +19 -0
- package/dist/native/driver.js.map +1 -0
- package/dist/native/drivers/better-sqlite3.d.ts +56 -0
- package/dist/native/drivers/better-sqlite3.d.ts.map +1 -0
- package/dist/native/drivers/better-sqlite3.js +171 -0
- package/dist/native/drivers/better-sqlite3.js.map +1 -0
- package/dist/native/drivers/mysql.d.ts +30 -0
- package/dist/native/drivers/mysql.d.ts.map +1 -0
- package/dist/native/drivers/mysql.js +176 -0
- package/dist/native/drivers/mysql.js.map +1 -0
- package/dist/native/drivers/postgres.d.ts +57 -0
- package/dist/native/drivers/postgres.d.ts.map +1 -0
- package/dist/native/drivers/postgres.js +155 -0
- package/dist/native/drivers/postgres.js.map +1 -0
- package/dist/native/errors.d.ts +43 -0
- package/dist/native/errors.d.ts.map +1 -0
- package/dist/native/errors.js +64 -0
- package/dist/native/errors.js.map +1 -0
- package/dist/native/index.d.ts +27 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +55 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/isolation.d.ts +14 -0
- package/dist/native/isolation.d.ts.map +1 -0
- package/dist/native/isolation.js +37 -0
- package/dist/native/isolation.js.map +1 -0
- package/dist/native/query-builder.d.ts +303 -0
- package/dist/native/query-builder.d.ts.map +1 -0
- package/dist/native/query-builder.js +984 -0
- package/dist/native/query-builder.js.map +1 -0
- package/dist/native/replica-picker.d.ts +22 -0
- package/dist/native/replica-picker.d.ts.map +1 -0
- package/dist/native/replica-picker.js +65 -0
- package/dist/native/replica-picker.js.map +1 -0
- package/dist/native/schema/alter-blueprint.d.ts +37 -0
- package/dist/native/schema/alter-blueprint.d.ts.map +1 -0
- package/dist/native/schema/alter-blueprint.js +56 -0
- package/dist/native/schema/alter-blueprint.js.map +1 -0
- package/dist/native/schema/blueprint.d.ts +151 -0
- package/dist/native/schema/blueprint.d.ts.map +1 -0
- package/dist/native/schema/blueprint.js +286 -0
- package/dist/native/schema/blueprint.js.map +1 -0
- package/dist/native/schema/column.d.ts +168 -0
- package/dist/native/schema/column.d.ts.map +1 -0
- package/dist/native/schema/column.js +190 -0
- package/dist/native/schema/column.js.map +1 -0
- package/dist/native/schema/ddl-compiler.d.ts +34 -0
- package/dist/native/schema/ddl-compiler.d.ts.map +1 -0
- package/dist/native/schema/ddl-compiler.js +352 -0
- package/dist/native/schema/ddl-compiler.js.map +1 -0
- package/dist/native/schema/inspect.d.ts +67 -0
- package/dist/native/schema/inspect.d.ts.map +1 -0
- package/dist/native/schema/inspect.js +312 -0
- package/dist/native/schema/inspect.js.map +1 -0
- package/dist/native/schema/introspect.d.ts +34 -0
- package/dist/native/schema/introspect.d.ts.map +1 -0
- package/dist/native/schema/introspect.js +101 -0
- package/dist/native/schema/introspect.js.map +1 -0
- package/dist/native/schema/migration.d.ts +8 -0
- package/dist/native/schema/migration.d.ts.map +1 -0
- package/dist/native/schema/migration.js +19 -0
- package/dist/native/schema/migration.js.map +1 -0
- package/dist/native/schema/migrator.d.ts +144 -0
- package/dist/native/schema/migrator.d.ts.map +1 -0
- package/dist/native/schema/migrator.js +240 -0
- package/dist/native/schema/migrator.js.map +1 -0
- package/dist/native/schema/rebuild.d.ts +11 -0
- package/dist/native/schema/rebuild.d.ts.map +1 -0
- package/dist/native/schema/rebuild.js +92 -0
- package/dist/native/schema/rebuild.js.map +1 -0
- package/dist/native/schema/schema-builder.d.ts +46 -0
- package/dist/native/schema/schema-builder.d.ts.map +1 -0
- package/dist/native/schema/schema-builder.js +153 -0
- package/dist/native/schema/schema-builder.js.map +1 -0
- package/dist/native/schema/schema-facade.d.ts +63 -0
- package/dist/native/schema/schema-facade.d.ts.map +1 -0
- package/dist/native/schema/schema-facade.js +124 -0
- package/dist/native/schema/schema-facade.js.map +1 -0
- package/dist/native/schema/schema-types.d.ts +27 -0
- package/dist/native/schema/schema-types.d.ts.map +1 -0
- package/dist/native/schema/schema-types.js +52 -0
- package/dist/native/schema/schema-types.js.map +1 -0
- package/dist/native/schema/types-generator.d.ts +73 -0
- package/dist/native/schema/types-generator.d.ts.map +1 -0
- package/dist/native/schema/types-generator.js +181 -0
- package/dist/native/schema/types-generator.js.map +1 -0
- package/dist/registry-bridge.d.ts +24 -4
- package/dist/registry-bridge.d.ts.map +1 -1
- package/dist/registry-bridge.js +20 -0
- package/dist/registry-bridge.js.map +1 -1
- package/dist/sticky.d.ts +22 -0
- package/dist/sticky.d.ts.map +1 -0
- package/dist/sticky.js +61 -0
- package/dist/sticky.js.map +1 -0
- 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
|
|
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
|
|
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;
|
|
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"}
|
package/dist/registry-bridge.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/sticky.d.ts
ADDED
|
@@ -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
|
|
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.
|
|
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
|
},
|