@jarenjs/db 0.49.2 → 0.66.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 (110) hide show
  1. package/ARCHITECTURE.md +420 -71
  2. package/README.md +711 -79
  3. package/docs/HOSTS.md +269 -0
  4. package/docs/JOBS-FORMAT.md +309 -45
  5. package/docs/LIVE-FORMAT.md +156 -19
  6. package/docs/MIGRATION-FORMAT.md +247 -40
  7. package/docs/MODEL-FORMAT.md +968 -86
  8. package/package.json +21 -8
  9. package/schemas/jaren-migration.draft-07.schema.json +73 -0
  10. package/schemas/jaren-migration.schema.json +73 -0
  11. package/schemas/jaren-model.draft-07.schema.json +224 -162
  12. package/schemas/jaren-model.schema.json +224 -162
  13. package/src/algebra.js +227 -9
  14. package/src/backup.js +161 -0
  15. package/src/cancellation.js +48 -0
  16. package/src/capture.js +255 -44
  17. package/src/cli.js +337 -50
  18. package/src/cursor.js +411 -0
  19. package/src/dag-job.js +154 -21
  20. package/src/ddl.js +125 -11
  21. package/src/dialect.js +267 -112
  22. package/src/dialects/expression-read.js +158 -0
  23. package/src/dialects/postgres.js +618 -0
  24. package/src/dialects/rtree-ddl.js +129 -0
  25. package/src/dialects/sqlite.js +245 -12
  26. package/src/document-files.js +311 -0
  27. package/src/document-steps.js +422 -0
  28. package/src/documents.js +335 -0
  29. package/src/driver.js +503 -69
  30. package/src/drivers/bun.js +37 -1
  31. package/src/drivers/indexeddb-snapshot.js +149 -0
  32. package/src/drivers/node-pool.js +11 -0
  33. package/src/drivers/node-worker-endpoint.js +105 -0
  34. package/src/drivers/node-worker.js +204 -0
  35. package/src/drivers/node.js +41 -7
  36. package/src/drivers/postgres.js +331 -0
  37. package/src/drivers/wasm-oo1.js +97 -0
  38. package/src/drivers/wasm-session.js +67 -0
  39. package/src/drivers/wasm.js +18 -83
  40. package/src/drivers/worker-pool.js +183 -0
  41. package/src/drivers/worker-protocol.js +79 -0
  42. package/src/drivers/worker-queue.js +60 -0
  43. package/src/emit-model.js +14 -0
  44. package/src/emit.js +349 -51
  45. package/src/entity.js +102 -59
  46. package/src/errors.js +430 -2
  47. package/src/expression.js +284 -0
  48. package/src/graph.js +64 -8
  49. package/src/index.js +48 -19
  50. package/src/introspect.js +583 -0
  51. package/src/jobs.js +870 -99
  52. package/src/json-bytes.js +58 -0
  53. package/src/live-time.js +12 -3
  54. package/src/live.js +11 -1
  55. package/src/maintenance.js +175 -0
  56. package/src/migrate.js +606 -333
  57. package/src/model.js +241 -8
  58. package/src/plan.js +1238 -160
  59. package/src/pragmas.js +314 -0
  60. package/src/profile.js +151 -3
  61. package/src/query.js +1748 -312
  62. package/src/residual.js +17 -0
  63. package/src/series.js +12 -4
  64. package/src/store.js +1672 -276
  65. package/src/tracker.js +367 -68
  66. package/src/udf.js +88 -7
  67. package/types/index.d.ts +1246 -32
  68. package/types/node-pool.d.ts +28 -0
  69. package/types/node-worker.d.ts +54 -0
  70. package/types/node.d.ts +72 -3
  71. package/types/postgres.d.ts +46 -0
  72. package/types/typed.d.ts +81 -3
  73. package/types/wasm.d.ts +21 -0
  74. package/dist/types/algebra.d.ts +0 -230
  75. package/dist/types/app.d.ts +0 -49
  76. package/dist/types/capture.d.ts +0 -85
  77. package/dist/types/cli.d.ts +0 -2
  78. package/dist/types/dag-job.d.ts +0 -40
  79. package/dist/types/ddl.d.ts +0 -229
  80. package/dist/types/derive.d.ts +0 -250
  81. package/dist/types/dialect.d.ts +0 -154
  82. package/dist/types/dialects/sqlite.d.ts +0 -9
  83. package/dist/types/driver.d.ts +0 -110
  84. package/dist/types/drivers/bun.d.ts +0 -47
  85. package/dist/types/drivers/node.d.ts +0 -37
  86. package/dist/types/drivers/wasm.d.ts +0 -65
  87. package/dist/types/emit-model.d.ts +0 -44
  88. package/dist/types/emit.d.ts +0 -75
  89. package/dist/types/entity.d.ts +0 -23
  90. package/dist/types/errors.d.ts +0 -170
  91. package/dist/types/graph.d.ts +0 -28
  92. package/dist/types/index.d.ts +0 -37
  93. package/dist/types/jobs.d.ts +0 -140
  94. package/dist/types/knn.d.ts +0 -69
  95. package/dist/types/live-time.d.ts +0 -141
  96. package/dist/types/live.d.ts +0 -64
  97. package/dist/types/migrate.d.ts +0 -170
  98. package/dist/types/model.d.ts +0 -36
  99. package/dist/types/patch-sql.d.ts +0 -37
  100. package/dist/types/plan.d.ts +0 -142
  101. package/dist/types/profile.d.ts +0 -80
  102. package/dist/types/query.d.ts +0 -112
  103. package/dist/types/residual.d.ts +0 -64
  104. package/dist/types/series.d.ts +0 -227
  105. package/dist/types/store.d.ts +0 -60
  106. package/dist/types/tracker.d.ts +0 -43
  107. package/dist/types/typed.d.ts +0 -15
  108. package/dist/types/types.d.ts +0 -26
  109. package/dist/types/udf.d.ts +0 -75
  110. package/dist/types/window.d.ts +0 -52
