@slicedearth/whoisleuth-cli 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +6 -0
- package/README.md +20 -15
- package/bin/whoisleuth.mjs +8 -1
- package/cli/archive-content-digest.mjs +14 -0
- package/cli/archive-inspect.mjs +20 -53
- package/cli/arguments.mjs +454 -1748
- package/cli/artifact-validation/case-response.mjs +66 -35
- package/cli/artifact-validation/investigation-capsule.mjs +52 -31
- package/cli/artifact-validation/lookup-evidence.mjs +12 -6
- package/cli/artifact-validation/signed-review.mjs +112 -72
- package/cli/artifact-validation/structure-primitives.mjs +1 -105
- package/cli/artifact-verify.mjs +90 -24
- package/cli/assurance-command-runner.mjs +39 -4
- package/cli/bagit-folder.mjs +57 -0
- package/cli/bagit-review.mjs +23 -0
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +34 -3
- package/cli/bulk-command-runner.mjs +2 -0
- package/cli/bulk-output.mjs +29 -3
- package/cli/case-command.mjs +195 -0
- package/cli/case-pack.mjs +66 -111
- package/cli/command-argument-grammar.mjs +215 -0
- package/cli/command-reference.mjs +342 -285
- package/cli/completion.mjs +303 -80
- package/cli/config-profile.mjs +7 -5
- package/cli/ct-event-intake.mjs +12 -12
- package/cli/discover.mjs +4 -3
- package/cli/discovery-observation-snapshot.mjs +59 -18
- package/cli/discovery-scan-command-runner.mjs +21 -0
- package/cli/discovery-scan.mjs +13 -4
- package/cli/discovery-workflow.mjs +1 -1
- package/cli/domain-control-monitor.mjs +50 -20
- package/cli/domain-control-observations.mjs +149 -102
- package/cli/errors.mjs +33 -1
- package/cli/evidence-command-runner.mjs +16 -5
- package/cli/evidence-signing.mjs +2 -2
- package/cli/external-observation-mapping.mjs +7 -8
- package/cli/formatters/terminal-lookup.mjs +18 -46
- package/cli/formatters/terminal-shared.mjs +5 -3
- package/cli/formatters/terminal-technology.mjs +114 -0
- package/cli/inline-command-families.mjs +2 -0
- package/cli/input.mjs +116 -0
- package/cli/interactive-launcher.mjs +2 -160
- package/cli/interchange-report.mjs +18 -8
- package/cli/investigation-artifacts.mjs +176 -0
- package/cli/investigation-checkpoint.mjs +10 -0
- package/cli/investigation-folder.mjs +114 -0
- package/cli/investigation-manifest.mjs +1 -112
- package/cli/investigation-package-review.mjs +103 -0
- package/cli/investigation-plan.mjs +32 -19
- package/cli/investigation-run.mjs +188 -33
- package/cli/local-document-checkpoint.mjs +170 -0
- package/cli/lookalike-calibration.mjs +7 -2
- package/cli/lookup-brief.mjs +4 -2
- package/cli/lookup-browser-view.mjs +1 -1
- package/cli/lookup-browser.mjs +5 -12
- package/cli/lookup-command-runner.mjs +17 -6
- package/cli/lookup-diff.mjs +37 -3
- package/cli/lookup-plan.mjs +11 -5
- package/cli/lookup-reconcile.mjs +10 -8
- package/cli/mail-header-review.mjs +467 -0
- package/cli/mail-review.mjs +66 -14
- package/cli/manual.mjs +1 -0
- package/cli/network-command-runner.mjs +2 -0
- package/cli/output-file.mjs +31 -5
- package/cli/registry-doctor.mjs +3 -1
- package/cli/registry-support.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +2 -2
- package/cli/review-command-runner.mjs +70 -0
- package/cli/risk-calibration.mjs +19 -17
- package/cli/runner.mjs +62 -7
- package/cli/sharing-review.mjs +21 -34
- package/cli/signer-trust.mjs +104 -0
- package/cli/source-reliability.mjs +74 -83
- package/cli/terminal-input.mjs +168 -0
- package/cli/workflow-command-runner.mjs +68 -13
- package/docs/cli-reference.md +77 -24
- package/docs/cli.md +300 -25
- package/lib/abort.mjs +4 -1
- package/lib/availability.mjs +161 -112
- package/lib/bounded-file.mjs +3 -1
- package/lib/bounded-html-document.mjs +125 -0
- package/lib/bounded-json.mjs +51 -8
- package/lib/browser-library-profile.mjs +42 -51
- package/lib/browser-library-worker.mjs +112 -0
- package/lib/client-behavior-profile.mjs +3 -3
- package/lib/ct-search.mjs +11 -4
- package/lib/dns-convergence-review.mjs +1 -0
- package/lib/dns-delegation-health.mjs +24 -14
- package/lib/dns-inheritance-review.mjs +217 -0
- package/lib/dns-intelligence.mjs +73 -26
- package/lib/dns-mx.mjs +1 -1
- package/lib/dnssec-chain-validation.mjs +6 -0
- package/lib/dnssec-evidence-validation.mjs +11 -3
- package/lib/domain-change-packet.mjs +49 -16
- package/lib/domain-change-review.mjs +14 -8
- package/lib/domain-control-flight-recorder.mjs +221 -117
- package/lib/domain-control-manifest.mjs +129 -69
- package/lib/domain-posture-analysis.mjs +20 -7
- package/lib/domain-posture-parsers.mjs +201 -8
- package/lib/domain-posture.mjs +147 -29
- package/lib/evidence-export-privacy.mjs +561 -0
- package/lib/evidence-export.mjs +11 -543
- package/lib/evidence-report.mjs +5 -3
- package/lib/favicon.mjs +117 -41
- package/lib/generated/cisa-kev-catalog.mjs +12 -4
- package/lib/generated/registrar-standing-catalogue.mjs +7 -7
- package/lib/generated/retire-browser-catalog.mjs +2335 -940
- package/lib/hostname.mjs +2 -38
- package/lib/html-signals.mjs +111 -108
- package/lib/http-evidence-bounds.mjs +2 -1
- package/lib/http-intelligence.mjs +39 -26
- package/lib/local-geoip-evidence.mjs +19 -3
- package/lib/lookup-cache.mjs +34 -11
- package/lib/lookup-child-profile-contract.mjs +24 -15
- package/lib/lookup-diagnostics.mjs +24 -1
- package/lib/lookup-progress.mjs +30 -13
- package/lib/lookup-response-contract.mjs +1048 -0
- package/lib/lookup-response.mjs +8 -10
- package/lib/lookup-source-progress.mjs +40 -14
- package/lib/lookup.mjs +48 -27
- package/lib/nameserver-preflight-review.mjs +1 -0
- package/lib/observed-network-context.mjs +43 -27
- package/lib/outbound-request-bounds.mjs +4 -1
- package/lib/page-fingerprints.mjs +69 -133
- package/lib/page-role-profile.mjs +12 -8
- package/lib/perceptual-hash.mjs +43 -14
- package/lib/rdap-bootstrap.mjs +72 -22
- package/lib/rdap-capabilities.mjs +1 -1
- package/lib/rdap-client.mjs +15 -7
- package/lib/rdap-normalization.mjs +33 -18
- package/lib/rdap-search-workbench.mjs +1 -1
- package/lib/rdap-transport.mjs +47 -20
- package/lib/rdap-types.mjs +2 -1
- package/lib/rdap.mjs +18 -3
- package/lib/registrable-domain.mjs +21 -1
- package/lib/registrar-standing-contract.mjs +1 -0
- package/lib/registry-capabilities.mjs +5 -1
- package/lib/registry-capability-catalogue.mjs +2 -1
- package/lib/registry-dates.mjs +2 -190
- package/lib/registry-insights.mjs +85 -34
- package/lib/response-policy.mjs +86 -48
- package/lib/risk-calibration-summary.mjs +18 -6
- package/lib/rpki-evidence.mjs +8 -5
- package/lib/safe-fetch.mjs +54 -47
- package/lib/security-txt.mjs +42 -7
- package/lib/service-binding-dns.mjs +8 -0
- package/lib/smtp-transport-review.mjs +27 -15
- package/lib/sslbl-certificates.generated.mjs +136 -135
- package/lib/static-html-analysis.mjs +178 -151
- package/lib/structured-data-identity.mjs +1 -0
- package/lib/technology-markup.mjs +100 -0
- package/lib/threat-intelligence-manifests.mjs +11 -139
- package/lib/threat-intelligence-runtime.mjs +24 -52
- package/lib/threat-intelligence-targets.mjs +6 -34
- package/lib/threat-intelligence-types.mjs +39 -0
- package/lib/typosquat-generator.mjs +19 -15
- package/lib/urlhaus-intelligence.mjs +8 -3
- package/lib/urlscan-intelligence.mjs +1 -1
- package/lib/website-security-posture.mjs +30 -6
- package/lib/website-technology.mjs +153 -45
- package/lib/whois-authority.mjs +14 -2
- package/lib/whois-chain.mjs +9 -2
- package/lib/whois-common-formats.mjs +5 -5
- package/lib/whois-contracts.mjs +2 -1
- package/lib/whois-dialects.mjs +2 -2
- package/lib/whois-parser-support.mjs +8 -1
- package/lib/whois-transport.mjs +23 -7
- package/lib/zone-intent-review.mjs +16 -6
- package/package.json +2 -1
- package/packages/cases/case-attachment-model.mjs +83 -0
- package/packages/cases/case-evidence-model.mjs +156 -62
- package/packages/cases/case-export-input.mjs +53 -0
- package/packages/cases/case-migration-model.mjs +127 -44
- package/packages/cases/case-model.mjs +1 -0
- package/packages/cases/case-recheck-model.mjs +91 -0
- package/packages/cases/case-record-contracts.mjs +5 -20
- package/packages/cases/case-record-core.mjs +12 -22
- package/packages/cases/case-record-decisions.mjs +72 -0
- package/packages/cases/case-record-model.mjs +1 -0
- package/packages/cases/case-record-operations.mjs +95 -31
- package/packages/cases/case-record-projection.mjs +221 -0
- package/packages/cases/case-report.mjs +70 -54
- package/packages/cases/case-response-actions.mjs +654 -0
- package/packages/cases/case-response-model.mjs +64 -1115
- package/packages/cases/case-response-outcomes.mjs +317 -0
- package/packages/cases/case-response-packet-render.mjs +153 -0
- package/packages/cases/case-response-packet-types.mjs +1 -0
- package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
- package/packages/cases/case-response-packet.mjs +74 -572
- package/packages/cases/case-response-records.mjs +94 -0
- package/packages/cases/case-response-review-inputs.mjs +371 -0
- package/packages/cases/case-response-values.mjs +85 -0
- package/packages/cases/case-review-package.mjs +45 -0
- package/packages/cases/case-selection.mjs +46 -0
- package/packages/cases/case-storage-model.mjs +31 -2
- package/packages/cases/response-route-freshness.mjs +16 -0
- package/packages/comparison/capture-context.mjs +61 -0
- package/packages/comparison/comparison-ledger-bulk.mjs +13 -11
- package/packages/comparison/page-similarity.mjs +5 -2
- package/packages/comparison/relationship-evidence.mjs +85 -8
- package/packages/comparison/relationship-provenance.mjs +93 -0
- package/packages/contracts/analyst-interchange.mjs +216 -0
- package/packages/contracts/analyst-review-state-contract.mjs +11 -0
- package/packages/contracts/analyst-review-state.mjs +5 -4
- package/packages/contracts/case-portability.mjs +366 -83
- package/packages/contracts/case-views-contract.mjs +12 -0
- package/packages/contracts/case-views.mjs +26 -0
- package/packages/contracts/cli-command-semantics.mjs +94 -0
- package/packages/contracts/cli-lookup.mjs +0 -1
- package/packages/contracts/domain-control-flight-recorder.mjs +80 -42
- package/packages/contracts/domain-control-manifest.mjs +127 -73
- package/packages/contracts/domain-control-monitor.mjs +48 -45
- package/packages/contracts/domain-control-review.mjs +179 -121
- package/packages/contracts/domain-name.mjs +21 -0
- package/packages/contracts/external-observation-interchange.mjs +2 -1
- package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
- package/packages/contracts/http-url.mjs +2 -0
- package/packages/contracts/investigation-package-limits.mjs +24 -0
- package/packages/contracts/investigation-portability.mjs +56 -27
- package/packages/contracts/investigation-run.mjs +16 -0
- package/packages/contracts/ip-address.mjs +34 -0
- package/packages/contracts/lookup-evidence.mjs +35 -0
- package/packages/contracts/offline-comparison.mjs +1 -2
- package/packages/contracts/page-fingerprints.mjs +15 -0
- package/packages/contracts/risk-calibration.mjs +15 -11
- package/packages/contracts/schema-compatibility.mjs +1 -1
- package/packages/contracts/schema-lifecycle.mjs +83 -153
- package/packages/contracts/selected-file-limits.mjs +6 -0
- package/packages/contracts/vulnerability-identifiers.mjs +5 -0
- package/packages/contracts/web-capture.mjs +10 -0
- package/packages/contracts/workspace-portability.mjs +183 -211
- package/packages/evidence/artifact-integrity.mjs +8 -2
- package/packages/evidence/artifact-structure.mjs +105 -0
- package/packages/evidence/decision-fact.mjs +4 -1
- package/packages/evidence/domain-control-runtime.mjs +201 -33
- package/packages/evidence/domain-posture-context.mjs +79 -0
- package/packages/evidence/image-regions.mjs +56 -0
- package/packages/evidence/latest-observations.mjs +28 -0
- package/packages/evidence/lookup-target.mjs +104 -0
- package/packages/evidence/observation.mjs +16 -1
- package/packages/evidence/passphrase-encryption.mjs +44 -0
- package/packages/evidence/record-identity.mjs +72 -0
- package/packages/evidence/registry-dates.mjs +190 -0
- package/packages/evidence/retained-file.mjs +69 -0
- package/packages/evidence/review-age.mjs +14 -0
- package/packages/interchange/bagit.mjs +342 -0
- package/packages/interchange/bounded-zip-extraction.mjs +278 -0
- package/packages/interchange/crc32.mjs +17 -0
- package/packages/interchange/external-findings-import.mjs +92 -51
- package/packages/interchange/web-capture-import.mjs +382 -0
- package/packages/investigation/bulk-mail-exposure.mjs +2 -1
- package/packages/investigation/investigation-bagit.mjs +14 -0
- package/packages/investigation/investigation-capsule.mjs +33 -5
- package/packages/investigation/investigation-manifest.mjs +200 -0
- package/packages/investigation/investigation-package-crypto.mjs +98 -0
- package/packages/investigation/investigation-package.mjs +200 -0
- package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
- package/packages/investigation/lookup-asset-graph.mjs +193 -165
- package/packages/investigation/lookup-claim-readiness.mjs +18 -0
- package/packages/investigation/lookup-investigation-brief.mjs +3 -3
- package/packages/investigation/lookup-task-guidance.mjs +58 -0
- package/packages/monitoring/analyst-review-state.mjs +38 -90
- package/packages/workspace/brand-profile-model.mjs +276 -22
- package/packages/workspace/bulk-columns.mjs +17 -0
- package/packages/workspace/bulk-review-model.mjs +23 -11
- package/packages/workspace/bulk-session-model.mjs +100 -24
- package/packages/workspace/bulk-sort.mjs +1 -0
- package/packages/workspace/case-views.mjs +102 -0
- package/packages/workspace/detection-rule-model.mjs +70 -22
- package/packages/workspace/hostile-input.mjs +29 -8
- package/packages/workspace/investigation-template-model.mjs +19 -4
- package/packages/workspace/page-baseline.mjs +18 -15
- package/packages/workspace/relationship-observation-model.mjs +41 -23
- package/packages/workspace/watchlist-history.mjs +2 -2
- package/packages/workspace/watchlist-store.mjs +8 -2
- package/packages/workspace/website-snapshot-model.mjs +164 -26
- package/packages/workspace/workspace-archive-crypto.mjs +41 -61
- package/packages/workspace/workspace-archive.mjs +87 -21
- package/third-party-notices.txt +29 -2
- package/cli/evidence-command-arguments.mjs +0 -156
- package/packages/investigation/bulk-review-export.mjs +0 -63
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineSchemaCompatibility, isCanonicalLocalSchemaIdentifier, } from "./schema-compatibility.mjs";
|
|
2
|
+
export const SCHEMA_LIFECYCLE_METADATA_VERSION = 4;
|
|
2
3
|
const FAMILY_KEYS = new Set(['id', 'owner', 'privacy', 'compatibility', 'contracts', 'fixtures']);
|
|
3
4
|
const EXTENDED_FAMILY_KEYS = new Set([...FAMILY_KEYS, 'metadata']);
|
|
4
|
-
const
|
|
5
|
-
'metadataVersion',
|
|
5
|
+
const METADATA_KEYS = new Set([
|
|
6
6
|
'enforcement',
|
|
7
7
|
'shapes',
|
|
8
8
|
'boundProfiles',
|
|
@@ -11,15 +11,8 @@ const METADATA_V1_KEYS = new Set([
|
|
|
11
11
|
'privacyProfiles',
|
|
12
12
|
'expiryProfiles',
|
|
13
13
|
'consumerEdges',
|
|
14
|
-
]);
|
|
15
|
-
const METADATA_V2_KEYS = new Set([
|
|
16
|
-
...METADATA_V1_KEYS,
|
|
17
14
|
'consumerRelationships',
|
|
18
15
|
]);
|
|
19
|
-
const METADATA_V3_KEYS = METADATA_V2_KEYS;
|
|
20
|
-
const METADATA_V4_KEYS = new Set([
|
|
21
|
-
...METADATA_V2_KEYS,
|
|
22
|
-
]);
|
|
23
16
|
const COMPATIBILITY_KEYS = new Set([
|
|
24
17
|
'id',
|
|
25
18
|
'tier',
|
|
@@ -52,7 +45,6 @@ const CONTRACT_KEYS = new Set([
|
|
|
52
45
|
'fixtureIds',
|
|
53
46
|
]);
|
|
54
47
|
const TARGET_KEYS = new Set(['schema', 'version']);
|
|
55
|
-
const EMITTED_TARGET_V4_KEYS = new Set([...TARGET_KEYS, 'discriminator']);
|
|
56
48
|
const FIXTURE_KEYS = new Set([
|
|
57
49
|
'id',
|
|
58
50
|
'path',
|
|
@@ -66,11 +58,8 @@ const FIXTURE_KEYS = new Set([
|
|
|
66
58
|
'expectedOutputFixtureId',
|
|
67
59
|
'scope',
|
|
68
60
|
]);
|
|
69
|
-
const FIXTURE_V4_KEYS = new Set([...FIXTURE_KEYS, 'shapeId']);
|
|
70
61
|
const SHAPE_KEYS = new Set(['id', 'schema', 'versions', 'objects', 'fixedArrays', 'normalisation', 'target']);
|
|
71
|
-
const SHAPE_V4_KEYS = new Set([...SHAPE_KEYS, 'discriminator']);
|
|
72
62
|
const OBJECT_SHAPE_KEYS = new Set(['path', 'requiredKeys', 'optionalKeys', 'unknownKeys']);
|
|
73
|
-
const OBJECT_SHAPE_V4_KEYS = new Set([...OBJECT_SHAPE_KEYS, 'alternativeRequiredKeys']);
|
|
74
63
|
const ALTERNATIVE_REQUIRED_KEYS = new Set(['keys', 'resolution']);
|
|
75
64
|
const FIXED_ARRAY_SHAPE_KEYS = new Set(['path', 'values']);
|
|
76
65
|
const BOUND_PROFILE_KEYS = new Set(['id', 'bounds']);
|
|
@@ -86,7 +75,6 @@ const PRIVACY_PROFILE_KEYS = new Set([
|
|
|
86
75
|
]);
|
|
87
76
|
const EXPIRY_POLICY_KEYS = new Set(['id', 'field', 'anchor', 'handling', 'phase', 'maximumLifetimeDays']);
|
|
88
77
|
const CONTRACT_REFERENCE_KEYS = new Set(['schema', 'versions', 'mode']);
|
|
89
|
-
const CONTRACT_REFERENCE_V3_KEYS = new Set([...CONTRACT_REFERENCE_KEYS, 'discriminator']);
|
|
90
78
|
const CONSUMER_DISCRIMINATOR_KEYS = new Set(['path', 'values']);
|
|
91
79
|
const CONTRACT_VARIANT_DISCRIMINATOR_KEYS = new Set(['path', 'value']);
|
|
92
80
|
const CONSUMER_KEYS = new Set([
|
|
@@ -150,7 +138,7 @@ function consumeMetadataValue(budget, value) {
|
|
|
150
138
|
}
|
|
151
139
|
}
|
|
152
140
|
}
|
|
153
|
-
function ordinaryRecord(value, keys, label, budget) {
|
|
141
|
+
function ordinaryRecord(value, keys, label, budget, optionalKeys = []) {
|
|
154
142
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
155
143
|
throw new TypeError(`${label} must be an ordinary object.`);
|
|
156
144
|
}
|
|
@@ -162,12 +150,14 @@ function ordinaryRecord(value, keys, label, budget) {
|
|
|
162
150
|
? keys
|
|
163
151
|
: [keys];
|
|
164
152
|
const ownKeys = Reflect.ownKeys(value);
|
|
165
|
-
const selectedKeys = keySets.find((candidate) => ownKeys.length
|
|
166
|
-
&& ownKeys.
|
|
153
|
+
const selectedKeys = keySets.find((candidate) => ownKeys.length >= candidate.size
|
|
154
|
+
&& ownKeys.length <= candidate.size + optionalKeys.length
|
|
155
|
+
&& [...candidate].every((key) => ownKeys.includes(key))
|
|
156
|
+
&& ownKeys.every((key) => typeof key === 'string' && (candidate.has(key) || optionalKeys.includes(key))));
|
|
167
157
|
if (!selectedKeys) {
|
|
168
158
|
throw new TypeError(`${label} must use its exact registered fields.`);
|
|
169
159
|
}
|
|
170
|
-
consumeMetadataSlots(budget,
|
|
160
|
+
consumeMetadataSlots(budget, ownKeys.length + 1);
|
|
171
161
|
consumeMetadataSerialisedBytes(budget, 2 + Math.max(0, ownKeys.length - 1) + ownKeys
|
|
172
162
|
.reduce((bytes, key) => bytes + utf8Length(JSON.stringify(key)) + 1, 0));
|
|
173
163
|
const result = Object.create(null);
|
|
@@ -324,18 +314,18 @@ function copyVariantDiscriminator(value, label, budget) {
|
|
|
324
314
|
value: boundedText(source.value, `${label} value`),
|
|
325
315
|
});
|
|
326
316
|
}
|
|
327
|
-
function copyEmittedTarget(value, label, budget
|
|
317
|
+
function copyEmittedTarget(value, label, budget) {
|
|
328
318
|
if (value === null)
|
|
329
319
|
return null;
|
|
330
|
-
const source = ordinaryRecord(value,
|
|
331
|
-
const discriminator =
|
|
320
|
+
const source = ordinaryRecord(value, TARGET_KEYS, label, budget, ['discriminator']);
|
|
321
|
+
const discriminator = Object.hasOwn(source, 'discriminator')
|
|
332
322
|
&& source.discriminator !== null
|
|
333
323
|
? copyVariantDiscriminator(source.discriminator, `${label} discriminator`, budget)
|
|
334
324
|
: null;
|
|
335
325
|
return Object.freeze({
|
|
336
326
|
schema: boundedSchema(source.schema, `${label} schema`),
|
|
337
327
|
version: positiveInteger(source.version, `${label} version`),
|
|
338
|
-
|
|
328
|
+
discriminator,
|
|
339
329
|
});
|
|
340
330
|
}
|
|
341
331
|
function copyStringArray(value, label) {
|
|
@@ -472,7 +462,7 @@ function copyContract(value, index) {
|
|
|
472
462
|
}
|
|
473
463
|
function copyFixture(value, index) {
|
|
474
464
|
const label = `Schema lifecycle fixture ${index + 1}`;
|
|
475
|
-
const source = ordinaryRecord(value,
|
|
465
|
+
const source = ordinaryRecord(value, FIXTURE_KEYS, label, undefined, ['shapeId']);
|
|
476
466
|
const role = source.role;
|
|
477
467
|
const expectation = source.expectation;
|
|
478
468
|
if (role !== 'input' && role !== 'historical' && role !== 'current') {
|
|
@@ -520,29 +510,27 @@ function copyFixture(value, index) {
|
|
|
520
510
|
: {}),
|
|
521
511
|
});
|
|
522
512
|
}
|
|
523
|
-
function copyObjectShape(value, index, shapeLabel, budget
|
|
513
|
+
function copyObjectShape(value, index, shapeLabel, budget) {
|
|
524
514
|
const label = `${shapeLabel} object ${index + 1}`;
|
|
525
|
-
const source = ordinaryRecord(value,
|
|
515
|
+
const source = ordinaryRecord(value, OBJECT_SHAPE_KEYS, label, budget, ['alternativeRequiredKeys']);
|
|
526
516
|
const requiredKeys = copyFieldArray(source.requiredKeys, `${label} required keys`, true, budget);
|
|
527
517
|
const optionalKeys = copyFieldArray(source.optionalKeys, `${label} optional keys`, true, budget);
|
|
528
518
|
if (requiredKeys.some((key) => optionalKeys.includes(key))) {
|
|
529
519
|
throw new TypeError(`${label} required and optional keys must not overlap.`);
|
|
530
520
|
}
|
|
531
|
-
const alternativeRequiredKeys =
|
|
521
|
+
const alternativeRequiredKeys = Object.hasOwn(source, 'alternativeRequiredKeys')
|
|
532
522
|
? copyAlternativeRequiredKeys(source.alternativeRequiredKeys, `${label} alternative required keys`, budget)
|
|
533
523
|
: Object.freeze([]);
|
|
534
524
|
if (alternativeRequiredKeys.flatMap((group) => group.keys)
|
|
535
525
|
.some((key) => requiredKeys.includes(key) || optionalKeys.includes(key))) {
|
|
536
526
|
throw new TypeError(`${label} alternative fields must not overlap required or optional keys.`);
|
|
537
527
|
}
|
|
538
|
-
const unknownKeys = oneOf(source.unknownKeys,
|
|
539
|
-
? ['discard_bounded', 'preserve_bounded', 'reject']
|
|
540
|
-
: ['preserve_bounded', 'reject'], `${label} unknown-key handling`);
|
|
528
|
+
const unknownKeys = oneOf(source.unknownKeys, ['discard_bounded', 'preserve_bounded', 'reject'], `${label} unknown-key handling`);
|
|
541
529
|
return Object.freeze({
|
|
542
530
|
path: boundedMetadataPath(source.path, `${label} path`),
|
|
543
531
|
requiredKeys,
|
|
544
532
|
optionalKeys,
|
|
545
|
-
|
|
533
|
+
alternativeRequiredKeys,
|
|
546
534
|
unknownKeys,
|
|
547
535
|
});
|
|
548
536
|
}
|
|
@@ -554,18 +542,16 @@ function copyFixedArrayShape(value, index, shapeLabel, budget) {
|
|
|
554
542
|
values: copyTextArray(source.values, `${label} values`, false, budget),
|
|
555
543
|
});
|
|
556
544
|
}
|
|
557
|
-
function copyShape(value, index, budget
|
|
545
|
+
function copyShape(value, index, budget) {
|
|
558
546
|
const label = `Schema lifecycle shape ${index + 1}`;
|
|
559
|
-
const source = ordinaryRecord(value,
|
|
560
|
-
const objects = Object.freeze(denseArray(source.objects, `${label} objects`, MAX_NESTED_METADATA_COLLECTION, 1, budget).map((item, objectIndex) => copyObjectShape(item, objectIndex, label, budget
|
|
547
|
+
const source = ordinaryRecord(value, SHAPE_KEYS, label, budget, ['discriminator']);
|
|
548
|
+
const objects = Object.freeze(denseArray(source.objects, `${label} objects`, MAX_NESTED_METADATA_COLLECTION, 1, budget).map((item, objectIndex) => copyObjectShape(item, objectIndex, label, budget)));
|
|
561
549
|
const fixedArrays = Object.freeze(denseArray(source.fixedArrays, `${label} fixed arrays`, MAX_NESTED_METADATA_COLLECTION, 0, budget).map((item, arrayIndex) => copyFixedArrayShape(item, arrayIndex, label, budget)));
|
|
562
550
|
if (new Set(objects.map((item) => item.path)).size !== objects.length
|
|
563
551
|
|| new Set(fixedArrays.map((item) => item.path)).size !== fixedArrays.length) {
|
|
564
552
|
throw new TypeError(`${label} paths must be unique within their shape kind.`);
|
|
565
553
|
}
|
|
566
|
-
const normalisation = oneOf(source.normalisation,
|
|
567
|
-
? ['input_to_current', 'preserve_bounded_document', 'preserve_document', 'preserve_signed_document', 'project_known_fields']
|
|
568
|
-
: ['input_to_current', 'preserve_bounded_document', 'preserve_document', 'preserve_signed_document'], `${label} normalisation`);
|
|
554
|
+
const normalisation = oneOf(source.normalisation, ['input_to_current', 'preserve_bounded_document', 'preserve_document', 'preserve_signed_document', 'project_known_fields'], `${label} normalisation`);
|
|
569
555
|
const target = copyTarget(source.target, `${label} target`, budget);
|
|
570
556
|
if ((normalisation === 'input_to_current') !== (target !== null)) {
|
|
571
557
|
throw new TypeError(`${label} input normalisation must name one target and document preservation must not.`);
|
|
@@ -578,13 +564,9 @@ function copyShape(value, index, budget, metadataVersion) {
|
|
|
578
564
|
fixedArrays,
|
|
579
565
|
normalisation,
|
|
580
566
|
target,
|
|
581
|
-
|
|
582
|
-
?
|
|
583
|
-
|
|
584
|
-
? null
|
|
585
|
-
: copyVariantDiscriminator(source.discriminator, `${label} discriminator`, budget),
|
|
586
|
-
}
|
|
587
|
-
: {}),
|
|
567
|
+
discriminator: !Object.hasOwn(source, 'discriminator') || source.discriminator === null
|
|
568
|
+
? null
|
|
569
|
+
: copyVariantDiscriminator(source.discriminator, `${label} discriminator`, budget),
|
|
588
570
|
});
|
|
589
571
|
}
|
|
590
572
|
function copyBound(value, index, profileLabel, budget) {
|
|
@@ -760,30 +742,20 @@ function copyExpiryPolicy(value, index, budget) {
|
|
|
760
742
|
maximumLifetimeDays,
|
|
761
743
|
});
|
|
762
744
|
}
|
|
763
|
-
function copyContractReference(value, index, consumerLabel, budget
|
|
745
|
+
function copyContractReference(value, index, consumerLabel, budget) {
|
|
764
746
|
const label = `${consumerLabel} accepted contract ${index + 1}`;
|
|
765
|
-
const source = ordinaryRecord(value,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
: CONTRACT_REFERENCE_KEYS, label, budget);
|
|
770
|
-
let discriminator;
|
|
771
|
-
if (metadataVersion >= 3) {
|
|
772
|
-
if (!Object.hasOwn(source, 'discriminator') || source.discriminator === null) {
|
|
773
|
-
discriminator = null;
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
discriminator = copyConsumerDiscriminator(source.discriminator, `${label} discriminator`, budget);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
747
|
+
const source = ordinaryRecord(value, CONTRACT_REFERENCE_KEYS, label, budget, ['discriminator']);
|
|
748
|
+
const discriminator = !Object.hasOwn(source, 'discriminator') || source.discriminator === null
|
|
749
|
+
? null
|
|
750
|
+
: copyConsumerDiscriminator(source.discriminator, `${label} discriminator`, budget);
|
|
779
751
|
return Object.freeze({
|
|
780
752
|
schema: boundedSchema(source.schema, `${label} schema`),
|
|
781
753
|
versions: copyVersionArray(source.versions, `${label} versions`, budget),
|
|
782
754
|
mode: oneOf(source.mode, ['direct', 'embedded'], `${label} mode`),
|
|
783
|
-
|
|
755
|
+
discriminator,
|
|
784
756
|
});
|
|
785
757
|
}
|
|
786
|
-
function copyConsumerEdge(value, index, budget
|
|
758
|
+
function copyConsumerEdge(value, index, budget) {
|
|
787
759
|
const label = `Schema lifecycle consumer ${index + 1}`;
|
|
788
760
|
const source = ordinaryRecord(value, CONSUMER_KEYS, label, budget);
|
|
789
761
|
const serialisationProfileId = source.serialisationProfileId;
|
|
@@ -794,8 +766,8 @@ function copyConsumerEdge(value, index, budget, metadataVersion) {
|
|
|
794
766
|
id: boundedId(source.id, `${label} id`),
|
|
795
767
|
plane: oneOf(source.plane, ['browser', 'cli', 'node', 'shared'], `${label} plane`),
|
|
796
768
|
operation: boundedId(source.operation, `${label} operation`),
|
|
797
|
-
acceptedContracts: Object.freeze(denseArray(source.acceptedContracts, `${label} accepted contracts`, MAX_NESTED_METADATA_COLLECTION, 0, budget).map((item, referenceIndex) => copyContractReference(item, referenceIndex, label, budget
|
|
798
|
-
emittedContract: copyEmittedTarget(source.emittedContract, `${label} emitted contract`, budget
|
|
769
|
+
acceptedContracts: Object.freeze(denseArray(source.acceptedContracts, `${label} accepted contracts`, MAX_NESTED_METADATA_COLLECTION, 0, budget).map((item, referenceIndex) => copyContractReference(item, referenceIndex, label, budget))),
|
|
770
|
+
emittedContract: copyEmittedTarget(source.emittedContract, `${label} emitted contract`, budget),
|
|
799
771
|
shapeIds: copyIdArray(source.shapeIds, `${label} shapes`, false, budget),
|
|
800
772
|
boundProfileIds: copyIdArray(source.boundProfileIds, `${label} bound profiles`, false, budget),
|
|
801
773
|
hookIds: copyIdArray(source.hookIds, `${label} hooks`, false, budget),
|
|
@@ -1036,9 +1008,6 @@ function utf8Length(value) {
|
|
|
1036
1008
|
}
|
|
1037
1009
|
return bytes;
|
|
1038
1010
|
}
|
|
1039
|
-
function metadataConsumerRelationships(metadata) {
|
|
1040
|
-
return metadata.metadataVersion === 1 ? [] : metadata.consumerRelationships;
|
|
1041
|
-
}
|
|
1042
1011
|
function validateMetadataSerialisedBudget(metadata) {
|
|
1043
1012
|
const serialised = JSON.stringify(metadata);
|
|
1044
1013
|
if (utf8Length(serialised) > MAX_METADATA_SERIALISED_BYTES) {
|
|
@@ -1046,11 +1015,6 @@ function validateMetadataSerialisedBudget(metadata) {
|
|
|
1046
1015
|
}
|
|
1047
1016
|
}
|
|
1048
1017
|
function validateMetadataRelations(family, metadata) {
|
|
1049
|
-
if (metadata.metadataVersion < 3 && (metadata.serialisationProfiles.some((profile) => profile.propertyOrder === 'source_insertion')
|
|
1050
|
-
|| metadata.privacyProfiles.some((profile) => profile.network === 'explicit_bounded_passive_fast_or_deep')
|
|
1051
|
-
|| metadata.consumerEdges.some((edge) => edge.requestMode === 'explicit_bounded_passive_fast_or_deep'))) {
|
|
1052
|
-
throw new TypeError('Schema lifecycle metadata version 3 is required for extensible serialisation and Fast-or-Deep request policy.');
|
|
1053
|
-
}
|
|
1054
1018
|
const contractPairs = new Map(family.contracts.map((contract) => [`${contract.schema}\u0000${contract.version}`, contract]));
|
|
1055
1019
|
const shapesById = new Map(metadata.shapes.map((shape) => [shape.id, shape]));
|
|
1056
1020
|
const boundProfilesById = new Map(metadata.boundProfiles.map((profile) => [profile.id, profile]));
|
|
@@ -1059,7 +1023,7 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1059
1023
|
const privacyProfilesById = new Map(metadata.privacyProfiles.map((profile) => [profile.id, profile]));
|
|
1060
1024
|
const expiryProfilesById = new Map(metadata.expiryProfiles.map((profile) => [profile.id, profile]));
|
|
1061
1025
|
const consumerEdgesById = new Map(metadata.consumerEdges.map((edge) => [edge.id, edge]));
|
|
1062
|
-
const consumerRelationships =
|
|
1026
|
+
const consumerRelationships = metadata.consumerRelationships;
|
|
1063
1027
|
const consumerRelationshipsById = new Map(consumerRelationships.map((relationship) => [relationship.id, relationship]));
|
|
1064
1028
|
const registries = [
|
|
1065
1029
|
[shapesById, metadata.shapes.length, 'shape'],
|
|
@@ -1109,12 +1073,6 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1109
1073
|
}
|
|
1110
1074
|
const preservesExtensions = shape.objects.some((object) => object.unknownKeys === 'preserve_bounded');
|
|
1111
1075
|
const discardsExtensions = shape.objects.some((object) => object.unknownKeys === 'discard_bounded');
|
|
1112
|
-
if (metadata.metadataVersion < 3 && preservesExtensions) {
|
|
1113
|
-
throw new TypeError(`Schema lifecycle shape ${shape.id} requires metadata version 3 for bounded extensions.`);
|
|
1114
|
-
}
|
|
1115
|
-
if (metadata.metadataVersion !== 4 && discardsExtensions) {
|
|
1116
|
-
throw new TypeError(`Schema lifecycle shape ${shape.id} requires metadata version 4 for bounded projection.`);
|
|
1117
|
-
}
|
|
1118
1076
|
if (preservesExtensions && discardsExtensions) {
|
|
1119
1077
|
throw new TypeError(`Schema lifecycle shape ${shape.id} must use one bounded extension policy.`);
|
|
1120
1078
|
}
|
|
@@ -1137,7 +1095,7 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1137
1095
|
&& contracts.some((contract) => contract?.canonicalisation !== null))) {
|
|
1138
1096
|
throw new TypeError(`Schema lifecycle shape ${shape.id} has inconsistent document integrity metadata.`);
|
|
1139
1097
|
}
|
|
1140
|
-
const discriminator =
|
|
1098
|
+
const discriminator = shape.discriminator ?? null;
|
|
1141
1099
|
if (discriminator) {
|
|
1142
1100
|
if (shape.versions.length !== 1) {
|
|
1143
1101
|
throw new TypeError(`Schema lifecycle shape ${shape.id} must qualify exactly one contract version.`);
|
|
@@ -1176,22 +1134,27 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1176
1134
|
throw new TypeError('Every schema lifecycle contract must have at least one shape.');
|
|
1177
1135
|
}
|
|
1178
1136
|
const fixtureShapeIds = new Set();
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1137
|
+
for (const fixture of family.fixtures) {
|
|
1138
|
+
const candidates = metadata.shapes.filter((shape) => shape.schema === fixture.schema && shape.versions.includes(fixture.version));
|
|
1139
|
+
// A routine fixture has one possible shape. Variants still require an
|
|
1140
|
+
// explicit fixture binding; never choose arbitrarily between them.
|
|
1141
|
+
const shape = fixture.shapeId
|
|
1142
|
+
? shapesById.get(fixture.shapeId)
|
|
1143
|
+
: fixture.shapeId === undefined && candidates.length === 1 && !candidates[0]?.discriminator
|
|
1144
|
+
? candidates[0]
|
|
1145
|
+
: null;
|
|
1146
|
+
if (!shape
|
|
1147
|
+
|| shape.schema !== fixture.schema
|
|
1148
|
+
|| !shape.versions.includes(fixture.version)) {
|
|
1149
|
+
throw new TypeError(`Schema lifecycle fixture ${fixture.id} must name a matching contract shape.`);
|
|
1188
1150
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1151
|
+
fixtureShapeIds.add(shape.id);
|
|
1152
|
+
}
|
|
1153
|
+
for (const shape of metadata.shapes) {
|
|
1154
|
+
const contracts = shape.versions.map((version) => contractPairs.get(`${shape.schema}\u0000${version}`));
|
|
1155
|
+
if ((shape.discriminator !== null || contracts.some((contract) => contract?.lifecycle === 'retired'))
|
|
1156
|
+
&& !fixtureShapeIds.has(shape.id)) {
|
|
1157
|
+
throw new TypeError(`Schema lifecycle shape ${shape.id} must have an exact repository fixture.`);
|
|
1195
1158
|
}
|
|
1196
1159
|
}
|
|
1197
1160
|
for (const profile of metadata.boundProfiles) {
|
|
@@ -1270,7 +1233,6 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1270
1233
|
}
|
|
1271
1234
|
const acceptedShapeIds = new Set();
|
|
1272
1235
|
const emittedShapeIds = new Set();
|
|
1273
|
-
const coveredContractPairs = new Set();
|
|
1274
1236
|
for (const edge of metadata.consumerEdges) {
|
|
1275
1237
|
if (edge.acceptedContracts.length === 0 && edge.emittedContract === null) {
|
|
1276
1238
|
throw new TypeError(`Schema lifecycle consumer ${edge.id} must accept or emit a contract.`);
|
|
@@ -1298,13 +1260,11 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1298
1260
|
|| !contract.readable
|
|
1299
1261
|
|| acceptedSelections.has(selectionKey)
|
|
1300
1262
|
|| selectedShapes.some((shape) => acceptedContractShapes.has(`${key}\u0000${shape.id}`))
|
|
1301
|
-
|| (
|
|
1302
|
-
|
|
1303
|
-
|| selectedShapes.length !== reference.discriminator.values.length))
|
|
1304
|
-
|| (!qualified && reference.discriminator !== null && reference.discriminator !== undefined)))) {
|
|
1263
|
+
|| (qualified && (!reference.discriminator
|
|
1264
|
+
|| selectedShapes.length !== reference.discriminator.values.length))) {
|
|
1305
1265
|
throw new TypeError(`Schema lifecycle consumer ${edge.id} references an unreadable or duplicate contract.`);
|
|
1306
1266
|
}
|
|
1307
|
-
if (
|
|
1267
|
+
if (!qualified && reference.discriminator) {
|
|
1308
1268
|
const parentPath = parentMetadataPath(reference.discriminator.path);
|
|
1309
1269
|
const field = metadataPathField(reference.discriminator.path);
|
|
1310
1270
|
if (contractShapes.some((shape) => {
|
|
@@ -1319,7 +1279,6 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1319
1279
|
}
|
|
1320
1280
|
acceptedSelections.add(selectionKey);
|
|
1321
1281
|
relevantContractPairs.add(key);
|
|
1322
|
-
coveredContractPairs.add(key);
|
|
1323
1282
|
if (selectedShapes.some((shape) => shape.normalisation === 'project_known_fields')) {
|
|
1324
1283
|
if (contract.byteBudget === null)
|
|
1325
1284
|
projectedContractWithoutByteBudget = true;
|
|
@@ -1329,7 +1288,7 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1329
1288
|
for (const shape of selectedShapes) {
|
|
1330
1289
|
shapeIds.add(shape.id);
|
|
1331
1290
|
acceptedContractShapes.add(`${key}\u0000${shape.id}`);
|
|
1332
|
-
acceptedShapeIds.add(shape.id);
|
|
1291
|
+
acceptedShapeIds.add(`${key}\u0000${shape.id}`);
|
|
1333
1292
|
}
|
|
1334
1293
|
}
|
|
1335
1294
|
}
|
|
@@ -1346,17 +1305,15 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1346
1305
|
: contractShapes;
|
|
1347
1306
|
if (!emitted
|
|
1348
1307
|
|| !emitted.emitted
|
|
1349
|
-
|| (
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
&& edge.emittedContract.discriminator !== undefined)))) {
|
|
1308
|
+
|| (qualified && selectedShapes.length !== 1)
|
|
1309
|
+
|| (!qualified && edge.emittedContract.discriminator !== null
|
|
1310
|
+
&& edge.emittedContract.discriminator !== undefined)) {
|
|
1353
1311
|
throw new TypeError(`Schema lifecycle consumer ${edge.id} must emit a registered writable contract.`);
|
|
1354
1312
|
}
|
|
1355
1313
|
relevantContractPairs.add(key);
|
|
1356
|
-
coveredContractPairs.add(key);
|
|
1357
1314
|
for (const shape of selectedShapes) {
|
|
1358
1315
|
shapeIds.add(shape.id);
|
|
1359
|
-
emittedShapeIds.add(shape.id);
|
|
1316
|
+
emittedShapeIds.add(`${key}\u0000${shape.id}`);
|
|
1360
1317
|
}
|
|
1361
1318
|
}
|
|
1362
1319
|
if (shapeIds.size !== edge.shapeIds.length || edge.shapeIds.some((id) => !shapeIds.has(id))) {
|
|
@@ -1446,25 +1403,19 @@ function validateMetadataRelations(family, metadata) {
|
|
|
1446
1403
|
}
|
|
1447
1404
|
for (const contract of family.contracts) {
|
|
1448
1405
|
const key = `${contract.schema}\u0000${contract.version}`;
|
|
1449
|
-
if (metadata.metadataVersion < 4) {
|
|
1450
|
-
if ((contract.readable || contract.emitted) && !coveredContractPairs.has(key)) {
|
|
1451
|
-
throw new TypeError('Every readable or emitted schema lifecycle contract must have a consumer edge.');
|
|
1452
|
-
}
|
|
1453
|
-
continue;
|
|
1454
|
-
}
|
|
1455
1406
|
const contractShapeIds = shapeIdsByContract.get(key) ?? new Set();
|
|
1456
1407
|
if ([...contractShapeIds].some((id) => {
|
|
1457
1408
|
const shape = shapesById.get(id);
|
|
1458
1409
|
return shape?.discriminator !== null
|
|
1459
1410
|
&& shape?.discriminator !== undefined
|
|
1460
1411
|
&& !(contractPairs.get(key)?.lifecycle === 'retired' && fixtureShapeIds.has(id))
|
|
1461
|
-
&& !acceptedShapeIds.has(id)
|
|
1462
|
-
&& !emittedShapeIds.has(id);
|
|
1412
|
+
&& !acceptedShapeIds.has(`${key}\u0000${id}`)
|
|
1413
|
+
&& !emittedShapeIds.has(`${key}\u0000${id}`);
|
|
1463
1414
|
})) {
|
|
1464
1415
|
throw new TypeError(`Schema lifecycle contract ${contract.schema} v${contract.version} has an uncovered shape variant.`);
|
|
1465
1416
|
}
|
|
1466
|
-
const hasReader = [...contractShapeIds].some((id) => acceptedShapeIds.has(id));
|
|
1467
|
-
const hasWriter = [...contractShapeIds].some((id) => emittedShapeIds.has(id));
|
|
1417
|
+
const hasReader = [...contractShapeIds].some((id) => acceptedShapeIds.has(`${key}\u0000${id}`));
|
|
1418
|
+
const hasWriter = [...contractShapeIds].some((id) => emittedShapeIds.has(`${key}\u0000${id}`));
|
|
1468
1419
|
if (contract.readable !== hasReader || contract.emitted !== hasWriter) {
|
|
1469
1420
|
throw new TypeError(`Schema lifecycle contract ${contract.schema} v${contract.version} must exactly aggregate its readable and emitted shape variants.`);
|
|
1470
1421
|
}
|
|
@@ -1500,13 +1451,13 @@ export function defineSchemaLifecycleFamily(value) {
|
|
|
1500
1451
|
validateFamilyRelations(family);
|
|
1501
1452
|
if (!metadataDescriptor) {
|
|
1502
1453
|
if (contracts.some((contract) => contract.extensionPolicy === 'preserve_bounded')) {
|
|
1503
|
-
throw new TypeError('Schema lifecycle metadata
|
|
1454
|
+
throw new TypeError('Schema lifecycle metadata is required for bounded extensions.');
|
|
1504
1455
|
}
|
|
1505
1456
|
if (contracts.some((contract) => contract.extensionPolicy === 'discard_bounded'
|
|
1506
1457
|
|| contract.lifecycle === 'retired')
|
|
1507
1458
|
|| fixtures.some((fixture) => Object.hasOwn(fixture, 'shapeId')
|
|
1508
1459
|
|| fixture.expectation === 'historical_output_exact')) {
|
|
1509
|
-
throw new TypeError('Schema lifecycle metadata
|
|
1460
|
+
throw new TypeError('Schema lifecycle metadata is required for retired or projected contracts.');
|
|
1510
1461
|
}
|
|
1511
1462
|
return family;
|
|
1512
1463
|
}
|
|
@@ -1515,49 +1466,28 @@ export function defineSchemaLifecycleFamily(value) {
|
|
|
1515
1466
|
textCodeUnits: 0,
|
|
1516
1467
|
serialisedBytes: 0,
|
|
1517
1468
|
};
|
|
1518
|
-
const metadataSource = ordinaryRecord(source.metadata,
|
|
1469
|
+
const metadataSource = ordinaryRecord(source.metadata, METADATA_KEYS, 'Schema lifecycle metadata', metadataBudget, ['metadataVersion']);
|
|
1519
1470
|
const metadataVersion = metadataSource.metadataVersion;
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|| metadataSource.enforcement !== 'declarative_only'
|
|
1523
|
-
|| (metadataVersion === 1 && hasConsumerRelationships)
|
|
1524
|
-
|| ((metadataVersion === 2 || metadataVersion === 3 || metadataVersion === 4) && !hasConsumerRelationships)) {
|
|
1471
|
+
if ((Object.hasOwn(metadataSource, 'metadataVersion') && metadataVersion !== SCHEMA_LIFECYCLE_METADATA_VERSION)
|
|
1472
|
+
|| metadataSource.enforcement !== 'declarative_only') {
|
|
1525
1473
|
throw new TypeError('Schema lifecycle metadata must use an exact registered declarative-only version.');
|
|
1526
1474
|
}
|
|
1527
|
-
const usesVersion4Vocabulary = contracts.some((contract) => contract.extensionPolicy === 'discard_bounded'
|
|
1528
|
-
|| contract.lifecycle === 'retired')
|
|
1529
|
-
|| fixtures.some((fixture) => Object.hasOwn(fixture, 'shapeId')
|
|
1530
|
-
|| fixture.expectation === 'historical_output_exact');
|
|
1531
|
-
if ((metadataVersion === 4) !== usesVersion4Vocabulary
|
|
1532
|
-
|| (metadataVersion === 4 && fixtures.some((fixture) => !Object.hasOwn(fixture, 'shapeId')))) {
|
|
1533
|
-
throw new TypeError('Schema lifecycle metadata version 4 must exclusively and completely own its retired or projected vocabulary.');
|
|
1534
|
-
}
|
|
1535
1475
|
const metadataCommon = {
|
|
1536
1476
|
enforcement: 'declarative_only',
|
|
1537
|
-
shapes: Object.freeze(denseArray(metadataSource.shapes, 'Schema lifecycle shapes', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyShape(item, index, metadataBudget
|
|
1477
|
+
shapes: Object.freeze(denseArray(metadataSource.shapes, 'Schema lifecycle shapes', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyShape(item, index, metadataBudget))),
|
|
1538
1478
|
boundProfiles: Object.freeze(denseArray(metadataSource.boundProfiles, 'Schema lifecycle bound profiles', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyBoundProfile(item, index, metadataBudget))),
|
|
1539
1479
|
hooks: Object.freeze(denseArray(metadataSource.hooks, 'Schema lifecycle hooks', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyHook(item, index, metadataBudget))),
|
|
1540
1480
|
serialisationProfiles: Object.freeze(denseArray(metadataSource.serialisationProfiles, 'Schema lifecycle serialisation profiles', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copySerialisationProfile(item, index, metadataBudget))),
|
|
1541
1481
|
privacyProfiles: Object.freeze(denseArray(metadataSource.privacyProfiles, 'Schema lifecycle privacy profiles', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyPrivacyProfile(item, index, metadataBudget))),
|
|
1542
1482
|
expiryProfiles: Object.freeze(denseArray(metadataSource.expiryProfiles, 'Schema lifecycle expiry profiles', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyExpiryPolicy(item, index, metadataBudget))),
|
|
1543
|
-
consumerEdges: Object.freeze(denseArray(metadataSource.consumerEdges, 'Schema lifecycle consumer edges', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyConsumerEdge(item, index, metadataBudget
|
|
1483
|
+
consumerEdges: Object.freeze(denseArray(metadataSource.consumerEdges, 'Schema lifecycle consumer edges', MAX_METADATA_COLLECTION, 1, metadataBudget).map((item, index) => copyConsumerEdge(item, index, metadataBudget))),
|
|
1544
1484
|
};
|
|
1545
|
-
const consumerRelationships =
|
|
1546
|
-
|
|
1547
|
-
:
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
? Object.freeze({ metadataVersion: 2, ...metadataCommon, consumerRelationships })
|
|
1552
|
-
: metadataVersion === 3
|
|
1553
|
-
? Object.freeze({ metadataVersion: 3, ...metadataCommon, consumerRelationships })
|
|
1554
|
-
: Object.freeze({
|
|
1555
|
-
metadataVersion: 4,
|
|
1556
|
-
...metadataCommon,
|
|
1557
|
-
shapes: metadataCommon.shapes,
|
|
1558
|
-
consumerEdges: metadataCommon.consumerEdges,
|
|
1559
|
-
consumerRelationships,
|
|
1560
|
-
});
|
|
1485
|
+
const consumerRelationships = Object.freeze(denseArray(metadataSource.consumerRelationships, 'Schema lifecycle consumer relationships', MAX_METADATA_COLLECTION, 0, metadataBudget).map((item, index) => copyConsumerRelationship(item, index, metadataBudget)));
|
|
1486
|
+
const metadata = Object.freeze({
|
|
1487
|
+
metadataVersion: SCHEMA_LIFECYCLE_METADATA_VERSION,
|
|
1488
|
+
...metadataCommon,
|
|
1489
|
+
consumerRelationships,
|
|
1490
|
+
});
|
|
1561
1491
|
validateMetadataRelations(family, metadata);
|
|
1562
1492
|
return Object.freeze({ ...family, metadata });
|
|
1563
1493
|
}
|
|
@@ -1586,7 +1516,7 @@ function validateRegistryConsumerRelationships(families) {
|
|
|
1586
1516
|
if (!Object.hasOwn(family, 'metadata'))
|
|
1587
1517
|
continue;
|
|
1588
1518
|
const metadata = family.metadata;
|
|
1589
|
-
for (const relationship of
|
|
1519
|
+
for (const relationship of metadata.consumerRelationships) {
|
|
1590
1520
|
const source = consumers.get(relationship.sourceConsumerId);
|
|
1591
1521
|
const target = consumers.get(relationship.targetConsumerId);
|
|
1592
1522
|
if (!source || source.familyId !== family.id) {
|
|
@@ -1686,7 +1616,7 @@ export function defineSchemaLifecycleRegistry(value) {
|
|
|
1686
1616
|
...metadata.privacyProfiles.map((item) => item.id),
|
|
1687
1617
|
...metadata.expiryProfiles.map((item) => item.id),
|
|
1688
1618
|
...metadata.consumerEdges.map((item) => item.id),
|
|
1689
|
-
...
|
|
1619
|
+
...metadata.consumerRelationships.map((item) => item.id),
|
|
1690
1620
|
];
|
|
1691
1621
|
for (const id of familyMetadataIds) {
|
|
1692
1622
|
if (metadataIds.has(id)) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// A complete 32-MiB workspace's encrypted JSON exceeds 42 MiB. This allowance
|
|
2
|
+
// admits that file and supporting evidence without splitting a selected file.
|
|
3
|
+
export const MAX_SELECTED_FILES = 128;
|
|
4
|
+
export const MAX_SELECTED_FILE_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
5
|
+
export const MAX_SELECTED_FILE_BYTES = MAX_SELECTED_FILE_TOTAL_BYTES;
|
|
6
|
+
export const SELECTED_FILE_MEDIA_TYPES = ['application/json', 'application/octet-stream', 'image/png', 'image/jpeg', 'image/webp', 'image/gif', 'application/pdf'];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { MAX_WEB_CAPTURE_MANIFEST_BYTES, WEB_CAPTURE_MANIFEST_SCHEMA, WEB_CAPTURE_MANIFEST_VERSION, WEB_CAPTURE_SUMMARY_SCHEMA, WEB_CAPTURE_SUMMARY_VERSION, } from "./analyst-interchange.mjs";
|
|
2
|
+
export const WEB_CAPTURE_DOM_DIGEST_SCHEMA = 'whoisleuth.dom-digest';
|
|
3
|
+
export const WEB_CAPTURE_DOM_DIGEST_VERSION = 1;
|
|
4
|
+
export const WEB_CAPTURE_COMPARISON_SCHEMA = 'whoisleuth.web-capture-comparison';
|
|
5
|
+
export const WEB_CAPTURE_COMPARISON_VERSION = 3;
|
|
6
|
+
export const MAX_WEB_CAPTURE_DOM_DIGEST_BYTES = 1024 * 1024;
|
|
7
|
+
export const MAX_WEB_CAPTURE_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
export const MAX_WEB_CAPTURE_DOM_ELEMENTS = 20_000;
|
|
9
|
+
export const MAX_WEB_CAPTURE_DOM_PROJECTION_CHARACTERS = 256 * 1024;
|
|
10
|
+
export const MAX_WEB_CAPTURE_VISIBLE_TEXT_BYTES = 256 * 1024;
|