worldvm 0.0.0-stage → 0.1.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/AUTHORING.md +833 -0
- package/README.md +154 -2
- package/bin/worldvm.mjs +3 -0
- package/dist/_internal/forma/index.d.ts +1 -0
- package/dist/_internal/forma/index.js +1 -0
- package/dist/_internal/forma/world/authoring-help.d.ts +25 -0
- package/dist/_internal/forma/world/authoring-help.js +130 -0
- package/dist/_internal/forma/world/citations.d.ts +3 -0
- package/dist/_internal/forma/world/citations.js +26 -0
- package/dist/_internal/forma/world/clock-bindings.d.ts +4 -0
- package/dist/_internal/forma/world/clock-bindings.js +45 -0
- package/dist/_internal/forma/world/compile.d.ts +13 -0
- package/dist/_internal/forma/world/compile.js +190 -0
- package/dist/_internal/forma/world/coverage.d.ts +10 -0
- package/dist/_internal/forma/world/coverage.js +242 -0
- package/dist/_internal/forma/world/digest.d.ts +1 -0
- package/dist/_internal/forma/world/digest.js +1 -0
- package/dist/_internal/forma/world/documentation.d.ts +3 -0
- package/dist/_internal/forma/world/documentation.js +38 -0
- package/dist/_internal/forma/world/homes.d.ts +4 -0
- package/dist/_internal/forma/world/homes.js +202 -0
- package/dist/_internal/forma/world/index.d.ts +6 -0
- package/dist/_internal/forma/world/index.js +5 -0
- package/dist/_internal/forma/world/lowering/accounts.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/accounts.js +36 -0
- package/dist/_internal/forma/world/lowering/action-policy.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/action-policy.js +16 -0
- package/dist/_internal/forma/world/lowering/agents.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/agents.js +116 -0
- package/dist/_internal/forma/world/lowering/bundles.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/bundles.js +13 -0
- package/dist/_internal/forma/world/lowering/child-parameters.d.ts +14 -0
- package/dist/_internal/forma/world/lowering/child-parameters.js +36 -0
- package/dist/_internal/forma/world/lowering/collects.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/collects.js +51 -0
- package/dist/_internal/forma/world/lowering/component-operations.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/component-operations.js +26 -0
- package/dist/_internal/forma/world/lowering/components.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/components.js +236 -0
- package/dist/_internal/forma/world/lowering/connectors.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/connectors.js +31 -0
- package/dist/_internal/forma/world/lowering/context.d.ts +80 -0
- package/dist/_internal/forma/world/lowering/context.js +316 -0
- package/dist/_internal/forma/world/lowering/contracts.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/contracts.js +73 -0
- package/dist/_internal/forma/world/lowering/decisions.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/decisions.js +164 -0
- package/dist/_internal/forma/world/lowering/derives.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/derives.js +22 -0
- package/dist/_internal/forma/world/lowering/discretionary.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/discretionary.js +15 -0
- package/dist/_internal/forma/world/lowering/documents.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/documents.js +68 -0
- package/dist/_internal/forma/world/lowering/effective.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/effective.js +26 -0
- package/dist/_internal/forma/world/lowering/entities.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/entities.js +43 -0
- package/dist/_internal/forma/world/lowering/escalations.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/escalations.js +31 -0
- package/dist/_internal/forma/world/lowering/evidence.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/evidence.js +75 -0
- package/dist/_internal/forma/world/lowering/files.d.ts +4 -0
- package/dist/_internal/forma/world/lowering/files.js +56 -0
- package/dist/_internal/forma/world/lowering/holds.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/holds.js +35 -0
- package/dist/_internal/forma/world/lowering/index.d.ts +89 -0
- package/dist/_internal/forma/world/lowering/index.js +115 -0
- package/dist/_internal/forma/world/lowering/interactions.d.ts +7 -0
- package/dist/_internal/forma/world/lowering/interactions.js +190 -0
- package/dist/_internal/forma/world/lowering/library.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/library.js +41 -0
- package/dist/_internal/forma/world/lowering/milestones.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/milestones.js +17 -0
- package/dist/_internal/forma/world/lowering/notices.d.ts +3 -0
- package/dist/_internal/forma/world/lowering/notices.js +33 -0
- package/dist/_internal/forma/world/lowering/param-bounds.d.ts +15 -0
- package/dist/_internal/forma/world/lowering/param-bounds.js +71 -0
- package/dist/_internal/forma/world/lowering/policies.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/policies.js +95 -0
- package/dist/_internal/forma/world/lowering/principals.d.ts +62 -0
- package/dist/_internal/forma/world/lowering/principals.js +32 -0
- package/dist/_internal/forma/world/lowering/recurrences.d.ts +9 -0
- package/dist/_internal/forma/world/lowering/recurrences.js +56 -0
- package/dist/_internal/forma/world/lowering/renewal.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/renewal.js +21 -0
- package/dist/_internal/forma/world/lowering/resources.d.ts +6 -0
- package/dist/_internal/forma/world/lowering/resources.js +19 -0
- package/dist/_internal/forma/world/lowering/reuse.d.ts +5 -0
- package/dist/_internal/forma/world/lowering/reuse.js +75 -0
- package/dist/_internal/forma/world/lowering/sources.d.ts +10 -0
- package/dist/_internal/forma/world/lowering/sources.js +47 -0
- package/dist/_internal/forma/world/lowering/syntax.d.ts +84 -0
- package/dist/_internal/forma/world/lowering/syntax.js +94 -0
- package/dist/_internal/forma/world/lowering/vaults.d.ts +11 -0
- package/dist/_internal/forma/world/lowering/vaults.js +148 -0
- package/dist/_internal/forma/world/prelude.generated.d.ts +1 -0
- package/dist/_internal/forma/world/prelude.generated.js +2 -0
- package/dist/_internal/forma/world/properties.d.ts +9 -0
- package/dist/_internal/forma/world/properties.js +387 -0
- package/dist/_internal/forma/world/scenarios.d.ts +10 -0
- package/dist/_internal/forma/world/scenarios.js +757 -0
- package/dist/_internal/forma/world/sensitivity.d.ts +3 -0
- package/dist/_internal/forma/world/sensitivity.js +118 -0
- package/dist/_internal/forma/world/source-syntax.d.ts +6 -0
- package/dist/_internal/forma/world/source-syntax.js +27 -0
- package/dist/_internal/forma/world/status-handling.d.ts +3 -0
- package/dist/_internal/forma/world/status-handling.js +143 -0
- package/dist/_internal/forma/world/surface.d.ts +30 -0
- package/dist/_internal/forma/world/surface.js +213 -0
- package/dist/_internal/forma/world/table-expressions.d.ts +3 -0
- package/dist/_internal/forma/world/table-expressions.js +41 -0
- package/dist/_internal/forma-tools/index.d.ts +17 -0
- package/dist/_internal/forma-tools/index.js +124 -0
- package/dist/_internal/ir-checks/advances.d.ts +7 -0
- package/dist/_internal/ir-checks/advances.js +6 -0
- package/dist/_internal/ir-checks/agent-reads.d.ts +14 -0
- package/dist/_internal/ir-checks/agent-reads.js +118 -0
- package/dist/_internal/ir-checks/amendments.d.ts +3 -0
- package/dist/_internal/ir-checks/amendments.js +21 -0
- package/dist/_internal/ir-checks/canonical-json.d.ts +3 -0
- package/dist/_internal/ir-checks/canonical-json.js +85 -0
- package/dist/_internal/ir-checks/child-parameters.d.ts +2 -0
- package/dist/_internal/ir-checks/child-parameters.js +43 -0
- package/dist/_internal/ir-checks/clock-bindings.d.ts +3 -0
- package/dist/_internal/ir-checks/clock-bindings.js +43 -0
- package/dist/_internal/ir-checks/collect-types.d.ts +14 -0
- package/dist/_internal/ir-checks/collect-types.js +34 -0
- package/dist/_internal/ir-checks/component-operations.d.ts +4 -0
- package/dist/_internal/ir-checks/component-operations.js +23 -0
- package/dist/_internal/ir-checks/content-digest.d.ts +4 -0
- package/dist/_internal/ir-checks/content-digest.js +78 -0
- package/dist/_internal/ir-checks/contract-validation.d.ts +3 -0
- package/dist/_internal/ir-checks/contract-validation.js +307 -0
- package/dist/_internal/ir-checks/contracts.d.ts +25 -0
- package/dist/_internal/ir-checks/contracts.js +80 -0
- package/dist/_internal/ir-checks/dates.d.ts +2 -0
- package/dist/_internal/ir-checks/dates.js +7 -0
- package/dist/_internal/ir-checks/documents.d.ts +3 -0
- package/dist/_internal/ir-checks/documents.js +45 -0
- package/dist/_internal/ir-checks/evidence.d.ts +20 -0
- package/dist/_internal/ir-checks/evidence.js +56 -0
- package/dist/_internal/ir-checks/files.d.ts +7 -0
- package/dist/_internal/ir-checks/files.js +47 -0
- package/dist/_internal/ir-checks/index.d.ts +59 -0
- package/dist/_internal/ir-checks/index.js +166 -0
- package/dist/_internal/ir-checks/instants.d.ts +7 -0
- package/dist/_internal/ir-checks/instants.js +61 -0
- package/dist/_internal/ir-checks/notices.d.ts +4 -0
- package/dist/_internal/ir-checks/notices.js +58 -0
- package/dist/_internal/ir-checks/policy-bounds.d.ts +5 -0
- package/dist/_internal/ir-checks/policy-bounds.js +28 -0
- package/dist/_internal/ir-checks/principals.d.ts +26 -0
- package/dist/_internal/ir-checks/principals.js +162 -0
- package/dist/_internal/ir-checks/prior-cycle.d.ts +41 -0
- package/dist/_internal/ir-checks/prior-cycle.js +21 -0
- package/dist/_internal/ir-checks/read-classification.d.ts +12 -0
- package/dist/_internal/ir-checks/read-classification.js +67 -0
- package/dist/_internal/ir-checks/read-types.d.ts +2 -0
- package/dist/_internal/ir-checks/read-types.js +40 -0
- package/dist/_internal/ir-checks/resource-proofs.d.ts +4 -0
- package/dist/_internal/ir-checks/resource-proofs.js +98 -0
- package/dist/_internal/ir-checks/resources.d.ts +3 -0
- package/dist/_internal/ir-checks/resources.js +30 -0
- package/dist/_internal/ir-checks/reuse.d.ts +10 -0
- package/dist/_internal/ir-checks/reuse.js +99 -0
- package/dist/_internal/ir-checks/sealing.d.ts +14 -0
- package/dist/_internal/ir-checks/sealing.js +123 -0
- package/dist/_internal/ir-checks/sensitive-flow.d.ts +4 -0
- package/dist/_internal/ir-checks/sensitive-flow.js +240 -0
- package/dist/_internal/ir-checks/sensitivity.d.ts +19 -0
- package/dist/_internal/ir-checks/sensitivity.js +133 -0
- package/dist/_internal/ir-checks/signatures.d.ts +4 -0
- package/dist/_internal/ir-checks/signatures.js +22 -0
- package/dist/_internal/ir-checks/vaults.d.ts +10 -0
- package/dist/_internal/ir-checks/vaults.js +140 -0
- package/dist/_internal/protocol/index.d.ts +1234 -0
- package/dist/_internal/protocol/index.js +215 -0
- package/dist/_internal/reconciler/action-intents.d.ts +17 -0
- package/dist/_internal/reconciler/action-intents.js +161 -0
- package/dist/_internal/reconciler/actions.d.ts +7 -0
- package/dist/_internal/reconciler/actions.js +134 -0
- package/dist/_internal/reconciler/amendments.d.ts +12 -0
- package/dist/_internal/reconciler/amendments.js +52 -0
- package/dist/_internal/reconciler/basis.d.ts +253 -0
- package/dist/_internal/reconciler/basis.js +16 -0
- package/dist/_internal/reconciler/bundles.d.ts +22 -0
- package/dist/_internal/reconciler/bundles.js +110 -0
- package/dist/_internal/reconciler/change-detection.d.ts +5 -0
- package/dist/_internal/reconciler/change-detection.js +82 -0
- package/dist/_internal/reconciler/collection-rules.d.ts +7 -0
- package/dist/_internal/reconciler/collection-rules.js +46 -0
- package/dist/_internal/reconciler/collection-state.d.ts +11 -0
- package/dist/_internal/reconciler/collection-state.js +62 -0
- package/dist/_internal/reconciler/collection-values.d.ts +5 -0
- package/dist/_internal/reconciler/collection-values.js +60 -0
- package/dist/_internal/reconciler/collections.d.ts +35 -0
- package/dist/_internal/reconciler/collections.js +141 -0
- package/dist/_internal/reconciler/commands.d.ts +60 -0
- package/dist/_internal/reconciler/commands.js +468 -0
- package/dist/_internal/reconciler/component-operations.d.ts +48 -0
- package/dist/_internal/reconciler/component-operations.js +256 -0
- package/dist/_internal/reconciler/context.d.ts +38 -0
- package/dist/_internal/reconciler/context.js +44 -0
- package/dist/_internal/reconciler/coverage.d.ts +31 -0
- package/dist/_internal/reconciler/coverage.js +124 -0
- package/dist/_internal/reconciler/dates.d.ts +38 -0
- package/dist/_internal/reconciler/dates.js +121 -0
- package/dist/_internal/reconciler/decide.d.ts +25 -0
- package/dist/_internal/reconciler/decide.js +263 -0
- package/dist/_internal/reconciler/decision-evaluation.d.ts +9 -0
- package/dist/_internal/reconciler/decision-evaluation.js +67 -0
- package/dist/_internal/reconciler/decision-records.d.ts +14 -0
- package/dist/_internal/reconciler/decision-records.js +77 -0
- package/dist/_internal/reconciler/decision-release.d.ts +3 -0
- package/dist/_internal/reconciler/decision-release.js +12 -0
- package/dist/_internal/reconciler/dependencies.d.ts +179 -0
- package/dist/_internal/reconciler/dependencies.js +372 -0
- package/dist/_internal/reconciler/discovery.d.ts +9 -0
- package/dist/_internal/reconciler/discovery.js +65 -0
- package/dist/_internal/reconciler/document-corrections.d.ts +18 -0
- package/dist/_internal/reconciler/document-corrections.js +59 -0
- package/dist/_internal/reconciler/documents.d.ts +26 -0
- package/dist/_internal/reconciler/documents.js +88 -0
- package/dist/_internal/reconciler/effective.d.ts +6 -0
- package/dist/_internal/reconciler/effective.js +17 -0
- package/dist/_internal/reconciler/escalations.d.ts +4 -0
- package/dist/_internal/reconciler/escalations.js +76 -0
- package/dist/_internal/reconciler/evaluation.d.ts +56 -0
- package/dist/_internal/reconciler/evaluation.js +704 -0
- package/dist/_internal/reconciler/evidence-authority.d.ts +6 -0
- package/dist/_internal/reconciler/evidence-authority.js +70 -0
- package/dist/_internal/reconciler/evidence-history.d.ts +5 -0
- package/dist/_internal/reconciler/evidence-history.js +22 -0
- package/dist/_internal/reconciler/evidence-validity.d.ts +17 -0
- package/dist/_internal/reconciler/evidence-validity.js +135 -0
- package/dist/_internal/reconciler/fact-validation.d.ts +5 -0
- package/dist/_internal/reconciler/fact-validation.js +49 -0
- package/dist/_internal/reconciler/files.d.ts +9 -0
- package/dist/_internal/reconciler/files.js +56 -0
- package/dist/_internal/reconciler/graph.d.ts +40 -0
- package/dist/_internal/reconciler/graph.js +298 -0
- package/dist/_internal/reconciler/holds.d.ts +6 -0
- package/dist/_internal/reconciler/holds.js +18 -0
- package/dist/_internal/reconciler/home-definitions.d.ts +11 -0
- package/dist/_internal/reconciler/home-definitions.js +25 -0
- package/dist/_internal/reconciler/homes.d.ts +6 -0
- package/dist/_internal/reconciler/homes.js +32 -0
- package/dist/_internal/reconciler/index.d.ts +42 -0
- package/dist/_internal/reconciler/index.js +58 -0
- package/dist/_internal/reconciler/instants.d.ts +17 -0
- package/dist/_internal/reconciler/instants.js +56 -0
- package/dist/_internal/reconciler/interaction-fields.d.ts +10 -0
- package/dist/_internal/reconciler/interaction-fields.js +30 -0
- package/dist/_internal/reconciler/invariants.d.ts +8 -0
- package/dist/_internal/reconciler/invariants.js +34 -0
- package/dist/_internal/reconciler/journal.d.ts +3 -0
- package/dist/_internal/reconciler/journal.js +4 -0
- package/dist/_internal/reconciler/linking.d.ts +10 -0
- package/dist/_internal/reconciler/linking.js +259 -0
- package/dist/_internal/reconciler/memory.d.ts +32 -0
- package/dist/_internal/reconciler/memory.js +190 -0
- package/dist/_internal/reconciler/milestones.d.ts +7 -0
- package/dist/_internal/reconciler/milestones.js +58 -0
- package/dist/_internal/reconciler/missing.d.ts +18 -0
- package/dist/_internal/reconciler/missing.js +79 -0
- package/dist/_internal/reconciler/model.d.ts +350 -0
- package/dist/_internal/reconciler/model.js +9 -0
- package/dist/_internal/reconciler/notices.d.ts +14 -0
- package/dist/_internal/reconciler/notices.js +80 -0
- package/dist/_internal/reconciler/occurrences.d.ts +3 -0
- package/dist/_internal/reconciler/occurrences.js +30 -0
- package/dist/_internal/reconciler/outputs.d.ts +12 -0
- package/dist/_internal/reconciler/outputs.js +132 -0
- package/dist/_internal/reconciler/parameters.d.ts +4 -0
- package/dist/_internal/reconciler/parameters.js +27 -0
- package/dist/_internal/reconciler/policies.d.ts +22 -0
- package/dist/_internal/reconciler/policies.js +94 -0
- package/dist/_internal/reconciler/policy-edits.d.ts +11 -0
- package/dist/_internal/reconciler/policy-edits.js +42 -0
- package/dist/_internal/reconciler/policy-linking.d.ts +4 -0
- package/dist/_internal/reconciler/policy-linking.js +104 -0
- package/dist/_internal/reconciler/principal-types.d.ts +2 -0
- package/dist/_internal/reconciler/principal-types.js +38 -0
- package/dist/_internal/reconciler/prior-decisions.d.ts +3 -0
- package/dist/_internal/reconciler/prior-decisions.js +34 -0
- package/dist/_internal/reconciler/projections.d.ts +4 -0
- package/dist/_internal/reconciler/projections.js +85 -0
- package/dist/_internal/reconciler/proofs.d.ts +9 -0
- package/dist/_internal/reconciler/proofs.js +24 -0
- package/dist/_internal/reconciler/properties.d.ts +25 -0
- package/dist/_internal/reconciler/properties.js +67 -0
- package/dist/_internal/reconciler/property-eval.d.ts +5 -0
- package/dist/_internal/reconciler/property-eval.js +101 -0
- package/dist/_internal/reconciler/property-generator.d.ts +9 -0
- package/dist/_internal/reconciler/property-generator.js +288 -0
- package/dist/_internal/reconciler/property-shrink.d.ts +14 -0
- package/dist/_internal/reconciler/property-shrink.js +183 -0
- package/dist/_internal/reconciler/provenance.d.ts +8 -0
- package/dist/_internal/reconciler/provenance.js +61 -0
- package/dist/_internal/reconciler/reconcile.d.ts +32 -0
- package/dist/_internal/reconciler/reconcile.js +164 -0
- package/dist/_internal/reconciler/recurrence.d.ts +9 -0
- package/dist/_internal/reconciler/recurrence.js +44 -0
- package/dist/_internal/reconciler/requirements.d.ts +5 -0
- package/dist/_internal/reconciler/requirements.js +331 -0
- package/dist/_internal/reconciler/resource-generation.d.ts +9 -0
- package/dist/_internal/reconciler/resource-generation.js +17 -0
- package/dist/_internal/reconciler/resource-scenarios.d.ts +26 -0
- package/dist/_internal/reconciler/resource-scenarios.js +78 -0
- package/dist/_internal/reconciler/resources.d.ts +24 -0
- package/dist/_internal/reconciler/resources.js +178 -0
- package/dist/_internal/reconciler/result-digest.d.ts +13 -0
- package/dist/_internal/reconciler/result-digest.js +66 -0
- package/dist/_internal/reconciler/reuse.d.ts +16 -0
- package/dist/_internal/reconciler/reuse.js +119 -0
- package/dist/_internal/reconciler/scenarios.d.ts +32 -0
- package/dist/_internal/reconciler/scenarios.js +442 -0
- package/dist/_internal/reconciler/scheduling.d.ts +9 -0
- package/dist/_internal/reconciler/scheduling.js +203 -0
- package/dist/_internal/reconciler/sha256.d.ts +1 -0
- package/dist/_internal/reconciler/sha256.js +1 -0
- package/dist/_internal/reconciler/signatures.d.ts +13 -0
- package/dist/_internal/reconciler/signatures.js +37 -0
- package/dist/_internal/reconciler/temporal.d.ts +10 -0
- package/dist/_internal/reconciler/temporal.js +46 -0
- package/dist/_internal/reconciler/upgrades.d.ts +6 -0
- package/dist/_internal/reconciler/upgrades.js +65 -0
- package/dist/_internal/reconciler/wire.d.ts +2 -0
- package/dist/_internal/reconciler/wire.js +5 -0
- package/dist/_internal/reconciler/work.d.ts +32 -0
- package/dist/_internal/reconciler/work.js +101 -0
- package/dist/_internal/world-ir/schema.generated.d.ts +180 -0
- package/dist/_internal/world-ir/schema.generated.js +1127 -0
- package/dist/_internal/world-ir/types.generated.d.ts +1011 -0
- package/dist/_internal/world-ir/types.generated.js +1 -0
- package/dist/authoring.d.ts +9 -0
- package/dist/authoring.js +10 -0
- package/dist/catalogue.d.ts +20 -0
- package/dist/catalogue.js +531 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +553 -0
- package/dist/codegen.d.ts +6 -0
- package/dist/codegen.js +89 -0
- package/dist/coverage.d.ts +60 -0
- package/dist/coverage.js +128 -0
- package/dist/deploy.d.ts +21 -0
- package/dist/deploy.js +72 -0
- package/dist/diagnostics.d.ts +9 -0
- package/dist/diagnostics.js +37 -0
- package/dist/diff.d.ts +17 -0
- package/dist/diff.js +19 -0
- package/dist/durable-object.d.ts +14 -0
- package/dist/durable-object.js +30 -0
- package/dist/explain.d.ts +113 -0
- package/dist/explain.js +225 -0
- package/dist/fixes.d.ts +4 -0
- package/dist/fixes.js +70 -0
- package/dist/fmt.d.ts +12 -0
- package/dist/fmt.js +39 -0
- package/dist/handle/open.d.ts +18 -0
- package/dist/handle/open.js +28 -0
- package/dist/handle/protocol.d.ts +12 -0
- package/dist/handle/protocol.js +96 -0
- package/dist/handle/store.d.ts +31 -0
- package/dist/handle/store.js +11 -0
- package/dist/handle/types.d.ts +37 -0
- package/dist/handle/types.js +1 -0
- package/dist/handle/world.d.ts +190 -0
- package/dist/handle/world.js +533 -0
- package/dist/http.d.ts +3 -0
- package/dist/http.js +72 -0
- package/dist/impact.d.ts +21 -0
- package/dist/impact.js +57 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +43 -0
- package/dist/init.d.ts +13 -0
- package/dist/init.js +99 -0
- package/dist/ir-schema.json +4213 -0
- package/dist/lints.d.ts +8 -0
- package/dist/lints.js +154 -0
- package/dist/lsp.d.ts +14 -0
- package/dist/lsp.js +380 -0
- package/dist/manual/catalog.d.ts +12 -0
- package/dist/manual/catalog.js +23 -0
- package/dist/manual/examples.d.ts +15 -0
- package/dist/manual/examples.js +39 -0
- package/dist/manual/projections.d.ts +77 -0
- package/dist/manual/projections.js +344 -0
- package/dist/manual/prose.d.ts +19 -0
- package/dist/manual/prose.js +215 -0
- package/dist/manual/render.d.ts +76 -0
- package/dist/manual/render.js +250 -0
- package/dist/manual/text.d.ts +7 -0
- package/dist/manual/text.js +15 -0
- package/dist/mcp.d.ts +32 -0
- package/dist/mcp.js +269 -0
- package/dist/mutate.d.ts +36 -0
- package/dist/mutate.js +123 -0
- package/dist/project.d.ts +26 -0
- package/dist/project.js +80 -0
- package/dist/refine.d.ts +18 -0
- package/dist/refine.js +197 -0
- package/dist/release/diff.d.ts +45 -0
- package/dist/release/diff.js +103 -0
- package/dist/release/impact.d.ts +83 -0
- package/dist/release/impact.js +214 -0
- package/dist/release/nodes.d.ts +20 -0
- package/dist/release/nodes.js +118 -0
- package/dist/render.d.ts +19 -0
- package/dist/render.js +43 -0
- package/dist/serve.d.ts +18 -0
- package/dist/serve.js +140 -0
- package/dist/snapshot.d.ts +19 -0
- package/dist/snapshot.js +45 -0
- package/dist/sqlite.d.ts +16 -0
- package/dist/sqlite.js +151 -0
- package/dist/test.d.ts +54 -0
- package/dist/test.js +131 -0
- package/dist/world.d.ts +7 -0
- package/dist/world.js +5 -0
- package/docs/authoring/vendor-risk/example.forma +11 -0
- package/docs/authoring/vendor-risk/library/vendor.forma +76 -0
- package/docs/authoring/vendor-risk/scenarios/reviews.forma +107 -0
- package/docs/protocol.md +195 -0
- package/docs/reference.md +258 -0
- package/package.json +81 -3
package/dist/mutate.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Span } from "./_internal/forma/world/index.js";
|
|
2
|
+
import { type Project } from "./project.js";
|
|
3
|
+
import { type TestOptions } from "./test.js";
|
|
4
|
+
/** One semantic edit to the source, and what the tests made of it. */
|
|
5
|
+
export interface Mutant {
|
|
6
|
+
readonly id: number;
|
|
7
|
+
readonly operator: "valid->attested" | "attested->valid" | "drop-conjunct" | "drop-disjunct" | "flip-equality" | "remove-applies-when";
|
|
8
|
+
readonly requirement: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
readonly at: Span;
|
|
11
|
+
/** killed: some scenario/property failed. survived: every test still passed (a gap). */
|
|
12
|
+
readonly outcome: "killed" | "survived" | "invalid";
|
|
13
|
+
readonly killedBy?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export interface MutationReport {
|
|
16
|
+
readonly mutants: readonly Mutant[];
|
|
17
|
+
readonly killed: number;
|
|
18
|
+
readonly survived: number;
|
|
19
|
+
readonly invalid: number;
|
|
20
|
+
}
|
|
21
|
+
interface Edit {
|
|
22
|
+
readonly src: string;
|
|
23
|
+
readonly from: number;
|
|
24
|
+
readonly to: number;
|
|
25
|
+
readonly text: string;
|
|
26
|
+
}
|
|
27
|
+
/** Candidate mutants for every requirement's :applies-when and :satisfied-by. */
|
|
28
|
+
export declare const mutants: (project: Project) => (Omit<Mutant, "id" | "outcome" | "killedBy"> & {
|
|
29
|
+
edit: Edit;
|
|
30
|
+
})[];
|
|
31
|
+
/**
|
|
32
|
+
* Apply each mutant alone, recompile and rerun every scenario and property. A mutant that
|
|
33
|
+
* compiles and still passes every test is a behavioural gap the tests do not pin down.
|
|
34
|
+
*/
|
|
35
|
+
export declare const mutationTest: (project: Project, options?: Pick<TestOptions, "seed" | "cases">) => MutationReport;
|
|
36
|
+
export {};
|
package/dist/mutate.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { compileProject } from "./project.js";
|
|
2
|
+
import { runTests } from "./test.js";
|
|
3
|
+
const calls = (e, f) => {
|
|
4
|
+
if (!e || typeof e !== "object")
|
|
5
|
+
return;
|
|
6
|
+
if (Array.isArray(e))
|
|
7
|
+
return e.forEach((x) => calls(x, f));
|
|
8
|
+
const node = e;
|
|
9
|
+
if (node.k === "call")
|
|
10
|
+
f(node);
|
|
11
|
+
for (const [key, child] of Object.entries(node))
|
|
12
|
+
if (key !== "at")
|
|
13
|
+
calls(child, f);
|
|
14
|
+
};
|
|
15
|
+
/** The head token of the list at `at`, located in the source text. */
|
|
16
|
+
const head = (text, at) => {
|
|
17
|
+
const m = /^\(\s*([^\s()[\]{}]+)/.exec(text.slice(at.from, at.to));
|
|
18
|
+
return m ? { name: m[1], from: at.from + m[0].length - m[1].length } : undefined;
|
|
19
|
+
};
|
|
20
|
+
/** Remove one element of a vector, with the whitespace that separated it. */
|
|
21
|
+
const removeItem = (text, items, index) => {
|
|
22
|
+
const item = items[index].at;
|
|
23
|
+
if (index < items.length - 1)
|
|
24
|
+
return { from: item.from, to: items[index + 1].at.from, text: "" };
|
|
25
|
+
return { from: items[index - 1].at.to, to: item.to, text: "" };
|
|
26
|
+
};
|
|
27
|
+
/** Candidate mutants for every requirement's :applies-when and :satisfied-by. */
|
|
28
|
+
export const mutants = (project) => {
|
|
29
|
+
const ir = project.ir;
|
|
30
|
+
const out = [];
|
|
31
|
+
const evidenceByName = new Map((ir.evidence ?? []).map((e) => [e.name, e]));
|
|
32
|
+
const source = (src) => project.files.find((f) => f.id === src)?.text ?? "";
|
|
33
|
+
for (const c of ir.components)
|
|
34
|
+
for (const r of c.requirements) {
|
|
35
|
+
const requirement = `${c.name}/${r.name}`;
|
|
36
|
+
for (const [slot, expr] of [
|
|
37
|
+
["applies-when", r.appliesWhen],
|
|
38
|
+
["satisfied-by", r.satisfiedBy],
|
|
39
|
+
]) {
|
|
40
|
+
if (!expr)
|
|
41
|
+
continue;
|
|
42
|
+
calls(expr, (call) => {
|
|
43
|
+
const text = source(call.at.src), h = head(text, call.at);
|
|
44
|
+
if (!h)
|
|
45
|
+
return;
|
|
46
|
+
const swap = (operator, to) => out.push({
|
|
47
|
+
operator,
|
|
48
|
+
requirement,
|
|
49
|
+
description: `${slot}: ${h.name} -> ${to} in ${text.slice(call.at.from, call.at.to)}`,
|
|
50
|
+
at: call.at,
|
|
51
|
+
edit: { src: call.at.src, from: h.from, to: h.from + h.name.length, text: to },
|
|
52
|
+
});
|
|
53
|
+
// Without :expires, valid and attested agree: that mutant would be equivalent.
|
|
54
|
+
const arg = call.args[0];
|
|
55
|
+
const expiring = arg?.k === "ref" && arg.kind === "evidence" && !!evidenceByName.get(arg.name)?.expires;
|
|
56
|
+
if (h.name === "valid" && expiring)
|
|
57
|
+
swap("valid->attested", "attested");
|
|
58
|
+
if (h.name === "attested" && expiring)
|
|
59
|
+
swap("attested->valid", "valid");
|
|
60
|
+
if (h.name === "=")
|
|
61
|
+
swap("flip-equality", "!=");
|
|
62
|
+
if (h.name === "not=" || h.name === "!=")
|
|
63
|
+
swap("flip-equality", "=");
|
|
64
|
+
const list = call.args[0];
|
|
65
|
+
if ((h.name === "and" || h.name === "or") && list?.k === "list" && list.items.length > 1)
|
|
66
|
+
list.items.forEach((item, index) => {
|
|
67
|
+
const edit = removeItem(text, list.items, index);
|
|
68
|
+
out.push({
|
|
69
|
+
operator: h.name === "and" ? "drop-conjunct" : "drop-disjunct",
|
|
70
|
+
requirement,
|
|
71
|
+
description: `${slot}: drop ${text.slice(item.at.from, item.at.to)} from ${h.name}`,
|
|
72
|
+
at: item.at,
|
|
73
|
+
edit: { src: call.at.src, ...edit },
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (r.appliesWhen) {
|
|
79
|
+
const at = r.appliesWhen.at, text = source(at.src);
|
|
80
|
+
const keyword = text.lastIndexOf(":applies-when", at.from);
|
|
81
|
+
if (keyword >= r.at.from && /^\s*$/.test(text.slice(keyword + 13, at.from)))
|
|
82
|
+
out.push({
|
|
83
|
+
operator: "remove-applies-when",
|
|
84
|
+
requirement,
|
|
85
|
+
description: `remove :applies-when ${text.slice(at.from, at.to)}`,
|
|
86
|
+
at,
|
|
87
|
+
edit: { src: at.src, from: keyword, to: at.to, text: "" },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Apply each mutant alone, recompile and rerun every scenario and property. A mutant that
|
|
95
|
+
* compiles and still passes every test is a behavioural gap the tests do not pin down.
|
|
96
|
+
*/
|
|
97
|
+
export const mutationTest = (project, options = {}) => {
|
|
98
|
+
const results = [];
|
|
99
|
+
mutants(project).forEach(({ edit, ...m }, index) => {
|
|
100
|
+
const files = project.files.map((f) => f.id === edit.src
|
|
101
|
+
? { id: f.id, text: f.text.slice(0, edit.from) + edit.text + f.text.slice(edit.to) }
|
|
102
|
+
: f);
|
|
103
|
+
const mutated = compileProject(project.directory, files);
|
|
104
|
+
const base = { id: index + 1, ...m };
|
|
105
|
+
if (!mutated.ir || mutated.diagnostics.some((d) => d.severity === "error")) {
|
|
106
|
+
results.push({ ...base, outcome: "invalid" });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const tests = runTests(mutated, options);
|
|
110
|
+
const killedBy = [
|
|
111
|
+
...tests.scenarios.filter((s) => !s.passed).map((s) => `scenario ${s.name}`),
|
|
112
|
+
...tests.properties.filter((p) => !p.passed).map((p) => `property ${p.name}`),
|
|
113
|
+
...(tests.diagnostics.some((d) => d.severity === "error") ? ["test runtime error"] : []),
|
|
114
|
+
];
|
|
115
|
+
results.push(killedBy.length ? { ...base, outcome: "killed", killedBy } : { ...base, outcome: "survived" });
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
mutants: results,
|
|
119
|
+
killed: results.filter((m) => m.outcome === "killed").length,
|
|
120
|
+
survived: results.filter((m) => m.outcome === "survived").length,
|
|
121
|
+
invalid: results.filter((m) => m.outcome === "invalid").length,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type SourceFile, type Span } from "./_internal/forma/world/index.js";
|
|
2
|
+
import { type IR } from "./_internal/reconciler/index.js";
|
|
3
|
+
export interface Fix {
|
|
4
|
+
readonly src: string;
|
|
5
|
+
readonly from: number;
|
|
6
|
+
readonly to: number;
|
|
7
|
+
readonly text: string;
|
|
8
|
+
readonly expected: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CliDiagnostic {
|
|
11
|
+
readonly code: string;
|
|
12
|
+
readonly severity: "error" | "warning" | "info";
|
|
13
|
+
readonly message: string;
|
|
14
|
+
readonly at: Span | null;
|
|
15
|
+
readonly help: string;
|
|
16
|
+
readonly fix: Fix | null;
|
|
17
|
+
}
|
|
18
|
+
export interface Project {
|
|
19
|
+
readonly directory: string;
|
|
20
|
+
readonly files: readonly SourceFile[];
|
|
21
|
+
readonly ir: IR.WorldIR | null;
|
|
22
|
+
readonly diagnostics: readonly CliDiagnostic[];
|
|
23
|
+
}
|
|
24
|
+
export declare const loadProject: (input: string) => Promise<Project>;
|
|
25
|
+
/** Compile in-memory sources as a project rooted at `directory` (used by mutation runs). */
|
|
26
|
+
export declare const compileProject: (directory: string, files: readonly SourceFile[]) => Project;
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve, relative, sep } from "node:path";
|
|
3
|
+
import { compileWorld } from "./_internal/forma/world/index.js";
|
|
4
|
+
import { decodeCompiled, link } from "./_internal/reconciler/index.js";
|
|
5
|
+
import { authoringDiagnostics } from "./diagnostics.js";
|
|
6
|
+
const ignored = new Set(["node_modules", "dist", "build", "coverage"]);
|
|
7
|
+
const discover = async (directory) => {
|
|
8
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
9
|
+
const lists = await Promise.all(entries
|
|
10
|
+
.filter((e) => !e.name.startsWith(".") && !ignored.has(e.name))
|
|
11
|
+
.map(async (e) => {
|
|
12
|
+
const path = resolve(directory, e.name);
|
|
13
|
+
return e.isDirectory()
|
|
14
|
+
? discover(path)
|
|
15
|
+
: e.isFile() && e.name.endsWith(".forma")
|
|
16
|
+
? [path]
|
|
17
|
+
: [];
|
|
18
|
+
}));
|
|
19
|
+
return lists.flat().sort();
|
|
20
|
+
};
|
|
21
|
+
const error = (code, message, help, at = null) => ({ code, message, help, at, severity: "error", fix: null });
|
|
22
|
+
export const loadProject = async (input) => {
|
|
23
|
+
const directory = resolve(input);
|
|
24
|
+
let files;
|
|
25
|
+
try {
|
|
26
|
+
const paths = await discover(directory);
|
|
27
|
+
files = await Promise.all(paths.map(async (path) => ({
|
|
28
|
+
id: relative(directory, path).split(sep).join("/"),
|
|
29
|
+
text: await readFile(path, "utf8"),
|
|
30
|
+
})));
|
|
31
|
+
}
|
|
32
|
+
catch (cause) {
|
|
33
|
+
return {
|
|
34
|
+
directory,
|
|
35
|
+
files: [],
|
|
36
|
+
ir: null,
|
|
37
|
+
diagnostics: [
|
|
38
|
+
error("E_PROJECT_IO", String(cause), "Pass a readable directory containing .forma source files."),
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!files.length)
|
|
43
|
+
return {
|
|
44
|
+
directory,
|
|
45
|
+
files,
|
|
46
|
+
ir: null,
|
|
47
|
+
diagnostics: [
|
|
48
|
+
error("E_NO_SOURCES", "No .forma source files found.", "Point at the directory containing the world's library, account and examples."),
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
return compileProject(directory, files);
|
|
52
|
+
};
|
|
53
|
+
/** Compile in-memory sources as a project rooted at `directory` (used by mutation runs). */
|
|
54
|
+
export const compileProject = (directory, files) => {
|
|
55
|
+
try {
|
|
56
|
+
const compiled = compileWorld(files);
|
|
57
|
+
const ir = compiled.ok ? decodeCompiled(compiled).ir : null;
|
|
58
|
+
const diagnostics = authoringDiagnostics(compiled, files, ir);
|
|
59
|
+
if (ir)
|
|
60
|
+
for (const account of ir.accounts) {
|
|
61
|
+
try {
|
|
62
|
+
link(ir, account.name);
|
|
63
|
+
}
|
|
64
|
+
catch (cause) {
|
|
65
|
+
diagnostics.push(error("E_LINK", String(cause), "Bind the roles and library release required by this account, then rerun check.", account.at));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { directory, files, ir, diagnostics };
|
|
69
|
+
}
|
|
70
|
+
catch (cause) {
|
|
71
|
+
return {
|
|
72
|
+
directory,
|
|
73
|
+
files,
|
|
74
|
+
ir: null,
|
|
75
|
+
diagnostics: [
|
|
76
|
+
error("E_CHECK_THROW", String(cause), "Reduce this compiler failure to the smallest source example and report it."),
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
};
|
package/dist/refine.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Span, type WorldIR } from "./_internal/forma/world/index.js";
|
|
2
|
+
import type { Fix } from "./project.js";
|
|
3
|
+
export interface SourceText {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly text: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Refinement {
|
|
8
|
+
readonly help?: string;
|
|
9
|
+
readonly fix?: Fix;
|
|
10
|
+
}
|
|
11
|
+
interface Input {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
readonly message: string;
|
|
14
|
+
readonly at: Span | null;
|
|
15
|
+
}
|
|
16
|
+
/** Contextual help and mechanical fixes for diagnostics whose compiler help is generic. */
|
|
17
|
+
export declare const refine: (d: Input, files: readonly SourceText[], ir: WorldIR | null) => Refinement | undefined;
|
|
18
|
+
export {};
|
package/dist/refine.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { KERNEL_FUNCTIONS, OLD_OPERATORS } from "./_internal/forma/world/index.js";
|
|
2
|
+
import { closest } from "./catalogue.js";
|
|
3
|
+
const textAt = (files, at) => at ? files.find((f) => f.id === at.src)?.text.slice(at.from, at.to) : undefined;
|
|
4
|
+
/** Replace `expected` at an absolute offset in `src`. */
|
|
5
|
+
const edit = (src, from, expected, text) => ({
|
|
6
|
+
src,
|
|
7
|
+
from,
|
|
8
|
+
to: from + expected.length,
|
|
9
|
+
text,
|
|
10
|
+
expected,
|
|
11
|
+
});
|
|
12
|
+
/** The head symbol of a list span and its absolute offset. */
|
|
13
|
+
const headOf = (text, at) => {
|
|
14
|
+
if (!text || !at)
|
|
15
|
+
return undefined;
|
|
16
|
+
const m = /^\(\s*([^\s()[\]{}]+)/.exec(text);
|
|
17
|
+
return m ? { name: m[1], from: at.from + m[0].length - m[1].length } : undefined;
|
|
18
|
+
};
|
|
19
|
+
/** Index just past the list that opens at `open` (text[open] === "("). */
|
|
20
|
+
const closeOf = (text, open) => {
|
|
21
|
+
let depth = 0, inString = false;
|
|
22
|
+
for (let i = open; i < text.length; i++) {
|
|
23
|
+
const c = text[i];
|
|
24
|
+
if (inString) {
|
|
25
|
+
if (c === "\\")
|
|
26
|
+
i++;
|
|
27
|
+
else if (c === '"')
|
|
28
|
+
inString = false;
|
|
29
|
+
}
|
|
30
|
+
else if (c === '"')
|
|
31
|
+
inString = true;
|
|
32
|
+
else if (c === ";")
|
|
33
|
+
while (i < text.length && text[i] !== "\n")
|
|
34
|
+
i++;
|
|
35
|
+
else if (c === "(" || c === "[" || c === "{")
|
|
36
|
+
depth++;
|
|
37
|
+
else if (c === ")" || c === "]" || c === "}") {
|
|
38
|
+
depth--;
|
|
39
|
+
if (depth === 0)
|
|
40
|
+
return i + 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
};
|
|
45
|
+
/** The list whose head is the symbol at `at`, when a diagnostic points at an operator. */
|
|
46
|
+
const enclosing = (files, at) => {
|
|
47
|
+
const file = at && files.find((f) => f.id === at.src);
|
|
48
|
+
if (!file || !at)
|
|
49
|
+
return undefined;
|
|
50
|
+
let open = at.from - 1;
|
|
51
|
+
while (open >= 0 && /\s/.test(file.text[open]))
|
|
52
|
+
open--;
|
|
53
|
+
if (file.text[open] !== "(")
|
|
54
|
+
return undefined;
|
|
55
|
+
const close = closeOf(file.text, open);
|
|
56
|
+
return close === undefined ? undefined : { ...at, from: open, to: close };
|
|
57
|
+
};
|
|
58
|
+
/** Names declared at top level, for did-you-mean on references. */
|
|
59
|
+
const declaredNames = (files) => {
|
|
60
|
+
const names = new Set();
|
|
61
|
+
for (const f of files)
|
|
62
|
+
for (const m of f.text.matchAll(/^\(\s*[a-z][\w-]*\s+([^\s()[\]{}"]+)/gm))
|
|
63
|
+
names.add(m[1]);
|
|
64
|
+
return [...names];
|
|
65
|
+
};
|
|
66
|
+
const fieldNames = (files) => {
|
|
67
|
+
const names = new Set();
|
|
68
|
+
for (const f of files)
|
|
69
|
+
for (const m of f.text.matchAll(/:([a-z][\w?-]*)\s+(?=[A-Z(])/g))
|
|
70
|
+
names.add(m[1]);
|
|
71
|
+
return [...names];
|
|
72
|
+
};
|
|
73
|
+
const functions = [...KERNEL_FUNCTIONS].map((name) => OLD_OPERATORS[name] ?? name);
|
|
74
|
+
const vocabulary = () => [...functions, "self", "today", "now"];
|
|
75
|
+
const holeType = (d, files) => {
|
|
76
|
+
const text = textAt(files, d.at);
|
|
77
|
+
// Truth/Bool mismatches get help from Forma (authoring-help.ts) and a fix from fixes.ts.
|
|
78
|
+
const unbound = /Unbound variable: (\S+)/.exec(d.message)?.[1];
|
|
79
|
+
if (unbound) {
|
|
80
|
+
const guess = closest(unbound, [...vocabulary(), ...declaredNames(files)]);
|
|
81
|
+
const fix = d.at && text === unbound && guess ? edit(d.at.src, d.at.from, unbound, guess) : undefined;
|
|
82
|
+
return {
|
|
83
|
+
help: `${unbound} is not in scope here.${guess ? ` Did you mean ${guess}?` : ""} Expressions may use the kernel vocabulary, this component's inputs, self, requirement names, and declared evidence/decision names.`,
|
|
84
|
+
...(fix ? { fix } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const missingField = /^\S+ has no field (\S+)(?: |$)/.exec(d.message)?.[1];
|
|
88
|
+
if (missingField && text) {
|
|
89
|
+
const last = missingField;
|
|
90
|
+
const guess = closest(last, fieldNames(files));
|
|
91
|
+
return {
|
|
92
|
+
help: `${text} names a field that the role/entity does not declare.${guess && guess !== last ? ` Did you mean .${guess}?` : ""} Fields come from the role declaration's field map.`,
|
|
93
|
+
...(guess && guess !== last && d.at
|
|
94
|
+
? { fix: edit(d.at.src, d.at.from + text.length - last.length, last, guess) }
|
|
95
|
+
: {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const reference = /Unknown reference (\S+)/.exec(d.message)?.[1];
|
|
99
|
+
if (reference) {
|
|
100
|
+
const guess = closest(reference, declaredNames(files).filter((n) => n !== reference));
|
|
101
|
+
return {
|
|
102
|
+
help: `${reference} is not declared in any .forma file of this world.${guess ? ` Did you mean ${guess}?` : ""} Names are case-sensitive and shared across the directory; if the declaration itself failed to compile, fix that error first.`,
|
|
103
|
+
...(guess && d.at && text === reference
|
|
104
|
+
? { fix: edit(d.at.src, d.at.from, reference, guess) }
|
|
105
|
+
: {}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (/Too many arguments|Too few arguments/.test(d.message))
|
|
109
|
+
return {
|
|
110
|
+
help: "The call has the wrong number of arguments. Comparisons take two (= a b); combine several with (and (= a b) (= c d)).",
|
|
111
|
+
};
|
|
112
|
+
return undefined;
|
|
113
|
+
};
|
|
114
|
+
const lower = (d, files) => {
|
|
115
|
+
const name = /^(\S+) is not a kernel function or a derive\./.exec(d.message)?.[1];
|
|
116
|
+
if (!name)
|
|
117
|
+
return undefined;
|
|
118
|
+
const text = textAt(files, d.at);
|
|
119
|
+
const guess = closest(name, functions);
|
|
120
|
+
const head = headOf(text, d.at);
|
|
121
|
+
return {
|
|
122
|
+
help: `${name} is not a kernel function.${guess ? ` Did you mean ${guess}?` : ""} Declare reusable logic with (derive name "Doc." :params {…} :returns Truth expr).`,
|
|
123
|
+
...(guess && head?.name === name ? { fix: edit(d.at.src, head.from, name, guess) } : {}),
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
const propertyShape = (input, files) => {
|
|
127
|
+
const bare = textAt(files, input.at);
|
|
128
|
+
const list = bare && !bare.startsWith("(") ? enclosing(files, input.at) : undefined;
|
|
129
|
+
const d = list ? { ...input, at: list } : input;
|
|
130
|
+
const text = textAt(files, d.at);
|
|
131
|
+
const head = headOf(text, d.at);
|
|
132
|
+
const operators = "Property operators: implies, and, or, not, =, !=, <, <=, >, >=, known?, unknown?, satisfied?, not-applicable?, (status (Component alias) name), (attested (Component alias) Evidence), work, due, occurrence-count.";
|
|
133
|
+
const op = head?.name ?? text?.trim();
|
|
134
|
+
if (op && ["valid", "decided", "met?"].includes(op))
|
|
135
|
+
return {
|
|
136
|
+
help: `${op} is not available in properties. Read the requirement that uses it instead, e.g. (= (status (Component alias) requirement) :Satisfied). ${operators}`,
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
help: `Unsupported or malformed property expression${op ? ` at ${op}` : ""}. A property is (property name "Doc." :world account :at "YYYY-MM-DD" :for-all (alias Entity) predicate). ${operators}`,
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
const noScenario = (d, ir) => {
|
|
143
|
+
const key = /^(\S+)\/(\S+) has no authored scenario reference/.exec(d.message);
|
|
144
|
+
if (!key || !ir)
|
|
145
|
+
return undefined;
|
|
146
|
+
const [, componentName, requirement] = key;
|
|
147
|
+
const component = ir.components.find((c) => c.name === componentName);
|
|
148
|
+
const input = component?.inputs.find((i) => i.name === component.key[0]);
|
|
149
|
+
const role = Array.isArray(input?.type) && input.type[0] === "Id" ? input.type[1] : undefined;
|
|
150
|
+
const entity = ir.entities.find((e) => e.implements.some((b) => b.role === role));
|
|
151
|
+
const account = ir.accounts.find((a) => a.forEach.some((f) => f.component === componentName)) ?? ir.accounts[0];
|
|
152
|
+
const keys = component?.key.map((_, i) => (i === 0 ? "x" : `k${i}`)).join(" ") ?? "x";
|
|
153
|
+
const skeleton = `(scenario ${requirement}-opens "${requirement} opens for a new subject." :world ${account?.name ?? "<account>"}\n (given (entity ${entity?.name ?? "<Entity>"} x {…}))\n (at "2026-01-01" (expect (status (${componentName} ${keys}) ${requirement}) :Open)))`;
|
|
154
|
+
return {
|
|
155
|
+
help: `Add a scenario that observes ${componentName}/${requirement} (then a step that satisfies it and a second expectation), for example:\n${skeleton}\nworldvm coverage shows which applicability and status branches the scenarios actually take.`,
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
const unreachable = (d, files, ir) => {
|
|
159
|
+
const key = /^(\S+)\/(\S+) has no status consumer/.exec(d.message);
|
|
160
|
+
if (!key || !ir)
|
|
161
|
+
return undefined;
|
|
162
|
+
const [, componentName, requirement] = key;
|
|
163
|
+
const component = ir.components.find((c) => c.name === componentName);
|
|
164
|
+
const outputs = component?.outputs.filter((o) => o.op === "all-of") ?? [];
|
|
165
|
+
const help = `Add ${requirement} to an output so its status counts, e.g. (output complete (all-of … ${requirement})), read it from another expression, or delete it if unintended. This advisory check is not a reachability proof.`;
|
|
166
|
+
if (outputs.length !== 1)
|
|
167
|
+
return { help };
|
|
168
|
+
const output = outputs[0], file = files.find((f) => f.id === output.at.src), text = file?.text.slice(output.at.from, output.at.to);
|
|
169
|
+
const open = text?.search(/\(all-of\b/);
|
|
170
|
+
if (!file || !text || open === undefined || open < 0)
|
|
171
|
+
return { help };
|
|
172
|
+
const close = closeOf(text, open);
|
|
173
|
+
if (close === undefined)
|
|
174
|
+
return { help };
|
|
175
|
+
const at = output.at.from + close - 1;
|
|
176
|
+
return {
|
|
177
|
+
help: `${help} The fix appends it to output ${output.name}.`,
|
|
178
|
+
fix: { src: file.id, from: at, to: at, text: ` ${requirement}`, expected: "" },
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
/** Contextual help and mechanical fixes for diagnostics whose compiler help is generic. */
|
|
182
|
+
export const refine = (d, files, ir) => {
|
|
183
|
+
switch (d.code) {
|
|
184
|
+
case "E_ELABORATE_HOLE_TYPE":
|
|
185
|
+
return holeType(d, files);
|
|
186
|
+
case "E_LOWER":
|
|
187
|
+
return lower(d, files);
|
|
188
|
+
case "E_PROPERTY_SHAPE":
|
|
189
|
+
return propertyShape(d, files);
|
|
190
|
+
case "W_NO_SCENARIO":
|
|
191
|
+
return noScenario(d, ir);
|
|
192
|
+
case "W_UNREACHABLE_REQUIREMENT":
|
|
193
|
+
return unreachable(d, files, ir);
|
|
194
|
+
default:
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Span, WorldIR } from "../_internal/forma/world/index.js";
|
|
2
|
+
export type ChangeKind = "added" | "removed" | "changed";
|
|
3
|
+
/** What a requirement change means for the people it governs. */
|
|
4
|
+
export type Aspect = "applicability" | "satisfaction" | "due" | "timing" | "assignment" | "citations" | "presentation"
|
|
5
|
+
/** A release or contract digest: identity of the compiled unit, not an obligation. */
|
|
6
|
+
| "identity" | "other";
|
|
7
|
+
export interface FieldChange {
|
|
8
|
+
readonly field: string;
|
|
9
|
+
readonly aspect: Aspect;
|
|
10
|
+
readonly old: Span | null;
|
|
11
|
+
readonly new: Span | null;
|
|
12
|
+
}
|
|
13
|
+
export interface DeclarationChange {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly kind: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly change: ChangeKind;
|
|
18
|
+
/** Only documentation, labels or titles changed; evaluation is identical. */
|
|
19
|
+
readonly presentationOnly: boolean;
|
|
20
|
+
/** A public component contract was removed or its digest changed. */
|
|
21
|
+
readonly breaking: boolean;
|
|
22
|
+
readonly old: Span | null;
|
|
23
|
+
readonly new: Span | null;
|
|
24
|
+
readonly fields: readonly FieldChange[];
|
|
25
|
+
}
|
|
26
|
+
export interface WorldDiff {
|
|
27
|
+
readonly format: "worldvm/diff@1";
|
|
28
|
+
readonly changes: readonly DeclarationChange[];
|
|
29
|
+
readonly examples: readonly DeclarationChange[];
|
|
30
|
+
readonly summary: {
|
|
31
|
+
readonly added: number;
|
|
32
|
+
readonly removed: number;
|
|
33
|
+
readonly changed: number;
|
|
34
|
+
readonly presentationOnly: number;
|
|
35
|
+
readonly breaking: number;
|
|
36
|
+
readonly requirements: {
|
|
37
|
+
readonly added: number;
|
|
38
|
+
readonly removed: number;
|
|
39
|
+
readonly changed: number;
|
|
40
|
+
};
|
|
41
|
+
readonly aspects: Readonly<Record<Aspect, number>>;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Semantic diff of two compiled releases: declarations, not text. Spans never count as change. */
|
|
45
|
+
export declare const diffWorlds: (before: WorldIR, after: WorldIR) => WorldDiff;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { behaviorDigest, exampleNodes, nodeDigest, releaseNodes, semanticJson, } from "./nodes.js";
|
|
2
|
+
const aspects = {
|
|
3
|
+
appliesWhen: "applicability",
|
|
4
|
+
presentWhen: "applicability",
|
|
5
|
+
effective: "applicability",
|
|
6
|
+
satisfiedBy: "satisfaction",
|
|
7
|
+
accepts: "satisfaction",
|
|
8
|
+
reuse: "satisfaction",
|
|
9
|
+
due: "due",
|
|
10
|
+
remind: "timing",
|
|
11
|
+
recur: "timing",
|
|
12
|
+
renewWithin: "timing",
|
|
13
|
+
escalate: "timing",
|
|
14
|
+
assignee: "assignment",
|
|
15
|
+
assigneeKind: "assignment",
|
|
16
|
+
by: "assignment",
|
|
17
|
+
approvals: "assignment",
|
|
18
|
+
distinctFrom: "assignment",
|
|
19
|
+
cites: "citations",
|
|
20
|
+
doc: "presentation",
|
|
21
|
+
label: "presentation",
|
|
22
|
+
title: "presentation",
|
|
23
|
+
digest: "identity",
|
|
24
|
+
};
|
|
25
|
+
const spanOf = (value) => {
|
|
26
|
+
const at = value?.at;
|
|
27
|
+
return at && typeof at === "object" && typeof at.src === "string" ? at : null;
|
|
28
|
+
};
|
|
29
|
+
const fieldChanges = (before, after) => {
|
|
30
|
+
const a = before.value, b = after.value;
|
|
31
|
+
return [...new Set([...Object.keys(a), ...Object.keys(b)])]
|
|
32
|
+
.filter((k) => k !== "at" && semanticJson(a[k]) !== semanticJson(b[k]))
|
|
33
|
+
.sort()
|
|
34
|
+
.map((field) => ({
|
|
35
|
+
field,
|
|
36
|
+
aspect: aspects[field] ?? "other",
|
|
37
|
+
old: spanOf(a[field]),
|
|
38
|
+
new: spanOf(b[field]),
|
|
39
|
+
}));
|
|
40
|
+
};
|
|
41
|
+
const compare = (before, after) => {
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const key of [...new Set([...before.keys(), ...after.keys()])].sort()) {
|
|
44
|
+
const a = before.get(key), b = after.get(key);
|
|
45
|
+
const node = (b ?? a);
|
|
46
|
+
if (a && b && nodeDigest(a) === nodeDigest(b))
|
|
47
|
+
continue;
|
|
48
|
+
const change = !a ? "added" : !b ? "removed" : "changed";
|
|
49
|
+
const presentationOnly = !!a && !!b && behaviorDigest(a) === behaviorDigest(b);
|
|
50
|
+
const breaking = node.kind === "contract" &&
|
|
51
|
+
(change === "removed" ||
|
|
52
|
+
(change === "changed" &&
|
|
53
|
+
a.value.digest !== b.value.digest));
|
|
54
|
+
out.push({
|
|
55
|
+
key,
|
|
56
|
+
kind: node.kind,
|
|
57
|
+
name: node.name,
|
|
58
|
+
change,
|
|
59
|
+
presentationOnly,
|
|
60
|
+
breaking,
|
|
61
|
+
old: a?.at ?? null,
|
|
62
|
+
new: b?.at ?? null,
|
|
63
|
+
fields: a && b ? fieldChanges(a, b) : [],
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
};
|
|
68
|
+
/** Semantic diff of two compiled releases: declarations, not text. Spans never count as change. */
|
|
69
|
+
export const diffWorlds = (before, after) => {
|
|
70
|
+
const changes = compare(releaseNodes(before), releaseNodes(after));
|
|
71
|
+
const examples = compare(exampleNodes(before), exampleNodes(after));
|
|
72
|
+
const count = (kind, list = changes) => list.filter((c) => c.change === kind).length;
|
|
73
|
+
const requirements = changes.filter((c) => c.kind === "requirement");
|
|
74
|
+
const tally = Object.fromEntries([
|
|
75
|
+
"applicability",
|
|
76
|
+
"satisfaction",
|
|
77
|
+
"due",
|
|
78
|
+
"timing",
|
|
79
|
+
"assignment",
|
|
80
|
+
"citations",
|
|
81
|
+
"presentation",
|
|
82
|
+
"identity",
|
|
83
|
+
"other",
|
|
84
|
+
].map((a) => [a, requirements.filter((c) => c.fields.some((f) => f.aspect === a)).length]));
|
|
85
|
+
return {
|
|
86
|
+
format: "worldvm/diff@1",
|
|
87
|
+
changes,
|
|
88
|
+
examples,
|
|
89
|
+
summary: {
|
|
90
|
+
added: count("added"),
|
|
91
|
+
removed: count("removed"),
|
|
92
|
+
changed: count("changed"),
|
|
93
|
+
presentationOnly: changes.filter((c) => c.presentationOnly).length,
|
|
94
|
+
breaking: changes.filter((c) => c.breaking).length,
|
|
95
|
+
requirements: {
|
|
96
|
+
added: count("added", requirements),
|
|
97
|
+
removed: count("removed", requirements),
|
|
98
|
+
changed: count("changed", requirements),
|
|
99
|
+
},
|
|
100
|
+
aspects: tally,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|