@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.
- package/ARCHITECTURE.md +420 -71
- package/README.md +711 -79
- package/docs/HOSTS.md +269 -0
- package/docs/JOBS-FORMAT.md +309 -45
- package/docs/LIVE-FORMAT.md +156 -19
- package/docs/MIGRATION-FORMAT.md +247 -40
- package/docs/MODEL-FORMAT.md +968 -86
- package/package.json +21 -8
- package/schemas/jaren-migration.draft-07.schema.json +73 -0
- package/schemas/jaren-migration.schema.json +73 -0
- package/schemas/jaren-model.draft-07.schema.json +224 -162
- package/schemas/jaren-model.schema.json +224 -162
- package/src/algebra.js +227 -9
- package/src/backup.js +161 -0
- package/src/cancellation.js +48 -0
- package/src/capture.js +255 -44
- package/src/cli.js +337 -50
- package/src/cursor.js +411 -0
- package/src/dag-job.js +154 -21
- package/src/ddl.js +125 -11
- package/src/dialect.js +267 -112
- package/src/dialects/expression-read.js +158 -0
- package/src/dialects/postgres.js +618 -0
- package/src/dialects/rtree-ddl.js +129 -0
- package/src/dialects/sqlite.js +245 -12
- package/src/document-files.js +311 -0
- package/src/document-steps.js +422 -0
- package/src/documents.js +335 -0
- package/src/driver.js +503 -69
- package/src/drivers/bun.js +37 -1
- package/src/drivers/indexeddb-snapshot.js +149 -0
- package/src/drivers/node-pool.js +11 -0
- package/src/drivers/node-worker-endpoint.js +105 -0
- package/src/drivers/node-worker.js +204 -0
- package/src/drivers/node.js +41 -7
- package/src/drivers/postgres.js +331 -0
- package/src/drivers/wasm-oo1.js +97 -0
- package/src/drivers/wasm-session.js +67 -0
- package/src/drivers/wasm.js +18 -83
- package/src/drivers/worker-pool.js +183 -0
- package/src/drivers/worker-protocol.js +79 -0
- package/src/drivers/worker-queue.js +60 -0
- package/src/emit-model.js +14 -0
- package/src/emit.js +349 -51
- package/src/entity.js +102 -59
- package/src/errors.js +430 -2
- package/src/expression.js +284 -0
- package/src/graph.js +64 -8
- package/src/index.js +48 -19
- package/src/introspect.js +583 -0
- package/src/jobs.js +870 -99
- package/src/json-bytes.js +58 -0
- package/src/live-time.js +12 -3
- package/src/live.js +11 -1
- package/src/maintenance.js +175 -0
- package/src/migrate.js +606 -333
- package/src/model.js +241 -8
- package/src/plan.js +1238 -160
- package/src/pragmas.js +314 -0
- package/src/profile.js +151 -3
- package/src/query.js +1748 -312
- package/src/residual.js +17 -0
- package/src/series.js +12 -4
- package/src/store.js +1672 -276
- package/src/tracker.js +367 -68
- package/src/udf.js +88 -7
- package/types/index.d.ts +1246 -32
- package/types/node-pool.d.ts +28 -0
- package/types/node-worker.d.ts +54 -0
- package/types/node.d.ts +72 -3
- package/types/postgres.d.ts +46 -0
- package/types/typed.d.ts +81 -3
- package/types/wasm.d.ts +21 -0
- package/dist/types/algebra.d.ts +0 -230
- package/dist/types/app.d.ts +0 -49
- package/dist/types/capture.d.ts +0 -85
- package/dist/types/cli.d.ts +0 -2
- package/dist/types/dag-job.d.ts +0 -40
- package/dist/types/ddl.d.ts +0 -229
- package/dist/types/derive.d.ts +0 -250
- package/dist/types/dialect.d.ts +0 -154
- package/dist/types/dialects/sqlite.d.ts +0 -9
- package/dist/types/driver.d.ts +0 -110
- package/dist/types/drivers/bun.d.ts +0 -47
- package/dist/types/drivers/node.d.ts +0 -37
- package/dist/types/drivers/wasm.d.ts +0 -65
- package/dist/types/emit-model.d.ts +0 -44
- package/dist/types/emit.d.ts +0 -75
- package/dist/types/entity.d.ts +0 -23
- package/dist/types/errors.d.ts +0 -170
- package/dist/types/graph.d.ts +0 -28
- package/dist/types/index.d.ts +0 -37
- package/dist/types/jobs.d.ts +0 -140
- package/dist/types/knn.d.ts +0 -69
- package/dist/types/live-time.d.ts +0 -141
- package/dist/types/live.d.ts +0 -64
- package/dist/types/migrate.d.ts +0 -170
- package/dist/types/model.d.ts +0 -36
- package/dist/types/patch-sql.d.ts +0 -37
- package/dist/types/plan.d.ts +0 -142
- package/dist/types/profile.d.ts +0 -80
- package/dist/types/query.d.ts +0 -112
- package/dist/types/residual.d.ts +0 -64
- package/dist/types/series.d.ts +0 -227
- package/dist/types/store.d.ts +0 -60
- package/dist/types/tracker.d.ts +0 -43
- package/dist/types/typed.d.ts +0 -15
- package/dist/types/types.d.ts +0 -26
- package/dist/types/udf.d.ts +0 -75
- package/dist/types/window.d.ts +0 -52
package/src/udf.js
CHANGED
|
@@ -16,10 +16,16 @@
|
|
|
16
16
|
* fingerprint clash between DIFFERENT identities is disambiguated with
|
|
17
17
|
* a suffix rather than collapsed.
|
|
18
18
|
*
|
|
19
|
-
* WHERE-clause use only
|
|
20
|
-
*
|
|
21
|
-
* registered
|
|
22
|
-
*
|
|
19
|
+
* WHERE-clause use only, and that is the difference between this hatch
|
|
20
|
+
* and a DECLARED index expression (MODEL-FORMAT §7A). An index over a
|
|
21
|
+
* registered function makes the database unwritable from a connection
|
|
22
|
+
* that has not registered the identical function; a fragment registered
|
|
23
|
+
* here is a QUERY's, discovered from the caller's document at run time,
|
|
24
|
+
* and indexing one would make a passing query a permanent schema
|
|
25
|
+
* dependency nobody declared. A model's `indexes[].expression` carries
|
|
26
|
+
* exactly that dependency in the model, where every store that opens it
|
|
27
|
+
* is handed the same declaration and one that cannot honour it refuses
|
|
28
|
+
* at open.
|
|
23
29
|
*
|
|
24
30
|
* Ring 3 extends the hatch to registry `pushable:'scalar'`
|
|
25
31
|
* operators: a predicate fragment that uses a registered scalar operator
|
|
@@ -34,7 +40,7 @@
|
|
|
34
40
|
|
|
35
41
|
import { semanticKey } from '@jarenjs/core/object';
|
|
36
42
|
import { hashContent } from '@jarenjs/core/string';
|
|
37
|
-
import { compileJsonQuery, analyzeQuery } from '@jarenjs/json/query';
|
|
43
|
+
import { compileJsonQuery, analyzeQuery, JsonQueryRuntimeError } from '@jarenjs/json/query';
|
|
38
44
|
|
|
39
45
|
/**
|
|
40
46
|
* The SQL identifier for one fragment identity: a short fingerprint of
|
|
@@ -61,7 +67,14 @@ const functionNameFor = (identity) => `jaren_p_${hashContent(identity)}`;
|
|
|
61
67
|
* external, the determinism check below rejects the fragment, and the
|
|
62
68
|
* hatch silently never engages.
|
|
63
69
|
* @returns {{ key: string, name: string,
|
|
64
|
-
* compile: () => (docText: string) => number } | null}
|
|
70
|
+
* compile: () => (docText: string, mount?: string) => number } | null}
|
|
71
|
+
* - the compiled function takes the row's document text and the
|
|
72
|
+
* conjunct's JSON Pointer in the CALLER's document (`/$where`, or
|
|
73
|
+
* `/$where/$and/<i>`), which the emitter passes as a literal: an
|
|
74
|
+
* engine error raised inside names the wrapper's path (`/$return/…`)
|
|
75
|
+
* and is rebased onto that mount, so the native mode and the residual
|
|
76
|
+
* report the same location while one registration still serves every
|
|
77
|
+
* document that carries the fragment
|
|
65
78
|
*/
|
|
66
79
|
export function deterministicFragment(fragment, operators = null, binding = 'it') {
|
|
67
80
|
const analyzeOpts = operators === null
|
|
@@ -103,6 +116,7 @@ export function deterministicFragment(fragment, operators = null, binding = 'it'
|
|
|
103
116
|
// correct, so it does not qualify for the hatch
|
|
104
117
|
return null;
|
|
105
118
|
}
|
|
119
|
+
const WRAPPER = '/$return';
|
|
106
120
|
return {
|
|
107
121
|
key,
|
|
108
122
|
name: functionNameFor(key),
|
|
@@ -111,7 +125,25 @@ export function deterministicFragment(fragment, operators = null, binding = 'it'
|
|
|
111
125
|
// different name than the analysis would judge one document and
|
|
112
126
|
// run another
|
|
113
127
|
const compiled = compileJsonQuery(wrap(fragment), analyzeOpts);
|
|
114
|
-
|
|
128
|
+
// two declared parameters on purpose: node:sqlite registers the
|
|
129
|
+
// function with the arity `fn.length` reports, and the emitter
|
|
130
|
+
// always passes the mount beside the document
|
|
131
|
+
return (docText, mount) => {
|
|
132
|
+
try {
|
|
133
|
+
return compiled.ebv(JSON.parse(docText)) ? 1 : 0;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// the engine's own refusal, relocated from the wrapper onto
|
|
137
|
+
// the caller's document; anything else propagates as it is
|
|
138
|
+
if (error instanceof JsonQueryRuntimeError && typeof error.docPath === 'string'
|
|
139
|
+
&& error.docPath.startsWith(WRAPPER)) {
|
|
140
|
+
throw new JsonQueryRuntimeError(error.code, error.reason,
|
|
141
|
+
(typeof mount === 'string' ? mount : '/$where') + error.docPath.slice(WRAPPER.length),
|
|
142
|
+
Object.hasOwn(error, 'cause') ? { cause: error.cause } : undefined);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
115
147
|
},
|
|
116
148
|
};
|
|
117
149
|
}
|
|
@@ -139,3 +171,52 @@ export function registerFragment(connection, registered, fragment) {
|
|
|
139
171
|
registered.set(fragment.key, name);
|
|
140
172
|
return name;
|
|
141
173
|
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The SQL identifier for one registered aggregate. Unlike a predicate
|
|
177
|
+
* fragment, the identity IS the operator name — one registry, one
|
|
178
|
+
* function per name — so the fingerprint has nothing to disambiguate.
|
|
179
|
+
* @param {string} name
|
|
180
|
+
* @returns {string}
|
|
181
|
+
*/
|
|
182
|
+
const aggregateNameFor = (name) => `jaren_a_${hashContent(name)}`;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Register a pushable aggregate once per store and answer the SQL name
|
|
186
|
+
* to call. The SQL fold accumulates the column's values and hands them
|
|
187
|
+
* to the SAME pure function the residual would call, so the two sides
|
|
188
|
+
* differ in who drives the loop and in nothing else.
|
|
189
|
+
*
|
|
190
|
+
* A `NULL` column value is SKIPPED, because the engine's sequence has no
|
|
191
|
+
* item where the member is absent — which is why only a path the schema
|
|
192
|
+
* types as a number that cannot hold `null` reaches here: a stored
|
|
193
|
+
* `null` and an absent member are one value in SQL, and dropping a
|
|
194
|
+
* present `null` would answer where the engine does not.
|
|
195
|
+
*
|
|
196
|
+
* `undefined` — what these summaries answer for an input they cannot
|
|
197
|
+
* summarise — becomes SQL `NULL`, which the aggregate decoder reads back
|
|
198
|
+
* as the empty answer, exactly as the engine's empty sequence does.
|
|
199
|
+
* @param {any} connection
|
|
200
|
+
* @param {Map<string, string>} registered - operator name → SQL name
|
|
201
|
+
* @param {string} name - the registry operator name (`$mean`)
|
|
202
|
+
* @param {{ fn: Function }} spec
|
|
203
|
+
* @returns {string} the SQL function name to call
|
|
204
|
+
*/
|
|
205
|
+
export function registerAggregateOperator(connection, registered, name, spec) {
|
|
206
|
+
const owned = registered.get(name);
|
|
207
|
+
if (owned !== undefined) return owned;
|
|
208
|
+
const sqlName = aggregateNameFor(name);
|
|
209
|
+
connection.registerAggregate(sqlName, {
|
|
210
|
+
start: () => [],
|
|
211
|
+
step: (values, value) => {
|
|
212
|
+
if (value !== null && value !== undefined) values.push(value);
|
|
213
|
+
return values;
|
|
214
|
+
},
|
|
215
|
+
result: (values) => {
|
|
216
|
+
const out = spec.fn(values);
|
|
217
|
+
return out === undefined || out === null ? null : out;
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
registered.set(name, sqlName);
|
|
221
|
+
return sqlName;
|
|
222
|
+
}
|