@@ -0,0 +1,284 @@
1
+ //@ts-check
2
+ /**
3
+ * @file Model-declared index expressions: a CLOSED abstract syntax tree
4
+ * over declared members, JSON scalars and registered deterministic
5
+ * functions — and never SQL text.
6
+ *
7
+ * An index over a function is a schema dependency: a database whose
8
+ * column is computed by `lower(…)` cannot be written from a connection
9
+ * that has no `lower`. That hazard is the reason this vocabulary is a
10
+ * DECLARATION rather than an escape hatch — the model names the
11
+ * function, every store that opens the model is handed the same
12
+ * declaration, and a store that cannot honour one refuses at open with
13
+ * `JD0004` before a single statement runs. A raw-SQL index would have
14
+ * had the same hazard with none of the checking.
15
+ *
16
+ * Three node kinds, and no fourth:
17
+ *
18
+ * ```jsonc
19
+ * { "member": "$.email" } // a singular member path
20
+ * { "value": "x" } // a JSON scalar
21
+ * { "call": "lower", "args": [ <node>, … ] } // a declared function
22
+ * ```
23
+ *
24
+ * A DECLARATION is what the host supplies per name:
25
+ *
26
+ * ```jsonc
27
+ * { "arity": 1, "deterministic": true,
28
+ * "apply": (value) => …, // for an engine that registers functions
29
+ * "sql": "lower" } // for one that cannot, and whose function is IMMUTABLE
30
+ * ```
31
+ *
32
+ * Neither half is optional in general: an engine that registers
33
+ * functions needs `apply`, one that does not needs `sql`, and a
34
+ * declaration missing the half its engine needs is `JD0004` at open —
35
+ * not a column that silently computes something else. Nothing here
36
+ * creates a function on the server; `sql` is a promise the host makes
37
+ * about one that already exists.
38
+ */
39
+
40
+ import { DbCompileError } from './errors.js';
41
+
42
+ /** The three node kinds, closed. */
43
+ export const EXPRESSION_KINDS = Object.freeze(['member', 'value', 'call']);
44
+
45
+ /** How deep a declared expression may nest. A bound, not a taste: the
46
+ * canonical form is hashed, compared and emitted, and an unbounded one
47
+ * would let a model document cost a store its stack at open. */
48
+ export const EXPRESSION_DEPTH = 8;
49
+
50
+ /**
51
+ * @param {string} reason
52
+ * @param {string} docPath
53
+ * @returns {DbCompileError}
54
+ */
55
+ function refuse(reason, docPath) {
56
+ return new DbCompileError('JD0004', reason, docPath);
57
+ }
58
+
59
+ /**
60
+ * Normalize one expression node, refusing everything the vocabulary
61
+ * does not name. The result is frozen and canonical: a member's path is
62
+ * the string it was declared as, a value is the scalar itself, and a
63
+ * call carries its resolved arity.
64
+ * @param {any} node
65
+ * @param {string} docPath
66
+ * @param {Record<string, any>} declarations - the host's function
67
+ * declarations, by name
68
+ * @param {number} [depth]
69
+ * @returns {any}
70
+ */
71
+ export function normalizeExpression(node, docPath, declarations, depth = 0) {
72
+ if (depth > EXPRESSION_DEPTH) {
73
+ throw refuse(`an index expression nests deeper than ${EXPRESSION_DEPTH}`, docPath);
74
+ }
75
+ if (node === null || typeof node !== 'object' || Array.isArray(node))
76
+ throw refuse('an index expression node must be an object', docPath);
77
+ const kinds = EXPRESSION_KINDS.filter((kind) => Object.hasOwn(node, kind));
78
+ if (kinds.length !== 1) {
79
+ throw refuse(
80
+ `an index expression node is exactly one of ${EXPRESSION_KINDS.map((k) => `'${k}'`).join(', ')}`
81
+ + `${kinds.length === 0 ? '' : `, and this one names ${kinds.length}`}`, docPath);
82
+ }
83
+ const [kind] = kinds;
84
+
85
+ if (kind === 'member') {
86
+ if (typeof node.member !== 'string' || node.member.length === 0)
87
+ throw refuse('a member node takes a JSONPath string', `${docPath}/member`);
88
+ for (const extra of Object.keys(node)) {
89
+ if (extra !== 'member')
90
+ throw refuse(`a member node takes no '${extra}'`, `${docPath}/${extra}`);
91
+ }
92
+ return Object.freeze({ member: node.member });
93
+ }
94
+
95
+ if (kind === 'value') {
96
+ const value = node.value;
97
+ if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
98
+ throw refuse('a value node takes a JSON string, number or boolean — a null or a '
99
+ + 'compound has no place in an index expression', `${docPath}/value`);
100
+ }
101
+ if (typeof value === 'number' && !Number.isFinite(value))
102
+ throw refuse('a value node takes a finite number', `${docPath}/value`);
103
+ for (const extra of Object.keys(node)) {
104
+ if (extra !== 'value')
105
+ throw refuse(`a value node takes no '${extra}'`, `${docPath}/${extra}`);
106
+ }
107
+ return Object.freeze({ value });
108
+ }
109
+
110
+ if (typeof node.call !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(node.call))
111
+ throw refuse('a call node names a function by identifier', `${docPath}/call`);
112
+ for (const extra of Object.keys(node)) {
113
+ if (extra !== 'call' && extra !== 'args')
114
+ throw refuse(`a call node takes no '${extra}'`, `${docPath}/${extra}`);
115
+ }
116
+ const args = node.args ?? [];
117
+ if (!Array.isArray(args))
118
+ throw refuse('a call node\'s args is an array', `${docPath}/args`);
119
+
120
+ const declared = declarations?.[node.call];
121
+ if (declared === undefined) {
122
+ const known = Object.keys(declarations ?? {}).sort();
123
+ throw refuse(`the index expression names the function '${node.call}', which this store was `
124
+ + `not given: the declared functions are ${known.length === 0
125
+ ? 'none' : known.map((name) => `'${name}'`).join(', ')} — pass them as `
126
+ + 'openStore({ expressions })', `${docPath}/call`);
127
+ }
128
+ if (declared.deterministic !== true) {
129
+ throw refuse(`the function '${node.call}' is not declared deterministic, and an index over `
130
+ + 'a function that may answer differently for one row is an index that lies',
131
+ `${docPath}/call`);
132
+ }
133
+ if (!Number.isInteger(declared.arity) || declared.arity < 0)
134
+ throw refuse(`the function '${node.call}' declares no whole arity`, `${docPath}/call`);
135
+ if (args.length !== declared.arity) {
136
+ throw refuse(`the function '${node.call}' takes ${declared.arity} argument(s) and the `
137
+ + `expression gives it ${args.length}`, `${docPath}/args`);
138
+ }
139
+ return Object.freeze({
140
+ call: node.call,
141
+ args: Object.freeze(args.map((argument, i) =>
142
+ normalizeExpression(argument, `${docPath}/args/${i}`, declarations, depth + 1))),
143
+ });
144
+ }
145
+
146
+ /**
147
+ * The canonical text of one normalized expression: the identity two
148
+ * declarations of the same expression share, and the identity the shape
149
+ * hash and the migration diff read.
150
+ *
151
+ * ORDER-PRESERVING, deliberately: `sub(a, b)` and `sub(b, a)` are
152
+ * different expressions and must be different columns, so nothing here
153
+ * sorts.
154
+ * @param {any} node
155
+ * @returns {string}
156
+ */
157
+ export function canonicalExpression(node) {
158
+ if (Object.hasOwn(node, 'member')) return `m(${JSON.stringify(node.member)})`;
159
+ if (Object.hasOwn(node, 'value')) return `v(${JSON.stringify(node.value)})`;
160
+ return `${node.call}(${node.args.map(canonicalExpression).join(',')})`;
161
+ }
162
+
163
+ /**
164
+ * Every member path one expression reads, in the order it reads them.
165
+ * @param {any} node
166
+ * @param {string[]} [out]
167
+ * @returns {string[]}
168
+ */
169
+ export function expressionMembers(node, out = []) {
170
+ if (Object.hasOwn(node, 'member')) out.push(node.member);
171
+ else if (Object.hasOwn(node, 'call')) for (const argument of node.args)
172
+ expressionMembers(argument, out);
173
+ return out;
174
+ }
175
+
176
+ /**
177
+ * Every function one expression calls, sorted and deduplicated — what a
178
+ * store registers before it can so much as SELECT from the table.
179
+ * @param {any} node
180
+ * @param {Set<string>} [out]
181
+ * @returns {string[]}
182
+ */
183
+ export function expressionFunctions(node, out = new Set()) {
184
+ if (Object.hasOwn(node, 'call')) {
185
+ out.add(node.call);
186
+ for (const argument of node.args) expressionFunctions(argument, out);
187
+ }
188
+ return [...out].sort();
189
+ }
190
+
191
+ /**
192
+ * A short, stable, readable column stem for one expression: the
193
+ * outermost call and the members it reads. Distinct expressions that
194
+ * sanitize to one stem are separated by the DDL planner's own
195
+ * collision suffixing; the canonical text is what decides identity.
196
+ * @param {any} node
197
+ * @returns {string}
198
+ */
199
+ export function expressionStem(node) {
200
+ const call = Object.hasOwn(node, 'call') ? node.call : 'x';
201
+ const members = expressionMembers(node)
202
+ .map((path) => path.replace(/^\$\.?/, '').replace(/[^A-Za-z0-9]+/g, '_'))
203
+ .filter((part) => part.length > 0);
204
+ return [call, ...members].join('_').replace(/^_+|_+$/g, '');
205
+ }
206
+
207
+ /**
208
+ * The SQL an expression compiles to on one dialect, and the check that
209
+ * it can be compiled at all.
210
+ *
211
+ * The two engines take different halves of a declaration: one registers
212
+ * the function and calls it by a generated name, the other calls the
213
+ * IMMUTABLE function the host promised already exists. A declaration
214
+ * missing the half its engine needs is `JD0004` HERE — at planning,
215
+ * before any DDL — which is the whole point of resolving it at open.
216
+ * @param {any} node - a normalized expression
217
+ * @param {any} dialect
218
+ * @param {{ docColumnSql: string, declarations: Record<string, any>,
219
+ * registered: boolean, docPath: string }} context - `registered` is
220
+ * whether this connection can register a deterministic function
221
+ * @returns {string}
222
+ */
223
+ export function expressionSql(node, dialect, context) {
224
+ if (Object.hasOwn(node, 'value')) {
225
+ return typeof node.value === 'string'
226
+ ? dialect.stringLiteral(node.value)
227
+ : typeof node.value === 'boolean'
228
+ ? dialect.booleanLiteral(node.value)
229
+ : String(node.value);
230
+ }
231
+ if (Object.hasOwn(node, 'member')) {
232
+ const pathText = dialect.jsonPathText(context.segmentsOf(node.member));
233
+ if (pathText === null) {
234
+ throw refuse(`the index expression reads '${node.member}', a member name the dialect's `
235
+ + 'JSON path grammar cannot carry', context.docPath);
236
+ }
237
+ // the member's own SCALAR, not its JSON text: a declared function is
238
+ // a function of the VALUE, and `lower` of a string must be the same
239
+ // answer on both engines rather than one of them quoting it first
240
+ return dialect.jsonExtract(context.docColumnSql, pathText, 'scalar');
241
+ }
242
+ const declared = context.declarations[node.call];
243
+ const args = node.args.map((argument) => expressionSql(argument, dialect, context));
244
+ if (context.registered) {
245
+ if (typeof declared.apply !== 'function') {
246
+ throw refuse(`the function '${node.call}' has no 'apply': this connection computes an `
247
+ + 'index expression by registering a deterministic function, and there is nothing '
248
+ + 'to register', context.docPath);
249
+ }
250
+ return `${registeredName(node.call)}(${args.join(', ')})`;
251
+ }
252
+ if (typeof declared.sql !== 'string' || !/^[A-Za-z_][A-Za-z0-9_.]*$/.test(declared.sql)) {
253
+ throw refuse(`the function '${node.call}' has no 'sql' name: this connection cannot register `
254
+ + 'a function, so an index over one needs the name of an IMMUTABLE function the server '
255
+ + 'already has — and it is an identifier, never SQL text', context.docPath);
256
+ }
257
+ return `${declared.sql}(${args.join(', ')})`;
258
+ }
259
+
260
+ /**
261
+ * The SQL name a declared function is registered under. Namespaced, so
262
+ * a model's `lower` never shadows the engine's own.
263
+ * @param {string} name
264
+ * @returns {string}
265
+ */
266
+ export function registeredName(name) {
267
+ return `jaren_x_${name}`;
268
+ }
269
+
270
+ /**
271
+ * Register every function a set of expressions calls on one connection.
272
+ * @param {any} connection
273
+ * @param {readonly string[]} names
274
+ * @param {Record<string, any>} declarations
275
+ * @returns {any} value-or-promise
276
+ */
277
+ export function registerExpressionFunctions(connection, names, declarations) {
278
+ if (names.length === 0 || connection.registerFunction === null) return null;
279
+ for (const name of names) {
280
+ connection.registerFunction(registeredName(name),
281
+ { deterministic: true, varargs: false }, declarations[name].apply);
282
+ }
283
+ return null;
284
+ }
package/src/graph.js CHANGED
@@ -6,9 +6,64 @@
6
6
  * derived epoch columns are skipped because the string never left the
