@ybotok/loam 0.2.0-alpha.7 → 0.2.0-alpha.9
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 +1315 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +83 -7
- package/SCHEMA.md +569 -98
- package/WORKFLOW.md +272 -62
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +33 -11
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/explore.js +4 -8
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +28 -33
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +21 -25
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +3 -7
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +3 -5
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +5 -8
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +14 -41
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +17 -38
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +32 -5
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +23 -23
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +6 -10
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +1 -3
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +8 -28
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/report.js +5 -4
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +16 -104
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +1 -1
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +54 -51
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +56 -101
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +4 -3
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +32 -18
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +9 -3
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +20 -16
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +50 -18
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +6 -18
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +15 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/documents.js +31 -6
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +6 -17
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/findings.js +15 -20
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/coherence.js +30 -16
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +119 -8
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +3 -6
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +28 -16
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +5 -2
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +4 -2
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +5 -36
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +22 -8
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +56 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +2 -16
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/repo.js +24 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/staging/commit.js +10 -12
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +174 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +3 -0
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +12 -2
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +21 -6
- package/examples/docs/architecture/landscape.likec4 +8 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What `loam rebase` writes on the event axis's SURFACE half: the
|
|
3
3
|
* `x-loam-baselines` record, rebuilt wholesale from what is on disk right now
|
|
4
|
-
* — `core/openapi/baseline/plan.ts`'s
|
|
5
|
-
* of path items and components.
|
|
4
|
+
* — `core/openapi/baseline/plan.ts`'s planner itself, over component surfaces
|
|
5
|
+
* instead of path items and components.
|
|
6
6
|
*
|
|
7
7
|
* A ROOT record rather than an in-value pin, and that is not symmetry for its
|
|
8
8
|
* own sake: a slot value is always a mapping, so `x-loam-based-on` has
|
|
@@ -17,94 +17,27 @@
|
|
|
17
17
|
* a claim nobody is making any more. The sorted serialization (`buildRecord`)
|
|
18
18
|
* is what makes a second run byte-identical.
|
|
19
19
|
*
|
|
20
|
+
* The planner is SHARED, not mirrored: it used to be a line-for-line copy of
|
|
21
|
+
* the OpenAPI one minus the path-item arm, and the only real difference was
|
|
22
|
+
* which walk enumerates the surfaces. `asyncapiSurfaces` is that walk, so this
|
|
23
|
+
* module passes it and owns nothing else.
|
|
24
|
+
*
|
|
20
25
|
* This module must not import from `../merge/`: the merge reads this package,
|
|
21
26
|
* so a value edge back would be a package cycle. That is also why an
|
|
22
27
|
* unreadable document is answered with "nothing to pin" rather than an
|
|
23
28
|
* `AsyncapiMergeError` — every caller chains this AFTER `pinAsyncapiSlots`,
|
|
24
29
|
* which has already thrown that error for a document either side cannot parse.
|
|
25
30
|
*/
|
|
26
|
-
import {
|
|
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";
|
|
31
|
+
import { planOpenapiBaselines } from "../../openapi/baseline/plan.js";
|
|
32
32
|
import { asyncapiSurfaces } from "./surfaces.js";
|
|
33
33
|
/**
|
|
34
34
|
* Pin every component surface of a feature event contract against the living
|
|
35
|
-
* one
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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.
|
|
35
|
+
* one — every `kind` in the answer is `"component"`, since an event contract
|
|
36
|
+
* has no path items. The rules (the LIVING digest, `unresolved` for a surface
|
|
37
|
+
* the living contract lacks, the idempotent rewrite) are the shared planner's,
|
|
38
|
+
* stated there once.
|
|
44
39
|
*/
|
|
45
|
-
export function planAsyncapiBaselines(featureText, livingText
|
|
46
|
-
|
|
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 };
|
|
40
|
+
export function planAsyncapiBaselines(featureText, livingText) {
|
|
41
|
+
return planOpenapiBaselines(featureText, livingText, asyncapiSurfaces);
|
|
109
42
|
}
|
|
110
43
|
//# sourceMappingURL=plan.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/baseline/plan.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/baseline/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,oBAAoB,EAA4B,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,UAAkB;IAC3E,OAAO,oBAAoB,CAAC,WAAW,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;AACzE,CAAC","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 planner itself, over component surfaces\n * instead 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 * The planner is SHARED, not mirrored: it used to be a line-for-line copy of\n * the OpenAPI one minus the path-item arm, and the only real difference was\n * which walk enumerates the surfaces. `asyncapiSurfaces` is that walk, so this\n * module passes it and owns nothing else.\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 { planOpenapiBaselines, type OpenapiBaselinePlan } from \"../../openapi/baseline/plan.js\";\nimport { asyncapiSurfaces } from \"./surfaces.js\";\n\n/**\n * Pin every component surface of a feature event contract against the living\n * one — every `kind` in the answer is `\"component\"`, since an event contract\n * has no path items. The rules (the LIVING digest, `unresolved` for a surface\n * the living contract lacks, the idempotent rewrite) are the shared planner's,\n * stated there once.\n */\nexport function planAsyncapiBaselines(featureText: string, livingText: string): OpenapiBaselinePlan {\n return planOpenapiBaselines(featureText, livingText, asyncapiSurfaces);\n}\n"]}
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
* is content identity (a changed payload moves it), never a join on payload
|
|
29
29
|
* fields — read.ts's header carries the full doctrine.
|
|
30
30
|
*/
|
|
31
|
-
import { isRemoval, OPENAPI_BASELINE_KEY, OPENAPI_BASELINES_KEY,
|
|
31
|
+
import { isRemoval, OPENAPI_BASELINE_KEY, OPENAPI_BASELINES_KEY, operationBaselineOf, operationDigest, } from "../openapi/digest.js";
|
|
32
32
|
/**
|
|
33
33
|
* The baseline marker: which living version of a slot a FEATURE delta was
|
|
34
34
|
* written against. The OpenAPI axis's key, not a restatement of its
|
|
35
|
-
* spelling, because a feature's asyncapi.yaml
|
|
36
|
-
* the same reason its openapi.yaml
|
|
37
|
-
*
|
|
38
|
-
* QUOTE from an EDIT.
|
|
35
|
+
* spelling, because a feature's asyncapi.yaml may restate living slots it
|
|
36
|
+
* does not change for the same reason its openapi.yaml may — it needs only
|
|
37
|
+
* the slots it adds, changes or removes, but quoting more is legal — and the
|
|
38
|
+
* pin is what lets the merge tell a QUOTE from an EDIT.
|
|
39
39
|
*/
|
|
40
40
|
export const ASYNCAPI_BASELINE_KEY = OPENAPI_BASELINE_KEY;
|
|
41
41
|
/**
|
|
@@ -52,39 +52,44 @@ export const ASYNCAPI_BASELINE_KEY = OPENAPI_BASELINE_KEY;
|
|
|
52
52
|
* own `true` is a legal component that no in-value key survives at all.
|
|
53
53
|
*/
|
|
54
54
|
export const ASYNCAPI_BASELINES_KEY = OPENAPI_BASELINES_KEY;
|
|
55
|
-
/**
|
|
56
|
-
|
|
55
|
+
/**
|
|
56
|
+
* A slot's identity as one map key — the (section, key) pair, NUL-joined: no
|
|
57
|
+
* section name or YAML key a slot walk returns can contain it. Spelled once,
|
|
58
|
+
* because the gate's walk, the rebase revival read, the marker and baseline
|
|
59
|
+
* graders, the pin planner and the merge all build or look up the same map,
|
|
60
|
+
* and a lookup spelled differently from its build finds nothing without
|
|
61
|
+
* failing.
|
|
62
|
+
*/
|
|
63
|
+
export function slotKey(slot) {
|
|
64
|
+
return `${slot.section}\0${slot.key}`;
|
|
65
|
+
}
|
|
66
|
+
/** Slots by `slotKey` — the living side of every slot judgement. */
|
|
67
|
+
export function slotsByKey(slots) {
|
|
68
|
+
return new Map(slots.map((slot) => [slotKey(slot), slot]));
|
|
69
|
+
}
|
|
57
70
|
/** Is this slot value a feature-only explicit removal marker? One test for both axes. */
|
|
58
71
|
export const isSlotRemoval = isRemoval;
|
|
59
72
|
/** The `x-loam-based-on` a slot declares, or undefined — `operationBaselineOf`, aliased. */
|
|
60
73
|
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
74
|
/**
|
|
69
75
|
* The identity of a slot's CONTENT: sha256 of its canonical form, pin
|
|
70
76
|
* 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.
|
|
77
|
+
* JSON, the same cut, so a pin either axis writes means the same thing. The
|
|
78
|
+
* pin is cut because it is a statement ABOUT a delta, never part of the slot
|
|
79
|
+
* it describes: inside the digest input, no baseline could ever be
|
|
80
|
+
* self-consistent (`withoutOperationBaseline` in ../openapi/digest.ts has the
|
|
81
|
+
* whole argument).
|
|
72
82
|
*/
|
|
73
83
|
export const slotDigest = operationDigest;
|
|
74
|
-
/**
|
|
75
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Entries of a mapping node, or nothing — the walker's one structural read,
|
|
86
|
+
* and every structural read the reader (./read.ts) makes goes through it too.
|
|
87
|
+
*/
|
|
88
|
+
export function entriesOf(node) {
|
|
76
89
|
if (node === null || typeof node !== "object" || Array.isArray(node))
|
|
77
90
|
return [];
|
|
78
91
|
return Object.entries(node);
|
|
79
92
|
}
|
|
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
93
|
/**
|
|
89
94
|
* Every path whose node carries `x-loam-remove: true`, at ANY depth — the
|
|
90
95
|
* three slot depths the format spec gives the key meaning at, inline channel
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"digest.js","sourceRoot":"","sources":["../../../src/core/asyncapi/digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D,oFAAoF;AACpF,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAoBtD,yFAAyF;AACzF,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEvC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,gFAAgF;AAChF,SAAS,SAAS,CAAC,IAAa;IAC9B,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAChF,OAAO,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAwB,EAAE,GAAW;IAC5D,OAAO,OAAO,KAAK,qBAAqB,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAa,EAAE,EAAU;IAC1D,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAU,CAAC;QACrD,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;QACpC,GAAG,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAChF,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,CAAC,OAAwB,EAAE,OAAgB,EAAQ,EAAE;QAChE,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC;gBACT,OAAO;gBACP,GAAG;gBACH,IAAI;gBACJ,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC;gBAC3B,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC;gBACxB,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;aACjF,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,qBAAqB,EAAG,GAAG,CAAC,YAAY,CAAyC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IACtG,OAAO,KAAK,CAAC;AACf,CAAC","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"]}
|
|
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 operationBaselineOf,\n operationDigest,\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 may restate living slots it\n * does not change for the same reason its openapi.yaml may — it needs only\n * the slots it adds, changes or removes, but quoting more is legal — and the\n * pin is what lets the merge tell a 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 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/**\n * A slot's identity as one map key — the (section, key) pair, NUL-joined: no\n * section name or YAML key a slot walk returns can contain it. Spelled once,\n * because the gate's walk, the rebase revival read, the marker and baseline\n * graders, the pin planner and the merge all build or look up the same map,\n * and a lookup spelled differently from its build finds nothing without\n * failing.\n */\nexport function slotKey(slot: { section: AsyncapiSection; key: string }): string {\n return `${slot.section}\\0${slot.key}`;\n}\n\n/** Slots by `slotKey` — the living side of every slot judgement. */\nexport function slotsByKey(slots: readonly AsyncapiSlot[]): Map<string, AsyncapiSlot> {\n return new Map(slots.map((slot) => [slotKey(slot), slot]));\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 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. The\n * pin is cut because it is a statement ABOUT a delta, never part of the slot\n * it describes: inside the digest input, no baseline could ever be\n * self-consistent (`withoutOperationBaseline` in ../openapi/digest.ts has the\n * whole argument).\n */\nexport const slotDigest = operationDigest;\n\n/**\n * Entries of a mapping node, or nothing — the walker's one structural read,\n * and every structural read the reader (./read.ts) makes goes through it too.\n */\nexport function 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 * 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"]}
|
|
@@ -19,11 +19,12 @@
|
|
|
19
19
|
import { isDeepStrictEqual } from "node:util";
|
|
20
20
|
import { entryFor, surfaceIn } from "../../openapi/baseline/record.js";
|
|
21
21
|
import { classifyBaselineDigests, valueDigest, withoutFeatureKeysDeep } from "../../openapi/digest.js";
|
|
22
|
-
// The OpenAPI axis's
|
|
22
|
+
// The OpenAPI axis's keyed writer, imported rather than respelled: it is
|
|
23
23
|
// path-agnostic and string-key-matched, so a numeric component name (`404:` is
|
|
24
24
|
// the YAML number) is REPLACED rather than appended a second time — the same
|
|
25
|
-
// hazard every slot
|
|
26
|
-
|
|
25
|
+
// hazard every slot write on this axis goes through it for, and every slot
|
|
26
|
+
// read through `slotPair`.
|
|
27
|
+
import { setKeyed } from "../../openapi/merge/components.js";
|
|
27
28
|
import { asyncapiSurfaces } from "../baseline/surfaces.js";
|
|
28
29
|
import { writableSection } from "./markers.js";
|
|
29
30
|
/**
|
|
@@ -84,7 +85,7 @@ export function mergeAsyncapiComponents(input) {
|
|
|
84
85
|
// every use of the anchor, so writing a surface through it would rewrite
|
|
85
86
|
// every other node that aliases it.
|
|
86
87
|
writableSection(living, livingPlain, ["components", kind], service);
|
|
87
|
-
|
|
88
|
+
setKeyed(living, ["components", kind], name, value);
|
|
88
89
|
}
|
|
89
90
|
return { componentsModified, componentsQuoted, componentsStale, copied: copies.length > 0 };
|
|
90
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/components.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAuB,MAAM,kCAAkC,CAAC;AAC5F,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACvG,
|
|
1
|
+
{"version":3,"file":"components.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/components.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The SURFACE half of the event merge: every `components/<kind>/<name>` the\n * feature declares outside `messages`, folded into the living contract by the\n * same verdict the slot loop takes.\n *\n * Verdict-driven and nothing else. A surface is classified against its\n * `x-loam-baselines` entry exactly as a slot is classified against its\n * in-value pin, so a QUOTE is skipped whatever landed on the living value\n * since, and an EDIT is written. There is deliberately no reachability\n * machinery here — the OpenAPI closure parks a genuinely new component until\n * something written reaches it, because on that axis a quoted OPERATION can\n * drag its authoring-time component copy along. This axis has no such carrier:\n * a surface is only ever copied because the delta declares it, and the ref\n * discipline is the post-hoc `danglingRefs` diff the merge runs over the whole\n * merged tree (../depth.ts) rather than the closure's queued `{ref, from}`.\n * Borrowing the parked set would mean a schema the author wrote on purpose\n * silently not merging, which is the defect this module closes.\n */\nimport { isDeepStrictEqual } from \"node:util\";\nimport type { Document } from \"yaml\";\nimport { entryFor, surfaceIn, type BaselineRecord } from \"../../openapi/baseline/record.js\";\nimport { classifyBaselineDigests, valueDigest, withoutFeatureKeysDeep } from \"../../openapi/digest.js\";\n// The OpenAPI axis's keyed writer, imported rather than respelled: it is\n// path-agnostic and string-key-matched, so a numeric component name (`404:` is\n// the YAML number) is REPLACED rather than appended a second time — the same\n// hazard every slot write on this axis goes through it for, and every slot\n// read through `slotPair`.\nimport { setKeyed } from \"../../openapi/merge/components.js\";\nimport { asyncapiSurfaces } from \"../baseline/surfaces.js\";\nimport { writableSection } from \"./markers.js\";\n\n/** Everything the surface merge needs; one object because the merge is its only caller. */\nexport interface AsyncapiComponentsInput {\n /** The living document the copies are written into. */\n living: Document;\n featPlain: unknown;\n livingPlain: unknown;\n /** The feature's `x-loam-baselines` record, read once by the merge. */\n record: BaselineRecord;\n service: string;\n}\n\n/** What the surface merge computed. */\nexport interface AsyncapiComponentsOutcome {\n /** `<kind>/<name>` of living surfaces overwritten with different content. */\n componentsModified: string[];\n /** Surfaces the delta QUOTED — not copied, living's copy kept. */\n componentsQuoted: string[];\n /** Surfaces written on a stale record entry — under `--approve`, like the slots. */\n componentsStale: string[];\n /**\n * Whether anything was actually written. The merge turns this into `edited`:\n * without it the surface verdicts would be computed, reported, and then\n * dropped on the floor by the `!edited` early return that keeps an all-quote\n * delta byte-identical — the defect surviving in a new shape, with the plan\n * naming components it never wrote.\n */\n copied: boolean;\n}\n\n/**\n * Merge the feature's component surfaces into the living document by verdict:\n *\n * - quote → skipped; the living copy stays, whatever landed on it since.\n * - edit, or stale (under `--approve`) → copied, and reported.\n * - unpinned and living-equal → skipped: there is nothing to write, and a\n * never-rebased delta that merely restates the contract must not churn its\n * bytes (the slot loop's rule, at surface depth).\n * - unpinned and differing → copied, the old upsert kept for never-rebased\n * deltas, and reported as a modification like any other overwrite.\n * - unfounded (pinned, living gone — the gate refused; `--approve`) and NEW\n * (unpinned, living-absent) → copied. Both write a surface the living\n * contract does not have, which is the ordinary shape of a delta that adds\n * a schema.\n *\n * Every write goes through the deep strip first: a component's value can nest\n * loam's keys anywhere (a schema `properties` entry, a `$ref` sibling), and\n * publishing one into a living contract is the failure `--approve` already\n * caused once on the OpenAPI axis.\n */\nexport function mergeAsyncapiComponents(input: AsyncapiComponentsInput): AsyncapiComponentsOutcome {\n const { living, featPlain, livingPlain, record, service } = input;\n const componentsModified: string[] = [];\n const componentsQuoted: string[] = [];\n const componentsStale: string[] = [];\n const copies: Array<{ kind: string; name: string; value: unknown }> = [];\n\n // ONE enumeration (../baseline/surfaces.ts) with the rebase plan and the\n // gate, and ONE living lookup (surfaceIn), so \"which surfaces does this\n // delta declare, and does living have this one\" cannot have two answers.\n for (const surface of asyncapiSurfaces(featPlain)) {\n // The first `/` is the kind boundary, exactly as `surfaceIn` reads it back\n // — a component name cannot legally contain one.\n const cut = surface.id.indexOf(\"/\");\n const inLiving = surfaceIn(livingPlain, surface);\n const verdict = classifyBaselineDigests(\n entryFor(record, surface),\n valueDigest(surface.value),\n inLiving.found ? valueDigest(inLiving.value) : undefined,\n );\n // A QUOTE is not a merge input, on this axis for the slot loop's reason:\n // the author wrote the surface down because an AsyncAPI delta is a\n // COMPLETE document, not because they changed it. Mechanical, not a\n // judgement — `--approve` does not turn it back on.\n if (verdict === \"quote\") {\n componentsQuoted.push(surface.id);\n continue;\n }\n if (verdict === \"stale\") componentsStale.push(surface.id);\n const publish = withoutFeatureKeysDeep(surface.value);\n if (inLiving.found && isDeepStrictEqual(inLiving.value, publish)) continue;\n if (inLiving.found) componentsModified.push(surface.id);\n copies.push({ kind: surface.id.slice(0, cut), name: surface.id.slice(cut + 1), value: publish });\n }\n\n for (const { kind, name, value } of copies) {\n // Guarded at the write, like every slot write: a living `components:\n // *alias` (or an aliased `components.schemas`) is one shared value behind\n // every use of the anchor, so writing a surface through it would rewrite\n // every other node that aliases it.\n writableSection(living, livingPlain, [\"components\", kind], service);\n setKeyed(living, [\"components\", kind], name, value);\n }\n\n return { componentsModified, componentsQuoted, componentsStale, copied: copies.length > 0 };\n}\n"]}
|
|
@@ -27,7 +27,12 @@ export class AsyncapiMergeError extends Error {
|
|
|
27
27
|
this.service = service;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The `fail` the shared tree helpers take (../../openapi/contract-tree.ts), for
|
|
32
|
+
* one side of this axis — always the default "is not valid YAML" problem,
|
|
33
|
+
* because every failure they report is a parse or a serialization.
|
|
34
|
+
*/
|
|
35
|
+
export function asyncapiRefusal(source, service) {
|
|
36
|
+
return (detail) => new AsyncapiMergeError(source, service, detail);
|
|
32
37
|
}
|
|
33
38
|
//# sourceMappingURL=error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;;;;GAQG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAIhC,MAAM;IACN,OAAO;IAJA,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YACW,MAA2B,EAC3B,OAAe,EACxB,MAAc,EACd,OAAO,GAAW,mBAAmB;QAErC,KAAK,CAAC,GAAG,MAAM,iBAAiB,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC,CAAC;sBALxD,MAAM;uBACN,OAAO;IAKlB,CAAC;CACF;AAED,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;;;;GAQG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAIhC,MAAM;IACN,OAAO;IAJA,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YACW,MAA2B,EAC3B,OAAe,EACxB,MAAc,EACd,OAAO,GAAW,mBAAmB;QAErC,KAAK,CAAC,GAAG,MAAM,iBAAiB,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC,CAAC;sBALxD,MAAM;uBACN,OAAO;IAKlB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAA2B,EAAE,OAAe;IAC1E,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACrE,CAAC","sourcesContent":["/**\n * The one refusal every stage of the event-contract delta path raises: a\n * document the planner cannot read, named by which side it came from.\n *\n * The mirror of ../../openapi/merge/error.ts, and the package's leaf for the\n * same reason: whichever sibling fails — the pin, the markers or the merge —\n * the command above has to map one domain error onto its own envelope, and a\n * module that owned the error would be imported by its siblings for that\n * reason alone.\n */\n\nexport type AsyncapiMergeSource = \"feature\" | \"living\";\n\n/**\n * A document the merge machinery cannot use. Commands map this domain error\n * to their own envelope. `problem` is the verb phrase between the document's\n * name and the detail — \"is not valid YAML\" for the parse failures that are\n * the common case, overridden where the document parses fine and the refusal\n * is about what it says (a section spelling one slot key twice is legal YAML,\n * and calling it invalid would send the author to a linter that agrees with\n * the file).\n */\nexport class AsyncapiMergeError extends Error {\n override readonly name = \"AsyncapiMergeError\";\n\n constructor(\n readonly source: AsyncapiMergeSource,\n readonly service: string,\n detail: string,\n problem: string = \"is not valid YAML\",\n ) {\n super(`${source} asyncapi for ${service} ${problem}: ${detail}`);\n }\n}\n\n/**\n * The `fail` the shared tree helpers take (../../openapi/contract-tree.ts), for\n * one side of this axis — always the default \"is not valid YAML\" problem,\n * because every failure they report is a parse or a serialization.\n */\nexport function asyncapiRefusal(source: AsyncapiMergeSource, service: string): (detail: string) => AsyncapiMergeError {\n return (detail) => new AsyncapiMergeError(source, service, detail);\n}\n"]}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* every section, and a strip that walked only the three sections shipped
|
|
20
20
|
* both into a living contract, unseen even by validate's marker sweep.
|
|
21
21
|
*/
|
|
22
|
-
import { isMap, isScalar
|
|
22
|
+
import { isMap, isScalar } from "yaml";
|
|
23
23
|
// Every `isRecord` below asks one question of a resolved plain tree: is this a
|
|
24
24
|
// node that can hold AsyncAPI keys — an object, not an array?
|
|
25
25
|
import { isRecord } from "../../kernel/records.js";
|
|
@@ -29,32 +29,21 @@ import { isRecord } from "../../kernel/records.js";
|
|
|
29
29
|
// rule (the openapi digest module's own failure mode).
|
|
30
30
|
import { FEATURE_ONLY_KEYS, withoutFeatureKeysDeep } from "../../openapi/digest.js";
|
|
31
31
|
import { ASYNCAPI_BASELINES_KEY, isSlotRemoval } from "../digest.js";
|
|
32
|
-
import {
|
|
32
|
+
import { pairAt, setKeyed } from "../../openapi/merge/components.js";
|
|
33
|
+
import { readTree, writeTree } from "../../openapi/contract-tree.js";
|
|
34
|
+
import { AsyncapiMergeError, asyncapiRefusal } from "./error.js";
|
|
33
35
|
/** The three slot sections, in document order — every whole-document walk here goes over them. */
|
|
34
36
|
const SECTIONS = ["channels", "operations", "components.messages"];
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* slot that survives.
|
|
42
|
-
*/
|
|
43
|
-
export function withoutFeatureMarkers(node) {
|
|
44
|
-
return withoutFeatureKeysDeep(node);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* The AST path of a section's own mapping — `slotPath` (../digest.ts) minus
|
|
48
|
-
* the slot key, spelled beside the writers so the strip and the merge cannot
|
|
49
|
-
* disagree about which node a section name addresses.
|
|
38
|
+
* The AST path of a section's own mapping — two levels deep for
|
|
39
|
+
* `components.messages`, one for the other two — spelled beside the writers so
|
|
40
|
+
* the strip and the merge cannot disagree about which node a section name
|
|
41
|
+
* addresses. A slot's own path is this plus its key; the `slotPath` helper that
|
|
42
|
+
* once spelled that in ../digest.ts had no caller and was deleted.
|
|
50
43
|
*/
|
|
51
44
|
export function sectionAstPath(section) {
|
|
52
45
|
return section === "components.messages" ? ["components", "messages"] : [section];
|
|
53
46
|
}
|
|
54
|
-
/** One mapping node's pair for a key, matched AS A STRING — `slotPair`'s find, reusable at any depth. */
|
|
55
|
-
function pairAt(node, key) {
|
|
56
|
-
return isMap(node) ? node.items.find((it) => isScalar(it.key) && String(it.key.value) === key) : undefined;
|
|
57
|
-
}
|
|
58
47
|
/** Delete one pair from a mapping node, string-key-matched. Returns whether a pair actually went. */
|
|
59
48
|
function deleteKeyIn(node, key) {
|
|
60
49
|
if (!isMap(node))
|
|
@@ -71,21 +60,17 @@ function deleteKeyIn(node, key) {
|
|
|
71
60
|
* with the string "404" misses it and APPENDS a second pair — the living
|
|
72
61
|
* contract then declares the slot twice, forever, with the pre-merge copy
|
|
73
62
|
* first in reading order. The openapi component write's lesson, applied to
|
|
74
|
-
* every slot access on this axis: the merge's writers below go through
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* document backed.
|
|
63
|
+
* every slot access on this axis: the merge's writers below go through the
|
|
64
|
+
* same string-matched find (`pairAt`, under `setKeyed`), and so does the pin
|
|
65
|
+
* (./pin.ts), whose raw `getIn` used to grade every non-string-keyed slot
|
|
66
|
+
* `unwritable` with an alias diagnosis nothing in the document backed.
|
|
78
67
|
*/
|
|
79
68
|
export function slotPair(doc, section, key) {
|
|
80
69
|
return pairAt(doc.getIn(sectionAstPath(section)), key);
|
|
81
70
|
}
|
|
82
|
-
/** Write one slot's value
|
|
71
|
+
/** Write one slot's value through `setKeyed`, string-key-matched — the merge's one upsert door. */
|
|
83
72
|
export function setSlotValue(doc, section, key, value) {
|
|
84
|
-
|
|
85
|
-
if (existing !== undefined)
|
|
86
|
-
existing.value = doc.createNode(value);
|
|
87
|
-
else
|
|
88
|
-
doc.setIn([...sectionAstPath(section), key], value);
|
|
73
|
+
setKeyed(doc, sectionAstPath(section), key, value);
|
|
89
74
|
}
|
|
90
75
|
/**
|
|
91
76
|
* Delete one slot's pair, string-key-matched for the numeric-key reason above
|
|
@@ -159,6 +144,17 @@ export function refuseAmbiguousSlotKeys(doc, source, service) {
|
|
|
159
144
|
}
|
|
160
145
|
}
|
|
161
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Read one side of an event contract the way every entry point that walks
|
|
149
|
+
* slots must — the shared `readTree`, with the ambiguous-key refusal above run
|
|
150
|
+
* on the AST before the aliases resolve. Two pairs whose keys stringify
|
|
151
|
+
* identically (404 and "404") would make the resolved walk and the
|
|
152
|
+
* string-matched writers disagree about which node a slot names, so it is
|
|
153
|
+
* refused on either side, before any verdict is taken.
|
|
154
|
+
*/
|
|
155
|
+
export function readEventTree(text, source, service) {
|
|
156
|
+
return readTree(text, asyncapiRefusal(source, service), (doc) => refuseAmbiguousSlotKeys(doc, source, service));
|
|
157
|
+
}
|
|
162
158
|
/**
|
|
163
159
|
* Remove feature-only markers — removal-marker slots, the pins on the slots
|
|
164
160
|
* that survive, and both loam keys at every nested depth of the whole
|
|
@@ -177,20 +173,9 @@ export function refuseAmbiguousSlotKeys(doc, source, service) {
|
|
|
177
173
|
* discipline, per section.
|
|
178
174
|
*/
|
|
179
175
|
export function stripAsyncapiMarkers(featureText, service) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
refuseAmbiguousSlotKeys(feature, "feature", service);
|
|
185
|
-
let plain;
|
|
186
|
-
try {
|
|
187
|
-
// Resolve aliases once with the document's own anchor context — an
|
|
188
|
-
// individual node's toJSON() loses it (the openapi merge's lesson).
|
|
189
|
-
plain = feature.toJS() ?? {};
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
throw new AsyncapiMergeError("feature", service, errorMessage(error));
|
|
193
|
-
}
|
|
176
|
+
// Aliases resolved once with the document's own anchor context — an
|
|
177
|
+
// individual node's toJSON() loses it (the openapi merge's lesson).
|
|
178
|
+
const { doc: feature, plain } = readEventTree(featureText, "feature", service);
|
|
194
179
|
let stripped = false;
|
|
195
180
|
for (const section of SECTIONS) {
|
|
196
181
|
// Shape from the RESOLVED tree, for the same reason the merge reads it
|
|
@@ -251,12 +236,7 @@ export function stripAsyncapiMarkers(featureText, service) {
|
|
|
251
236
|
}
|
|
252
237
|
if (!stripped)
|
|
253
238
|
return featureText;
|
|
254
|
-
|
|
255
|
-
return feature.toString();
|
|
256
|
-
}
|
|
257
|
-
catch (error) {
|
|
258
|
-
throw new AsyncapiMergeError("feature", service, errorMessage(error));
|
|
259
|
-
}
|
|
239
|
+
return writeTree(feature, asyncapiRefusal("feature", service));
|
|
260
240
|
}
|
|
261
241
|
/**
|
|
262
242
|
* The strip's reach OUTSIDE the three sections: the document root's own
|