@jarenjs/db 0.49.2 → 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 (70) hide show
  1. package/ARCHITECTURE.md +27 -15
  2. package/README.md +141 -41
  3. package/docs/JOBS-FORMAT.md +24 -8
  4. package/docs/LIVE-FORMAT.md +38 -9
  5. package/docs/MIGRATION-FORMAT.md +118 -36
  6. package/docs/MODEL-FORMAT.md +232 -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/capture.js +66 -28
  11. package/src/cli.js +225 -44
  12. package/src/ddl.js +23 -3
  13. package/src/dialects/sqlite.js +2 -1
  14. package/src/driver.js +63 -16
  15. package/src/drivers/wasm.js +1 -0
  16. package/src/emit-model.js +14 -0
  17. package/src/emit.js +10 -3
  18. package/src/entity.js +92 -47
  19. package/src/errors.js +25 -0
  20. package/src/index.js +2 -2
  21. package/src/jobs.js +40 -5
  22. package/src/live-time.js +12 -3
  23. package/src/live.js +11 -1
  24. package/src/migrate.js +397 -191
  25. package/src/model.js +173 -8
  26. package/src/plan.js +135 -38
  27. package/src/query.js +138 -13
  28. package/src/store.js +221 -66
  29. package/src/tracker.js +173 -48
  30. package/types/index.d.ts +152 -10
  31. package/types/node.d.ts +3 -1
  32. package/types/typed.d.ts +58 -2
  33. package/types/wasm.d.ts +7 -0
  34. package/dist/types/algebra.d.ts +0 -230
  35. package/dist/types/app.d.ts +0 -49
  36. package/dist/types/capture.d.ts +0 -85
  37. package/dist/types/cli.d.ts +0 -2
  38. package/dist/types/dag-job.d.ts +0 -40
  39. package/dist/types/ddl.d.ts +0 -229
  40. package/dist/types/derive.d.ts +0 -250
  41. package/dist/types/dialect.d.ts +0 -154
  42. package/dist/types/dialects/sqlite.d.ts +0 -9
  43. package/dist/types/driver.d.ts +0 -110
  44. package/dist/types/drivers/bun.d.ts +0 -47
  45. package/dist/types/drivers/node.d.ts +0 -37
  46. package/dist/types/drivers/wasm.d.ts +0 -65
  47. package/dist/types/emit-model.d.ts +0 -44
  48. package/dist/types/emit.d.ts +0 -75
  49. package/dist/types/entity.d.ts +0 -23
  50. package/dist/types/errors.d.ts +0 -170
  51. package/dist/types/graph.d.ts +0 -28
  52. package/dist/types/index.d.ts +0 -37
  53. package/dist/types/jobs.d.ts +0 -140
  54. package/dist/types/knn.d.ts +0 -69
  55. package/dist/types/live-time.d.ts +0 -141
  56. package/dist/types/live.d.ts +0 -64
  57. package/dist/types/migrate.d.ts +0 -170
  58. package/dist/types/model.d.ts +0 -36
  59. package/dist/types/patch-sql.d.ts +0 -37
  60. package/dist/types/plan.d.ts +0 -142
  61. package/dist/types/profile.d.ts +0 -80
  62. package/dist/types/query.d.ts +0 -112
  63. package/dist/types/residual.d.ts +0 -64
  64. package/dist/types/series.d.ts +0 -227
  65. package/dist/types/store.d.ts +0 -60
  66. package/dist/types/tracker.d.ts +0 -43
  67. package/dist/types/typed.d.ts +0 -15
  68. package/dist/types/types.d.ts +0 -26
  69. package/dist/types/udf.d.ts +0 -75
  70. package/dist/types/window.d.ts +0 -52
package/types/typed.d.ts CHANGED
@@ -15,6 +15,8 @@
15
15
  import type {
16
16
  EntityKeyArg, LoadExplanation, SaveReport, Store, StoreCapabilities,
17
17
  StoreStats, Collection, ExecuteOptions, SequenceResult, ValueOrPromise,
18
+ Dialect, ChangeRecord, LiveOptions, LiveQuery, JobsApi, SyncStore,
19
+ EntityScope, RelationEntry, RelationTable,
18
20
  } from '@jarenjs/db';
19
21
 