7
7
  * document), foreign-key columns fold in the same way, and — for the
8
8
  * one-statement graph loads — projected relation JSON parses
9
- * recursively into child arrays or single children.
9
+ * recursively into child arrays or single children — and, per root,
10
+ * holds every included relation to its declared bound (MODEL-FORMAT
11
+ * §10.4): more rows than `maxRows` or more serialised bytes than
12
+ * `maxBytes` is the coded refusal `JD2073` naming the root, the member
13
+ * and the bound, never a truncated graph the caller cannot tell from a
14
+ * whole one.
10
15
  */
11
16
 
17
+ import { DbRuntimeError } from './errors.js';
18
+ import { utf8Length } from './cursor.js';
19
+ import { jsonBytes, decodeCountedJson } from './json-bytes.js';
20
+
21
+ /**
22
+ * Refuse an include that crossed its per-root bound.
23
+ * @param {any} node - the parent node (`entity`, `entityMapping`)
24
+ * @param {any} include - the include entry (`name`, `maxRows`, `maxBytes`)
25
+ * @param {any} keyed - the parent row or child object the key is read from
26
+ * @param {'maxRows' | 'maxBytes'} bound
27
+ * @param {number} measured
28
+ * @returns {DbRuntimeError}
29
+ */
30
+ function boundRefusal(node, include, keyed, bound, measured) {
31
+ const limit = include[bound];
32
+ const key = node.entityMapping.keys.map((name) => JSON.stringify(keyed[name])).join(', ');
33
+ const entity = node.entity?.name ?? node.entityMapping.table;
34
+ return new DbRuntimeError('JD2073',
35
+ `the include '${include.name}' of ${entity} ${key} holds ${bound === 'maxRows'
36
+ ? `more than ${limit} rows` : `${measured} serialised bytes, more than ${limit}`} — its `
37
+ + `${bound} bound for one root. Declare ${bound}: Infinity on the include to load it whole `
38
+ + `by decision, read { count: true } for the size, or page the relation separately`,
39
+ { collection: entity, key: node.entityMapping.keys.length === 1 ? keyed[node.entityMapping.keys[0]] : undefined });
40
+ }
41
+
42
+ /**
43
+ * Hold one include of one parent to its bounds. Bytes are measured on
44
+ * the serialised text the database projected (a nested include arrives
45
+ * already embedded as JSON and carries its decoder-computed size); rows on
46
+ * the parsed array.
47
+ * @param {any} node
48
+ * @param {any} include
49
+ * @param {any} keyed
50
+ * @param {any} raw - the projected value: text at the root, JSON below
51
+ * @param {WeakMap<object, number>} sizes
52
+ * @returns {any} the bounded decoded value
53
+ */
54
+ function checkBounds(node, include, keyed, raw, sizes) {
55
+ if (include.maxBytes !== null && include.maxBytes !== undefined) {
56
+ const bytes = typeof raw === 'string' ? utf8Length(raw) : jsonBytes(raw, sizes);
57
+ if (bytes > include.maxBytes) throw boundRefusal(node, include, keyed, 'maxBytes', bytes);
58
+ }
59
+ const parsed = typeof raw === 'string' ? decodeCountedJson(raw, sizes) : raw;
60
+ if (include.many && include.maxRows !== null && include.maxRows !== undefined
61
+ && parsed.length > include.maxRows) {
62
+ throw boundRefusal(node, include, keyed, 'maxRows', parsed.length);
63
+ }
64
+ return parsed;
65
+ }
66
+
12
67
  /**
13
68
  * Merge one database row back into its entity document.
14
69
  * @param {any} entityMapping - `explainMapping(...).entities[name]`
@@ -42,6 +97,7 @@ export function mergeEntityRow(entityMapping, row, docField = 'doc') {
42
97
  * @returns {any}
43
98
  */
