primitive-admin 1.1.0-alpha.82 → 1.1.0-alpha.83

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 (86) hide show
  1. package/dist/src/commands/documents.js +333 -42
  2. package/dist/src/commands/documents.js.map +1 -1
  3. package/dist/src/commands/functions.js +302 -120
  4. package/dist/src/commands/functions.js.map +1 -1
  5. package/dist/src/commands/locks.js +15 -1
  6. package/dist/src/commands/locks.js.map +1 -1
  7. package/dist/src/commands/sync.d.ts +50 -0
  8. package/dist/src/commands/sync.js +560 -312
  9. package/dist/src/commands/sync.js.map +1 -1
  10. package/dist/src/lib/api-client.d.ts +75 -2
  11. package/dist/src/lib/api-client.js +122 -7
  12. package/dist/src/lib/api-client.js.map +1 -1
  13. package/dist/src/lib/config-object-descriptor.js +5 -5
  14. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  15. package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
  16. package/dist/src/lib/document-ingest-artifact.js +505 -0
  17. package/dist/src/lib/document-ingest-artifact.js.map +1 -0
  18. package/dist/src/lib/document-ingest-input.d.ts +51 -0
  19. package/dist/src/lib/document-ingest-input.js +132 -0
  20. package/dist/src/lib/document-ingest-input.js.map +1 -0
  21. package/dist/src/lib/document-ingest.d.ts +78 -0
  22. package/dist/src/lib/document-ingest.js +231 -0
  23. package/dist/src/lib/document-ingest.js.map +1 -0
  24. package/dist/src/lib/function-bundle.d.ts +6 -0
  25. package/dist/src/lib/function-bundle.js +8 -0
  26. package/dist/src/lib/function-bundle.js.map +1 -1
  27. package/dist/src/lib/function-collect.d.ts +1 -1
  28. package/dist/src/lib/function-collect.js +16 -0
  29. package/dist/src/lib/function-collect.js.map +1 -1
  30. package/dist/src/lib/function-db-types.d.ts +4 -0
  31. package/dist/src/lib/function-db-types.js +31 -2
  32. package/dist/src/lib/function-db-types.js.map +1 -1
  33. package/dist/src/lib/function-document-types.d.ts +83 -0
  34. package/dist/src/lib/function-document-types.js +133 -0
  35. package/dist/src/lib/function-document-types.js.map +1 -1
  36. package/dist/src/lib/function-grants-preflight.js +5 -0
  37. package/dist/src/lib/function-grants-preflight.js.map +1 -1
  38. package/dist/src/lib/function-log-lines.d.ts +76 -0
  39. package/dist/src/lib/function-log-lines.js +160 -0
  40. package/dist/src/lib/function-log-lines.js.map +1 -0
  41. package/dist/src/lib/function-log-row.d.ts +29 -0
  42. package/dist/src/lib/function-log-row.js +73 -0
  43. package/dist/src/lib/function-log-row.js.map +1 -0
  44. package/dist/src/lib/function-run.d.ts +53 -35
  45. package/dist/src/lib/function-run.js +55 -44
  46. package/dist/src/lib/function-run.js.map +1 -1
  47. package/dist/src/lib/function-schema-codegen.d.ts +6 -9
  48. package/dist/src/lib/function-schema-codegen.js +22 -35
  49. package/dist/src/lib/function-schema-codegen.js.map +1 -1
  50. package/dist/src/lib/function-sync.d.ts +226 -35
  51. package/dist/src/lib/function-sync.js +731 -99
  52. package/dist/src/lib/function-sync.js.map +1 -1
  53. package/dist/src/lib/function-versions.d.ts +122 -0
  54. package/dist/src/lib/function-versions.js +182 -0
  55. package/dist/src/lib/function-versions.js.map +1 -0
  56. package/dist/src/lib/generated-config-surfaces.d.ts +390 -146
  57. package/dist/src/lib/generated-config-surfaces.js +1207 -401
  58. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  59. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  60. package/dist/src/lib/generated-sdk-types.js +1 -1
  61. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  62. package/dist/src/lib/log-inspection.d.ts +2 -0
  63. package/dist/src/lib/log-inspection.js +4 -0
  64. package/dist/src/lib/log-inspection.js.map +1 -1
  65. package/dist/src/lib/output.d.ts +15 -0
  66. package/dist/src/lib/output.js +28 -0
  67. package/dist/src/lib/output.js.map +1 -1
  68. package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
  69. package/dist/src/lib/snapshot-audit-source.js +58 -0
  70. package/dist/src/lib/snapshot-audit-source.js.map +1 -0
  71. package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
  72. package/dist/src/lib/snapshot-audit-store.js +196 -0
  73. package/dist/src/lib/snapshot-audit-store.js.map +1 -0
  74. package/dist/src/lib/snapshot-audit.d.ts +207 -0
  75. package/dist/src/lib/snapshot-audit.js +431 -0
  76. package/dist/src/lib/snapshot-audit.js.map +1 -0
  77. package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
  78. package/dist/src/lib/snapshot-build-rows.js +87 -0
  79. package/dist/src/lib/snapshot-build-rows.js.map +1 -0
  80. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +23 -16
  81. package/dist/src/lib/swift-codegen/functionGenerator.js +34 -33
  82. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
  83. package/dist/src/lib/sync-selectors.d.ts +45 -2
  84. package/dist/src/lib/sync-selectors.js +66 -5
  85. package/dist/src/lib/sync-selectors.js.map +1 -1
  86. package/package.json +2 -2
