@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.
Files changed (34) hide show
  1. package/README.md +85 -2
  2. package/dist/src/cli.js +544 -3
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
  5. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js +175 -0
  6. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js.map +1 -0
  7. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +511 -0
  8. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -0
  9. package/dist/src/lib/dataset-maintenance-flow-identity-command.js +26 -0
  10. package/dist/src/lib/dataset-maintenance-flow-identity-command.js.map +1 -0
  11. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js +784 -0
  12. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js.map +1 -0
  13. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js +1317 -0
  14. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js.map +1 -0
  15. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js +342 -0
  16. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js.map +1 -0
  17. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js +900 -0
  18. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js.map +1 -0
  19. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js +688 -0
  20. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js.map +1 -0
  21. package/dist/src/lib/dataset-maintenance-flow-identity-run.js +1369 -0
  22. package/dist/src/lib/dataset-maintenance-flow-identity-run.js.map +1 -0
  23. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js +144 -0
  24. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js.map +1 -0
  25. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js +377 -0
  26. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js.map +1 -0
  27. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js +178 -0
  28. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js.map +1 -0
  29. package/dist/src/lib/dataset-maintenance-remote.js +156 -10
  30. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  31. package/dist/src/lib/http.js.map +1 -1
  32. package/dist/src/lib/lca-release.js +683 -0
  33. package/dist/src/lib/lca-release.js.map +1 -0
  34. package/package.json +1 -1
