@jarenjs/db 0.46.5 → 0.56.0

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 (72) hide show
  1. package/ARCHITECTURE.md +133 -17
  2. package/README.md +270 -36
  3. package/docs/JOBS-FORMAT.md +24 -8
  4. package/docs/LIVE-FORMAT.md +139 -7
  5. package/docs/MIGRATION-FORMAT.md +118 -36
  6. package/docs/MODEL-FORMAT.md +251 -30
  7. package/package.json +4 -5
  8. package/schemas/jaren-migration.draft-07.schema.json +73 -0
  9. package/schemas/jaren-migration.schema.json +73 -0
  10. package/src/algebra.js +22 -3
  11. package/src/capture.js +66 -28
  12. package/src/cli.js +225 -44
  13. package/src/ddl.js +23 -3
  14. package/src/dialect.js +13 -0
  15. package/src/dialects/sqlite.js +21 -1
  16. package/src/driver.js +63 -16
  17. package/src/drivers/wasm.js +1 -0
  18. package/src/emit-model.js +14 -0
  19. package/src/emit.js +42 -9
  20. package/src/entity.js +92 -47
  21. package/src/errors.js +28 -0
  22. package/src/index.js +2 -2
  23. package/src/jobs.js +40 -5
  24. package/src/live-time.js +605 -0
  25. package/src/live.js +52 -9
  26. package/src/migrate.js +397 -191
  27. package/src/model.js +173 -8
  28. package/src/plan.js +834 -47
  29. package/src/query.js +296 -22
  30. package/src/residual.js +15 -6
  31. package/src/series.js +349 -0
  32. package/src/store.js +243 -69
  33. package/src/tracker.js +173 -48
  34. package/types/index.d.ts +206 -12
  35. package/types/node.d.ts +3 -1
  36. package/types/typed.d.ts +58 -2
  37. package/types/wasm.d.ts +7 -0
  38. package/dist/types/algebra.d.ts +0 -199
  39. package/dist/types/app.d.ts +0 -49
  40. package/dist/types/capture.d.ts +0 -85
  41. package/dist/types/cli.d.ts +0 -2
  42. package/dist/types/dag-job.d.ts +0 -40
  43. package/dist/types/ddl.d.ts +0 -229
  44. package/dist/types/derive.d.ts +0 -250
  45. package/dist/types/dialect.d.ts +0 -149
  46. package/dist/types/dialects/sqlite.d.ts +0 -9
  47. package/dist/types/driver.d.ts +0 -110
  48. package/dist/types/drivers/bun.d.ts +0 -47
  49. package/dist/types/drivers/node.d.ts +0 -37
  50. package/dist/types/drivers/wasm.d.ts +0 -65
  51. package/dist/types/emit-model.d.ts +0 -44
  52. package/dist/types/emit.d.ts +0 -75
  53. package/dist/types/entity.d.ts +0 -23
  54. package/dist/types/errors.d.ts +0 -167
  55. package/dist/types/graph.d.ts +0 -28
  56. package/dist/types/index.d.ts +0 -37
  57. package/dist/types/jobs.d.ts +0 -140
  58. package/dist/types/knn.d.ts +0 -69
  59. package/dist/types/live.d.ts +0 -62
  60. package/dist/types/migrate.d.ts +0 -170
  61. package/dist/types/model.d.ts +0 -36
  62. package/dist/types/patch-sql.d.ts +0 -37
  63. package/dist/types/plan.d.ts +0 -140
  64. package/dist/types/profile.d.ts +0 -80
  65. package/dist/types/query.d.ts +0 -111
  66. package/dist/types/residual.d.ts +0 -61
  67. package/dist/types/store.d.ts +0 -53
  68. package/dist/types/tracker.d.ts +0 -43
  69. package/dist/types/typed.d.ts +0 -15
  70. package/dist/types/types.d.ts +0 -26
  71. package/dist/types/udf.d.ts +0 -75
  72. package/dist/types/window.d.ts +0 -52
