@tiangong-lca/cli 0.0.28 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -2
- package/dist/src/cli.js +544 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js +175 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +511 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.js +26 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.js +784 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js +1317 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js +342 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.js +900 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js +688 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.js +1369 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.js +144 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.js +377 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.js +178 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-remote.js +156 -10
- package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
- package/dist/src/lib/http.js.map +1 -1
- package/dist/src/lib/lca-release.js +683 -0
- package/dist/src/lib/lca-release.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { ensurePrivateArtifactDirectory, readProtectedJsonArtifact, writePrivateImmutableJson, } from './dataset-maintenance-protected-artifacts.js';
|
|
3
|
+
import { parseFlowIdentityScopePreflightProof, parseFlowIdentityScopeStatus, prepareFlowIdentityExecution, } from './dataset-maintenance-flow-identity-execution-contract.js';
|
|
4
|
+
import { extractFlowIdentityReference, parseFlowIdentityCapture, } from './dataset-maintenance-flow-identity-contract.js';
|
|
5
|
+
import { isJsonObject, maintenanceRowKey, sha256Json, snapshotRemoteRow, stableJsonText, } from './dataset-maintenance-contract.js';
|
|
6
|
+
import { fetchMaintenanceAccountTableRows, fetchMaintenanceExactRows, normalizeMaintenancePageSize, readMaintenanceFlowIdentityScope, resolveMaintenanceRemoteContext, } from './dataset-maintenance-remote.js';
|
|
7
|
+
import { CliError } from './errors.js';
|
|
8
|
+
function fail(message, code) {
|
|
9
|
+
throw new CliError(message, { code, exitCode: 1 });
|
|
10
|
+
}
|
|
11
|
+
function readCanonicalJson(filePath, label) {
|
|
12
|
+
const artifact = readProtectedJsonArtifact({ filePath, label });
|
|
13
|
+
if (artifact.text !== `${stableJsonText(artifact.value)}\n`) {
|
|
14
|
+
fail(`${label} must be canonical JSON.`, 'DATASET_FLOW_IDENTITY_ARTIFACT_NONCANONICAL');
|
|
15
|
+
}
|
|
16
|
+
return artifact.value;
|
|
17
|
+
}
|
|
18
|
+
function rowKey(table, id, version, userId) {
|
|
19
|
+
return `${table}\u0000${id}\u0000${version}\u0000${userId ?? ''}`;
|
|
20
|
+
}
|
|
21
|
+
function snapshotKey(row) {
|
|
22
|
+
return rowKey(row.table, row.id, row.version, row.user_id);
|
|
23
|
+
}
|
|
24
|
+
function currentKey(row) {
|
|
25
|
+
return rowKey(row.table, row.id, row.version, row.user_id);
|
|
26
|
+
}
|
|
27
|
+
function snapshotWithoutJson(row) {
|
|
28
|
+
const snapshotInput = { ...row };
|
|
29
|
+
delete snapshotInput.json;
|
|
30
|
+
return snapshotRemoteRow(snapshotInput);
|
|
31
|
+
}
|
|
32
|
+
function jsonColumnsMatch(row) {
|
|
33
|
+
return Boolean(row.json !== undefined &&
|
|
34
|
+
row.json !== null &&
|
|
35
|
+
row.json_ordered !== null &&
|
|
36
|
+
sha256Json(row.json) === sha256Json(row.json_ordered));
|
|
37
|
+
}
|
|
38
|
+
function processExchanges(payload) {
|
|
39
|
+
if (!payload || !isJsonObject(payload.processDataSet))
|
|
40
|
+
return null;
|
|
41
|
+
const exchanges = isJsonObject(payload.processDataSet.exchanges)
|
|
42
|
+
? payload.processDataSet.exchanges.exchange
|
|
43
|
+
: null;
|
|
44
|
+
const rows = Array.isArray(exchanges) ? exchanges : isJsonObject(exchanges) ? [exchanges] : null;
|
|
45
|
+
return rows?.every(isJsonObject) ? rows : null;
|
|
46
|
+
}
|
|
47
|
+
function buildOccurrenceIndex(rows, issues) {
|
|
48
|
+
const result = new Map();
|
|
49
|
+
for (const row of [...rows].sort((left, right) => maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)))) {
|
|
50
|
+
const exchanges = processExchanges(row.json_ordered);
|
|
51
|
+
if (!exchanges) {
|
|
52
|
+
issues.push({
|
|
53
|
+
code: 'FLOW_IDENTITY_PROCESS_EXCHANGES_INVALID',
|
|
54
|
+
message: 'An owner-draft process has a malformed exchange collection.',
|
|
55
|
+
details: { id: row.id, version: row.version },
|
|
56
|
+
});
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
exchanges.forEach((exchange, exchangeIndex) => {
|
|
60
|
+
let reference;
|
|
61
|
+
try {
|
|
62
|
+
reference = extractFlowIdentityReference(exchange.referenceToFlowDataSet, 'exchange reference');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
issues.push({
|
|
66
|
+
code: 'FLOW_IDENTITY_PROCESS_REFERENCE_INVALID',
|
|
67
|
+
message: 'An owner-draft process has a malformed flow reference.',
|
|
68
|
+
details: { id: row.id, version: row.version, exchange_index: exchangeIndex },
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const internalId = exchange['@dataSetInternalID'];
|
|
73
|
+
const direction = exchange.exchangeDirection;
|
|
74
|
+
if (typeof internalId !== 'string' || (direction !== 'Input' && direction !== 'Output')) {
|
|
75
|
+
issues.push({
|
|
76
|
+
code: 'FLOW_IDENTITY_PROCESS_EXCHANGE_IDENTITY_INVALID',
|
|
77
|
+
message: 'An owner-draft exchange has an invalid internal ID or direction.',
|
|
78
|
+
details: { id: row.id, version: row.version, exchange_index: exchangeIndex },
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const key = `${reference['@refObjectId']}\u0000${reference['@version']}`;
|
|
83
|
+
const entries = result.get(key) ?? [];
|
|
84
|
+
entries.push({
|
|
85
|
+
process_id: row.id,
|
|
86
|
+
process_version: row.version,
|
|
87
|
+
exchange_index: exchangeIndex,
|
|
88
|
+
internal_id: internalId,
|
|
89
|
+
direction,
|
|
90
|
+
reference_sha256: sha256Json(reference),
|
|
91
|
+
});
|
|
92
|
+
result.set(key, entries);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function compareStableRows(options) {
|
|
98
|
+
let valid = true;
|
|
99
|
+
for (const expected of options.expected) {
|
|
100
|
+
const current = options.currentByKey.get(snapshotKey(expected));
|
|
101
|
+
if (!current ||
|
|
102
|
+
!jsonColumnsMatch(current) ||
|
|
103
|
+
snapshotWithoutJson(current).row_sha256 !== expected.row_sha256) {
|
|
104
|
+
valid = false;
|
|
105
|
+
options.issues.push({
|
|
106
|
+
code: options.code,
|
|
107
|
+
message: 'A sealed source/public/support row is missing or changed.',
|
|
108
|
+
details: { table: expected.table, id: expected.id, version: expected.version },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return valid;
|
|
113
|
+
}
|
|
114
|
+
function derivativeSetIsCausallyTerminal(input) {
|
|
115
|
+
const proof = input.status.derivative_set_proof;
|
|
116
|
+
return Boolean(input.status.status === 'completed' &&
|
|
117
|
+
input.status.primary_complete &&
|
|
118
|
+
input.status.protected_closure_current &&
|
|
119
|
+
input.status.derivatives_current &&
|
|
120
|
+
typeof input.status.terminal_proof_sha256 === 'string' &&
|
|
121
|
+
/^[a-f0-9]{64}$/u.test(input.status.terminal_proof_sha256) &&
|
|
122
|
+
proof.ok &&
|
|
123
|
+
proof.status === 'completed' &&
|
|
124
|
+
proof.target_count === input.plan.processes.length &&
|
|
125
|
+
proof.completed_count === input.plan.processes.length &&
|
|
126
|
+
proof.pending_count === 0 &&
|
|
127
|
+
proof.failed_count === 0 &&
|
|
128
|
+
proof.causal_terminal_proof &&
|
|
129
|
+
proof.targets.length === input.plan.processes.length &&
|
|
130
|
+
proof.compensation_targets.length === 0 &&
|
|
131
|
+
/^[a-f0-9]{64}$/u.test(proof.proof_sha256) &&
|
|
132
|
+
proof.targets.every((target, index) => {
|
|
133
|
+
const process = input.plan.processes[index];
|
|
134
|
+
return Boolean(process &&
|
|
135
|
+
target.ordinal === index + 1 &&
|
|
136
|
+
target.id === process.id &&
|
|
137
|
+
target.version === process.version &&
|
|
138
|
+
target.status === 'completed' &&
|
|
139
|
+
target.request_status === 'completed' &&
|
|
140
|
+
target.phase === 'completed' &&
|
|
141
|
+
target.lineage_ok &&
|
|
142
|
+
target.proposals_committed &&
|
|
143
|
+
target.terminal_audit_present &&
|
|
144
|
+
/^[a-f0-9]{64}$/u.test(target.current_json_ordered_sha256) &&
|
|
145
|
+
/^[a-f0-9]{64}$/u.test(target.current_snapshot_sha256) &&
|
|
146
|
+
Object.values(target.residue).every((count) => count === 0) &&
|
|
147
|
+
target.causal_terminal_proof);
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
export function verifyFlowIdentityReadback(input) {
|
|
151
|
+
const issues = [];
|
|
152
|
+
const currentByKey = new Map(input.currentStableRows.map((row) => [currentKey(row), row]));
|
|
153
|
+
const sourceRowsUnchanged = compareStableRows({
|
|
154
|
+
expected: input.capture.source_rows,
|
|
155
|
+
currentByKey,
|
|
156
|
+
code: 'FLOW_IDENTITY_SOURCE_ROW_DRIFT',
|
|
157
|
+
issues,
|
|
158
|
+
});
|
|
159
|
+
const targetRowsUnchanged = compareStableRows({
|
|
160
|
+
expected: input.capture.target_rows,
|
|
161
|
+
currentByKey,
|
|
162
|
+
code: 'FLOW_IDENTITY_PUBLIC_TARGET_ROW_DRIFT',
|
|
163
|
+
issues,
|
|
164
|
+
});
|
|
165
|
+
const supportRowsUnchanged = compareStableRows({
|
|
166
|
+
expected: input.capture.support_rows,
|
|
167
|
+
currentByKey,
|
|
168
|
+
code: 'FLOW_IDENTITY_SUPPORT_ROW_DRIFT',
|
|
169
|
+
issues,
|
|
170
|
+
});
|
|
171
|
+
const processByKey = new Map(input.currentOwnerDraftProcesses.map((row) => [`${row.id}\u0000${row.version}`, row]));
|
|
172
|
+
let affectedProcessesExact = true;
|
|
173
|
+
for (const expected of input.plan.processes) {
|
|
174
|
+
const current = processByKey.get(`${expected.id}\u0000${expected.version}`);
|
|
175
|
+
const exchanges = processExchanges(current?.json_ordered ?? null);
|
|
176
|
+
if (!current ||
|
|
177
|
+
current.user_id !== input.plan.account.user_id ||
|
|
178
|
+
current.state_code !== 0 ||
|
|
179
|
+
current.model_id !== expected.model_id ||
|
|
180
|
+
current.rule_verification !== expected.rule_verification ||
|
|
181
|
+
!jsonColumnsMatch(current) ||
|
|
182
|
+
sha256Json(current.json_ordered) !== expected.desired_payload_sha256 ||
|
|
183
|
+
!exchanges ||
|
|
184
|
+
sha256Json(exchanges) !== expected.desired_exchange_set_sha256) {
|
|
185
|
+
affectedProcessesExact = false;
|
|
186
|
+
issues.push({
|
|
187
|
+
code: 'FLOW_IDENTITY_AFFECTED_PROCESS_DRIFT',
|
|
188
|
+
message: 'An affected process does not match the exact desired payload/exchange/metadata seal.',
|
|
189
|
+
details: { id: expected.id, version: expected.version },
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const occurrenceIndex = buildOccurrenceIndex(input.currentOwnerDraftProcesses, issues);
|
|
194
|
+
let residue = 0;
|
|
195
|
+
for (const mapping of input.plan.mappings) {
|
|
196
|
+
residue +=
|
|
197
|
+
occurrenceIndex.get(`${mapping.source.id}\u0000${mapping.source.version}`)?.length ?? 0;
|
|
198
|
+
}
|
|
199
|
+
if (residue > 0) {
|
|
200
|
+
issues.push({
|
|
201
|
+
code: 'FLOW_IDENTITY_APPROVED_SOURCE_REFERENCE_RESIDUE',
|
|
202
|
+
message: 'At least one approved source flow reference remains in owner-draft processes.',
|
|
203
|
+
details: { count: residue },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
let protectedClosureExact = true;
|
|
207
|
+
for (const expected of [
|
|
208
|
+
...input.plan.protected_closure.pending,
|
|
209
|
+
...input.plan.protected_closure.blockers,
|
|
210
|
+
]) {
|
|
211
|
+
const observed = occurrenceIndex.get(`${expected.source_id}\u0000${expected.source_version}`) ?? [];
|
|
212
|
+
if (observed.length !== expected.expected_reference_count ||
|
|
213
|
+
sha256Json(observed) !== expected.occurrence_set_sha256) {
|
|
214
|
+
protectedClosureExact = false;
|
|
215
|
+
issues.push({
|
|
216
|
+
code: 'FLOW_IDENTITY_PROTECTED_REFERENCE_DRIFT',
|
|
217
|
+
message: 'A pending/blocker occurrence closure changed.',
|
|
218
|
+
details: { source_id: expected.source_id, source_version: expected.source_version },
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const expected of input.plan.protected_closure.orphans) {
|
|
223
|
+
const observed = occurrenceIndex.get(`${expected.source_id}\u0000${expected.source_version}`) ?? [];
|
|
224
|
+
if (observed.length > 0) {
|
|
225
|
+
protectedClosureExact = false;
|
|
226
|
+
issues.push({
|
|
227
|
+
code: 'FLOW_IDENTITY_ORPHAN_REFERENCE_APPEARED',
|
|
228
|
+
message: 'A sealed orphan now has a process reference.',
|
|
229
|
+
details: { source_id: expected.source_id, source_version: expected.source_version },
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (!input.processScanComplete) {
|
|
234
|
+
issues.push({
|
|
235
|
+
code: 'FLOW_IDENTITY_PROCESS_SCAN_INCOMPLETE',
|
|
236
|
+
message: 'The owner-draft process census did not have exact-count completeness proof.',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const terminalProof = derivativeSetIsCausallyTerminal(input);
|
|
240
|
+
if (!terminalProof) {
|
|
241
|
+
issues.push({
|
|
242
|
+
code: 'FLOW_IDENTITY_TERMINAL_PROOF_NOT_CURRENT',
|
|
243
|
+
message: 'The durable scope does not yet prove current causal derivatives and protected closure.',
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const passed = sourceRowsUnchanged &&
|
|
247
|
+
targetRowsUnchanged &&
|
|
248
|
+
supportRowsUnchanged &&
|
|
249
|
+
affectedProcessesExact &&
|
|
250
|
+
residue === 0 &&
|
|
251
|
+
protectedClosureExact &&
|
|
252
|
+
input.processScanComplete &&
|
|
253
|
+
terminalProof &&
|
|
254
|
+
issues.length === 0;
|
|
255
|
+
const hardReadbackMismatch = issues.some((issue) => issue.code !== 'FLOW_IDENTITY_TERMINAL_PROOF_NOT_CURRENT');
|
|
256
|
+
const reportStatus = passed
|
|
257
|
+
? 'passed'
|
|
258
|
+
: input.status.status === 'derivatives_pending' && !hardReadbackMismatch
|
|
259
|
+
? 'pending'
|
|
260
|
+
: 'failed';
|
|
261
|
+
const terminalSha = input.status.terminal_proof_sha256;
|
|
262
|
+
return {
|
|
263
|
+
schema_version: 'dataset-flow-identity-verification-report.v1',
|
|
264
|
+
generated_at_utc: new Date().toISOString(),
|
|
265
|
+
status: reportStatus,
|
|
266
|
+
plan_sha256: input.plan.plan_sha256,
|
|
267
|
+
operation_id: input.plan.operation_id,
|
|
268
|
+
scope_id: input.status.scope_id,
|
|
269
|
+
database_status: input.status.status,
|
|
270
|
+
terminal_proof_sha256: terminalSha,
|
|
271
|
+
checks: {
|
|
272
|
+
source_rows_unchanged: sourceRowsUnchanged,
|
|
273
|
+
public_target_rows_unchanged: targetRowsUnchanged,
|
|
274
|
+
support_rows_unchanged: supportRowsUnchanged,
|
|
275
|
+
affected_processes_exact: affectedProcessesExact,
|
|
276
|
+
approved_source_reference_residue: residue,
|
|
277
|
+
protected_closure_exact: protectedClosureExact,
|
|
278
|
+
complete_owner_draft_process_scan: input.processScanComplete,
|
|
279
|
+
derivatives_causally_terminal: terminalProof,
|
|
280
|
+
},
|
|
281
|
+
counts: {
|
|
282
|
+
source_rows: input.capture.source_rows.length,
|
|
283
|
+
public_target_rows: input.capture.target_rows.length,
|
|
284
|
+
support_rows: input.capture.support_rows.length,
|
|
285
|
+
affected_processes: input.plan.processes.length,
|
|
286
|
+
owner_draft_processes_scanned: input.currentOwnerDraftProcesses.length,
|
|
287
|
+
},
|
|
288
|
+
issues,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
async function fetchStableRows(options) {
|
|
292
|
+
const result = [];
|
|
293
|
+
const concurrency = 10;
|
|
294
|
+
for (let offset = 0; offset < options.snapshots.length; offset += concurrency) {
|
|
295
|
+
const chunk = options.snapshots.slice(offset, offset + concurrency);
|
|
296
|
+
const reads = await Promise.all(chunk.map(async (expected) => {
|
|
297
|
+
const exact = await fetchMaintenanceExactRows({
|
|
298
|
+
context: options.context,
|
|
299
|
+
table: expected.table,
|
|
300
|
+
id: expected.id,
|
|
301
|
+
version: expected.version,
|
|
302
|
+
includeJson: true,
|
|
303
|
+
});
|
|
304
|
+
const matching = exact.rows.filter((row) => row.user_id === expected.user_id && row.state_code === expected.state_code);
|
|
305
|
+
if (matching.length !== 1) {
|
|
306
|
+
fail('A sealed source/public/support row is missing or ambiguous.', 'DATASET_FLOW_IDENTITY_STABLE_ROW_READ_INVALID');
|
|
307
|
+
}
|
|
308
|
+
return matching[0];
|
|
309
|
+
}));
|
|
310
|
+
result.push(...reads);
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
export async function verifyFlowIdentity(options) {
|
|
315
|
+
const planPath = path.resolve(options.planPath);
|
|
316
|
+
const prepared = prepareFlowIdentityExecution({
|
|
317
|
+
plan: readCanonicalJson(planPath, 'Flow identity plan'),
|
|
318
|
+
freeze: readCanonicalJson(options.freezePath, 'Flow identity freeze'),
|
|
319
|
+
approval: readCanonicalJson(options.approvalPath, 'Flow identity approval'),
|
|
320
|
+
});
|
|
321
|
+
const capture = parseFlowIdentityCapture(readCanonicalJson(path.join(path.dirname(planPath), prepared.plan.artifacts.live_capture), 'Flow identity live capture'));
|
|
322
|
+
if (capture.capture_artifact_sha256 !== prepared.plan.capture_artifact_sha256) {
|
|
323
|
+
fail('Live capture does not bind the plan.', 'DATASET_FLOW_IDENTITY_CAPTURE_MISMATCH');
|
|
324
|
+
}
|
|
325
|
+
const scope = parseFlowIdentityScopePreflightProof(readCanonicalJson(path.join(options.runDir, 'scope-preflight-proof.json'), 'Flow identity scope preflight proof'), prepared.plan);
|
|
326
|
+
const context = await resolveMaintenanceRemoteContext({
|
|
327
|
+
env: options.env,
|
|
328
|
+
fetchImpl: options.fetchImpl,
|
|
329
|
+
timeoutMs: options.timeoutMs,
|
|
330
|
+
now: options.now,
|
|
331
|
+
});
|
|
332
|
+
if (context.project_ref !== prepared.plan.project_ref ||
|
|
333
|
+
context.account.user_id !== prepared.plan.account.user_id ||
|
|
334
|
+
context.account.email.trim().toLowerCase() !== prepared.plan.account.email) {
|
|
335
|
+
fail('Authenticated RLS context does not match the plan.', 'DATASET_FLOW_IDENTITY_CONTEXT_MISMATCH');
|
|
336
|
+
}
|
|
337
|
+
const rawStatus = await readMaintenanceFlowIdentityScope({ context, scopeId: scope.scope_id });
|
|
338
|
+
const status = parseFlowIdentityScopeStatus(rawStatus, prepared.plan, scope.scope_id, scope.scope_proof_sha256);
|
|
339
|
+
const stableRows = await fetchStableRows({
|
|
340
|
+
context,
|
|
341
|
+
snapshots: [...capture.source_rows, ...capture.target_rows, ...capture.support_rows],
|
|
342
|
+
});
|
|
343
|
+
const processScan = await fetchMaintenanceAccountTableRows({
|
|
344
|
+
context,
|
|
345
|
+
userId: prepared.plan.account.user_id,
|
|
346
|
+
table: 'processes',
|
|
347
|
+
stateCode: 0,
|
|
348
|
+
includeJson: true,
|
|
349
|
+
pageSize: normalizeMaintenancePageSize(options.pageSize),
|
|
350
|
+
});
|
|
351
|
+
const generatedAt = options.now ?? new Date();
|
|
352
|
+
const report = verifyFlowIdentityReadback({
|
|
353
|
+
plan: prepared.plan,
|
|
354
|
+
capture,
|
|
355
|
+
status,
|
|
356
|
+
currentStableRows: stableRows,
|
|
357
|
+
currentOwnerDraftProcesses: processScan.rows,
|
|
358
|
+
processScanComplete: processScan.completeness.complete &&
|
|
359
|
+
processScan.completeness.rows_fetched === processScan.rows.length,
|
|
360
|
+
});
|
|
361
|
+
report.generated_at_utc = generatedAt.toISOString();
|
|
362
|
+
const outDir = ensurePrivateArtifactDirectory(options.outDir);
|
|
363
|
+
writePrivateImmutableJson(path.join(outDir, 'flow-identity-verification-report.json'), report);
|
|
364
|
+
return report;
|
|
365
|
+
}
|
|
366
|
+
export const __testInternals = {
|
|
367
|
+
buildOccurrenceIndex,
|
|
368
|
+
compareStableRows,
|
|
369
|
+
currentKey,
|
|
370
|
+
derivativeSetIsCausallyTerminal,
|
|
371
|
+
processExchanges,
|
|
372
|
+
jsonColumnsMatch,
|
|
373
|
+
rowKey,
|
|
374
|
+
snapshotKey,
|
|
375
|
+
snapshotWithoutJson,
|
|
376
|
+
};
|
|
377
|
+
//# sourceMappingURL=dataset-maintenance-flow-identity-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-maintenance-flow-identity-verify.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-flow-identity-verify.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport {\n ensurePrivateArtifactDirectory,\n readProtectedJsonArtifact,\n writePrivateImmutableJson,\n} from './dataset-maintenance-protected-artifacts.js';\nimport {\n parseFlowIdentityScopePreflightProof,\n parseFlowIdentityScopeStatus,\n prepareFlowIdentityExecution,\n type FlowIdentityScopeStatus,\n} from './dataset-maintenance-flow-identity-execution-contract.js';\nimport {\n extractFlowIdentityReference,\n parseFlowIdentityCapture,\n type FlowIdentityLiveCapture,\n type FlowIdentityOccurrence,\n type FlowIdentityPlan,\n} from './dataset-maintenance-flow-identity-contract.js';\nimport {\n isJsonObject,\n maintenanceRowKey,\n sha256Json,\n snapshotRemoteRow,\n stableJsonText,\n type DatasetMaintenanceRemoteRow,\n type DatasetMaintenanceRowSnapshot,\n type JsonObject,\n} from './dataset-maintenance-contract.js';\nimport {\n fetchMaintenanceAccountTableRows,\n fetchMaintenanceExactRows,\n normalizeMaintenancePageSize,\n readMaintenanceFlowIdentityScope,\n resolveMaintenanceRemoteContext,\n type DatasetMaintenanceRemoteContext,\n} from './dataset-maintenance-remote.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike } from './http.js';\n\nexport type VerifyFlowIdentityOptions = {\n planPath: string;\n freezePath: string;\n approvalPath: string;\n runDir: string;\n outDir: string;\n pageSize?: number;\n timeoutMs?: number;\n env: NodeJS.ProcessEnv;\n fetchImpl: FetchLike;\n now?: Date;\n};\n\nexport type FlowIdentityVerificationIssue = {\n code: string;\n message: string;\n details?: unknown;\n};\n\nexport type FlowIdentityVerificationReport = {\n schema_version: 'dataset-flow-identity-verification-report.v1';\n generated_at_utc: string;\n status: 'passed' | 'pending' | 'failed';\n plan_sha256: string;\n operation_id: string;\n scope_id: string;\n database_status: FlowIdentityScopeStatus['status'];\n terminal_proof_sha256: string | null;\n checks: {\n source_rows_unchanged: boolean;\n public_target_rows_unchanged: boolean;\n support_rows_unchanged: boolean;\n affected_processes_exact: boolean;\n approved_source_reference_residue: number;\n protected_closure_exact: boolean;\n complete_owner_draft_process_scan: boolean;\n derivatives_causally_terminal: boolean;\n };\n counts: {\n source_rows: number;\n public_target_rows: number;\n support_rows: number;\n affected_processes: number;\n owner_draft_processes_scanned: number;\n };\n issues: FlowIdentityVerificationIssue[];\n};\n\ntype ReadbackInput = {\n plan: FlowIdentityPlan;\n capture: FlowIdentityLiveCapture;\n status: FlowIdentityScopeStatus;\n currentStableRows: DatasetMaintenanceRemoteRow[];\n currentOwnerDraftProcesses: DatasetMaintenanceRemoteRow[];\n processScanComplete: boolean;\n};\n\nfunction fail(message: string, code: string): never {\n throw new CliError(message, { code, exitCode: 1 });\n}\n\nfunction readCanonicalJson(filePath: string, label: string): unknown {\n const artifact = readProtectedJsonArtifact({ filePath, label });\n if (artifact.text !== `${stableJsonText(artifact.value)}\\n`) {\n fail(`${label} must be canonical JSON.`, 'DATASET_FLOW_IDENTITY_ARTIFACT_NONCANONICAL');\n }\n return artifact.value;\n}\n\nfunction rowKey(table: string, id: string, version: string, userId: string | null): string {\n return `${table}\\u0000${id}\\u0000${version}\\u0000${userId ?? ''}`;\n}\n\nfunction snapshotKey(row: DatasetMaintenanceRowSnapshot): string {\n return rowKey(row.table, row.id, row.version, row.user_id);\n}\n\nfunction currentKey(row: DatasetMaintenanceRemoteRow): string {\n return rowKey(row.table, row.id, row.version, row.user_id);\n}\n\nfunction snapshotWithoutJson(row: DatasetMaintenanceRemoteRow): DatasetMaintenanceRowSnapshot {\n const snapshotInput = { ...row };\n delete snapshotInput.json;\n return snapshotRemoteRow(snapshotInput);\n}\n\nfunction jsonColumnsMatch(row: DatasetMaintenanceRemoteRow): boolean {\n return Boolean(\n row.json !== undefined &&\n row.json !== null &&\n row.json_ordered !== null &&\n sha256Json(row.json) === sha256Json(row.json_ordered),\n );\n}\n\nfunction processExchanges(payload: JsonObject | null): JsonObject[] | null {\n if (!payload || !isJsonObject(payload.processDataSet)) return null;\n const exchanges = isJsonObject(payload.processDataSet.exchanges)\n ? payload.processDataSet.exchanges.exchange\n : null;\n const rows = Array.isArray(exchanges) ? exchanges : isJsonObject(exchanges) ? [exchanges] : null;\n return rows?.every(isJsonObject) ? rows : null;\n}\n\nfunction buildOccurrenceIndex(\n rows: DatasetMaintenanceRemoteRow[],\n issues: FlowIdentityVerificationIssue[],\n): Map<string, FlowIdentityOccurrence[]> {\n const result = new Map<string, FlowIdentityOccurrence[]>();\n for (const row of [...rows].sort((left, right) =>\n maintenanceRowKey(left).localeCompare(maintenanceRowKey(right)),\n )) {\n const exchanges = processExchanges(row.json_ordered);\n if (!exchanges) {\n issues.push({\n code: 'FLOW_IDENTITY_PROCESS_EXCHANGES_INVALID',\n message: 'An owner-draft process has a malformed exchange collection.',\n details: { id: row.id, version: row.version },\n });\n continue;\n }\n exchanges.forEach((exchange, exchangeIndex) => {\n let reference;\n try {\n reference = extractFlowIdentityReference(\n exchange.referenceToFlowDataSet,\n 'exchange reference',\n );\n } catch {\n issues.push({\n code: 'FLOW_IDENTITY_PROCESS_REFERENCE_INVALID',\n message: 'An owner-draft process has a malformed flow reference.',\n details: { id: row.id, version: row.version, exchange_index: exchangeIndex },\n });\n return;\n }\n const internalId = exchange['@dataSetInternalID'];\n const direction = exchange.exchangeDirection;\n if (typeof internalId !== 'string' || (direction !== 'Input' && direction !== 'Output')) {\n issues.push({\n code: 'FLOW_IDENTITY_PROCESS_EXCHANGE_IDENTITY_INVALID',\n message: 'An owner-draft exchange has an invalid internal ID or direction.',\n details: { id: row.id, version: row.version, exchange_index: exchangeIndex },\n });\n return;\n }\n const key = `${reference['@refObjectId']}\\u0000${reference['@version']}`;\n const entries = result.get(key) ?? [];\n entries.push({\n process_id: row.id,\n process_version: row.version,\n exchange_index: exchangeIndex,\n internal_id: internalId,\n direction,\n reference_sha256: sha256Json(reference),\n });\n result.set(key, entries);\n });\n }\n return result;\n}\n\nfunction compareStableRows(options: {\n expected: DatasetMaintenanceRowSnapshot[];\n currentByKey: Map<string, DatasetMaintenanceRemoteRow>;\n code: string;\n issues: FlowIdentityVerificationIssue[];\n}): boolean {\n let valid = true;\n for (const expected of options.expected) {\n const current = options.currentByKey.get(snapshotKey(expected));\n if (\n !current ||\n !jsonColumnsMatch(current) ||\n snapshotWithoutJson(current).row_sha256 !== expected.row_sha256\n ) {\n valid = false;\n options.issues.push({\n code: options.code,\n message: 'A sealed source/public/support row is missing or changed.',\n details: { table: expected.table, id: expected.id, version: expected.version },\n });\n }\n }\n return valid;\n}\n\nfunction derivativeSetIsCausallyTerminal(input: ReadbackInput): boolean {\n const proof = input.status.derivative_set_proof;\n return Boolean(\n input.status.status === 'completed' &&\n input.status.primary_complete &&\n input.status.protected_closure_current &&\n input.status.derivatives_current &&\n typeof input.status.terminal_proof_sha256 === 'string' &&\n /^[a-f0-9]{64}$/u.test(input.status.terminal_proof_sha256) &&\n proof.ok &&\n proof.status === 'completed' &&\n proof.target_count === input.plan.processes.length &&\n proof.completed_count === input.plan.processes.length &&\n proof.pending_count === 0 &&\n proof.failed_count === 0 &&\n proof.causal_terminal_proof &&\n proof.targets.length === input.plan.processes.length &&\n proof.compensation_targets.length === 0 &&\n /^[a-f0-9]{64}$/u.test(proof.proof_sha256) &&\n proof.targets.every((target, index) => {\n const process = input.plan.processes[index];\n return Boolean(\n process &&\n target.ordinal === index + 1 &&\n target.id === process.id &&\n target.version === process.version &&\n target.status === 'completed' &&\n target.request_status === 'completed' &&\n target.phase === 'completed' &&\n target.lineage_ok &&\n target.proposals_committed &&\n target.terminal_audit_present &&\n /^[a-f0-9]{64}$/u.test(target.current_json_ordered_sha256) &&\n /^[a-f0-9]{64}$/u.test(target.current_snapshot_sha256) &&\n Object.values(target.residue).every((count) => count === 0) &&\n target.causal_terminal_proof,\n );\n }),\n );\n}\n\nexport function verifyFlowIdentityReadback(input: ReadbackInput): FlowIdentityVerificationReport {\n const issues: FlowIdentityVerificationIssue[] = [];\n const currentByKey = new Map(input.currentStableRows.map((row) => [currentKey(row), row]));\n const sourceRowsUnchanged = compareStableRows({\n expected: input.capture.source_rows,\n currentByKey,\n code: 'FLOW_IDENTITY_SOURCE_ROW_DRIFT',\n issues,\n });\n const targetRowsUnchanged = compareStableRows({\n expected: input.capture.target_rows,\n currentByKey,\n code: 'FLOW_IDENTITY_PUBLIC_TARGET_ROW_DRIFT',\n issues,\n });\n const supportRowsUnchanged = compareStableRows({\n expected: input.capture.support_rows,\n currentByKey,\n code: 'FLOW_IDENTITY_SUPPORT_ROW_DRIFT',\n issues,\n });\n\n const processByKey = new Map(\n input.currentOwnerDraftProcesses.map((row) => [`${row.id}\\u0000${row.version}`, row]),\n );\n let affectedProcessesExact = true;\n for (const expected of input.plan.processes) {\n const current = processByKey.get(`${expected.id}\\u0000${expected.version}`);\n const exchanges = processExchanges(current?.json_ordered ?? null);\n if (\n !current ||\n current.user_id !== input.plan.account.user_id ||\n current.state_code !== 0 ||\n current.model_id !== expected.model_id ||\n current.rule_verification !== expected.rule_verification ||\n !jsonColumnsMatch(current) ||\n sha256Json(current.json_ordered) !== expected.desired_payload_sha256 ||\n !exchanges ||\n sha256Json(exchanges) !== expected.desired_exchange_set_sha256\n ) {\n affectedProcessesExact = false;\n issues.push({\n code: 'FLOW_IDENTITY_AFFECTED_PROCESS_DRIFT',\n message:\n 'An affected process does not match the exact desired payload/exchange/metadata seal.',\n details: { id: expected.id, version: expected.version },\n });\n }\n }\n\n const occurrenceIndex = buildOccurrenceIndex(input.currentOwnerDraftProcesses, issues);\n let residue = 0;\n for (const mapping of input.plan.mappings) {\n residue +=\n occurrenceIndex.get(`${mapping.source.id}\\u0000${mapping.source.version}`)?.length ?? 0;\n }\n if (residue > 0) {\n issues.push({\n code: 'FLOW_IDENTITY_APPROVED_SOURCE_REFERENCE_RESIDUE',\n message: 'At least one approved source flow reference remains in owner-draft processes.',\n details: { count: residue },\n });\n }\n\n let protectedClosureExact = true;\n for (const expected of [\n ...input.plan.protected_closure.pending,\n ...input.plan.protected_closure.blockers,\n ]) {\n const observed =\n occurrenceIndex.get(`${expected.source_id}\\u0000${expected.source_version}`) ?? [];\n if (\n observed.length !== expected.expected_reference_count ||\n sha256Json(observed) !== expected.occurrence_set_sha256\n ) {\n protectedClosureExact = false;\n issues.push({\n code: 'FLOW_IDENTITY_PROTECTED_REFERENCE_DRIFT',\n message: 'A pending/blocker occurrence closure changed.',\n details: { source_id: expected.source_id, source_version: expected.source_version },\n });\n }\n }\n for (const expected of input.plan.protected_closure.orphans) {\n const observed =\n occurrenceIndex.get(`${expected.source_id}\\u0000${expected.source_version}`) ?? [];\n if (observed.length > 0) {\n protectedClosureExact = false;\n issues.push({\n code: 'FLOW_IDENTITY_ORPHAN_REFERENCE_APPEARED',\n message: 'A sealed orphan now has a process reference.',\n details: { source_id: expected.source_id, source_version: expected.source_version },\n });\n }\n }\n if (!input.processScanComplete) {\n issues.push({\n code: 'FLOW_IDENTITY_PROCESS_SCAN_INCOMPLETE',\n message: 'The owner-draft process census did not have exact-count completeness proof.',\n });\n }\n\n const terminalProof = derivativeSetIsCausallyTerminal(input);\n if (!terminalProof) {\n issues.push({\n code: 'FLOW_IDENTITY_TERMINAL_PROOF_NOT_CURRENT',\n message:\n 'The durable scope does not yet prove current causal derivatives and protected closure.',\n });\n }\n\n const passed =\n sourceRowsUnchanged &&\n targetRowsUnchanged &&\n supportRowsUnchanged &&\n affectedProcessesExact &&\n residue === 0 &&\n protectedClosureExact &&\n input.processScanComplete &&\n terminalProof &&\n issues.length === 0;\n const hardReadbackMismatch = issues.some(\n (issue) => issue.code !== 'FLOW_IDENTITY_TERMINAL_PROOF_NOT_CURRENT',\n );\n const reportStatus: FlowIdentityVerificationReport['status'] = passed\n ? 'passed'\n : input.status.status === 'derivatives_pending' && !hardReadbackMismatch\n ? 'pending'\n : 'failed';\n const terminalSha = input.status.terminal_proof_sha256;\n return {\n schema_version: 'dataset-flow-identity-verification-report.v1',\n generated_at_utc: new Date().toISOString(),\n status: reportStatus,\n plan_sha256: input.plan.plan_sha256,\n operation_id: input.plan.operation_id,\n scope_id: input.status.scope_id,\n database_status: input.status.status,\n terminal_proof_sha256: terminalSha,\n checks: {\n source_rows_unchanged: sourceRowsUnchanged,\n public_target_rows_unchanged: targetRowsUnchanged,\n support_rows_unchanged: supportRowsUnchanged,\n affected_processes_exact: affectedProcessesExact,\n approved_source_reference_residue: residue,\n protected_closure_exact: protectedClosureExact,\n complete_owner_draft_process_scan: input.processScanComplete,\n derivatives_causally_terminal: terminalProof,\n },\n counts: {\n source_rows: input.capture.source_rows.length,\n public_target_rows: input.capture.target_rows.length,\n support_rows: input.capture.support_rows.length,\n affected_processes: input.plan.processes.length,\n owner_draft_processes_scanned: input.currentOwnerDraftProcesses.length,\n },\n issues,\n };\n}\n\nasync function fetchStableRows(options: {\n context: DatasetMaintenanceRemoteContext;\n snapshots: DatasetMaintenanceRowSnapshot[];\n}): Promise<DatasetMaintenanceRemoteRow[]> {\n const result: DatasetMaintenanceRemoteRow[] = [];\n const concurrency = 10;\n for (let offset = 0; offset < options.snapshots.length; offset += concurrency) {\n const chunk = options.snapshots.slice(offset, offset + concurrency);\n const reads = await Promise.all(\n chunk.map(async (expected) => {\n const exact = await fetchMaintenanceExactRows({\n context: options.context,\n table: expected.table,\n id: expected.id,\n version: expected.version,\n includeJson: true,\n });\n const matching = exact.rows.filter(\n (row) => row.user_id === expected.user_id && row.state_code === expected.state_code,\n );\n if (matching.length !== 1) {\n fail(\n 'A sealed source/public/support row is missing or ambiguous.',\n 'DATASET_FLOW_IDENTITY_STABLE_ROW_READ_INVALID',\n );\n }\n return matching[0]!;\n }),\n );\n result.push(...reads);\n }\n return result;\n}\n\nexport async function verifyFlowIdentity(\n options: VerifyFlowIdentityOptions,\n): Promise<FlowIdentityVerificationReport> {\n const planPath = path.resolve(options.planPath);\n const prepared = prepareFlowIdentityExecution({\n plan: readCanonicalJson(planPath, 'Flow identity plan'),\n freeze: readCanonicalJson(options.freezePath, 'Flow identity freeze'),\n approval: readCanonicalJson(options.approvalPath, 'Flow identity approval'),\n });\n const capture = parseFlowIdentityCapture(\n readCanonicalJson(\n path.join(path.dirname(planPath), prepared.plan.artifacts.live_capture),\n 'Flow identity live capture',\n ),\n );\n if (capture.capture_artifact_sha256 !== prepared.plan.capture_artifact_sha256) {\n fail('Live capture does not bind the plan.', 'DATASET_FLOW_IDENTITY_CAPTURE_MISMATCH');\n }\n const scope = parseFlowIdentityScopePreflightProof(\n readCanonicalJson(\n path.join(options.runDir, 'scope-preflight-proof.json'),\n 'Flow identity scope preflight proof',\n ),\n prepared.plan,\n );\n const context = await resolveMaintenanceRemoteContext({\n env: options.env,\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n now: options.now,\n });\n if (\n context.project_ref !== prepared.plan.project_ref ||\n context.account.user_id !== prepared.plan.account.user_id ||\n context.account.email.trim().toLowerCase() !== prepared.plan.account.email\n ) {\n fail(\n 'Authenticated RLS context does not match the plan.',\n 'DATASET_FLOW_IDENTITY_CONTEXT_MISMATCH',\n );\n }\n const rawStatus = await readMaintenanceFlowIdentityScope({ context, scopeId: scope.scope_id });\n const status = parseFlowIdentityScopeStatus(\n rawStatus,\n prepared.plan,\n scope.scope_id,\n scope.scope_proof_sha256,\n );\n const stableRows = await fetchStableRows({\n context,\n snapshots: [...capture.source_rows, ...capture.target_rows, ...capture.support_rows],\n });\n const processScan = await fetchMaintenanceAccountTableRows({\n context,\n userId: prepared.plan.account.user_id,\n table: 'processes',\n stateCode: 0,\n includeJson: true,\n pageSize: normalizeMaintenancePageSize(options.pageSize),\n });\n const generatedAt = options.now ?? new Date();\n const report = verifyFlowIdentityReadback({\n plan: prepared.plan,\n capture,\n status,\n currentStableRows: stableRows,\n currentOwnerDraftProcesses: processScan.rows,\n processScanComplete:\n processScan.completeness.complete &&\n processScan.completeness.rows_fetched === processScan.rows.length,\n });\n report.generated_at_utc = generatedAt.toISOString();\n const outDir = ensurePrivateArtifactDirectory(options.outDir);\n writePrivateImmutableJson(path.join(outDir, 'flow-identity-verification-report.json'), report);\n return report;\n}\n\nexport const __testInternals = {\n buildOccurrenceIndex,\n compareStableRows,\n currentKey,\n derivativeSetIsCausallyTerminal,\n processExchanges,\n jsonColumnsMatch,\n rowKey,\n snapshotKey,\n snapshotWithoutJson,\n};\n"]}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { isJsonObject, sha256Text } from './dataset-maintenance-contract.js';
|
|
2
|
+
import { CliError } from './errors.js';
|
|
3
|
+
const MAX_ORDINAL = 100_000;
|
|
4
|
+
const MAX_COUNT = 1_000_000;
|
|
5
|
+
const MAX_INDEX = 1_000_000;
|
|
6
|
+
function fail(path, message) {
|
|
7
|
+
throw new CliError(`Flow identity wire value ${path} ${message}.`, {
|
|
8
|
+
code: 'DATASET_FLOW_IDENTITY_WIRE_INVALID',
|
|
9
|
+
exitCode: 2,
|
|
10
|
+
details: { path },
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function assertTransportString(value, path) {
|
|
14
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
15
|
+
const code = value.charCodeAt(index);
|
|
16
|
+
if (code === 0)
|
|
17
|
+
fail(path, 'must not contain U+0000');
|
|
18
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
19
|
+
const next = value.charCodeAt(index + 1);
|
|
20
|
+
if (!Number.isInteger(next) || next < 0xdc00 || next > 0xdfff) {
|
|
21
|
+
fail(path, 'must not contain an unpaired UTF-16 surrogate');
|
|
22
|
+
}
|
|
23
|
+
index += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (code >= 0xdc00 && code <= 0xdfff) {
|
|
27
|
+
fail(path, 'must not contain an unpaired UTF-16 surrogate');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isExactStMultiLangEntry(value) {
|
|
32
|
+
if (!isPlainWireObject(value))
|
|
33
|
+
return false;
|
|
34
|
+
const keys = Object.keys(value).sort();
|
|
35
|
+
return (keys.length === 2 &&
|
|
36
|
+
keys[0] === '#text' &&
|
|
37
|
+
keys[1] === '@xml:lang' &&
|
|
38
|
+
typeof value['@xml:lang'] === 'string' &&
|
|
39
|
+
typeof value['#text'] === 'string');
|
|
40
|
+
}
|
|
41
|
+
function isPlainWireObject(value) {
|
|
42
|
+
if (!isJsonObject(value))
|
|
43
|
+
return false;
|
|
44
|
+
const prototype = Object.getPrototypeOf(value);
|
|
45
|
+
return prototype === Object.prototype || prototype === null;
|
|
46
|
+
}
|
|
47
|
+
export function isStandardFlowIdentityShortDescription(value) {
|
|
48
|
+
return (typeof value === 'string' ||
|
|
49
|
+
isExactStMultiLangEntry(value) ||
|
|
50
|
+
(Array.isArray(value) && value.length > 0 && value.every(isExactStMultiLangEntry)));
|
|
51
|
+
}
|
|
52
|
+
function assertNarrowInteger(key, value, path) {
|
|
53
|
+
if (!Number.isSafeInteger(value))
|
|
54
|
+
fail(path, 'must be a safe integer');
|
|
55
|
+
if (key === 'ordinal' || key?.endsWith('_ordinal')) {
|
|
56
|
+
if (value < 1 || value > MAX_ORDINAL) {
|
|
57
|
+
fail(path, `must be between 1 and ${MAX_ORDINAL}`);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (key === 'exchange_index' || key?.endsWith('_index')) {
|
|
62
|
+
if (value < 0 || value > MAX_INDEX) {
|
|
63
|
+
fail(path, `must be between 0 and ${MAX_INDEX}`);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (key === 'state_code') {
|
|
68
|
+
if (value < 0 || value > 1_000)
|
|
69
|
+
fail(path, 'must be between 0 and 1000');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (key === 'count' || key?.endsWith('_count')) {
|
|
73
|
+
if (value < 0 || value > MAX_COUNT) {
|
|
74
|
+
fail(path, `must be between 0 and ${MAX_COUNT}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function visit(value, path, key, active) {
|
|
79
|
+
if (value === null || typeof value === 'boolean')
|
|
80
|
+
return;
|
|
81
|
+
if (typeof value === 'string') {
|
|
82
|
+
assertTransportString(value, path);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === 'number') {
|
|
86
|
+
assertNarrowInteger(key, value, path);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
if (active.has(value))
|
|
91
|
+
fail(path, 'must not contain a cycle');
|
|
92
|
+
active.add(value);
|
|
93
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
94
|
+
if (!(index in value))
|
|
95
|
+
fail(`${path}[${index}]`, 'must not be a sparse array entry');
|
|
96
|
+
visit(value[index], `${path}[${index}]`, null, active);
|
|
97
|
+
}
|
|
98
|
+
active.delete(value);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!isPlainWireObject(value)) {
|
|
102
|
+
fail(path, 'must contain only JSON strings, booleans, nulls, safe integers, arrays, or objects');
|
|
103
|
+
}
|
|
104
|
+
if (active.has(value))
|
|
105
|
+
fail(path, 'must not contain a cycle');
|
|
106
|
+
active.add(value);
|
|
107
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
108
|
+
const childPath = `${path}.${childKey}`;
|
|
109
|
+
assertTransportString(childKey, `${childPath} (key)`);
|
|
110
|
+
if (childKey === 'common:shortDescription' &&
|
|
111
|
+
!isStandardFlowIdentityShortDescription(childValue)) {
|
|
112
|
+
fail(childPath, 'must be a string or an exact standard STMultiLang object/array with @xml:lang and #text strings');
|
|
113
|
+
}
|
|
114
|
+
visit(childValue, childPath, childKey, active);
|
|
115
|
+
}
|
|
116
|
+
active.delete(value);
|
|
117
|
+
}
|
|
118
|
+
export function assertFlowIdentityWireJson(value, label = 'request') {
|
|
119
|
+
if (!isPlainWireObject(value))
|
|
120
|
+
fail(label, 'must be a JSON object');
|
|
121
|
+
assertFlowIdentityWireValue(value, label);
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
export function assertFlowIdentityWireValue(value, label = 'value') {
|
|
125
|
+
visit(value, label, null, new Set());
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
function arrayIndex(key) {
|
|
129
|
+
if (!/^(?:0|[1-9]\d*)$/u.test(key))
|
|
130
|
+
return null;
|
|
131
|
+
const value = Number(key);
|
|
132
|
+
return Number.isSafeInteger(value) && value >= 0 && value <= 4_294_967_294 ? value : null;
|
|
133
|
+
}
|
|
134
|
+
function compareCanonicalKeys(left, right) {
|
|
135
|
+
const leftIndex = arrayIndex(left);
|
|
136
|
+
const rightIndex = arrayIndex(right);
|
|
137
|
+
if (leftIndex !== null && rightIndex !== null)
|
|
138
|
+
return leftIndex - rightIndex;
|
|
139
|
+
if (leftIndex !== null)
|
|
140
|
+
return -1;
|
|
141
|
+
if (rightIndex !== null)
|
|
142
|
+
return 1;
|
|
143
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
144
|
+
}
|
|
145
|
+
function canonicalRestrictedJson(value) {
|
|
146
|
+
if (value === null)
|
|
147
|
+
return 'null';
|
|
148
|
+
if (typeof value === 'string' || typeof value === 'boolean')
|
|
149
|
+
return JSON.stringify(value);
|
|
150
|
+
if (typeof value === 'number')
|
|
151
|
+
return String(Object.is(value, -0) ? 0 : value);
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
return `[${value.map(canonicalRestrictedJson).join(',')}]`;
|
|
154
|
+
}
|
|
155
|
+
const object = value;
|
|
156
|
+
return `{${Object.keys(object)
|
|
157
|
+
.sort(compareCanonicalKeys)
|
|
158
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalRestrictedJson(object[key])}`)
|
|
159
|
+
.join(',')}}`;
|
|
160
|
+
}
|
|
161
|
+
export function flowIdentityRestrictedJsonText(value) {
|
|
162
|
+
assertFlowIdentityWireValue(value);
|
|
163
|
+
return canonicalRestrictedJson(value);
|
|
164
|
+
}
|
|
165
|
+
export function flowIdentityRestrictedSha256(value) {
|
|
166
|
+
return sha256Text(flowIdentityRestrictedJsonText(value));
|
|
167
|
+
}
|
|
168
|
+
export const __testInternals = {
|
|
169
|
+
MAX_COUNT,
|
|
170
|
+
MAX_INDEX,
|
|
171
|
+
MAX_ORDINAL,
|
|
172
|
+
isExactStMultiLangEntry,
|
|
173
|
+
arrayIndex,
|
|
174
|
+
compareCanonicalKeys,
|
|
175
|
+
canonicalRestrictedJson,
|
|
176
|
+
assertTransportString,
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=dataset-maintenance-flow-identity-wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-maintenance-flow-identity-wire.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-flow-identity-wire.ts"],"names":[],"mappings":"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","sourcesContent":["import { isJsonObject, sha256Text, type JsonObject } from './dataset-maintenance-contract.js';\nimport { CliError } from './errors.js';\n\nconst MAX_ORDINAL = 100_000;\nconst MAX_COUNT = 1_000_000;\nconst MAX_INDEX = 1_000_000;\n\nexport type StandardStMultiLang =\n | string\n | { '@xml:lang': string; '#text': string }\n | Array<{ '@xml:lang': string; '#text': string }>;\n\nfunction fail(path: string, message: string): never {\n throw new CliError(`Flow identity wire value ${path} ${message}.`, {\n code: 'DATASET_FLOW_IDENTITY_WIRE_INVALID',\n exitCode: 2,\n details: { path },\n });\n}\n\nfunction assertTransportString(value: string, path: string): void {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code === 0) fail(path, 'must not contain U+0000');\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = value.charCodeAt(index + 1);\n if (!Number.isInteger(next) || next < 0xdc00 || next > 0xdfff) {\n fail(path, 'must not contain an unpaired UTF-16 surrogate');\n }\n index += 1;\n continue;\n }\n if (code >= 0xdc00 && code <= 0xdfff) {\n fail(path, 'must not contain an unpaired UTF-16 surrogate');\n }\n }\n}\n\nfunction isExactStMultiLangEntry(value: unknown): value is {\n '@xml:lang': string;\n '#text': string;\n} {\n if (!isPlainWireObject(value)) return false;\n const keys = Object.keys(value).sort();\n return (\n keys.length === 2 &&\n keys[0] === '#text' &&\n keys[1] === '@xml:lang' &&\n typeof value['@xml:lang'] === 'string' &&\n typeof value['#text'] === 'string'\n );\n}\n\nfunction isPlainWireObject(value: unknown): value is JsonObject {\n if (!isJsonObject(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nexport function isStandardFlowIdentityShortDescription(\n value: unknown,\n): value is StandardStMultiLang {\n return (\n typeof value === 'string' ||\n isExactStMultiLangEntry(value) ||\n (Array.isArray(value) && value.length > 0 && value.every(isExactStMultiLangEntry))\n );\n}\n\nfunction assertNarrowInteger(key: string | null, value: number, path: string): void {\n if (!Number.isSafeInteger(value)) fail(path, 'must be a safe integer');\n if (key === 'ordinal' || key?.endsWith('_ordinal')) {\n if (value < 1 || value > MAX_ORDINAL) {\n fail(path, `must be between 1 and ${MAX_ORDINAL}`);\n }\n return;\n }\n if (key === 'exchange_index' || key?.endsWith('_index')) {\n if (value < 0 || value > MAX_INDEX) {\n fail(path, `must be between 0 and ${MAX_INDEX}`);\n }\n return;\n }\n if (key === 'state_code') {\n if (value < 0 || value > 1_000) fail(path, 'must be between 0 and 1000');\n return;\n }\n if (key === 'count' || key?.endsWith('_count')) {\n if (value < 0 || value > MAX_COUNT) {\n fail(path, `must be between 0 and ${MAX_COUNT}`);\n }\n }\n}\n\nfunction visit(value: unknown, path: string, key: string | null, active: Set<object>): void {\n if (value === null || typeof value === 'boolean') return;\n if (typeof value === 'string') {\n assertTransportString(value, path);\n return;\n }\n if (typeof value === 'number') {\n assertNarrowInteger(key, value, path);\n return;\n }\n if (Array.isArray(value)) {\n if (active.has(value)) fail(path, 'must not contain a cycle');\n active.add(value);\n for (let index = 0; index < value.length; index += 1) {\n if (!(index in value)) fail(`${path}[${index}]`, 'must not be a sparse array entry');\n visit(value[index], `${path}[${index}]`, null, active);\n }\n active.delete(value);\n return;\n }\n if (!isPlainWireObject(value)) {\n fail(\n path,\n 'must contain only JSON strings, booleans, nulls, safe integers, arrays, or objects',\n );\n }\n if (active.has(value)) fail(path, 'must not contain a cycle');\n active.add(value);\n for (const [childKey, childValue] of Object.entries(value)) {\n const childPath = `${path}.${childKey}`;\n assertTransportString(childKey, `${childPath} (key)`);\n if (\n childKey === 'common:shortDescription' &&\n !isStandardFlowIdentityShortDescription(childValue)\n ) {\n fail(\n childPath,\n 'must be a string or an exact standard STMultiLang object/array with @xml:lang and #text strings',\n );\n }\n visit(childValue, childPath, childKey, active);\n }\n active.delete(value);\n}\n\nexport function assertFlowIdentityWireJson(value: unknown, label = 'request'): JsonObject {\n if (!isPlainWireObject(value)) fail(label, 'must be a JSON object');\n assertFlowIdentityWireValue(value, label);\n return value;\n}\n\nexport function assertFlowIdentityWireValue(value: unknown, label = 'value'): unknown {\n visit(value, label, null, new Set<object>());\n return value;\n}\n\nfunction arrayIndex(key: string): number | null {\n if (!/^(?:0|[1-9]\\d*)$/u.test(key)) return null;\n const value = Number(key);\n return Number.isSafeInteger(value) && value >= 0 && value <= 4_294_967_294 ? value : null;\n}\n\nfunction compareCanonicalKeys(left: string, right: string): number {\n const leftIndex = arrayIndex(left);\n const rightIndex = arrayIndex(right);\n if (leftIndex !== null && rightIndex !== null) return leftIndex - rightIndex;\n if (leftIndex !== null) return -1;\n if (rightIndex !== null) return 1;\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction canonicalRestrictedJson(value: unknown): string {\n if (value === null) return 'null';\n if (typeof value === 'string' || typeof value === 'boolean') return JSON.stringify(value);\n if (typeof value === 'number') return String(Object.is(value, -0) ? 0 : value);\n if (Array.isArray(value)) {\n return `[${value.map(canonicalRestrictedJson).join(',')}]`;\n }\n const object = value as JsonObject;\n return `{${Object.keys(object)\n .sort(compareCanonicalKeys)\n .map((key) => `${JSON.stringify(key)}:${canonicalRestrictedJson(object[key])}`)\n .join(',')}}`;\n}\n\nexport function flowIdentityRestrictedJsonText(value: unknown): string {\n assertFlowIdentityWireValue(value);\n return canonicalRestrictedJson(value);\n}\n\nexport function flowIdentityRestrictedSha256(value: unknown): string {\n return sha256Text(flowIdentityRestrictedJsonText(value));\n}\n\nexport const __testInternals = {\n MAX_COUNT,\n MAX_INDEX,\n MAX_ORDINAL,\n isExactStMultiLangEntry,\n arrayIndex,\n compareCanonicalKeys,\n canonicalRestrictedJson,\n assertTransportString,\n};\n"]}
|