44
99
  export function parseGraphRow(node, row, docField = '__doc') {
100
+ const sizes = new WeakMap();
45
101
  const doc = mergeEntityRow(node.entityMapping, row, docField);
46
102
  for (const include of node.includes) {
47
103
  const raw = row[include.field];
@@ -53,10 +109,10 @@ export function parseGraphRow(node, row, docField = '__doc') {
53
109
  doc[include.name] = include.many ? [] : null;
54
110
  continue;
55
111
  }
56
- const parsed = JSON.parse(raw);
112
+ const parsed = checkBounds(node, include, row, raw, sizes);
57
113
  doc[include.name] = include.many
58
- ? parsed.map((child) => parseGraphChild(include.child, child))
59
- : parseGraphChild(include.child, parsed);
114
+ ? parsed.map((child) => parseGraphChild(include.child, child, sizes))
115
+ : parseGraphChild(include.child, parsed, sizes);
60
116
  }
61
117
  return doc;
62
118
  }
@@ -70,7 +126,7 @@ export function parseGraphRow(node, row, docField = '__doc') {
70
126
  * @param {any} child
71
127
  * @returns {any}
72
128
  */
73
- function parseGraphChild(node, child) {
129
+ function parseGraphChild(node, child, sizes) {
74
130
  const doc = typeof child.__doc === 'string' ? JSON.parse(child.__doc) : child.__doc;
75
131
  for (const column of node.entityMapping.columns) {
76
132
  if (column.source === 'epoch(document)') continue;
@@ -92,10 +148,10 @@ function parseGraphChild(node, child) {
92
148
  doc[include.name] = include.many ? [] : null;
93
149
  continue;
94
150
  }
95
- const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
151
+ const parsed = checkBounds(node, include, child, raw, sizes);
96
152
  doc[include.name] = include.many
97
- ? parsed.map((grandchild) => parseGraphChild(include.child, grandchild))
98
- : parseGraphChild(include.child, parsed);
153
+ ? parsed.map((grandchild) => parseGraphChild(include.child, grandchild, sizes))
154
+ : parseGraphChild(include.child, parsed, sizes);
99
155
  }
100
156
  return doc;
101
157
  }
package/src/index.js CHANGED
@@ -1,34 +1,55 @@
1
1
  //@ts-check
2
2
  /**
3
- * @file @jarenjs/db — document storage over SQLite through two seams:
4
- * a driver (how a connection is made: `@jarenjs/db/node`, `/bun`, or
5
- * `/wasm` with an injected handle) and a dialect (how SQL is spelled).
6
- * This root subpath never touches a runtime builtina browser
7
- * bundler resolves it clean; the bindings live behind their own
8
- * subpaths and load their builtin lazily inside `open()`.
3
+ * @file @jarenjs/db — document storage through two seams: a driver (how
4
+ * a connection is made: `@jarenjs/db/node`, `/bun`, `/wasm` with an
5
+ * injected handle, or `/postgres` with an injected client) and a
6
+ * dialect (how SQL is spelled). Two engines satisfy both SQLite and
7
+ * PostgreSQL 16+ and what differs between them is a declared
8
+ * capability rather than a discovered surprise.
9
+ *
10
+ * This root subpath never touches a runtime builtin and imports no
11
+ * third-party client — a browser bundler resolves it clean; the
12
+ * bindings live behind their own subpaths and load their builtin
13
+ * lazily inside `open()`.
9
14
  */
10
15
 
11
16
  export { openStore, normalizeModel, MODEL_VERSION } from './store.js';
12
- export { createDialect } from './dialect.js';
17
+ export { createDialect, DIALECT_CAPABILITIES } from './dialect.js';
18
+ export { rtreeDdl } from './dialects/rtree-ddl.js';
13
19
  export { sqliteDialect } from './dialects/sqlite.js';
20
+ export { PRAGMA_NAMES } from './pragmas.js';
21
+ export { CHECKPOINT_MODES, MAINTENANCE_OPERATIONS } from './maintenance.js';
14
22
  export {
15
23
  SQLITE_FLOOR, chain, toPromise, isThenable, compareVersions,
16
- openConnection, wrapStatement, lazyOpen,
24
+ openConnection, finishConnection, sqliteProbe, baseCapabilities,
25
+ wrapStatement, lazyOpen,
17
26
  } from './driver.js';
18
27
  export {
19
- planCollection, compileIndexPath, schemaTypeAt, KEY_COLUMN, DOC_COLUMN,
28
+ planCollection, compileIndexPath, schemaTypeAt, columnKindFor, KEY_COLUMN, DOC_COLUMN,
20
29
  normalizeDeclaredSql, comparableDeclaredSql,
21
30
  } from './ddl.js';
22
31
  export {
23
32
  planQuery, assertDecidedKind, entityShape, entityPathRef,
24
- planEntityPredicate, planEntityQuery,
33
+ planEntityPredicate, planEntityQuery, collectEntityRoots, entityRoot,
34
+ PLANNER_REASONS, reasonId,
25
35
  } from './plan.js';
26
36
  export { emitPlan, createEntityPredicateEmitters, emitEntityPlan } from './emit.js';
27
37
  export { mergeEntityRow, parseGraphRow } from './graph.js';
28
- export { selectPlan, conjoin, assertNoSqlText, PLAN_VERSION } from './algebra.js';
38
+ export {
39
+ selectPlan, conjoin, assertNoSqlText, effectiveOrder, planOrder, ordersByColumn,
40
+ PLAN_VERSION,
41
+ } from './algebra.js';
29
42
  export { typeOfPath, isNumericType } from './types.js';
30
- export { compileSetResidual, compileRowResidual, sequenceResult } from './residual.js';
43
+ export {
44
+ compileSetResidual, compileRowResidual, compilePackedResidual, sequenceResult,
45
+ } from './residual.js';
46
+ export { createCursor, PAGE_LIMIT_DEFAULT } from './cursor.js';
31
47
  export { deterministicFragment, registerFragment } from './udf.js';
48
+ export {
49
+ normalizeExpression, canonicalExpression, expressionMembers, expressionFunctions,
50
+ expressionSql, expressionStem, registeredName, registerExpressionFunctions,
51
+ EXPRESSION_KINDS, EXPRESSION_DEPTH,
52
+ } from './expression.js';
32
53
  export {
33
54
  DERIVE_KINDS, DERIVE_MAPPING, PHYSICAL_KINDS, BBOX_COMPONENTS, BBOX_INDEX_ORDER,
34
55
  PRECISION_MIN, PRECISION_MAX, DIMS_MIN, DIMS_MAX,
@@ -38,34 +59,42 @@ export {
38
59
  export { KNN_MARGIN, IDENTITY_CHUNK, cutCandidates, identityBatches } from './knn.js';
39
60
  export {
40
61
  createQueryEngine, createQueryState, createEntityQueryEngine,
41
- createLoadEngine, INCLUDE_DEPTH_DEFAULT,
62
+ createLoadEngine, INCLUDE_DEPTH_DEFAULT, INCLUDE_ROWS_DEFAULT, INCLUDE_BYTES_DEFAULT,
42
63
  } from './query.js';
43
64
  export {
44
65
  normalizeProfile, SAFE_PROFILE, translateProfilePredicate,
45
66
  applyMandatoryPredicate, applyRowBound,
46
67
  } from './profile.js';
47
68
  export { translatePatch } from './patch-sql.js';
48
- export { normalizeEntities, explainMapping } from './model.js';
69
+ export { normalizeEntities, explainMapping, relationTables } from './model.js';
49
70
  export { planEntity, planJoinTable } from './ddl.js';
50
71
  export { entityCore } from './entity.js';
51
72
  export { entityEmitModel } from './emit-model.js';
52
73
  export {
53
74
  parseChangeset, translateOperations, keyToken, createCaptureEngine,
54
- CHANGES_TABLE, DEFAULT_RETENTION,
75
+ CHANGES_TABLE, CHANGES_STATE_TABLE, DEFAULT_RETENTION,
55
76
  } from './capture.js';
56
77
  export {
57
78
  createTracker, deepFreeze, BATCH_PARAM_BUDGET, BATCH_ROW_BOUND,
58
79
  } from './tracker.js';
59
80
  export {
60
- planMigration, planModelMigration, migrate, migrationStatus, shapeHash,
81
+ planMigration, planModelMigration, migrate, migrationStatus, shapeHash, isPerDocumentAssertion,
61
82
  migrationChecksum, createModelShape, schemaShapeOf, compareShapeToModel,
62
- MIGRATION_VERSION, HISTORY_TABLE,
83
+ MIGRATION_VERSION, HISTORY_TABLE, ASSERTION_BOUNDS_DEFAULT,
63
84
  } from './migrate.js';
64
- export { DbCompileError, DbRuntimeError, DB_CODES } from './errors.js';
85
+ export { introspectModel, readSchema, INTROSPECT_CODES } from './introspect.js';
86
+ export { migrateDocuments, streamDocuments } from './documents.js';
87
+ export {
88
+ compileDocumentStep, checkMigrationDocument, stepFailure, classifyAssertion,
89
+ DOCUMENT_STEP_KINDS, PHYSICAL_STEP_KINDS,
90
+ } from './document-steps.js';
91
+ export {
92
+ DbCompileError, DbRuntimeError, DB_CODES, classifyDriverError, wrapDriverError, isDriverError,
93
+ } from './errors.js';
65
94
  export { classifyLiveQuery, createLiveRegistry, diffRows, LIVE_DEFAULTS } from './live.js';
66
95
  export { createSortedWindow, compareCodepoint } from './window.js';
67
96
  export {
68
97
  createJobEngine, JOBS_TABLE, JOB_CHECKPOINTS_TABLE, JOB_DEFAULTS,
69
98
  describeValue, serializeResult,
70
99
  } from './jobs.js';
71
- export { createDagJobRunner } from './dag-job.js';
100
+ export { createDagJobRunner, RUN_IDENTITY_NODE } from './dag-job.js';