20
22
  /** The self-referential constraint an interface can satisfy: generated
@@ -36,9 +38,14 @@ export type TypedInclude<E extends MetaMap<E>, M extends EntityMeta> = {
36
38
  readonly [K in keyof M['relations']]?:
37
39
  true
38
40
  | { count: true }
39
- | TypedLoadSpec<E, E[M['relations'][K]['entity'] & keyof E]>;
41
+ | TypedIncludeSpec<E, E[M['relations'][K]['entity'] & keyof E]>;
40
42
  };
41
43
 
44
+ /** An include's clauses: the root's without `after` (a keyset cursor
45
+ * paginates the root alone; an include windows with `skip`/`take`). */
46
+ export type TypedIncludeSpec<E extends MetaMap<E>, M extends EntityMeta> =
47
+ TypedLoadSpecBase & { include?: TypedInclude<E, M> };
48
+
42
49
  export interface TypedLoadSpecBase {
43
50
  /** A query expression over `$it` — its format is the runtime's. */
44
51
  where?: unknown;
@@ -68,15 +75,31 @@ export type Loaded<
68
75
  : Loaded<E, E[M['relations'][K]['entity'] & keyof E], I[K]> | null;
69
76
  } : NonNullable<unknown>);
70
77
 
78
+ /** The relation members `link`/`unlink` take: the many-to-many ones —
79
+ * exactly the relation members the generated INPUT type also carries,
80
+ * since a membership array is writable where a projection is not. */
81
+ export type MembershipMember<M extends EntityMeta> =
82
+ keyof M['relations'] & keyof M['input'] & string;
83
+
84
+ /** What a membership names on the target side: the target's key, or a
85
+ * document carrying it. */
86
+ export type MembershipTarget<E extends MetaMap<E>, M extends EntityMeta, K extends keyof M['relations']> =
87
+ E[M['relations'][K]['entity'] & keyof E]['key'] | M['relations'][K]['doc'];
88
+
71
89
  export interface TypedUntrackedReads<E extends MetaMap<E>, M extends EntityMeta> {
72
90
  get(key: EntityKeyArg): Promise<M['doc'] | undefined>;
73
91
  load<const S extends TypedLoadSpec<E, M>>(spec?: S): Promise<Array<Loaded<E, M, S>>>;
74
92
  }
75
93
 