@@ -1,44 +0,0 @@
1
- /**
2
- * @file Entity types from models (D17): build an EMIT MODEL DOCUMENT
3
- * — the published contract from EMIT-FORMAT.md — that renders entity
4
- * interfaces, input variants and the typed-store metadata from the
5
- * same model document the runtime validates against.
6
- *
7
- * The seam (EMIT-FORMAT §4.1, recorded here as the order demanded):
8
- * the schema compiler is INJECTED (`options.compile` is
9
- * `compileEmitModel` from `@jarenjs/emit`), called with
10
- * `extensions: ['x-entity']` so the vocabulary rides the member nodes
11
- * verbatim; this module then post-processes the MODEL DOCUMENT —
12
- * replacing relation member types with references, flipping
13
- * optionality, adding declarations — and never renders a character of
14
- * TypeScript itself. Emit stays database-free, db stays
15
- * renderer-free, and the injection keeps `@jarenjs/emit` out of db's
16
- * dependency graph (the generator script wires the two).
17
- *
18
- * What the artifact says, deliberately:
19
- * - entity interfaces are CLOSED objects (excess-property checking is
20
- * the point of generated types; the runtime validator stays
21
- * authoritative for what a database accepts);
22
- * - relation members are optional references — present only when a
23
- * graph load included them; `date-time`/`date` strings carry the
24
- * `DateTime` brand so the linq date operators light up;
25
- * - the `<Name>Input` variant makes defaulted and generated members
26
- * optional, drops to-one/to-many relation members (`create`/`add`
27
- * refuse them), and types many-to-many members as key-or-document
28
- * arrays (what `add()` accepts);
29
- * - `EntityMetaMap` carries doc/input/key/relations per entity — the
30
- * generic typed-store surface (`@jarenjs/db/typed`) binds to it.
31
- */
32
- /**
33
- * Build the emit-model document for a model's entities.
34
- * @param {any} model - a jaren-model document with `entities`
35
- * @param {{ compile: (schema: any, options?: any) => any,
36
- * source?: string, reserved?: string[] }} options - `compile` is
37
- * `compileEmitModel` (injected; see the header)
38
- * @returns {any} an EMIT-FORMAT `0.1` model document
39
- */
40
- export declare function entityEmitModel(model: any, options: {
41
- compile: (schema: any, options?: any) => any;
42
- source?: string;
43
- reserved?: string[];
44
- }): any;
@@ -1,75 +0,0 @@
1
- /**
2
- * @file Plan → SQL through a dialect. This is the query layer's shared
3
- * emitter, the same division of labour as `createDialect`'s DDL/DML
4
- * builders: structural SQL composition lives here, every
5
- * dialect-varying spelling (identifiers, parameters, string literals,
6
- * JSON access, `json_type`, `typeof`, string-operator forms, NULLS
7
- * placement, EXPLAIN phrasing) comes from the dialect. Values are
8
- * NEVER interpolated into the text: every literal and every external
9
- * becomes an ordered parameter slot, which is what makes injection
10
- * structurally impossible.
11
- *
12
- * Every emitted predicate is TOTAL (two-valued) by construction — the
13
- * `json_type` guards from the truth table in ARCHITECTURE.md — so
14
- * `NOT`/`AND`/`OR` compose classically and SQL's three-valued NULL
15
- * logic never decides a row.
16
- */
17
- export type ParamSlot = {
18
- external: string;
19
- } | {
20
- literal: unknown;
21
- } | {
22
- derived: {
23
- kind: 'bboxAxis';
24
- external: string;
25
- axis: 'w' | 's' | 'e' | 'n';
26
- };
27
- };
28
- /**
29
- * Emit one plan as SQL plus its ordered parameter slots.
30
- * @param {import('./algebra.js').Plan} plan
31
- * @param {any} dialect
32
- * @param {{ table: string, keyColumn: string, docColumn: string }} physical
33
- * @returns {{ sql: string, slots: ParamSlot[] }}
34
- */
35
- export declare function emitPlan(plan: import('./algebra.js').Plan, dialect: any, physical: {
36
- table: string;
37
- keyColumn: string;
38
- docColumn: string;
39
- }): {
40
- sql: string;
41
- slots: ParamSlot[];
42
- };
43
- /**
44
- * The entity predicate emitters, shared by the entity plan emitter
45
- * and the graph-load builder: given an alias and its document column,
46
- * emit one predicate with the flavor-correct forms.
47
- * @param {any} dialect
48
- * @param {(slot: ParamSlot) => string} param
49
- * @returns {{ emitPred: (aliasSql: string, docSql: string, pred: any) => string }}
50
- */
51
- export declare function createEntityPredicateEmitters(dialect: any, param: (slot: ParamSlot) => string): {
52
- emitPred: (aliasSql: string, docSql: string, pred: any) => string;
53
- };
54
- /**
55
- * Emit an entity plan (`entity-select` or `entity-join`) as SQL plus
56
- * ordered parameter slots. Entity-COLUMN refs compare real typed
57
- * columns with TOTAL forms and no `json_type` guard — a column-mapped
58
- * property has no present-`null` (§9.3), so presence IS `IS NOT
59
- * NULL`; entity-EPOCH refs compare the derived integer column against
60
- * a plan-time epoch translation; entity-DOC refs ride the phase-A
61
- * guarded truth table over the entity's JSONB column. Join emission
62
- * appends BOTH bindings' row identities in binding order, which is
63
- * exactly the engine's nested-loop order — determinism the oracle
64
- * depends on.
65
- * @param {any} plan - from `planEntityQuery`
66
- * @param {any} dialect
67
- * @param {(entity: string) => { table: string }} physicalOf
68
- * @returns {{ sql: string, slots: ParamSlot[] }}
69
- */
70
- export declare function emitEntityPlan(plan: any, dialect: any, physicalOf: (entity: string) => {
71
- table: string;
72
- }): {
73
- sql: string;
74
- slots: ParamSlot[];
75
- };
@@ -1,23 +0,0 @@
1
- /**
2
- * @file Entity sets: create / read by key / update / delete over the
3
- * hybrid mapping. The physical row is the mapped scalar columns, the
4
- * foreign-key columns, and one JSONB `doc` column for everything
5
- * else; a write SPLITS the completed document along the mapping and a
6
- * read MERGES it back. Defaults apply in JavaScript before validation
7
- * — the value the application sees and the value stored are the same
8
- * — and identity follows the declared strategy (caller, uuid, auto).
9
- *
10
- * Epoch date columns are DERIVED: the document keeps the RFC 3339
11
- * string, the column carries `getEpochOf…RFC3339(value)` so range
12
- * predicates are index-friendly; reads take the string from the
13
- * document and skip the derived column.
14
- */
15
- /**
16
- * The write/read machinery for one entity, prepared once.
17
- * @param {any} connection
18
- * @param {any} entity - the normalized entity (model.js)
19
- * @param {any} entityMapping - `explainMapping(...).entities[name]`
20
- * @param {((doc: any) => any) | null} validate
21
- * @returns {any}
22
- */
23
- export declare function entityCore(connection: any, entity: any, entityMapping: any, validate: ((doc: any) => any) | null): any;
@@ -1,167 +0,0 @@
1
- /**
2
- * @file Error types for @jarenjs/db, built on `@jarenjs/core`'s coded
3
- * contract: every failure carries a stable `code` (JD0xxx compile-time,
4
- * JD2xxx runtime), a bare `reason`, a composed `message`, and — where a
5
- * position in the model document exists — a `docPath`. Runtime errors
6
- * additionally carry the `collection` and, where one exists, the `key`
7
- * as own properties. Database errors are wrapped, never leaked raw: the
8
- * reason keeps the original text, `cause` keeps the original error. The
9
- * normative table lives in docs/MODEL-FORMAT.md §7, proven in sync with
10
- * `DB_CODES` below by a test.
11
- */
12
- import { CodedError } from '@jarenjs/core/errors';
13
- /**
14
- * The runtime code table (the `CSV_CODES` shape): one entry per code
15
- * this package can raise, proven in sync with MODEL-FORMAT.md §7's
16
- * normative table by a test.
17
- */
18
- export declare const DB_CODES: Readonly<{
19
- JD0001: "the SQLite library is below the supported floor";
20
- JD0002: "the declared model disagrees with the existing database";
21
- JD0003: "the driver binding is unavailable on this runtime";
22
- JD0004: "a declared index cannot be mapped to a column";
23
- JD0005: "the model document is invalid";
24
- JD0010: "strict mode refused a residual";
25
- JD0011: "the profile refused the document";
26
- JD0012: "work waited too long for the open transaction to settle";
27
- JD0030: "an unknown x-entity member was declared";
28
- JD0031: "relation declarations contradict each other";
29
- JD0032: "the include specification is invalid";
30
- JD0040: "the save spans a relation cycle";
31
- JD0050: "live queries require change capture";
32
- JD0051: "the demanded live mode is unavailable";
33
- JD0052: "the live-query bound was reached";
34
- JD0020: "the migration's from-shape does not match the database";
35
- JD0021: "the migration is missing a required data transform";
36
- JD0022: "an applied migration disagrees with the history record";
37
- JD0023: "a migration step failed";
38
- JD2001: "insert found the key already present";
39
- JD2002: "a usable key could not be resolved for the write";
40
- JD2003: "the write failed schema validation";
41
- JD2004: "an undeclared collection was requested";
42
- JD2005: "a database operation failed";
43
- JD2006: "patch found no document at the key";
44
- JD2007: "the result exceeded the profile row bound";
45
- JD2040: "the row changed under an optimistic update";
46
- JD2050: "a changeset could not be decoded";
47
- JD2051: "the change log is not enabled";
48
- JD2060: "the maintained live state exceeded its bound";
49
- JD2061: "another context owns the database";
50
- JD2062: "the store closed with job handlers still in flight";
51
- }>;
52
- /**
53
- * A defect found while opening a store — in the model document, the
54
- * declared indexes, the driver binding, or the database's agreement
55
- * with the declaration. Codes:
56
- *
57
- * - `JD0001` — the SQLite library reported a version below the
58
- * supported floor; the reason names the version found
59
- * - `JD0002` — a declared collection already exists in the database
60
- * with a different shape; nothing was altered — changing shape is
61
- * the migration story, a later capability
62
- * - `JD0003` — the runtime builtin behind a driver could not be
63
- * loaded here (Node cannot resolve `bun:`; Bun ships no
64
- * `node:sqlite`), or an injected handle is missing
65
- * - `JD0004` — a declared index cannot be mapped to a column: its
66
- * path does not select exactly one member (wildcards, slices,
67
- * filters and descendants are not indexable), or its `derive`
68
- * declaration is not one the storage vocabulary carries; the reason
69
- * names the expression or the member and `docPath` points at it
70
- * - `JD0005` — the model document is invalid; `docPath` points at
71
- * the offending member
72
- * - `JD0010` — `strict: true` and part of the query would have run
73
- * outside the database; the reason names the forcing construct
74
- * - `JD0011` — the active profile refused the document before any
75
- * execution: an undeclared external, host function, collation or
76
- * collection, or a refused full-table scan; the reason names it
77
- * - `JD0030` — an unknown member inside an `x-entity` block; a
78
- * silently ignored mapping directive is a data-loss bug waiting
79
- * - `JD0031` — two relation declarations whose inverses contradict
80
- * (different `via`, impossible `many` pairings)
81
- * - `JD0032` — a graph-load include specification is invalid: an
82
- * unknown relation, a cycle, an untranslatable filter, or the
83
- * depth bound exceeded (the bound is printed, never silent)
84
- * - `JD0040` — `saveChanges()` cannot order its statements: the
85
- * entities being inserted or deleted form a foreign-key cycle
86
- * (self-references included); break the save in two
87
- * - `JD0050` — a live query was registered on a store opened without
88
- * `capture`; the patch stream is the invalidation source
89
- * - `JD0051` — `mode: 'incremental'` was demanded but the document
90
- * classifies as re-run; the reason names the forcing construct
91
- * - `JD0052` — registering would exceed the store's `live.maxQueries`
92
- * bound; the bound is printed, never silent
93
- * - `JD0020` — a migration's `from` hash does not match the
94
- * database's recorded shape; running it would corrupt
95
- * - `JD0021` — a draft transform was not filled in, or a document no
96
- * longer validates after the migration (a narrowing without an
97
- * adequate transform)
98
- * - `JD0022` — the migration list disagrees with the applied history
99
- * (an edited file, a missing file, a reordered sequence)
100
- * - `JD0023` — a step failed: an assertion returned rows, DDL was
101
- * rejected, or a transform produced an unstorable value
102
- */
103
- export declare class DbCompileError extends CodedError {
104
- /**
105
- * @param {string} code
106
- * @param {string} reason - The bare reason; `message` is composed per
107
- * the coded contract.
108
- * @param {string} [docPath] - JSON Pointer into the model document,
109
- * where one exists.
110
- * @param {Error} [cause]
111
- */
112
- constructor(code: string, reason: string, docPath?: string, cause?: Error);
113
- }
114
- /**
115
- * A failure while reading or writing an open store. Codes:
116
- *
117
- * - `JD2001` — `insert` hit a document already stored under the key
118
- * - `JD2002` — the declared key pointer resolved to nothing or to a
119
- * non-scalar, or an explicit key argument is not a string or number
120
- * - `JD2003` — the injected validation hook rejected the document
121
- * that a write would have stored; `errors` carries the hook's
122
- * findings when it produced any
123
- * - `JD2004` — `collection()` named a collection the model does not
124
- * declare
125
- * - `JD2005` — the database rejected an operation for a reason that
126
- * is not a duplicate key; the original error is the `cause`
127
- * - `JD2006` — `patch` addressed a key with no stored document
128
- * - `JD2007` — a fetch crossed the profile's `maxRows` bound; the
129
- * result is refused whole, never silently truncated
130
- * - `JD2040` — an optimistic update or delete matched no row: the
131
- * declared version changed under the save (or the row is gone);
132
- * the error names the entity and key, and the whole save rolled
133
- * back
134
- * - `JD2050` — a session changeset carried bytes this decoder does
135
- * not recognise (a future SQLite format change would land here)
136
- * - `JD2051` — `changesSince` was called on a store whose capture
137
- * has no persisted log
138
- * - `JD2060` — maintenance crossed the live query's `maxMaintained`
139
- * bound; the query delivered this error and closed rather than
140
- * degrade
141
- * - `JD2061` — a second context tried to open a database whose
142
- * storage grants one context exclusive access (the owner topology
143
- * of LIVE-FORMAT §11); connect to the owner instead
144
- */
145
- export declare class DbRuntimeError extends CodedError {
146
- collection: string | undefined;
147
- key: string | number | undefined;
148
- errors: unknown[] | undefined;
149
- /**
150
- * @param {string} code
151
- * @param {string} reason - The bare reason; `message` is composed per
152
- * the coded contract.
153
- * @param {{ docPath?: string, collection?: string,
154
- * key?: string | number, errors?: unknown[], cause?: unknown }} [details]
155
- * - `docPath` points into the model document (the collection the
156
- * failure belongs to); `collection`/`key` are installed as own
157
- * properties; `errors` carries validation findings; `cause` follows
158
- * the coded contract's `hasOwn` form.
159
- */
160
- constructor(code: string, reason: string, details?: {
161
- docPath?: string;
162
- collection?: string;
163
- key?: string | number;
164
- errors?: unknown[];
165
- cause?: unknown;
166
- });
167
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * @file Row → entity-graph reconstruction. One place owns the merge
3
- * discipline (§9.3): mapped scalar columns fold back into the JSONB
4
- * document's parse (booleans un-integer, SQL NULL reads back ABSENT,
5
- * derived epoch columns are skipped because the string never left the
6
- * document), foreign-key columns fold in the same way, and — for the
7
- * one-statement graph loads — projected relation JSON parses
8
- * recursively into child arrays or single children.
9
- */
10
- /**
11
- * Merge one database row back into its entity document.
12
- * @param {any} entityMapping - `explainMapping(...).entities[name]`
13
- * @param {any} row - a row carrying the entity's columns plus the
14
- * rendered document text
15
- * @param {string} [docField] - the column the document text rides in
16
- * @returns {any}
17
- */
18
- export declare function mergeEntityRow(entityMapping: any, row: any, docField?: string): any;
19
- /**
20
- * Parse one graph-load row: the root entity's merge plus every
21
- * included relation's projected JSON, recursively.
22
- * @param {any} node - the include-plan node
23
- * `{ entityMapping, includes: { name, field, many, count, child }[] }`
24
- * @param {any} row
25
- * @param {string} docField
26
- * @returns {any}
27
- */
28
- export declare function parseGraphRow(node: any, row: any, docField?: string): any;
@@ -1,37 +0,0 @@
1
- /**
2
- * @file @jarenjs/db — document storage over SQLite through two seams:
3
- * a driver (how a connection is made: `@jarenjs/db/node`, `/bun`, or
4
- * `/wasm` with an injected handle) and a dialect (how SQL is spelled).
5
- * This root subpath never touches a runtime builtin — a browser
6
- * bundler resolves it clean; the bindings live behind their own
7
- * subpaths and load their builtin lazily inside `open()`.
8
- */
9
- export { openStore, normalizeModel, MODEL_VERSION } from './store.js';
10
- export { createDialect } from './dialect.js';
11
- export { sqliteDialect } from './dialects/sqlite.js';
12
- export { SQLITE_FLOOR, chain, toPromise, isThenable, compareVersions, openConnection, wrapStatement, lazyOpen, } from './driver.js';
13
- export { planCollection, compileIndexPath, schemaTypeAt, KEY_COLUMN, DOC_COLUMN, normalizeDeclaredSql, comparableDeclaredSql, } from './ddl.js';
14
- export { planQuery, assertDecidedKind, entityShape, entityPathRef, planEntityPredicate, planEntityQuery, } from './plan.js';
15
- export { emitPlan, createEntityPredicateEmitters, emitEntityPlan } from './emit.js';
16
- export { mergeEntityRow, parseGraphRow } from './graph.js';
17
- export { selectPlan, conjoin, assertNoSqlText, PLAN_VERSION } from './algebra.js';
18
- export { typeOfPath, isNumericType } from './types.js';
19
- export { compileSetResidual, compileRowResidual, sequenceResult } from './residual.js';
20
- export { deterministicFragment, registerFragment } from './udf.js';
21
- export { DERIVE_KINDS, DERIVE_MAPPING, PHYSICAL_KINDS, BBOX_COMPONENTS, BBOX_INDEX_ORDER, PRECISION_MIN, PRECISION_MAX, DIMS_MIN, DIMS_MAX, deriveGeohash, deriveBboxEdge, deriveVector, derivedValue, derivedMappingFor, memberAt, storedMemberForm, registerDeriveFunctions, probeVector, columnScore, } from './derive.js';
22
- export { KNN_MARGIN, IDENTITY_CHUNK, cutCandidates, identityBatches } from './knn.js';
23
- export { createQueryEngine, createQueryState, createEntityQueryEngine, createLoadEngine, INCLUDE_DEPTH_DEFAULT, } from './query.js';
24
- export { normalizeProfile, SAFE_PROFILE, translateProfilePredicate, applyMandatoryPredicate, applyRowBound, } from './profile.js';
25
- export { translatePatch } from './patch-sql.js';
26
- export { normalizeEntities, explainMapping } from './model.js';
27
- export { planEntity, planJoinTable } from './ddl.js';
28
- export { entityCore } from './entity.js';
29
- export { entityEmitModel } from './emit-model.js';
30
- export { parseChangeset, translateOperations, keyToken, createCaptureEngine, CHANGES_TABLE, DEFAULT_RETENTION, } from './capture.js';
31
- export { createTracker, deepFreeze, BATCH_PARAM_BUDGET, BATCH_ROW_BOUND, } from './tracker.js';
32
- export { planMigration, planModelMigration, migrate, migrationStatus, shapeHash, migrationChecksum, createModelShape, schemaShapeOf, compareShapeToModel, MIGRATION_VERSION, HISTORY_TABLE, } from './migrate.js';
33
- export { DbCompileError, DbRuntimeError, DB_CODES } from './errors.js';
34
- export { classifyLiveQuery, createLiveRegistry, diffRows, LIVE_DEFAULTS } from './live.js';
35
- export { createSortedWindow, compareCodepoint } from './window.js';
36
- export { createJobEngine, JOBS_TABLE, JOB_CHECKPOINTS_TABLE, JOB_DEFAULTS, describeValue, serializeResult, } from './jobs.js';
37
- export { createDagJobRunner } from './dag-job.js';
@@ -1,140 +0,0 @@
1
- /**
2
- * @file The durable job queue (JOBS-FORMAT): enqueue, the
3
- * single-statement guarded claim (§3 — one statement is one
4
- * transaction, so no double-claim without any distributed lock),
5
- * retry with exponential backoff and jitter (§4), recovery as
6
- * re-claim of expired leases (§5), polling workers with same-process
7
- * wake-on-enqueue (§6), and the per-job flow checkpoint store the DAG
8
- * composition binds (§7) — completion marks the job done and records
9
- * the result in ONE guarded transaction.
10
- *
11
- * Every worker transition is guarded by `state='leased' AND
12
- * lease_owner=?`: execution is at-least-once, completion is
13
- * exactly-once. `now` and `random` are injectable — the runtime
14
- * defaults are the clock and `Math.random`; every test injects.
15
- *
16
- * The worker LIFECYCLE holds two invariants that a long-running process
17
- * depends on, and neither is a detail:
18
- *
19
- * - **No handler value can break the loop.** A handler is host code and
20
- * may resolve with something JSON cannot express, or reject with a
21
- * value whose own `message` throws when read. Both are normalized
22
- * totally, and `runOne` is isolated inside the loop, so the worst a
23
- * single job can do is fail its own attempt. A rejected claim-execute
24
- * loop would stop draining the queue silently.
25
- * - **Shutdown is bounded.** Handlers receive an `AbortSignal` and
26
- * `stop()` takes a deadline, so a handler that never settles cannot
27
- * hold `stop()` — and therefore `store.close()`, and therefore the
28
- * database file — open forever. A loop the deadline could not drain
29
- * is CANCELLED, not merely left behind: when its handler finally
30
- * settles it exits without another claim, store write or poll
31
- * timer, and its abandoned job recovers by lease expiry (§5).
32
- */
33
- export declare const JOBS_TABLE = "_jaren_jobs";
34
- export declare const JOB_CHECKPOINTS_TABLE = "_jaren_job_checkpoints";
35
- /** §4 defaults, all overridable per worker. */
36
- export declare const JOB_DEFAULTS: Readonly<{
37
- maxAttempts: 5;
38
- leaseMs: 30000;
39
- pollInterval: 500;
40
- backoffBase: 1000;
41
- backoffCap: 60000;
42
- /** How long `stop()` waits for in-flight handlers after signalling
43
- * abort, before it stops waiting and reports what is still running.
44
- * Bounded on purpose: an unbounded wait makes one stuck handler
45
- * indistinguishable from a hung process. */
46
- stopGraceMs: 5000;
47
- }>;
48
- /**
49
- * A diagnostic string for ANY value, including ones that fight back — a
50
- * getter that throws, a null-prototype object, a revoked proxy, a
51
- * symbol. Total by construction: an error report is never the thing that
52
- * fails.
53
- * @param {any} value
54
- * @returns {string}
55
- */
56
- export declare function describeValue(value: any): string;
57
- /**
58
- * JSON text for a job result, or `null` when the value cannot be
59
- * expressed — a BigInt, a cycle, a `toJSON` that throws. The caller
60
- * treats that as a failed attempt, never as a broken worker.
61
- * @param {any} value
62
- * @returns {{ text: string | null } | { reason: string }}
63
- */
64
- export declare function serializeResult(value: any): {
65
- text: string | null;
66
- } | {
67
- reason: string;
68
- };
69
- /**
70
- * The queue engine over one open connection.
71
- * @param {{ connection: any, now?: () => number,
72
- * random?: () => number,
73
- * defaults?: Partial<typeof JOB_DEFAULTS> }} options
74
- */
75
- export declare function createJobEngine(options: {
76
- connection: any;
77
- now?: () => number;
78
- random?: () => number;
79
- defaults?: Partial<typeof JOB_DEFAULTS>;
80
- }): {
81
- ready: any;
82
- enqueue: (kind: any, payload: any, enqueueOptions: any) => any;
83
- get: (id: any) => any;
84
- counts: () => any;
85
- claim: (claimOptions: {
86
- kinds: string[];
87
- owner: string;
88
- leaseMs?: number;
89
- }) => any;
90
- complete: (id: any, owner: any, result: any) => any;
91
- fail: (id: any, owner: any, error: any, workerDefaults: any) => any;
92
- checkpointsFor: (job: {
93
- id: string;
94
- leaseOwner: string | null;
95
- }) => {
96
- load: (runId: any) => any;
97
- save: (runId: any, nodeId: any, value: any) => any;
98
- complete: (runId: any, result: any) => any;
99
- };
100
- createWorker: (workerOptions: {
101
- handlers: Record<string, Function>;
102
- concurrency?: number;
103
- pollInterval?: number;
104
- leaseMs?: number;
105
- owner?: string;
106
- backoffBase?: number;
107
- backoffCap?: number;
108
- }) => {
109
- stats: () => {
110
- claims: number;
111
- completions: number;
112
- failures: number;
113
- polls: number;
114
- wakes: number;
115
- inFlight: number;
116
- };
117
- start(): /*elided*/ any;
118
- /**
119
- * Stop claiming, signal in-flight handlers to abort, and wait for
120
- * the loops — but only up to `graceMs`. A handler that ignores its
121
- * signal cannot hold the process open; the resolved record says so
122
- * instead, and the lease expiry (§5) lets another worker re-claim.
123
- * A loop the grace period could not drain is cancelled outright:
124
- * when its handler finally settles it exits without another
125
- * claim, store write or poll timer.
126
- * @param {{ graceMs?: number }} [stopOptions]
127
- * @returns {Promise<{ drained: boolean, inFlight: number }>}
128
- */
129
- stop(stopOptions?: {
130
- graceMs?: number;
131
- }): Promise<{
132
- drained: boolean;
133
- inFlight: number;
134
- }>;
135
- };
136
- /** Stop every worker, bounded. Resolves to the per-worker outcome so
137
- * `close()` can report a handler it could not wait out rather than
138
- * hanging on it. */
139
- stopAll: (stopOptions: any) => Promise<any[]>;
140
- };
@@ -1,69 +0,0 @@
1
- /**
2
- * @file The candidate cut of a k-nearest plan: the arithmetic between
3
- * the scores a statement fetched and the row identities the engine
4
- * will decide over. No vector arithmetic lives here — a score is
5
- * `derive.js`'s, through `@jarenjs/core/vector` — and no SQL: the
6
- * fetch of the winners is the dialect's. This is the one place the
7
- * margin is applied and the one place candidate identities are
8
- * batched for it.
9
- */
10
- /**
11
- * The inclusive score margin of the cut. The column's score is a dot
12
- * product over binary32-normalized forms; the engine's key is the
13
- * cosine of the raw doubles; measured, the two differ by at most
14
- * ~1e-8. Any margin of at least twice that makes the engine's top
15
- * `offset + limit` a SUBSET of the candidates: were a row the engine
16
- * ranks inside the window cut, some candidate the engine ranks outside
17
- * it would have to score higher by the column and lower by the engine,
18
- * which two scores within half the margin of each other cannot do.
19
- * This is a hundred times that bound — it admits, in practice, only
20
- * true ties, and those the engine breaks by the document's own keys.
21
- */
22
- export declare const KNN_MARGIN = 0.000001;
23
- /**
24
- * The most identities one fetch statement binds: under the parameter
25
- * cap of every SQLite build the store runs on. A guard, not a design —
26
- * a k-nearest window is a handful of rows, and this only matters for a
27
- * collection of many exact duplicates.
28
- */
29
- export declare const IDENTITY_CHUNK = 512;
30
- /**
31
- * The rows a k-nearest window can contain, from the scored fetch.
32
- *
33
- * `m = offset + limit` rows are needed. When at least `m` rows scored,
34
- * the candidates are every scored row within `margin` of the m-th best
35
- * score — ties at the boundary included by construction. When fewer
36
- * did, the window reaches the unrankable tail (NULL columns, or a
37
- * collection smaller than the window), which only the documents can
38
- * order: every row is then a candidate, and the collection is no
39
- * larger than the window.
40
- * @param {{ identity: any, score: number | null }[]} rows - one per
41
- * fetched row; `score` is `null` where the column held no vector
42
- * @param {number} m - `offset + limit`
43
- * @param {number} margin
44
- * @returns {{ identities: any[], scored: number, full: boolean }} the
45
- * candidate identities in ascending identity order — the
46
- * collection's own order, which a stable sort over the candidates
47
- * must see — with how many rows scored and whether every row was
48
- * taken
49
- */
50
- export declare function cutCandidates(rows: {
51
- identity: any;
52
- score: number | null;
53
- }[], m: number, margin: number): {
54
- identities: any[];
55
- scored: number;
56
- full: boolean;
57
- };
58
- /**
59
- * The identities sliced into fetch batches: at most `IDENTITY_CHUNK`
60
- * each, and each padded with `null` to the next power of two — a NULL
61
- * in an IN list matches no row — so a handful of prepared statements
62
- * serve every candidate count instead of one per count seen.
63
- * @param {any[]} identities - in the order they should be fetched
64
- * @returns {{ size: number, params: any[] }[]}
65
- */
66
- export declare function identityBatches(identities: any[]): {
67
- size: number;
68
- params: any[];
69
- }[];
@@ -1,62 +0,0 @@
1
- /**
2
- * @file Live queries (LIVE-FORMAT §§7–12): a registered query document
3
- * whose result is maintained as capture records arrive, emitting
4
- * RFC 6902 patches against its own `{ rows }` result document.
5
- *
6
- * The CLASSIFIER implements §7's normative table and nothing more —
7
- * it unwraps the one-element array pack and literal `$subsequence`
8
- * windows the same way the planner does, then reads the compiled
9
- * plan: translated filters, order terms and aggregates are exactly
10
- * the planner's, never a re-implementation. Everything outside the
11
- * table re-runs on invalidation with the reason named (`live.mode`).
12
- *
13
- * Maintenance is synchronous inside capture delivery (§8): inserts
14
- * carry their document in the patch, updates point-read the touched
15
- * row, deletes are answered from maintained state. Per-row semantics
16
- * reuse the ENGINE via packed one-row compilation (the residual
17
- * discipline) — a live row evaluates exactly as the query would.
18
- */
19
- /** The store-level live bounds and their defaults (§12: printed,
20
- * never silent). */
21
- export declare const LIVE_DEFAULTS: Readonly<{
22
- maxQueries: 64;
23
- maxMaintained: 10000;
24
- }>;
25
- /**
26
- * Classify a collection query document against §7's table. Pure —
27
- * given the document and the collection's planner shape, returns the
28
- * strategy description, or a re-run description with the reason.
29
- * @param {any} document
30
- * @param {any} queryShape - the planner shape (collection, schema,
31
- * columnByCanonical)
32
- * @param {boolean} keyed - whether documents carry their key (a
33
- * declared key pointer); unkeyed rows cannot be tracked by key
34
- * @returns {any}
35
- */
36
- export declare function classifyLiveQuery(document: any, queryShape: any, keyed: boolean): any;
37
- /**
38
- * Diff two row arrays into sequential add/remove/replace ops under
39
- * `/rows`, relying on REFERENCE identity for unchanged rows (the §9
40
- * sharing contract makes identity the equality that matters). A
41
- * working copy is replayed op by op, so the emitted patch transforms
42
- * the old array into the new one BY CONSTRUCTION; a remove re-filled
43
- * at the same index merges into a replace.
44
- * @param {any[]} oldRows
45
- * @param {any[]} newRows
46
- * @returns {any[]} ops
47
- */
48
- export declare function diffRows(oldRows: any[], newRows: any[]): any[];
49
- /**
50
- * The store-level live-query registry: registration against the §12
51
- * bounds, capture-record delivery in commit order, lifecycle.
52
- * @param {{ maxQueries: number, maxMaintained: number }} bounds
53
- */
54
- export declare function createLiveRegistry(bounds: {
55
- maxQueries: number;
56
- maxMaintained: number;
57
- }): {
58
- register: (definition: any) => any;
59
- count: () => number;
60
- deliver(record: any): void;
61
- closeAll(): void;
62
- };