@ybotok/loam 0.1.0-beta.2 → 0.2.0-alpha.1
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/CHANGELOG.md +2570 -61
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +494 -0
- package/SCHEMA.md +2362 -245
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +317 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +161 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +251 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +82 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +194 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +236 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +250 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +219 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/agent/agents-stamp.js +155 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +334 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +239 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +47 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +282 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +88 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +183 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/resolve/service.js +113 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +172 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +251 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +59 -55
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +132 -77
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +118 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +192 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +236 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +171 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +187 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +235 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +55 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -291
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -254
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1591
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -343
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2065
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js +0 -68
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/brief.js +0 -733
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -559
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -479
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -720
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -436
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -481
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1104
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `loam rebase` writes on the event axis's SURFACE half: the
|
|
3
|
+
* `x-loam-baselines` record, rebuilt wholesale from what is on disk right now
|
|
4
|
+
* — `core/openapi/baseline/plan.ts`'s mirror, over component surfaces instead
|
|
5
|
+
* of path items and components.
|
|
6
|
+
*
|
|
7
|
+
* A ROOT record rather than an in-value pin, and that is not symmetry for its
|
|
8
|
+
* own sake: a slot value is always a mapping, so `x-loam-based-on` has
|
|
9
|
+
* somewhere to live inside it, but a `components/schemas/<name>` value is a
|
|
10
|
+
* JSON Schema — where an in-value loam key would be a schema keyword, and
|
|
11
|
+
* where JSON Schema's own `true` is a legal component that no in-value key
|
|
12
|
+
* survives at all.
|
|
13
|
+
*
|
|
14
|
+
* Rebuilt rather than patched for the reason the OpenAPI plan states: a pin is
|
|
15
|
+
* a claim about what its author read, and an entry left behind for a surface
|
|
16
|
+
* the delta no longer declares — or one whose living counterpart is gone — is
|
|
17
|
+
* a claim nobody is making any more. The sorted serialization (`buildRecord`)
|
|
18
|
+
* is what makes a second run byte-identical.
|
|
19
|
+
*
|
|
20
|
+
* This module must not import from `../merge/`: the merge reads this package,
|
|
21
|
+
* so a value edge back would be a package cycle. That is also why an
|
|
22
|
+
* unreadable document is answered with "nothing to pin" rather than an
|
|
23
|
+
* `AsyncapiMergeError` — every caller chains this AFTER `pinAsyncapiSlots`,
|
|
24
|
+
* which has already thrown that error for a document either side cannot parse.
|
|
25
|
+
*/
|
|
26
|
+
import { isDeepStrictEqual } from "node:util";
|
|
27
|
+
import { parseDocument } from "yaml";
|
|
28
|
+
import { isRecord } from "../../kernel/records.js";
|
|
29
|
+
import { buildRecord, entryFor, readBaselineRecord, surfaceIn } from "../../openapi/baseline/record.js";
|
|
30
|
+
import { valueDigest } from "../../openapi/digest.js";
|
|
31
|
+
import { ASYNCAPI_BASELINES_KEY } from "../digest.js";
|
|
32
|
+
import { asyncapiSurfaces } from "./surfaces.js";
|
|
33
|
+
/**
|
|
34
|
+
* Pin every component surface of a feature event contract against the living
|
|
35
|
+
* one. The entry is always `valueDigest` of the LIVING value, never of the
|
|
36
|
+
* delta's own — the one rule that yields both merge verdicts: a surface the
|
|
37
|
+
* author only QUOTED equals its own entry, one they EDITED differs from it.
|
|
38
|
+
*
|
|
39
|
+
* A surface with no living counterpart is `unresolved` and deliberately absent
|
|
40
|
+
* from the record. Stamping a pin against nothing is not a harmless extra key:
|
|
41
|
+
* `classifyBaselineDigests` calls a pin with no living side `unfounded`, and
|
|
42
|
+
* the gate then refuses the delta as `asyncapi.baseline-invalid` — rebase
|
|
43
|
+
* would be writing the file the gate sends its author back to rebase.
|
|
44
|
+
*/
|
|
45
|
+
export function planAsyncapiBaselines(featureText, livingText, _service) {
|
|
46
|
+
const doc = parseDocument(featureText);
|
|
47
|
+
if (doc.errors.length > 0)
|
|
48
|
+
return { text: null, pins: [] };
|
|
49
|
+
let featPlain;
|
|
50
|
+
let livingPlain;
|
|
51
|
+
try {
|
|
52
|
+
featPlain = doc.toJS() ?? {};
|
|
53
|
+
const living = parseDocument(livingText);
|
|
54
|
+
if (living.errors.length > 0)
|
|
55
|
+
return { text: null, pins: [] };
|
|
56
|
+
livingPlain = living.toJS() ?? {};
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { text: null, pins: [] };
|
|
60
|
+
}
|
|
61
|
+
const current = readBaselineRecord(featPlain).record;
|
|
62
|
+
const components = new Map();
|
|
63
|
+
const pins = [];
|
|
64
|
+
for (const surface of asyncapiSurfaces(featPlain)) {
|
|
65
|
+
// A malformed current entry reads back as absent (readBaselineRecord drops
|
|
66
|
+
// it into `problems`), so this run reports `pinned` and overwrites it —
|
|
67
|
+
// rebase is the command `asyncapi.baseline-invalid` sends people to, and
|
|
68
|
+
// it must repair what it names.
|
|
69
|
+
const from = entryFor(current, surface);
|
|
70
|
+
const livingValue = surfaceIn(livingPlain, surface);
|
|
71
|
+
if (!livingValue.found) {
|
|
72
|
+
pins.push({ target: surface.id, status: "unresolved", from: from ?? null, to: null });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const digest = valueDigest(livingValue.value);
|
|
76
|
+
components.set(surface.id, digest);
|
|
77
|
+
pins.push({
|
|
78
|
+
target: surface.id,
|
|
79
|
+
status: from === digest ? "unchanged" : from === undefined ? "pinned" : "repinned",
|
|
80
|
+
from: from ?? null,
|
|
81
|
+
to: digest,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// The whole key goes when no section survives — a bare `x-loam-baselines:
|
|
85
|
+
// {}` would be bookkeeping about nothing, in every delta whose surfaces are
|
|
86
|
+
// all new. `buildRecord` is the OpenAPI axis's sorted writer, given an empty
|
|
87
|
+
// `pathItems`: an AsyncAPI document has no path items, and the section it
|
|
88
|
+
// would write is dropped below with every other empty one.
|
|
89
|
+
const rebuilt = buildRecord({ pathItems: new Map(), components });
|
|
90
|
+
const written = {};
|
|
91
|
+
if (Object.keys(rebuilt.components).length > 0)
|
|
92
|
+
written["components"] = rebuilt.components;
|
|
93
|
+
const raw = isRecord(featPlain) ? featPlain[ASYNCAPI_BASELINES_KEY] : undefined;
|
|
94
|
+
if (Object.keys(written).length === 0) {
|
|
95
|
+
if (raw === undefined)
|
|
96
|
+
return { text: null, pins };
|
|
97
|
+
doc.deleteIn([ASYNCAPI_BASELINES_KEY]);
|
|
98
|
+
return { text: doc.toString(), pins };
|
|
99
|
+
}
|
|
100
|
+
// Deep-equal, not byte-equal: the comparison must ignore key order exactly
|
|
101
|
+
// as the digests do, so a record an author reordered by hand is left in
|
|
102
|
+
// their order — and a second run over this function's own sorted output
|
|
103
|
+
// finds the parse equal and writes nothing, which is the byte-for-byte
|
|
104
|
+
// idempotence `loam rebase` promises.
|
|
105
|
+
if (isDeepStrictEqual(raw, written))
|
|
106
|
+
return { text: null, pins };
|
|
107
|
+
doc.setIn([ASYNCAPI_BASELINES_KEY], written);
|
|
108
|
+
return { text: doc.toString(), pins };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/baseline/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What `loam rebase` writes on the event axis's SURFACE half: the\n * `x-loam-baselines` record, rebuilt wholesale from what is on disk right now\n * — `core/openapi/baseline/plan.ts`'s mirror, over component surfaces instead\n * of path items and components.\n *\n * A ROOT record rather than an in-value pin, and that is not symmetry for its\n * own sake: a slot value is always a mapping, so `x-loam-based-on` has\n * somewhere to live inside it, but a `components/schemas/<name>` value is a\n * JSON Schema — where an in-value loam key would be a schema keyword, and\n * where JSON Schema's own `true` is a legal component that no in-value key\n * survives at all.\n *\n * Rebuilt rather than patched for the reason the OpenAPI plan states: a pin is\n * a claim about what its author read, and an entry left behind for a surface\n * the delta no longer declares — or one whose living counterpart is gone — is\n * a claim nobody is making any more. The sorted serialization (`buildRecord`)\n * is what makes a second run byte-identical.\n *\n * This module must not import from `../merge/`: the merge reads this package,\n * so a value edge back would be a package cycle. That is also why an\n * unreadable document is answered with \"nothing to pin\" rather than an\n * `AsyncapiMergeError` — every caller chains this AFTER `pinAsyncapiSlots`,\n * which has already thrown that error for a document either side cannot parse.\n */\nimport { isDeepStrictEqual } from \"node:util\";\nimport { parseDocument } from \"yaml\";\nimport { isRecord } from \"../../kernel/records.js\";\nimport { buildRecord, entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport { valueDigest } from \"../../openapi/digest.js\";\nimport { ASYNCAPI_BASELINES_KEY } from \"../digest.js\";\nimport { asyncapiSurfaces } from \"./surfaces.js\";\n\n/** What happened to one declared surface's baseline entry. */\nexport interface AsyncapiSurfacePin {\n /** `<kind>/<name>` — the component surface the entry pins. */\n target: string;\n status:\n /** It had no entry and now has one. */\n | \"pinned\"\n /** It named an older living version; it now names the current one. */\n | \"repinned\"\n /** It already named the current living version — no write. */\n | \"unchanged\"\n /** The living contract has no such surface: this feature is adding it, and it gets NO entry. */\n | \"unresolved\";\n from: string | null;\n to: string | null;\n}\n\nexport interface AsyncapiBaselinePlan {\n /** The rewritten contract, or null when the record already says what this run would write. */\n text: string | null;\n pins: AsyncapiSurfacePin[];\n}\n\n/**\n * Pin every component surface of a feature event contract against the living\n * one. The entry is always `valueDigest` of the LIVING value, never of the\n * delta's own — the one rule that yields both merge verdicts: a surface the\n * author only QUOTED equals its own entry, one they EDITED differs from it.\n *\n * A surface with no living counterpart is `unresolved` and deliberately absent\n * from the record. Stamping a pin against nothing is not a harmless extra key:\n * `classifyBaselineDigests` calls a pin with no living side `unfounded`, and\n * the gate then refuses the delta as `asyncapi.baseline-invalid` — rebase\n * would be writing the file the gate sends its author back to rebase.\n */\nexport function planAsyncapiBaselines(\n featureText: string,\n livingText: string,\n _service: string,\n): AsyncapiBaselinePlan {\n const doc = parseDocument(featureText);\n if (doc.errors.length > 0) return { text: null, pins: [] };\n let featPlain: unknown;\n let livingPlain: unknown;\n try {\n featPlain = doc.toJS() ?? {};\n const living = parseDocument(livingText);\n if (living.errors.length > 0) return { text: null, pins: [] };\n livingPlain = living.toJS() ?? {};\n } catch {\n return { text: null, pins: [] };\n }\n\n const current = readBaselineRecord(featPlain).record;\n const components = new Map<string, string>();\n const pins: AsyncapiSurfacePin[] = [];\n for (const surface of asyncapiSurfaces(featPlain)) {\n // A malformed current entry reads back as absent (readBaselineRecord drops\n // it into `problems`), so this run reports `pinned` and overwrites it —\n // rebase is the command `asyncapi.baseline-invalid` sends people to, and\n // it must repair what it names.\n const from = entryFor(current, surface);\n const livingValue = surfaceIn(livingPlain, surface);\n if (!livingValue.found) {\n pins.push({ target: surface.id, status: \"unresolved\", from: from ?? null, to: null });\n continue;\n }\n const digest = valueDigest(livingValue.value);\n components.set(surface.id, digest);\n pins.push({\n target: surface.id,\n status: from === digest ? \"unchanged\" : from === undefined ? \"pinned\" : \"repinned\",\n from: from ?? null,\n to: digest,\n });\n }\n\n // The whole key goes when no section survives — a bare `x-loam-baselines:\n // {}` would be bookkeeping about nothing, in every delta whose surfaces are\n // all new. `buildRecord` is the OpenAPI axis's sorted writer, given an empty\n // `pathItems`: an AsyncAPI document has no path items, and the section it\n // would write is dropped below with every other empty one.\n const rebuilt = buildRecord({ pathItems: new Map(), components });\n const written: Record<string, unknown> = {};\n if (Object.keys(rebuilt.components).length > 0) written[\"components\"] = rebuilt.components;\n const raw = isRecord(featPlain) ? featPlain[ASYNCAPI_BASELINES_KEY] : undefined;\n if (Object.keys(written).length === 0) {\n if (raw === undefined) return { text: null, pins };\n doc.deleteIn([ASYNCAPI_BASELINES_KEY]);\n return { text: doc.toString(), pins };\n }\n // Deep-equal, not byte-equal: the comparison must ignore key order exactly\n // as the digests do, so a record an author reordered by hand is left in\n // their order — and a second run over this function's own sorted output\n // finds the parse equal and writes nothing, which is the byte-for-byte\n // idempotence `loam rebase` promises.\n if (isDeepStrictEqual(raw, written)) return { text: null, pins };\n doc.setIn([ASYNCAPI_BASELINES_KEY], written);\n return { text: doc.toString(), pins };\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The values a feature event contract restates that the SLOT pin cannot reach:
|
|
3
|
+
* every `components/<kind>/<name>` outside `messages`.
|
|
4
|
+
*
|
|
5
|
+
* A package of its own rather than a sixth file in `core/asyncapi/` — that
|
|
6
|
+
* directory is exactly at the five-file limit — and a separate ENUMERATION
|
|
7
|
+
* from the OpenAPI axis's `restatedSurfaces` rather than an import of it,
|
|
8
|
+
* although the two are the same shape. `restatedSurfaces` walks `paths` (a key
|
|
9
|
+
* an AsyncAPI document does not have) and includes `components/messages`,
|
|
10
|
+
* which on this axis is a SLOT: a message pinned both in-value by
|
|
11
|
+
* `x-loam-based-on` and by a root record entry would have two identities, and
|
|
12
|
+
* the merge would classify it twice — once as a slot, once as a surface — with
|
|
13
|
+
* verdicts that need not agree. What IS shared is everything downstream of the
|
|
14
|
+
* enumeration: `readBaselineRecord`, `entryFor`, `surfaceIn` and the record's
|
|
15
|
+
* `components` half are axis-neutral, so the record means the same thing on
|
|
16
|
+
* both axes and only the walk that fills it differs.
|
|
17
|
+
*
|
|
18
|
+
* Import direction is asyncapi → openapi, as core/asyncapi/digest.ts's header
|
|
19
|
+
* states; nothing under core/openapi/ imports this package.
|
|
20
|
+
*/
|
|
21
|
+
import { isRecord } from "../../kernel/records.js";
|
|
22
|
+
import { FEATURE_ONLY_KEYS } from "../../openapi/digest.js";
|
|
23
|
+
/**
|
|
24
|
+
* Every component surface a plain-parsed feature document declares, in
|
|
25
|
+
* document order.
|
|
26
|
+
*
|
|
27
|
+
* `messages` is skipped because it is the third slot section (core/asyncapi/
|
|
28
|
+
* digest.ts): a message carries its own in-value pin, and giving it a root
|
|
29
|
+
* record entry as well would be two pins on one value. The feature-only keys
|
|
30
|
+
* are skipped in the KIND position because `components: {x-loam-remove: true,
|
|
31
|
+
* schemas: {…}}` is bookkeeping sitting where a component kind goes — the
|
|
32
|
+
* strip already treats it that way, and a surface named after a marker would
|
|
33
|
+
* be pinned by `loam rebase` and then graded as an orphan the moment the strip
|
|
34
|
+
* took it out.
|
|
35
|
+
*/
|
|
36
|
+
export function asyncapiSurfaces(plain) {
|
|
37
|
+
const out = [];
|
|
38
|
+
if (!isRecord(plain))
|
|
39
|
+
return out;
|
|
40
|
+
const comps = plain["components"];
|
|
41
|
+
if (!isRecord(comps))
|
|
42
|
+
return out;
|
|
43
|
+
for (const [kind, names] of Object.entries(comps)) {
|
|
44
|
+
if (kind === "messages" || FEATURE_ONLY_KEYS.has(kind))
|
|
45
|
+
continue;
|
|
46
|
+
if (!isRecord(names))
|
|
47
|
+
continue;
|
|
48
|
+
for (const [name, value] of Object.entries(names)) {
|
|
49
|
+
out.push({ kind: "component", id: `${kind}/${name}`, value });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=surfaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surfaces.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/baseline/surfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAW5D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,IAAI,IAAI,KAAK,UAAU,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACjE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * The values a feature event contract restates that the SLOT pin cannot reach:\n * every `components/<kind>/<name>` outside `messages`.\n *\n * A package of its own rather than a sixth file in `core/asyncapi/` — that\n * directory is exactly at the five-file limit — and a separate ENUMERATION\n * from the OpenAPI axis's `restatedSurfaces` rather than an import of it,\n * although the two are the same shape. `restatedSurfaces` walks `paths` (a key\n * an AsyncAPI document does not have) and includes `components/messages`,\n * which on this axis is a SLOT: a message pinned both in-value by\n * `x-loam-based-on` and by a root record entry would have two identities, and\n * the merge would classify it twice — once as a slot, once as a surface — with\n * verdicts that need not agree. What IS shared is everything downstream of the\n * enumeration: `readBaselineRecord`, `entryFor`, `surfaceIn` and the record's\n * `components` half are axis-neutral, so the record means the same thing on\n * both axes and only the walk that fills it differs.\n *\n * Import direction is asyncapi → openapi, as core/asyncapi/digest.ts's header\n * states; nothing under core/openapi/ imports this package.\n */\nimport { isRecord } from \"../../kernel/records.js\";\nimport { FEATURE_ONLY_KEYS } from \"../../openapi/digest.js\";\nimport type { RestatedSurface } from \"../../openapi/baseline/record.js\";\n\n/**\n * One surface of an event contract. Structurally the OpenAPI record's\n * component branch, on purpose: `entryFor` and `surfaceIn` take that union, so\n * a separate shape here would mean a second spelling of \"the record's entry\n * for this value\" — the drift both axes' digest modules exist to prevent.\n */\nexport type AsyncapiSurface = Extract<RestatedSurface, { kind: \"component\" }>;\n\n/**\n * Every component surface a plain-parsed feature document declares, in\n * document order.\n *\n * `messages` is skipped because it is the third slot section (core/asyncapi/\n * digest.ts): a message carries its own in-value pin, and giving it a root\n * record entry as well would be two pins on one value. The feature-only keys\n * are skipped in the KIND position because `components: {x-loam-remove: true,\n * schemas: {…}}` is bookkeeping sitting where a component kind goes — the\n * strip already treats it that way, and a surface named after a marker would\n * be pinned by `loam rebase` and then graded as an orphan the moment the strip\n * took it out.\n */\nexport function asyncapiSurfaces(plain: unknown): AsyncapiSurface[] {\n const out: AsyncapiSurface[] = [];\n if (!isRecord(plain)) return out;\n const comps = plain[\"components\"];\n if (!isRecord(comps)) return out;\n for (const [kind, names] of Object.entries(comps)) {\n if (kind === \"messages\" || FEATURE_ONLY_KEYS.has(kind)) continue;\n if (!isRecord(names)) continue;\n for (const [name, value] of Object.entries(names)) {\n out.push({ kind: \"component\", id: `${kind}/${name}`, value });\n }\n }\n return out;\n}\n"]}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pointer resolution and the contract-depth probes for the async axis: does a
|
|
3
|
+
* message's payload declare any shape at all, and does every internal `$ref`
|
|
4
|
+
* point at something.
|
|
5
|
+
*
|
|
6
|
+
* `resolvePointer` and `deref` moved here verbatim from read.ts when the
|
|
7
|
+
* probes arrived — they are what the probes and the reader both walk with,
|
|
8
|
+
* and read.ts was nine lines under the file limit.
|
|
9
|
+
*
|
|
10
|
+
* The probes are PRESENCE probes, never field joins: nothing here contributes
|
|
11
|
+
* a name to the spine, nothing reads what a schema says — only whether one
|
|
12
|
+
* was declared — and a payload declaring a non-JSON `schemaFormat` is skipped
|
|
13
|
+
* outright, so the Avro migration stays a document change (read.ts's header
|
|
14
|
+
* states the full stance).
|
|
15
|
+
*/
|
|
16
|
+
/** How deep a `$ref` chain is followed before the reader gives up. */
|
|
17
|
+
const MAX_REF_DEPTH = 8;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve an internal JSON Pointer (`#/a/b/c`) against the document root.
|
|
20
|
+
*
|
|
21
|
+
* Internal only, matching the OpenAPI axis's documented stance: external
|
|
22
|
+
* references — URLs, file paths, anything not starting `#/` — are out of scope,
|
|
23
|
+
* left untouched and never graded. That is also why authors are told to keep
|
|
24
|
+
* payloads inside the document rather than `$ref`-ing an external `.avsc`: the
|
|
25
|
+
* merge cannot carry what this reader cannot see.
|
|
26
|
+
*/
|
|
27
|
+
export function resolvePointer(root, ref) {
|
|
28
|
+
if (typeof ref !== "string" || !ref.startsWith("#/"))
|
|
29
|
+
return undefined;
|
|
30
|
+
let node = root;
|
|
31
|
+
for (const raw of ref.slice(2).split("/")) {
|
|
32
|
+
// JSON Pointer escaping, `~1` before `~0` so an encoded `~1` survives.
|
|
33
|
+
const seg = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
34
|
+
if (node === null || typeof node !== "object")
|
|
35
|
+
return undefined;
|
|
36
|
+
node = node[seg];
|
|
37
|
+
}
|
|
38
|
+
return node;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Follow `$ref` hops until a node that is not a reference, or the depth budget
|
|
42
|
+
* runs out. The budget is the cycle guard: a document whose `$ref` points at
|
|
43
|
+
* itself is legal YAML, and an unbounded walk would hang the whole validate run
|
|
44
|
+
* rather than reporting a contract nobody can read.
|
|
45
|
+
*
|
|
46
|
+
* Returns the resolved node and the last pointer segment that reached it — the
|
|
47
|
+
* segment is the declaration key, which is the message-name fallback, and it is
|
|
48
|
+
* lost by the time a caller holds only the node.
|
|
49
|
+
*/
|
|
50
|
+
export function deref(root, node, key) {
|
|
51
|
+
let current = node;
|
|
52
|
+
let currentKey = key;
|
|
53
|
+
for (let hop = 0; hop < MAX_REF_DEPTH; hop += 1) {
|
|
54
|
+
if (current === null || typeof current !== "object" || Array.isArray(current))
|
|
55
|
+
break;
|
|
56
|
+
const ref = current["$ref"];
|
|
57
|
+
if (typeof ref !== "string")
|
|
58
|
+
break;
|
|
59
|
+
const resolved = resolvePointer(root, ref);
|
|
60
|
+
if (resolved === undefined)
|
|
61
|
+
return { node: undefined, key: currentKey };
|
|
62
|
+
const seg = ref.slice(ref.lastIndexOf("/") + 1);
|
|
63
|
+
current = resolved;
|
|
64
|
+
// An alias keeps the TARGET's key, not the local one it was filed under: a
|
|
65
|
+
// channel may list `#/components/messages/PaymentAuthorized` under any alias
|
|
66
|
+
// it likes, and the fallback name has to be the declaration's own identity
|
|
67
|
+
// or two services aliasing one message differently would stop joining.
|
|
68
|
+
currentKey = seg.length > 0 ? seg : currentKey;
|
|
69
|
+
}
|
|
70
|
+
return { node: current, key: currentKey };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The JSON-schema keys that say anything about shape. Presence is all that is
|
|
74
|
+
* checked — a schema carrying any of these declared SOMETHING a consumer can
|
|
75
|
+
* code against, however incompletely.
|
|
76
|
+
*/
|
|
77
|
+
const SHAPE_KEYS = [
|
|
78
|
+
"properties",
|
|
79
|
+
"$ref",
|
|
80
|
+
"items",
|
|
81
|
+
"enum",
|
|
82
|
+
"const",
|
|
83
|
+
"oneOf",
|
|
84
|
+
"anyOf",
|
|
85
|
+
"allOf",
|
|
86
|
+
"not",
|
|
87
|
+
"patternProperties",
|
|
88
|
+
"additionalProperties",
|
|
89
|
+
"required",
|
|
90
|
+
];
|
|
91
|
+
/**
|
|
92
|
+
* True when a message declaration's payload defines no shape at all: no
|
|
93
|
+
* `payload`, or a schema with none of the keys that say anything and a `type`
|
|
94
|
+
* of `object` or none — `type: object` and not one word more validates as
|
|
95
|
+
* cleanly as a full schema even though it carries no usable shape.
|
|
96
|
+
*
|
|
97
|
+
* Deliberately NOT empty: a payload behind a dangling `$ref` (that is the ref
|
|
98
|
+
* probe's finding), a non-mapping payload (`payload: true` is a declared
|
|
99
|
+
* decision), a primitive `type`, and any payload declaring a non-JSON
|
|
100
|
+
* `schemaFormat` — Avro's shape is not ours to judge.
|
|
101
|
+
*/
|
|
102
|
+
export function payloadUndeclared(root, msg) {
|
|
103
|
+
if (msg === null || typeof msg !== "object" || Array.isArray(msg))
|
|
104
|
+
return false;
|
|
105
|
+
const raw = msg["payload"];
|
|
106
|
+
if (raw === undefined || raw === null)
|
|
107
|
+
return true;
|
|
108
|
+
const payload = deref(root, raw, "").node;
|
|
109
|
+
if (payload === undefined)
|
|
110
|
+
return false;
|
|
111
|
+
if (payload === null)
|
|
112
|
+
return true;
|
|
113
|
+
if (typeof payload !== "object" || Array.isArray(payload))
|
|
114
|
+
return false;
|
|
115
|
+
let schema = payload;
|
|
116
|
+
const format = schema["schemaFormat"];
|
|
117
|
+
if (typeof format === "string") {
|
|
118
|
+
if (!/json/i.test(format))
|
|
119
|
+
return false;
|
|
120
|
+
// Multi-format shape: the actual schema sits one level down.
|
|
121
|
+
const inner = deref(root, schema["schema"], "").node;
|
|
122
|
+
if (inner === undefined)
|
|
123
|
+
return false;
|
|
124
|
+
if (inner === null)
|
|
125
|
+
return true;
|
|
126
|
+
if (typeof inner !== "object" || Array.isArray(inner))
|
|
127
|
+
return false;
|
|
128
|
+
schema = inner;
|
|
129
|
+
}
|
|
130
|
+
if (SHAPE_KEYS.some((k) => k in schema))
|
|
131
|
+
return false;
|
|
132
|
+
const type = schema["type"];
|
|
133
|
+
return type === undefined || type === "object";
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Every internal `#/` reference in the document that resolves to nothing —
|
|
137
|
+
* deduped, document order. This is the probe that catches BOTH silent-skip
|
|
138
|
+
* sites in the reader: an operation's message-list entry that derefs to
|
|
139
|
+
* nothing (it "contributes no name" and vanishes from sent/received), and a
|
|
140
|
+
* channel alias whose dangling target leaves a phantom local key behind.
|
|
141
|
+
*/
|
|
142
|
+
export function danglingRefs(root) {
|
|
143
|
+
const out = new Set();
|
|
144
|
+
const walk = (node) => {
|
|
145
|
+
if (Array.isArray(node)) {
|
|
146
|
+
for (const value of node)
|
|
147
|
+
walk(value);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (node === null || typeof node !== "object")
|
|
151
|
+
return;
|
|
152
|
+
for (const [key, value] of Object.entries(node)) {
|
|
153
|
+
if (key === "$ref" && typeof value === "string") {
|
|
154
|
+
if (value.startsWith("#/") && resolvePointer(root, value) === undefined)
|
|
155
|
+
out.add(value);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
walk(value);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
walk(root);
|
|
162
|
+
return [...out];
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=depth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depth.js","sourceRoot":"","sources":["../../../src/core/asyncapi/depth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pointer resolution and the contract-depth probes for the async axis: does a\n * message's payload declare any shape at all, and does every internal `$ref`\n * point at something.\n *\n * `resolvePointer` and `deref` moved here verbatim from read.ts when the\n * probes arrived — they are what the probes and the reader both walk with,\n * and read.ts was nine lines under the file limit.\n *\n * The probes are PRESENCE probes, never field joins: nothing here contributes\n * a name to the spine, nothing reads what a schema says — only whether one\n * was declared — and a payload declaring a non-JSON `schemaFormat` is skipped\n * outright, so the Avro migration stays a document change (read.ts's header\n * states the full stance).\n */\n\n/** How deep a `$ref` chain is followed before the reader gives up. */\nconst MAX_REF_DEPTH = 8;\n\n/**\n * Resolve an internal JSON Pointer (`#/a/b/c`) against the document root.\n *\n * Internal only, matching the OpenAPI axis's documented stance: external\n * references — URLs, file paths, anything not starting `#/` — are out of scope,\n * left untouched and never graded. That is also why authors are told to keep\n * payloads inside the document rather than `$ref`-ing an external `.avsc`: the\n * merge cannot carry what this reader cannot see.\n */\nexport function resolvePointer(root: unknown, ref: unknown): unknown {\n if (typeof ref !== \"string\" || !ref.startsWith(\"#/\")) return undefined;\n let node = root;\n for (const raw of ref.slice(2).split(\"/\")) {\n // JSON Pointer escaping, `~1` before `~0` so an encoded `~1` survives.\n const seg = raw.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n if (node === null || typeof node !== \"object\") return undefined;\n node = (node as Record<string, unknown>)[seg];\n }\n return node;\n}\n\n/**\n * Follow `$ref` hops until a node that is not a reference, or the depth budget\n * runs out. The budget is the cycle guard: a document whose `$ref` points at\n * itself is legal YAML, and an unbounded walk would hang the whole validate run\n * rather than reporting a contract nobody can read.\n *\n * Returns the resolved node and the last pointer segment that reached it — the\n * segment is the declaration key, which is the message-name fallback, and it is\n * lost by the time a caller holds only the node.\n */\nexport function deref(root: unknown, node: unknown, key: string): { node: unknown; key: string } {\n let current = node;\n let currentKey = key;\n for (let hop = 0; hop < MAX_REF_DEPTH; hop += 1) {\n if (current === null || typeof current !== \"object\" || Array.isArray(current)) break;\n const ref = (current as Record<string, unknown>)[\"$ref\"];\n if (typeof ref !== \"string\") break;\n const resolved = resolvePointer(root, ref);\n if (resolved === undefined) return { node: undefined, key: currentKey };\n const seg = ref.slice(ref.lastIndexOf(\"/\") + 1);\n current = resolved;\n // An alias keeps the TARGET's key, not the local one it was filed under: a\n // channel may list `#/components/messages/PaymentAuthorized` under any alias\n // it likes, and the fallback name has to be the declaration's own identity\n // or two services aliasing one message differently would stop joining.\n currentKey = seg.length > 0 ? seg : currentKey;\n }\n return { node: current, key: currentKey };\n}\n\n/**\n * The JSON-schema keys that say anything about shape. Presence is all that is\n * checked — a schema carrying any of these declared SOMETHING a consumer can\n * code against, however incompletely.\n */\nconst SHAPE_KEYS = [\n \"properties\",\n \"$ref\",\n \"items\",\n \"enum\",\n \"const\",\n \"oneOf\",\n \"anyOf\",\n \"allOf\",\n \"not\",\n \"patternProperties\",\n \"additionalProperties\",\n \"required\",\n];\n\n/**\n * True when a message declaration's payload defines no shape at all: no\n * `payload`, or a schema with none of the keys that say anything and a `type`\n * of `object` or none — `type: object` and not one word more validates as\n * cleanly as a full schema even though it carries no usable shape.\n *\n * Deliberately NOT empty: a payload behind a dangling `$ref` (that is the ref\n * probe's finding), a non-mapping payload (`payload: true` is a declared\n * decision), a primitive `type`, and any payload declaring a non-JSON\n * `schemaFormat` — Avro's shape is not ours to judge.\n */\nexport function payloadUndeclared(root: unknown, msg: unknown): boolean {\n if (msg === null || typeof msg !== \"object\" || Array.isArray(msg)) return false;\n const raw = (msg as Record<string, unknown>)[\"payload\"];\n if (raw === undefined || raw === null) return true;\n const payload = deref(root, raw, \"\").node;\n if (payload === undefined) return false;\n if (payload === null) return true;\n if (typeof payload !== \"object\" || Array.isArray(payload)) return false;\n let schema = payload as Record<string, unknown>;\n const format = schema[\"schemaFormat\"];\n if (typeof format === \"string\") {\n if (!/json/i.test(format)) return false;\n // Multi-format shape: the actual schema sits one level down.\n const inner = deref(root, schema[\"schema\"], \"\").node;\n if (inner === undefined) return false;\n if (inner === null) return true;\n if (typeof inner !== \"object\" || Array.isArray(inner)) return false;\n schema = inner as Record<string, unknown>;\n }\n if (SHAPE_KEYS.some((k) => k in schema)) return false;\n const type = schema[\"type\"];\n return type === undefined || type === \"object\";\n}\n\n/**\n * Every internal `#/` reference in the document that resolves to nothing —\n * deduped, document order. This is the probe that catches BOTH silent-skip\n * sites in the reader: an operation's message-list entry that derefs to\n * nothing (it \"contributes no name\" and vanishes from sent/received), and a\n * channel alias whose dangling target leaves a phantom local key behind.\n */\nexport function danglingRefs(root: unknown): string[] {\n const out = new Set<string>();\n const walk = (node: unknown): void => {\n if (Array.isArray(node)) {\n for (const value of node) walk(value);\n return;\n }\n if (node === null || typeof node !== \"object\") return;\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n if (key === \"$ref\" && typeof value === \"string\") {\n if (value.startsWith(\"#/\") && resolvePointer(root, value) === undefined) out.add(value);\n continue;\n }\n walk(value);\n }\n };\n walk(root);\n return [...out];\n}\n"]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot identity for the event axis: what a feature-local asyncapi.yaml's
|
|
3
|
+
* slots hash to, and the markers `loam rebase` and the merge read from them.
|
|
4
|
+
*
|
|
5
|
+
* The digest RULE is imported from the OpenAPI axis (../openapi/digest.js)
|
|
6
|
+
* rather than respelled: the marker keys, the removal test, the baseline
|
|
7
|
+
* accessors and the digest itself are ALIASES of that module's — one
|
|
8
|
+
* definition each, renamed to this axis's vocabulary. Two spellings of the
|
|
9
|
+
* one rule is the exact drift that module's own header names as the failure
|
|
10
|
+
* mode, and it is not hypothetical here: the deep strip filters on the
|
|
11
|
+
* OpenAPI key constants, so a second spelling of either key would be two
|
|
12
|
+
* definitions of "which keys are feature-only" — the marker leak the strip
|
|
13
|
+
* exists to close. The import direction is asyncapi → openapi only,
|
|
14
|
+
* verified cycle-free — no openapi module imports asyncapi
|
|
15
|
+
* (openapi/depth.ts names it in a comment alone).
|
|
16
|
+
*
|
|
17
|
+
* Identity is the SLOT: the pair (section, key) over the three named
|
|
18
|
+
* sections `channels.<key>`, `operations.<key>` and
|
|
19
|
+
* `components.messages.<key>` — SCHEMA.md's format spec for this axis. An
|
|
20
|
+
* inline channel message (`channels.<ck>.messages.<mk>`) is deliberately
|
|
21
|
+
* NOT a slot: its identity is interior content of its channel's slot, so
|
|
22
|
+
* the walker below never visits it, it cannot carry its own pin, and a
|
|
23
|
+
* removal marker nested on it grades as part of the channel's content —
|
|
24
|
+
* a feature declares or retires an INDIVIDUAL message under
|
|
25
|
+
* `components.messages`, with the channel aliasing it by `$ref`.
|
|
26
|
+
*
|
|
27
|
+
* Nothing here reads inside `payload` as anything but bytes: a slot digest
|
|
28
|
+
* is content identity (a changed payload moves it), never a join on payload
|
|
29
|
+
* fields — read.ts's header carries the full doctrine.
|
|
30
|
+
*/
|
|
31
|
+
import { isRemoval, OPENAPI_BASELINE_KEY, OPENAPI_BASELINES_KEY, OPENAPI_REMOVE_KEY, operationBaselineOf, operationDigest, withoutOperationBaseline, } from "../openapi/digest.js";
|
|
32
|
+
/**
|
|
33
|
+
* The baseline marker: which living version of a slot a FEATURE delta was
|
|
34
|
+
* written against. The OpenAPI axis's key, not a restatement of its
|
|
35
|
+
* spelling, because a feature's asyncapi.yaml is a complete document for
|
|
36
|
+
* the same reason its openapi.yaml is: authors restate the living contract
|
|
37
|
+
* around the slot they change, and the pin is what lets the merge tell a
|
|
38
|
+
* QUOTE from an EDIT.
|
|
39
|
+
*/
|
|
40
|
+
export const ASYNCAPI_BASELINE_KEY = OPENAPI_BASELINE_KEY;
|
|
41
|
+
/**
|
|
42
|
+
* The feature-only ROOT record that pins what the SLOT pin cannot reach: every
|
|
43
|
+
* `components/<kind>/<name>` outside `messages`. The OpenAPI axis's key and
|
|
44
|
+
* the OpenAPI axis's reader (core/openapi/baseline/record.ts) — one record
|
|
45
|
+
* shape for both axes, because a second spelling of "which living version was
|
|
46
|
+
* this written against" is the drift this module's header names.
|
|
47
|
+
*
|
|
48
|
+
* A root record rather than an in-value pin here for the reason it is one
|
|
49
|
+
* there: a slot value is always a mapping, so `x-loam-based-on` has somewhere
|
|
50
|
+
* to live inside it, but a `components/schemas/<name>` value is a JSON Schema
|
|
51
|
+
* — an in-value loam key there would be a schema keyword, and JSON Schema's
|
|
52
|
+
* own `true` is a legal component that no in-value key survives at all.
|
|
53
|
+
*/
|
|
54
|
+
export const ASYNCAPI_BASELINES_KEY = OPENAPI_BASELINES_KEY;
|
|
55
|
+
/** The feature-only explicit removal marker's key — the OpenAPI axis's, aliased. */
|
|
56
|
+
export const ASYNCAPI_REMOVE_KEY = OPENAPI_REMOVE_KEY;
|
|
57
|
+
/** Is this slot value a feature-only explicit removal marker? One test for both axes. */
|
|
58
|
+
export const isSlotRemoval = isRemoval;
|
|
59
|
+
/** The `x-loam-based-on` a slot declares, or undefined — `operationBaselineOf`, aliased. */
|
|
60
|
+
export const slotBaselineOf = operationBaselineOf;
|
|
61
|
+
/**
|
|
62
|
+
* The slot without its own baseline marker — what a digest is taken over. A
|
|
63
|
+
* pin is a statement ABOUT a delta, never part of the slot it describes:
|
|
64
|
+
* inside the digest input, no baseline could ever be self-consistent. The
|
|
65
|
+
* OpenAPI rule, aliased — the key is the same key.
|
|
66
|
+
*/
|
|
67
|
+
export const withoutSlotBaseline = withoutOperationBaseline;
|
|
68
|
+
/**
|
|
69
|
+
* The identity of a slot's CONTENT: sha256 of its canonical form, pin
|
|
70
|
+
* excluded. `operationDigest` under this axis's name — the same canonical
|
|
71
|
+
* JSON, the same cut, so a pin either axis writes means the same thing.
|
|
72
|
+
*/
|
|
73
|
+
export const slotDigest = operationDigest;
|
|
74
|
+
/** Entries of a mapping node, or nothing — the walker's one structural read. */
|
|
75
|
+
function entriesOf(node) {
|
|
76
|
+
if (node === null || typeof node !== "object" || Array.isArray(node))
|
|
77
|
+
return [];
|
|
78
|
+
return Object.entries(node);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The document path of one slot — where the pin and the merge write. Spelled
|
|
82
|
+
* here so `rebase`'s stamp and the merge's upsert cannot disagree about which
|
|
83
|
+
* node a (section, key) pair addresses.
|
|
84
|
+
*/
|
|
85
|
+
export function slotPath(section, key) {
|
|
86
|
+
return section === "components.messages" ? ["components", "messages", key] : [section, key];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Every path whose node carries `x-loam-remove: true`, at ANY depth — the
|
|
90
|
+
* three slot depths the format spec gives the key meaning at, inline channel
|
|
91
|
+
* messages, and every place the key means nothing at all: the document root,
|
|
92
|
+
* `info`, a `components` sibling, the inside of a payload. The reader's
|
|
93
|
+
* `markers` field is built from this walk so `asyncapi.remove-marker-living`
|
|
94
|
+
* can NAME a marker wherever one leaked: the strip removes the loam keys at
|
|
95
|
+
* document depth, and a sweep narrower than the strip is how the OpenAPI
|
|
96
|
+
* axis once published a marker invisible even to validate.
|
|
97
|
+
*/
|
|
98
|
+
export function removalMarkerPaths(node, at) {
|
|
99
|
+
if (node === null || typeof node !== "object")
|
|
100
|
+
return [];
|
|
101
|
+
const out = [];
|
|
102
|
+
if (isSlotRemoval(node))
|
|
103
|
+
out.push(at === "" ? "(document root)" : at);
|
|
104
|
+
const children = Array.isArray(node)
|
|
105
|
+
? node.map((value, i) => [String(i), value])
|
|
106
|
+
: Object.entries(node);
|
|
107
|
+
for (const [key, value] of children) {
|
|
108
|
+
out.push(...removalMarkerPaths(value, at === "" ? key : `${at}.${key}`));
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Every slot a parsed document declares, in document order across the three
|
|
114
|
+
* sections. Inline channel messages are NOT visited: they are interior
|
|
115
|
+
* content of their channel's slot (the header carries the decision), so a
|
|
116
|
+
* pin or marker nested on one is part of the `channels.<ck>` value this
|
|
117
|
+
* walker already returns — never a slot of its own.
|
|
118
|
+
*/
|
|
119
|
+
export function asyncapiSlots(root) {
|
|
120
|
+
if (root === null || typeof root !== "object" || Array.isArray(root))
|
|
121
|
+
return [];
|
|
122
|
+
const doc = root;
|
|
123
|
+
const slots = [];
|
|
124
|
+
const walk = (section, mapping) => {
|
|
125
|
+
for (const [key, node] of entriesOf(mapping)) {
|
|
126
|
+
slots.push({
|
|
127
|
+
section,
|
|
128
|
+
key,
|
|
129
|
+
node,
|
|
130
|
+
remove: isSlotRemoval(node),
|
|
131
|
+
digest: slotDigest(node),
|
|
132
|
+
...(slotBaselineOf(node) === undefined ? {} : { basedOn: slotBaselineOf(node) }),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
walk("channels", doc["channels"]);
|
|
137
|
+
walk("operations", doc["operations"]);
|
|
138
|
+
walk("components.messages", doc["components"]?.["messages"]);
|
|
139
|
+
return slots;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../../src/core/asyncapi/digest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Slot identity for the event axis: what a feature-local asyncapi.yaml's\n * slots hash to, and the markers `loam rebase` and the merge read from them.\n *\n * The digest RULE is imported from the OpenAPI axis (../openapi/digest.js)\n * rather than respelled: the marker keys, the removal test, the baseline\n * accessors and the digest itself are ALIASES of that module's — one\n * definition each, renamed to this axis's vocabulary. Two spellings of the\n * one rule is the exact drift that module's own header names as the failure\n * mode, and it is not hypothetical here: the deep strip filters on the\n * OpenAPI key constants, so a second spelling of either key would be two\n * definitions of \"which keys are feature-only\" — the marker leak the strip\n * exists to close. The import direction is asyncapi → openapi only,\n * verified cycle-free — no openapi module imports asyncapi\n * (openapi/depth.ts names it in a comment alone).\n *\n * Identity is the SLOT: the pair (section, key) over the three named\n * sections `channels.<key>`, `operations.<key>` and\n * `components.messages.<key>` — SCHEMA.md's format spec for this axis. An\n * inline channel message (`channels.<ck>.messages.<mk>`) is deliberately\n * NOT a slot: its identity is interior content of its channel's slot, so\n * the walker below never visits it, it cannot carry its own pin, and a\n * removal marker nested on it grades as part of the channel's content —\n * a feature declares or retires an INDIVIDUAL message under\n * `components.messages`, with the channel aliasing it by `$ref`.\n *\n * Nothing here reads inside `payload` as anything but bytes: a slot digest\n * is content identity (a changed payload moves it), never a join on payload\n * fields — read.ts's header carries the full doctrine.\n */\nimport {\n isRemoval,\n OPENAPI_BASELINE_KEY,\n OPENAPI_BASELINES_KEY,\n OPENAPI_REMOVE_KEY,\n operationBaselineOf,\n operationDigest,\n withoutOperationBaseline,\n} from \"../openapi/digest.js\";\n\n/**\n * The baseline marker: which living version of a slot a FEATURE delta was\n * written against. The OpenAPI axis's key, not a restatement of its\n * spelling, because a feature's asyncapi.yaml is a complete document for\n * the same reason its openapi.yaml is: authors restate the living contract\n * around the slot they change, and the pin is what lets the merge tell a\n * QUOTE from an EDIT.\n */\nexport const ASYNCAPI_BASELINE_KEY = OPENAPI_BASELINE_KEY;\n\n/**\n * The feature-only ROOT record that pins what the SLOT pin cannot reach: every\n * `components/<kind>/<name>` outside `messages`. The OpenAPI axis's key and\n * the OpenAPI axis's reader (core/openapi/baseline/record.ts) — one record\n * shape for both axes, because a second spelling of \"which living version was\n * this written against\" is the drift this module's header names.\n *\n * A root record rather than an in-value pin here for the reason it is one\n * there: a slot value is always a mapping, so `x-loam-based-on` has somewhere\n * to live inside it, but a `components/schemas/<name>` value is a JSON Schema\n * — an in-value loam key there would be a schema keyword, and JSON Schema's\n * own `true` is a legal component that no in-value key survives at all.\n */\nexport const ASYNCAPI_BASELINES_KEY = OPENAPI_BASELINES_KEY;\n\n/** The feature-only explicit removal marker's key — the OpenAPI axis's, aliased. */\nexport const ASYNCAPI_REMOVE_KEY = OPENAPI_REMOVE_KEY;\n\n/** The three sections whose entries are slots. */\nexport type AsyncapiSection = \"channels\" | \"operations\" | \"components.messages\";\n\n/** One slot of an AsyncAPI document, as the identity layer sees it. */\nexport interface AsyncapiSlot {\n section: AsyncapiSection;\n /** The entry's key under its section — the slot half of the identity pair. */\n key: string;\n /** The slot's resolved value, pin and marker still in place. */\n node: unknown;\n /** True when the value carries `x-loam-remove: true` — a feature retiring the slot. */\n remove: boolean;\n /** The `x-loam-based-on` the value declares, or undefined — non-strings stringified, so the gate can refuse them. */\n basedOn?: string;\n /** `slotDigest` of the value as it stands in ITS OWN document. */\n digest: string;\n}\n\n/** Is this slot value a feature-only explicit removal marker? One test for both axes. */\nexport const isSlotRemoval = isRemoval;\n\n/** The `x-loam-based-on` a slot declares, or undefined — `operationBaselineOf`, aliased. */\nexport const slotBaselineOf = operationBaselineOf;\n\n/**\n * The slot without its own baseline marker — what a digest is taken over. A\n * pin is a statement ABOUT a delta, never part of the slot it describes:\n * inside the digest input, no baseline could ever be self-consistent. The\n * OpenAPI rule, aliased — the key is the same key.\n */\nexport const withoutSlotBaseline = withoutOperationBaseline;\n\n/**\n * The identity of a slot's CONTENT: sha256 of its canonical form, pin\n * excluded. `operationDigest` under this axis's name — the same canonical\n * JSON, the same cut, so a pin either axis writes means the same thing.\n */\nexport const slotDigest = operationDigest;\n\n/** Entries of a mapping node, or nothing — the walker's one structural read. */\nfunction entriesOf(node: unknown): [string, unknown][] {\n if (node === null || typeof node !== \"object\" || Array.isArray(node)) return [];\n return Object.entries(node as Record<string, unknown>);\n}\n\n/**\n * The document path of one slot — where the pin and the merge write. Spelled\n * here so `rebase`'s stamp and the merge's upsert cannot disagree about which\n * node a (section, key) pair addresses.\n */\nexport function slotPath(section: AsyncapiSection, key: string): string[] {\n return section === \"components.messages\" ? [\"components\", \"messages\", key] : [section, key];\n}\n\n/**\n * Every path whose node carries `x-loam-remove: true`, at ANY depth — the\n * three slot depths the format spec gives the key meaning at, inline channel\n * messages, and every place the key means nothing at all: the document root,\n * `info`, a `components` sibling, the inside of a payload. The reader's\n * `markers` field is built from this walk so `asyncapi.remove-marker-living`\n * can NAME a marker wherever one leaked: the strip removes the loam keys at\n * document depth, and a sweep narrower than the strip is how the OpenAPI\n * axis once published a marker invisible even to validate.\n */\nexport function removalMarkerPaths(node: unknown, at: string): string[] {\n if (node === null || typeof node !== \"object\") return [];\n const out: string[] = [];\n if (isSlotRemoval(node)) out.push(at === \"\" ? \"(document root)\" : at);\n const children = Array.isArray(node)\n ? node.map((value, i) => [String(i), value] as const)\n : Object.entries(node as Record<string, unknown>);\n for (const [key, value] of children) {\n out.push(...removalMarkerPaths(value, at === \"\" ? key : `${at}.${key}`));\n }\n return out;\n}\n\n/**\n * Every slot a parsed document declares, in document order across the three\n * sections. Inline channel messages are NOT visited: they are interior\n * content of their channel's slot (the header carries the decision), so a\n * pin or marker nested on one is part of the `channels.<ck>` value this\n * walker already returns — never a slot of its own.\n */\nexport function asyncapiSlots(root: unknown): AsyncapiSlot[] {\n if (root === null || typeof root !== \"object\" || Array.isArray(root)) return [];\n const doc = root as Record<string, unknown>;\n const slots: AsyncapiSlot[] = [];\n const walk = (section: AsyncapiSection, mapping: unknown): void => {\n for (const [key, node] of entriesOf(mapping)) {\n slots.push({\n section,\n key,\n node,\n remove: isSlotRemoval(node),\n digest: slotDigest(node),\n ...(slotBaselineOf(node) === undefined ? {} : { basedOn: slotBaselineOf(node) }),\n });\n }\n };\n walk(\"channels\", doc[\"channels\"]);\n walk(\"operations\", doc[\"operations\"]);\n walk(\"components.messages\", (doc[\"components\"] as Record<string, unknown> | undefined)?.[\"messages\"]);\n return slots;\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SURFACE half of the event merge: every `components/<kind>/<name>` the
|
|
3
|
+
* feature declares outside `messages`, folded into the living contract by the
|
|
4
|
+
* same verdict the slot loop takes.
|
|
5
|
+
*
|
|
6
|
+
* Verdict-driven and nothing else. A surface is classified against its
|
|
7
|
+
* `x-loam-baselines` entry exactly as a slot is classified against its
|
|
8
|
+
* in-value pin, so a QUOTE is skipped whatever landed on the living value
|
|
9
|
+
* since, and an EDIT is written. There is deliberately no reachability
|
|
10
|
+
* machinery here — the OpenAPI closure parks a genuinely new component until
|
|
11
|
+
* something written reaches it, because on that axis a quoted OPERATION can
|
|
12
|
+
* drag its authoring-time component copy along. This axis has no such carrier:
|
|
13
|
+
* a surface is only ever copied because the delta declares it, and the ref
|
|
14
|
+
* discipline is the post-hoc `danglingRefs` diff the merge runs over the whole
|
|
15
|
+
* merged tree (../depth.ts) rather than the closure's queued `{ref, from}`.
|
|
16
|
+
* Borrowing the parked set would mean a schema the author wrote on purpose
|
|
17
|
+
* silently not merging, which is the defect this module closes.
|
|
18
|
+
*/
|
|
19
|
+
import { isDeepStrictEqual } from "node:util";
|
|
20
|
+
import { entryFor, surfaceIn } from "../../openapi/baseline/record.js";
|
|
21
|
+
import { classifyBaselineDigests, valueDigest, withoutFeatureKeysDeep } from "../../openapi/digest.js";
|
|
22
|
+
// The OpenAPI axis's component writer, imported rather than respelled: it is
|
|
23
|
+
// path-agnostic and string-key-matched, so a numeric component name (`404:` is
|
|
24
|
+
// the YAML number) is REPLACED rather than appended a second time — the same
|
|
25
|
+
// hazard every slot writer on this axis goes through `slotPair` for.
|
|
26
|
+
import { setComponentValue } from "../../openapi/merge/components.js";
|
|
27
|
+
import { asyncapiSurfaces } from "../baseline/surfaces.js";
|
|
28
|
+
import { writableSection } from "./markers.js";
|
|
29
|
+
/**
|
|
30
|
+
* Merge the feature's component surfaces into the living document by verdict:
|
|
31
|
+
*
|
|
32
|
+
* - quote → skipped; the living copy stays, whatever landed on it since.
|
|
33
|
+
* - edit, or stale (under `--approve`) → copied, and reported.
|
|
34
|
+
* - unpinned and living-equal → skipped: there is nothing to write, and a
|
|
35
|
+
* never-rebased delta that merely restates the contract must not churn its
|
|
36
|
+
* bytes (the slot loop's rule, at surface depth).
|
|
37
|
+
* - unpinned and differing → copied, the old upsert kept for never-rebased
|
|
38
|
+
* deltas, and reported as a modification like any other overwrite.
|
|
39
|
+
* - unfounded (pinned, living gone — the gate refused; `--approve`) and NEW
|
|
40
|
+
* (unpinned, living-absent) → copied. Both write a surface the living
|
|
41
|
+
* contract does not have, which is the ordinary shape of a delta that adds
|
|
42
|
+
* a schema.
|
|
43
|
+
*
|
|
44
|
+
* Every write goes through the deep strip first: a component's value can nest
|
|
45
|
+
* loam's keys anywhere (a schema `properties` entry, a `$ref` sibling), and
|
|
46
|
+
* publishing one into a living contract is the failure `--approve` already
|
|
47
|
+
* caused once on the OpenAPI axis.
|
|
48
|
+
*/
|
|
49
|
+
export function mergeAsyncapiComponents(input) {
|
|
50
|
+
const { living, featPlain, livingPlain, record, service } = input;
|
|
51
|
+
const componentsModified = [];
|
|
52
|
+
const componentsQuoted = [];
|
|
53
|
+
const componentsStale = [];
|
|
54
|
+
const copies = [];
|
|
55
|
+
// ONE enumeration (../baseline/surfaces.ts) with the rebase plan and the
|
|
56
|
+
// gate, and ONE living lookup (surfaceIn), so "which surfaces does this
|
|
57
|
+
// delta declare, and does living have this one" cannot have two answers.
|
|
58
|
+
for (const surface of asyncapiSurfaces(featPlain)) {
|
|
59
|
+
// The first `/` is the kind boundary, exactly as `surfaceIn` reads it back
|
|
60
|
+
// — a component name cannot legally contain one.
|
|
61
|
+
const cut = surface.id.indexOf("/");
|
|
62
|
+
const inLiving = surfaceIn(livingPlain, surface);
|
|
63
|
+
const verdict = classifyBaselineDigests(entryFor(record, surface), valueDigest(surface.value), inLiving.found ? valueDigest(inLiving.value) : undefined);
|
|
64
|
+
// A QUOTE is not a merge input, on this axis for the slot loop's reason:
|
|
65
|
+
// the author wrote the surface down because an AsyncAPI delta is a
|
|
66
|
+
// COMPLETE document, not because they changed it. Mechanical, not a
|
|
67
|
+
// judgement — `--approve` does not turn it back on.
|
|
68
|
+
if (verdict === "quote") {
|
|
69
|
+
componentsQuoted.push(surface.id);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (verdict === "stale")
|
|
73
|
+
componentsStale.push(surface.id);
|
|
74
|
+
const publish = withoutFeatureKeysDeep(surface.value);
|
|
75
|
+
if (inLiving.found && isDeepStrictEqual(inLiving.value, publish))
|
|
76
|
+
continue;
|
|
77
|
+
if (inLiving.found)
|
|
78
|
+
componentsModified.push(surface.id);
|
|
79
|
+
copies.push({ kind: surface.id.slice(0, cut), name: surface.id.slice(cut + 1), value: publish });
|
|
80
|
+
}
|
|
81
|
+
for (const { kind, name, value } of copies) {
|
|
82
|
+
// Guarded at the write, like every slot write: a living `components:
|
|
83
|
+
// *alias` (or an aliased `components.schemas`) is one shared value behind
|
|
84
|
+
// every use of the anchor, so writing a surface through it would rewrite
|
|
85
|
+
// every other node that aliases it.
|
|
86
|
+
writableSection(living, livingPlain, ["components", kind], service);
|
|
87
|
+
setComponentValue(living, kind, name, value);
|
|
88
|
+
}
|
|
89
|
+
return { componentsModified, componentsQuoted, componentsStale, copied: copies.length > 0 };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=components.js.map
|