76
94
  export interface TypedEntitySet<E extends MetaMap<E>, M extends EntityMeta> {
95
+ /** The provider phantom: `from(typed.entity('User'))` infers `User`
96
+ * without a cast. */
97
+ readonly __item?: M['doc'];
77
98
  create(doc: M['input']): Promise<Readonly<M['doc']>>;
78
99
  get(key: EntityKeyArg): Promise<Readonly<M['doc']> | undefined>;
79
- update(key: EntityKeyArg, changes: Partial<M['doc']>): Promise<Readonly<M['doc']>>;
100
+ /** A relation member is a projection, never stored state: `update()`
101
+ * refuses it (`JD2003`), and the type does not offer it. */
102
+ update(key: EntityKeyArg, changes: Partial<Omit<M['doc'], keyof M['relations']>>): Promise<Readonly<M['doc']>>;
80
103
  delete(key: EntityKeyArg): Promise<boolean>;
81
104
  load<const S extends TypedLoadSpec<E, M>>(spec?: S):
82
105
  Promise<Array<Readonly<Loaded<E, M, S>>>>;
@@ -85,18 +108,51 @@ export interface TypedEntitySet<E extends MetaMap<E>, M extends EntityMeta> {
85
108
  put(next: M['doc']): Readonly<M['doc']>;
86
109
  remove(key: EntityKeyArg | M['doc']): void;
87
110
  discard(key: EntityKeyArg | M['doc']): void;
111
+ /** Attach / detach one many-to-many membership through the unit of
112
+ * work (MODEL-FORMAT §11.7): `member` is one of the relation members
113
+ * `create`/`add` also take as an array — never a projection — and
114
+ * `target` the target's key or a document carrying it. */
115
+ link<K extends MembershipMember<M>>(own: M['key'] | M['doc'], member: K, target: MembershipTarget<E, M, K>): void;
116
+ unlink<K extends MembershipMember<M>>(own: M['key'] | M['doc'], member: K, target: MembershipTarget<E, M, K>): void;
88
117
  asNoTracking(): TypedUntrackedReads<E, M>;
118
+ /** The provider contract over this entity's root (MODEL-FORMAT §10.1);
119
+ * the answer is the engine's result shape, value-or-promise (D2). */
120
+ execute<R = unknown>(document: unknown, options?: ExecuteOptions): ValueOrPromise<SequenceResult<R>>;
121
+ explain(document: unknown, options?: ExecuteOptions): Promise<unknown>;
122
+ /** The root expression this set's rows are bound through (`$.<Name>[*]`). */
123
+ readonly root: string;
124
+ /** The identity every entity set of one store shares; it carries every
125
+ * root's relation table. */
126
+ readonly scope: EntityScope;
127
+ /** This entity's relation table (MODEL-FORMAT §10.1): exactly the
128
+ * generated metadata's relation members, as the plain rows a query
129
+ * producer lowers a hop from. */
130
+ readonly relations: Readonly<Record<keyof M['relations'] & string, RelationEntry>>;
89
131
  }
90
132
 
133
+ /** The typed store: every member of `Store` (a typed store is the same
134
+ * object, identity at runtime), with the entity sets typed. */
91
135
  export interface TypedStore<E extends MetaMap<E>> {
92
136
  readonly capabilities: StoreCapabilities;
137
+ readonly dialect: Dialect;
93
138
  stats(): StoreStats;
94
139
  collection<T = unknown>(name: string): Collection<T>;
95
140
  entity<K extends keyof E & string>(name: K): TypedEntitySet<E, E[K]>;
96
141
  execute?<R = unknown>(document: unknown, options?: ExecuteOptions): ValueOrPromise<SequenceResult<R>>;
142
+ explain?(document: unknown, options?: ExecuteOptions): Promise<unknown>;
143
+ /** The entity roots this store-level provider serves (present with entities). */
144
+ readonly roots?: readonly (keyof E & string)[];
145
+ /** The relation tables of every entity, keyed by entity name. */
146
+ readonly relations?: Readonly<Record<keyof E & string, RelationTable>>;
97
147
  saveChanges?(): Promise<SaveReport>;
98
148
  transaction<R>(fn: (store: Store) => R | Promise<R>): Promise<Awaited<R>>;
149
+ observe(fn: (record: ChangeRecord) => void): () => void;
150
+ changesSince?(after: number): Promise<ChangeRecord[]>;
151
+ dataVersion(): Promise<number>;
152
+ live?(document: unknown, options?: LiveOptions): Promise<LiveQuery>;
99
153
  close(options?: { graceMs?: number }): Promise<void>;
154
+ readonly jobs?: JobsApi;
155
+ readonly sync?: SyncStore;
100
156
  }
101
157
 
102
158
  /**
package/types/wasm.d.ts CHANGED
@@ -3,3 +3,10 @@ import type { Driver } from '@jarenjs/db';
3
3
 
4
4
  /** A driver over an injected wasm SQLite handle (possibly async). */
5
5
  export declare function wasmDriver(handle: unknown): Driver;
6
+ /** Adapt an `sqlite3.oo1.DB`-shaped database (a loaded sqlite3 module
7
+ * and one of its database objects) to the raw connection contract. */
8
+ export declare function adaptOo1Database(sqlite3: unknown, db: unknown): unknown;
9
+ /** Build the injected HANDLE for `wasmDriver` from a loaded sqlite3
10
+ * module: `DbClass` picks the database class (default `sqlite3.oo1.DB`;
11
+ * the SAH-pool util's `OpfsSAHPoolDb` for OPFS persistence). */
12
+ export declare function sqlite3Handle(sqlite3: unknown, options?: { DbClass?: unknown }): unknown;
@@ -1,230 +0,0 @@
1
- /**
2
- * @file The Plan algebra: the dialect-neutral middle stage between the
3
- * engine's AST and a dialect's SQL. A plan is a plain JSON value —
4
- * inspectable, golden-testable without a database — and it carries NO
5
- * SQL text: every string in a plan is a member name, a type tag, an
6
- * external name or a reason sentence, never a fragment of any query
7
- * language. `assertNoSqlText` is the tripwire the tests run over every
8
- * golden.
9
- *
10
- * One plan shape covers this version: a guarded selection over ONE
11
- * collection with optional ordering, window, aggregate and a
12
- * whole-document projection — or, instead of an ordering and a window,
13
- * a k-nearest RANK the engine finishes over the rows the plan fetches,
14
- * or, instead of a projection, a fixed-width temporal BUCKET the plan
15
- * groups and aggregates itself. Constructs beyond it are residuals by
16
- * design (see ARCHITECTURE.md's deliberate-residual table).
17
- */
18
- /** The plan format version, carried on every plan. */
19
- export declare const PLAN_VERSION = 2;
20
- export type PlanRef = {
21
- segments: ({
22
- name: string;
23
- } | {
24
- index: number;
25
- })[];
26
- type: string;
27
- column: string | null;
28
- };
29
- export type PlanOperand = {
30
- lit: unknown;
31
- } | {
32
- ext: string;
33
- };
34
- export type PlanPredicate = ({
35
- p: 'and' | 'or';
36
- items: PlanPredicate[];
37
- } | {
38
- p: 'not';
39
- item: PlanPredicate;
40
- } | {
41
- p: 'cmp';
42
- op: 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge';
43
- ref: PlanRef;
44
- operand: PlanOperand;
45
- } | {
46
- p: 'typeIs';
47
- ref: PlanRef;
48
- types: string[];
49
- positive: boolean;
50
- } | {
51
- p: 'strop';
52
- kind: 'starts' | 'ends' | 'contains';
53
- ref: PlanRef;
54
- operand: PlanOperand;
55
- } | {
56
- p: 'const';
57
- value: boolean;
58
- } | {
59
- p: 'udf';
60
- name: string;
61
- key: string;
62
- } | {
63
- p: 'bboxOverlap';
64
- columns: {
65
- w: string;
66
- s: string;
67
- e: string;
68
- n: string;
69
- };
70
- probe: {
71
- box: number[];
72
- } | {
73
- ext: string;
74
- };
75
- } | {
76
- p: 'cellIn';
77
- column: string;
78
- cells: string[];
79
- } | {
80
- p: 'cellPrefix';
81
- column: string;
82
- prefix: string;
83
- });
84
- export type PlanOrderTerm = {
85
- ref: PlanRef;
86
- desc: boolean;
87
- emptyGreatest: boolean;
88
- };
89
- export type PlanBucket = {
90
- ref: PlanRef;
91
- every: number;
92
- origin: number;
93
- as: string;
94
- order: 'asc' | 'desc' | 'first-seen';
95
- aggregates: {
96
- fn: 'rows' | 'sum' | 'avg' | 'min' | 'max';
97
- ref: PlanRef | null;
98
- as: string;
99
- empty: 'null' | 'zero' | 'omit';
100
- }[];
101
- };
102
- export type PlanRank = {
103
- column: string;
104
- dims: number;
105
- probe: {
106
- lit: number[];
107
- } | {
108
- ext: string;
109
- };
110
- offset: number;
111
- limit: number;
112
- margin: number;
113
- };
114
- export type Plan = {
115
- planVersion: number;
116
- alg: 'select';
117
- collection: string;
118
- filter: PlanPredicate | null;
119
- order: PlanOrderTerm[] | null;
120
- window: {
121
- offset: number;
122
- limit: number | null;
123
- } | null;
124
- rank: PlanRank | null;
125
- bucket: PlanBucket | null;
126
- aggregate: {
127
- fn: 'count' | 'sum' | 'avg' | 'min' | 'max';
128
- ref: PlanRef | null;
129
- } | null;
130
- project: 'document';
131
- };
132
- /**
133
- * @typedef {{ segments: ({ name: string } | { index: number })[],
134
- * type: string, column: string | null }} PlanRef
135
- * A typed reference into the stored document: `type` is the
136
- * schema-declared type or `'unknown'`; `column` is the generated
137
- * column name when the collection indexes this path.
138
- *
139
- * @typedef {{ lit: unknown } | { ext: string }} PlanOperand
140
- *
141
- * @typedef {(
142
- * { p: 'and' | 'or', items: PlanPredicate[] } |
143
- * { p: 'not', item: PlanPredicate } |
144
- * { p: 'cmp', op: 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge',
145
- * ref: PlanRef, operand: PlanOperand } |
146
- * { p: 'typeIs', ref: PlanRef, types: string[], positive: boolean } |
147
- * { p: 'strop', kind: 'starts' | 'ends' | 'contains',
148
- * ref: PlanRef, operand: PlanOperand } |
149
- * { p: 'const', value: boolean } |
150
- * { p: 'udf', name: string, key: string } |
151
- * { p: 'bboxOverlap', columns: { w: string, s: string, e: string,
152
- * n: string }, probe: { box: number[] } | { ext: string } } |
153
- * { p: 'cellIn', column: string, cells: string[] } |
154
- * { p: 'cellPrefix', column: string, prefix: string }
155
- * )} PlanPredicate
156
- * The last three are the SPATIAL forms: predicates over the derived
157
- * index columns a model declares, which a spatial conjunct either
158
- * translates to exactly or is proven to IMPLY. `bboxOverlap` is true
159
- * when the row's stored box meets the probe's (touching edges count,
160
- * as the kernel's `bboxIntersects` does); `cellIn` when the row's
161
- * cell is one of the listed ones (the nine-cell neighbourhood, or a
162
- * single whole cell); `cellPrefix` when it begins with a shorter one.
163
- * None carries a `json_type` guard — the derived column IS the value
164
- * — but each is TOTAL through its own `IS NOT NULL`, so a row with no
165
- * box or no cell answers FALSE rather than SQL's NULL and negation
166
- * still composes classically.
167
- *
168
- * @typedef {{ ref: PlanRef, desc: boolean, emptyGreatest: boolean }} PlanOrderTerm
169
- *
170
- * @typedef {{ ref: PlanRef, every: number, origin: number, as: string,
171
- * order: 'asc' | 'desc' | 'first-seen',
172
- * aggregates: { fn: 'rows' | 'sum' | 'avg' | 'min' | 'max',
173
- * ref: PlanRef | null, as: string,
174
- * empty: 'null' | 'zero' | 'omit' }[] }} PlanBucket
175
- * The fixed-width temporal GROUP BY: the instant column, the ladder's
176
- * width and anchor in epoch milliseconds, the name the bucket's start
177
- * is answered under, how the groups are ordered, and one aggregate
178
- * per answered member. `rows` is `COUNT(*)` — the D5 count of SOURCE
179
- * rows, duplicates and measured gaps included — and the four value
180
- * aggregates skip a `NULL` reading exactly as the kernel skips a
181
- * `null` one. `first-seen` order is the group's earliest row identity,
182
- * which is the engine's own "order of first appearance" (§6.5).
183
- * A plan carrying a bucket carries no `aggregate` and no `rank`.
184
- *
185
- * @typedef {{ column: string, dims: number,
186
- * probe: { lit: number[] } | { ext: string },
187
- * offset: number, limit: number, margin: number }} PlanRank
188
- * The k-nearest stage: the packed vector column the ranking reads,
189
- * its declared width, the probe (a plan-time literal vector, or the
190
- * external that carries one at call time), the window the ENGINE
191
- * will apply, and the inclusive score margin of the candidate cut.
192
- * The column cuts — every row whose column score is within `margin`
193
- * of the `offset + limit`-th best is a candidate — and the engine
194
- * decides: the original document, its whole ordering and window
195
- * included, runs over the candidates' documents. A plan carrying a
196
- * rank carries no order and no window of its own: nothing in SQL
197
- * orders or limits the fetch.
198
- *
199
- * @typedef {{
200
- * planVersion: number,
201
- * alg: 'select',
202
- * collection: string,
203
- * filter: PlanPredicate | null,
204
- * order: PlanOrderTerm[] | null,
205
- * window: { offset: number, limit: number | null } | null,
206
- * rank: PlanRank | null,
207
- * bucket: PlanBucket | null,
208
- * aggregate: { fn: 'count' | 'sum' | 'avg' | 'min' | 'max',
209
- * ref: PlanRef | null } | null,
210
- * project: 'document',
211
- * }} Plan
212
- */
213
- /**
214
- * A fresh select plan over one collection.
215
- * @param {string} collection
216
- * @returns {Plan}
217
- */
218
- export declare function selectPlan(collection: string): Plan;
219
- /**
220
- * Conjoin a predicate onto a plan's filter.
221
- * @param {PlanPredicate | null} filter
222
- * @param {PlanPredicate} predicate
223
- * @returns {PlanPredicate}
224
- */
225
- export declare function conjoin(filter: PlanPredicate | null, predicate: PlanPredicate): PlanPredicate;
226
- /**
227
- * Throw when a plan value carries anything that smells like SQL.
228
- * @param {unknown} plan
229
- */
230
- export declare function assertNoSqlText(plan: unknown): void;
@@ -1,49 +0,0 @@
1
- /**
2
- * @file The app binding for live queries (LIVE-FORMAT §10): GENERATED
3
- * documents plus a handler factory — the `fsmToApp` precedent. The db
4
- * package never imports `@jarenjs/app`; the app document declares a
5
- * subscription (`APP-FORMAT §5.3`) whose registered handler is
6
- * `createLiveSubscription(store)`, and a two-line action whose whole
7
- * body is `{ patch: '$payload' }` — the handler prefixes every op
8
- * with the declared state path, so the app loop applies live patches
9
- * with the machinery it already has.
10
- */
11
- /**
12
- * Prefix every op path in a live patch with the state slot.
13
- * @param {any[]} patch
14
- * @param {string} statePath - JSON Pointer to the slot holding the
15
- * live result document
16
- */
17
- export declare function prefixLivePatch(patch: any[], statePath: string): any[];
18
- /**
19
- * The generated documents (§10): a subscription entry and the
20
- * patch-forwarding action, both plain data for the app document.
21
- * @param {{ run?: string, action?: string, statePath: string,
22
- * collection?: string, query: any, externals?: any, mode?: string,
23
- * when?: any }} options
24
- * @returns {{ subscription: any, actions: any }}
25
- */
26
- export declare function liveAppBinding(options: {
27
- run?: string;
28
- action?: string;
29
- statePath: string;
30
- collection?: string;
31
- query: any;
32
- externals?: any;
33
- mode?: string;
34
- when?: any;
35
- }): {
36
- subscription: any;
37
- actions: any;
38
- };
39
- /**
40
- * The subscription handler factory: registers the live query when the
41
- * subscription starts, dispatches ONE initializing patch (a `replace`
42
- * of the whole slot), forwards each emission prefixed, and closes on
43
- * cleanup. An emission error surfaces as a dispatch of
44
- * `<action>/error` so the app can render it — silence is not an
45
- * option the format allows.
46
- * @param {any} store - an open store with capture
47
- * @returns {(props: any, dispatch: Function) => Function}
48
- */
49
- export declare function createLiveSubscription(store: any): (props: any, dispatch: Function) => Function;
@@ -1,85 +0,0 @@
1
- /**
2
- * @file Change capture (D13): committed writes become an observable,
3
- * ordered stream of RFC 6902 patches — derived from SQLite's own
4
- * session changesets where the binding has them, from a write-path
5
- * journal where it does not (`bun:sqlite` has no `createSession`), or
6
- * off entirely. One diff format then runs end to end: store → patch →
7
- * live query → patch → O(k) render.
8
- *
9
- * The pointer contract (LIVE-FORMAT §2): `/<table>/<key>/<path…>`,
10
- * every token escaped per RFC 6901. A single key renders as its
11
- * scalar text (integers in decimal); a composite key renders as the
12
- * JSON text of its parts array. Join-table rows are tiny documents
13
- * under the join table's name — membership changes are part of the
14
- * stream, not a blind spot.
15
- *
16
- * Session facts this file is built on (probed, 3.51.2):
17
- * - a changeset carries ONE NET OP PER ROW (insert+update coalesce;
18
- * insert+delete vanish; a no-op update is absent), and within-table
19
- * order is NOT statement order — every row op targets a distinct
20
- * pointer, so application order across rows cannot matter;
21
- * - `ROLLBACK TO` a savepoint removes the undone rows from the
22
- * session (pinned by test — the classic caveat does NOT hold here);
23
- * - a rolled-back transaction yields an empty changeset;
24
- * - virtual generated columns are invisible;
25
- * - an UPDATE's old record carries the primary key and the CHANGED
26
- * columns only — which is exactly enough for property-level ops,
27
- * and why the doc column's old/new blobs make a minimal nested
28
- * diff possible (`SELECT json(?)` turns JSONB back into text).
29
- */
30
- /** The persisted change log (LIVE-FORMAT §5). */
31
- export declare const CHANGES_TABLE = "_jaren_changes";
32
- export declare const DEFAULT_RETENTION = 1000;
33
- /**
34
- * Decode a binary changeset into row operations.
35
- * @param {Uint8Array} bytes
36
- * @returns {{ table: string, pk: boolean[], op: 'insert'|'update'|'delete',
37
- * indirect: boolean, oldValues: any[] | null, newValues: any[] | null }[]}
38
- */
39
- export declare function parseChangeset(bytes: Uint8Array): {
40
- table: string;
41
- pk: boolean[];
42
- op: 'insert' | 'update' | 'delete';
43
- indirect: boolean;
44
- oldValues: any[] | null;
45
- newValues: any[] | null;
46
- }[];
47
- /**
48
- * The key token (LIVE-FORMAT §2): a single key is its scalar text;
49
- * a composite key is the JSON text of its parts array.
50
- * @param {any[]} parts
51
- * @returns {string}
52
- */
53
- export declare function keyToken(parts: any[]): string;
54
- export type TableShape = {
55
- kind: 'collection' | 'entity' | 'join';
56
- columns: {
57
- name: string;
58
- role: 'key' | 'doc' | 'scalar' | 'fk' | 'epoch';
59
- storage?: string;
60
- }[];
61
- keyIndexes: number[];
62
- docIndex: number;
63
- };
64
- /**
65
- * Translate parsed row operations into RFC 6902 ops, resolving JSONB
66
- * blobs through the connection (`SELECT json(?)`).
67
- * @param {any} connection
68
- * @param {Map<string, TableShape>} shapes
69
- * @param {any[]} operations
70
- * @returns {any} value-or-promise of RFC 6902 ops
71
- */
72
- export declare function translateOperations(connection: any, shapes: Map<string, TableShape>, operations: any[]): any;
73
- /**
74
- * @param {{ connection: any, shapes: Map<string, TableShape>,
75
- * mode: 'session' | 'journal',
76
- * log: boolean, retention: number }} options
77
- * @returns {any}
78
- */
79
- export declare function createCaptureEngine(options: {
80
- connection: any;
81
- shapes: Map<string, TableShape>;
82
- mode: 'session' | 'journal';
83
- log: boolean;
84
- retention: number;
85
- }): any;
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,40 +0,0 @@
1
- /**
2
- * @file The composition (JOBS-FORMAT §7): a persisted `@jarenjs/flow`
3
- * DAG run wired to a queue job. THE FLOW ENGINE IS INJECTED, NEVER
4
- * IMPORTED — the shared invariant forbids `@jarenjs/db` importing
5
- * `@jarenjs/flow`, so `compileDag` arrives as a capability (the D10
6
- * shape applied to flow) and a test asserts the manifest and import
7
- * graph name flow nowhere.
8
- *
9
- * Each kind's document compiles ONCE against a delegating checkpoint
10
- * store; per claimed job, the delegate binds the engine's guarded
11
- * per-job store (`checkpointsFor`) — `save` refuses once the lease is
12
- * lost and `complete` records the DAG result, marks the job done and
13
- * prunes the checkpoint rows in ONE transaction, so a failure leaves
14
- * neither and a crash resumes instead of restarting.
15
- */
16
- /**
17
- * Build a worker whose handlers run checkpointed DAG documents.
18
- * @param {any} store - an open store with `{ jobs: true }`
19
- * @param {{ compileDag: Function,
20
- * documents: Record<string, any>,
21
- * tasks?: Record<string, Function>,
22
- * concurrency?: number, pollInterval?: number, leaseMs?: number,
23
- * owner?: string, backoffBase?: number, backoffCap?: number }} options
24
- * @returns {{ start: () => any, stop: () => Promise<void>, stats: () => any }}
25
- */
26
- export declare function createDagJobRunner(store: any, options: {
27
- compileDag: Function;
28
- documents: Record<string, any>;
29
- tasks?: Record<string, Function>;
30
- concurrency?: number;
31
- pollInterval?: number;
32
- leaseMs?: number;
33
- owner?: string;
34
- backoffBase?: number;
35
- backoffCap?: number;
36
- }): {
37
- start: () => any;
38
- stop: () => Promise<void>;
39
- stats: () => any;
40
- };