@tiangong-lca/cli 0.0.28 → 0.0.30

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 (38) hide show
  1. package/README.md +90 -2
  2. package/dist/src/cli.js +553 -4
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-command.js +11 -0
  5. package/dist/src/lib/dataset-command.js.map +1 -1
  6. package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
  7. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js +175 -0
  8. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js.map +1 -0
  9. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +511 -0
  10. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -0
  11. package/dist/src/lib/dataset-maintenance-flow-identity-command.js +26 -0
  12. package/dist/src/lib/dataset-maintenance-flow-identity-command.js.map +1 -0
  13. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js +784 -0
  14. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js.map +1 -0
  15. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js +1317 -0
  16. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js.map +1 -0
  17. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js +342 -0
  18. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js.map +1 -0
  19. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js +900 -0
  20. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js.map +1 -0
  21. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js +688 -0
  22. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js.map +1 -0
  23. package/dist/src/lib/dataset-maintenance-flow-identity-run.js +1369 -0
  24. package/dist/src/lib/dataset-maintenance-flow-identity-run.js.map +1 -0
  25. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js +144 -0
  26. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js.map +1 -0
  27. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js +377 -0
  28. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js.map +1 -0
  29. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js +178 -0
  30. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js.map +1 -0
  31. package/dist/src/lib/dataset-maintenance-remote.js +156 -10
  32. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  33. package/dist/src/lib/dataset-save-draft-run.js +667 -0
  34. package/dist/src/lib/dataset-save-draft-run.js.map +1 -1
  35. package/dist/src/lib/http.js.map +1 -1
  36. package/dist/src/lib/lca-release.js +683 -0
  37. package/dist/src/lib/lca-release.js.map +1 -0
  38. 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