project-tiny-context-harness 0.8.4 → 0.8.6
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/LICENSE +21 -21
- package/README.md +394 -363
- package/assets/README.md +570 -514
- package/assets/README.zh-CN.md +331 -300
- package/assets/agents/.gitkeep +1 -1
- package/assets/agents/AGENTS_CORE.md +45 -25
- package/assets/context_templates/architecture.md +26 -25
- package/assets/context_templates/area.md +32 -31
- package/assets/context_templates/context.toml +30 -24
- package/assets/context_templates/deployment.md +35 -35
- package/assets/context_templates/global.md +57 -57
- package/assets/context_templates/product-surface-contract.md +70 -70
- package/assets/context_templates/screen-contract.md +189 -189
- package/assets/context_templates/verification.md +31 -30
- package/assets/github/.gitkeep +1 -1
- package/assets/github/harness.yml +39 -39
- package/assets/make/.gitkeep +1 -1
- package/assets/make/ty-context.mk +48 -48
- package/assets/skills/context_development_engineer/SKILL.md +155 -122
- package/assets/skills/context_full_project_export/SKILL.md +28 -28
- package/assets/skills/context_harness_upgrade/SKILL.md +60 -60
- package/assets/skills/context_product_plan/SKILL.md +99 -79
- package/assets/skills/context_surface_contract/SKILL.md +191 -191
- package/assets/skills/context_uiux_design/SKILL.md +171 -171
- package/assets/skills/design-resource-authoring/SKILL.md +86 -84
- package/assets/skills/design-resource-authoring/references/downstream-handoff.md +138 -123
- package/assets/skills/design-resource-authoring/references/open-design-provider.md +132 -127
- package/assets/skills/design-resource-authoring/references/resource-selection.md +181 -154
- package/assets/skills/design-system-authoring/SKILL.md +57 -57
- package/assets/skills/design-system-authoring/agents/openai.yaml +6 -6
- package/assets/skills/design-system-authoring/references/authority-adoption.md +48 -48
- package/assets/skills/design-system-authoring/references/open-design-design-system-provider.md +110 -110
- package/assets/skills/long-task-workflow/SKILL.md +98 -92
- package/assets/skills/long-task-workflow/agents/openai.yaml +3 -1
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +72 -72
- package/assets/skills/long-task-workflow/references/contract-authoring.md +122 -101
- package/assets/skills/long-task-workflow/references/evidence-design.md +82 -61
- package/assets/skills/long-task-workflow/references/source-authoring.md +109 -90
- package/assets/skills/source-plan-authoring/SKILL.md +14 -14
- package/assets/tools/validate_context.py +442 -442
- package/dist/commands/long-task-authoring.js +27 -2
- package/dist/lib/context-default-footprint.js +2 -0
- package/dist/lib/context-manifest.js +6 -0
- package/dist/lib/context-templates.js +1 -1
- package/dist/lib/design-resource-fact-enums.d.ts +20 -0
- package/dist/lib/design-resource-fact-enums.js +170 -0
- package/dist/lib/design-resource-fact-locator-extractors.d.ts +8 -0
- package/dist/lib/design-resource-fact-locator-extractors.js +108 -0
- package/dist/lib/design-resource-fact-locator-resolver.d.ts +3 -0
- package/dist/lib/design-resource-fact-locator-resolver.js +112 -0
- package/dist/lib/design-resource-fact-locator-validation.d.ts +5 -0
- package/dist/lib/design-resource-fact-locator-validation.js +16 -0
- package/dist/lib/design-resource-fact-manifest-catalog.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-catalog.js +249 -0
- package/dist/lib/design-resource-fact-manifest-model.d.ts +91 -0
- package/dist/lib/design-resource-fact-manifest-model.js +21 -0
- package/dist/lib/design-resource-fact-manifest-shape-axes.d.ts +6 -0
- package/dist/lib/design-resource-fact-manifest-shape-axes.js +159 -0
- package/dist/lib/design-resource-fact-manifest-shape-evidence.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-shape-evidence.js +59 -0
- package/dist/lib/design-resource-fact-manifest-shape-facts.d.ts +4 -0
- package/dist/lib/design-resource-fact-manifest-shape-facts.js +109 -0
- package/dist/lib/design-resource-fact-manifest-shape-inspector.d.ts +5 -0
- package/dist/lib/design-resource-fact-manifest-shape-inspector.js +144 -0
- package/dist/lib/design-resource-fact-manifest-shape.d.ts +7 -0
- package/dist/lib/design-resource-fact-manifest-shape.js +75 -0
- package/dist/lib/design-resource-fact-manifest-types.d.ts +3 -0
- package/dist/lib/design-resource-fact-manifest-types.js +3 -0
- package/dist/lib/design-resource-fact-manifest-universe.d.ts +5 -0
- package/dist/lib/design-resource-fact-manifest-universe.js +50 -0
- package/dist/lib/design-resource-fact-manifest-validation.d.ts +3 -0
- package/dist/lib/design-resource-fact-manifest-validation.js +106 -0
- package/dist/lib/design-resource-fact-policy.d.ts +18 -0
- package/dist/lib/design-resource-fact-policy.js +72 -1
- package/dist/lib/design-resource-fact-property-methods.d.ts +3 -0
- package/dist/lib/design-resource-fact-property-methods.js +158 -0
- package/dist/lib/design-resource-fact-shape-primitives.d.ts +3 -0
- package/dist/lib/design-resource-fact-shape-primitives.js +18 -0
- package/dist/lib/design-resource-fact-types.d.ts +164 -0
- package/dist/lib/design-resource-fact-types.js +1 -0
- package/dist/lib/design-resource-fact-universe-assets.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-assets.js +71 -0
- package/dist/lib/design-resource-fact-universe-catalog.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-catalog.js +151 -0
- package/dist/lib/design-resource-fact-universe-conditions.d.ts +2 -0
- package/dist/lib/design-resource-fact-universe-conditions.js +92 -0
- package/dist/lib/design-resource-fact-universe-facts.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-facts.js +108 -0
- package/dist/lib/design-resource-fact-universe-helpers.d.ts +30 -0
- package/dist/lib/design-resource-fact-universe-helpers.js +167 -0
- package/dist/lib/design-resource-fact-universe-inspector.d.ts +7 -0
- package/dist/lib/design-resource-fact-universe-inspector.js +119 -0
- package/dist/lib/design-resource-fact-universe-proof.d.ts +4 -0
- package/dist/lib/design-resource-fact-universe-proof.js +113 -0
- package/dist/lib/design-resource-fact-universe-subjects.d.ts +5 -0
- package/dist/lib/design-resource-fact-universe-subjects.js +97 -0
- package/dist/lib/design-resource-fact-universe-variations.d.ts +2 -0
- package/dist/lib/design-resource-fact-universe-variations.js +75 -0
- package/dist/lib/design-resource-fact-value-validation.d.ts +2 -0
- package/dist/lib/design-resource-fact-value-validation.js +80 -0
- package/dist/lib/design-resource-handoff-file-primitives.js +2 -0
- package/dist/lib/design-resource-handoff-file-validation.js +13 -100
- package/dist/lib/design-resource-handoff-policy.d.ts +1 -0
- package/dist/lib/design-resource-handoff-policy.js +118 -11
- package/dist/lib/design-resource-handoff-shape-evidence.js +46 -3
- package/dist/lib/design-resource-handoff-shape-primitives.d.ts +5 -0
- package/dist/lib/design-resource-handoff-shape-primitives.js +28 -0
- package/dist/lib/design-resource-handoff-shape-structure.js +143 -22
- package/dist/lib/design-resource-handoff-shape.js +25 -0
- package/dist/lib/design-resource-handoff-types.d.ts +97 -21
- package/dist/lib/design-resource-handoff-types.js +22 -0
- package/dist/lib/design-resource-handoff-validation-coverage.d.ts +1 -1
- package/dist/lib/design-resource-handoff-validation-coverage.js +145 -118
- package/dist/lib/design-resource-handoff-validation-fact-cells.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-fact-cells.js +60 -0
- package/dist/lib/design-resource-handoff-validation-fact-records.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-fact-records.js +90 -0
- package/dist/lib/design-resource-handoff-validation-facts.js +10 -94
- package/dist/lib/design-resource-handoff-validation-proofs.d.ts +2 -0
- package/dist/lib/design-resource-handoff-validation-proofs.js +67 -0
- package/dist/lib/design-resource-handoff-validation-resource-closure.d.ts +3 -0
- package/dist/lib/design-resource-handoff-validation-resource-closure.js +88 -0
- package/dist/lib/design-resource-handoff-validation-structure.js +43 -14
- package/dist/lib/design-resource-handoff-validation.js +32 -0
- package/dist/lib/design-resource-handoff-web-dependency-validation.d.ts +2 -1
- package/dist/lib/design-resource-handoff-web-dependency-validation.js +67 -7
- package/dist/lib/execution-target-capabilities.d.ts +1 -1
- package/dist/lib/execution-target-capabilities.js +14 -0
- package/dist/lib/long-task-activation-validation.js +6 -2
- package/dist/lib/long-task-applicability-shape.js +3 -3
- package/dist/lib/long-task-authoring-authority-preview.js +1 -0
- package/dist/lib/long-task-authority-policy.d.ts +1 -0
- package/dist/lib/long-task-authority-policy.js +1 -0
- package/dist/lib/long-task-authority.js +13 -0
- package/dist/lib/long-task-claim-definitions.js +13 -1
- package/dist/lib/long-task-claims.js +23 -1
- package/dist/lib/long-task-contract-types.d.ts +3 -0
- package/dist/lib/long-task-delivery-compiler.js +1 -0
- package/dist/lib/long-task-delivery-parser.js +11 -1
- package/dist/lib/long-task-delivery-types.d.ts +1 -0
- package/dist/lib/long-task-delivery-types.js +1 -0
- package/dist/lib/long-task-delivery-validation.js +17 -0
- package/dist/lib/long-task-design-resource-handoff.js +62 -38
- package/dist/lib/long-task-design-target-capabilities.d.ts +14 -0
- package/dist/lib/long-task-design-target-capabilities.js +126 -0
- package/dist/lib/long-task-evidence-capability-codec.d.ts +94 -0
- package/dist/lib/long-task-evidence-capability-codec.js +395 -22
- package/dist/lib/long-task-evidence-capability-policy.js +18 -0
- package/dist/lib/long-task-evidence-capability-runtime.js +78 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +114 -1
- package/dist/lib/long-task-outcome-parser.js +4 -0
- package/dist/lib/long-task-playwright-capability-records.d.ts +5 -0
- package/dist/lib/long-task-playwright-capability-records.js +146 -0
- package/dist/lib/long-task-playwright-case-evidence.d.ts +3 -2
- package/dist/lib/long-task-playwright-case-evidence.js +10 -79
- package/dist/lib/long-task-playwright-case-primitives.d.ts +28 -0
- package/dist/lib/long-task-playwright-case-primitives.js +143 -0
- package/dist/lib/long-task-playwright-evidence.d.ts +1 -1
- package/dist/lib/long-task-playwright-evidence.js +10 -75
- package/dist/lib/long-task-runner-freeze.d.ts +2 -2
- package/dist/lib/long-task-runner-freeze.js +15 -1
- package/dist/lib/long-task-runtime-types.d.ts +4 -1
- package/dist/lib/long-task-semantic-contract-types.d.ts +1 -1
- package/dist/lib/long-task-semantic-drift-migration.js +2 -2
- package/dist/lib/long-task-semantic-fact-binding-types.d.ts +57 -0
- package/dist/lib/long-task-semantic-fact-binding-types.js +1 -0
- package/dist/lib/long-task-semantic-fact-closure-primitives.d.ts +5 -0
- package/dist/lib/long-task-semantic-fact-closure-primitives.js +43 -0
- package/dist/lib/long-task-semantic-fact-closure.d.ts +13 -0
- package/dist/lib/long-task-semantic-fact-closure.js +48 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-closure.js +24 -0
- package/dist/lib/long-task-semantic-fact-contract-facts.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-facts.js +30 -0
- package/dist/lib/long-task-semantic-fact-contract-proofs.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-contract-proofs.js +97 -0
- package/dist/lib/long-task-semantic-fact-evidence.d.ts +21 -0
- package/dist/lib/long-task-semantic-fact-evidence.js +165 -0
- package/dist/lib/long-task-semantic-fact-input-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-input-closure.js +101 -0
- package/dist/lib/long-task-semantic-fact-provenance-closure.d.ts +4 -0
- package/dist/lib/long-task-semantic-fact-provenance-closure.js +140 -0
- package/dist/lib/long-task-semantic-fact-shape.d.ts +3 -0
- package/dist/lib/long-task-semantic-fact-shape.js +89 -0
- package/dist/lib/long-task-semantic-fact-value-closure.d.ts +3 -0
- package/dist/lib/long-task-semantic-fact-value-closure.js +110 -0
- package/dist/lib/long-task-shape-primitives.d.ts +2 -1
- package/dist/lib/long-task-shape-primitives.js +7 -0
- package/dist/lib/long-task-source-item-parser.js +25 -6
- package/dist/lib/long-task-source-target-continuity.js +4 -1
- package/dist/lib/long-task-source-target-index.d.ts +1 -1
- package/dist/lib/long-task-source-target-index.js +5 -1
- package/dist/lib/long-task-ui-design-policy.js +8 -4
- package/dist/lib/long-task-ui-surface-shape.js +83 -1
- package/dist/lib/long-task-ui-surface-types.d.ts +29 -0
- package/dist/lib/modularity.js +7 -1
- package/dist/lib/semantic-fact-base-types.d.ts +14 -0
- package/dist/lib/semantic-fact-base-types.js +1 -0
- package/dist/lib/semantic-fact-catalog.d.ts +8 -0
- package/dist/lib/semantic-fact-catalog.js +191 -0
- package/dist/lib/semantic-fact-condition-shape.d.ts +48 -0
- package/dist/lib/semantic-fact-condition-shape.js +111 -0
- package/dist/lib/semantic-fact-input-shape.d.ts +34 -0
- package/dist/lib/semantic-fact-input-shape.js +111 -0
- package/dist/lib/semantic-fact-inventory-types.d.ts +147 -0
- package/dist/lib/semantic-fact-inventory-types.js +1 -0
- package/dist/lib/semantic-fact-manifest-shape.d.ts +4 -0
- package/dist/lib/semantic-fact-manifest-shape.js +135 -0
- package/dist/lib/semantic-fact-manifest-types.d.ts +58 -0
- package/dist/lib/semantic-fact-manifest-types.js +19 -0
- package/dist/lib/semantic-fact-policy-authority.d.ts +8 -0
- package/dist/lib/semantic-fact-policy-authority.js +79 -0
- package/dist/lib/semantic-fact-policy-census.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-census.js +80 -0
- package/dist/lib/semantic-fact-policy-condition-references.d.ts +7 -0
- package/dist/lib/semantic-fact-policy-condition-references.js +19 -0
- package/dist/lib/semantic-fact-policy-conditions.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-conditions.js +105 -0
- package/dist/lib/semantic-fact-policy-facts.d.ts +3 -0
- package/dist/lib/semantic-fact-policy-facts.js +73 -0
- package/dist/lib/semantic-fact-policy-primitives.d.ts +26 -0
- package/dist/lib/semantic-fact-policy-primitives.js +169 -0
- package/dist/lib/semantic-fact-policy-proofs.d.ts +2 -0
- package/dist/lib/semantic-fact-policy-proofs.js +75 -0
- package/dist/lib/semantic-fact-policy-properties.d.ts +3 -0
- package/dist/lib/semantic-fact-policy-properties.js +84 -0
- package/dist/lib/semantic-fact-policy-units.d.ts +4 -0
- package/dist/lib/semantic-fact-policy-units.js +103 -0
- package/dist/lib/semantic-fact-policy.d.ts +17 -0
- package/dist/lib/semantic-fact-policy.js +41 -0
- package/dist/lib/semantic-fact-proof-shape.d.ts +51 -0
- package/dist/lib/semantic-fact-proof-shape.js +121 -0
- package/dist/lib/semantic-fact-proof-types.d.ts +74 -0
- package/dist/lib/semantic-fact-proof-types.js +1 -0
- package/dist/lib/semantic-fact-property-shape.d.ts +29 -0
- package/dist/lib/semantic-fact-property-shape.js +77 -0
- package/dist/lib/semantic-fact-shape-constants.d.ts +3 -0
- package/dist/lib/semantic-fact-shape-constants.js +38 -0
- package/dist/lib/semantic-fact-shape-primitives.d.ts +15 -0
- package/dist/lib/semantic-fact-shape-primitives.js +68 -0
- package/dist/lib/semantic-fact-source-parser.d.ts +9 -0
- package/dist/lib/semantic-fact-source-parser.js +46 -0
- package/dist/lib/semantic-fact-support-shape.d.ts +21 -0
- package/dist/lib/semantic-fact-support-shape.js +59 -0
- package/dist/lib/semantic-fact-types.d.ts +5 -0
- package/dist/lib/semantic-fact-types.js +5 -0
- package/dist/lib/semantic-fact-unit-shape.d.ts +34 -0
- package/dist/lib/semantic-fact-unit-shape.js +89 -0
- package/dist/lib/semantic-fact-value-shape.d.ts +12 -0
- package/dist/lib/semantic-fact-value-shape.js +39 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +1339 -131
- package/migrations/README.md +15 -15
- package/package.json +84 -84
- package/source-mappings.yaml +25 -25
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { resolveDesignResourceLocatorValue, validateDesignResourceLocatedDigest, } from "./design-resource-fact-locator-validation.js";
|
|
2
|
+
import { invalid, nonempty, refsKnown, sameSet, unique, validateBasis, } from "./design-resource-fact-universe-helpers.js";
|
|
3
|
+
export function validateManifestLineageNodes(manifest, census, resources, contents) {
|
|
4
|
+
const nodes = new Map(manifest.lineage_nodes.map((node) => [node.key, node]));
|
|
5
|
+
for (const node of manifest.lineage_nodes) {
|
|
6
|
+
unique(node.predecessor_refs, `manifest_lineage_predecessor_duplicate:${node.key}`);
|
|
7
|
+
refsKnown(node.predecessor_refs, nodes, "manifest_lineage_predecessor_unknown", node.key);
|
|
8
|
+
if (node.predecessor_refs.includes(node.key))
|
|
9
|
+
invalid("manifest_lineage_self_reference", node.key);
|
|
10
|
+
if (["base_token", "direct_value"].includes(node.kind) &&
|
|
11
|
+
node.predecessor_refs.length)
|
|
12
|
+
invalid("manifest_lineage_root_predecessor_forbidden", node.key);
|
|
13
|
+
if (!["base_token", "direct_value"].includes(node.kind) &&
|
|
14
|
+
node.predecessor_refs.length === 0)
|
|
15
|
+
invalid("manifest_lineage_predecessor_required", node.key);
|
|
16
|
+
nonempty(node.census_refs, `manifest_lineage_census_required:${node.key}`);
|
|
17
|
+
refsKnown(node.census_refs, census, "manifest_lineage_census_unknown", node.key);
|
|
18
|
+
for (const ref of node.census_refs)
|
|
19
|
+
if (!["token", "declaration"].includes(census.get(ref).kind))
|
|
20
|
+
invalid("manifest_lineage_census_kind_invalid", `${node.key}:${ref}:${census.get(ref).kind}`);
|
|
21
|
+
validateDesignResourceLocatedDigest(node.value, resources, contents, `manifest.lineage_node.${node.key}.value`);
|
|
22
|
+
}
|
|
23
|
+
validateLineageAcyclic(manifest, nodes);
|
|
24
|
+
}
|
|
25
|
+
function validateLineageAcyclic(manifest, nodes) {
|
|
26
|
+
for (const node of manifest.lineage_nodes) {
|
|
27
|
+
const visiting = new Set();
|
|
28
|
+
const visited = new Set();
|
|
29
|
+
const walk = (ref) => {
|
|
30
|
+
if (visiting.has(ref))
|
|
31
|
+
invalid("manifest_lineage_cycle", node.key);
|
|
32
|
+
if (visited.has(ref))
|
|
33
|
+
return;
|
|
34
|
+
visiting.add(ref);
|
|
35
|
+
for (const predecessor of nodes.get(ref).predecessor_refs)
|
|
36
|
+
walk(predecessor);
|
|
37
|
+
visiting.delete(ref);
|
|
38
|
+
visited.add(ref);
|
|
39
|
+
};
|
|
40
|
+
walk(node.key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function validateManifestInspector(manifest, handoff, target, resources, contents, sourceItems) {
|
|
44
|
+
const inspector = manifest.inspector;
|
|
45
|
+
validateInspectorIdentity(inspector);
|
|
46
|
+
if (inspector.entry_resource_ref !== target.source_profile.entry_resource_ref)
|
|
47
|
+
invalid("manifest_inspector_entry_mismatch", `${manifest.target_key}:${inspector.entry_resource_ref}:${target.source_profile.entry_resource_ref}`);
|
|
48
|
+
validateInspectorInputs(manifest, target, resources);
|
|
49
|
+
validateInspectorCensus(manifest, resources, contents, sourceItems);
|
|
50
|
+
const expectedInspectorIdentity = `${inspector.identity}@${inspector.version}`;
|
|
51
|
+
for (const resourceRef of target.resource_refs) {
|
|
52
|
+
const closure = handoff.resource_fact_closure.find((item) => item.resource_ref === resourceRef);
|
|
53
|
+
if (!closure || closure.inspection.inspector !== expectedInspectorIdentity)
|
|
54
|
+
invalid("manifest_resource_closure_inspector_mismatch", `${resourceRef}:${closure?.inspection.inspector ?? "missing"}:${expectedInspectorIdentity}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function validateInspectorIdentity(inspector) {
|
|
58
|
+
if (inspector.trust === "frozen_executable" &&
|
|
59
|
+
inspector.implementation_sha256 === null)
|
|
60
|
+
invalid("manifest_inspector_digest_required", inspector.identity);
|
|
61
|
+
if (inspector.trust === "named_external_tcb" &&
|
|
62
|
+
inspector.implementation_sha256 !== null)
|
|
63
|
+
invalid("manifest_external_inspector_digest_forbidden", inspector.identity);
|
|
64
|
+
nonempty(inspector.capability_refs, "manifest_inspector_capabilities_required");
|
|
65
|
+
unique(inspector.capability_refs, "manifest_inspector_capability_duplicate");
|
|
66
|
+
}
|
|
67
|
+
function validateInspectorInputs(manifest, target, resources) {
|
|
68
|
+
const manifestResourceRef = target.source_profile.fact_manifest_resource_ref;
|
|
69
|
+
const expectedInputs = target.resource_refs
|
|
70
|
+
.filter((ref) => ref !== manifestResourceRef)
|
|
71
|
+
.map((ref) => {
|
|
72
|
+
const resource = resources.get(ref);
|
|
73
|
+
return `${ref}\0${resource.path}\0${resource.sha256}`;
|
|
74
|
+
});
|
|
75
|
+
const actualInputs = manifest.inspector.input_resources.map((item) => `${item.resource_ref}\0${item.path}\0${item.sha256}`);
|
|
76
|
+
sameSet(actualInputs, expectedInputs, "manifest_inspector_input_resource_mismatch", manifest.target_key);
|
|
77
|
+
unique(manifest.inspector.input_resources.map((item) => item.resource_ref), "manifest_inspector_input_resource_duplicate");
|
|
78
|
+
}
|
|
79
|
+
function validateInspectorCensus(manifest, resources, contents, sourceItems) {
|
|
80
|
+
const inputRefs = new Set(manifest.inspector.input_resources.map((item) => item.resource_ref));
|
|
81
|
+
const censusByResource = new Map();
|
|
82
|
+
const censusFactRefs = new Set();
|
|
83
|
+
const censusFactCellRefs = new Set();
|
|
84
|
+
const facts = new Map(manifest.facts.map((fact) => [fact.key, fact]));
|
|
85
|
+
const factCells = new Map(manifest.fact_cells.map((cell) => [cell.key, cell]));
|
|
86
|
+
const censusEntries = new Map(manifest.inspector.census.map((entry) => [entry.key, entry]));
|
|
87
|
+
for (const entry of manifest.inspector.census) {
|
|
88
|
+
validateCensusEntry(entry, inputRefs, censusByResource, facts, factCells, censusEntries, resources, contents, sourceItems);
|
|
89
|
+
for (const ref of entry.fact_refs)
|
|
90
|
+
censusFactRefs.add(ref);
|
|
91
|
+
for (const ref of entry.fact_cell_refs)
|
|
92
|
+
censusFactCellRefs.add(ref);
|
|
93
|
+
}
|
|
94
|
+
for (const ref of inputRefs)
|
|
95
|
+
if (!censusByResource.has(ref))
|
|
96
|
+
invalid("manifest_census_resource_missing", ref);
|
|
97
|
+
sameSet([...censusFactRefs], manifest.facts.map((fact) => fact.key), "manifest_census_fact_set_mismatch", manifest.target_key);
|
|
98
|
+
sameSet([...censusFactCellRefs], manifest.fact_cells.map((cell) => cell.key), "manifest_census_fact_cell_set_mismatch", manifest.target_key);
|
|
99
|
+
}
|
|
100
|
+
function validateCensusEntry(entry, inputRefs, censusByResource, facts, factCells, censusEntries, resources, contents, sourceItems) {
|
|
101
|
+
validateBasis(entry, sourceItems, censusEntries, `manifest_census:${entry.key}`);
|
|
102
|
+
if (entry.basis_refs.includes(entry.key))
|
|
103
|
+
invalid("manifest_census_self_basis_forbidden", entry.key);
|
|
104
|
+
if (!inputRefs.has(entry.resource_ref))
|
|
105
|
+
invalid("manifest_census_resource_outside_inputs", `${entry.key}:${entry.resource_ref}`);
|
|
106
|
+
censusByResource.set(entry.resource_ref, (censusByResource.get(entry.resource_ref) ?? 0) + 1);
|
|
107
|
+
resolveDesignResourceLocatorValue({ resource_ref: entry.resource_ref, ...entry.locator }, resources.get(entry.resource_ref), contents.get(entry.resource_ref), `manifest.census.${entry.key}`);
|
|
108
|
+
unique(entry.fact_refs, `manifest_census_fact_ref_duplicate:${entry.key}`);
|
|
109
|
+
unique(entry.fact_cell_refs, `manifest_census_fact_cell_ref_duplicate:${entry.key}`);
|
|
110
|
+
refsKnown(entry.fact_refs, facts, "manifest_census_fact_ref_unknown", entry.key);
|
|
111
|
+
refsKnown(entry.fact_cell_refs, factCells, "manifest_census_fact_cell_ref_unknown", entry.key);
|
|
112
|
+
if (entry.disposition === "covered" &&
|
|
113
|
+
entry.fact_refs.length === 0 &&
|
|
114
|
+
entry.fact_cell_refs.length === 0)
|
|
115
|
+
invalid("manifest_census_covered_binding_required", entry.key);
|
|
116
|
+
if (entry.disposition === "non_material" &&
|
|
117
|
+
(entry.fact_refs.length > 0 || entry.fact_cell_refs.length > 0))
|
|
118
|
+
invalid("manifest_census_non_material_binding_forbidden", entry.key);
|
|
119
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DesignResourceObservableFactManifestV1 } from "./design-resource-fact-manifest-types.js";
|
|
2
|
+
import type { DesignResource } from "./design-resource-handoff-file-primitives.js";
|
|
3
|
+
import type { DesignResourceHandoffTargetV1 } from "./design-resource-handoff-types.js";
|
|
4
|
+
export declare function validateManifestProofAndEvidence(manifest: DesignResourceObservableFactManifestV1, target: DesignResourceHandoffTargetV1, resources: Map<string, DesignResource>, contents: Map<string, Buffer>): void;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { designFactComparatorSupportsMethod, designFactMethodIsCompatible, designFactOracleSupportsMethod, EXACT_TARGET_FULL_TARGET_METHODS, } from "./design-resource-fact-policy.js";
|
|
2
|
+
import { resolveDesignResourceLocatorValue, validateDesignResourceLocatedDigest, } from "./design-resource-fact-locator-validation.js";
|
|
3
|
+
import { invalid, nonempty, unique, } from "./design-resource-fact-universe-helpers.js";
|
|
4
|
+
export function validateManifestProofAndEvidence(manifest, target, resources, contents) {
|
|
5
|
+
validateOraclesAndEnvironments(manifest, resources, contents);
|
|
6
|
+
validateProofObligations(manifest, resources, contents);
|
|
7
|
+
validateExactTargetFullTargetProof(manifest, target);
|
|
8
|
+
validateEvidence(manifest, resources, contents);
|
|
9
|
+
}
|
|
10
|
+
function validateOraclesAndEnvironments(manifest, resources, contents) {
|
|
11
|
+
for (const oracle of manifest.oracles) {
|
|
12
|
+
nonempty(oracle.capability_refs, `manifest_oracle_capabilities_required:${oracle.key}`);
|
|
13
|
+
unique(oracle.capability_refs, `manifest_oracle_capability_duplicate:${oracle.key}`);
|
|
14
|
+
if (oracle.trust === "frozen_executable" && oracle.sha256 === null)
|
|
15
|
+
invalid("manifest_oracle_digest_required", oracle.key);
|
|
16
|
+
if (oracle.trust === "named_external_tcb" && oracle.sha256 !== null)
|
|
17
|
+
invalid("manifest_external_oracle_digest_forbidden", oracle.key);
|
|
18
|
+
}
|
|
19
|
+
for (const environment of manifest.environments)
|
|
20
|
+
validateDesignResourceLocatedDigest(environment.definition, resources, contents, `manifest.environment.${environment.key}`);
|
|
21
|
+
}
|
|
22
|
+
function validateProofObligations(manifest, resources, contents) {
|
|
23
|
+
const properties = new Map(manifest.properties.map((property) => [property.key, property]));
|
|
24
|
+
const oracles = new Map(manifest.oracles.map((item) => [item.key, item]));
|
|
25
|
+
const environments = new Map(manifest.environments.map((item) => [item.key, item]));
|
|
26
|
+
const proofMethodsByFact = new Map();
|
|
27
|
+
const factMethodIdentities = [];
|
|
28
|
+
for (const proof of manifest.proof_obligations) {
|
|
29
|
+
validateProof(proof, manifest, oracles, environments, resources, contents);
|
|
30
|
+
const identity = `${proof.fact_ref}\0${proof.method}`;
|
|
31
|
+
factMethodIdentities.push(identity);
|
|
32
|
+
const methods = proofMethodsByFact.get(proof.fact_ref) ?? new Set();
|
|
33
|
+
methods.add(proof.method);
|
|
34
|
+
proofMethodsByFact.set(proof.fact_ref, methods);
|
|
35
|
+
}
|
|
36
|
+
unique(factMethodIdentities, "manifest_fact_method_obligation_duplicate");
|
|
37
|
+
for (const fact of manifest.facts) {
|
|
38
|
+
const actualMethods = proofMethodsByFact.get(fact.key);
|
|
39
|
+
if (!actualMethods)
|
|
40
|
+
invalid("manifest_fact_proof_obligation_required", fact.key);
|
|
41
|
+
const requiredMethods = new Set(properties.get(fact.property_ref).required_methods);
|
|
42
|
+
if (fact.lineage.design_system_ref !== null &&
|
|
43
|
+
(fact.lineage.token_chain_refs.length > 0 ||
|
|
44
|
+
fact.lineage.override_chain_refs.length > 0))
|
|
45
|
+
requiredMethods.add("design_token");
|
|
46
|
+
for (const method of requiredMethods)
|
|
47
|
+
if (!actualMethods.has(method))
|
|
48
|
+
invalid("manifest_fact_required_proof_method_missing", `${fact.key}:${method}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function validateProof(proof, manifest, oracles, environments, resources, contents) {
|
|
52
|
+
const fact = manifest.facts.find((item) => item.key === proof.fact_ref);
|
|
53
|
+
if (!fact)
|
|
54
|
+
invalid("manifest_proof_fact_unknown", proof.key);
|
|
55
|
+
if (!designFactMethodIsCompatible(fact.dimension, proof.method))
|
|
56
|
+
invalid("manifest_proof_method_incompatible", `${proof.key}:${fact.dimension}:${proof.method}`);
|
|
57
|
+
if (!designFactComparatorSupportsMethod(proof.method, proof.comparison.comparator))
|
|
58
|
+
invalid("manifest_proof_comparator_method_incompatible", `${proof.key}:${proof.method}:${proof.comparison.comparator}`);
|
|
59
|
+
const oracle = oracles.get(proof.oracle_ref);
|
|
60
|
+
if (!oracle)
|
|
61
|
+
invalid("manifest_proof_oracle_unknown", proof.key);
|
|
62
|
+
if (!designFactOracleSupportsMethod(proof.method, oracle.capability_refs))
|
|
63
|
+
invalid("manifest_proof_oracle_capability_missing", `${proof.key}:${proof.oracle_ref}:${proof.method}`);
|
|
64
|
+
if (!environments.has(proof.environment_ref))
|
|
65
|
+
invalid("manifest_proof_environment_unknown", proof.key);
|
|
66
|
+
validateComparison(proof, resources, contents);
|
|
67
|
+
}
|
|
68
|
+
function validateComparison(proof, resources, contents) {
|
|
69
|
+
validateDesignResourceLocatedDigest(proof.comparison.parameters, resources, contents, `manifest.proof.${proof.key}.parameters`);
|
|
70
|
+
if (proof.comparison.mode === "exact") {
|
|
71
|
+
if (proof.comparison.tolerance !== null || proof.comparison.mask !== null)
|
|
72
|
+
invalid("manifest_exact_proof_tolerance_forbidden", proof.key);
|
|
73
|
+
}
|
|
74
|
+
else if (proof.comparison.tolerance === null)
|
|
75
|
+
invalid("manifest_tolerance_proof_tolerance_required", proof.key);
|
|
76
|
+
if (proof.comparison.tolerance)
|
|
77
|
+
validateDesignResourceLocatedDigest(proof.comparison.tolerance, resources, contents, `manifest.proof.${proof.key}.tolerance`);
|
|
78
|
+
if (proof.comparison.mask)
|
|
79
|
+
validateDesignResourceLocatedDigest(proof.comparison.mask, resources, contents, `manifest.proof.${proof.key}.mask`);
|
|
80
|
+
if (proof.comparison.mask !== null &&
|
|
81
|
+
proof.comparison.comparator !== "pixel_diff" &&
|
|
82
|
+
!proof.comparison.comparator.startsWith("custom."))
|
|
83
|
+
invalid("manifest_proof_mask_comparator_incompatible", `${proof.key}:${proof.comparison.comparator}`);
|
|
84
|
+
}
|
|
85
|
+
function validateExactTargetFullTargetProof(manifest, target) {
|
|
86
|
+
if (target.interpretation !== "exact_target")
|
|
87
|
+
return;
|
|
88
|
+
for (const condition of manifest.conditions)
|
|
89
|
+
for (const method of EXACT_TARGET_FULL_TARGET_METHODS)
|
|
90
|
+
if (!hasFullTargetProof(manifest, condition.key, method))
|
|
91
|
+
invalid("manifest_exact_target_full_target_proof_missing", `${target.key}:${condition.key}:${method}`);
|
|
92
|
+
}
|
|
93
|
+
function hasFullTargetProof(manifest, conditionRef, method) {
|
|
94
|
+
return manifest.proof_obligations.some((proof) => {
|
|
95
|
+
const fact = manifest.facts.find((item) => item.key === proof.fact_ref);
|
|
96
|
+
return (fact?.condition_ref === conditionRef &&
|
|
97
|
+
fact.observation_scope === "full_target" &&
|
|
98
|
+
proof.method === method);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function validateEvidence(manifest, resources, contents) {
|
|
102
|
+
const conditions = new Set(manifest.conditions.map((item) => item.key));
|
|
103
|
+
for (const evidenceItem of manifest.evidence) {
|
|
104
|
+
const resource = resources.get(evidenceItem.resource_ref);
|
|
105
|
+
const bytes = contents.get(evidenceItem.resource_ref);
|
|
106
|
+
if (!resource || !bytes)
|
|
107
|
+
invalid("manifest_evidence_resource_unknown", evidenceItem.key);
|
|
108
|
+
for (const conditionRef of evidenceItem.condition_refs)
|
|
109
|
+
if (!conditions.has(conditionRef))
|
|
110
|
+
invalid("manifest_evidence_condition_unknown", `${evidenceItem.key}:${conditionRef}`);
|
|
111
|
+
resolveDesignResourceLocatorValue({ resource_ref: evidenceItem.resource_ref, ...evidenceItem.locator }, resource, bytes, `manifest.evidence.${evidenceItem.key}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DesignResourceObservableFactManifestV1 } from "./design-resource-fact-manifest-types.js";
|
|
2
|
+
import type { DesignResource } from "./design-resource-handoff-file-primitives.js";
|
|
3
|
+
import type { DesignResourceHandoffTargetV1, DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
|
|
4
|
+
export declare function validateManifestDesignSystem(manifest: DesignResourceObservableFactManifestV1, handoff: DesignResourceHandoffV1, target: DesignResourceHandoffTargetV1, resources: Map<string, DesignResource>): void;
|
|
5
|
+
export declare function validateManifestSubjects(manifest: DesignResourceObservableFactManifestV1, census: Map<string, DesignResourceObservableFactManifestV1["inspector"]["census"][number]>): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { invalid, nonempty, refsKnown, unique, } from "./design-resource-fact-universe-helpers.js";
|
|
2
|
+
export function validateManifestDesignSystem(manifest, handoff, target, resources) {
|
|
3
|
+
const snapshot = manifest.design_system;
|
|
4
|
+
const styleBearing = handoff.scope.style_dependency !== "non-fidelity";
|
|
5
|
+
if (styleBearing && snapshot.disposition !== "used")
|
|
6
|
+
invalid("manifest_design_system_required", manifest.target_key);
|
|
7
|
+
if (!styleBearing && snapshot.disposition !== "not_applicable")
|
|
8
|
+
invalid("manifest_design_system_not_applicable_required", manifest.target_key);
|
|
9
|
+
if (snapshot.id !== handoff.provenance.design_system_id)
|
|
10
|
+
invalid("manifest_design_system_id_mismatch", `${snapshot.id}:${handoff.provenance.design_system_id}`);
|
|
11
|
+
const resource = resources.get(snapshot.resource_ref);
|
|
12
|
+
if (!resource)
|
|
13
|
+
invalid("manifest_design_system_resource_unknown", snapshot.resource_ref);
|
|
14
|
+
if (!target.resource_refs.includes(snapshot.resource_ref))
|
|
15
|
+
invalid("manifest_design_system_resource_outside_target", `${target.key}:${snapshot.resource_ref}`);
|
|
16
|
+
if (resource.sha256 !== snapshot.sha256)
|
|
17
|
+
invalid("manifest_design_system_digest_mismatch", `${snapshot.resource_ref}:${snapshot.sha256}:${resource.sha256}`);
|
|
18
|
+
}
|
|
19
|
+
export function validateManifestSubjects(manifest, census) {
|
|
20
|
+
const subjects = new Map(manifest.subjects.map((subject) => [subject.key, subject]));
|
|
21
|
+
const roots = manifest.subjects.filter((subject) => subject.parent_ref === null &&
|
|
22
|
+
(subject.kind === "surface" || subject.kind === "flow"));
|
|
23
|
+
if (!roots.length)
|
|
24
|
+
invalid("manifest_subject_root_required", manifest.target_key);
|
|
25
|
+
for (const subject of manifest.subjects)
|
|
26
|
+
validateSubject(subject, subjects, census);
|
|
27
|
+
validateSubjectParentAcyclic(manifest, subjects);
|
|
28
|
+
}
|
|
29
|
+
function validateSubject(subject, subjects, census) {
|
|
30
|
+
nonempty(subject.census_refs, `manifest_subject_census_required:${subject.key}`);
|
|
31
|
+
refsKnown(subject.census_refs, census, "manifest_subject_census_unknown", subject.key);
|
|
32
|
+
if (subject.presence === "always" && subject.presence_rule_ref !== null)
|
|
33
|
+
invalid("manifest_always_presence_rule_forbidden", subject.key);
|
|
34
|
+
if (subject.presence !== "always" && subject.presence_rule_ref === null)
|
|
35
|
+
invalid("manifest_dynamic_presence_rule_required", subject.key);
|
|
36
|
+
validatePresenceAndPopulation(subject, census);
|
|
37
|
+
for (const ref of [
|
|
38
|
+
subject.parent_ref,
|
|
39
|
+
subject.instance_of_ref,
|
|
40
|
+
subject.override_of_ref,
|
|
41
|
+
subject.family_ref,
|
|
42
|
+
subject.portal_host_ref,
|
|
43
|
+
])
|
|
44
|
+
if (ref !== null && !subjects.has(ref))
|
|
45
|
+
invalid("manifest_subject_relation_unknown", `${subject.key}:${ref}`);
|
|
46
|
+
if (subject.parent_ref === null &&
|
|
47
|
+
subject.kind !== "surface" &&
|
|
48
|
+
subject.kind !== "flow")
|
|
49
|
+
invalid("manifest_subject_parent_required", subject.key);
|
|
50
|
+
if (subject.presence === "portal" && subject.portal_host_ref === null)
|
|
51
|
+
invalid("manifest_portal_host_required", subject.key);
|
|
52
|
+
validateRelationSubject(subject, subjects);
|
|
53
|
+
}
|
|
54
|
+
function validatePresenceAndPopulation(subject, census) {
|
|
55
|
+
if (subject.presence_rule_ref !== null) {
|
|
56
|
+
const presenceRule = census.get(subject.presence_rule_ref);
|
|
57
|
+
if (!presenceRule)
|
|
58
|
+
invalid("manifest_presence_rule_census_unknown", `${subject.key}:${subject.presence_rule_ref}`);
|
|
59
|
+
if (!["declaration", "state", "dynamic_population"].includes(presenceRule.kind))
|
|
60
|
+
invalid("manifest_presence_rule_census_kind_invalid", `${subject.key}:${subject.presence_rule_ref}:${presenceRule.kind}`);
|
|
61
|
+
}
|
|
62
|
+
if (subject.presence === "virtualized" && subject.population_ref === null)
|
|
63
|
+
invalid("manifest_virtualized_population_required", subject.key);
|
|
64
|
+
if (subject.population_ref === null)
|
|
65
|
+
return;
|
|
66
|
+
const population = census.get(subject.population_ref);
|
|
67
|
+
if (!population)
|
|
68
|
+
invalid("manifest_population_census_unknown", `${subject.key}:${subject.population_ref}`);
|
|
69
|
+
if (population.kind !== "dynamic_population")
|
|
70
|
+
invalid("manifest_population_census_kind_invalid", `${subject.key}:${subject.population_ref}:${population.kind}`);
|
|
71
|
+
}
|
|
72
|
+
function validateRelationSubject(subject, subjects) {
|
|
73
|
+
if (subject.kind !== "relation") {
|
|
74
|
+
if (subject.relation_endpoints.length)
|
|
75
|
+
invalid("manifest_nonrelation_endpoints_forbidden", subject.key);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (subject.relation_endpoints.length < 2)
|
|
79
|
+
invalid("manifest_relation_endpoints_required", subject.key);
|
|
80
|
+
unique(subject.relation_endpoints.map((item) => item.role), `manifest_relation_endpoint_role_duplicate:${subject.key}`);
|
|
81
|
+
for (const endpoint of subject.relation_endpoints)
|
|
82
|
+
if (endpoint.subject_ref === subject.key ||
|
|
83
|
+
!subjects.has(endpoint.subject_ref))
|
|
84
|
+
invalid("manifest_relation_endpoint_invalid", `${subject.key}:${endpoint.role}:${endpoint.subject_ref}`);
|
|
85
|
+
}
|
|
86
|
+
function validateSubjectParentAcyclic(manifest, subjects) {
|
|
87
|
+
for (const subject of manifest.subjects) {
|
|
88
|
+
const visited = new Set();
|
|
89
|
+
let current = subject;
|
|
90
|
+
while (current?.parent_ref) {
|
|
91
|
+
if (visited.has(current.key))
|
|
92
|
+
invalid("manifest_subject_parent_cycle", subject.key);
|
|
93
|
+
visited.add(current.key);
|
|
94
|
+
current = subjects.get(current.parent_ref);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { type DesignResourceObservableFactManifestV1 } from "./design-resource-fact-manifest-types.js";
|
|
2
|
+
export declare function validateManifestVariationUniverse(manifest: DesignResourceObservableFactManifestV1, census: Map<string, DesignResourceObservableFactManifestV1["inspector"]["census"][number]>, sourceItems: Map<string, string>): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DESIGN_RESOURCE_VARIATION_AXES, } from "./design-resource-fact-manifest-types.js";
|
|
2
|
+
import { axisFingerprint, cartesian, exactAxisPairs, groupBy, invalid, nonempty, refsKnown, sameSet, unique, validateBasis, variationValues, } from "./design-resource-fact-universe-helpers.js";
|
|
3
|
+
export function validateManifestVariationUniverse(manifest, census, sourceItems) {
|
|
4
|
+
const subjects = new Set(manifest.subjects.map((item) => item.key));
|
|
5
|
+
const variationsBySubject = groupBy(manifest.variations, (item) => item.subject_ref);
|
|
6
|
+
const axesBySubject = groupBy(manifest.variation_axis_dispositions, (item) => item.subject_ref);
|
|
7
|
+
const exclusionsBySubject = groupBy(manifest.variation_exclusions, (item) => item.subject_ref);
|
|
8
|
+
for (const subjectRef of subjects) {
|
|
9
|
+
validateSubjectVariations(subjectRef, variationsBySubject.get(subjectRef) ?? [], axesBySubject.get(subjectRef) ?? [], exclusionsBySubject.get(subjectRef) ?? [], census, sourceItems);
|
|
10
|
+
}
|
|
11
|
+
for (const ref of [
|
|
12
|
+
...variationsBySubject.keys(),
|
|
13
|
+
...axesBySubject.keys(),
|
|
14
|
+
...exclusionsBySubject.keys(),
|
|
15
|
+
])
|
|
16
|
+
if (!subjects.has(ref))
|
|
17
|
+
invalid("manifest_variation_subject_unknown", ref);
|
|
18
|
+
}
|
|
19
|
+
function validateSubjectVariations(subjectRef, variations, axes, exclusions, census, sourceItems) {
|
|
20
|
+
unique(axes.map((axis) => axis.axis), `manifest_variation_axis_duplicate:${subjectRef}`);
|
|
21
|
+
for (const axis of DESIGN_RESOURCE_VARIATION_AXES)
|
|
22
|
+
if (!axes.some((item) => item.axis === axis))
|
|
23
|
+
invalid("manifest_variation_standard_axis_missing", `${subjectRef}:${axis}`);
|
|
24
|
+
for (const axis of axes)
|
|
25
|
+
validateVariationAxis(axis, census, sourceItems);
|
|
26
|
+
const byAxis = new Map(axes.map((axis) => [axis.axis, axis]));
|
|
27
|
+
const actual = validateVariations(variations, byAxis);
|
|
28
|
+
const excluded = validateVariationExclusions(exclusions, census, sourceItems, actual);
|
|
29
|
+
const expected = cartesian(axes.map((axis) => axis.values.map((value) => [axis.axis, value.key]))).map(axisFingerprint);
|
|
30
|
+
sameSet([...actual.keys(), ...excluded.keys()], expected, "manifest_variation_universe_mismatch", subjectRef);
|
|
31
|
+
if (!actual.size)
|
|
32
|
+
invalid("manifest_subject_variation_required", subjectRef);
|
|
33
|
+
}
|
|
34
|
+
function validateVariationAxis(axis, census, sourceItems) {
|
|
35
|
+
validateBasis(axis, sourceItems, census, `manifest_variation_axis:${axis.key}`);
|
|
36
|
+
nonempty(axis.values, `manifest_variation_axis_values_required:${axis.key}`);
|
|
37
|
+
unique(axis.values.map((value) => value.key), `manifest_variation_axis_value_duplicate:${axis.key}`);
|
|
38
|
+
if (axis.disposition === "not_applicable" &&
|
|
39
|
+
(axis.values.length !== 1 || axis.values[0].key !== "not-applicable"))
|
|
40
|
+
invalid("manifest_variation_axis_not_applicable_invalid", axis.key);
|
|
41
|
+
for (const value of axis.values) {
|
|
42
|
+
if (axis.disposition === "applicable")
|
|
43
|
+
nonempty(value.census_refs, `manifest_variation_value_census_required:${axis.key}:${value.key}`);
|
|
44
|
+
refsKnown(value.census_refs, census, "manifest_variation_value_census_unknown", `${axis.key}:${value.key}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function validateVariations(variations, byAxis) {
|
|
48
|
+
const actual = new Map();
|
|
49
|
+
for (const variation of variations) {
|
|
50
|
+
const values = variationValues(variation);
|
|
51
|
+
for (const [axis, value] of values)
|
|
52
|
+
if (!byAxis.get(axis).values.some((candidate) => candidate.key === value))
|
|
53
|
+
invalid("manifest_variation_axis_value_unknown", `${variation.key}:${axis}:${value}`);
|
|
54
|
+
const fingerprint = axisFingerprint(values);
|
|
55
|
+
if (actual.has(fingerprint))
|
|
56
|
+
invalid("manifest_variation_combination_duplicate", `${actual.get(fingerprint)}:${variation.key}`);
|
|
57
|
+
actual.set(fingerprint, variation.key);
|
|
58
|
+
}
|
|
59
|
+
return actual;
|
|
60
|
+
}
|
|
61
|
+
function validateVariationExclusions(exclusions, census, sourceItems, actual) {
|
|
62
|
+
const excluded = new Map();
|
|
63
|
+
for (const row of exclusions) {
|
|
64
|
+
validateBasis(row, sourceItems, census, `manifest_variation_exclusion:${row.key}`);
|
|
65
|
+
if (row.disposition === "decision_required" ||
|
|
66
|
+
row.disposition === "unavailable")
|
|
67
|
+
invalid("manifest_variation_exclusion_unresolved", row.key);
|
|
68
|
+
const values = exactAxisPairs(row.axis_values, [...DESIGN_RESOURCE_VARIATION_AXES], `manifest_variation_exclusion:${row.key}`);
|
|
69
|
+
const fingerprint = axisFingerprint(values);
|
|
70
|
+
if (actual.has(fingerprint) || excluded.has(fingerprint))
|
|
71
|
+
invalid("manifest_variation_exclusion_duplicate", row.key);
|
|
72
|
+
excluded.set(fingerprint, row.key);
|
|
73
|
+
}
|
|
74
|
+
return excluded;
|
|
75
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { invalidDesignResourceHandoff } from "./design-resource-handoff-validation-primitives.js";
|
|
2
|
+
const VALUE_VALIDATORS = {
|
|
3
|
+
boolean: (text) => /^(?:true|false)$/u.test(text),
|
|
4
|
+
color: (text) => !text.includes("|") &&
|
|
5
|
+
(/^#[a-f0-9]{3,8}$/iu.test(text) ||
|
|
6
|
+
/^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix|light-dark|var)\(.+\)$/iu.test(text) ||
|
|
7
|
+
/^(?:transparent|currentcolor|[a-z]+)$/iu.test(text)),
|
|
8
|
+
digest: (text) => /^[a-f0-9]{64}$/u.test(text),
|
|
9
|
+
enum: (text) => /^[a-z0-9][a-z0-9._:-]*$/iu.test(text),
|
|
10
|
+
length: (text, structured) => isLength(text, structured),
|
|
11
|
+
list: (text, structured) => structured === null
|
|
12
|
+
? !text.includes("|")
|
|
13
|
+
: Array.isArray(structured) || typeof structured === "object",
|
|
14
|
+
locator: () => true,
|
|
15
|
+
number: (text) => isFiniteNumber(text),
|
|
16
|
+
ratio: (text, structured) => isRatio(text, structured),
|
|
17
|
+
relation: (_text, structured) => isStructuredCollection(structured),
|
|
18
|
+
semantic: (text, structured) => structured !== null || /^[a-z0-9][a-z0-9._:-]*$/iu.test(text),
|
|
19
|
+
string: () => true,
|
|
20
|
+
timeline: (_text, structured) => isStructuredCollection(structured),
|
|
21
|
+
token_ref: (text, structured) => structured !== null ||
|
|
22
|
+
/^(?:--[a-z0-9_-]+|[a-z0-9][a-z0-9._/-]*)$/iu.test(text),
|
|
23
|
+
trace: (_text, structured) => isStructuredCollection(structured),
|
|
24
|
+
};
|
|
25
|
+
export function validateDesignResourceValueKind(kind, value, label) {
|
|
26
|
+
const text = value.toString("utf8").trim();
|
|
27
|
+
const structured = text ? parseStructuredValue(text) : null;
|
|
28
|
+
if (!text || !VALUE_VALIDATORS[kind](text, structured)) {
|
|
29
|
+
invalidDesignResourceHandoff("located_value_kind_mismatch", `${label}:${kind}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseStructuredValue(value) {
|
|
33
|
+
if (!value.startsWith("{") && !value.startsWith("["))
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(value);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function isStructuredCollection(value) {
|
|
43
|
+
return value !== null && typeof value === "object";
|
|
44
|
+
}
|
|
45
|
+
function isFiniteNumber(value) {
|
|
46
|
+
if (!/^-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/iu.test(value))
|
|
47
|
+
return false;
|
|
48
|
+
return Number.isFinite(Number(value));
|
|
49
|
+
}
|
|
50
|
+
function isLength(value, structured) {
|
|
51
|
+
if (structured &&
|
|
52
|
+
typeof structured === "object" &&
|
|
53
|
+
!Array.isArray(structured)) {
|
|
54
|
+
const row = structured;
|
|
55
|
+
return (typeof row.value === "number" &&
|
|
56
|
+
Number.isFinite(row.value) &&
|
|
57
|
+
typeof row.unit === "string" &&
|
|
58
|
+
/^[a-z%][a-z0-9%_-]*$/iu.test(row.unit));
|
|
59
|
+
}
|
|
60
|
+
return (/^(?:0|-?(?:\d+\.?\d*|\.\d+)(?:px|dp|sp|pt|pc|in|cm|mm|q|em|rem|ex|ch|cap|ic|lh|rlh|vw|vh|vi|vb|vmin|vmax|svw|svh|lvw|lvh|dvw|dvh|cqw|cqh|cqi|cqb|cqmin|cqmax|%))$/iu.test(value) ||
|
|
61
|
+
/^(?:calc|min|max|clamp|fit-content|var|env)\(.+\)$/iu.test(value) ||
|
|
62
|
+
/^(?:auto|min-content|max-content|stretch|normal|hairline)$/iu.test(value));
|
|
63
|
+
}
|
|
64
|
+
function isRatio(value, structured) {
|
|
65
|
+
if (isFiniteNumber(value))
|
|
66
|
+
return true;
|
|
67
|
+
if (/^-?(?:\d+\.?\d*|\.\d+)\s*\/\s*-?(?:\d+\.?\d*|\.\d+)$/u.test(value))
|
|
68
|
+
return true;
|
|
69
|
+
if (Array.isArray(structured)) {
|
|
70
|
+
return (structured.length === 2 &&
|
|
71
|
+
structured.every((item) => typeof item === "number" && Number.isFinite(item)));
|
|
72
|
+
}
|
|
73
|
+
if (!structured || typeof structured !== "object")
|
|
74
|
+
return false;
|
|
75
|
+
const row = structured;
|
|
76
|
+
return (typeof row.width === "number" &&
|
|
77
|
+
Number.isFinite(row.width) &&
|
|
78
|
+
typeof row.height === "number" &&
|
|
79
|
+
Number.isFinite(row.height));
|
|
80
|
+
}
|