@jarenjs/db 0.73.0 → 0.83.2
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 +70 -7
- package/README.md +69 -6
- package/docs/HOSTS.md +17 -0
- package/docs/JOBS-FORMAT.md +26 -0
- package/docs/LIVE-FORMAT.md +52 -13
- package/docs/MIGRATION-FORMAT.md +34 -0
- package/docs/MODEL-FORMAT.md +163 -15
- package/docs/NATIVE-PLANS.md +111 -0
- package/docs/REPLICATION-FORMAT.md +19 -13
- package/docs/SEARCH.md +55 -0
- package/package.json +8 -4
- package/schemas/jaren-migration.draft-07.schema.json +54 -5
- package/schemas/jaren-migration.schema.json +49 -0
- package/schemas/jaren-model.authoring.schema.json +360 -0
- package/schemas/jaren-model.draft-07.schema.json +128 -0
- package/schemas/jaren-model.schema.json +128 -0
- package/src/algebra.js +26 -4
- package/src/backup.js +12 -7
- package/src/cursor.js +27 -4
- package/src/dag-job.js +2 -1
- package/src/ddl.js +13 -0
- package/src/derive.js +14 -3
- package/src/dialect.js +12 -0
- package/src/dialects/check-read.js +151 -0
- package/src/dialects/invariant-sql.js +117 -0
- package/src/dialects/postgres.js +28 -4
- package/src/dialects/sqlite.js +23 -3
- package/src/driver.js +1 -0
- package/src/drivers/bun.js +22 -4
- package/src/emit.js +133 -25
- package/src/entity.js +98 -41
- package/src/errors.js +8 -0
- package/src/graph.js +8 -1
- package/src/index.js +3 -0
- package/src/introspect.js +81 -12
- package/src/invariants.js +45 -0
- package/src/jobs.js +39 -6
- package/src/live-nested.js +27 -10
- package/src/live.js +51 -136
- package/src/migrate.js +136 -22
- package/src/model.js +12 -0
- package/src/mutation.js +165 -0
- package/src/physical.js +147 -0
- package/src/plan.js +275 -64
- package/src/query.js +175 -78
- package/src/search.js +144 -0
- package/src/sql.js +60 -0
- package/src/store.js +49 -13
- package/src/tracker.js +63 -39
- package/src/window.js +1 -0
- package/types/index.d.ts +59 -4
- package/types/search.d.ts +20 -0
- package/types/typed.d.ts +1 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -177,9 +177,19 @@ stable code, an object and a detail, sorted, once:
|
|
|
177
177
|
| `unmapped-table` | a table with neither a document column nor a key |
|
|
178
178
|
| `unmapped-column` | a generated column whose expression is not a member path this dialect wrote |
|
|
179
179
|
| `unmapped-index` | an index over an expression, a predicate, or a column no member path explains |
|
|
180
|
+
| `unmapped-constraint` | a CHECK that is not a complete scalar enum of a mapped entity column |
|
|
180
181
|
| `unmapped-type` | a column type no schema type maps back from — the member is derived untyped |
|
|
181
182
|
| `ambiguous-relation` | a foreign key says which entity it points at; which side declared the edge, and whether the other holds many, is not in the shape |
|
|
182
183
|
|
|
184
|
+
Scalar entity enum CHECKs are recovered on both dialects. SQLite reads its
|
|
185
|
+
stored CREATE text; PostgreSQL reads `pg_constraint` expressions, including
|
|
186
|
+
its `ANY (ARRAY[...])` rendering and numeric literal casts. The parser accepts
|
|
187
|
+
only the entire enum expression. Unknown predicates, NULL lists, non-binary SQLite collations, nondeterministic
|
|
188
|
+
PostgreSQL collations and casts whose meaning is not proven remain `unmapped-constraint`. Partial indexes and
|
|
189
|
+
expression terms retain their catalog flags; they are never converted into
|
|
190
|
+
unconditional uniqueness. Optional `introspect.checks`/`readChecks` hooks leave
|
|
191
|
+
injected dialects compatible with the earlier catalog surface.
|
|
192
|
+
|
|
183
193
|
`strict: true` refuses instead of returning a partial model, because a
|
|
184
194
|
caller about to diff the result against a declared model needs to know
|
|
185
195
|
the difference is real.
|
|
@@ -351,7 +361,8 @@ A single-binding FLWOR over the collection (`$for: { <name>: '$[*]' }`
|
|
|
351
361
|
— the examples here write `it`, but the binding is the **document's** to
|
|
352
362
|
name and nothing translates differently under another one)
|
|
353
363
|
with: comparison predicates (`$eq $ne $lt $le $gt $ge`) between a
|
|
354
|
-
singular member path and a literal or external
|
|
364
|
+
singular member path and a literal or external, or two paths in the same
|
|
365
|
+
non-null number/string family; `$and`/`$or`/`$not`
|
|
355
366
|
composition; `$exists`/`$empty`; `$starts-with`/`$ends-with`/
|
|
356
367
|
`$contains` on schema-typed string paths with literal patterns;
|
|
357
368
|
`$orderby` over singular schema-typed paths (`$dir`, `$empty`, no
|
|
@@ -359,11 +370,33 @@ collation); a top-level `$subsequence` window with literal bounds; the
|
|
|
359
370
|
top-level aggregates `$count` and `$sum`/`$avg`/`$min`/`$max` over a
|
|
360
371
|
singular schema-typed path; the whole-document projection that returns
|
|
361
372
|
the bare binding; ONE member path, projected as its value beside its
|
|
362
|
-
JSON type; and a nested SHAPE of objects, arrays, literals and member
|
|
373
|
+
JSON type; and a nested SHAPE of objects, arrays, literals, whole collection bindings and member
|
|
363
374
|
paths, projected as one value/type pair per distinct leaf and rebuilt
|
|
364
375
|
by the decoder — never by parsing a JSON text the database assembled,
|
|
365
376
|
which could not tell an absent member from a present `null`.
|
|
366
377
|
|
|
378
|
+
Constant projection trees fetch a row marker instead of a document. A
|
|
379
|
+
window over a single path removes absent members before its SQL limit;
|
|
380
|
+
a window over an opaque projection stays set-residual because that projection
|
|
381
|
+
may emit zero or several items per source row.
|
|
382
|
+
|
|
383
|
+
General scalar grouping preserves absent and null keys separately through
|
|
384
|
+
value/type pairs. Null and boolean keys may group, but ordering them stays
|
|
385
|
+
residual to preserve `JQ2005`. Explicit group ordering accepts orderable keys and proven count/min/max
|
|
386
|
+
expressions, including aggregates absent from the return; first appearance
|
|
387
|
+
breaks ties. SUM/AVG ordering remains residual: compensated or differently
|
|
388
|
+
ordered SQL accumulation can change which group sorts first. Literal windows lower over singleton group constructors;
|
|
389
|
+
`$count` over such constructors with only row-count aggregates counts a grouped
|
|
390
|
+
subquery. `$distinct` over a typed scalar projection uses the same key relation, with
|
|
391
|
+
absent members removed first. Unordered inputs retain first appearance; an
|
|
392
|
+
ordering composed solely of the projected path lowers to group-key order.
|
|
393
|
+
|
|
394
|
+
Entity projection trees, constants, counts and single-path windows follow the
|
|
395
|
+
same cardinality rules. An equijoin graph can additionally filter through
|
|
396
|
+
boolean trees whose leaves belong to individual bindings. The entity emitter
|
|
397
|
+
resolves each leaf's binding alias before composing the total predicates;
|
|
398
|
+
a disjunction never supplies a mandatory join edge.
|
|
399
|
+
|
|
367
400
|
Plus the spatial predicates a **derived** index makes decidable:
|
|
368
401
|
`$bbox-intersects` against a literal or external region, a geohash
|
|
369
402
|
prefix no longer than a `derive: 'geohash'` column's precision, and the
|
|
@@ -396,15 +429,16 @@ mode, `knn`, beside native, row and set.
|
|
|
396
429
|
| construct | reason |
|
|
397
430
|
|---|---|
|
|
398
431
|
| `$let` bindings, `$fold`, positional/window bindings | no equivalence proof exists yet; residual by default |
|
|
399
|
-
| a `$groupby` whose key is untyped
|
|
400
|
-
| a window over
|
|
432
|
+
| a `$groupby` whose key is untyped, whose `$return` reads the binding, or whose `$orderby` names anything but an orderable key or count/min/max | SQL's grouping and the engine's need not agree on an untyped key; after a grouping the binding holds the group's ROWS, which an object member cannot take |
|
|
433
|
+
| a window over a group return that may omit an item, or a group aggregate beyond the proven constructor count | SQL group cardinality must equal the projected item cardinality; numeric and error semantics need their own proof |
|
|
401
434
|
| a `$for` binding nothing joins to — a cartesian product | the engine builds the product; a plan that emitted one by accident is the thing an equi-join graph exists to prevent |
|
|
402
435
|
| non-singular path expansion | one relation per binding in this version |
|
|
403
436
|
| `$match` and other unlisted operators, `$call` | no native spelling proven equivalent |
|
|
404
437
|
| `$orderby` with a `$collation` | a collation the dialect cannot reproduce is refused, not approximated |
|
|
405
|
-
| a projection the tree cannot rebuild: an operator over a member, a reference to the binding itself, a non-singular path
|
|
438
|
+
| a projection the tree cannot rebuild: an operator over a member, a reference to the binding itself, a non-singular path | the WHOLE projection runs per row (the row residual) — pushed and ordered rows, projected by the engine; a window over these items runs in the set residual; promoting the part that composes would answer a shape nobody asked for |
|
|
406
439
|
| string operators with an external pattern | the pattern's type is unknowable at plan time and the engine ERRORS on non-string patterns |
|
|
407
|
-
| comparisons
|
|
440
|
+
| path comparisons with untyped, nullable, boolean or differing comparison families | the total typed comparison proof does not cover these shapes |
|
|
441
|
+
| `$distinct` over untyped/compound projections or ordering by other paths | first-occurrence order and structural equality need additional lowering |
|
|
408
442
|
| array/object literals in comparisons | deep-equality has no guarded native form |
|
|
409
443
|
| `$within` over a `derive: 'bbox'` column | a bounding-box pre-filter is pushed; exact containment refines in the engine |
|
|
410
444
|
| a bounded `$distance` over a `derive: 'bbox'` column | a geodesic-circle box pre-filter is pushed; the exact distance refines in the engine |
|
|
@@ -788,7 +822,7 @@ exactly what an honest explain may not print.
|
|
|
788
822
|
### The two residual modes
|
|
789
823
|
|
|
790
824
|
- **Row residual** — only the projection is untranslated: predicates,
|
|
791
|
-
|
|
825
|
+
and ordering are fully pushed, with no output window; each fetched row runs
|
|
792
826
|
`{ $for: { <the document's own binding>: '$[*]' },
|
|
793
827
|
$return: [ <the document's $return> ] }` (the array wrapper keeps
|
|
794
828
|
array-valued items unambiguous) and the items concatenate in row
|
|
@@ -1116,3 +1150,32 @@ by refreshing affected parents from bounded leaves. Source and result payloads
|
|
|
1116
1150
|
consume row and byte credits; unsupported shapes keep named reruns. See
|
|
1117
1151
|
[the replication contract](docs/REPLICATION-FORMAT.md) and
|
|
1118
1152
|
[the strategy matrix](docs/LIVE-FORMAT.md).
|
|
1153
|
+
|
|
1154
|
+
Parameterized distance bounds use the closed `circleAxis` derived parameter:
|
|
1155
|
+
`{ kind: 'circleAxis', centre: { external } | { literal }, radius: { external }
|
|
1156
|
+
| { literal }, axis: 'w' | 's' | 'e' | 'n' }`. Both inputs are named in every
|
|
1157
|
+
edge slot. The binder uses the shared geographic kernel and diverts the whole
|
|
1158
|
+
query for missing/invalid values, negative radii, polar or antimeridian boxes.
|
|
1159
|
+
The exact distance predicate still refines the candidates. The shared oracle
|
|
1160
|
+
covers SQLite column/R*Tree indexes and PostgreSQL, including repeated cached
|
|
1161
|
+
calls with different bound values.
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
## Explicit relational adoption
|
|
1165
|
+
|
|
1166
|
+
`introspect.js` owns physical inventory independently of model derivation.
|
|
1167
|
+
`physical.js` compiles column codecs and verifies declarations against that
|
|
1168
|
+
inventory. The existing entity core, tracker and graph row merger execute both
|
|
1169
|
+
hybrid and column layouts; there is no separate relational store. The query
|
|
1170
|
+
planner reports decoded evaluation for physical codecs, and refuses physical
|
|
1171
|
+
keyset continuation until its identity semantics are qualified.
|
|
1172
|
+
|
|
1173
|
+
`sql.js` binds trusted statements to `store.js` transaction views. It reuses the
|
|
1174
|
+
read classifier's tokenizer and the driver's scope owner. Writes invalidate all
|
|
1175
|
+
clean tracked entities; pending edits and incomplete capture populations refuse.
|
|
1176
|
+
`invariants.js` uses the shared Query compiler for store rules; the dialect lowers
|
|
1177
|
+
a bounded database subset into ordered trigger bodies. `migrate.js` reuses its
|
|
1178
|
+
existing rebuild/receipt transaction and verifies preservation before publication.
|
|
1179
|
+
The backup publisher remains shared by Node online and Bun serialized snapshots.
|
|
1180
|
+
|
|
1181
|
+
`src/search.js` composes core lexical mechanics and the JSON predicate compiler over complete bounded entity snapshots. Committed capture and data-version checks invalidate derived state; SHA-256 source content validates persisted caches across reopen. Snapshot storage uses existing collection transactions. See [search execution](docs/SEARCH.md); native full-text dialects remain unqualified.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @jarenjs/db
|
|
2
2
|
|
|
3
|
+
For existing-file adoption with native queries, receipts and jobs, start with the [combined public recipe and evidence ledger](../../docs/ADOPTION-EVIDENCE.md). Adopted-trigger capture, physical keysets and PostgreSQL subsystem gaps retain their documented refusals; synthetic SQLite proof does not retire downstream SQL.
|
|
4
|
+
|
|
3
5
|
Documents AND entities in SQLite. A **model document** declares
|
|
4
6
|
collections (a JSON Schema, a key, indexes) and — since phase B —
|
|
5
7
|
**entities**: keys, typed columns, relations, defaults and an
|
|
@@ -214,7 +216,7 @@ no continuation to emit.
|
|
|
214
216
|
`explain().projection` names the path or the leaf `paths`, and
|
|
215
217
|
reading a shape no longer reads every document. A shape the tree
|
|
216
218
|
cannot rebuild — an operator over a member, a reference to the
|
|
217
|
-
binding
|
|
219
|
+
whole entity binding, a non-singular path — refuses WHOLE and
|
|
218
220
|
runs per row, with `explain().residualProjection` naming what stayed
|
|
219
221
|
behind: promoting the half that composes would answer a shape nobody
|
|
220
222
|
asked for. Every `explain()` also carries `budget`: the profile
|
|
@@ -227,6 +229,10 @@ no continuation to emit.
|
|
|
227
229
|
arithmetic deviation declared rather than hidden (MODEL-FORMAT §10.6: SQLite's
|
|
228
230
|
compensated `SUM` and the engine's naive one differ in the last
|
|
229
231
|
bit). `strict: true` turns any residual into a compile error.
|
|
232
|
+
Constant trees fetch only a row marker. Counts over entity projection trees
|
|
233
|
+
lower too; a single-path count or window excludes absent members before
|
|
234
|
+
counting items. A window over a projection that can emit several items runs
|
|
235
|
+
in the engine.
|
|
230
236
|
- **Registered operators, correct in the residual, pushed where it
|
|
231
237
|
pays.** Open with a registry (`operators:
|
|
232
238
|
createJsltRegistry().use(mathPack).use(financePack)`) and a query may
|
|
@@ -254,6 +260,12 @@ no continuation to emit.
|
|
|
254
260
|
`openStore` by name — and the promotion still needs a numeric member
|
|
255
261
|
the schema forbids `null` on, because SQL cannot tell a stored `null`
|
|
256
262
|
from an absent one and the engine can.
|
|
263
|
+
- **Typed path comparisons and distinct scalar projections.** Comparisons
|
|
264
|
+
between two paths in the same non-null number/string family lower without
|
|
265
|
+
a UDF. `$distinct` over a typed scalar projection groups by its value and JSON
|
|
266
|
+
type, excluding missing members. Unordered inputs retain first occurrence;
|
|
267
|
+
ordering solely by the projected path also lowers. Literal windows apply
|
|
268
|
+
to the distinct items.
|
|
257
269
|
- **Grouping and joins lower whole, or not at all.** A `$groupby` over
|
|
258
270
|
schema-typed member keys becomes a real `GROUP BY`: the keys come back
|
|
259
271
|
with their JSON types beside them, so a group whose key is ABSENT
|
|
@@ -262,13 +274,20 @@ no continuation to emit.
|
|
|
262
274
|
in SQL under the ENGINE's empty rules (`0` for a count or a sum, no
|
|
263
275
|
member at all for the other three), and the groups come out in the
|
|
264
276
|
engine's own order of first appearance unless an `$orderby` over the
|
|
265
|
-
keys says otherwise. Entity queries join any number of bindings: every
|
|
277
|
+
keys or count/min/max says otherwise. Entity queries join any number of bindings: every
|
|
266
278
|
binding past the first must be attached by a column equality to one
|
|
267
279
|
already joined, which is what makes the plan a nested loop the engine
|
|
268
280
|
can be compared against — a binding nothing attaches would be a
|
|
269
281
|
cartesian product, so it is the residual, named, and `strict: true`
|
|
270
282
|
refuses it. `explain()` lists the join order with the equalities that
|
|
271
283
|
attached each binding, and the group's keys, aggregates and order.
|
|
284
|
+
Nullable scalar keys also group; ordering null or boolean keys remains
|
|
285
|
+
residual to preserve the engine's error. Windows over singleton group
|
|
286
|
+
constructors lower, and counts over constructors containing only keys,
|
|
287
|
+
literals and row counts count the grouped subquery. Ties under a partial
|
|
288
|
+
group ordering retain first appearance.
|
|
289
|
+
Boolean predicates may span joined bindings when each leaf belongs to one
|
|
290
|
+
binding; the equality graph still establishes every join.
|
|
272
291
|
A join predicate that is not an equality — a range between two mapped
|
|
273
292
|
columns of one family — refines a match it never makes: the anchor is
|
|
274
293
|
still an equality, so a range alone stays the residual. A projected
|
|
@@ -1177,6 +1196,13 @@ be declared (`unmapped-view`), a foreign key cannot say which side
|
|
|
1177
1196
|
declared the edge (`ambiguous-relation`). `strict: true` refuses rather
|
|
1178
1197
|
than answering a partial model.
|
|
1179
1198
|
|
|
1199
|
+
Scalar enum CHECKs on mapped entity columns are recovered from both
|
|
1200
|
+
catalogs. A partial index or an expression term is reported as `unmapped-index`
|
|
1201
|
+
without inventing unconditional uniqueness. CHECKs outside the complete scalar
|
|
1202
|
+
enum grammar, including NULL lists, collation-dependent equality and unproven
|
|
1203
|
+
type conversions, remain
|
|
1204
|
+
`unmapped-constraint`.
|
|
1205
|
+
|
|
1180
1206
|
The derived model is usable as a migration's `from`: read a database,
|
|
1181
1207
|
plan against your declared model, and an unchanged shape plans nothing.
|
|
1182
1208
|
SQLite and PostgreSQL derive the same logical model from equivalent
|
|
@@ -1309,10 +1335,10 @@ its side-effect-free status read are in
|
|
|
1309
1335
|
capture after a disposable live probe. Failed session probes select journal
|
|
1310
1336
|
capture. OPFS, IndexedDB snapshots and memory have explicit capability and
|
|
1311
1337
|
durability differences; see [execution hosts](docs/HOSTS.md).
|
|
1312
|
-
- **
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1338
|
+
- **Remaining boundaries**: untyped and opaque grouped/projection shapes,
|
|
1339
|
+
federation beyond two sources, richer incremental live queries, portable
|
|
1340
|
+
capture and introspection outside the model vocabulary (MODEL-FORMAT §10.6
|
|
1341
|
+
and the roadmap).
|
|
1316
1342
|
|
|
1317
1343
|
The normative formats are
|
|
1318
1344
|
[docs/MODEL-FORMAT.md](docs/MODEL-FORMAT.md) (storage §§1–7, safe
|
|
@@ -1333,6 +1359,7 @@ Every subpath a consumer can import, derived from the manifest by
|
|
|
1333
1359
|
<!--fact:exports.db-->
|
|
1334
1360
|
| Import | Kind | Declarations |
|
|
1335
1361
|
|---|---|---|
|
|
1362
|
+
| `@jarenjs/db/search` | JavaScript | declared |
|
|
1336
1363
|
| `@jarenjs/db` | JavaScript | declared |
|
|
1337
1364
|
| `@jarenjs/db/node` | JavaScript | declared |
|
|
1338
1365
|
| `@jarenjs/db/postgres` | JavaScript | declared |
|
|
@@ -1342,6 +1369,7 @@ Every subpath a consumer can import, derived from the manifest by
|
|
|
1342
1369
|
| `@jarenjs/db/app` | JavaScript | declared |
|
|
1343
1370
|
| `@jarenjs/db/schemas/jaren-migration.draft-07.schema.json` | schema | — |
|
|
1344
1371
|
| `@jarenjs/db/schemas/jaren-migration.schema.json` | schema | — |
|
|
1372
|
+
| `@jarenjs/db/schemas/jaren-model.authoring.schema.json` | schema | — |
|
|
1345
1373
|
| `@jarenjs/db/schemas/jaren-model.draft-07.schema.json` | schema | — |
|
|
1346
1374
|
| `@jarenjs/db/schemas/jaren-model.schema.json` | schema | — |
|
|
1347
1375
|
| `@jarenjs/db/schemas/jaren-replication-snapshot.draft-07.schema.json` | schema | — |
|
|
@@ -1368,3 +1396,38 @@ The output is one atomically published collection bundle; read it with
|
|
|
1368
1396
|
`--format collections`. Assertion plans expose provider, ordered-fold and
|
|
1369
1397
|
bounded materialization strategies through `onAssertionPlan`.
|
|
1370
1398
|
See [MIGRATION-FORMAT §6 and §11](docs/MIGRATION-FORMAT.md).
|
|
1399
|
+
|
|
1400
|
+
Collection constructor projections can include a whole row beside member
|
|
1401
|
+
paths. Group ordering lowers count/min/max expressions, including aggregates
|
|
1402
|
+
used only for sorting; SUM/AVG ordering remains in the engine to preserve
|
|
1403
|
+
floating-point accumulation order. Parameterized distance bounds bind both
|
|
1404
|
+
centre and radius through the spatial index, with full-query fallback for
|
|
1405
|
+
invalid, polar and antimeridian probes.
|
|
1406
|
+
|
|
1407
|
+
Live collection groups now maintain multiple keys, unordered typed scalar
|
|
1408
|
+
distinct projections, and count/sum/avg/min/max over canonical group results.
|
|
1409
|
+
Only affected groups are reevaluated in source order; a final aggregate folds
|
|
1410
|
+
the retained group outputs in first-appearance order. Input documents and
|
|
1411
|
+
group outputs consume both state-entry and byte credits. See
|
|
1412
|
+
[LIVE-FORMAT](docs/LIVE-FORMAT.md) for the supported shapes and measured costs.
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
## Existing relational SQLite files
|
|
1416
|
+
|
|
1417
|
+
Use `readSchema(connection)` for physical inventory, then declare an entity's
|
|
1418
|
+
`physical` table, ordered keys and column codecs and open with `{ adopt: true }`.
|
|
1419
|
+
Opening verifies the existing shape and emits no DDL. Ordinary column tables need
|
|
1420
|
+
no document column; integer identities, exact hexadecimal BLOBs, database defaults
|
|
1421
|
+
and read-only views have explicit contracts in [MODEL-FORMAT](docs/MODEL-FORMAT.md#12-existing-column-layouts).
|
|
1422
|
+
|
|
1423
|
+
Inside `store.transaction`, `tx.sql.prepare(text, { access: 'read' | 'write' })`
|
|
1424
|
+
shares the entity/outbox connection and savepoint owner. Statements expire with
|
|
1425
|
+
the scope. [The client recipe](../linq/docs/DB-CLIENT.md#trusted-sql-during-adoption)
|
|
1426
|
+
documents trust, invalidation and synchronous execution. Schema changes use
|
|
1427
|
+
`planPhysicalMigration` and `migrate`; [preservation and forward recovery](docs/MIGRATION-FORMAT.md#existing-physical-files-and-forward-recovery)
|
|
1428
|
+
require explicit dispositions and assertions. `planInvariants` supplies declared
|
|
1429
|
+
SQLite constraint/audit triggers for installation through that migration boundary.
|
|
1430
|
+
|
|
1431
|
+
Native column reads and bounded mutation documents are specified in [NATIVE-PLANS](docs/NATIVE-PLANS.md), including SQL census coverage, resource accounting and refusals.
|
|
1432
|
+
|
|
1433
|
+
`@jarenjs/db/search` composes the resident ranker with bounded authoritative entity reads and optional atomic snapshot storage. See [persisted search](docs/SEARCH.md).
|
package/docs/HOSTS.md
CHANGED
|
@@ -272,3 +272,20 @@ Checked and dropped:
|
|
|
272
272
|
- Read-only WAL workers do not accept writes, nested scopes do not migrate,
|
|
273
273
|
transient cursors do not exhaust the statement cap, and failed writes are not
|
|
274
274
|
automatically replayed. The lifecycle and fault corpus exercises each boundary.
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
## Existing-file adoption and backup
|
|
278
|
+
|
|
279
|
+
Node and Bun qualify explicit SQLite column mappings and scoped prepared SQL.
|
|
280
|
+
The synchronous transaction API exists only when the driver's observed
|
|
281
|
+
`synchronous` capability is true; worker and other async-only hosts expose no
|
|
282
|
+
sync twin. Physical adoption on PostgreSQL refuses pending a separate mapping
|
|
283
|
+
and codec qualification. Unknown application-trigger effects do not qualify
|
|
284
|
+
capture or replication; those combinations refuse before an adoption claim.
|
|
285
|
+
|
|
286
|
+
Node backups use the built-in online snapshot. Bun uses `Database.serialize()`
|
|
287
|
+
under the store gate, writes and flushes a sibling temporary, then uses the shared
|
|
288
|
+
atomic publisher. Both include committed WAL. Bun's snapshot holds the whole
|
|
289
|
+
image in memory and cancellation takes effect between phases. Process-kill tests
|
|
290
|
+
cover rebuild copy, table drop, commit and backup publication on both hosts;
|
|
291
|
+
these tests do not establish power-loss durability or native executable packaging.
|
package/docs/JOBS-FORMAT.md
CHANGED
|
@@ -504,3 +504,29 @@ a cancellation policy, a retry policy are the host's.
|
|
|
504
504
|
only by `cancel()`; `counts()` reports it. A transaction view's `jobs`
|
|
505
505
|
(the outbox, §3) carries none of the four: an administration call is a
|
|
506
506
|
root call.
|
|
507
|
+
|
|
508
|
+
## Business receipt and external-effect composition
|
|
509
|
+
|
|
510
|
+
`jobs.assertLease(lease)` checks current execution authority without a write or
|
|
511
|
+
renewal. It uses the same token/expiry guard and `JD2065`/`JD2066`/`JD2067` refusals
|
|
512
|
+
as settlement; malformed authority is `JD2068`. Inside `tx.jobs`, this read and
|
|
513
|
+
subsequent mapped writes share the transaction lock. Workers receive
|
|
514
|
+
`context.lease()` to read their current token after automatic renewal.
|
|
515
|
+
|
|
516
|
+
`createWorker({ effectSafety(job, context), ... })` optionally gates each handler
|
|
517
|
+
admission. Only `true` admits; any other resolved value pauses the queue attempt
|
|
518
|
+
in its existing `cancelled` state without running the handler. The worker
|
|
519
|
+
`context.pause()` exposes the same operation. An authorized operator can
|
|
520
|
+
explicitly `requeue` and claim it for reconciliation; it never becomes an
|
|
521
|
+
unreclaimable completed job merely because its external outcome is unknown.
|
|
522
|
+
A thrown policy error follows the ordinary failure path and is checked again on
|
|
523
|
+
a later attempt. `createDagJobRunner` forwards the same hook. The hook supplements
|
|
524
|
+
the mapped effect store's mandatory pre-dispatch fence and durable intent; it is
|
|
525
|
+
not a replacement for them.
|
|
526
|
+
|
|
527
|
+
Business receipts and external intents belong to application-mapped tables via
|
|
528
|
+
`@jarenjs/linq/db`. Job reset/sweep operate on queue/checkpoint rows only and do
|
|
529
|
+
not erase those facts. An expired lease, reset or retryable job failure cannot
|
|
530
|
+
provide permission to resend unresolved external writes. Local co-commit does
|
|
531
|
+
not mean exactly-once remote delivery. See the contract package's
|
|
532
|
+
[durable composition](../../contract/docs/DURABLE.md).
|
package/docs/LIVE-FORMAT.md
CHANGED
|
@@ -192,11 +192,11 @@ Choosing how much history to keep is the host's decision
|
|
|
192
192
|
(`retention`); what the reader owes is that when rows go, it reports
|
|
193
193
|
the gap instead of hiding it.
|
|
194
194
|
|
|
195
|
-
**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
**The log alone is not replication.** Replica identity, causal frontiers,
|
|
196
|
+
durable receipts, conflicts and bounded reset snapshots belong to the opt-in
|
|
197
|
+
[replication subsystem](REPLICATION-FORMAT.md). Its protocol builds on these
|
|
198
|
+
watermarks; a change-log cursor by itself supplies no conflict policy or causal
|
|
199
|
+
ordering across writers.
|
|
200
200
|
|
|
201
201
|
## 6. Cross-connection behaviour and non-claims
|
|
202
202
|
|
|
@@ -208,8 +208,8 @@ which changes when ANOTHER connection commits; poll it and treat a
|
|
|
208
208
|
change as "re-read what you care about". Cross-tab delivery is §7's
|
|
209
209
|
story (the live-query layer).
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
Capture-layer non-claims: no conflict resolution or capture of writes made
|
|
212
|
+
by other connections, no capture on stores
|
|
213
213
|
opened without `capture`, and no statement-level ordering within a
|
|
214
214
|
commit (§2).
|
|
215
215
|
|
|
@@ -255,9 +255,8 @@ the maintenance are this table's — an entity chain re-runs, declared —
|
|
|
255
255
|
and this document stays the only place they are decided.
|
|
256
256
|
|
|
257
257
|
**This table is normative.** Every row is implemented and tested;
|
|
258
|
-
nothing outside it is attempted. Classification
|
|
259
|
-
|
|
260
|
-
what the pushdown planner already means by it.
|
|
258
|
+
nothing outside it is attempted. Classification combines canonical document shapes with the compiled selection
|
|
259
|
+
plan; "extractable" below means what the pushdown planner already proves.
|
|
261
260
|
|
|
262
261
|
| Construct (as planned) | Strategy | Maintained state |
|
|
263
262
|
|---|---|---|
|
|
@@ -265,7 +264,10 @@ what the pushdown planner already means by it.
|
|
|
265
264
|
| the same with a per-row `select` projection (row-mode plan) | **incremental rows**: the affected row alone is recomputed; a source row may project to several items | result rows, grouped by source key |
|
|
266
265
|
| `orderBy` over extractable paths, optional `limit`, offset 0 | **maintained window**: a sorted structure; ties broken by the collection key, appended as the final sort term; an insert sorting beyond a full window is a no-op | the window rows and their sort keys |
|
|
267
266
|
| whole-query `count` / `sum` / `avg` / `min` / `max` (the plan's aggregate), optional `where` | **running accumulator** plus a per-row contribution map — a delete can only be answered from retained contributions (§3: a `remove` carries no old value). `min`/`max` removal of the last extremum holder FALLS BACK to a recompute over the retained contributions; the accumulator alone cannot answer, and this fallback is the documented cost | one contribution per matching row |
|
|
268
|
-
| single-level `groupBy` with
|
|
267
|
+
| single-level `groupBy` with one or more keys and canonical returns below | **group maintenance**: reevaluate affected groups through the engine in source-row order; first surviving source occurrence determines group order | source documents and group outputs, with entry and byte credits |
|
|
268
|
+
| unordered SQL-native typed scalar distinct projections | **distinct maintenance**: keep the source holders of each value; deleting its earliest holder may move the value in first-occurrence order | source documents and unique outputs, with entry and byte credits |
|
|
269
|
+
| count/sum/avg/min/max over canonical unwindowed groups | **group maintenance** followed by an engine fold over the retained group outputs in first-occurrence order | source documents and group outputs, with entry and byte credits |
|
|
270
|
+
| ordered/windowed distinct or other aggregates over groups | **re-run on invalidation** | the previous result, for diffing |
|
|
269
271
|
| `where` whose spatial predicate is **refined** (the plan pushed a bounding-box or cell-range pre-filter and left the exact `$within`, bounded `$distance` or over-long cell prefix to the residual — `explain().prefilters` with `exact: false`), no order, no aggregate; optional per-row `select` | **incremental rows** — the geofence: the initial fetch narrows through the derived index, and every touched row is re-evaluated by the engine's EXACT predicate, so a point emits `add` when it enters the region, `remove` when it leaves, and nothing while it moves within (a whole-document return sees a `replace` carrying the new position) | the result rows |
|
|
270
272
|
| a refined spatial predicate over a collection with **no document key** (`key: null`, rowid identity) | **re-run on invalidation** — the per-row strategy tracks a row by its declared key, and a rowid is not one; the reason says `rows without a document key cannot be tracked` | the previous result, for diffing |
|
|
271
273
|
| `orderBy` beside a refined spatial predicate — over `$distance` (not a path) or over a member (the set residual drops the planner's order terms) | **re-run on invalidation**, the ordering named as the reason | the previous result, for diffing |
|
|
@@ -326,8 +328,8 @@ nested groups have a separate bounded strategy below):
|
|
|
326
328
|
|
|
327
329
|
After `$groupby`, `$it` is the group's item sequence and `$g` its key;
|
|
328
330
|
return members are the group key or an aggregate over `$it` (a path
|
|
329
|
-
below it selects the aggregated member).
|
|
330
|
-
|
|
331
|
+
below it selects the aggregated member). A bare key or a single aggregate is also canonical; other return shapes
|
|
332
|
+
re-run. Multiple declared keys follow the same rule.
|
|
331
333
|
|
|
332
334
|
## 8. Invalidation
|
|
333
335
|
|
|
@@ -657,3 +659,40 @@ Count, sum, average, minimum and maximum recompute from only the affected
|
|
|
657
659
|
parent's bounded leaves. An offset, an unsupported operator, a global input to
|
|
658
660
|
the nested group, or a group-of-groups LINQ emission remains a named rerun.
|
|
659
661
|
Replicated writes enter the same committed capture stream as local writes.
|
|
662
|
+
|
|
663
|
+
### Group maintenance details
|
|
664
|
+
|
|
665
|
+
Canonical single-level groups accept one or more key expressions over one
|
|
666
|
+
collection binding, an optional fully translated filter, and either a bare
|
|
667
|
+
key, one count/sum/avg/min/max expression, or an object containing key,
|
|
668
|
+
`$default: [key, null]`, and aggregate members. Global-root reads, ordering,
|
|
669
|
+
and windows remain rerun shapes. Aggregate expressions are evaluated by the
|
|
670
|
+
query engine, including empty results and errors; maintenance does not
|
|
671
|
+
substitute JavaScript arithmetic for query operators.
|
|
672
|
+
|
|
673
|
+
Keys preserve structural equality and distinguish missing from null. Source
|
|
674
|
+
holders retain physical row positions: changing a holder preserves its
|
|
675
|
+
position, and deleting a group's earliest holder may move that group's
|
|
676
|
+
output. Unaffected group outputs retain reference identity. Only affected
|
|
677
|
+
groups are reevaluated, followed by an optional final aggregate over all group
|
|
678
|
+
outputs. This costs work proportional to the affected groups plus group-output
|
|
679
|
+
ordering/folding, rather than constant-time arithmetic deltas.
|
|
680
|
+
|
|
681
|
+
`live.maxMaintained` counts source holders and group output items;
|
|
682
|
+
`live.maxBytes` counts their serialized documents. This includes document
|
|
683
|
+
members omitted from a small aggregate result. Exceeding a bound refuses
|
|
684
|
+
registration or closes an active query with `JD2060`; the last delivered
|
|
685
|
+
result remains unchanged. Engine failures follow the same invalidation path.
|
|
686
|
+
|
|
687
|
+
`node --expose-gc benchmark/changeflow.js` compares every mutation against a
|
|
688
|
+
fresh query and a patch-only consumer. The
|
|
689
|
+
[generated comparison table](REPLICATION-FORMAT.md#measurements) includes
|
|
690
|
+
multiple-key groups, an aggregate of groups and distinct beside forced reruns.
|
|
691
|
+
It reports initialization allocations and both median and tail mutation time;
|
|
692
|
+
heap deltas before collection are not precise retained-state sizes. The fixture
|
|
693
|
+
has 200 initial rows and 15 mutations per case, so these measurements establish
|
|
694
|
+
correctness and costs for that fixture rather than a universal crossover.
|
|
695
|
+
|
|
696
|
+
## Structural range adapter
|
|
697
|
+
|
|
698
|
+
`@jarenjs/linq/db` exposes bounded ranges over captured hybrid entity roots, using the existing keyset pager and committed observer. Its live event is an explicit source reset with monotone revision; this does not promote offset windows or the remaining incremental shapes. See [COLLECTION-PROVIDER](../../app/docs/COLLECTION-PROVIDER.md) for credits, snapshot identity and disposal. Maintained top windows check offscreen rows against their bound and release retained state on close.
|
package/docs/MIGRATION-FORMAT.md
CHANGED
|
@@ -598,3 +598,37 @@ difference between a puzzled afternoon and a five-minute fix.
|
|
|
598
598
|
Down migrations REMAIN a non-goal (§7's reasoning is unchanged): a
|
|
599
599
|
down migration is a data-loss generator wearing a seatbelt; recovery
|
|
600
600
|
is a backup restored plus the forward chain.
|
|
601
|
+
|
|
602
|
+
## Existing physical files and forward recovery
|
|
603
|
+
|
|
604
|
+
`planPhysicalMigration(connection, fromModel, toModel, options)` records the
|
|
605
|
+
source schema and explicit DDL/SQL/rebuild steps as an ordinary migration document.
|
|
606
|
+
`options` supplies an `id`, `steps`, a disposition for every source `type:name`
|
|
607
|
+
(`preserve`, `replace`, or `drop`), and optional `{ sql, params, expected }`
|
|
608
|
+
preservation assertions. Assertions are SELECTs evaluated before and after the
|
|
609
|
+
steps. They should cover committed identities, exact BLOB hex and application
|
|
610
|
+
history facts. Unknown objects cannot disappear without a declared disposition.
|
|
611
|
+
Automatic hybrid model diffing refuses column layouts; an explicit plan is required.
|
|
612
|
+
|
|
613
|
+
Apply through `migrate(target, [plan], { baseline, model, shadow: false })`.
|
|
614
|
+
A physical plan must be qualified on an explicit backup and fresh-target fixture;
|
|
615
|
+
an empty model-generated shadow cannot recreate the original file's application
|
|
616
|
+
programs. The runner uses its existing immediate transaction, ordered steps,
|
|
617
|
+
checksummed receipt and FK checks. It checks the source schema before destructive
|
|
618
|
+
steps and preserved objects and assertions before publication. Target mapped
|
|
619
|
+
columns and declared invariant triggers are verified. A changed source is
|
|
620
|
+
`JD0020`, a lost object/fact is `JD0023`, and an edited applied receipt is `JD0022`.
|
|
621
|
+
|
|
622
|
+
Migration history is created inside the applying transaction only when needed.
|
|
623
|
+
An identical second run executes no DDL or DML. Failed steps and failed commits
|
|
624
|
+
roll back; after a process kill SQLite recovery leaves the source or the committed
|
|
625
|
+
target. Re-running resumes from committed receipts. Forward repair plans start
|
|
626
|
+
from the newest file, including later application edits; restoring an older
|
|
627
|
+
backup does not qualify as forward repair.
|
|
628
|
+
|
|
629
|
+
`backupTo()` publishes a sibling temporary only after a complete snapshot.
|
|
630
|
+
Node uses online backup. Bun uses its native serialized SQLite snapshot under
|
|
631
|
+
the store gate, then flushes and atomically renames through the same publisher.
|
|
632
|
+
Bun holds a full database image in memory and cannot offer page-granular copy
|
|
633
|
+
cancellation. Both snapshots include committed WAL; interruption before rename
|
|
634
|
+
leaves the previous destination valid, while a leftover temporary is not published.
|