@@ -0,0 +1,207 @@
1
+ /**
2
+ * `snapshot ⊕ overlays == table`, recomputed and compared (#3433, criterion 10).
3
+ *
4
+ * Per-build verification checks a base against the table it was built FROM:
5
+ * the chunks are read back, their digests match, their id sequences cover the
6
+ * ranges. What it cannot check is the other half of the contract — that
7
+ * folding the overlays onto that base, the way a CLIENT folds them, arrives at
8
+ * the document the server holds. A fold that means something slightly
9
+ * different on the two ends puts records into a client's authoritative table
10
+ * that the server never wrote, silently, because on a format-2 document there
11
+ * is no whole-document comparison left downstream to catch it.
12
+ *
13
+ * So this runs OUTSIDE the Durable Object, on the CLI:
14
+ *
15
+ * 0. Read the document's schema, which is the same permission the table pages
16
+ * need. The chain export takes the app-admin arm and the pages take the
17
+ * console arm or a read grant on the document, so a caller holding only the
18
+ * first is refused here rather than after downloading the whole chain
19
+ * (3433-C08).
20
+ * 1. Export the chain — the newest complete base, every overlay sealed after
21
+ * it, and the open epoch's overlay inline.
22
+ * 2. Load the base through the checked-in loader into a local record store,
23
+ * then fold each overlay in order with the client's own fold.
24
+ * 3. Page the authoritative table through the records API and compare it,
25
+ * record by record, with the recomputed view.
26
+ *
27
+ * Running it inside the room would need a scratch copy of up to 2 GB of rows
28
+ * and a new multi-tick state machine; running it with the SERVER's fold would
29
+ * make it agree with the room by construction.
30
+ *
31
+ * ## The fence, and why there is one
32
+ *
33
+ * The chain export captures the open overlay once (`Y.encodeStateAsUpdate` at
34
+ * one moment); each records page is an independent query of the live table
35
+ * later (3433-R03). An ordinary write in between would read as corruption. The
36
+ * export is therefore its own fence: after the last page the chain is exported
37
+ * AGAIN and the open epoch number and a fingerprint of the inline overlay are
38
+ * compared with the capture. The fingerprint is the overlay's state vector AND
39
+ * its delete set, because a Yjs deletion creates no struct and therefore never
40
+ * moves a state vector (3433-C05) — and a record deleted under the comparison
41
+ * is precisely what a divergence looks like. The epoch moves on every
42
+ * rotation, so it is checked on its own. Either moving means the document
43
+ * changed under the comparison: that attempt yields NO VERDICT and is retried
44
+ * from a fresh capture; a document that moves on every attempt is reported as
45
+ * `none` rather than as a divergence.
46
+ */
47
+ import { type ManifestChunkLike, type ManifestLike } from "./snapshot-manifest-layout.js";
48
+ /** One artifact of the chain, as the export names it. */
49
+ export interface AuditChainDownload {
50
+ path: string;
51
+ }
52
+ /** What `GET .../export/chain` answers for a large document. */
53
+ export interface AuditChainDescriptor {
54
+ documentFormat?: number;
55
+ documentId?: string;
56
+ epoch: number;
57
+ base: {
58
+ epoch: number;
59
+ buildId: string;
60
+ rows?: number;
61
+ download: AuditChainDownload;
62
+ } | null;
63
+ overlays: Array<{
64
+ epoch: number;
65
+ download: AuditChainDownload;
66
+ }>;
67
+ current: {
68
+ epoch: number;
69
+ update: string;
70
+ };
71
+ }
72
+ /** One page of the authoritative table, as the records route answers it. */
73
+ export interface AuditTablePage {
74
+ items: Array<Record<string, unknown>>;
75
+ nextCursor?: string | null;
76
+ }
77
+ /**
78
+ * Everything the audit reads from the SERVER.
79
+ *
80
+ * Injected rather than reached for, so the whole decision — the fence, the
81
+ * model union, the comparison — is gradeable against a source a test can move
82
+ * between calls, which is the one thing a live run cannot stage reliably.
83
+ */
84
+ export interface AuditSource {
85
+ /** Export the chain. Throws the server's refusal verbatim. */
86
+ chain(): Promise<AuditChainDescriptor>;
87
+ /** One artifact, by its path within the chain's grant. */
88
+ download(path: string): Promise<Uint8Array>;
89
+ /**
90
+ * Every model the authoritative table holds a row of (#3433, behavior 30).
91
+ *
92
+ * Read before the chain is exported: it comes off the schema response, which
93
+ * takes the same permission the table pages do, so it is also the audit's
94
+ * check that the caller can finish (3433-C08).
95
+ */
96
+ tableModels(): Promise<string[]>;
97
+ /** One page of a model's records, in the route's own order. */
98
+ readPage(model: string, cursor?: string): Promise<AuditTablePage>;
99
+ /**
100
+ * The stringset fields the DOCUMENT declares now, per model.
101
+ *
102
+ * The base manifest carries the declarations as they stood when it was
103
+ * built, which is the only record for a model no later overlay touched — but
104
+ * a model or a field declared AFTER the base is not in it, and a stringset
105
+ * compared as an ordered array is a divergence the moment the two sides
106
+ * added the same members in a different order (finding 3433-C06). The two
107
+ * sources are unioned; what a field MEANS is the schema's job either way.
108
+ */
109
+ stringSetFields?(): Promise<Record<string, string[]>>;
110
+ }
111
+ /**
112
+ * The local reconstruction the audit compares against.
113
+ *
114
+ * A port rather than a concrete store: the verb builds it over js-bao's Node
115
+ * SQLite engine, and the hermetic tests build it over a map, so the comparison
116
+ * is graded without a database and the database is proven separately.
117
+ */
118
+ export interface AuditLocalStore {
119
+ /** Materialise the base. `fetchChunk` is handed each manifest entry. */
120
+ loadSnapshot(manifest: ManifestLike, fetchChunk: (chunk: ManifestChunkLike) => Promise<Uint8Array>): Promise<void>;
121
+ /** Fold one overlay update — sealed or open — in chain order. */
122
+ applyOverlay(update: Uint8Array): Promise<void>;
123
+ /** Every model the reconstruction holds a row of. */
124
+ models(): Promise<string[]>;
125
+ /**
126
+ * The recomputed rows for these ids, and mark them compared.
127
+ *
128
+ * Reading and marking together is what keeps the audit's memory to one page
129
+ * plus the store: the ids the server has already shown are never held.
130
+ */
131
+ take(model: string, ids: readonly string[]): Promise<Map<string, Record<string, unknown>>>;
132
+ /** Rows of a model no page asked for, in id order, after `after`. */
133
+ remaining(model: string, after: string | null, limit: number): Promise<Array<{
134
+ id: string;
135
+ data: Record<string, unknown>;
136
+ }>>;
137
+ /** Stringset fields per model, as the base manifest's schema declares them. */
138
+ stringSetFields(model: string): ReadonlySet<string>;
139
+ close(): Promise<void>;
140
+ }
141
+ /** What one model's comparison found. */
142
+ export interface AuditModelReport {
143
+ model: string;
144
+ /** Records present on both sides and compared. */
145
+ compared: number;
146
+ /** Ids the recomputed view holds and the table does not. */
147
+ onlyRecomputed: string[];
148
+ /** Ids the table holds and the recomputed view does not. */
149
+ onlyTable: string[];
150
+ /** Records both hold, with the fields that differ. */
151
+ differing: Array<{
152
+ id: string;
153
+ fields: string[];
154
+ }>;
155
+ /** A list above was capped at `maxDivergences`. */
156
+ truncated: boolean;
157
+ }
158
+ /** What the audit decided, and what it looked at. */
159
+ export interface AuditReport {
160
+ verdict: "match" | "divergence" | "none";
161
+ /** Attempts made, including the ones the fence discarded. */
162
+ attempts: number;
163
+ /** Why there is no verdict. Absent on `match` and `divergence`. */
164
+ reason?: string;
165
+ buildId: string | null;
166
+ epoch: number | null;
167
+ models: AuditModelReport[];
168
+ }
169
+ export interface AuditOptions {
170
+ /** A fresh store per attempt: an attempt the fence discards is thrown away. */
171
+ store: () => Promise<AuditLocalStore>;
172
+ /** Ids listed per model per category before the report says `truncated`. */
173
+ maxDivergences?: number;
174
+ /** Attempts before the audit gives up on a moving document. */
175
+ retries?: number;
176
+ /** Records per page asked of the table. */
177
+ pageSize?: number;
178
+ /**
179
+ * How far one overlay update has got — its state vector AND its delete set.
180
+ *
181
+ * Injected because it is the one thing in the fence that needs Yjs, and the
182
+ * library is graded without it. `createChainFingerprintReader` is what the
183
+ * verb passes; a reader that answered with the state vector alone would miss
184
+ * a deletion (3433-C05).
185
+ */
186
+ fingerprint: (update: Uint8Array) => string;
187
+ }
188
+ /** The server refused to export a chain; there is nothing to audit. */
189
+ export declare const EXPORT_CHAIN_INCOMPLETE = "EXPORT_CHAIN_INCOMPLETE";
190
+ /**
191
+ * The stringset fields a schema response declares, per model.
192
+ *
193
+ * `GET documents/:id/schema` answers `{ schema: { models: { Note: { fields:
194
+ * { tags: { type: "stringset" } } } } } }` — the declarations the document's
195
+ * `_meta_` maps carry, which is where a field added after the base snapshot
196
+ * says what it is. A model with no stringset is left out rather than mapped to
197
+ * an empty list, so the union with the manifest's is a plain spread.
198
+ */
199
+ export declare function stringSetFieldsFromSchema(schema: any): Record<string, string[]>;
200
+ /**
201
+ * Recompute a document's chain locally and compare it with its table.
202
+ *
203
+ * Returns a verdict rather than throwing: a divergence is the answer the
204
+ * command exists to give, and "no verdict" is a third answer the validation
205
+ * child scripts on, not an error to catch.
206
+ */
207
+ export declare function auditSnapshotChain(source: AuditSource, options: AuditOptions): Promise<AuditReport>;
@@ -0,0 +1,431 @@
1
+ /**
2
+ * `snapshot ⊕ overlays == table`, recomputed and compared (#3433, criterion 10).
3
+ *
4
+ * Per-build verification checks a base against the table it was built FROM:
5
+ * the chunks are read back, their digests match, their id sequences cover the
6
+ * ranges. What it cannot check is the other half of the contract — that
7
+ * folding the overlays onto that base, the way a CLIENT folds them, arrives at
8
+ * the document the server holds. A fold that means something slightly
9
+ * different on the two ends puts records into a client's authoritative table
10
+ * that the server never wrote, silently, because on a format-2 document there
11
+ * is no whole-document comparison left downstream to catch it.
12
+ *
13
+ * So this runs OUTSIDE the Durable Object, on the CLI:
14
+ *
15
+ * 0. Read the document's schema, which is the same permission the table pages
16
+ * need. The chain export takes the app-admin arm and the pages take the
17
+ * console arm or a read grant on the document, so a caller holding only the
18
+ * first is refused here rather than after downloading the whole chain
19
+ * (3433-C08).
20
+ * 1. Export the chain — the newest complete base, every overlay sealed after
21
+ * it, and the open epoch's overlay inline.
22
+ * 2. Load the base through the checked-in loader into a local record store,
23
+ * then fold each overlay in order with the client's own fold.
24
+ * 3. Page the authoritative table through the records API and compare it,
25
+ * record by record, with the recomputed view.
26
+ *
27
+ * Running it inside the room would need a scratch copy of up to 2 GB of rows
28
+ * and a new multi-tick state machine; running it with the SERVER's fold would
29
+ * make it agree with the room by construction.
30
+ *
31
+ * ## The fence, and why there is one
32
+ *
33
+ * The chain export captures the open overlay once (`Y.encodeStateAsUpdate` at
34
+ * one moment); each records page is an independent query of the live table
35
+ * later (3433-R03). An ordinary write in between would read as corruption. The
36
+ * export is therefore its own fence: after the last page the chain is exported
37
+ * AGAIN and the open epoch number and a fingerprint of the inline overlay are
38
+ * compared with the capture. The fingerprint is the overlay's state vector AND
39
+ * its delete set, because a Yjs deletion creates no struct and therefore never
40
+ * moves a state vector (3433-C05) — and a record deleted under the comparison
41
+ * is precisely what a divergence looks like. The epoch moves on every
42
+ * rotation, so it is checked on its own. Either moving means the document
43
+ * changed under the comparison: that attempt yields NO VERDICT and is retried
44
+ * from a fresh capture; a document that moves on every attempt is reported as
45
+ * `none` rather than as a divergence.
46
+ */
47
+ import { chunkAddressOf, } from "./snapshot-manifest-layout.js";
48
+ /** The server refused to export a chain; there is nothing to audit. */
49
+ export const EXPORT_CHAIN_INCOMPLETE = "EXPORT_CHAIN_INCOMPLETE";
50
+ /**
51
+ * The stringset fields a schema response declares, per model.
52
+ *
53
+ * `GET documents/:id/schema` answers `{ schema: { models: { Note: { fields:
54
+ * { tags: { type: "stringset" } } } } } }` — the declarations the document's
55
+ * `_meta_` maps carry, which is where a field added after the base snapshot
56
+ * says what it is. A model with no stringset is left out rather than mapped to
57
+ * an empty list, so the union with the manifest's is a plain spread.
58
+ */
59
+ export function stringSetFieldsFromSchema(schema) {
60
+ const models = schema?.schema?.models ?? schema?.models ?? {};
61
+ // A model's name is DATA — an undeclared model is whatever `_type` the
62
+ // records route was handed — so `constructor` and `toString` are names this
63
+ // dictionary can be asked for. On a plain object either answers a function
64
+ // off the prototype rather than `undefined`, which the caller's `?? []`
65
+ // cannot see and its spread cannot iterate (3433-C11). Nothing is inherited
66
+ // here, so a lookup answers only what a schema actually declared.
67
+ const out = Object.create(null);
68
+ for (const [model, declaration] of Object.entries(models)) {
69
+ const fields = declaration?.fields ?? {};
70
+ const sets = Object.entries(fields)
71
+ .filter(([, field]) => String(field?.type ?? "").toLowerCase() === "stringset")
72
+ .map(([name]) => name);
73
+ if (sets.length > 0)
74
+ out[model] = sets;
75
+ }
76
+ return out;
77
+ }
78
+ const DEFAULT_MAX_DIVERGENCES = 20;
79
+ const DEFAULT_RETRIES = 3;
80
+ const DEFAULT_PAGE_SIZE = 200;
81
+ function decodeBase64(value) {
82
+ const binary = Buffer.from(value, "base64");
83
+ return new Uint8Array(binary.buffer, binary.byteOffset, binary.byteLength);
84
+ }
85
+ /**
86
+ * The value of one field, in a form two sides can be compared by.
87
+ *
88
+ * Canonical JSON — object keys sorted — because the two sides reach the same
89
+ * record by different routes (one out of SQLite's `json_patch`, one through
90
+ * the records route's serialisation) and key order is not a difference a
91
+ * reader would call corruption.
92
+ */
93
+ function canonical(value) {
94
+ if (value === null || typeof value !== "object")
95
+ return JSON.stringify(value) ?? "null";
96
+ if (Array.isArray(value))
97
+ return `[${value.map(canonical).join(",")}]`;
98
+ const entries = Object.entries(value).sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0));
99
+ return `{${entries.map(([key, inner]) => `${JSON.stringify(key)}:${canonical(inner)}`).join(",")}}`;
100
+ }
101
+ /** A stringset is a SET: the order its members come back in means nothing. */
102
+ function canonicalSet(value) {
103
+ if (!Array.isArray(value))
104
+ return canonical(value);
105
+ return `[${[...value].map(canonical).sort().join(",")}]`;
106
+ }
107
+ /**
108
+ * The fields of a record that mean something to a comparison.
109
+ *
110
+ * `id` is the key both sides are looked up by, not a field; a `_`-prefixed key
111
+ * is the route's own bookkeeping (`_type`, `_meta_doc_id`), not the record.
112
+ * `type` is the ORM's own column for the model name — written beside `id` on
113
+ * every projected row and reserved from a model's fields — so it is dropped
114
+ * when it says what the model already says, and compared like any other field
115
+ * when it does not.
116
+ */
117
+ function comparableFields(record, model) {
118
+ const out = {};
119
+ for (const [field, value] of Object.entries(record)) {
120
+ if (field === "id" || field.startsWith("_"))
121
+ continue;
122
+ if (field === "type" && value === model)
123
+ continue;
124
+ if (value === undefined)
125
+ continue;
126
+ out[field] = value;
127
+ }
128
+ return out;
129
+ }
130
+ /** Which fields of two records disagree. */
131
+ function differingFields(left, right, stringSets) {
132
+ const fields = new Set([...Object.keys(left), ...Object.keys(right)]);
133
+ const out = [];
134
+ for (const field of Array.from(fields).sort()) {
135
+ const render = stringSets.has(field) ? canonicalSet : canonical;
136
+ if (render(left[field]) !== render(right[field]))
137
+ out.push(field);
138
+ }
139
+ return out;
140
+ }
141
+ /** The stringset fields declared for one model, and nothing inherited. */
142
+ function declaredFields(declared, model) {
143
+ const fields = Object.prototype.hasOwnProperty.call(declared, model)
144
+ ? declared[model]
145
+ : undefined;
146
+ return Array.isArray(fields) ? fields : [];
147
+ }
148
+ /**
149
+ * A read of the server that did not finish — never a divergence (3433-C10).
150
+ *
151
+ * Exit 1 means "the table is not the chain". A page the server refused, an
152
+ * artifact that would not download, a grant withdrawn mid-run: none of them
153
+ * says anything whatever about that, and an error escaping this library used
154
+ * to reach the CLI's top-level handler and exit 1 all the same. Tagged here,
155
+ * caught at the attempt, and answered as the third verdict with its reason and
156
+ * no per-model report.
157
+ */
158
+ class AuditSourceError extends Error {
159
+ what;
160
+ failure;
161
+ constructor(what, failure) {
162
+ super(`${what}: ${failure?.message ?? String(failure)}`);
163
+ this.what = what;
164
+ this.failure = failure;
165
+ this.name = "AuditSourceError";
166
+ }
167
+ /** Why there is no verdict, in the operator's terms. */
168
+ describe() {
169
+ const detail = this.failure?.message ?? String(this.failure);
170
+ const status = this.failure?.statusCode ?? this.failure?.status;
171
+ if (status === 403) {
172
+ return `${this.what} could not be read: ${missingGrant(detail)}`;
173
+ }
174
+ return `${this.what} could not be read: ${detail}`;
175
+ }
176
+ }
177
+ /** The permission the comparison needs, which the chain export does not give. */
178
+ function missingGrant(detail) {
179
+ return (`this account may export the document's chain but not read its table: ` +
180
+ `comparing them needs the console-admin arm or a read grant on this ` +
181
+ `document (${detail})`);
182
+ }
183
+ /** One read of the server, tagged so a failure is no verdict rather than one. */
184
+ async function read(what, run) {
185
+ try {
186
+ return await run();
187
+ }
188
+ catch (error) {
189
+ throw new AuditSourceError(what, error);
190
+ }
191
+ }
192
+ /** The id the records route gives a record. */
193
+ function recordId(item) {
194
+ const id = item.id ?? item._id;
195
+ return typeof id === "string" || typeof id === "number" ? String(id) : null;
196
+ }
197
+ /**
198
+ * Recompute a document's chain locally and compare it with its table.
199
+ *
200
+ * Returns a verdict rather than throwing: a divergence is the answer the
201
+ * command exists to give, and "no verdict" is a third answer the validation
202
+ * child scripts on, not an error to catch.
203
+ */
204
+ export async function auditSnapshotChain(source, options) {
205
+ const retries = Math.max(1, options.retries ?? DEFAULT_RETRIES);
206
+ const maxDivergences = options.maxDivergences ?? DEFAULT_MAX_DIVERGENCES;
207
+ const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
208
+ const none = (reason, attempts) => ({
209
+ verdict: "none",
210
+ attempts,
211
+ reason,
212
+ buildId: null,
213
+ epoch: null,
214
+ models: [],
215
+ });
216
+ let attempts = 0;
217
+ let lastMoved = "";
218
+ for (let attempt = 0; attempt < retries; attempt++) {
219
+ attempts++;
220
+ // The table FIRST, before anything is exported (3433-C08).
221
+ //
222
+ // The audit is two reads with two different answers: the chain export
223
+ // takes the app-admin arm, and the records pages take the console arm or a
224
+ // read grant on the document, because the documents surface grants no
225
+ // app-role bypass on a read (#3279, D3279-002). Reading the schema is the
226
+ // same permission the pages need, so a caller who would be refused halfway
227
+ // is refused here — with the grant named, and with nothing downloaded.
228
+ let tableModels;
229
+ try {
230
+ tableModels = await source.tableModels();
231
+ }
232
+ catch (error) {
233
+ const status = error?.statusCode ?? error?.status;
234
+ const reason = status === 403
235
+ ? missingGrant(error?.message ?? "forbidden")
236
+ : `the document's table could not be read: ${error?.message ?? String(error)}`;
237
+ return none(reason, attempts);
238
+ }
239
+ let captured;
240
+ try {
241
+ captured = await source.chain();
242
+ }
243
+ catch (error) {
244
+ // The server's own refusal, not a shape this library decides. A chain it
245
+ // will not export is no verdict — never a divergence.
246
+ const code = error?.code ?? error?.body?.code;
247
+ const reason = code === EXPORT_CHAIN_INCOMPLETE
248
+ ? `the server will not export this document's chain (${EXPORT_CHAIN_INCOMPLETE}): ${error?.message ?? "incomplete"}`
249
+ : `the chain could not be exported: ${error?.message ?? String(error)}`;
250
+ return none(reason, attempts);
251
+ }
252
+ if (captured.documentFormat !== undefined && Number(captured.documentFormat) !== 2) {
253
+ return none("this is an ordinary document: it has no snapshot chain to recompute", attempts);
254
+ }
255
+ if (!captured.base) {
256
+ return none("this document has no completed base snapshot yet, so there is nothing to fold onto", attempts);
257
+ }
258
+ const fence = {
259
+ epoch: Number(captured.current?.epoch ?? captured.epoch),
260
+ fingerprint: options.fingerprint(decodeBase64(captured.current.update)),
261
+ };
262
+ const store = await options.store();
263
+ // A failure inside the loader's own callback can reach the catch below
264
+ // through the loader rather than as itself, so the tag is carried here too.
265
+ let carried = null;
266
+ const chainArtifact = async (path) => {
267
+ try {
268
+ return await read("an artifact of the chain", () => source.download(path));
269
+ }
270
+ catch (error) {
271
+ if (error instanceof AuditSourceError)
272
+ carried = error;
273
+ throw error;
274
+ }
275
+ };
276
+ try {
277
+ const manifest = JSON.parse(Buffer.from(await chainArtifact(captured.base.download.path)).toString("utf-8"));
278
+ const basePath = captured.base.download.path;
279
+ await store.loadSnapshot(manifest, async (chunk) => {
280
+ // Addressed by the entry's own path within the granted build — the
281
+ // own-build `{model}/{n}` or the `{epoch}-{buildId}/{model}/{n}` of a
282
+ // chunk carried forward — never re-derived from the key, which for a
283
+ // reused entry names a DIFFERENT build's object (the real cause of
284
+ // what was escalated as 3433-C01).
285
+ return await chainArtifact(`${basePath}/${chunkAddressOf(chunk).path}`);
286
+ });
287
+ // Every overlay sealed after the base, then the open one, in order: a
288
+ // fold applied out of order is a different document.
289
+ for (const overlay of captured.overlays) {
290
+ await store.applyOverlay(await chainArtifact(overlay.download.path));
291
+ }
292
+ await store.applyOverlay(decodeBase64(captured.current.update));
293
+ // The model list is the UNION. A model the table holds and the chain
294
+ // does not is exactly the case an audit exists to catch, and a model the
295
+ // chain holds and the table does not is its mirror; taking either side
296
+ // alone would skip one of them (3433-R04).
297
+ const models = Array.from(new Set([...tableModels, ...(await store.models())])).sort();
298
+ // What the document declares a stringset NOW, beside what the base
299
+ // manifest recorded: a field declared after the base is a set on both
300
+ // sides and an ordered array to nobody (3433-C06).
301
+ const declaredSets = source.stringSetFields
302
+ ? await read("the document's schema", () => source.stringSetFields())
303
+ : {};
304
+ const reports = [];
305
+ for (const model of models) {
306
+ const stringSets = new Set([
307
+ ...store.stringSetFields(model),
308
+ ...declaredFields(declaredSets, model),
309
+ ]);
310
+ reports.push(await compareModel(source, store, model, pageSize, maxDivergences, stringSets));
311
+ }
312
+ // The fence: nothing may have moved between the capture and here.
313
+ let after;
314
+ try {
315
+ after = await source.chain();
316
+ }
317
+ catch (error) {
318
+ return none(`the chain could not be re-exported to confirm the document held still: ${error?.message ?? String(error)}`, attempts);
319
+ }
320
+ const moved = fenceMoved(fence, after, options.fingerprint);
321
+ if (moved) {
322
+ lastMoved = moved;
323
+ continue;
324
+ }
325
+ const diverged = reports.some((report) => report.onlyRecomputed.length > 0 ||
326
+ report.onlyTable.length > 0 ||
327
+ report.differing.length > 0);
328
+ return {
329
+ verdict: diverged ? "divergence" : "match",
330
+ attempts,
331
+ buildId: captured.base.buildId,
332
+ epoch: captured.base.epoch,
333
+ models: reports,
334
+ };
335
+ }
336
+ catch (error) {
337
+ // A side that could not be read to the end is no verdict, and the
338
+ // partial comparison behind it is thrown away with the attempt: nothing
339
+ // it found could be said to cover one state of the document.
340
+ const failure = error instanceof AuditSourceError ? error : carried;
341
+ if (!failure)
342
+ throw error;
343
+ return none(failure.describe(), attempts);
344
+ }
345
+ finally {
346
+ await store.close();
347
+ }
348
+ }
349
+ return none(`the document changed during every attempt (${lastMoved}), so no comparison ` +
350
+ `covers one state of it`, attempts);
351
+ }
352
+ /** Where the document moved between the capture and the re-export, if it did. */
353
+ function fenceMoved(captured, after, fingerprint) {
354
+ const epoch = Number(after.current?.epoch ?? after.epoch);
355
+ if (epoch !== captured.epoch) {
356
+ // A seal with no write still moves the epoch, and the new open overlay's
357
+ // state vector is empty — so the epoch is checked on its own (edge E14).
358
+ return `the open epoch moved from ${captured.epoch} to ${epoch}`;
359
+ }
360
+ if (fingerprint(decodeBase64(after.current.update)) !== captured.fingerprint) {
361
+ // A write or a deletion: the fingerprint covers the delete set, which a
362
+ // state vector does not move for (3433-C05).
363
+ return "the open overlay changed";
364
+ }
365
+ return null;
366
+ }
367
+ /** One model's two sides, compared a page at a time. */
368
+ async function compareModel(source, store, model, pageSize, maxDivergences, stringSets) {
369
+ const report = {
370
+ model,
371
+ compared: 0,
372
+ onlyRecomputed: [],
373
+ onlyTable: [],
374
+ differing: [],
375
+ truncated: false,
376
+ };
377
+ const note = (list, id) => {
378
+ if (list.length >= maxDivergences) {
379
+ report.truncated = true;
380
+ return;
381
+ }
382
+ list.push(id);
383
+ };
384
+ let cursor;
385
+ for (;;) {
386
+ const page = await read("the document's table", () => source.readPage(model, cursor));
387
+ const items = page.items ?? [];
388
+ const ids = [];
389
+ const byId = new Map();
390
+ for (const item of items) {
391
+ const id = recordId(item);
392
+ if (id === null)
393
+ continue;
394
+ ids.push(id);
395
+ byId.set(id, item);
396
+ }
397
+ const recomputed = await store.take(model, ids);
398
+ for (const id of ids) {
399
+ const local = recomputed.get(id);
400
+ if (!local) {
401
+ note(report.onlyTable, id);
402
+ continue;
403
+ }
404
+ report.compared++;
405
+ const fields = differingFields(comparableFields(local, model), comparableFields(byId.get(id), model), stringSets);
406
+ if (fields.length === 0)
407
+ continue;
408
+ if (report.differing.length >= maxDivergences)
409
+ report.truncated = true;
410
+ else
411
+ report.differing.push({ id, fields });
412
+ }
413
+ if (!page.nextCursor || items.length === 0)
414
+ break;
415
+ cursor = page.nextCursor;
416
+ }
417
+ // What the recomputed view holds that no page ever showed.
418
+ let after = null;
419
+ for (;;) {
420
+ const rows = await store.remaining(model, after, pageSize);
421
+ if (rows.length === 0)
422
+ break;
423
+ for (const row of rows)
424
+ note(report.onlyRecomputed, row.id);
425
+ after = rows[rows.length - 1].id;
426
+ if (rows.length < pageSize)
427
+ break;
428
+ }
429
+ return report;
430
+ }
431
+ //# sourceMappingURL=snapshot-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-audit.js","sourceRoot":"","sources":["../../../src/lib/snapshot-audit.ts"],"names":[],"mappings":"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"}