@zmdb/orm 1.0.0-beta.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 (87) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +30 -0
  3. package/dist/cache/index.d.ts +34 -0
  4. package/dist/cache/index.d.ts.map +1 -0
  5. package/dist/cache/index.js +149 -0
  6. package/dist/cache/index.js.map +1 -0
  7. package/dist/drivers/transactional.d.ts +6 -0
  8. package/dist/drivers/transactional.d.ts.map +1 -0
  9. package/dist/drivers/transactional.js +2 -0
  10. package/dist/drivers/transactional.js.map +1 -0
  11. package/dist/dto/index.d.ts +41 -0
  12. package/dist/dto/index.d.ts.map +1 -0
  13. package/dist/dto/index.js +334 -0
  14. package/dist/dto/index.js.map +1 -0
  15. package/dist/entity-modeling/index.d.ts +18 -0
  16. package/dist/entity-modeling/index.d.ts.map +1 -0
  17. package/dist/entity-modeling/index.js +26 -0
  18. package/dist/entity-modeling/index.js.map +1 -0
  19. package/dist/filters/index.d.ts +62 -0
  20. package/dist/filters/index.d.ts.map +1 -0
  21. package/dist/filters/index.js +163 -0
  22. package/dist/filters/index.js.map +1 -0
  23. package/dist/index.d.ts +413 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +2003 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/loaders/index.d.ts +38 -0
  28. package/dist/loaders/index.d.ts.map +1 -0
  29. package/dist/loaders/index.js +164 -0
  30. package/dist/loaders/index.js.map +1 -0
  31. package/dist/outbox/index.d.ts +59 -0
  32. package/dist/outbox/index.d.ts.map +1 -0
  33. package/dist/outbox/index.js +323 -0
  34. package/dist/outbox/index.js.map +1 -0
  35. package/dist/outbox/sql.d.ts +52 -0
  36. package/dist/outbox/sql.d.ts.map +1 -0
  37. package/dist/outbox/sql.js +115 -0
  38. package/dist/outbox/sql.js.map +1 -0
  39. package/dist/relations/index.d.ts +38 -0
  40. package/dist/relations/index.d.ts.map +1 -0
  41. package/dist/relations/index.js +164 -0
  42. package/dist/relations/index.js.map +1 -0
  43. package/dist/replicas/index.d.ts +10 -0
  44. package/dist/replicas/index.d.ts.map +1 -0
  45. package/dist/replicas/index.js +42 -0
  46. package/dist/replicas/index.js.map +1 -0
  47. package/dist/seeding/index.d.ts +18 -0
  48. package/dist/seeding/index.d.ts.map +1 -0
  49. package/dist/seeding/index.js +66 -0
  50. package/dist/seeding/index.js.map +1 -0
  51. package/dist/streaming/index.d.ts +7 -0
  52. package/dist/streaming/index.d.ts.map +1 -0
  53. package/dist/streaming/index.js +67 -0
  54. package/dist/streaming/index.js.map +1 -0
  55. package/dist/testing/official-dialects.fixture.d.ts +16 -0
  56. package/dist/testing/official-dialects.fixture.d.ts.map +1 -0
  57. package/dist/testing/official-dialects.fixture.js +21 -0
  58. package/dist/testing/official-dialects.fixture.js.map +1 -0
  59. package/dist/testing/repository.fixture.d.ts +11 -0
  60. package/dist/testing/repository.fixture.d.ts.map +1 -0
  61. package/dist/testing/repository.fixture.js +33 -0
  62. package/dist/testing/repository.fixture.js.map +1 -0
  63. package/dist/transactions/index.d.ts +40 -0
  64. package/dist/transactions/index.d.ts.map +1 -0
  65. package/dist/transactions/index.js +249 -0
  66. package/dist/transactions/index.js.map +1 -0
  67. package/dist/typed-populate/nested.fixtures.d.ts +34 -0
  68. package/dist/typed-populate/nested.fixtures.d.ts.map +1 -0
  69. package/dist/typed-populate/nested.fixtures.js +4 -0
  70. package/dist/typed-populate/nested.fixtures.js.map +1 -0
  71. package/package.json +78 -0
  72. package/src/cache/index.ts +188 -0
  73. package/src/drivers/transactional.ts +6 -0
  74. package/src/dto/index.ts +379 -0
  75. package/src/entity-modeling/index.ts +38 -0
  76. package/src/filters/index.ts +267 -0
  77. package/src/index.ts +2757 -0
  78. package/src/loaders/index.ts +256 -0
  79. package/src/outbox/index.ts +409 -0
  80. package/src/outbox/sql.ts +179 -0
  81. package/src/relations/index.ts +221 -0
  82. package/src/replicas/index.ts +51 -0
  83. package/src/seeding/index.ts +72 -0
  84. package/src/streaming/index.ts +67 -0
  85. package/src/testing/repository.fixture.ts +44 -0
  86. package/src/transactions/index.ts +315 -0
  87. package/src/typed-populate/nested.fixtures.ts +59 -0