@@ -0,0 +1,784 @@
1
+ import { isJsonObject, sha256Json, } from './dataset-maintenance-contract.js';
2
+ import { CliError } from './errors.js';
3
+ import { assertFlowIdentityWireJson, flowIdentityRestrictedSha256, isStandardFlowIdentityShortDescription, } from './dataset-maintenance-flow-identity-wire.js';
4
+ export const FLOW_IDENTITY_SOURCE_COUNT = 305;
5
+ export const FLOW_IDENTITY_REFERENCE_FIELDS = [
6
+ '@refObjectId',
7
+ '@type',
8
+ '@uri',
9
+ '@version',
10
+ 'common:shortDescription',
11
+ ];
12
+ export const HISTORICAL_FLOW_IDENTITY_AUTHORITY_SHA256 = new Set([
13
+ '70fc59ec2fc6059d5c38f7e36aad0d83e977244f84d2e618da9a964d8b8bcb24',
14
+ '6de26ed76f41f9fd37473fd1eb2ba34084ad28a273db01d97d97b434c38c5a9a',
15
+ '1ea6b533cd2e0b7ac75c72bdc2140cb64dac3422deb25c1f880795c42a5b9505',
16
+ 'cc0d2c13763666be51a837c87c013ff7d6b9a2a2a141fa3f14bffb07b7429829',
17
+ '06f81114f6a6c473d401612b16594ee15f017e9f627ac7189ccf7b94d32dcd58',
18
+ ]);
19
+ const HISTORICAL_APPROVAL_PATTERN = /^APPROVE BAFU STEP3 (?:LEGACY TARGET POLICY|EVIDENCE RESOLUTION)(?: V2)? /u;
20
+ const HASH_PATTERN = /^[a-f0-9]{64}$/u;
21
+ const UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u;
22
+ const VERSION_PATTERN = /^\d{2}\.\d{2}\.\d{3}$/u;
23
+ const POSTGREST_UTC_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?\+00:00$/u;
24
+ function fail(message, code = 'DATASET_FLOW_IDENTITY_CONTRACT_INVALID') {
25
+ throw new CliError(message, { code, exitCode: 2 });
26
+ }
27
+ function token(value, label) {
28
+ if (typeof value !== 'string' || !value.trim())
29
+ fail(`${label} must be a non-empty string.`);
30
+ return value.trim();
31
+ }
32
+ function hash(value, label) {
33
+ const normalized = token(value, label);
34
+ if (!HASH_PATTERN.test(normalized))
35
+ fail(`${label} must be a lowercase SHA-256.`);
36
+ return normalized;
37
+ }
38
+ function instant(value, label) {
39
+ const normalized = token(value, label);
40
+ if (!Number.isFinite(Date.parse(normalized)))
41
+ fail(`${label} must be an RFC3339 timestamp.`);
42
+ return normalized;
43
+ }
44
+ function version(value, label) {
45
+ const normalized = token(value, label);
46
+ if (!VERSION_PATTERN.test(normalized))
47
+ fail(`${label} must match NN.NN.NNN.`);
48
+ return normalized;
49
+ }
50
+ function computeSelfHash(value, field) {
51
+ return sha256Json({ ...value, [field]: '' });
52
+ }
53
+ function hasExactKeys(value, expected) {
54
+ if (!isJsonObject(value))
55
+ return false;
56
+ const actual = Object.keys(value);
57
+ return actual.length === expected.length && expected.every((key) => actual.includes(key));
58
+ }
59
+ export function assertCurrentFlowIdentityAuthority(value) {
60
+ for (const candidate of [value.oracleSha256, value.authoritySha256]) {
61
+ if (typeof candidate === 'string' && HISTORICAL_FLOW_IDENTITY_AUTHORITY_SHA256.has(candidate)) {
62
+ fail('Historical Step 3 authority hash is permanently non-executable.', 'DATASET_FLOW_IDENTITY_HISTORICAL_AUTHORITY');
63
+ }
64
+ }
65
+ if (typeof value.approvalText === 'string' &&
66
+ HISTORICAL_APPROVAL_PATTERN.test(value.approvalText.trim())) {
67
+ fail('Historical Step 3 v1/v2 approval text is permanently non-executable.', 'DATASET_FLOW_IDENTITY_HISTORICAL_AUTHORITY');
68
+ }
69
+ if (value.oracleGeneration === 'pre_step2' || value.sourceCount === 224) {
70
+ fail('Pre-Step-2 and 224-row Step 3 projections are permanently non-executable.', 'DATASET_FLOW_IDENTITY_HISTORICAL_AUTHORITY');
71
+ }
72
+ }
73
+ export function computeFlowIdentityReviewLedgerSha256(ledger) {
74
+ return computeSelfHash(ledger, 'ledger_sha256');
75
+ }
76
+ export function computeFlowIdentityCaptureSha256(capture) {
77
+ return computeSelfHash(capture, 'capture_artifact_sha256');
78
+ }
79
+ export function computeFlowIdentityCaptureEvidenceSha256(capture) {
80
+ return sha256Json({
81
+ schema_version: capture.schema_version,
82
+ captured_at_utc: capture.captured_at_utc,
83
+ environment: capture.environment,
84
+ project_ref: capture.project_ref,
85
+ account: capture.account,
86
+ prerequisites: capture.prerequisites,
87
+ sdk: capture.sdk,
88
+ review_ledger_sha256: capture.artifact_evidence.review_ledger_sha256,
89
+ toolchain_evidence_sha256: capture.artifact_evidence.toolchain_evidence_sha256,
90
+ completeness: capture.completeness,
91
+ source_rows: capture.source_rows,
92
+ target_rows: capture.target_rows,
93
+ support_rows: capture.support_rows,
94
+ process_rows: capture.process_rows,
95
+ });
96
+ }
97
+ export function computeFlowIdentityMappingId(mapping) {
98
+ const identity = Object.fromEntries(Object.entries(mapping).filter(([key]) => key !== 'ordinal' && key !== 'mapping_id'));
99
+ return sha256Json(identity);
100
+ }
101
+ export function computeFlowIdentityProcessTemplateSha256(process) {
102
+ const body = Object.fromEntries(Object.entries(process).filter(([key]) => key !== 'process_template_sha256'));
103
+ return sha256Json(body);
104
+ }
105
+ export function computeFlowIdentityPlanSha256(plan) {
106
+ return computeSelfHash(plan, 'plan_sha256');
107
+ }
108
+ export function parseFlowIdentityReference(value, label) {
109
+ if (!isJsonObject(value))
110
+ fail(`${label} must be an object.`);
111
+ const keys = Object.keys(value).sort();
112
+ if (sha256Json(keys) !== sha256Json([...FLOW_IDENTITY_REFERENCE_FIELDS].sort())) {
113
+ fail(`${label} must contain exactly the five approved reference fields.`);
114
+ }
115
+ const id = token(value['@refObjectId'], `${label}.@refObjectId`);
116
+ const referenceVersion = version(value['@version'], `${label}.@version`);
117
+ const uri = token(value['@uri'], `${label}.@uri`);
118
+ const shortDescription = value['common:shortDescription'];
119
+ if (!UUID_PATTERN.test(id) ||
120
+ value['@type'] !== 'flow data set' ||
121
+ Buffer.byteLength(uri, 'utf8') > 2_048 ||
122
+ !isStandardFlowIdentityShortDescription(shortDescription)) {
123
+ fail(`${label} type or URI is invalid.`);
124
+ }
125
+ return {
126
+ '@refObjectId': id,
127
+ '@type': 'flow data set',
128
+ '@uri': uri,
129
+ '@version': referenceVersion,
130
+ 'common:shortDescription': structuredClone(shortDescription),
131
+ };
132
+ }
133
+ export function extractFlowIdentityReference(value, label) {
134
+ if (!isJsonObject(value))
135
+ fail(`${label} must be an object.`);
136
+ return parseFlowIdentityReference(Object.fromEntries(FLOW_IDENTITY_REFERENCE_FIELDS.map((field) => [field, value[field]])), label);
137
+ }
138
+ export function parseFlowIdentityPolicy(value) {
139
+ if (!isJsonObject(value))
140
+ fail('Flow identity compatibility policy is invalid.');
141
+ assertCurrentFlowIdentityAuthority({
142
+ authoritySha256: value.policy_sha256,
143
+ approvalText: value.approval_text,
144
+ });
145
+ assertCurrentFlowIdentityAuthority({ authoritySha256: value.evidence_resolution_sha256 });
146
+ assertCurrentFlowIdentityAuthority({ authoritySha256: value.approval_text_sha256 });
147
+ if (!hasExactKeys(value, [
148
+ 'schema_version',
149
+ 'policy_sha256',
150
+ 'evidence_resolution_sha256',
151
+ 'approved_at_utc',
152
+ 'approval_text_sha256',
153
+ ]) ||
154
+ value.schema_version !== 'dataset-flow-identity-compatibility-policy.v1' ||
155
+ !HASH_PATTERN.test(token(value.policy_sha256, 'policy_sha256')) ||
156
+ !HASH_PATTERN.test(token(value.evidence_resolution_sha256, 'evidence_resolution_sha256')) ||
157
+ !HASH_PATTERN.test(token(value.approval_text_sha256, 'approval_text_sha256'))) {
158
+ fail('Flow identity compatibility policy fields are invalid.');
159
+ }
160
+ instant(value.approved_at_utc, 'approved_at_utc');
161
+ return value;
162
+ }
163
+ function parseReviewEntry(value, index) {
164
+ const label = `entries[${index}]`;
165
+ if (!isJsonObject(value) || !isJsonObject(value.source))
166
+ fail(`${label} is invalid.`);
167
+ const disposition = token(value.disposition, `${label}.disposition`);
168
+ const sourceId = token(value.source.id, `${label}.source.id`);
169
+ if (!['map_public', 'pending', 'blocker', 'orphan'].includes(disposition)) {
170
+ fail(`${label}.disposition is unsupported.`);
171
+ }
172
+ const target = isJsonObject(value.target)
173
+ ? {
174
+ id: token(value.target.id, `${label}.target.id`),
175
+ version: version(value.target.version, `${label}.target.version`),
176
+ reference: parseFlowIdentityReference(value.target.reference, `${label}.target.reference`),
177
+ }
178
+ : null;
179
+ const directions = Array.isArray(value.allowed_directions)
180
+ ? value.allowed_directions.map((entry) => token(entry, `${label}.allowed_directions`))
181
+ : [];
182
+ if (!UUID_PATTERN.test(sourceId) ||
183
+ (target !== null && !UUID_PATTERN.test(target.id)) ||
184
+ directions.some((entry) => entry !== 'Input' && entry !== 'Output') ||
185
+ new Set(directions).size !== directions.length ||
186
+ (disposition === 'map_public') !== Boolean(target && directions.length)) {
187
+ fail(`${label} target/direction fields do not match its disposition.`);
188
+ }
189
+ return {
190
+ source: {
191
+ id: sourceId,
192
+ version: version(value.source.version, `${label}.source.version`),
193
+ },
194
+ disposition: disposition,
195
+ target,
196
+ allowed_directions: directions,
197
+ source_trace_sha256: hash(value.source_trace_sha256, `${label}.source_trace_sha256`),
198
+ compartment_evidence_sha256: hash(value.compartment_evidence_sha256, `${label}.compartment_evidence_sha256`),
199
+ decision_evidence_sha256: hash(value.decision_evidence_sha256, `${label}.decision_evidence_sha256`),
200
+ };
201
+ }
202
+ export function parseFlowIdentityReviewLedger(value) {
203
+ if (!isJsonObject(value) || !Array.isArray(value.entries)) {
204
+ fail('Flow identity review ledger is invalid.');
205
+ }
206
+ assertCurrentFlowIdentityAuthority({
207
+ oracleSha256: value.ledger_sha256,
208
+ oracleGeneration: value.oracle_generation,
209
+ sourceCount: value.source_count,
210
+ });
211
+ const entries = value.entries.map(parseReviewEntry);
212
+ const ledger = { ...value, entries };
213
+ if (ledger.schema_version !== 'dataset-flow-identity-review-ledger.v3' ||
214
+ ledger.source_count !== FLOW_IDENTITY_SOURCE_COUNT ||
215
+ ledger.execution_authority !== false ||
216
+ entries.length !== FLOW_IDENTITY_SOURCE_COUNT ||
217
+ new Set(entries.map((entry) => `${entry.source.id}@${entry.source.version}`)).size !==
218
+ entries.length ||
219
+ !Number.isFinite(Date.parse(ledger.generated_at_utc)) ||
220
+ !HASH_PATTERN.test(ledger.review_evidence_sha256) ||
221
+ ledger.ledger_sha256 !== computeFlowIdentityReviewLedgerSha256(ledger)) {
222
+ fail('Review ledger must contain exactly 305 unique v3 source decisions.');
223
+ }
224
+ return ledger;
225
+ }
226
+ export function parseFlowIdentityCapture(value) {
227
+ if (!isJsonObject(value) ||
228
+ !isJsonObject(value.account) ||
229
+ !isJsonObject(value.prerequisites) ||
230
+ !isJsonObject(value.sdk) ||
231
+ !isJsonObject(value.artifact_evidence) ||
232
+ !isJsonObject(value.completeness) ||
233
+ !isJsonObject(value.capture_request) ||
234
+ !isJsonObject(value.attestation)) {
235
+ fail('Flow identity live capture is invalid.');
236
+ }
237
+ assertCurrentFlowIdentityAuthority({ oracleSha256: value.capture_artifact_sha256 });
238
+ const capture = value;
239
+ const capturedAt = Date.parse(instant(capture.captured_at_utc, 'captured_at_utc'));
240
+ const step2At = Date.parse(instant(capture.prerequisites.step2_completed_at_utc, 'step2_completed_at_utc'));
241
+ const issue29Target1At = Date.parse(instant(capture.prerequisites.issue29_target1_completed_at_utc, 'issue29_target1_completed_at_utc'));
242
+ const issue29Target2At = Date.parse(instant(capture.prerequisites.issue29_target2_completed_at_utc, 'issue29_target2_completed_at_utc'));
243
+ const attestation = capture.attestation;
244
+ const attestedAt = Date.parse(instant(attestation.captured_at, 'attestation.captured_at'));
245
+ const expiresAt = Date.parse(instant(attestation.expires_at, 'attestation.expires_at'));
246
+ const attestationKeys = [
247
+ 'ok',
248
+ 'command',
249
+ 'schema_version',
250
+ 'proof_domain',
251
+ 'receipt_id',
252
+ 'receipt_proof_sha256',
253
+ 'operation_id',
254
+ 'environment',
255
+ 'project_ref',
256
+ 'captured_at',
257
+ 'expires_at',
258
+ 'source_guard_set_sha256',
259
+ 'support_guard_set_sha256',
260
+ 'target_guard_set_sha256',
261
+ 'mapping_guard_set_sha256',
262
+ 'process_intent_set_sha256',
263
+ 'protected_closure_sha256',
264
+ 'whole_scope_proof_sha256',
265
+ 'policy_sha256',
266
+ 'policy_approval_text_sha256',
267
+ 'source_count',
268
+ 'target_count',
269
+ 'support_count',
270
+ 'mapping_count',
271
+ 'process_count',
272
+ 'rewrite_count',
273
+ 'capture_request_sha256',
274
+ 'replay',
275
+ ];
276
+ const completeness = capture.completeness;
277
+ const processScan = completeness.owner_draft_process_scan;
278
+ const request = assertFlowIdentityWireJson(capture.capture_request);
279
+ const requestHash = flowIdentityRestrictedSha256(request);
280
+ if (capture.schema_version !== 'dataset-flow-identity-live-capture.v2' ||
281
+ !['production', 'preview', 'local'].includes(capture.environment) ||
282
+ !token(capture.project_ref, 'project_ref') ||
283
+ !token(capture.account.user_id, 'account.user_id') ||
284
+ capture.account.email !== token(capture.account.email, 'account.email').toLowerCase() ||
285
+ capture.sdk.package !== '@tiangong-lca/tidas-sdk' ||
286
+ !token(capture.sdk.version, 'sdk.version') ||
287
+ !Array.isArray(capture.source_rows) ||
288
+ !Array.isArray(capture.target_rows) ||
289
+ !Array.isArray(capture.support_rows) ||
290
+ !Array.isArray(capture.process_rows) ||
291
+ !hasExactKeys(request, [
292
+ 'schema_version',
293
+ 'request_id',
294
+ 'environment',
295
+ 'project_ref',
296
+ 'actor',
297
+ 'target_visibility',
298
+ 'operation_id',
299
+ 'compatibility_policy',
300
+ 'artifact_evidence',
301
+ 'mappings',
302
+ 'process_intents',
303
+ 'protected_closure',
304
+ ]) ||
305
+ !isJsonObject(request.actor) ||
306
+ !hasExactKeys(request.actor, ['user_id', 'email']) ||
307
+ !isJsonObject(request.artifact_evidence) ||
308
+ !hasExactKeys(request.artifact_evidence, [
309
+ 'review_ledger_sha256',
310
+ 'live_capture_artifact_sha256',
311
+ 'toolchain_evidence_sha256',
312
+ ]) ||
313
+ !isJsonObject(request.compatibility_policy) ||
314
+ !Array.isArray(request.mappings) ||
315
+ !Array.isArray(request.process_intents) ||
316
+ !isJsonObject(request.protected_closure) ||
317
+ request.schema_version !== 'dataset-flow-identity-capture-attest.v2' ||
318
+ !UUID_PATTERN.test(request.request_id) ||
319
+ request.environment !== capture.environment ||
320
+ request.project_ref !== capture.project_ref ||
321
+ request.actor.user_id !== capture.account.user_id ||
322
+ request.actor.email !== capture.account.email ||
323
+ request.target_visibility !== 'owner_draft' ||
324
+ request.operation_id !== attestation.operation_id ||
325
+ request.artifact_evidence.review_ledger_sha256 !==
326
+ capture.artifact_evidence.review_ledger_sha256 ||
327
+ request.artifact_evidence.live_capture_artifact_sha256 !==
328
+ capture.artifact_evidence.live_capture_artifact_sha256 ||
329
+ request.artifact_evidence.toolchain_evidence_sha256 !==
330
+ capture.artifact_evidence.toolchain_evidence_sha256 ||
331
+ !hasExactKeys(attestation, attestationKeys) ||
332
+ attestation.ok !== true ||
333
+ attestation.command !== 'cmd_dataset_flow_identity_capture_attest_guarded' ||
334
+ attestation.schema_version !== 'dataset-flow-identity-capture-attest-result.v2' ||
335
+ attestation.proof_domain !== 'dataset-flow-identity-db-proof.v2' ||
336
+ !UUID_PATTERN.test(attestation.receipt_id) ||
337
+ !HASH_PATTERN.test(attestation.receipt_proof_sha256) ||
338
+ ![
339
+ attestation.receipt_proof_sha256,
340
+ attestation.source_guard_set_sha256,
341
+ attestation.support_guard_set_sha256,
342
+ attestation.target_guard_set_sha256,
343
+ attestation.mapping_guard_set_sha256,
344
+ attestation.process_intent_set_sha256,
345
+ attestation.protected_closure_sha256,
346
+ attestation.whole_scope_proof_sha256,
347
+ attestation.policy_sha256,
348
+ attestation.policy_approval_text_sha256,
349
+ attestation.capture_request_sha256,
350
+ ].every((digest) => HASH_PATTERN.test(digest)) ||
351
+ attestation.capture_request_sha256 !== requestHash ||
352
+ attestation.policy_sha256 !== request.compatibility_policy.policy_sha256 ||
353
+ attestation.policy_approval_text_sha256 !== request.compatibility_policy.approval_text_sha256 ||
354
+ attestation.environment !== capture.environment ||
355
+ attestation.project_ref !== capture.project_ref ||
356
+ attestation.source_count !== FLOW_IDENTITY_SOURCE_COUNT ||
357
+ !Number.isSafeInteger(attestation.target_count) ||
358
+ attestation.target_count < 1 ||
359
+ !Number.isSafeInteger(attestation.support_count) ||
360
+ attestation.support_count < 2 ||
361
+ !Number.isSafeInteger(attestation.mapping_count) ||
362
+ attestation.mapping_count < 1 ||
363
+ attestation.mapping_count !== request.mappings.length ||
364
+ !Number.isSafeInteger(attestation.process_count) ||
365
+ attestation.process_count < 1 ||
366
+ attestation.process_count !== request.process_intents.length ||
367
+ !Number.isSafeInteger(attestation.rewrite_count) ||
368
+ attestation.rewrite_count < 1 ||
369
+ typeof attestation.replay !== 'boolean' ||
370
+ attestedAt + 5 * 60 * 1_000 < capturedAt ||
371
+ expiresAt <= attestedAt ||
372
+ expiresAt - attestedAt > 7 * 24 * 60 * 60 * 1_000 ||
373
+ completeness.schema_version !== 'dataset-flow-identity-capture-completeness.v2' ||
374
+ completeness.source_count !== FLOW_IDENTITY_SOURCE_COUNT ||
375
+ completeness.target_count !== capture.target_rows.length ||
376
+ completeness.support_count !== capture.support_rows.length ||
377
+ !Number.isSafeInteger(completeness.owner_draft_process_count) ||
378
+ completeness.owner_draft_process_count < capture.process_rows.length ||
379
+ !isJsonObject(processScan) ||
380
+ processScan.status !== 'complete' ||
381
+ processScan.complete !== true ||
382
+ processScan.strategy !== 'postgrest_exact_count' ||
383
+ processScan.rows_fetched !== completeness.owner_draft_process_count ||
384
+ processScan.exact_total !== completeness.owner_draft_process_count ||
385
+ processScan.content_range_verified !== true ||
386
+ processScan.ordering_verified !== true ||
387
+ processScan.duplicate_count !== 0 ||
388
+ !HASH_PATTERN.test(String(processScan.row_identity_set_sha256)) ||
389
+ !HASH_PATTERN.test(String(processScan.row_snapshot_set_sha256)) ||
390
+ new Set(capture.process_rows.map((row) => `${row.id}\u0000${row.version}`)).size !==
391
+ capture.process_rows.length ||
392
+ capture.source_rows.length !== FLOW_IDENTITY_SOURCE_COUNT ||
393
+ attestation.target_count !== capture.target_rows.length ||
394
+ attestation.support_count !== capture.support_rows.length ||
395
+ capturedAt < step2At ||
396
+ capturedAt < issue29Target1At ||
397
+ capturedAt < issue29Target2At ||
398
+ !HASH_PATTERN.test(capture.prerequisites.step2_readback_sha256) ||
399
+ !HASH_PATTERN.test(capture.prerequisites.issue29_target1_readback_sha256) ||
400
+ !HASH_PATTERN.test(capture.prerequisites.issue29_target2_readback_sha256) ||
401
+ !HASH_PATTERN.test(capture.artifact_evidence.review_ledger_sha256) ||
402
+ !HASH_PATTERN.test(capture.artifact_evidence.live_capture_artifact_sha256) ||
403
+ !HASH_PATTERN.test(capture.artifact_evidence.toolchain_evidence_sha256) ||
404
+ capture.artifact_evidence.live_capture_artifact_sha256 !==
405
+ computeFlowIdentityCaptureEvidenceSha256(capture) ||
406
+ capture.capture_artifact_sha256 !== computeFlowIdentityCaptureSha256(capture)) {
407
+ fail('Live capture is stale, incomplete, historical, or tampered.');
408
+ }
409
+ return capture;
410
+ }
411
+ function validOneBasedOrdinals(values) {
412
+ return values.every((value, index) => value.ordinal === index + 1);
413
+ }
414
+ function validOccurrenceEntry(entry) {
415
+ return Boolean(hasExactKeys(entry, [
416
+ 'source_id',
417
+ 'source_version',
418
+ 'expected_reference_count',
419
+ 'occurrences',
420
+ 'occurrence_set_sha256',
421
+ 'evidence_sha256',
422
+ ]) &&
423
+ UUID_PATTERN.test(entry.source_id) &&
424
+ VERSION_PATTERN.test(entry.source_version) &&
425
+ Number.isInteger(entry.expected_reference_count) &&
426
+ entry.expected_reference_count === entry.occurrences.length &&
427
+ HASH_PATTERN.test(entry.evidence_sha256) &&
428
+ entry.occurrence_set_sha256 === sha256Json(entry.occurrences) &&
429
+ entry.occurrences.every((occurrence, index) => hasExactKeys(occurrence, [
430
+ 'process_id',
431
+ 'process_version',
432
+ 'exchange_index',
433
+ 'internal_id',
434
+ 'direction',
435
+ 'reference_sha256',
436
+ ]) &&
437
+ UUID_PATTERN.test(occurrence.process_id) &&
438
+ VERSION_PATTERN.test(occurrence.process_version) &&
439
+ Number.isInteger(occurrence.exchange_index) &&
440
+ occurrence.exchange_index >= 0 &&
441
+ Boolean(occurrence.internal_id) &&
442
+ ['Input', 'Output'].includes(occurrence.direction) &&
443
+ HASH_PATTERN.test(occurrence.reference_sha256) &&
444
+ (index === 0 ||
445
+ `${entry.occurrences[index - 1].process_id}\u0000${entry.occurrences[index - 1].process_version}\u0000${String(entry.occurrences[index - 1].exchange_index).padStart(12, '0')}` <
446
+ `${occurrence.process_id}\u0000${occurrence.process_version}\u0000${String(occurrence.exchange_index).padStart(12, '0')}`)));
447
+ }
448
+ function supportSnapshotRowSha256(snapshot) {
449
+ return sha256Json({
450
+ id: snapshot.id,
451
+ version: snapshot.version,
452
+ user_id: snapshot.user_id,
453
+ state_code: snapshot.state_code,
454
+ modified_at: snapshot.modified_at,
455
+ payload_sha256: snapshot.payload_sha256,
456
+ });
457
+ }
458
+ function validSupportSnapshot(snapshot, actorUserId) {
459
+ return Boolean(hasExactKeys(snapshot, [
460
+ 'ordinal',
461
+ 'table',
462
+ 'id',
463
+ 'version',
464
+ 'user_id',
465
+ 'state_code',
466
+ 'modified_at',
467
+ 'payload_sha256',
468
+ 'row_sha256',
469
+ ]) &&
470
+ ['flowproperties', 'unitgroups'].includes(snapshot.table) &&
471
+ UUID_PATTERN.test(snapshot.id) &&
472
+ VERSION_PATTERN.test(snapshot.version) &&
473
+ UUID_PATTERN.test(snapshot.user_id) &&
474
+ [0, 100].includes(snapshot.state_code) &&
475
+ (snapshot.state_code !== 0 || snapshot.user_id === actorUserId) &&
476
+ POSTGREST_UTC_TIMESTAMP_PATTERN.test(snapshot.modified_at) &&
477
+ HASH_PATTERN.test(snapshot.payload_sha256) &&
478
+ snapshot.row_sha256 === supportSnapshotRowSha256(snapshot));
479
+ }
480
+ function validMapping(mapping, actorUserId) {
481
+ const targetReference = parseFlowIdentityReference(mapping.target.reference, 'mapping target.reference');
482
+ const hashes = [
483
+ mapping.source.payload_sha256,
484
+ mapping.source.row_sha256,
485
+ mapping.source.category_path_sha256,
486
+ mapping.source.source_trace_sha256,
487
+ mapping.target.payload_sha256,
488
+ mapping.target.row_sha256,
489
+ mapping.target.category_path_sha256,
490
+ mapping.compatibility.policy_sha256,
491
+ mapping.compatibility.evidence_sha256,
492
+ mapping.compatibility.flow_schema.warning_set_sha256,
493
+ ];
494
+ return Boolean(hasExactKeys(mapping, ['ordinal', 'mapping_id', 'source', 'target', 'compatibility']) &&
495
+ hasExactKeys(mapping.source, [
496
+ 'id',
497
+ 'version',
498
+ 'user_id',
499
+ 'state_code',
500
+ 'modified_at',
501
+ 'payload_sha256',
502
+ 'row_sha256',
503
+ 'flow_type',
504
+ 'flow_property_id',
505
+ 'flow_property_version',
506
+ 'unit_group_id',
507
+ 'unit_group_version',
508
+ 'category_path_sha256',
509
+ 'source_trace_sha256',
510
+ ]) &&
511
+ hasExactKeys(mapping.target, [
512
+ 'id',
513
+ 'version',
514
+ 'user_id',
515
+ 'state_code',
516
+ 'modified_at',
517
+ 'payload_sha256',
518
+ 'row_sha256',
519
+ 'flow_type',
520
+ 'flow_property_id',
521
+ 'flow_property_version',
522
+ 'unit_group_id',
523
+ 'unit_group_version',
524
+ 'category_path_sha256',
525
+ 'reference',
526
+ ]) &&
527
+ hasExactKeys(mapping.compatibility, [
528
+ 'policy_sha256',
529
+ 'mode',
530
+ 'confidence',
531
+ 'flow_property_compatible',
532
+ 'unit_group_compatible',
533
+ 'direction_compatible',
534
+ 'compartment_compatible',
535
+ 'conversion_factor',
536
+ 'evidence_sha256',
537
+ 'flow_schema',
538
+ 'process_schema_required',
539
+ ]) &&
540
+ hasExactKeys(mapping.compatibility.flow_schema, ['status', 'warning_set_sha256']) &&
541
+ UUID_PATTERN.test(mapping.source.id) &&
542
+ UUID_PATTERN.test(mapping.target.id) &&
543
+ VERSION_PATTERN.test(mapping.source.version) &&
544
+ VERSION_PATTERN.test(mapping.target.version) &&
545
+ mapping.source.user_id === actorUserId &&
546
+ mapping.source.state_code === 0 &&
547
+ mapping.source.flow_type === 'Elementary flow' &&
548
+ POSTGREST_UTC_TIMESTAMP_PATTERN.test(mapping.source.modified_at) &&
549
+ mapping.target.user_id !== actorUserId &&
550
+ mapping.target.state_code === 100 &&
551
+ mapping.target.flow_type === 'Elementary flow' &&
552
+ POSTGREST_UTC_TIMESTAMP_PATTERN.test(mapping.target.modified_at) &&
553
+ mapping.source.flow_property_id === mapping.target.flow_property_id &&
554
+ mapping.source.flow_property_version === mapping.target.flow_property_version &&
555
+ mapping.source.unit_group_id === mapping.target.unit_group_id &&
556
+ mapping.source.unit_group_version === mapping.target.unit_group_version &&
557
+ targetReference['@refObjectId'] === mapping.target.id &&
558
+ targetReference['@version'] === mapping.target.version &&
559
+ mapping.compatibility.mode === 'identity' &&
560
+ mapping.compatibility.confidence === 'approved' &&
561
+ mapping.compatibility.flow_property_compatible === true &&
562
+ mapping.compatibility.unit_group_compatible === true &&
563
+ mapping.compatibility.direction_compatible === true &&
564
+ mapping.compatibility.compartment_compatible === true &&
565
+ mapping.compatibility.conversion_factor === '1' &&
566
+ ['pass', 'legacy_warning'].includes(mapping.compatibility.flow_schema.status) &&
567
+ mapping.compatibility.process_schema_required === 'pass' &&
568
+ hashes.every((value) => HASH_PATTERN.test(value)));
569
+ }
570
+ function validProcess(process, actorUserId) {
571
+ const hashes = [
572
+ process.before_row_sha256,
573
+ process.before_payload_sha256,
574
+ process.before_exchange_set_sha256,
575
+ process.desired_payload_sha256,
576
+ process.desired_exchange_set_sha256,
577
+ process.process_template_sha256,
578
+ process.rewrite_set_sha256,
579
+ process.collision_ledger_sha256,
580
+ process.process_schema.evidence_sha256,
581
+ process.pending_blocker_closure_sha256,
582
+ ];
583
+ return Boolean(hasExactKeys(process, [
584
+ 'ordinal',
585
+ 'id',
586
+ 'version',
587
+ 'user_id',
588
+ 'state_code',
589
+ 'modified_at',
590
+ 'model_id',
591
+ 'rule_verification',
592
+ 'before_row_sha256',
593
+ 'before_payload_sha256',
594
+ 'before_exchange_set_sha256',
595
+ 'before_exchange_count',
596
+ 'desired_payload_sha256',
597
+ 'desired_exchange_set_sha256',
598
+ 'rewrite_count',
599
+ 'process_template_sha256',
600
+ 'rewrite_set_sha256',
601
+ 'collision_ledger_sha256',
602
+ 'process_schema',
603
+ 'pending_blocker_closure_sha256',
604
+ ]) &&
605
+ hasExactKeys(process.process_schema, ['status', 'evidence_sha256']) &&
606
+ UUID_PATTERN.test(process.id) &&
607
+ VERSION_PATTERN.test(process.version) &&
608
+ process.user_id === actorUserId &&
609
+ process.state_code === 0 &&
610
+ POSTGREST_UTC_TIMESTAMP_PATTERN.test(process.modified_at) &&
611
+ (process.model_id === null || UUID_PATTERN.test(process.model_id)) &&
612
+ (process.rule_verification === null || typeof process.rule_verification === 'boolean') &&
613
+ Number.isInteger(process.before_exchange_count) &&
614
+ process.before_exchange_count > 0 &&
615
+ Number.isInteger(process.rewrite_count) &&
616
+ process.rewrite_count > 0 &&
617
+ process.process_schema.status === 'pass' &&
618
+ hashes.every((value) => HASH_PATTERN.test(value)) &&
619
+ process.process_template_sha256 === computeFlowIdentityProcessTemplateSha256(process));
620
+ }
621
+ export function parseFlowIdentityPlan(value) {
622
+ if (!isJsonObject(value) ||
623
+ !Array.isArray(value.support_snapshots) ||
624
+ !Array.isArray(value.mappings) ||
625
+ !Array.isArray(value.processes) ||
626
+ !isJsonObject(value.protected_closure) ||
627
+ !isJsonObject(value.summary) ||
628
+ !isJsonObject(value.artifacts)) {
629
+ fail('Flow identity plan is invalid.');
630
+ }
631
+ assertCurrentFlowIdentityAuthority({ oracleSha256: value.capture_artifact_sha256 });
632
+ const plan = value;
633
+ parseFlowIdentityPolicy(plan.compatibility_policy);
634
+ const protectedClosure = plan.protected_closure;
635
+ const sourceUniverse = [
636
+ ...plan.mappings.map((mapping) => ({
637
+ id: mapping.source.id,
638
+ version: mapping.source.version,
639
+ user_id: plan.account.user_id,
640
+ state_code: 0,
641
+ flow_type: 'Elementary flow',
642
+ })),
643
+ ...protectedClosure.pending.map((entry) => ({
644
+ id: entry.source_id,
645
+ version: entry.source_version,
646
+ user_id: plan.account.user_id,
647
+ state_code: 0,
648
+ flow_type: 'Elementary flow',
649
+ })),
650
+ ...protectedClosure.blockers.map((entry) => ({
651
+ id: entry.source_id,
652
+ version: entry.source_version,
653
+ user_id: plan.account.user_id,
654
+ state_code: 0,
655
+ flow_type: 'Elementary flow',
656
+ })),
657
+ ...protectedClosure.orphans.map((entry) => ({
658
+ id: entry.source_id,
659
+ version: entry.source_version,
660
+ user_id: plan.account.user_id,
661
+ state_code: 0,
662
+ flow_type: 'Elementary flow',
663
+ })),
664
+ ].sort((left, right) => `${left.id}\u0000${left.version}`.localeCompare(`${right.id}\u0000${right.version}`));
665
+ const validSourceUniverse = sourceUniverse.length === FLOW_IDENTITY_SOURCE_COUNT &&
666
+ new Set(sourceUniverse.map((entry) => `${entry.id}\u0000${entry.version}`)).size ===
667
+ FLOW_IDENTITY_SOURCE_COUNT &&
668
+ plan.source_universe_artifact_sha256 === sha256Json(sourceUniverse);
669
+ const claimedSupport = new Set();
670
+ for (const mapping of plan.mappings) {
671
+ for (const endpoint of [mapping.source, mapping.target]) {
672
+ claimedSupport.add(`flowproperties\u0000${endpoint.flow_property_id}\u0000${endpoint.flow_property_version}`);
673
+ claimedSupport.add(`unitgroups\u0000${endpoint.unit_group_id}\u0000${endpoint.unit_group_version}`);
674
+ }
675
+ }
676
+ const sealedSupport = new Set(plan.support_snapshots.map((snapshot) => `${snapshot.table}\u0000${snapshot.id}\u0000${snapshot.version}`));
677
+ const validSupport = plan.support_snapshots.length >= 2 &&
678
+ plan.support_snapshots.length <= 100 &&
679
+ validOneBasedOrdinals(plan.support_snapshots) &&
680
+ sealedSupport.size === plan.support_snapshots.length &&
681
+ sealedSupport.size === claimedSupport.size &&
682
+ [...sealedSupport].every((identity) => claimedSupport.has(identity)) &&
683
+ plan.support_snapshots.every((snapshot) => validSupportSnapshot(snapshot, plan.account.user_id)) &&
684
+ plan.support_snapshot_artifact_sha256 === sha256Json(plan.support_snapshots);
685
+ const validProtected = hasExactKeys(protectedClosure, [
686
+ 'schema_version',
687
+ 'pending',
688
+ 'blockers',
689
+ 'orphans',
690
+ 'pending_set_sha256',
691
+ 'blocker_set_sha256',
692
+ 'orphan_set_sha256',
693
+ 'total_expected_reference_count',
694
+ ]) &&
695
+ protectedClosure.schema_version === 'dataset-flow-identity-protected-closure.v1' &&
696
+ Array.isArray(protectedClosure.pending) &&
697
+ Array.isArray(protectedClosure.blockers) &&
698
+ Array.isArray(protectedClosure.orphans) &&
699
+ protectedClosure.pending_set_sha256 === sha256Json(protectedClosure.pending) &&
700
+ protectedClosure.blocker_set_sha256 === sha256Json(protectedClosure.blockers) &&
701
+ protectedClosure.orphan_set_sha256 === sha256Json(protectedClosure.orphans) &&
702
+ protectedClosure.total_expected_reference_count ===
703
+ [...protectedClosure.pending, ...protectedClosure.blockers].reduce((sum, entry) => sum + entry.expected_reference_count, 0) &&
704
+ [...protectedClosure.pending, ...protectedClosure.blockers].every(validOccurrenceEntry) &&
705
+ protectedClosure.orphans.every((entry) => hasExactKeys(entry, ['source_id', 'source_version', 'evidence_sha256']) &&
706
+ UUID_PATTERN.test(entry.source_id) &&
707
+ VERSION_PATTERN.test(entry.source_version) &&
708
+ HASH_PATTERN.test(entry.evidence_sha256));
709
+ const valid = plan.schema_version === 'dataset-flow-identity-plan.v2' &&
710
+ plan.status === 'ready' &&
711
+ plan.target_visibility === 'owner_draft' &&
712
+ plan.summary.semantic_sources === FLOW_IDENTITY_SOURCE_COUNT &&
713
+ plan.summary.mappings === plan.mappings.length &&
714
+ plan.summary.mappings > 0 &&
715
+ plan.summary.processes === plan.processes.length &&
716
+ plan.summary.processes > 0 &&
717
+ plan.summary.rewrites === plan.processes.reduce((sum, entry) => sum + entry.rewrite_count, 0) &&
718
+ Number.isInteger(plan.summary.collision_entries) &&
719
+ plan.summary.collision_entries >= 0 &&
720
+ plan.summary.pending === protectedClosure.pending.length &&
721
+ plan.summary.blockers === protectedClosure.blockers.length &&
722
+ plan.summary.orphans === protectedClosure.orphans.length &&
723
+ plan.summary.protected_references === protectedClosure.total_expected_reference_count &&
724
+ validSourceUniverse &&
725
+ validSupport &&
726
+ validOneBasedOrdinals(plan.mappings) &&
727
+ validOneBasedOrdinals(plan.processes) &&
728
+ new Set(plan.mappings.map((mapping) => mapping.mapping_id)).size === plan.mappings.length &&
729
+ new Set(plan.mappings.map((mapping) => `${mapping.source.id}\u0000${mapping.source.version}`))
730
+ .size === plan.mappings.length &&
731
+ new Set(plan.processes.map((process) => `${process.id}\u0000${process.version}`)).size ===
732
+ plan.processes.length &&
733
+ plan.mappings.every((mapping) => {
734
+ return (validMapping(mapping, plan.account.user_id) &&
735
+ mapping.compatibility.policy_sha256 === plan.compatibility_policy.policy_sha256 &&
736
+ mapping.mapping_id === computeFlowIdentityMappingId(mapping));
737
+ }) &&
738
+ plan.processes.every((process) => validProcess(process, plan.account.user_id) &&
739
+ process.pending_blocker_closure_sha256 === plan.protected_closure_artifact_sha256) &&
740
+ plan.mapping_artifact_sha256 === sha256Json(plan.mappings) &&
741
+ plan.process_manifest_artifact_sha256 === sha256Json(plan.processes) &&
742
+ plan.protected_closure_artifact_sha256 === sha256Json(protectedClosure) &&
743
+ UUID_PATTERN.test(plan.receipt_id) &&
744
+ [
745
+ plan.receipt_proof_sha256,
746
+ plan.capture_request_sha256,
747
+ plan.source_guard_set_sha256,
748
+ plan.support_guard_set_sha256,
749
+ plan.target_guard_set_sha256,
750
+ plan.mapping_guard_set_sha256,
751
+ plan.process_intent_set_sha256,
752
+ plan.receipt_protected_closure_sha256,
753
+ plan.capture_whole_scope_proof_sha256,
754
+ plan.capture_artifact_sha256,
755
+ ].every((digest) => HASH_PATTERN.test(digest)) &&
756
+ validProtected &&
757
+ plan.plan_sha256 === computeFlowIdentityPlanSha256(plan) &&
758
+ plan.artifacts.plan === 'flow-identity-plan.json' &&
759
+ plan.artifacts.live_capture === 'flow-identity-live-capture.json' &&
760
+ plan.artifacts.process_manifest === 'flow-identity-process-manifest.jsonl' &&
761
+ plan.artifacts.collision_ledger === 'flow-identity-collision-ledger.jsonl' &&
762
+ plan.artifacts.protected_closure === 'flow-identity-protected-closure.json' &&
763
+ plan.artifacts.desired_payload_dir === 'desired-processes' &&
764
+ plan.artifacts.process_request_dir === 'process-requests';
765
+ if (!valid)
766
+ fail('Flow identity plan is internally inconsistent or tampered.');
767
+ return plan;
768
+ }
769
+ export const __testInternals = {
770
+ computeSelfHash,
771
+ hash,
772
+ hasExactKeys,
773
+ instant,
774
+ parseReviewEntry,
775
+ supportSnapshotRowSha256,
776
+ token,
777
+ validMapping,
778
+ validOccurrenceEntry,
779
+ validOneBasedOrdinals,
780
+ validProcess,
781
+ validSupportSnapshot,
782
+ version,
783
+ };
784
+ //# sourceMappingURL=dataset-maintenance-flow-identity-contract.js.map