@cruba/crubal-postgres 0.0.0-stage → 0.3.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.
@@ -0,0 +1,138 @@
1
+ // state.ts: an owner's state in the tables, read into the engine's State and written back as the diff of
2
+ // two states. The engine's applyEntry is the one meaning of a write; this file only persists its result,
3
+ // so the Postgres store can never apply an entry differently from the memory one.
4
+ import { serialize } from "@cruba/crubal";
5
+ import { emptyState } from "@cruba/crubal";
6
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7
+ /** A record as the engine holds it, from its row. Absent members stay absent. */
8
+ export function recordOf(row) {
9
+ const doc = (row.doc ?? {});
10
+ // The row's owner is the store's; the record's own owner (a hosted person's, another owner's) is in doc.
11
+ const r = { id: String(row.id), kind: String(row.kind), level: String(row.level), owner: String(doc.owner ?? row.owner), values: (doc.values ?? {}) };
12
+ if (row.of)
13
+ r.of = String(row.of);
14
+ if (row.within)
15
+ r.within = String(row.within);
16
+ if (row.status)
17
+ r.status = row.status;
18
+ if (row.applied)
19
+ r.applied = row.applied;
20
+ if (Array.isArray(row.wears) && row.wears.length)
21
+ r.wears = row.wears.map(String);
22
+ if (row.created)
23
+ r.created = String(row.created);
24
+ if (row.updated)
25
+ r.updated = String(row.updated);
26
+ const extra = r;
27
+ for (const m of ["parts", "sources", "alternatives", "suggestions", "held"])
28
+ if (doc[m] !== undefined)
29
+ extra[m] = doc[m];
30
+ return r;
31
+ }
32
+ /** The members of a record that live in `doc`, not in a column. */
33
+ function docOf(r) {
34
+ const x = r;
35
+ const doc = { owner: r.owner, values: r.values };
36
+ for (const m of ["parts", "sources", "alternatives", "suggestions", "held"])
37
+ if (x[m] !== undefined)
38
+ doc[m] = x[m];
39
+ return doc;
40
+ }
41
+ /** Every record up a record's `within` chain (14.2), nearest first. */
42
+ function ancestorsOf(records, r) {
43
+ const out = [];
44
+ const seen = new Set([r.id]);
45
+ let cur = r.within;
46
+ while (cur && !seen.has(cur)) {
47
+ out.push(cur);
48
+ seen.add(cur);
49
+ cur = records.get(cur)?.within;
50
+ }
51
+ return out;
52
+ }
53
+ /** Read an owner's whole state. Order is the order the log made things, by `ord`. */
54
+ export async function loadState(client, owner) {
55
+ const state = emptyState();
56
+ const q = async (sql) => (await client.query(sql, [owner])).rows;
57
+ for (const row of await q("select * from records where owner = $1 order by ord"))
58
+ state.records.set(String(row.id), recordOf(row));
59
+ for (const row of await q("select edge, json from edges where owner = $1 order by ord"))
60
+ state.edges.set(String(row.edge), row.json);
61
+ for (const row of await q("select key, record from bindings where owner = $1 order by ord"))
62
+ state.bindings.set(String(row.key), String(row.record));
63
+ for (const row of await q("select issuer, json from instructions where owner = $1 order by issuer, n")) {
64
+ const issuer = String(row.issuer);
65
+ state.instructions.set(issuer, [...(state.instructions.get(issuer) ?? []), row.json]);
66
+ }
67
+ for (const row of await q("select json from observations where owner = $1 order by ord"))
68
+ state.observations.push(row.json);
69
+ for (const row of await q("select json from review where owner = $1 order by ord"))
70
+ state.reviews.push(row.json);
71
+ for (const row of await q("select run, json from runs where owner = $1 order by ord"))
72
+ state.runs.set(String(row.run), row.json);
73
+ for (const row of await q("select json from installed where owner = $1 order by ord"))
74
+ state.installed.push(row.json);
75
+ for (const row of await q("select json from reads where owner = $1 order by ord"))
76
+ state.reads.push(row.json);
77
+ for (const row of await q("select key, json from definitions where owner = $1 order by ord"))
78
+ state.definitions.set(String(row.key), row.json);
79
+ return state;
80
+ }
81
+ /** Write what changed between two states of one owner: upserts, deletes, and appends to the lists. */
82
+ export async function persist(client, owner, before, after) {
83
+ const changed = (a, b) => serialize((a ?? null)) !== serialize((b ?? null));
84
+ const beforeRecords = new Map(Map.prototype.entries.call(before.records));
85
+ for (const [id, r] of Map.prototype.entries.call(after.records)) {
86
+ const was = beforeRecords.get(id);
87
+ const ancestors = ancestorsOf(after.records, r);
88
+ const wasAncestors = was ? ancestorsOf(beforeRecords, was) : undefined;
89
+ if (was && !changed(was, r) && !changed(wasAncestors, ancestors))
90
+ continue;
91
+ await client.query(`insert into records (owner, id, kind, level, "of", within, ancestors, status, applied, wears, created, updated, doc)
92
+ values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
93
+ on conflict (owner, id) do update set kind = excluded.kind, level = excluded.level, "of" = excluded."of", within = excluded.within,
94
+ ancestors = excluded.ancestors, status = excluded.status, applied = excluded.applied, wears = excluded.wears,
95
+ created = excluded.created, updated = excluded.updated, doc = excluded.doc`, [owner, id, r.kind, r.level, r.of ?? null, r.within ?? null, ancestors, r.status ?? null, r.applied ?? null, r.wears ?? [], r.created ?? null, r.updated ?? null, docOf(r)]);
96
+ }
97
+ // Plain copies: a working map notes every walk and miss, and persisting is neither.
98
+ const plain = (m) => new Map(Map.prototype.entries.call(m));
99
+ const upsertMap = async (table, keyCol, valCol, wasMap, nowMap, asJson = true) => {
100
+ const was = plain(wasMap);
101
+ const now = plain(nowMap);
102
+ for (const [k, v] of now) {
103
+ if (was.has(k) && !changed(was.get(k), v))
104
+ continue;
105
+ await client.query(`insert into ${table} (owner, ${keyCol}, ${valCol}) values ($1, $2, $3) on conflict (owner, ${keyCol}) do update set ${valCol} = excluded.${valCol}`, [owner, k, asJson ? v : String(v)]);
106
+ }
107
+ for (const k of was.keys())
108
+ if (!now.has(k))
109
+ await client.query(`delete from ${table} where owner = $1 and ${keyCol} = $2`, [owner, k]);
110
+ };
111
+ await upsertMap("edges", "edge", "json", before.edges, after.edges);
112
+ await upsertMap("bindings", "key", "record", before.bindings, after.bindings, false);
113
+ await upsertMap("runs", "run", "json", before.runs, after.runs);
114
+ await upsertMap("definitions", "key", "json", before.definitions, after.definitions);
115
+ for (const [issuer, list] of after.instructions) {
116
+ const have = before.instructions.get(issuer)?.length ?? 0;
117
+ for (let i = have; i < list.length; i++) {
118
+ const ins = list[i];
119
+ await client.query("insert into instructions (owner, issuer, n, json) values ($1, $2, $3, $4) on conflict do nothing", [owner, issuer, Number(ins.n ?? i + 1), ins]);
120
+ }
121
+ }
122
+ const appendList = async (table, was, now) => {
123
+ for (const item of now.slice(was.length))
124
+ await client.query(`insert into ${table} (owner, json) values ($1, $2)`, [owner, item]);
125
+ };
126
+ await appendList("observations", before.observations, after.observations);
127
+ await appendList("review", before.reviews, after.reviews);
128
+ await appendList("installed", before.installed, after.installed);
129
+ await appendList("reads", before.reads, after.reads);
130
+ }
131
+ /** Forget an owner's state tables (not its log), before a rebuild. */
132
+ export async function wipeState(client, owner) {
133
+ for (const t of ["records", "edges", "bindings", "instructions", "review", "runs", "observations", "installed", "reads", "definitions"]) {
134
+ await client.query(`delete from ${t} where owner = $1`, [owner]);
135
+ }
136
+ }
137
+ export { isObj };
138
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,269 @@
1
+ import { type Draft, type Entry, type State } from "@cruba/crubal";
2
+ import { type Edge } from "@cruba/crubal";
3
+ import type { View } from "@cruba/crubal";
4
+ import type { Json, JsonObject, Record } from "@cruba/crubal";
5
+ import { verifyChain, type Keys } from "@cruba/crubal";
6
+ import { type Pool } from "./db.ts";
7
+ import { type Parsed } from "@cruba/crubal";
8
+ import { type StepStats } from "@cruba/crubal";
9
+ import { type Decision } from "@cruba/crubal";
10
+ import { type Projecting } from "./projector.ts";
11
+ import { Needs } from "./working.ts";
12
+ import type { StreamEntry } from "@cruba/crubal";
13
+ export type StoreOptions = {
14
+ /**
15
+ * Whether `state()` and `records.all()` may load an owner's whole state. `refuse` is for a host serving
16
+ * requests: nothing on a request path may be proportional to the catalog (brief 18, the scale rule);
17
+ * rebuild, erase and an install's restamp read whole regardless, since they are what they are.
18
+ */
19
+ wholeState?: "allow" | "refuse";
20
+ chain?: string;
21
+ /** Definitions handed in instead of read from the store's installed bundles and define writes (tests). */
22
+ projecting?: Projecting;
23
+ /** Install an unsigned bundle (development only; refused by default, docs/design/publishing.md). */
24
+ allowUnsigned?: boolean;
25
+ /** Sign each entry's hash with the owner's key (8.5): the host holds the key, the store never sees it. */
26
+ signer?: (hash: string) => string;
27
+ /** Publish every view right after each write commits (`fresh: write`); else the host calls `project`. */
28
+ fresh?: "write" | "asked";
29
+ /** The signer for a person this owner hosts (14.1), made on their first write; the host holds the key. */
30
+ hostedSigner?: (owner: string) => Promise<((hash: string) => string) | undefined>;
31
+ };
32
+ export declare class PgStore {
33
+ readonly pool: Pool;
34
+ readonly owner: string;
35
+ readonly chain: string;
36
+ readonly options: StoreOptions;
37
+ constructor(pool: Pool, owner: string, options?: StoreOptions | string);
38
+ /**
39
+ * Publish the views whose rows a write made stale (12.6): every view, or the named one, for its own
40
+ * audience or the one given. Runs outside any write transaction. Returns the stream entries published.
41
+ */
42
+ project(view?: string, audience?: string, only?: (v: View) => boolean): Promise<StreamEntry[]>;
43
+ /**
44
+ * Publish every view whole next time, with a reset on each stream (12.6.2): an index that lost rows, or
45
+ * kept rows nothing removed, is brought back to what the store holds. The named view alone when given.
46
+ */
47
+ republish(view?: string): Promise<void>;
48
+ /** The head of a view's stream for an audience (12.6.2): the last sequence number published, 0 when none. */
49
+ streamHead(view: string, audience?: string): Promise<number>;
50
+ /** The entries of a view's stream after `seq` (12.6.2). */
51
+ entriesSince(view: string, seq: number, audience?: string, limit?: number): Promise<StreamEntry[]>;
52
+ /** After a persist: mark what the entry made stale, inside the same transaction. */
53
+ private afterPersist;
54
+ private freshen;
55
+ /** Set while a bulk load runs: the per-commit publish waits for the one publish at the end. */
56
+ private quiet;
57
+ /** A bulk load that republishes at the end marks nothing stale per batch (load, `stale: false`). */
58
+ private skipStale;
59
+ /**
60
+ * Load many records as the owner's own, in batches: one entry per batch, each record set whole with its
61
+ * `sources` (or the `origin` given), the checks, rules and stale marks of a commit applying to every one;
62
+ * the views publish once at the end. For a conversion or a migration (brief 18 step 1): a million rows
63
+ * in an afternoon, not a day per hundred thousand through arrivals.
64
+ */
65
+ load(records: Record[], by: JsonObject, opts?: {
66
+ batch?: number;
67
+ at?: string;
68
+ origin?: string;
69
+ publish?: boolean;
70
+ stale?: boolean;
71
+ }): Promise<{
72
+ entries: string[];
73
+ records: number;
74
+ published: number;
75
+ }>;
76
+ private defsCache;
77
+ /** The definitions in force: installed bundles in order, then the owner's define writes. Cached per chain head. */
78
+ definitions(fresh?: boolean): Promise<Parsed>;
79
+ /** `fresh` builds a copy to try a change on (a trial reads onto it and must not touch the cache). */
80
+ private definitionsFrom;
81
+ /** What the store projects with: handed in, else built from its definitions. */
82
+ projecting(): Promise<Projecting | undefined>;
83
+ private projectingFrom;
84
+ /**
85
+ * Install a published version (docs/design/publishing.md): verify the hash and signature, require the
86
+ * language version at or below ours and every extended package installed at its pinned version, write
87
+ * the install entry and keep the bundle, restamp through the recipes when an earlier version was
88
+ * installed, and check the definitions that result. Refused whole on any failure.
89
+ */
90
+ install(bundle: JsonObject, keys?: Keys, at?: string): Promise<Entry>;
91
+ /**
92
+ * Mark every row of every view stale: after a reinstall or a rebuild the next publish rebuilds and
93
+ * compares every row and announces what left, and the stream keeps its numbering. (Wiping the stored
94
+ * rows instead, as before 0.1.16, made the next publish a first one, which announces no removals, so an
95
+ * index kept rows nothing ever removed: ME's index held three versions of its shop.)
96
+ */
97
+ private staleAll;
98
+ /** Well-formed access (14.12) on what an entry changed: ownership, wearing, within, and every edge written. */
99
+ /**
100
+ * What the rules produce for the records an operation changed, and for their ancestors (6.4): a plain
101
+ * write runs the productions as a border run does, so a rollup over units follows a unit written by the
102
+ * owner's own commit. Derived values land in the draft with their rule as source, so the entry's writes
103
+ * carry them like any other value; a person's value is never overwritten.
104
+ */
105
+ /** Records a `rederive` asks for, derived whether or not the operation changed them. */
106
+ private alsoDerive?;
107
+ /**
108
+ * Run the productions over records as they stand (6.4), for records written before a rule existed: one
109
+ * entry per call, holding only the derived values that changed. The ids are loaded into the working set
110
+ * and derived with their ancestors; nothing else is written.
111
+ */
112
+ rederive(ids: string[], by?: JsonObject, at?: string): Promise<Entry | null>;
113
+ private derive;
114
+ private checkWrites;
115
+ /** One record by id, from its row alone (no whole state): undefined when there is none. */
116
+ get(id: string): Promise<Record | undefined>;
117
+ /** The review items still open (9.5). */
118
+ review(): Promise<JsonObject[]>;
119
+ /**
120
+ * A review decision (9.5, 11.9, 14.7, runs.md): one entry holding the review write and what it causes;
121
+ * then the intakes the decision affects are replayed (9.6), each its own entry.
122
+ */
123
+ decide(d: Decision, by?: JsonObject, at?: string): Promise<{
124
+ entry: Entry | null;
125
+ replayed: string[];
126
+ }>;
127
+ /** Replay the kept intakes a decision reaches: by facet, by contract field, or by unit spelling (9.6). */
128
+ private replayFor;
129
+ /**
130
+ * Drain (docs/design/store.md, completion.md P): apply pending instructions to the records that have
131
+ * them, at most `rate` records per call, each its own entry `by: migration: drain`; the ids done and
132
+ * how many still wait.
133
+ */
134
+ drain(rate?: number, at?: string): Promise<{
135
+ applied: string[];
136
+ remaining: number;
137
+ }>;
138
+ /** What installing a version would restamp (10.4): per recipe and step, how many values change and how many go to review. */
139
+ dryRun(bundle: JsonObject): Promise<{
140
+ recipes: {
141
+ from: string;
142
+ to: string;
143
+ steps: StepStats[];
144
+ review: number;
145
+ }[];
146
+ records: number;
147
+ }>;
148
+ /** A define write (8.5, 11.4): one member of the working package as it now stands, checked with the rest first. */
149
+ define(member: JsonObject, by?: JsonObject, at?: string): Promise<Entry | null>;
150
+ /**
151
+ * The owner's state as the tables hold it, kept per chain head: the same head answers from memory; a
152
+ * head a little further on replays the entries since onto a copy (the engine's own replay), so a write
153
+ * never costs a reload of every record; a gap over 200 entries, or an entry the replay can't take,
154
+ * reads the tables whole. A commit works on a copy of the loaded state, so a kept state is never shared
155
+ * with a draft. (G6: Cashierfu carried this around the store until 0.1.15.)
156
+ */
157
+ state(): Promise<State>;
158
+ /**
159
+ * The whole state regardless of the option: for a rebuild, an erase, an install's restamp, an export and a
160
+ * test's assertions. Never on a request path: `state()` is the guarded door, this one says what it is.
161
+ */
162
+ wholeState(): Promise<State>;
163
+ /**
164
+ * A read on a working set: `fn` runs on a state whose records, bindings and runs load as it asks for them
165
+ * (by id, level, parent, target or key), and runs again until it asked for nothing new; edges,
166
+ * instructions and the working package are loaded whole (they are small). The lists (reviews,
167
+ * observations, installed, reads) are not loaded here: the readers below serve them. `fn` MUST be pure.
168
+ */
169
+ read<T>(fn: (s: State) => T, opts?: {
170
+ allowAll?: boolean;
171
+ }): Promise<T>;
172
+ /** Records by level, kind, parent, placement or one key's value, in log order; paged by `ord`. */
173
+ records(where?: {
174
+ level?: string;
175
+ kind?: string;
176
+ of?: string;
177
+ within?: string;
178
+ key?: [string, Json];
179
+ }, opts?: {
180
+ limit?: number;
181
+ after?: number;
182
+ archived?: boolean;
183
+ }): Promise<Record[]>;
184
+ /** Every edge of the owner (small: people and their grants). */
185
+ edges(): Promise<Edge[]>;
186
+ /** The install log: every version taken, in order. */
187
+ installed(): Promise<JsonObject[]>;
188
+ /** The record a binding (`contract|ref`) names, if any. */
189
+ binding(key: string): Promise<string | undefined>;
190
+ /** The owner's working package (11.4): `<member>:<name>` → the definition as it now stands. */
191
+ defines(): Promise<Map<string, JsonObject>>;
192
+ /** Every review item ever written, decided or not, in order (9.5); `review()` is the open ones. */
193
+ reviews(): Promise<JsonObject[]>;
194
+ /** The records several bindings name, by key: one query for an import's references. */
195
+ bindings(keys: string[]): Promise<Map<string, string>>;
196
+ /** The log of reads under an emergency edge (14.13), in order. */
197
+ emergencyReads(): Promise<JsonObject[]>;
198
+ /** The observations on one record, or on or supporting any of several (9.7). */
199
+ observationsOn(id: string | string[]): Promise<JsonObject[]>;
200
+ /** Observations by what they are on or support, their evidence type and their address (9.7); never all of them unasked. */
201
+ observations(where: {
202
+ on?: string | string[];
203
+ evidenceType?: string;
204
+ address?: string;
205
+ }): Promise<JsonObject[]>;
206
+ private keptState;
207
+ /** Forget the kept state: after a rewrite of history that moves no head (an erase, a rebuild). */
208
+ forgetState(): void;
209
+ /** The owner's entries, in chain order. */
210
+ entries(): Promise<Entry[]>;
211
+ /** Lock the chain for this transaction and return its head. Writers to one chain serialize here. */
212
+ private lockChain;
213
+ /** Link an entry to the chain's head, insert it, and move the head. The entry gets its `prev`. */
214
+ private link;
215
+ /**
216
+ * A hosted person's records live in their own chain (14.1): the writes to records owned `<owner>:<id>`
217
+ * leave the owner's entry and become one entry per person, linked and signed in the same transaction.
218
+ * The chains lock in a fixed order (the owner's first, then the persons' by name).
219
+ */
220
+ private linkSplit;
221
+ /**
222
+ * Append an entry made elsewhere (a rebuilt log, a hosted person's chain the host carries): it is
223
+ * linked to this chain's head, applied with the engine's applyEntry, and its effects persisted.
224
+ */
225
+ append(entry: Entry): Promise<Entry>;
226
+ /** An empty working state with the whole small tables in it; records, bindings and runs load as needed. */
227
+ private base;
228
+ /** What the last operation loaded, for tests and for watching a store's cost. */
229
+ trace: Needs | undefined;
230
+ /** Set while an install restamps: its commit walks the package's records by design, whatever `wholeState` says. */
231
+ private walking;
232
+ /**
233
+ * One operation, one entry (8.5): the engine runs on a draft of the state, what it changed becomes the
234
+ * entry's writes (plus `extra` writes the diff can't see), and the entry is applied and persisted in the
235
+ * same transaction. Nothing happens when nothing changed; a throw inside `mutate` rolls everything back.
236
+ */
237
+ commit(by: JsonObject, mutate: (draft: Draft) => void, at?: string, extra?: JsonObject[]): Promise<Entry | null>;
238
+ /**
239
+ * The same, for an operation that decides its own cause and extra writes as it runs (a border run): it
240
+ * returns `by`, `extra` and an outcome, and the outcome comes back beside the entry.
241
+ */
242
+ commitFrom<T>(run: (draft: Draft) => {
243
+ by: JsonObject;
244
+ extra?: JsonObject[];
245
+ out: T;
246
+ }, at?: string, mark?: {
247
+ witnessed?: true;
248
+ }): Promise<{
249
+ entry: Entry | null;
250
+ out: T;
251
+ }>;
252
+ /**
253
+ * Erase a record's values from the log (8.5.2): every value write to it, and every observation on it, in
254
+ * every entry, is redacted in place; creations, envelopes and everything else stay. The state is rebuilt.
255
+ */
256
+ erase(recordId: string): Promise<{
257
+ redacted: number;
258
+ witnessed: string[];
259
+ }>;
260
+ /** Check the chain's links and, with the owner's key, its signatures (8.5). */
261
+ verify(keys?: Keys): Promise<ReturnType<typeof verifyChain>>;
262
+ /** Empty the owner's state tables and apply the log again, in order (8.5.3). */
263
+ rebuild(): Promise<State>;
264
+ /**
265
+ * Redact one write of an entry, or the whole entry (8.5.2): the log's one update, which the trigger
266
+ * admits and nothing else. The state is then rebuilt, so the cache forgets what the log forgot.
267
+ */
268
+ redact(logId: string, writeIndex?: number): Promise<void>;
269
+ }