@@ -0,0 +1,164 @@
1
+ import { resolveRelation } from '@zmdb/schema/relations';
2
+ import { dialectName, dialectTraits, formatPlaceholder, quoteIdentifier, renderPredicate, UnsupportedFeatureError, } from '@zmdb/sql';
3
+ function sanitizeKeys(keys) {
4
+ const result = [];
5
+ const seen = new Set();
6
+ for (const k of keys) {
7
+ if (k !== null && k !== undefined && !seen.has(k)) {
8
+ seen.add(k);
9
+ result.push(k);
10
+ }
11
+ }
12
+ return result;
13
+ }
14
+ function sameKeyValue(left, right) {
15
+ if (left instanceof Date && right instanceof Date)
16
+ return left.getTime() === right.getTime();
17
+ return Object.is(left, right);
18
+ }
19
+ function sanitizeCompositeKeys(ir, relationName, keys, arity) {
20
+ const result = [];
21
+ for (const key of keys) {
22
+ if (key === null || key === undefined)
23
+ continue;
24
+ if (!Array.isArray(key)) {
25
+ throw new Error(`${ir.table}.${relationName}: composite-key populate expects a ${String(arity)}-column tuple for every parent`);
26
+ }
27
+ if (key.length !== arity) {
28
+ throw new Error(`${ir.table}.${relationName}: composite-key populate expected ${String(arity)} values, received ${String(key.length)}`);
29
+ }
30
+ if (key.some(value => value === null || value === undefined))
31
+ continue;
32
+ if (result.some(existing => existing.every((value, index) => sameKeyValue(value, key[index]))))
33
+ continue;
34
+ result.push([...key]);
35
+ }
36
+ return result;
37
+ }
38
+ /**
39
+ * Compile a populate hint into SQL: a to-one becomes an `INNER JOIN`, a to-many a batched
40
+ * `IN (…)` select over the parent keys.
41
+ *
42
+ * Takes the declaring table's IR and a relation name rather than a relation object, which is
43
+ * what makes the two spellings one: the columns on either side of the `ON` come out of
44
+ * `resolveRelation`, the same call `@zmdb/orm`'s `populate` makes.
45
+ */
46
+ export function compilePopulate(ir, relationName, dialect, parentIds = [], targetFilters = [], schemas = []) {
47
+ const rel = resolveRelation(ir, relationName);
48
+ const q = (name) => quoteIdentifier(dialect, name);
49
+ const targetIr = schemas.find(schema => schema.table === rel.targetTable);
50
+ const sourceTable = ir.physicalTable;
51
+ const targetTable = targetIr?.physicalTable ?? rel.targetTable;
52
+ const physicalColumn = (schema, declared) => {
53
+ if (schema === undefined)
54
+ return declared;
55
+ const separator = declared.lastIndexOf('.');
56
+ const property = separator === -1 ? declared : declared.slice(separator + 1);
57
+ const physical = schema.columns.find(column => column.name === property)?.physicalName;
58
+ if (physical === undefined)
59
+ return declared;
60
+ if (separator === -1)
61
+ return physical;
62
+ const qualifier = declared.slice(0, separator);
63
+ return `${qualifier === schema.table ? schema.physicalTable : qualifier}.${physical}`;
64
+ };
65
+ const parentKeys = rel.parentKey.map(column => physicalColumn(ir, column));
66
+ const targetKeys = rel.targetKey.map(column => physicalColumn(targetIr, column));
67
+ const physicalFilters = targetFilters.map(predicate => ({
68
+ ...predicate,
69
+ col: physicalColumn(targetIr, predicate.col),
70
+ }));
71
+ const renderFilters = (parameters) => {
72
+ if (physicalFilters.length === 0)
73
+ return '';
74
+ const body = physicalFilters
75
+ .map((predicate, index) => {
76
+ const rendered = renderPredicate(dialect, predicate, parameters);
77
+ return index === 0 ? rendered : `${predicate.connector ?? 'AND'} ${rendered}`;
78
+ })
79
+ .join(' ');
80
+ const grouped = targetFilters.some((predicate, index) => index > 0 && predicate.connector === 'OR');
81
+ return `AND ${grouped ? `(${body})` : body}`;
82
+ };
83
+ if (!rel.toMany) {
84
+ const parameters = [];
85
+ const filtered = physicalFilters.length > 0;
86
+ const onFilters = renderFilters(parameters);
87
+ const conditions = parentKeys.map((parentKey, index) => {
88
+ const targetKey = targetKeys[index];
89
+ if (targetKey === undefined) {
90
+ throw new Error(`${ir.table}.${relationName}: resolved relation keys have different lengths`);
91
+ }
92
+ return `${q(sourceTable)}.${q(parentKey)} = ${q(targetTable)}.${q(targetKey)}`;
93
+ });
94
+ const sql = `SELECT * FROM ${q(sourceTable)} ${filtered ? 'LEFT' : 'INNER'} JOIN ${q(targetTable)} ` +
95
+ `ON ${conditions.join(' AND ')}` +
96
+ (onFilters.length === 0 ? '' : ` ${onFilters}`);
97
+ return { kind: 'join', sql, parameters };
98
+ }
99
+ if (parentIds.length === 0) {
100
+ return { kind: 'batched', sql: `SELECT * FROM ${q(targetTable)} WHERE 1 = 0`, parameters: [] };
101
+ }
102
+ if (targetKeys.length === 1) {
103
+ const sanitized = sanitizeKeys(parentIds);
104
+ if (sanitized.length === 0) {
105
+ return { kind: 'batched', sql: `SELECT * FROM ${q(targetTable)} WHERE 1 = 0`, parameters: [] };
106
+ }
107
+ const [targetKey] = targetKeys;
108
+ if (targetKey === undefined) {
109
+ throw new Error(`${ir.table}.${relationName}: resolved relation has no target key`);
110
+ }
111
+ const inList = sanitized.map((_, i) => formatPlaceholder(dialect, i + 1)).join(', ');
112
+ const parameters = [...sanitized];
113
+ const filters = renderFilters(parameters);
114
+ const sql = `SELECT * FROM ${q(targetTable)} WHERE ${q(targetKey)} IN (${inList})` +
115
+ (filters.length === 0 ? '' : ` ${filters}`);
116
+ return { kind: 'batched', sql, parameters };
117
+ }
118
+ if (!dialectTraits(dialect).rowValueIn) {
119
+ const name = dialectName(dialect);
120
+ throw new UnsupportedFeatureError(`composite-key populate for relation "${relationName}"`, name, `${ir.table}.${relationName}: dialect "${name}" does not support row-value IN for a composite-key populate`);
121
+ }
122
+ const sanitized = sanitizeCompositeKeys(ir, relationName, parentIds, targetKeys.length);
123
+ if (sanitized.length === 0) {
124
+ return { kind: 'batched', sql: `SELECT * FROM ${q(targetTable)} WHERE 1 = 0`, parameters: [] };
125
+ }
126
+ const parameters = [];
127
+ const inList = sanitized
128
+ .map(tuple => {
129
+ const placeholders = tuple.map(value => {
130
+ parameters.push(value);
131
+ return formatPlaceholder(dialect, parameters.length);
132
+ });
133
+ return `(${placeholders.join(', ')})`;
134
+ })
135
+ .join(', ');
136
+ const columns = targetKeys.map(targetKey => q(targetKey)).join(', ');
137
+ const filters = renderFilters(parameters);
138
+ const sql = `SELECT * FROM ${q(targetTable)} WHERE (${columns}) IN (${inList})` + (filters.length === 0 ? '' : ` ${filters}`);
139
+ return { kind: 'batched', sql, parameters };
140
+ }
141
+ /**
142
+ * Attach a populated relation to a parent row, without mutating it.
143
+ *
144
+ * The type widening is `Populated<T, K>` in `../derive/query.ts`, which reads the declared
145
+ * relation property; this only puts the value there.
146
+ */
147
+ export function attachPopulated(parent, name, value) {
148
+ // boundary: a computed key in an object literal widens to `string`, so TS types
149
+ // this spread as `P & { [x: string]: V }`. `name` is the literal `N` at the call
150
+ // site, which is what the return type states.
151
+ return { ...parent, [name]: value };
152
+ }
153
+ /** Rename aliased columns per a { alias: outKey } map (stable, non-mutating). */
154
+ export function aliasRow(row, map) {
155
+ const out = { ...row };
156
+ for (const [alias, outKey] of Object.entries(map)) {
157
+ if (alias in out) {
158
+ out[outKey] = out[alias];
159
+ delete out[alias]; // rename: drop the original aliased key
160
+ }
161
+ }
162
+ return out;
163
+ }
164
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/relations/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type Driver } from '../index.js';
2
+ export interface ReplicaOptions {
3
+ primary: Driver;
4
+ replicas: readonly Driver[];
5
+ pick?: (replicas: readonly Driver[], nextIndex: number) => Driver;
6
+ }
7
+ export declare function isWrite(sql: string): boolean;
8
+ /** Wrap primary+replicas into a single Driver that routes reads to replicas. */
9
+ export declare function withReplicas(opts: ReplicaOptions): Driver;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/replicas/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CACnE;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG5C;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAiCzD"}
@@ -0,0 +1,42 @@
1
+ import {} from '@zmdb/sql';
2
+ // Read-replica routing — see ./SPEC.md.
3
+ import {} from '../index.js';
4
+ export function isWrite(sql) {
5
+ const s = sql.trimStart().toUpperCase();
6
+ return s.startsWith('INSERT') || s.startsWith('UPDATE') || s.startsWith('DELETE');
7
+ }
8
+ /** Wrap primary+replicas into a single Driver that routes reads to replicas. */
9
+ export function withReplicas(opts) {
10
+ const { primary, replicas } = opts;
11
+ let rr = 0;
12
+ const pick = (query) => {
13
+ if (isWrite(query.text) || replicas.length === 0)
14
+ return primary;
15
+ const driver = opts.pick ? opts.pick(replicas, rr) : replicas[rr % replicas.length];
16
+ rr = (rr + 1) % replicas.length;
17
+ // `replicas` is non-empty here (checked above), so the modulo index always
18
+ // hits — but a custom `pick` is caller code, so fall back to the primary
19
+ // rather than crashing on a bad index.
20
+ return driver ?? primary;
21
+ };
22
+ const canStream = typeof primary.stream === 'function' && replicas.every(driver => typeof driver.stream === 'function');
23
+ return {
24
+ dialect: primary.dialect,
25
+ execute(query, executeOpts) {
26
+ return pick(query).execute(query, executeOpts);
27
+ },
28
+ ...(canStream
29
+ ? {
30
+ stream(query, executeOpts) {
31
+ const driver = pick(query);
32
+ const stream = driver.stream;
33
+ if (typeof stream !== 'function') {
34
+ throw new Error('replica routing selected a driver without stream support');
35
+ }
36
+ return stream.call(driver, query, executeOpts);
37
+ },
38
+ }
39
+ : {}),
40
+ };
41
+ }
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/replicas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,WAAW,CAAC;AAE/C,wCAAwC;AACxC,OAAO,EAAe,MAAM,aAAa,CAAC;AAQ1C,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,CAAC,GAAG,GAAG,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,IAAoB;IAC/C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACnC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,MAAM,IAAI,GAAG,CAAC,KAAoB,EAAU,EAAE;QAC5C,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpF,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;QAChC,2EAA2E;QAC3E,yEAAyE;QACzE,uCAAuC;QACvC,OAAO,MAAM,IAAI,OAAO,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,SAAS,GACb,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IACxG,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,CAAC,KAAK,EAAE,WAAW;YACxB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACjD,CAAC;QACD,GAAG,CAAC,SAAS;YACX,CAAC,CAAC;gBACE,MAAM,CAAC,KAAoB,EAAE,WAA0D;oBACrF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;oBAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;oBAC7B,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;wBACjC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;oBAC9E,CAAC;oBACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;gBACjD,CAAC;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type CreateDTO, type DeclaredTable, type TaggedSchema } from '@zmdb/schema';
2
+ /** Deterministic PRNG (mulberry32). Same seed ⇒ same sequence. */
3
+ export declare function makeRng(seed: number): () => number;
4
+ export interface SeedOptions {
5
+ seed?: number;
6
+ count: number;
7
+ }
8
+ /**
9
+ * `count` rows shaped like `T`'s create DTO, reproducible from `seed`.
10
+ *
11
+ * Typed as `CreateDTO<T>[]`, which the column-map version could not be: it built its rows
12
+ * from the lossy projection and had no way to tell the compiler what it had made. `random`'s
13
+ * type parameter is inferred from the return type here, so there is no assertion — and no
14
+ * type argument either, which is what keeps the transformer out of a call whose shape is only
15
+ * known at runtime.
16
+ */
17
+ export declare function seedRows<T extends DeclaredTable>(schema: TaggedSchema<T>, opts: SeedOptions): CreateDTO<T>[];
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seeding/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AA2BrF,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CASlD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAcD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAM5G"}
@@ -0,0 +1,66 @@
1
+ import {} from '@zmdb/schema';
2
+ import { objectTypeFromIR } from '@zmdb/schema/ir';
3
+ // Deterministic seeding — see ./SPEC.md.
4
+ //
5
+ // This module used to live in `@zmdb/schema/seeding` and generate its values from a
6
+ // walk of its own over `schema.columns`: a `switch` on `col.type` returning a random number
7
+ // for an integer, a random `s…` for anything string-ish, and a member of `flags.enum` for an
8
+ // enum. It was the fifth walker over column metadata, and it had the failure mode all five
9
+ // shared — it read part of what a column says. `Min<18>`, `Max<120>`, `Pattern<…>`,
10
+ // `MinLength`, a `json` payload shape and nullability were all invisible to it, so seeding a
11
+ // table with any of them produced rows the table's own validator rejects.
12
+ //
13
+ // It generates from the IR now, through the same sampler `random<T>()` uses, which reads the
14
+ // whole vocabulary and *refuses* what it cannot satisfy rather than guessing. Two
15
+ // consequences worth stating, because both are visible to a caller:
16
+ //
17
+ // - The rows honour the constraints. A `Min<18>` column is seeded within its bounds.
18
+ // - A `Pattern<…>` column is refused, with the column named. Nothing inverts a regular
19
+ // expression, so the alternative is a value that violates the pattern — which is what
20
+ // used to happen, silently, and surfaced later as a validation failure in a test whose
21
+ // subject was something else.
22
+ //
23
+ // Seeding lives in ORM because its sampler ships in `@zmdb/validator`, which
24
+ // depends on `@zmdb/schema/ir`. The dependencies point inward from ORM. That is also the honest home: a seeded row exists to be handed to
25
+ // `repository.create`, and now it type-checks as one.
26
+ import { random } from '@zmdb/validator';
27
+ /** Deterministic PRNG (mulberry32). Same seed ⇒ same sequence. */
28
+ export function makeRng(seed) {
29
+ let a = seed >>> 0;
30
+ return () => {
31
+ a |= 0;
32
+ a = (a + 0x6d2b79f5) | 0;
33
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
34
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
35
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
36
+ };
37
+ }
38
+ /**
39
+ * The create shape with its optional properties dropped — the columns a caller *must* supply.
40
+ *
41
+ * The sampler fills every property of an object it is given, including the optional ones, and
42
+ * that is right for `random<T>()`: a sample of a type is a value of that type. It is wrong
43
+ * here. A column is optional in `CreateDTO` because the database has a default for it, and
44
+ * seeding a value over the default is how a seeded row stops resembling an inserted one.
45
+ */
46
+ function requiredColumns(shape) {
47
+ return { ...shape, properties: shape.properties.filter(property => !property.optional) };
48
+ }
49
+ /**
50
+ * `count` rows shaped like `T`'s create DTO, reproducible from `seed`.
51
+ *
52
+ * Typed as `CreateDTO<T>[]`, which the column-map version could not be: it built its rows
53
+ * from the lossy projection and had no way to tell the compiler what it had made. `random`'s
54
+ * type parameter is inferred from the return type here, so there is no assertion — and no
55
+ * type argument either, which is what keeps the transformer out of a call whose shape is only
56
+ * known at runtime.
57
+ */
58
+ export function seedRows(schema, opts) {
59
+ const rng = makeRng(opts.seed ?? 1);
60
+ const shape = requiredColumns(objectTypeFromIR(schema.ir, 'create'));
61
+ const rows = [];
62
+ for (let i = 0; i < opts.count; i++)
63
+ rows.push(random(shape, rng));
64
+ return rows;
65
+ }
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seeding/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAiB,MAAM,iBAAiB,CAAC;AAClE,yCAAyC;AACzC,EAAE;AACF,oFAAoF;AACpF,4FAA4F;AAC5F,6FAA6F;AAC7F,2FAA2F;AAC3F,oFAAoF;AACpF,6FAA6F;AAC7F,0EAA0E;AAC1E,EAAE;AACF,6FAA6F;AAC7F,kFAAkF;AAClF,oEAAoE;AACpE,EAAE;AACF,uFAAuF;AACvF,yFAAyF;AACzF,0FAA0F;AAC1F,2FAA2F;AAC3F,kCAAkC;AAClC,EAAE;AACF,6EAA6E;AAC7E,0IAA0I;AAC1I,sDAAsD;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,kEAAkE;AAClE,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC;IACnB,OAAO,GAAG,EAAE;QACV,CAAC,IAAI,CAAC,CAAC;QACP,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;IAC/C,CAAC,CAAC;AACJ,CAAC;AAOD;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,KAAe;IACtC,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAA0B,MAAuB,EAAE,IAAiB;IAC1F,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,eAAe,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrE,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Add repository ergonomics around a driver's deliberately small stream
3
+ * surface. The driver owns its cursor; this wrapper owns single-shot use,
4
+ * per-row mapping, and exactly one cleanup path.
5
+ */
6
+ export declare function createRepositoryStream<Raw, Row>(open: () => AsyncIterable<Raw>, map: (row: Raw) => Row, signal?: AbortSignal): AsyncIterable<Row> & AsyncDisposable;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/streaming/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAC7C,IAAI,EAAE,MAAM,aAAa,CAAC,GAAG,CAAC,EAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,aAAa,CAAC,GAAG,CAAC,GAAG,eAAe,CAyDtC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Add repository ergonomics around a driver's deliberately small stream
3
+ * surface. The driver owns its cursor; this wrapper owns single-shot use,
4
+ * per-row mapping, and exactly one cleanup path.
5
+ */
6
+ export function createRepositoryStream(open, map, signal) {
7
+ let started = false;
8
+ let disposed = false;
9
+ let active;
10
+ let closing;
11
+ const close = () => {
12
+ if (closing !== undefined)
13
+ return closing;
14
+ disposed = true;
15
+ const iterator = active;
16
+ active = undefined;
17
+ closing = (async () => {
18
+ if (iterator?.return !== undefined)
19
+ await iterator.return();
20
+ })();
21
+ return closing;
22
+ };
23
+ return {
24
+ [Symbol.asyncIterator]() {
25
+ if (started)
26
+ throw new Error('repository stream is single-shot');
27
+ if (disposed)
28
+ throw new Error('repository stream has been disposed');
29
+ started = true;
30
+ const iterate = async function* () {
31
+ let completed = false;
32
+ try {
33
+ if (disposed)
34
+ throw new Error('repository stream has been disposed');
35
+ signal?.throwIfAborted();
36
+ active = open()[Symbol.asyncIterator]();
37
+ for (;;) {
38
+ signal?.throwIfAborted();
39
+ const iterator = active;
40
+ if (iterator === undefined)
41
+ return;
42
+ const next = await iterator.next();
43
+ if (disposed)
44
+ return;
45
+ signal?.throwIfAborted();
46
+ if (next.done) {
47
+ completed = true;
48
+ active = undefined;
49
+ disposed = true;
50
+ return;
51
+ }
52
+ yield map(next.value);
53
+ }
54
+ }
55
+ finally {
56
+ if (!completed)
57
+ await close();
58
+ }
59
+ };
60
+ return iterate();
61
+ },
62
+ [Symbol.asyncDispose]() {
63
+ return close();
64
+ },
65
+ };
66
+ }
67
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/streaming/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAA8B,EAC9B,GAAsB,EACtB,MAAoB;IAEpB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,MAAsC,CAAC;IAC3C,IAAI,OAAkC,CAAC;IAEvC,MAAM,KAAK,GAAG,GAAkB,EAAE;QAChC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC;QAC1C,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,QAAQ,GAAG,MAAM,CAAC;QACxB,MAAM,GAAG,SAAS,CAAC;QACnB,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE;YACpB,IAAI,QAAQ,EAAE,MAAM,KAAK,SAAS;gBAAE,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9D,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,CAAC,MAAM,CAAC,aAAa,CAAC;YACpB,IAAI,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACjE,IAAI,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACrE,OAAO,GAAG,IAAI,CAAC;YAEf,MAAM,OAAO,GAAG,KAAK,SAAS,CAAC;gBAC7B,IAAI,SAAS,GAAG,KAAK,CAAC;gBACtB,IAAI,CAAC;oBACH,IAAI,QAAQ;wBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;oBACrE,MAAM,EAAE,cAAc,EAAE,CAAC;oBACzB,MAAM,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;oBAExC,SAAS,CAAC;wBACR,MAAM,EAAE,cAAc,EAAE,CAAC;wBACzB,MAAM,QAAQ,GAAG,MAAM,CAAC;wBACxB,IAAI,QAAQ,KAAK,SAAS;4BAAE,OAAO;wBACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;wBACnC,IAAI,QAAQ;4BAAE,OAAO;wBACrB,MAAM,EAAE,cAAc,EAAE,CAAC;wBACzB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;4BACd,SAAS,GAAG,IAAI,CAAC;4BACjB,MAAM,GAAG,SAAS,CAAC;4BACnB,QAAQ,GAAG,IAAI,CAAC;4BAChB,OAAO;wBACT,CAAC;wBACD,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACxB,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,IAAI,CAAC,SAAS;wBAAE,MAAM,KAAK,EAAE,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC;YAEF,OAAO,OAAO,EAAE,CAAC;QACnB,CAAC;QAED,CAAC,MAAM,CAAC,YAAY,CAAC;YACnB,OAAO,KAAK,EAAE,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const cockroachDialect: import("@zmdb/sql").SqlDialect<"cockroach">;
2
+ export declare const mssqlDialect: import("@zmdb/sql").SqlDialect<"mssql">;
3
+ export declare const mysqlDialect: import("@zmdb/sql").SqlDialect<"mysql">;
4
+ export declare const postgresDialect: import("@zmdb/sql").SqlDialect<"postgres">;
5
+ export declare const singlestoreDialect: import("@zmdb/sql").SqlDialect<"singlestore">;
6
+ export declare const sqliteDialect: import("@zmdb/sql").SqlDialect<"sqlite">;
7
+ export declare const officialDialects: Readonly<{
8
+ cockroach: import("@zmdb/sql").SqlDialect<"cockroach">;
9
+ mssql: import("@zmdb/sql").SqlDialect<"mssql">;
10
+ mysql: import("@zmdb/sql").SqlDialect<"mysql">;
11
+ postgres: import("@zmdb/sql").SqlDialect<"postgres">;
12
+ singlestore: import("@zmdb/sql").SqlDialect<"singlestore">;
13
+ sqlite: import("@zmdb/sql").SqlDialect<"sqlite">;
14
+ }>;
15
+ export type OfficialDialectName = keyof typeof officialDialects;
16
+ //# sourceMappingURL=official-dialects.fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official-dialects.fixture.d.ts","sourceRoot":"","sources":["../../src/testing/official-dialects.fixture.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,gBAAgB,6CAAY,CAAC;AAC1C,eAAO,MAAM,YAAY,yCAAQ,CAAC;AAClC,eAAO,MAAM,YAAY,yCAAQ,CAAC;AAClC,eAAO,MAAM,eAAe,4CAAW,CAAC;AACxC,eAAO,MAAM,kBAAkB,+CAAc,CAAC;AAC9C,eAAO,MAAM,aAAa,0CAAS,CAAC;AAEpC,eAAO,MAAM,gBAAgB;;;;;;;EAO3B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,gBAAgB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { cockroach } from '@zmdb/cockroach';
2
+ import { mssql } from '@zmdb/mssql';
3
+ import { mysql } from '@zmdb/mysql';
4
+ import { postgres } from '@zmdb/postgres';
5
+ import { singlestore } from '@zmdb/singlestore';
6
+ import { sqlite } from '@zmdb/sqlite';
7
+ export const cockroachDialect = cockroach;
8
+ export const mssqlDialect = mssql;
9
+ export const mysqlDialect = mysql;
10
+ export const postgresDialect = postgres;
11
+ export const singlestoreDialect = singlestore;
12
+ export const sqliteDialect = sqlite;
13
+ export const officialDialects = Object.freeze({
14
+ cockroach: cockroachDialect,
15
+ mssql: mssqlDialect,
16
+ mysql: mysqlDialect,
17
+ postgres: postgresDialect,
18
+ singlestore: singlestoreDialect,
19
+ sqlite: sqliteDialect,
20
+ });
21
+ //# sourceMappingURL=official-dialects.fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official-dialects.fixture.js","sourceRoot":"","sources":["../../src/testing/official-dialects.fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AACxC,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAC9C,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAEpC,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,SAAS,EAAE,gBAAgB;IAC3B,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,eAAe;IACzB,WAAW,EAAE,kBAAkB;IAC/B,MAAM,EAAE,aAAa;CACtB,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ColumnIR } from '@zmdb/schema/ir';
2
+ import { type CompiledQuery } from '@zmdb/sql';
3
+ import { type Driver } from '../index.js';
4
+ export declare function column(name: string, sql: ColumnIR['sql'], overrides?: Partial<ColumnIR>): ColumnIR;
5
+ interface RecordingDriver extends Driver {
6
+ readonly calls: CompiledQuery[];
7
+ }
8
+ type DriverAnswer = (query: CompiledQuery, call: number) => readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;
9
+ export declare function recordingDriver(answer: DriverAnswer): RecordingDriver;
10
+ export {};
11
+ //# sourceMappingURL=repository.fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.fixture.d.ts","sourceRoot":"","sources":["../../src/testing/repository.fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,QAAQ,CAetG;AAED,UAAU,eAAgB,SAAQ,MAAM;IACtC,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC;CACjC;AAED,KAAK,YAAY,GAAG,CAClB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,KACT,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAEtF,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,CAWrE"}
@@ -0,0 +1,33 @@
1
+ import {} from '@zmdb/schema/ir';
2
+ import {} from '@zmdb/sql';
3
+ import {} from '../index.js';
4
+ import { postgresDialect } from './official-dialects.fixture.js';
5
+ export function column(name, sql, overrides = {}) {
6
+ return {
7
+ name,
8
+ physicalName: name,
9
+ sql,
10
+ nullable: false,
11
+ primaryKey: false,
12
+ serial: false,
13
+ unique: false,
14
+ hasDefault: false,
15
+ sensitive: false,
16
+ constraints: {},
17
+ rules: [],
18
+ ...overrides,
19
+ };
20
+ }
21
+ export function recordingDriver(answer) {
22
+ const calls = [];
23
+ return {
24
+ dialect: postgresDialect,
25
+ calls,
26
+ async execute(query) {
27
+ const call = calls.length;
28
+ calls.push(query);
29
+ return answer(query, call);
30
+ },
31
+ };
32
+ }
33
+ //# sourceMappingURL=repository.fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.fixture.js","sourceRoot":"","sources":["../../src/testing/repository.fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAsB,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAe,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,GAAoB,EAAE,SAAS,GAAsB,EAAE;IAC1F,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,IAAI;QAClB,GAAG;QACH,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,KAAK;QACjB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,EAAE;QACf,KAAK,EAAE,EAAE;QACT,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAWD,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,OAAO;QACL,OAAO,EAAE,eAAe;QACxB,KAAK;QACL,KAAK,CAAC,OAAO,CAAC,KAAK;YACjB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,OAAO,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { type CompiledQuery, type DialectTarget } from '@zmdb/sql';
2
+ import { type ExecuteOptions } from '../index.js';
3
+ export type TransactionState = 'active' | 'closed' | 'committed' | 'rolled_back' | string;
4
+ export interface TransactionContext<State extends string = 'active'> {
5
+ readonly _state?: State | undefined;
6
+ readonly dialect?: DialectTarget;
7
+ execute(query: CompiledQuery, opts?: ExecuteOptions): Promise<readonly Record<string, unknown>[]>;
8
+ stream?(query: CompiledQuery, opts?: ExecuteOptions): AsyncIterable<Record<string, unknown>>;
9
+ savepoint<R>(fn: (tx: TransactionContext<State>) => Promise<R>): Promise<R>;
10
+ }
11
+ export type ActiveTransactionContext = TransactionContext<'active'>;
12
+ export type ClosedTransactionContext = TransactionContext<'closed'>;
13
+ export declare function markTransactionClosed<State extends string = 'active'>(tx: TransactionContext<State>): ClosedTransactionContext;
14
+ export interface TxConnection {
15
+ readonly dialect?: DialectTarget;
16
+ raw(sql: string): Promise<void>;
17
+ execute(query: CompiledQuery, opts?: ExecuteOptions): Promise<readonly Record<string, unknown>[]>;
18
+ stream?(query: CompiledQuery, opts?: ExecuteOptions): AsyncIterable<Record<string, unknown>>;
19
+ }
20
+ export interface TransactionalDb {
21
+ transaction<R, State extends string = 'active'>(fn: (tx: TransactionContext<State>) => Promise<R>, options?: TransactionOptions): Promise<R>;
22
+ }
23
+ export interface TransactionRetryPolicy {
24
+ /** Retries after the first failed attempt. The transaction body may run `maxRetries + 1` times. */
25
+ readonly maxRetries: number;
26
+ /** Exponential-backoff starting delay. Defaults to 10 ms. */
27
+ readonly baseDelayMs?: number;
28
+ /** Backoff ceiling. Defaults to 1,000 ms. */
29
+ readonly maxDelayMs?: number;
30
+ }
31
+ export interface TransactionOptions {
32
+ /**
33
+ * Explicit opt-in only: retrying re-runs the callback, including any side effects
34
+ * outside the database. Keep those effects out of a retrying transaction body.
35
+ */
36
+ readonly retry?: TransactionRetryPolicy;
37
+ }
38
+ export declare function createTransactionalDb(conn: TxConnection): TransactionalDb;
39
+ export declare function batch<R, State extends string = 'active'>(db: TransactionalDb, ops: readonly ((tx: TransactionContext<State>) => Promise<R>)[]): Promise<R[]>;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transactions/index.ts"],"names":[],"mappings":"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"}