@intentius/chant 0.101.0 → 0.102.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/dist/agents/checks.d.ts.map +1 -1
- package/dist/attrref.d.ts.map +1 -1
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/nginx.d.ts.map +1 -1
- package/dist/audit/prior-art.d.ts +48 -0
- package/dist/audit/prior-art.d.ts.map +1 -1
- package/dist/behaviour-kinds.d.ts.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +16 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts +8 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +9 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +9 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/lsp/server.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/build.d.ts.map +1 -1
- package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/tools/import.d.ts.map +1 -1
- package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
- package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
- package/dist/cli/mcp/tools/search.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +11 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/naming.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts +67 -3
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/process-runner.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts.map +1 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +19 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/declarable.d.ts +16 -0
- package/dist/declarable.d.ts.map +1 -1
- package/dist/discovery/cache.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +16 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.d.ts +37 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +3 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-layout.d.ts.map +1 -1
- package/dist/import/base-parser.d.ts.map +1 -1
- package/dist/import/embedded.d.ts +7 -0
- package/dist/import/embedded.d.ts.map +1 -1
- package/dist/import/parser-options.d.ts +14 -0
- package/dist/import/parser-options.d.ts.map +1 -0
- package/dist/import/parser.d.ts +17 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts.map +1 -1
- package/dist/lexicon-output.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +3 -3
- package/dist/lexicon.d.ts +55 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/assert-live.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +15 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/work-lease.d.ts +61 -2
- package/dist/lifecycle/work-lease.d.ts.map +1 -1
- package/dist/lint/config.d.ts +8 -8
- package/dist/lint/engine.d.ts +6 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +11 -11
- package/dist/op/activities/apply.d.ts +79 -9
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
- package/dist/op/activity-contract.d.ts +1 -1
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +12 -5
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts.map +1 -1
- package/dist/op/decide-read-inputs.d.ts.map +1 -1
- package/dist/op/emulator-lifecycle.d.ts +21 -2
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts +5 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-wait.d.ts.map +1 -1
- package/dist/op/gate.d.ts +19 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -0
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +6 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/run-context.d.ts +39 -0
- package/dist/op/run-context.d.ts.map +1 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts.map +1 -1
- package/dist/op/steward-beside.d.ts.map +1 -1
- package/dist/op/steward-points.d.ts.map +1 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +8 -3
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +24 -4
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/pseudo-parameter.d.ts.map +1 -1
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts.map +1 -1
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
- package/dist/workspace/agent-cli.d.ts +5 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/box-factory.d.ts +73 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +5 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +10 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts +67 -6
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +12 -3
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +368 -0
- package/dist/workspace/conformance/writer.d.ts.map +1 -0
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +91 -4
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +234 -5
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/identity.d.ts +111 -0
- package/dist/workspace/identity.d.ts.map +1 -0
- package/dist/workspace/intent-cli.d.ts +7 -4
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-record.d.ts +8 -1
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +149 -5
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/kinds.d.ts +18 -4
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +2 -2
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/principal-classes.d.ts +114 -0
- package/dist/workspace/principal-classes.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +21 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/records-checkout.d.ts +54 -0
- package/dist/workspace/records-checkout.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +27 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-since.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +5 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +15 -0
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +164 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -0
- package/dist/workspace/runs.d.ts +423 -0
- package/dist/workspace/runs.d.ts.map +1 -0
- package/dist/workspace/source-block.d.ts +1 -1
- package/dist/workspace/source-block.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +15 -3
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +10 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +52 -0
- package/dist/workspace/symbols.d.ts.map +1 -0
- package/dist/workspace/trailer-joins.d.ts +46 -0
- package/dist/workspace/trailer-joins.d.ts.map +1 -0
- package/dist/workspace/trailers.d.ts +89 -0
- package/dist/workspace/trailers.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/seal.d.ts +33 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +39 -3
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work.d.ts +26 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/dist/workspace/workspace-principals.schema.json +60 -0
- package/dist/workspace/write-scope.d.ts +79 -19
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/yaml.d.ts +21 -39
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/audit/prior-art.ts +15 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/carve-emit-state.test.ts +4 -0
- package/src/cli/commands/carve-emit.ts +5 -19
- package/src/cli/commands/check-lexicon.test.ts +62 -1
- package/src/cli/commands/check-lexicon.ts +36 -6
- package/src/cli/commands/import-live.test.ts +18 -0
- package/src/cli/commands/import-parser-options.test.ts +133 -0
- package/src/cli/commands/import.ts +21 -3
- package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
- package/src/cli/commands/init.test.ts +8 -1
- package/src/cli/commands/init.ts +23 -8
- package/src/cli/commands/lint.test.ts +37 -0
- package/src/cli/commands/onboard.test.ts +53 -3
- package/src/cli/commands/onboard.ts +52 -4
- package/src/cli/commands/pinned-upgrade.ts +73 -33
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/dev.ts +8 -6
- package/src/cli/handlers/lifecycle.test.ts +67 -0
- package/src/cli/handlers/lifecycle.ts +6 -5
- package/src/cli/handlers/misc.ts +2 -0
- package/src/cli/handlers/operator.ts +87 -3
- package/src/cli/handlers/run.test.ts +111 -0
- package/src/cli/handlers/run.ts +85 -24
- package/src/cli/handlers/scenario.ts +2 -2
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +65 -11
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +29 -2
- package/src/cli/registry.ts +11 -2
- package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
- package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
- package/src/codegen/pinned-upgrade.test.ts +104 -0
- package/src/codegen/pinned-upgrade.ts +250 -56
- package/src/codegen/testdata/postgres-tags.json +242 -0
- package/src/config.test.ts +21 -0
- package/src/config.ts +15 -0
- package/src/declarable.test.ts +21 -0
- package/src/declarable.ts +19 -0
- package/src/discovery/fold-composite.test.ts +41 -5
- package/src/discovery/fold-import.test.ts +129 -0
- package/src/discovery/fold-import.ts +643 -131
- package/src/discovery/fold-package-composite.test.ts +264 -0
- package/src/discovery/fold-specifiers.test.ts +191 -0
- package/src/discovery/param-deps.test.ts +66 -1
- package/src/discovery/param-deps.ts +45 -0
- package/src/discovery/specifier-resolve.ts +344 -0
- package/src/fold/fold.test.ts +39 -1
- package/src/fold/fold.ts +83 -9
- package/src/fold-provenance.test.ts +140 -0
- package/src/graph-ir.test.ts +83 -0
- package/src/graph-ir.ts +29 -0
- package/src/import/embedded.test.ts +20 -0
- package/src/import/embedded.ts +13 -0
- package/src/import/parser-options.ts +46 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +55 -3
- package/src/lifecycle/gate-ledger.ts +16 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +6 -4
- package/src/lifecycle/snapshot.ts +2 -2
- package/src/lifecycle/work-lease.test.ts +1 -1
- package/src/lifecycle/work-lease.ts +93 -2
- package/src/lint/engine.test.ts +55 -0
- package/src/lint/engine.ts +30 -4
- package/src/lint/output-docs.test.ts +59 -56
- package/src/op/activities/apply.test.ts +189 -5
- package/src/op/activities/apply.ts +230 -8
- package/src/op/activities/index.ts +3 -0
- package/src/op/activities/lexicon-upgrade.test.ts +160 -6
- package/src/op/activities/lexicon-upgrade.ts +154 -51
- package/src/op/composites/apply-op.ts +12 -5
- package/src/op/composites/composites.test.ts +30 -3
- package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
- package/src/op/composites/lexicon-upgrade-op.ts +29 -8
- package/src/op/emulator-lifecycle.test.ts +23 -0
- package/src/op/emulator-lifecycle.ts +52 -14
- package/src/op/gate-approval.test.ts +74 -0
- package/src/op/gate-approval.ts +5 -1
- package/src/op/gate.ts +31 -5
- package/src/op/index.ts +2 -0
- package/src/op/local-executor.test.ts +15 -0
- package/src/op/local-executor.ts +40 -12
- package/src/op/local-output.ts +1 -1
- package/src/op/operator.ts +4 -0
- package/src/op/run-context.test.ts +141 -0
- package/src/op/run-context.ts +51 -0
- package/src/op/runtimes/local.test.ts +14 -0
- package/src/op/runtimes/local.ts +2 -0
- package/src/op/steward-beside.ts +2 -0
- package/src/op/types.ts +8 -3
- package/src/op/work-lease-run.test.ts +47 -0
- package/src/op/work-lease-run.ts +73 -11
- package/src/provenance.ts +35 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/agent-cli.ts +7 -3
- package/src/workspace/agent.schema.json +22 -0
- package/src/workspace/box-factory.test.ts +320 -0
- package/src/workspace/box-factory.ts +103 -0
- package/src/workspace/changes.schema.json +12 -20
- package/src/workspace/changes.ts +4 -1
- package/src/workspace/checks/boxes.test.ts +2 -0
- package/src/workspace/checks.ts +29 -5
- package/src/workspace/compose-graph.ts +7 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
- package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
- package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
- package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
- package/src/workspace/conformance/index.mjs +30 -3
- package/src/workspace/conformance/index.ts +103 -15
- package/src/workspace/conformance/vitest.ts +44 -2
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
- package/src/workspace/conformance/writer.test.ts +156 -0
- package/src/workspace/conformance/writer.ts +880 -0
- package/src/workspace/decide.ts +7 -1
- package/src/workspace/declaration.schema.json +234 -5
- package/src/workspace/declaration.ts +174 -10
- package/src/workspace/graph-cli.ts +5 -2
- package/src/workspace/graph.schema.json +247 -0
- package/src/workspace/identity.test.ts +196 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/intent-cli.ts +64 -10
- package/src/workspace/intent-record.schema.json +208 -0
- package/src/workspace/intent-record.ts +35 -1
- package/src/workspace/intent-why.test.ts +274 -0
- package/src/workspace/intent.schema.json +718 -7
- package/src/workspace/intent.test.ts +11 -2
- package/src/workspace/intent.ts +495 -32
- package/src/workspace/kinds.ts +44 -37
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/reason-codes.test.ts +11 -3
- package/src/workspace/reason-codes.ts +24 -1
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +103 -11
- package/src/workspace/records-close.schema.json +1 -0
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.ts +28 -6
- package/src/workspace/records.schema.json +72 -1
- package/src/workspace/records.ts +27 -0
- package/src/workspace/runs-cli.ts +381 -0
- package/src/workspace/runs-write.schema.json +583 -0
- package/src/workspace/runs.schema.json +810 -0
- package/src/workspace/runs.test.ts +242 -0
- package/src/workspace/runs.ts +709 -0
- package/src/workspace/status-contract.test.ts +5 -0
- package/src/workspace/status-gates.ts +22 -8
- package/src/workspace/status.schema.json +127 -2
- package/src/workspace/status.ts +21 -1
- package/src/workspace/symbols.ts +180 -0
- package/src/workspace/trailer-joins.test.ts +171 -0
- package/src/workspace/trailer-joins.ts +82 -0
- package/src/workspace/trailers.ts +148 -0
- package/src/workspace/trust/seal.ts +52 -1
- package/src/workspace/work-cli.e2e.test.ts +28 -1
- package/src/workspace/work-cli.ts +133 -8
- package/src/workspace/work-evidence.schema.json +2 -0
- package/src/workspace/work-history.schema.json +73 -1
- package/src/workspace/work-history.test.ts +48 -5
- package/src/workspace/work-kind-fields.test.ts +154 -0
- package/src/workspace/work-lease.schema.json +14 -1
- package/src/workspace/work.ts +82 -0
- package/src/workspace/workspace-principals.schema.json +60 -0
- package/src/workspace/write-scope.test.ts +128 -2
- package/src/workspace/write-scope.ts +170 -45
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
package/src/yaml.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* YAML emitter and parser.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* keys
|
|
7
|
-
*
|
|
8
|
-
* parser throws a `YAMLParseError`
|
|
9
|
-
* dropping it (#2991).
|
|
4
|
+
* The emitter writes the block-style YAML chant's serializers produce. The
|
|
5
|
+
* parser is js-yaml with chant's schema (#3006): YAML 1.2 core types,
|
|
6
|
+
* merge keys, `yes`/`no` booleans, and local tags such as `!reference` read
|
|
7
|
+
* as strings. `splitYAMLDocuments` splits a multi-document stream. Input the
|
|
8
|
+
* parser cannot read throws a `YAMLParseError` naming the line.
|
|
10
9
|
*/
|
|
11
10
|
|
|
11
|
+
import yaml from "js-yaml";
|
|
12
|
+
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Emitter
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
@@ -79,7 +80,13 @@ export function emitYAML(value: unknown, indent: number): string {
|
|
|
79
80
|
value.startsWith("'") ||
|
|
80
81
|
value.startsWith('"') ||
|
|
81
82
|
value.startsWith("$") ||
|
|
82
|
-
/^\d/.test(value)
|
|
83
|
+
/^\d/.test(value) ||
|
|
84
|
+
// What a YAML parser reads as something other than this string: an
|
|
85
|
+
// indicator that cannot start a plain scalar, a trailing colon, edge
|
|
86
|
+
// whitespace, a signed or dotted number, and the other spellings of
|
|
87
|
+
// booleans, null and the float constants (#3006).
|
|
88
|
+
/^[-?:](?:\s|$)|^[,\]}|>%@`]|:$|^\s|\s$|^[-+.]\d/.test(value) ||
|
|
89
|
+
/^(?:true|false|null|~|yes|no|[-+]?\.(?:inf|nan))$/i.test(value)
|
|
83
90
|
) {
|
|
84
91
|
// Use single quotes, escaping internal single quotes
|
|
85
92
|
return `'${value.replace(/'/g, "''")}'`;
|
|
@@ -147,21 +154,24 @@ export function emitYAML(value: unknown, indent: number): string {
|
|
|
147
154
|
return String(value);
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
/**
|
|
158
|
+
* `key: value` at column `indent * 2`, for a serializer that writes a
|
|
159
|
+
* document key by key. A block value follows the colon on the next line; an
|
|
160
|
+
* inline one (a scalar, `{}`, `[]`) needs a space after the colon, or
|
|
161
|
+
* `jobs:{}` is the plain scalar "jobs:{}" rather than a key (#3006).
|
|
162
|
+
*/
|
|
163
|
+
export function emitYAMLEntry(key: string, value: unknown, indent = 0): string {
|
|
164
|
+
const emitted = emitYAML(value, indent + 1);
|
|
165
|
+
return `${" ".repeat(indent)}${key}:${emitted.startsWith("\n") ? "" : " "}${emitted}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
150
168
|
// ---------------------------------------------------------------------------
|
|
151
169
|
// Parser
|
|
152
170
|
// ---------------------------------------------------------------------------
|
|
153
171
|
|
|
154
|
-
/** Result of parsing a YAML block. */
|
|
155
|
-
export interface ParseResult {
|
|
156
|
-
value: unknown;
|
|
157
|
-
endIndex: number;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
172
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* mis-indented or unsupported construct came back as a quietly different
|
|
164
|
-
* document. Now it is an error naming the line.
|
|
173
|
+
* A document the parser cannot read. Its message and `line` name the line,
|
|
174
|
+
* so `chant import` can report "YAML line N: ..." instead of a stack trace.
|
|
165
175
|
*/
|
|
166
176
|
export class YAMLParseError extends Error {
|
|
167
177
|
/** 1-based line number within the text handed to the parser. */
|
|
@@ -174,157 +184,141 @@ export class YAMLParseError extends Error {
|
|
|
174
184
|
}
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
/**
|
|
178
|
-
|
|
179
|
-
* colon, or a plain run of text up to the first colon.
|
|
180
|
-
*/
|
|
181
|
-
const KEY = String.raw`("(?:[^"\\]|\\.)*"|'(?:[^']|'')*'|[^\s:"'][^:]*?)`;
|
|
187
|
+
/** A document start (`---`) or end (`...`) marker, optionally followed by a comment. */
|
|
188
|
+
const DOCUMENT_MARKER = /^(?:---|\.\.\.)(?:[ \t]+#.*)?[ \t]*$/;
|
|
182
189
|
|
|
183
190
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* The lookahead is the rule both patterns used to miss: a `:` opens a mapping
|
|
188
|
-
* only when what follows it is whitespace or the end of the line, and a plain
|
|
189
|
-
* scalar may carry any other colon (YAML 1.2 §7.4.2). Written as `:\s*`, which
|
|
190
|
-
* matches zero whitespace, EVERY colon opened one — so a bare URL in a
|
|
191
|
-
* sequence,
|
|
192
|
-
*
|
|
193
|
-
* sourceRepos:
|
|
194
|
-
* - https://github.com/INTENTIUS/behold
|
|
195
|
-
*
|
|
196
|
-
* came back as `{ https: "//github.com/INTENTIUS/behold" }`, and the CRD-schema
|
|
197
|
-
* post-synth check (#1372) re-reading emitted YAML reported the string list
|
|
198
|
-
* this file's own emitter had just written as a list of objects (#2013). By the
|
|
199
|
-
* same rule `- key:value` is the scalar `"key:value"`, not a mapping.
|
|
191
|
+
* Booleans as YAML 1.2's core schema reads them, plus `yes` and `no`, which
|
|
192
|
+
* this parser has always read as booleans and its callers expect.
|
|
200
193
|
*/
|
|
201
|
-
const
|
|
202
|
-
|
|
194
|
+
const BOOL = new yaml.Type("tag:yaml.org,2002:bool", {
|
|
195
|
+
kind: "scalar",
|
|
196
|
+
resolve: (data: string | null) => data !== null && /^(?:true|True|TRUE|false|False|FALSE|yes|no)$/.test(data),
|
|
197
|
+
construct: (data: string) => /^(?:true|True|TRUE|yes)$/.test(data),
|
|
198
|
+
predicate: (value: unknown) => typeof value === "boolean",
|
|
199
|
+
represent: { lowercase: (value: object) => (value ? "true" : "false") },
|
|
200
|
+
defaultStyle: "lowercase",
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
/** A tagged value written back as text, the way it appeared: `!reference [.base, script]`. */
|
|
204
|
+
function flowText(value: unknown): string {
|
|
205
|
+
if (Array.isArray(value)) return `[${value.map(flowText).join(", ")}]`;
|
|
206
|
+
if (typeof value === "object" && value !== null) {
|
|
207
|
+
return `{${Object.entries(value).map(([k, v]) => `${k}: ${flowText(v)}`).join(", ")}}`;
|
|
208
|
+
}
|
|
209
|
+
return String(value);
|
|
210
|
+
}
|
|
203
211
|
|
|
204
212
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
213
|
+
* Local tags (`!reference`, `!Ref`, `!GetAtt`) on a scalar or a sequence read
|
|
214
|
+
* as the string `"<tag> <value>"`, as they always have: GitLab CI's
|
|
215
|
+
* `!reference [.base, script]` is the string `"!reference [.base, script]"`.
|
|
216
|
+
* A tag on a mapping has no such form and is an error.
|
|
207
217
|
*/
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function indentOf(line: string): number {
|
|
226
|
-
return line.search(/\S/);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function isSeqEntry(line: string): boolean {
|
|
230
|
-
return SEQ_ENTRY.test(line);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** A key as written, with the quotes and escapes of a quoted key removed. */
|
|
234
|
-
function keyName(raw: string): string {
|
|
235
|
-
const key = raw.trim();
|
|
236
|
-
return key.startsWith('"') || key.startsWith("'") ? String(parseScalar(key)) : key;
|
|
237
|
-
}
|
|
218
|
+
const TAGGED_SCALAR = new yaml.Type("!", {
|
|
219
|
+
kind: "scalar",
|
|
220
|
+
multi: true,
|
|
221
|
+
construct: (data: string | null, tag?: string) => (data === null || data === "" ? tag : `${tag} ${data}`),
|
|
222
|
+
});
|
|
223
|
+
const TAGGED_SEQUENCE = new yaml.Type("!", {
|
|
224
|
+
kind: "sequence",
|
|
225
|
+
multi: true,
|
|
226
|
+
construct: (data: unknown[] | null, tag?: string) => `${tag} ${flowText(data ?? [])}`,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
/** The merge key `<<`, as js-yaml's own merge type reads it (its typings omit `types`). */
|
|
230
|
+
const MERGE = new yaml.Type("tag:yaml.org,2002:merge", {
|
|
231
|
+
kind: "scalar",
|
|
232
|
+
resolve: (data: string | null) => data === "<<" || data === null,
|
|
233
|
+
});
|
|
238
234
|
|
|
239
235
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* `---`-led file (a compose file, a GitLab CI file) by skipping the marker;
|
|
243
|
-
* that is kept, now that a line the parser cannot place is an error.
|
|
236
|
+
* YAML 1.2's core schema (no timestamps, so a date stays a string) with merge
|
|
237
|
+
* keys, `yes`/`no` booleans and local tags read as strings.
|
|
244
238
|
*/
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
.map((line) => (DOCUMENT_MARKER.test(line) ? "" : line));
|
|
250
|
-
}
|
|
239
|
+
const SCHEMA = yaml.CORE_SCHEMA.extend({
|
|
240
|
+
implicit: [BOOL, MERGE],
|
|
241
|
+
explicit: [TAGGED_SCALAR, TAGGED_SEQUENCE],
|
|
242
|
+
});
|
|
251
243
|
|
|
252
244
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* one that reaches the top is unplaceable, and throws.
|
|
245
|
+
* Give every alias its own copy of the anchored value. js-yaml shares one
|
|
246
|
+
* object between an anchor and its aliases, so a caller editing the alias
|
|
247
|
+
* would edit the anchor too.
|
|
257
248
|
*/
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
const rest = nextContentLine(lines, result.endIndex);
|
|
268
|
-
if (rest < lines.length) {
|
|
269
|
-
throw new YAMLParseError(
|
|
270
|
-
rest + 1,
|
|
271
|
-
`cannot place ${JSON.stringify(lines[rest].trim())} at column ${indentOf(lines[rest]) + 1}; ` +
|
|
272
|
-
"check its indentation, or the construct is one this parser does not read",
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
return result.value;
|
|
276
|
-
} finally {
|
|
277
|
-
anchors = outer;
|
|
249
|
+
function unshareAliases(value: unknown, seen = new Set<object>()): unknown {
|
|
250
|
+
if (typeof value !== "object" || value === null) return value;
|
|
251
|
+
if (seen.has(value)) return unshareAliases(structuredClone(value), seen);
|
|
252
|
+
seen.add(value);
|
|
253
|
+
if (Array.isArray(value)) {
|
|
254
|
+
for (let i = 0; i < value.length; i++) value[i] = unshareAliases(value[i], seen);
|
|
255
|
+
} else {
|
|
256
|
+
const obj = value as Record<string, unknown>;
|
|
257
|
+
for (const key of Object.keys(obj)) obj[key] = unshareAliases(obj[key], seen);
|
|
278
258
|
}
|
|
259
|
+
return value;
|
|
279
260
|
}
|
|
280
261
|
|
|
281
262
|
/**
|
|
282
|
-
* The
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
263
|
+
* The 1-based line to report for a js-yaml error. js-yaml marks where it
|
|
264
|
+
* gave up, which for a stray line of text above a key is the key's line:
|
|
265
|
+
* `a: 1\njust text\nb: 2` fails at `b: 2`, reading `just text b` as one
|
|
266
|
+
* multiline key. Report the line where that key starts instead, the text
|
|
267
|
+
* that is not a key.
|
|
287
268
|
*/
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
269
|
+
function errorLine(lines: string[], err: yaml.YAMLException): number {
|
|
270
|
+
let line = err.mark?.line ?? 0;
|
|
271
|
+
if (/multiline key may not be an implicit key/.test(err.reason)) {
|
|
272
|
+
const indent = lines[line]?.search(/\S/) ?? -1;
|
|
273
|
+
for (let k = line - 1; k >= 0; k--) {
|
|
274
|
+
const prev = lines[k];
|
|
275
|
+
if (prev.trim() === "" || prev.trim().startsWith("#")) continue;
|
|
276
|
+
if (prev.search(/\S/) !== indent || /:(?:\s|$)/.test(prev) || /^\s*-(?:\s|$)/.test(prev)) break;
|
|
277
|
+
line = k;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return line + 1;
|
|
298
281
|
}
|
|
299
282
|
|
|
300
|
-
/** The merge key: its value's mappings fill in the keys a mapping does not set itself. */
|
|
301
|
-
const MERGE_KEY = "<<";
|
|
302
|
-
|
|
303
283
|
/**
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
284
|
+
* Read one YAML document with js-yaml (#3006).
|
|
285
|
+
*
|
|
286
|
+
* Kept from the line parser this replaced: `---` and `...` lines are skipped
|
|
287
|
+
* (so a stream reads as one document, as it always has), duplicate keys take
|
|
288
|
+
* the last value, and a document that is not a mapping or a sequence is a
|
|
289
|
+
* {@link YAMLParseError}, as is anything js-yaml rejects. An empty document is
|
|
290
|
+
* `{}`.
|
|
307
291
|
*/
|
|
308
|
-
function
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
292
|
+
function loadDocument(content: string): unknown {
|
|
293
|
+
const text = content
|
|
294
|
+
.replace(/\r\n?/g, "\n")
|
|
295
|
+
.split("\n")
|
|
296
|
+
.map((line) => (DOCUMENT_MARKER.test(line) ? "" : line))
|
|
297
|
+
.join("\n");
|
|
298
|
+
let value: unknown;
|
|
299
|
+
try {
|
|
300
|
+
value = yaml.load(text, { schema: SCHEMA, json: true });
|
|
301
|
+
} catch (err) {
|
|
302
|
+
if (!(err instanceof yaml.YAMLException)) throw err;
|
|
303
|
+
const line = errorLine(text.split("\n"), err);
|
|
304
|
+
const tag = err.reason.match(/^unknown tag !<(![^>]*)>/)?.[1];
|
|
305
|
+
throw new YAMLParseError(line, tag ? `the tag ${JSON.stringify(tag)} on a mapping is not supported` : err.reason);
|
|
306
|
+
}
|
|
307
|
+
if (value === undefined || value === null) return {};
|
|
308
|
+
if (typeof value !== "object") {
|
|
309
|
+
const line = text.split("\n").findIndex((l) => l.trim() !== "" && !l.trim().startsWith("#")) + 1;
|
|
310
|
+
throw new YAMLParseError(line, `expected a mapping or a sequence, found the scalar ${JSON.stringify(value)}`);
|
|
319
311
|
}
|
|
312
|
+
return /[&]/.test(text) ? unshareAliases(value) : value;
|
|
320
313
|
}
|
|
321
314
|
|
|
322
315
|
/**
|
|
323
316
|
* Parse a YAML document (or JSON document) into a plain object.
|
|
324
317
|
*
|
|
325
|
-
* Tries `JSON.parse` first
|
|
326
|
-
*
|
|
327
|
-
* {@link
|
|
318
|
+
* Tries `JSON.parse` first, then js-yaml (#3006). A top-level sequence reads
|
|
319
|
+
* as `{}`, since callers rely on getting a mapping back; use
|
|
320
|
+
* {@link parseYAMLDocument} for the list. Throws {@link YAMLParseError} with
|
|
321
|
+
* the line number on input it cannot read.
|
|
328
322
|
*/
|
|
329
323
|
export function parseYAML(content: string): Record<string, unknown> {
|
|
330
324
|
try {
|
|
@@ -332,21 +326,14 @@ export function parseYAML(content: string): Record<string, unknown> {
|
|
|
332
326
|
} catch {
|
|
333
327
|
// Fall through to YAML parsing
|
|
334
328
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const first = nextContentLine(lines, 0);
|
|
338
|
-
// A document with no content, or (see parseYAMLDocument) a top-level list,
|
|
339
|
-
// reads as an empty mapping; the callers rely on getting a mapping back.
|
|
340
|
-
if (first === lines.length || isSeqEntry(lines[first])) return {};
|
|
341
|
-
return parseWhole(lines, first, parseYAMLLines) as Record<string, unknown>;
|
|
329
|
+
const value = loadDocument(content);
|
|
330
|
+
return Array.isArray(value) ? {} : (value as Record<string, unknown>);
|
|
342
331
|
}
|
|
343
332
|
|
|
344
333
|
/**
|
|
345
334
|
* Parse one YAML (or JSON) document whose top level may be a sequence
|
|
346
|
-
* (#2965). {@link parseYAML}
|
|
347
|
-
*
|
|
348
|
-
* it keeps doing that. This returns the list instead, and otherwise defers to
|
|
349
|
-
* `parseYAML`.
|
|
335
|
+
* (#2965). {@link parseYAML} returns `{}` for a file like `- a\n- b`; this
|
|
336
|
+
* returns the list.
|
|
350
337
|
*/
|
|
351
338
|
export function parseYAMLDocument(content: string): unknown {
|
|
352
339
|
try {
|
|
@@ -354,11 +341,7 @@ export function parseYAMLDocument(content: string): unknown {
|
|
|
354
341
|
} catch {
|
|
355
342
|
// Fall through to YAML parsing
|
|
356
343
|
}
|
|
357
|
-
|
|
358
|
-
const lines = toLines(content);
|
|
359
|
-
const first = nextContentLine(lines, 0);
|
|
360
|
-
if (first === lines.length) return {};
|
|
361
|
-
return parseWhole(lines, first, isSeqEntry(lines[first]) ? parseYAMLArray : parseYAMLLines);
|
|
344
|
+
return loadDocument(content);
|
|
362
345
|
}
|
|
363
346
|
|
|
364
347
|
/**
|
|
@@ -384,397 +367,6 @@ export function splitYAMLDocuments(content: string): string[] {
|
|
|
384
367
|
return documents;
|
|
385
368
|
}
|
|
386
369
|
|
|
387
|
-
/**
|
|
388
|
-
* A block-scalar header: `|` (literal, keep newlines) or `>` (folded, newlines →
|
|
389
|
-
* spaces), with a chomping indicator (`-` strip trailing newlines, `+` keep them,
|
|
390
|
-
* default clip to a single one). Returns null when `inline` isn't a block header.
|
|
391
|
-
*/
|
|
392
|
-
interface BlockScalarHeader {
|
|
393
|
-
style: "literal" | "folded";
|
|
394
|
-
chomp: "clip" | "strip" | "keep";
|
|
395
|
-
}
|
|
396
|
-
function blockScalarHeader(inline: string): BlockScalarHeader | null {
|
|
397
|
-
const m = inline.match(/^([|>])([+-]?)(?:\s+#.*)?$/);
|
|
398
|
-
if (!m) return null;
|
|
399
|
-
return {
|
|
400
|
-
style: m[1] === "|" ? "literal" : "folded",
|
|
401
|
-
chomp: m[2] === "-" ? "strip" : m[2] === "+" ? "keep" : "clip",
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Parse a block scalar's body — the lines indented past `parentIndent`, dedented
|
|
407
|
-
* by the block's own indent (the first content line's). Handles literal/folded
|
|
408
|
-
* styles and clip/strip/keep chomping, matching js-yaml. Returns the string value
|
|
409
|
-
* and the index of the first line that is NOT part of the block.
|
|
410
|
-
*/
|
|
411
|
-
function parseBlockScalar(
|
|
412
|
-
lines: string[],
|
|
413
|
-
startIndex: number,
|
|
414
|
-
parentIndent: number,
|
|
415
|
-
header: BlockScalarHeader,
|
|
416
|
-
): { value: string; endIndex: number } {
|
|
417
|
-
const raw: string[] = [];
|
|
418
|
-
let blockIndent = -1;
|
|
419
|
-
let i = startIndex;
|
|
420
|
-
for (; i < lines.length; i++) {
|
|
421
|
-
const line = lines[i];
|
|
422
|
-
if (line.trim() === "") {
|
|
423
|
-
raw.push(""); // a blank line is part of the block (kept/chomped later)
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
const ni = line.search(/\S/);
|
|
427
|
-
if (ni <= parentIndent) break; // dedented out of the block
|
|
428
|
-
if (blockIndent === -1) blockIndent = ni;
|
|
429
|
-
if (ni < blockIndent) break;
|
|
430
|
-
raw.push(line.slice(blockIndent));
|
|
431
|
-
}
|
|
432
|
-
// Trailing blank lines belong to the block (chomping decides their fate).
|
|
433
|
-
let text: string;
|
|
434
|
-
if (header.style === "folded") {
|
|
435
|
-
// Fold each run of non-empty lines into a space-joined paragraph; a run of N
|
|
436
|
-
// blank lines between paragraphs becomes N newlines (a single blank → one
|
|
437
|
-
// newline), matching YAML folded semantics.
|
|
438
|
-
text = "";
|
|
439
|
-
let buf: string[] = [];
|
|
440
|
-
let blankRun = 0;
|
|
441
|
-
let wrote = false;
|
|
442
|
-
const flush = (): void => {
|
|
443
|
-
if (buf.length === 0) return;
|
|
444
|
-
if (wrote) text += "\n".repeat(blankRun);
|
|
445
|
-
text += buf.join(" ");
|
|
446
|
-
buf = [];
|
|
447
|
-
blankRun = 0;
|
|
448
|
-
wrote = true;
|
|
449
|
-
};
|
|
450
|
-
for (const l of raw) {
|
|
451
|
-
if (l === "") { flush(); blankRun++; }
|
|
452
|
-
else buf.push(l);
|
|
453
|
-
}
|
|
454
|
-
flush();
|
|
455
|
-
} else {
|
|
456
|
-
text = raw.join("\n");
|
|
457
|
-
}
|
|
458
|
-
const trailing = text.match(/\n*$/)?.[0].length ?? 0;
|
|
459
|
-
const stripped = text.replace(/\n+$/, "");
|
|
460
|
-
if (header.chomp === "strip") text = stripped;
|
|
461
|
-
else if (header.chomp === "keep") text = stripped + "\n".repeat(Math.max(trailing, raw.length > 0 ? 1 : 0));
|
|
462
|
-
else text = stripped === "" ? "" : stripped + "\n"; // clip
|
|
463
|
-
return { value: text, endIndex: i };
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Parse indentation-based YAML lines into a key-value object: the mapping
|
|
468
|
-
* whose keys sit at column `baseIndent`, starting at `startIndex`.
|
|
469
|
-
*
|
|
470
|
-
* It ends at the first line that is not one of its keys: a line at another
|
|
471
|
-
* column, a sequence entry, or text that is not `key:`. That line goes back
|
|
472
|
-
* to the caller in `endIndex`; if no caller owns it, `parseYAML` reports it
|
|
473
|
-
* (#2991). This function never skips a content line.
|
|
474
|
-
*/
|
|
475
|
-
export function parseYAMLLines(
|
|
476
|
-
lines: string[],
|
|
477
|
-
startIndex: number,
|
|
478
|
-
baseIndent: number,
|
|
479
|
-
): ParseResult {
|
|
480
|
-
const result: Record<string, unknown> = {};
|
|
481
|
-
const merges: unknown[] = [];
|
|
482
|
-
let i = startIndex;
|
|
483
|
-
|
|
484
|
-
while (i < lines.length) {
|
|
485
|
-
const line = lines[i];
|
|
486
|
-
if (isBlankOrComment(line)) {
|
|
487
|
-
i++;
|
|
488
|
-
continue;
|
|
489
|
-
}
|
|
490
|
-
if (indentOf(line) !== baseIndent || isSeqEntry(line)) break;
|
|
491
|
-
const keyMatch = line.match(KEY_LINE);
|
|
492
|
-
if (!keyMatch) break;
|
|
493
|
-
const value = parseValue(keyMatch[3].trim(), lines, i, baseIndent);
|
|
494
|
-
setKey(result, merges, keyName(keyMatch[2]), value.value);
|
|
495
|
-
i = value.endIndex;
|
|
496
|
-
}
|
|
497
|
-
applyMerges(result, merges, startIndex + 1);
|
|
498
|
-
|
|
499
|
-
return { value: result, endIndex: i };
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Parse the value of the key on line `at`, whose text after the colon is
|
|
504
|
-
* `inline` and whose own column is `keyIndent`. Used for mapping keys and for
|
|
505
|
-
* the keys of a sequence item alike.
|
|
506
|
-
*
|
|
507
|
-
* An empty inline value (or only a comment) means the value is the block on
|
|
508
|
-
* the next content line. Blank, whitespace-only and comment lines between the
|
|
509
|
-
* key and that block do not end it (#2991): Helm renders an empty `{{ if }}`
|
|
510
|
-
* as exactly such a line, and treating it as the end of the value turned
|
|
511
|
-
*
|
|
512
|
-
* spec:
|
|
513
|
-
*
|
|
514
|
-
* serviceAccountName: collector
|
|
515
|
-
*
|
|
516
|
-
* into `spec: null` with `serviceAccountName` hoisted to the parent.
|
|
517
|
-
*
|
|
518
|
-
* The two nested shapes do NOT share a threshold (#1311), except below a
|
|
519
|
-
* sequence entry (`sequenceAtKeyColumn` false), where a `-` at the entry's
|
|
520
|
-
* own column is the next entry:
|
|
521
|
-
*
|
|
522
|
-
* - a SEQUENCE may sit at the key's own column (valid YAML, and what
|
|
523
|
-
* kubectl and Kubernetes manifests emit);
|
|
524
|
-
* - a MAPPING must be indented past it, otherwise the next line is a
|
|
525
|
-
* sibling key and this key's value is null:
|
|
526
|
-
*
|
|
527
|
-
* - name: a
|
|
528
|
-
* meta: <- no value
|
|
529
|
-
* other: b <- a sibling, NOT meta's content
|
|
530
|
-
*/
|
|
531
|
-
function parseValue(
|
|
532
|
-
inline: string,
|
|
533
|
-
lines: string[],
|
|
534
|
-
at: number,
|
|
535
|
-
keyIndent: number,
|
|
536
|
-
sequenceAtKeyColumn = true,
|
|
537
|
-
): ParseResult {
|
|
538
|
-
const anchor = inline.match(ANCHOR);
|
|
539
|
-
if (anchor) {
|
|
540
|
-
const value = parseValue((anchor[2] ?? "").trim(), lines, at, keyIndent, sequenceAtKeyColumn);
|
|
541
|
-
anchors.set(anchor[1], structuredClone(value.value));
|
|
542
|
-
return value;
|
|
543
|
-
}
|
|
544
|
-
const alias = inline.match(ALIAS);
|
|
545
|
-
if (alias) return { value: resolveAlias(alias[1], at + 1), endIndex: at + 1 };
|
|
546
|
-
if (ALIAS_LIST.test(inline)) {
|
|
547
|
-
const names = [...inline.matchAll(/\*([^\s,[\]{}]+)/g)].map((m) => m[1]);
|
|
548
|
-
return { value: names.map((name) => resolveAlias(name, at + 1)), endIndex: at + 1 };
|
|
549
|
-
}
|
|
550
|
-
if (inline === "" || inline.startsWith("#")) {
|
|
551
|
-
const k = nextContentLine(lines, at + 1);
|
|
552
|
-
if (k < lines.length) {
|
|
553
|
-
const ni = indentOf(lines[k]);
|
|
554
|
-
const nested = isSeqEntry(lines[k]) && sequenceAtKeyColumn ? ni >= keyIndent : ni > keyIndent;
|
|
555
|
-
if (nested) return parseNode(lines, k, keyIndent);
|
|
556
|
-
}
|
|
557
|
-
return { value: null, endIndex: at + 1 };
|
|
558
|
-
}
|
|
559
|
-
if (inline.startsWith("[") || inline.startsWith("{")) {
|
|
560
|
-
// Inline array or object
|
|
561
|
-
try {
|
|
562
|
-
return { value: JSON.parse(inline), endIndex: at + 1 };
|
|
563
|
-
} catch {
|
|
564
|
-
return { value: inline, endIndex: at + 1 };
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
const header = blockScalarHeader(inline);
|
|
568
|
-
if (header) return parseBlockScalar(lines, at + 1, keyIndent, header);
|
|
569
|
-
return parseFlowScalar(inline, lines, at + 1, keyIndent);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Parse the node that starts on content line `k`, below a key or a bare `-`
|
|
574
|
-
* at column `parentIndent`: a sequence, a mapping, or a scalar written on the
|
|
575
|
-
* line after its key.
|
|
576
|
-
*/
|
|
577
|
-
function parseNode(lines: string[], k: number, parentIndent: number): ParseResult {
|
|
578
|
-
const line = lines[k];
|
|
579
|
-
if (isSeqEntry(line)) return parseYAMLArray(lines, k, indentOf(line));
|
|
580
|
-
if (KEY_LINE.test(line)) return parseYAMLLines(lines, k, indentOf(line));
|
|
581
|
-
return parseValue(line.trim(), lines, k, parentIndent);
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* The index of the quote that closes a quoted scalar opened by `quote`,
|
|
586
|
-
* scanning `text` from `from`, or -1. A double-quoted scalar escapes with a
|
|
587
|
-
* backslash, a single-quoted one by doubling the quote.
|
|
588
|
-
*/
|
|
589
|
-
function closingQuote(text: string, quote: string, from: number): number {
|
|
590
|
-
for (let i = from; i < text.length; i++) {
|
|
591
|
-
const ch = text[i];
|
|
592
|
-
if (quote === '"' && ch === "\\") {
|
|
593
|
-
i++;
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
if (ch === quote) {
|
|
597
|
-
if (quote === "'" && text[i + 1] === "'") {
|
|
598
|
-
i++;
|
|
599
|
-
continue;
|
|
600
|
-
}
|
|
601
|
-
return i;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return -1;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/** Whether `text` is exactly one quoted scalar, e.g. `"80:80"`. */
|
|
608
|
-
function isWholeQuotedScalar(text: string): boolean {
|
|
609
|
-
const quote = text[0];
|
|
610
|
-
if (quote !== '"' && quote !== "'") return false;
|
|
611
|
-
return closingQuote(text, quote, 1) === text.length - 1;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Parse a plain or quoted scalar whose first line is `first` and whose
|
|
616
|
-
* continuation lines, if any, start at `from` and are indented past
|
|
617
|
-
* `parentIndent`. YAML lets both kinds span lines, and go-yaml (so Helm's
|
|
618
|
-
* `toYaml`) wraps long strings that way. Lines fold into one: a line break
|
|
619
|
-
* becomes a space, and each blank line in between becomes a newline.
|
|
620
|
-
*
|
|
621
|
-
* These continuation lines used to be skipped, keeping only the first line.
|
|
622
|
-
*/
|
|
623
|
-
function parseFlowScalar(first: string, lines: string[], from: number, parentIndent: number): ParseResult {
|
|
624
|
-
const quote = first[0];
|
|
625
|
-
if (quote === '"' || quote === "'") {
|
|
626
|
-
if (closingQuote(first, quote, 1) !== -1) return { value: parseScalar(first), endIndex: from };
|
|
627
|
-
return parseMultilineQuoted(first, lines, from, parentIndent, quote);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
let text = first;
|
|
631
|
-
let endIndex = from;
|
|
632
|
-
let blanks = 0;
|
|
633
|
-
for (let j = from; j < lines.length; j++) {
|
|
634
|
-
const line = lines[j];
|
|
635
|
-
const t = line.trim();
|
|
636
|
-
if (t === "") {
|
|
637
|
-
blanks++;
|
|
638
|
-
continue;
|
|
639
|
-
}
|
|
640
|
-
// A comment ends a plain scalar; so does a line back at the parent's column.
|
|
641
|
-
if (t.startsWith("#") || indentOf(line) <= parentIndent) break;
|
|
642
|
-
if (KEY_LINE.test(line)) {
|
|
643
|
-
throw new YAMLParseError(
|
|
644
|
-
j + 1,
|
|
645
|
-
/^!\S*$/.test(first)
|
|
646
|
-
? `the tag ${JSON.stringify(first)} before a nested block is not supported`
|
|
647
|
-
: `${JSON.stringify(t)} is indented under the value ${JSON.stringify(first)}, ` +
|
|
648
|
-
"and a scalar cannot hold a mapping; check its indentation",
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
text += blanks > 0 ? "\n".repeat(blanks) : " ";
|
|
652
|
-
text += t;
|
|
653
|
-
blanks = 0;
|
|
654
|
-
endIndex = j + 1;
|
|
655
|
-
}
|
|
656
|
-
return { value: parseScalar(text), endIndex };
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* The rest of a quoted scalar left open on its first line: lines up to the
|
|
661
|
-
* closing quote, folded as {@link parseFlowScalar} describes. For a
|
|
662
|
-
* double-quoted scalar a line ending in `\` is an escaped line break that
|
|
663
|
-
* joins the next line with nothing between; it is kept as `\` + newline for
|
|
664
|
-
* `unescapeDoubleQuoted`, which removes it with the indentation after it.
|
|
665
|
-
*/
|
|
666
|
-
function parseMultilineQuoted(
|
|
667
|
-
first: string,
|
|
668
|
-
lines: string[],
|
|
669
|
-
from: number,
|
|
670
|
-
parentIndent: number,
|
|
671
|
-
quote: string,
|
|
672
|
-
): ParseResult {
|
|
673
|
-
const segments = [first.slice(1)];
|
|
674
|
-
for (let j = from; j < lines.length; j++) {
|
|
675
|
-
const line = lines[j];
|
|
676
|
-
if (line.trim() !== "" && indentOf(line) <= parentIndent) break;
|
|
677
|
-
const close = closingQuote(line, quote, 0);
|
|
678
|
-
if (close === -1) {
|
|
679
|
-
segments.push(line);
|
|
680
|
-
continue;
|
|
681
|
-
}
|
|
682
|
-
segments.push(line.slice(0, close));
|
|
683
|
-
let body = "";
|
|
684
|
-
let blanks = 0;
|
|
685
|
-
let escapedBreak = false;
|
|
686
|
-
segments.forEach((raw, n) => {
|
|
687
|
-
const isFirst = n === 0;
|
|
688
|
-
const isLast = n === segments.length - 1;
|
|
689
|
-
let s = raw;
|
|
690
|
-
if (!isFirst && !escapedBreak) s = s.replace(/^[ \t]+/, "");
|
|
691
|
-
const endsEscaped = !isLast && quote === '"' && /(?:^|[^\\])(?:\\\\)*\\$/.test(s);
|
|
692
|
-
if (!isLast && !endsEscaped) s = s.replace(/[ \t]+$/, "");
|
|
693
|
-
if (!isFirst && !isLast && s === "" && !escapedBreak) {
|
|
694
|
-
blanks++;
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
if (!isFirst) body += escapedBreak ? "\n" : blanks > 0 ? "\n".repeat(blanks) : " ";
|
|
698
|
-
body += s;
|
|
699
|
-
blanks = 0;
|
|
700
|
-
escapedBreak = endsEscaped;
|
|
701
|
-
});
|
|
702
|
-
return { value: parseScalar(quote + body + quote), endIndex: j + 1 };
|
|
703
|
-
}
|
|
704
|
-
throw new YAMLParseError(from, `unterminated ${quote === '"' ? "double" : "single"}-quoted scalar`);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* Parse a block array: the `- ` entries at column `baseIndent`, starting at
|
|
709
|
-
* `startIndex`. Like {@link parseYAMLLines} it ends at the first line that is
|
|
710
|
-
* not one of its entries and hands that line back in `endIndex`.
|
|
711
|
-
*/
|
|
712
|
-
export function parseYAMLArray(
|
|
713
|
-
lines: string[],
|
|
714
|
-
startIndex: number,
|
|
715
|
-
baseIndent: number,
|
|
716
|
-
): ParseResult {
|
|
717
|
-
const result: unknown[] = [];
|
|
718
|
-
let i = startIndex;
|
|
719
|
-
|
|
720
|
-
while (i < lines.length) {
|
|
721
|
-
const line = lines[i];
|
|
722
|
-
if (isBlankOrComment(line)) {
|
|
723
|
-
i++;
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
const itemMatch = line.match(SEQ_ENTRY);
|
|
727
|
-
if (!itemMatch || indentOf(line) !== baseIndent) break;
|
|
728
|
-
const itemValue = (itemMatch[3] ?? "").trim();
|
|
729
|
-
|
|
730
|
-
// The column the entry's content starts at, which is where the keys of a
|
|
731
|
-
// mapping entry sit: two past the dash for `- key`, more for `- key`.
|
|
732
|
-
const keyIndent = baseIndent + 1 + (itemMatch[2]?.length ?? 1);
|
|
733
|
-
// A quoted scalar holding a colon (e.g. "80:80") is not a key-value pair.
|
|
734
|
-
const kvMatch = isWholeQuotedScalar(itemValue) ? null : itemValue.match(ITEM_KEY);
|
|
735
|
-
if (kvMatch) {
|
|
736
|
-
const obj: Record<string, unknown> = {};
|
|
737
|
-
const merges: unknown[] = [];
|
|
738
|
-
const firstValue = parseValue(kvMatch[2].trim(), lines, i, keyIndent);
|
|
739
|
-
setKey(obj, merges, keyName(kvMatch[1]), firstValue.value);
|
|
740
|
-
// The entry's other keys, at the first key's column. Each value's
|
|
741
|
-
// parse reports where it ended, nested block, same-column sequence
|
|
742
|
-
// (#1311) and block scalar (#910) alike.
|
|
743
|
-
let j = firstValue.endIndex;
|
|
744
|
-
while (j < lines.length) {
|
|
745
|
-
const nextLine = lines[j];
|
|
746
|
-
if (isBlankOrComment(nextLine)) {
|
|
747
|
-
j++;
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
if (indentOf(nextLine) !== keyIndent || isSeqEntry(nextLine)) break;
|
|
751
|
-
const nextKV = nextLine.match(KEY_LINE);
|
|
752
|
-
if (!nextKV) break;
|
|
753
|
-
const value = parseValue(nextKV[3].trim(), lines, j, keyIndent);
|
|
754
|
-
setKey(obj, merges, keyName(nextKV[2]), value.value);
|
|
755
|
-
j = value.endIndex;
|
|
756
|
-
}
|
|
757
|
-
applyMerges(obj, merges, i + 1);
|
|
758
|
-
result.push(obj);
|
|
759
|
-
i = j;
|
|
760
|
-
continue;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Anything else is read the way a mapping value is: a scalar, an inline
|
|
764
|
-
// list or object, an anchor or alias, a block scalar, or, for a dash alone
|
|
765
|
-
// on its line (#1286), the block below it after any blank or comment
|
|
766
|
-
// lines, or null. A block scalar's body (`- |`) is indented past the
|
|
767
|
-
// dash's column; before #1482 the header parsed as the literal string "|"
|
|
768
|
-
// and the body lines leaked into whatever came next, hoisting a
|
|
769
|
-
// container's keys after `args:` to the document root.
|
|
770
|
-
const item = parseValue(itemValue, lines, i, baseIndent, false);
|
|
771
|
-
result.push(item.value);
|
|
772
|
-
i = item.endIndex;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
return { value: result, endIndex: i };
|
|
776
|
-
}
|
|
777
|
-
|
|
778
370
|
/**
|
|
779
371
|
* Coerce a scalar string to a typed value.
|
|
780
372
|
*/
|