@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.
Files changed (648) hide show
  1. package/CHANGELOG.md +1315 -1
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +40 -22
  5. package/QUICKSTART.md +181 -9
  6. package/README.md +256 -57
  7. package/ROADMAP.md +83 -7
  8. package/SCHEMA.md +569 -98
  9. package/WORKFLOW.md +272 -62
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/archive/archive.js +15 -18
  15. package/dist/commands/archive/archive.js.map +1 -1
  16. package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
  17. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  18. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  19. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/openapi.js +25 -42
  21. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
  23. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  24. package/dist/commands/archive/plan/gate.js +3 -7
  25. package/dist/commands/archive/plan/gate.js.map +1 -1
  26. package/dist/commands/archive/plan/landscape.js +4 -4
  27. package/dist/commands/archive/plan/landscape.js.map +1 -1
  28. package/dist/commands/archive/plan/refusal.js +8 -18
  29. package/dist/commands/archive/plan/refusal.js.map +1 -1
  30. package/dist/commands/archive/plan/specs.js +134 -25
  31. package/dist/commands/archive/plan/specs.js.map +1 -1
  32. package/dist/commands/archive/plan/state.js +1 -0
  33. package/dist/commands/archive/plan/state.js.map +1 -1
  34. package/dist/commands/archive/run.js +33 -11
  35. package/dist/commands/archive/run.js.map +1 -1
  36. package/dist/commands/archive/shipped.js +49 -0
  37. package/dist/commands/archive/shipped.js.map +1 -0
  38. package/dist/commands/context/context.js +4 -7
  39. package/dist/commands/context/context.js.map +1 -1
  40. package/dist/commands/context/print.js +2 -2
  41. package/dist/commands/context/print.js.map +1 -1
  42. package/dist/commands/delta/delta.js +44 -12
  43. package/dist/commands/delta/delta.js.map +1 -1
  44. package/dist/commands/delta/print.js +1 -1
  45. package/dist/commands/delta/print.js.map +1 -1
  46. package/dist/commands/dependencies.js +3 -7
  47. package/dist/commands/dependencies.js.map +1 -1
  48. package/dist/commands/diff/diff.js +20 -27
  49. package/dist/commands/diff/diff.js.map +1 -1
  50. package/dist/commands/explore.js +4 -8
  51. package/dist/commands/explore.js.map +1 -1
  52. package/dist/commands/gate/gate.js +45 -8
  53. package/dist/commands/gate/gate.js.map +1 -1
  54. package/dist/commands/gate/print.js +15 -3
  55. package/dist/commands/gate/print.js.map +1 -1
  56. package/dist/commands/gherkin/commit.js +13 -54
  57. package/dist/commands/gherkin/commit.js.map +1 -1
  58. package/dist/commands/gherkin/gherkin.js +36 -20
  59. package/dist/commands/gherkin/gherkin.js.map +1 -1
  60. package/dist/commands/gherkin/reconcile.js +10 -13
  61. package/dist/commands/gherkin/reconcile.js.map +1 -1
  62. package/dist/commands/init/first-hour.js +44 -0
  63. package/dist/commands/init/first-hour.js.map +1 -1
  64. package/dist/commands/init/init.js +18 -22
  65. package/dist/commands/init/init.js.map +1 -1
  66. package/dist/commands/list/campaign/campaign.js +4 -6
  67. package/dist/commands/list/campaign/campaign.js.map +1 -1
  68. package/dist/commands/list/json.js +0 -4
  69. package/dist/commands/list/json.js.map +1 -1
  70. package/dist/commands/list/list.js +38 -23
  71. package/dist/commands/list/list.js.map +1 -1
  72. package/dist/commands/list/print.js +38 -6
  73. package/dist/commands/list/print.js.map +1 -1
  74. package/dist/commands/list/suites/suites.js +140 -0
  75. package/dist/commands/list/suites/suites.js.map +1 -0
  76. package/dist/commands/list/views.js +28 -33
  77. package/dist/commands/list/views.js.map +1 -1
  78. package/dist/commands/mcp/dispatch.js +91 -36
  79. package/dist/commands/mcp/dispatch.js.map +1 -1
  80. package/dist/commands/mcp/mcp.js +56 -15
  81. package/dist/commands/mcp/mcp.js.map +1 -1
  82. package/dist/commands/mcp/serve.js.map +1 -1
  83. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  84. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  85. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  86. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  87. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  88. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  89. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  90. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  91. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  92. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  93. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  94. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  95. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  96. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  97. package/dist/commands/new/capability-template.js +118 -0
  98. package/dist/commands/new/capability-template.js.map +1 -0
  99. package/dist/commands/new/commit.js +8 -22
  100. package/dist/commands/new/commit.js.map +1 -1
  101. package/dist/commands/new/modifies.js +206 -0
  102. package/dist/commands/new/modifies.js.map +1 -0
  103. package/dist/commands/new/new.js +59 -14
  104. package/dist/commands/new/new.js.map +1 -1
  105. package/dist/commands/new/templates.js +48 -124
  106. package/dist/commands/new/templates.js.map +1 -1
  107. package/dist/commands/open.js +3 -7
  108. package/dist/commands/open.js.map +1 -1
  109. package/dist/commands/policy/gate.js +27 -0
  110. package/dist/commands/policy/gate.js.map +1 -1
  111. package/dist/commands/rebase/plan.js +2 -2
  112. package/dist/commands/rebase/plan.js.map +1 -1
  113. package/dist/commands/rebase/rebase.js +60 -35
  114. package/dist/commands/rebase/rebase.js.map +1 -1
  115. package/dist/commands/seed/commit.js +21 -25
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/seed.js +6 -9
  118. package/dist/commands/seed/seed.js.map +1 -1
  119. package/dist/commands/show/show.js +3 -7
  120. package/dist/commands/show/show.js.map +1 -1
  121. package/dist/commands/status/print.js +13 -4
  122. package/dist/commands/status/print.js.map +1 -1
  123. package/dist/commands/status/status.js +3 -7
  124. package/dist/commands/status/status.js.map +1 -1
  125. package/dist/commands/steps/steps.js +3 -7
  126. package/dist/commands/steps/steps.js.map +1 -1
  127. package/dist/commands/subsystem/edit.js +3 -5
  128. package/dist/commands/subsystem/edit.js.map +1 -1
  129. package/dist/commands/subsystem/move.js +5 -8
  130. package/dist/commands/subsystem/move.js.map +1 -1
  131. package/dist/commands/subsystem/query.js +2 -4
  132. package/dist/commands/subsystem/query.js.map +1 -1
  133. package/dist/commands/subsystem/subsystem.js +4 -7
  134. package/dist/commands/subsystem/subsystem.js.map +1 -1
  135. package/dist/commands/subsystem/sync.js +14 -41
  136. package/dist/commands/subsystem/sync.js.map +1 -1
  137. package/dist/commands/subsystem/txn/txn.js +17 -38
  138. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  139. package/dist/commands/subsystem/txn/views.js +32 -5
  140. package/dist/commands/subsystem/txn/views.js.map +1 -1
  141. package/dist/commands/tasks/render.js +116 -0
  142. package/dist/commands/tasks/render.js.map +1 -0
  143. package/dist/commands/tasks/tasks.js +113 -0
  144. package/dist/commands/tasks/tasks.js.map +1 -0
  145. package/dist/commands/unarchive/manifest.js +6 -9
  146. package/dist/commands/unarchive/manifest.js.map +1 -1
  147. package/dist/commands/unarchive/restore.js +17 -28
  148. package/dist/commands/unarchive/restore.js.map +1 -1
  149. package/dist/commands/unarchive/unarchive.js +22 -30
  150. package/dist/commands/unarchive/unarchive.js.map +1 -1
  151. package/dist/commands/validate/arch-coverage.js +6 -15
  152. package/dist/commands/validate/arch-coverage.js.map +1 -1
  153. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  154. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  155. package/dist/commands/validate/checks/requirements.js +18 -8
  156. package/dist/commands/validate/checks/requirements.js.map +1 -1
  157. package/dist/commands/validate/checks/sources.js +6 -2
  158. package/dist/commands/validate/checks/sources.js.map +1 -1
  159. package/dist/commands/validate/feature.js +23 -19
  160. package/dist/commands/validate/feature.js.map +1 -1
  161. package/dist/commands/validate/fleet/landscape.js +23 -23
  162. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  163. package/dist/commands/validate/fleet/map/attest.js +6 -10
  164. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  165. package/dist/commands/validate/fleet/map/bindings.js +1 -3
  166. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  167. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  168. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  169. package/dist/commands/validate/fleet/obligations.js +1 -5
  170. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  171. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  172. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  173. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  174. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  175. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  176. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  177. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  178. package/dist/commands/validate/fleet/views/root-documents.js +8 -28
  179. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  180. package/dist/commands/validate/links/corpus.js +1 -4
  181. package/dist/commands/validate/links/corpus.js.map +1 -1
  182. package/dist/commands/validate/links/glossary.js.map +1 -1
  183. package/dist/commands/validate/report.js +5 -4
  184. package/dist/commands/validate/report.js.map +1 -1
  185. package/dist/commands/validate/scope.js +113 -0
  186. package/dist/commands/validate/scope.js.map +1 -0
  187. package/dist/commands/validate/service/api.js +16 -104
  188. package/dist/commands/validate/service/api.js.map +1 -1
  189. package/dist/commands/validate/service/contracts/generated.js +134 -0
  190. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  191. package/dist/commands/validate/service/events/events.js +1 -1
  192. package/dist/commands/validate/service/events/events.js.map +1 -1
  193. package/dist/commands/validate/service/events/message-producers.js +1 -1
  194. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  195. package/dist/commands/validate/service/evidence-pins.js +56 -10
  196. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  197. package/dist/commands/validate/service/service.js +16 -26
  198. package/dist/commands/validate/service/service.js.map +1 -1
  199. package/dist/commands/validate/service/specs.js +31 -38
  200. package/dist/commands/validate/service/specs.js.map +1 -1
  201. package/dist/commands/validate/service/spine.js +8 -11
  202. package/dist/commands/validate/service/spine.js.map +1 -1
  203. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  204. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  205. package/dist/commands/validate/validate.js +44 -26
  206. package/dist/commands/validate/validate.js.map +1 -1
  207. package/dist/commands/verify/cross/diff.js +2 -6
  208. package/dist/commands/verify/cross/diff.js.map +1 -1
  209. package/dist/commands/verify/evidence/contract.js +1 -1
  210. package/dist/commands/verify/evidence/contract.js.map +1 -1
  211. package/dist/commands/verify/evidence/read.js +12 -3
  212. package/dist/commands/verify/evidence/read.js.map +1 -1
  213. package/dist/commands/verify/frozen.js +45 -23
  214. package/dist/commands/verify/frozen.js.map +1 -1
  215. package/dist/commands/verify/record.js +69 -17
  216. package/dist/commands/verify/record.js.map +1 -1
  217. package/dist/commands/verify/report.js +27 -44
  218. package/dist/commands/verify/report.js.map +1 -1
  219. package/dist/commands/verify/results.js +112 -30
  220. package/dist/commands/verify/results.js.map +1 -1
  221. package/dist/commands/verify/verify.js +54 -51
  222. package/dist/commands/verify/verify.js.map +1 -1
  223. package/dist/commands/vouch/pack/pack.js +2 -2
  224. package/dist/commands/vouch/pack/pack.js.map +1 -1
  225. package/dist/commands/vouch/pack/print.js +1 -6
  226. package/dist/commands/vouch/pack/print.js.map +1 -1
  227. package/dist/commands/vouch/pack/sections.js +1 -2
  228. package/dist/commands/vouch/pack/sections.js.map +1 -1
  229. package/dist/commands/vouch/run.js +26 -81
  230. package/dist/commands/vouch/run.js.map +1 -1
  231. package/dist/commands/vouch/sample/plan.js +20 -34
  232. package/dist/commands/vouch/sample/plan.js.map +1 -1
  233. package/dist/commands/vouch/vet/verify.js +39 -7
  234. package/dist/commands/vouch/vet/verify.js.map +1 -1
  235. package/dist/commands/vouch/vouch.js +10 -17
  236. package/dist/commands/vouch/vouch.js.map +1 -1
  237. package/dist/core/agent/agents-md/command-map.js +56 -101
  238. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  239. package/dist/core/agent/agents-md/map/explain.js +4 -3
  240. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  241. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  242. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  243. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  244. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  245. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  246. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  247. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  248. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  249. package/dist/core/agent/agents-md/map/status.js +9 -3
  250. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  251. package/dist/core/agent/agents-md/map/subsystem.js +4 -3
  252. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  253. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  254. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  255. package/dist/core/agent/agents-md/refusals.js +32 -18
  256. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  257. package/dist/core/agent/agents-md.js +9 -3
  258. package/dist/core/agent/agents-md.js.map +1 -1
  259. package/dist/core/agent/agents-stamp.js +10 -0
  260. package/dist/core/agent/agents-stamp.js.map +1 -1
  261. package/dist/core/agent/scaffold.js +10 -15
  262. package/dist/core/agent/scaffold.js.map +1 -1
  263. package/dist/core/agent/tools/dialects.js +1 -0
  264. package/dist/core/agent/tools/dialects.js.map +1 -1
  265. package/dist/core/agent/tools/registry.js +41 -76
  266. package/dist/core/agent/tools/registry.js.map +1 -1
  267. package/dist/core/agent/workflows/adopt.js +5 -3
  268. package/dist/core/agent/workflows/adopt.js.map +1 -1
  269. package/dist/core/agent/workflows/check.js +20 -16
  270. package/dist/core/agent/workflows/check.js.map +1 -1
  271. package/dist/core/agent/workflows/closing.js +54 -21
  272. package/dist/core/agent/workflows/closing.js.map +1 -1
  273. package/dist/core/agent/workflows/feature.js +79 -11
  274. package/dist/core/agent/workflows/feature.js.map +1 -1
  275. package/dist/core/agent/workflows/implement.js +91 -13
  276. package/dist/core/agent/workflows/implement.js.map +1 -1
  277. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  278. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  279. package/dist/core/agent/workflows/reference/codes.js +8 -6
  280. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  281. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  282. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  283. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  284. package/dist/core/agent/workflows/reference/spine.js +50 -18
  285. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  286. package/dist/core/asyncapi/baseline/plan.js +14 -81
  287. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  288. package/dist/core/asyncapi/digest.js +30 -25
  289. package/dist/core/asyncapi/digest.js.map +1 -1
  290. package/dist/core/asyncapi/merge/components.js +5 -4
  291. package/dist/core/asyncapi/merge/components.js.map +1 -1
  292. package/dist/core/asyncapi/merge/error.js +7 -2
  293. package/dist/core/asyncapi/merge/error.js.map +1 -1
  294. package/dist/core/asyncapi/merge/markers.js +30 -50
  295. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  296. package/dist/core/asyncapi/merge/merge.js +31 -53
  297. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  298. package/dist/core/asyncapi/merge/pin.js +10 -36
  299. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  300. package/dist/core/asyncapi/model.js.map +1 -1
  301. package/dist/core/asyncapi/read.js +36 -12
  302. package/dist/core/asyncapi/read.js.map +1 -1
  303. package/dist/core/brief/brief.js +6 -18
  304. package/dist/core/brief/brief.js.map +1 -1
  305. package/dist/core/brief/landscape.js +8 -12
  306. package/dist/core/brief/landscape.js.map +1 -1
  307. package/dist/core/brief/map/extend.js +8 -5
  308. package/dist/core/brief/map/extend.js.map +1 -1
  309. package/dist/core/brief/map/owed.js +8 -26
  310. package/dist/core/brief/map/owed.js.map +1 -1
  311. package/dist/core/brief/targets.js +15 -5
  312. package/dist/core/brief/targets.js.map +1 -1
  313. package/dist/core/c4/likec4.js +147 -16
  314. package/dist/core/c4/likec4.js.map +1 -1
  315. package/dist/core/c4/locate/unlocated.js +150 -0
  316. package/dist/core/c4/locate/unlocated.js.map +1 -0
  317. package/dist/core/c4/parsed/values.js +54 -1
  318. package/dist/core/c4/parsed/values.js.map +1 -1
  319. package/dist/core/c4/prefetch/fleet.js +1 -9
  320. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  321. package/dist/core/c4/project/documents.js +31 -6
  322. package/dist/core/c4/project/documents.js.map +1 -1
  323. package/dist/core/c4/project/load.js +6 -17
  324. package/dist/core/c4/project/load.js.map +1 -1
  325. package/dist/core/c4/resolve/external.js +51 -0
  326. package/dist/core/c4/resolve/external.js.map +1 -0
  327. package/dist/core/c4/resolve/service.js +25 -1
  328. package/dist/core/c4/resolve/service.js.map +1 -1
  329. package/dist/core/c4/seed/template.js +15 -1
  330. package/dist/core/c4/seed/template.js.map +1 -1
  331. package/dist/core/c4/service-model/shape.js +2 -22
  332. package/dist/core/c4/service-model/shape.js.map +1 -1
  333. package/dist/core/c4/source-mask.js +288 -4
  334. package/dist/core/c4/source-mask.js.map +1 -1
  335. package/dist/core/c4/source-scan.js +203 -147
  336. package/dist/core/c4/source-scan.js.map +1 -1
  337. package/dist/core/c4/splice/authored-source.js +19 -0
  338. package/dist/core/c4/splice/authored-source.js.map +1 -1
  339. package/dist/core/c4/splice/delta-blocks.js +5 -18
  340. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  341. package/dist/core/c4/splice/landscape-merge.js +13 -8
  342. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  343. package/dist/core/c4/splice/model/merge.js +10 -6
  344. package/dist/core/c4/splice/model/merge.js.map +1 -1
  345. package/dist/core/c4/splice/placement.js +108 -18
  346. package/dist/core/c4/splice/placement.js.map +1 -1
  347. package/dist/core/c4/workspace.js +13 -22
  348. package/dist/core/c4/workspace.js.map +1 -1
  349. package/dist/core/capabilities/capabilities.js +6 -6
  350. package/dist/core/capabilities/capabilities.js.map +1 -1
  351. package/dist/core/capabilities/delta/doc.js +7 -10
  352. package/dist/core/capabilities/delta/doc.js.map +1 -1
  353. package/dist/core/capabilities/findings.js +15 -20
  354. package/dist/core/capabilities/findings.js.map +1 -1
  355. package/dist/core/capabilities/realizes/findings.js +12 -19
  356. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  357. package/dist/core/capabilities/realizes/join.js +0 -20
  358. package/dist/core/capabilities/realizes/join.js.map +1 -1
  359. package/dist/core/capabilities/rollup.js +6 -0
  360. package/dist/core/capabilities/rollup.js.map +1 -1
  361. package/dist/core/capabilities/tree.js +6 -28
  362. package/dist/core/capabilities/tree.js.map +1 -1
  363. package/dist/core/capabilities/usecase-join.js +3 -13
  364. package/dist/core/capabilities/usecase-join.js.map +1 -1
  365. package/dist/core/coherence/authoring/scaffold.js +11 -3
  366. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  367. package/dist/core/coherence/authoring/sentinels.js +2 -1
  368. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  369. package/dist/core/coherence/coherence.js +30 -16
  370. package/dist/core/coherence/coherence.js.map +1 -1
  371. package/dist/core/coherence/consumed/operations.js +70 -0
  372. package/dist/core/coherence/consumed/operations.js.map +1 -0
  373. package/dist/core/coherence/consumed/refusal.js +38 -0
  374. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  375. package/dist/core/coherence/declared.js +60 -34
  376. package/dist/core/coherence/declared.js.map +1 -1
  377. package/dist/core/coherence/events/declared.js +44 -59
  378. package/dist/core/coherence/events/declared.js.map +1 -1
  379. package/dist/core/coherence/events/events.js +119 -8
  380. package/dist/core/coherence/events/events.js.map +1 -1
  381. package/dist/core/coherence/events/grades.js +31 -8
  382. package/dist/core/coherence/events/grades.js.map +1 -1
  383. package/dist/core/coherence/events/lookups.js +14 -82
  384. package/dist/core/coherence/events/lookups.js.map +1 -1
  385. package/dist/core/coherence/lookups.js +18 -78
  386. package/dist/core/coherence/lookups.js.map +1 -1
  387. package/dist/core/coherence/retired/archived.js +285 -0
  388. package/dist/core/coherence/retired/archived.js.map +1 -0
  389. package/dist/core/coherence/retired/event-pair.js +62 -0
  390. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  391. package/dist/core/coherence/retired/merged.js +79 -0
  392. package/dist/core/coherence/retired/merged.js.map +1 -0
  393. package/dist/core/coherence/retired/retirement.js +49 -0
  394. package/dist/core/coherence/retired/retirement.js.map +1 -0
  395. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  396. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  397. package/dist/core/coherence/retired/revival/revived.js +297 -0
  398. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  399. package/dist/core/consumers/after.js +113 -0
  400. package/dist/core/consumers/after.js.map +1 -0
  401. package/dist/core/consumers/consumers.js +296 -0
  402. package/dist/core/consumers/consumers.js.map +1 -0
  403. package/dist/core/consumers/current.js +211 -0
  404. package/dist/core/consumers/current.js.map +1 -0
  405. package/dist/core/consumers/scan.js +46 -0
  406. package/dist/core/consumers/scan.js.map +1 -0
  407. package/dist/core/consumers/state.js +27 -0
  408. package/dist/core/consumers/state.js.map +1 -0
  409. package/dist/core/delta/delta.js +1 -1
  410. package/dist/core/delta/delta.js.map +1 -1
  411. package/dist/core/deployment/delta.js +3 -6
  412. package/dist/core/deployment/delta.js.map +1 -1
  413. package/dist/core/diff/base-git.js +94 -12
  414. package/dist/core/diff/base-git.js.map +1 -1
  415. package/dist/core/diff/base-state.js +28 -16
  416. package/dist/core/diff/base-state.js.map +1 -1
  417. package/dist/core/diff/joins.js +136 -0
  418. package/dist/core/diff/joins.js.map +1 -0
  419. package/dist/core/diff/scope/changed-paths.js +22 -6
  420. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  421. package/dist/core/diff/scope/edges.js +82 -0
  422. package/dist/core/diff/scope/edges.js.map +1 -0
  423. package/dist/core/diff/semantic.js +100 -31
  424. package/dist/core/diff/semantic.js.map +1 -1
  425. package/dist/core/doctor/agents.js +34 -18
  426. package/dist/core/doctor/agents.js.map +1 -1
  427. package/dist/core/doctor/report.js.map +1 -1
  428. package/dist/core/document/apply.js +10 -2
  429. package/dist/core/document/apply.js.map +1 -1
  430. package/dist/core/document/frontmatter.js +55 -0
  431. package/dist/core/document/frontmatter.js.map +1 -1
  432. package/dist/core/document/parse.js +29 -6
  433. package/dist/core/document/parse.js.map +1 -1
  434. package/dist/core/document/scenarios.js +17 -0
  435. package/dist/core/document/scenarios.js.map +1 -1
  436. package/dist/core/document/spec.js +4 -11
  437. package/dist/core/document/spec.js.map +1 -1
  438. package/dist/core/envelope/config.js +10 -0
  439. package/dist/core/envelope/config.js.map +1 -1
  440. package/dist/core/envelope/json.js +34 -0
  441. package/dist/core/envelope/json.js.map +1 -1
  442. package/dist/core/explain/families.js +17 -12
  443. package/dist/core/explain/families.js.map +1 -1
  444. package/dist/core/explain/refusals.js +3 -3
  445. package/dist/core/explain/refusals.js.map +1 -1
  446. package/dist/core/explain/terms.js +18 -9
  447. package/dist/core/explain/terms.js.map +1 -1
  448. package/dist/core/explore/explore.js +2 -7
  449. package/dist/core/explore/explore.js.map +1 -1
  450. package/dist/core/fleet-context.js +34 -95
  451. package/dist/core/fleet-context.js.map +1 -1
  452. package/dist/core/gate/checks.js +5 -2
  453. package/dist/core/gate/checks.js.map +1 -1
  454. package/dist/core/gate/partners.js +4 -2
  455. package/dist/core/gate/partners.js.map +1 -1
  456. package/dist/core/gate/report.js.map +1 -1
  457. package/dist/core/gate/verification.js +75 -12
  458. package/dist/core/gate/verification.js.map +1 -1
  459. package/dist/core/gherkin/stale.js +13 -37
  460. package/dist/core/gherkin/stale.js.map +1 -1
  461. package/dist/core/glossary/tree.js +6 -19
  462. package/dist/core/glossary/tree.js.map +1 -1
  463. package/dist/core/kernel/concurrency.js +19 -0
  464. package/dist/core/kernel/concurrency.js.map +1 -1
  465. package/dist/core/kernel/records.js +11 -0
  466. package/dist/core/kernel/records.js.map +1 -1
  467. package/dist/core/links/backlinks.js +1 -3
  468. package/dist/core/links/backlinks.js.map +1 -1
  469. package/dist/core/links/corpus.js +5 -36
  470. package/dist/core/links/corpus.js.map +1 -1
  471. package/dist/core/mcp/argv.js +101 -0
  472. package/dist/core/mcp/argv.js.map +1 -1
  473. package/dist/core/mcp/author-tools.js +2 -1
  474. package/dist/core/mcp/author-tools.js.map +1 -1
  475. package/dist/core/mcp/protocol.js +2 -2
  476. package/dist/core/mcp/protocol.js.map +1 -1
  477. package/dist/core/mcp/tools.js +43 -33
  478. package/dist/core/mcp/tools.js.map +1 -1
  479. package/dist/core/openapi/baseline/gate.js +53 -12
  480. package/dist/core/openapi/baseline/gate.js.map +1 -1
  481. package/dist/core/openapi/baseline/plan.js +22 -8
  482. package/dist/core/openapi/baseline/plan.js.map +1 -1
  483. package/dist/core/openapi/baseline/record.js +0 -1
  484. package/dist/core/openapi/baseline/record.js.map +1 -1
  485. package/dist/core/openapi/contract-text.js +33 -0
  486. package/dist/core/openapi/contract-text.js.map +1 -0
  487. package/dist/core/openapi/contract-tree.js +56 -0
  488. package/dist/core/openapi/contract-tree.js.map +1 -0
  489. package/dist/core/openapi/depth.js +38 -24
  490. package/dist/core/openapi/depth.js.map +1 -1
  491. package/dist/core/openapi/digest.js +52 -2
  492. package/dist/core/openapi/digest.js.map +1 -1
  493. package/dist/core/openapi/doc.js +12 -4
  494. package/dist/core/openapi/doc.js.map +1 -1
  495. package/dist/core/openapi/merge/components.js +25 -60
  496. package/dist/core/openapi/merge/components.js.map +1 -1
  497. package/dist/core/openapi/merge/error.js +3 -13
  498. package/dist/core/openapi/merge/error.js.map +1 -1
  499. package/dist/core/openapi/merge/markers.js +26 -30
  500. package/dist/core/openapi/merge/markers.js.map +1 -1
  501. package/dist/core/openapi/merge/merge.js +26 -52
  502. package/dist/core/openapi/merge/merge.js.map +1 -1
  503. package/dist/core/openapi/merge/pin.js +15 -35
  504. package/dist/core/openapi/merge/pin.js.map +1 -1
  505. package/dist/core/openspec/decide/verdict.js.map +1 -1
  506. package/dist/core/openspec/inventory.js +4 -5
  507. package/dist/core/openspec/inventory.js.map +1 -1
  508. package/dist/core/openspec/model/mapping.js +30 -28
  509. package/dist/core/openspec/model/mapping.js.map +1 -1
  510. package/dist/core/openspec/model/model.js +12 -7
  511. package/dist/core/openspec/model/model.js.map +1 -1
  512. package/dist/core/openspec/scan/changes.js +10 -4
  513. package/dist/core/openspec/scan/changes.js.map +1 -1
  514. package/dist/core/openspec/scan/config.js +7 -16
  515. package/dist/core/openspec/scan/config.js.map +1 -1
  516. package/dist/core/openspec/scan/shape.js +63 -19
  517. package/dist/core/openspec/scan/shape.js.map +1 -1
  518. package/dist/core/openspec/scan/walk.js +21 -38
  519. package/dist/core/openspec/scan/walk.js.map +1 -1
  520. package/dist/core/pack/living.js +2 -16
  521. package/dist/core/pack/living.js.map +1 -1
  522. package/dist/core/provenance/findings.js +48 -12
  523. package/dist/core/provenance/findings.js.map +1 -1
  524. package/dist/core/provenance/git.js +33 -57
  525. package/dist/core/provenance/git.js.map +1 -1
  526. package/dist/core/provenance/gitq/moves.js +71 -114
  527. package/dist/core/provenance/gitq/moves.js.map +1 -1
  528. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  529. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  530. package/dist/core/provenance/sources.js +23 -6
  531. package/dist/core/provenance/sources.js.map +1 -1
  532. package/dist/core/provenance/stamp.js +5 -17
  533. package/dist/core/provenance/stamp.js.map +1 -1
  534. package/dist/core/repo/entries.js.map +1 -1
  535. package/dist/core/repo/repo.js +24 -14
  536. package/dist/core/repo/repo.js.map +1 -1
  537. package/dist/core/repo/tree/find.js +12 -3
  538. package/dist/core/repo/tree/find.js.map +1 -1
  539. package/dist/core/repo/tree/fs.js +27 -1
  540. package/dist/core/repo/tree/fs.js.map +1 -1
  541. package/dist/core/repo/tree/walk.js +8 -30
  542. package/dist/core/repo/tree/walk.js.map +1 -1
  543. package/dist/core/results.js +47 -12
  544. package/dist/core/results.js.map +1 -1
  545. package/dist/core/staging/commit.js +10 -12
  546. package/dist/core/staging/commit.js.map +1 -1
  547. package/dist/core/staging/lock.js +74 -12
  548. package/dist/core/staging/lock.js.map +1 -1
  549. package/dist/core/staging/recovery/intent.js +2 -5
  550. package/dist/core/staging/recovery/intent.js.map +1 -1
  551. package/dist/core/staging/recovery/recover.js +60 -1
  552. package/dist/core/staging/recovery/recover.js.map +1 -1
  553. package/dist/core/staging/snapshot/manifest.js +152 -0
  554. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  555. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  556. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  557. package/dist/core/status/actions/execution.js +11 -0
  558. package/dist/core/status/actions/execution.js.map +1 -1
  559. package/dist/core/status/feature/artifacts.js +2 -2
  560. package/dist/core/status/feature/artifacts.js.map +1 -1
  561. package/dist/core/status/feature/feature.js +5 -11
  562. package/dist/core/status/feature/feature.js.map +1 -1
  563. package/dist/core/status/fleet/fleet.js +6 -2
  564. package/dist/core/status/fleet/fleet.js.map +1 -1
  565. package/dist/core/status/fleet/next.js +26 -3
  566. package/dist/core/status/fleet/next.js.map +1 -1
  567. package/dist/core/status/report.js.map +1 -1
  568. package/dist/core/status/verification.js +30 -15
  569. package/dist/core/status/verification.js.map +1 -1
  570. package/dist/core/tasks/packet.js +174 -0
  571. package/dist/core/tasks/packet.js.map +1 -0
  572. package/dist/core/tasks/record.js +43 -0
  573. package/dist/core/tasks/record.js.map +1 -0
  574. package/dist/core/tasks/sources.js +181 -0
  575. package/dist/core/tasks/sources.js.map +1 -0
  576. package/dist/core/tasks/stamps.js +76 -0
  577. package/dist/core/tasks/stamps.js.map +1 -0
  578. package/dist/core/tasks/unit.js +0 -0
  579. package/dist/core/tasks/unit.js.map +1 -0
  580. package/dist/core/usecases/capability.js +11 -14
  581. package/dist/core/usecases/capability.js.map +1 -1
  582. package/dist/core/usecases/delta/flows.js +5 -7
  583. package/dist/core/usecases/delta/flows.js.map +1 -1
  584. package/dist/core/usecases/fleet.js +178 -31
  585. package/dist/core/usecases/fleet.js.map +1 -1
  586. package/dist/core/usecases/operations.js +5 -4
  587. package/dist/core/usecases/operations.js.map +1 -1
  588. package/dist/core/usecases/service/flows.js +9 -6
  589. package/dist/core/usecases/service/flows.js.map +1 -1
  590. package/dist/core/usecases/suites/participants.js +83 -0
  591. package/dist/core/usecases/suites/participants.js.map +1 -0
  592. package/dist/core/usecases/suites/suites.js +45 -0
  593. package/dist/core/usecases/suites/suites.js.map +1 -0
  594. package/dist/core/verify/answers.js +12 -5
  595. package/dist/core/verify/answers.js.map +1 -1
  596. package/dist/core/verify/build.js +102 -82
  597. package/dist/core/verify/build.js.map +1 -1
  598. package/dist/core/verify/checklist.js +3 -0
  599. package/dist/core/verify/checklist.js.map +1 -1
  600. package/dist/core/verify/location.js +67 -0
  601. package/dist/core/verify/location.js.map +1 -0
  602. package/dist/core/verify/pins/lint.js +3 -2
  603. package/dist/core/verify/pins/lint.js.map +1 -1
  604. package/dist/core/verify/pins/pin.js +1 -1
  605. package/dist/core/verify/pins/pin.js.map +1 -1
  606. package/dist/core/verify/read/file.js +224 -0
  607. package/dist/core/verify/read/file.js.map +1 -0
  608. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  609. package/dist/core/verify/read/shape.js.map +1 -0
  610. package/dist/core/verify/read/union.js +105 -0
  611. package/dist/core/verify/read/union.js.map +1 -0
  612. package/dist/core/verify/record.js +35 -12
  613. package/dist/core/verify/record.js.map +1 -1
  614. package/dist/core/verify/store/commit.js +40 -30
  615. package/dist/core/verify/store/commit.js.map +1 -1
  616. package/dist/core/verify/store/render.js +8 -4
  617. package/dist/core/verify/store/render.js.map +1 -1
  618. package/dist/core/vocabulary/issue.js +1 -0
  619. package/dist/core/vocabulary/issue.js.map +1 -1
  620. package/dist/core/vocabulary/maturity.js +12 -2
  621. package/dist/core/vocabulary/maturity.js.map +1 -1
  622. package/examples/README.md +21 -6
  623. package/examples/docs/architecture/landscape.likec4 +8 -0
  624. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  625. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  626. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  627. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  628. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  629. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  630. package/package.json +1 -1
  631. package/dist/commands/validate/checks/vocabulary.js +0 -16
  632. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  633. package/dist/core/agent/agents-md/artifacts.js +0 -221
  634. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  635. package/dist/core/agent/agents-md/cycle.js +0 -180
  636. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  637. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  638. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  639. package/dist/core/agent/agents-md/spine.js +0 -40
  640. package/dist/core/agent/agents-md/spine.js.map +0 -1
  641. package/dist/core/coherence/events/merged.js +0 -40
  642. package/dist/core/coherence/events/merged.js.map +0 -1
  643. package/dist/core/diff/current-state.js +0 -96
  644. package/dist/core/diff/current-state.js.map +0 -1
  645. package/dist/core/diff/victims.js +0 -256
  646. package/dist/core/diff/victims.js.map +0 -1
  647. package/dist/core/staging/snapshot.js.map +0 -1
  648. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/markers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The keys loam adds to a feature's event contract that must never reach the\n * living one: `x-loam-based-on`, `x-loam-remove`, and the root\n * `x-loam-baselines` record — the same three spellings the OpenAPI axis\n * strips, gathered here for the same reason its markers\n * module exists: `stripAsyncapiMarkers` takes them out for the create branch\n * that publishes a feature document verbatim, and the merge takes them out\n * again on the way into a living document. Two spellings of \"which keys are\n * feature-only\" is how one of the two starts leaking a marker.\n *\n * The strip is DEEP, and it covers the WHOLE document. Deep by the format\n * spec's own decision: an inline channel message (`channels.<ck>.messages.<mk>`)\n * is channel-slot interior, so a pin or marker nested on it is part of the\n * channel's VALUE — the slot walker never visits it, and a slot-level strip\n * would publish it into the fleet's living contract exactly the way\n * `--approve` once published `x-loam-remove: true` inside a living OpenAPI\n * component. Whole-document because the create branch publishes this text\n * VERBATIM: a marker at the document root or a pin under `info` sits outside\n * every section, and a strip that walked only the three sections shipped\n * both into a living contract, unseen even by validate's marker sweep.\n */\nimport { isMap, isScalar, parseDocument, type Document, type Pair } from \"yaml\";\n// Every `isRecord` below asks one question of a resolved plain tree: is this a\n// node that can hold AsyncAPI keys — an object, not an array?\nimport { isRecord } from \"../../kernel/records.js\";\n// One spelling of the deep strip for both axes: the feature-only keys are the\n// SAME two strings on either (core/asyncapi/digest.ts aliases the constants\n// outright), so a drift between the strips would be two definitions of one\n// rule (the openapi digest module's own failure mode).\nimport { FEATURE_ONLY_KEYS, withoutFeatureKeysDeep } from \"../../openapi/digest.js\";\nimport { ASYNCAPI_BASELINES_KEY, isSlotRemoval, type AsyncapiSection } from \"../digest.js\";\nimport { errorMessage, AsyncapiMergeError, type AsyncapiMergeSource } from \"./error.js\";\n\n/** The three slot sections, in document order — every whole-document walk here goes over them. */\nconst SECTIONS: AsyncapiSection[] = [\"channels\", \"operations\", \"components.messages\"];\n\n/**\n * A slot's PUBLISHABLE value: every feature-only key removed, at any depth —\n * the top-level pin, a nested pin or marker on an inline channel message, and\n * anything an author buried deeper. What the merge writes and what the create\n * branch keeps; a removal-marker slot is never published at all (the merge\n * deletes its target, the strip drops it), so this is only ever asked of a\n * slot that survives.\n */\nexport function withoutFeatureMarkers(node: unknown): unknown {\n return withoutFeatureKeysDeep(node);\n}\n\n/**\n * The AST path of a section's own mapping — `slotPath` (../digest.ts) minus\n * the slot key, spelled beside the writers so the strip and the merge cannot\n * disagree about which node a section name addresses.\n */\nexport function sectionAstPath(section: AsyncapiSection): string[] {\n return section === \"components.messages\" ? [\"components\", \"messages\"] : [section];\n}\n\n/** One mapping node's pair for a key, matched AS A STRING — `slotPair`'s find, reusable at any depth. */\nfunction pairAt(node: unknown, key: string): Pair | undefined {\n return isMap(node) ? node.items.find((it) => isScalar(it.key) && String(it.key.value) === key) : undefined;\n}\n\n/** Delete one pair from a mapping node, string-key-matched. Returns whether a pair actually went. */\nfunction deleteKeyIn(node: unknown, key: string): boolean {\n if (!isMap(node)) return false;\n const at = node.items.findIndex((it) => isScalar(it.key) && String(it.key.value) === key);\n if (at < 0) return false;\n node.items.splice(at, 1);\n return true;\n}\n\n/**\n * Find one slot's key/value pair, matching the existing key AS A STRING: a\n * channel named `404:` is the YAML number 404, and a plain `getIn`/`setIn`\n * with the string \"404\" misses it and APPENDS a second pair — the living\n * contract then declares the slot twice, forever, with the pre-merge copy\n * first in reading order. The openapi component write's lesson, applied to\n * every slot access on this axis: the merge's writers below go through it,\n * and so does the pin (./pin.ts), whose raw `getIn` used to grade every\n * non-string-keyed slot `unwritable` with an alias diagnosis nothing in the\n * document backed.\n */\nexport function slotPair(doc: Document, section: AsyncapiSection, key: string): Pair | undefined {\n return pairAt(doc.getIn(sectionAstPath(section)), key);\n}\n\n/** Write one slot's value, string-key-matched — the merge's one upsert door. */\nexport function setSlotValue(doc: Document, section: AsyncapiSection, key: string, value: unknown): void {\n const existing = slotPair(doc, section, key);\n if (existing !== undefined) existing.value = doc.createNode(value);\n else doc.setIn([...sectionAstPath(section), key], value);\n}\n\n/**\n * Delete one slot's pair, string-key-matched for the numeric-key reason above\n * (`deleteIn` with \"404\" reports nothing deleted and the slot survives in\n * silence). Returns whether a pair actually went.\n */\nexport function deleteSlot(doc: Document, section: AsyncapiSection, key: string): boolean {\n return deleteKeyIn(doc.getIn(sectionAstPath(section)), key);\n}\n\n/**\n * Refuse a living container the merge cannot write into: a container spelled\n * as something other than a mapping is a document loam cannot merge, and one\n * spelled as a YAML alias would take the write into every node that shares\n * the anchor. Asked at the WRITE, because reading is safe and writing is not\n * — an absent container is fine, the writers above create it.\n *\n * Takes a raw path rather than an `AsyncapiSection`, because the merge writes\n * outside the three slot sections too: a component surface lands at\n * `[\"components\", <kind>]`, and a section-typed guard simply could not be\n * asked about it. Skipping the question there would let a living\n * `components: *alias` take a surface write into every node sharing the\n * anchor — the exact corruption this function exists to refuse, at the one\n * depth it could not see. Lives beside the writers rather than in the merge\n * so the guard and the write cannot disagree about which node a path names.\n */\nexport function writableSection(\n living: Document,\n livingPlain: unknown,\n path: string[],\n service: string,\n): void {\n for (let depth = 1; depth <= path.length; depth += 1) {\n const prefix = path.slice(0, depth);\n const plain = prefix.reduce<unknown>((node, step) => (isRecord(node) ? node[step] : undefined), livingPlain);\n if (plain === undefined || plain === null) return;\n const label = prefix.join(\".\");\n if (!isRecord(plain)) {\n throw new AsyncapiMergeError(\"living\", service, `'${label}' is not a mapping`);\n }\n if (!isMap(living.getIn(prefix))) {\n throw new AsyncapiMergeError(\n \"living\",\n service,\n `'${label}' is written as a YAML alias — one shared value backs every use, so merging into it would rewrite every other node that aliases it. Expand it before archiving.`,\n );\n }\n }\n}\n\n/** One slot's human label: `channels.<key>` — with the message's join name where it has one. */\nexport function slotLabel(section: AsyncapiSection, key: string, name?: string): string {\n const at = `${section}.${key}`;\n return name !== undefined && name !== key ? `'${name}' (${at})` : at;\n}\n\n/**\n * Refuse a document whose section spells one slot key twice — `channels:\n * {404: …, \"404\": …}` is legal YAML (a number and a string are distinct\n * keys), but the resolved walk (`asyncapiSlots` reads `Object.entries` of\n * `toJS()`) keeps the LAST pair while every string-matched AST access above\n * finds the FIRST: the walker would grade one pair and the writer replace\n * the other, and no single read discipline can make the two agree about\n * which node the (section, key) names. Every entry point that walks slots —\n * the pin, the strip, the merge — refuses it by name instead of writing\n * through the wrong pair.\n */\nexport function refuseAmbiguousSlotKeys(doc: Document, source: AsyncapiMergeSource, service: string): void {\n for (const section of SECTIONS) {\n const node = doc.getIn(sectionAstPath(section));\n if (!isMap(node)) continue;\n const seen = new Set<string>();\n for (const it of node.items) {\n if (!isScalar(it.key)) continue;\n const key = String(it.key.value);\n if (seen.has(key)) {\n throw new AsyncapiMergeError(\n source,\n service,\n `'${section}' spells the key '${key}' in two pairs (two YAML scalars that stringify identically, e.g. 404 and \"404\") — loam cannot tell which pair a slot access should read or write; merge them into one entry`,\n \"is ambiguous\",\n );\n }\n seen.add(key);\n }\n }\n}\n\n/**\n * Remove feature-only markers — removal-marker slots, the pins on the slots\n * that survive, and both loam keys at every nested depth of the whole\n * document — before a feature contract is used to create a brand-new living\n * document. Normally coherence refuses the removal shape because there cannot\n * be a removal target; this guard also keeps `--approve` from ever persisting\n * a marker into living docs.\n *\n * Edit the AST in place when every node the strip touches IS a node — the\n * author's comments, key order and formatting are theirs to keep. An alias\n * cannot be edited in place (there is one shared value behind it, and editing\n * a slot through it would change every use), and a slot whose KEY is not a\n * scalar is not findable by name at all — so a section holding either gets\n * rewritten from the resolved tree instead: losing formatting is a cost,\n * shipping the marker is a corruption. The openapi strip's two-branch\n * discipline, per section.\n */\nexport function stripAsyncapiMarkers(featureText: string, service: string): string {\n const feature = parseDocument(featureText);\n if (feature.errors.length > 0) {\n throw new AsyncapiMergeError(\"feature\", service, feature.errors[0]!.message);\n }\n refuseAmbiguousSlotKeys(feature, \"feature\", service);\n let plain: unknown;\n try {\n // Resolve aliases once with the document's own anchor context — an\n // individual node's toJSON() loses it (the openapi merge's lesson).\n plain = feature.toJS() ?? {};\n } catch (error) {\n throw new AsyncapiMergeError(\"feature\", service, errorMessage(error));\n }\n\n let stripped = false;\n for (const section of SECTIONS) {\n // Shape from the RESOLVED tree, for the same reason the merge reads it\n // there: an aliased section is not a map node, and treating it as\n // \"nothing to strip\" is how a feature-only marker reached a living\n // contract on the OpenAPI axis.\n const sectionPlain = sectionAstPath(section).reduce<unknown>(\n (node, step) => (isRecord(node) ? node[step] : undefined),\n plain,\n );\n if (!isRecord(sectionPlain)) continue;\n\n const cleaned: Record<string, unknown> = {};\n // `kept: undefined` means the slot goes — a resolved YAML value is never\n // undefined itself (`~` reads as null), so the sentinel is unambiguous.\n const edits: Array<{ key: string; kept: unknown }> = [];\n for (const [key, value] of Object.entries(sectionPlain)) {\n // A removal marker asserts a slot rather than declaring content; the\n // create branch has no target to delete, so the slot itself goes.\n const kept = isSlotRemoval(value) ? undefined : withoutFeatureKeysDeep(value);\n if (kept !== value) edits.push({ key, kept });\n if (kept !== undefined) cleaned[key] = kept;\n }\n if (edits.length === 0) continue;\n stripped = true;\n\n // In place only when every touched node is reachable: the section is a\n // real map and each edited slot's pair is findable by string key.\n const editable =\n isMap(feature.getIn(sectionAstPath(section))) &&\n edits.every(({ key }) => slotPair(feature, section, key) !== undefined);\n if (!editable) {\n feature.setIn(sectionAstPath(section), cleaned);\n continue;\n }\n for (const { key, kept } of edits) {\n if (kept === undefined) deleteSlot(feature, section, key);\n // The surviving slot's value is replaced wholesale with the deep-strip:\n // a pin at slot depth could be deleted key-by-key, but a marker nested\n // on an inline channel message sits where no fixed path reaches — the\n // resolved copy is the one branch both depths share.\n else setSlotValue(feature, section, key, kept);\n }\n }\n\n if (isRecord(plain)) stripped = stripBeyondSections(feature, plain) || stripped;\n\n // The root `x-loam-baselines` record, on EVERY branch — including the\n // verbatim early return below. `stripBeyondSections` cannot do it: it\n // filters on FEATURE_ONLY_KEYS, which holds the two IN-VALUE keys and not\n // this one, so the record's own subtree (16-hex digests) carries no loam key\n // for the deep strip to find and the whole entry rides out untouched. That\n // is the create branch publishing loam's bookkeeping into a living\n // asyncapi.yaml, where nothing catches it: `removalMarkerPaths` — validate's\n // marker sweep on this axis — only ever looks for `x-loam-remove`.\n if (isRecord(plain) && ASYNCAPI_BASELINES_KEY in plain) {\n feature.deleteIn([ASYNCAPI_BASELINES_KEY]);\n stripped = true;\n }\n\n if (!stripped) return featureText;\n try {\n return feature.toString();\n } catch (error) {\n throw new AsyncapiMergeError(\"feature\", service, errorMessage(error));\n }\n}\n\n/**\n * The strip's reach OUTSIDE the three sections: the document root's own\n * keys, `info`, `servers`, and the `components` siblings of `messages` —\n * everything the section loop above deliberately does not touch. A loam key\n * here is feature-only exactly as it is inside a slot, and the create branch\n * used to publish it (an `x-loam-remove: true` at the root reached a living\n * contract with zero diagnosis). A loam key at this depth is deleted; any\n * other entry whose subtree carries one is replaced with its deep-strip,\n * through the same string-matched pair access the slot writers use.\n */\nfunction stripBeyondSections(feature: Document, plain: Record<string, unknown>): boolean {\n let stripped = false;\n const strip = (path: string[], key: string, value: unknown): void => {\n const parent = path.length === 0 ? feature.contents : feature.getIn(path);\n if (FEATURE_ONLY_KEYS.has(key)) {\n stripped = deleteKeyIn(parent, key) || stripped;\n return;\n }\n const kept = withoutFeatureKeysDeep(value);\n if (kept === value) return;\n stripped = true;\n const pair = pairAt(parent, key);\n // The section loop's fallback, one entry wide: a pair no string can find\n // is rewritten from the resolved tree rather than left carrying markers.\n if (pair !== undefined) pair.value = feature.createNode(kept);\n else feature.setIn([...path, key], kept);\n };\n for (const [key, value] of Object.entries(plain)) {\n if (key === \"channels\" || key === \"operations\") continue;\n if (key === \"components\") {\n // `components.messages` is the section loop's; the siblings are ours.\n if (!isRecord(value)) continue;\n for (const [ck, cv] of Object.entries(value)) {\n if (ck !== \"messages\") strip([\"components\"], ck, cv);\n }\n continue;\n }\n strip([], key, value);\n }\n return stripped;\n}\n"]}
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/markers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The keys loam adds to a feature's event contract that must never reach the\n * living one: `x-loam-based-on`, `x-loam-remove`, and the root\n * `x-loam-baselines` record — the same three spellings the OpenAPI axis\n * strips, gathered here for the same reason its markers\n * module exists: `stripAsyncapiMarkers` takes them out for the create branch\n * that publishes a feature document verbatim, and the merge takes them out\n * again on the way into a living document. Two spellings of \"which keys are\n * feature-only\" is how one of the two starts leaking a marker.\n *\n * The strip is DEEP, and it covers the WHOLE document. Deep by the format\n * spec's own decision: an inline channel message (`channels.<ck>.messages.<mk>`)\n * is channel-slot interior, so a pin or marker nested on it is part of the\n * channel's VALUE — the slot walker never visits it, and a slot-level strip\n * would publish it into the fleet's living contract exactly the way\n * `--approve` once published `x-loam-remove: true` inside a living OpenAPI\n * component. Whole-document because the create branch publishes this text\n * VERBATIM: a marker at the document root or a pin under `info` sits outside\n * every section, and a strip that walked only the three sections shipped\n * both into a living contract, unseen even by validate's marker sweep.\n */\nimport { isMap, isScalar, type Document, type Pair } from \"yaml\";\n// Every `isRecord` below asks one question of a resolved plain tree: is this a\n// node that can hold AsyncAPI keys — an object, not an array?\nimport { isRecord } from \"../../kernel/records.js\";\n// One spelling of the deep strip for both axes: the feature-only keys are the\n// SAME two strings on either (core/asyncapi/digest.ts aliases the constants\n// outright), so a drift between the strips would be two definitions of one\n// rule (the openapi digest module's own failure mode).\nimport { FEATURE_ONLY_KEYS, withoutFeatureKeysDeep } from \"../../openapi/digest.js\";\nimport { ASYNCAPI_BASELINES_KEY, isSlotRemoval, type AsyncapiSection } from \"../digest.js\";\nimport { pairAt, setKeyed } from \"../../openapi/merge/components.js\";\nimport { readTree, writeTree, type ContractTree } from \"../../openapi/contract-tree.js\";\nimport { AsyncapiMergeError, asyncapiRefusal, type AsyncapiMergeSource } from \"./error.js\";\n\n/** The three slot sections, in document order — every whole-document walk here goes over them. */\nconst SECTIONS: AsyncapiSection[] = [\"channels\", \"operations\", \"components.messages\"];\n\n/**\n * The AST path of a section's own mapping — two levels deep for\n * `components.messages`, one for the other two — spelled beside the writers so\n * the strip and the merge cannot disagree about which node a section name\n * addresses. A slot's own path is this plus its key; the `slotPath` helper that\n * once spelled that in ../digest.ts had no caller and was deleted.\n */\nexport function sectionAstPath(section: AsyncapiSection): string[] {\n return section === \"components.messages\" ? [\"components\", \"messages\"] : [section];\n}\n\n/** Delete one pair from a mapping node, string-key-matched. Returns whether a pair actually went. */\nfunction deleteKeyIn(node: unknown, key: string): boolean {\n if (!isMap(node)) return false;\n const at = node.items.findIndex((it) => isScalar(it.key) && String(it.key.value) === key);\n if (at < 0) return false;\n node.items.splice(at, 1);\n return true;\n}\n\n/**\n * Find one slot's key/value pair, matching the existing key AS A STRING: a\n * channel named `404:` is the YAML number 404, and a plain `getIn`/`setIn`\n * with the string \"404\" misses it and APPENDS a second pair — the living\n * contract then declares the slot twice, forever, with the pre-merge copy\n * first in reading order. The openapi component write's lesson, applied to\n * every slot access on this axis: the merge's writers below go through the\n * same string-matched find (`pairAt`, under `setKeyed`), and so does the pin\n * (./pin.ts), whose raw `getIn` used to grade every non-string-keyed slot\n * `unwritable` with an alias diagnosis nothing in the document backed.\n */\nexport function slotPair(doc: Document, section: AsyncapiSection, key: string): Pair | undefined {\n return pairAt(doc.getIn(sectionAstPath(section)), key);\n}\n\n/** Write one slot's value through `setKeyed`, string-key-matched — the merge's one upsert door. */\nexport function setSlotValue(doc: Document, section: AsyncapiSection, key: string, value: unknown): void {\n setKeyed(doc, sectionAstPath(section), key, value);\n}\n\n/**\n * Delete one slot's pair, string-key-matched for the numeric-key reason above\n * (`deleteIn` with \"404\" reports nothing deleted and the slot survives in\n * silence). Returns whether a pair actually went.\n */\nexport function deleteSlot(doc: Document, section: AsyncapiSection, key: string): boolean {\n return deleteKeyIn(doc.getIn(sectionAstPath(section)), key);\n}\n\n/**\n * Refuse a living container the merge cannot write into: a container spelled\n * as something other than a mapping is a document loam cannot merge, and one\n * spelled as a YAML alias would take the write into every node that shares\n * the anchor. Asked at the WRITE, because reading is safe and writing is not\n * — an absent container is fine, the writers above create it.\n *\n * Takes a raw path rather than an `AsyncapiSection`, because the merge writes\n * outside the three slot sections too: a component surface lands at\n * `[\"components\", <kind>]`, and a section-typed guard simply could not be\n * asked about it. Skipping the question there would let a living\n * `components: *alias` take a surface write into every node sharing the\n * anchor — the exact corruption this function exists to refuse, at the one\n * depth it could not see. Lives beside the writers rather than in the merge\n * so the guard and the write cannot disagree about which node a path names.\n */\nexport function writableSection(\n living: Document,\n livingPlain: unknown,\n path: string[],\n service: string,\n): void {\n for (let depth = 1; depth <= path.length; depth += 1) {\n const prefix = path.slice(0, depth);\n const plain = prefix.reduce<unknown>((node, step) => (isRecord(node) ? node[step] : undefined), livingPlain);\n if (plain === undefined || plain === null) return;\n const label = prefix.join(\".\");\n if (!isRecord(plain)) {\n throw new AsyncapiMergeError(\"living\", service, `'${label}' is not a mapping`);\n }\n if (!isMap(living.getIn(prefix))) {\n throw new AsyncapiMergeError(\n \"living\",\n service,\n `'${label}' is written as a YAML alias — one shared value backs every use, so merging into it would rewrite every other node that aliases it. Expand it before archiving.`,\n );\n }\n }\n}\n\n/** One slot's human label: `channels.<key>` — with the message's join name where it has one. */\nexport function slotLabel(section: AsyncapiSection, key: string, name?: string): string {\n const at = `${section}.${key}`;\n return name !== undefined && name !== key ? `'${name}' (${at})` : at;\n}\n\n/**\n * Refuse a document whose section spells one slot key twice — `channels:\n * {404: …, \"404\": …}` is legal YAML (a number and a string are distinct\n * keys), but the resolved walk (`asyncapiSlots` reads `Object.entries` of\n * `toJS()`) keeps the LAST pair while every string-matched AST access above\n * finds the FIRST: the walker would grade one pair and the writer replace\n * the other, and no single read discipline can make the two agree about\n * which node the (section, key) names. Every entry point that walks slots —\n * the pin, the strip, the merge — refuses it by name instead of writing\n * through the wrong pair.\n */\nexport function refuseAmbiguousSlotKeys(doc: Document, source: AsyncapiMergeSource, service: string): void {\n for (const section of SECTIONS) {\n const node = doc.getIn(sectionAstPath(section));\n if (!isMap(node)) continue;\n const seen = new Set<string>();\n for (const it of node.items) {\n if (!isScalar(it.key)) continue;\n const key = String(it.key.value);\n if (seen.has(key)) {\n throw new AsyncapiMergeError(\n source,\n service,\n `'${section}' spells the key '${key}' in two pairs (two YAML scalars that stringify identically, e.g. 404 and \"404\") — loam cannot tell which pair a slot access should read or write; merge them into one entry`,\n \"is ambiguous\",\n );\n }\n seen.add(key);\n }\n }\n}\n\n/**\n * Read one side of an event contract the way every entry point that walks\n * slots must — the shared `readTree`, with the ambiguous-key refusal above run\n * on the AST before the aliases resolve. Two pairs whose keys stringify\n * identically (404 and \"404\") would make the resolved walk and the\n * string-matched writers disagree about which node a slot names, so it is\n * refused on either side, before any verdict is taken.\n */\nexport function readEventTree(text: string, source: AsyncapiMergeSource, service: string): ContractTree {\n return readTree(text, asyncapiRefusal(source, service), (doc) => refuseAmbiguousSlotKeys(doc, source, service));\n}\n\n/**\n * Remove feature-only markers — removal-marker slots, the pins on the slots\n * that survive, and both loam keys at every nested depth of the whole\n * document — before a feature contract is used to create a brand-new living\n * document. Normally coherence refuses the removal shape because there cannot\n * be a removal target; this guard also keeps `--approve` from ever persisting\n * a marker into living docs.\n *\n * Edit the AST in place when every node the strip touches IS a node — the\n * author's comments, key order and formatting are theirs to keep. An alias\n * cannot be edited in place (there is one shared value behind it, and editing\n * a slot through it would change every use), and a slot whose KEY is not a\n * scalar is not findable by name at all — so a section holding either gets\n * rewritten from the resolved tree instead: losing formatting is a cost,\n * shipping the marker is a corruption. The openapi strip's two-branch\n * discipline, per section.\n */\nexport function stripAsyncapiMarkers(featureText: string, service: string): string {\n // Aliases resolved once with the document's own anchor context — an\n // individual node's toJSON() loses it (the openapi merge's lesson).\n const { doc: feature, plain } = readEventTree(featureText, \"feature\", service);\n\n let stripped = false;\n for (const section of SECTIONS) {\n // Shape from the RESOLVED tree, for the same reason the merge reads it\n // there: an aliased section is not a map node, and treating it as\n // \"nothing to strip\" is how a feature-only marker reached a living\n // contract on the OpenAPI axis.\n const sectionPlain = sectionAstPath(section).reduce<unknown>(\n (node, step) => (isRecord(node) ? node[step] : undefined),\n plain,\n );\n if (!isRecord(sectionPlain)) continue;\n\n const cleaned: Record<string, unknown> = {};\n // `kept: undefined` means the slot goes — a resolved YAML value is never\n // undefined itself (`~` reads as null), so the sentinel is unambiguous.\n const edits: Array<{ key: string; kept: unknown }> = [];\n for (const [key, value] of Object.entries(sectionPlain)) {\n // A removal marker asserts a slot rather than declaring content; the\n // create branch has no target to delete, so the slot itself goes.\n const kept = isSlotRemoval(value) ? undefined : withoutFeatureKeysDeep(value);\n if (kept !== value) edits.push({ key, kept });\n if (kept !== undefined) cleaned[key] = kept;\n }\n if (edits.length === 0) continue;\n stripped = true;\n\n // In place only when every touched node is reachable: the section is a\n // real map and each edited slot's pair is findable by string key.\n const editable =\n isMap(feature.getIn(sectionAstPath(section))) &&\n edits.every(({ key }) => slotPair(feature, section, key) !== undefined);\n if (!editable) {\n feature.setIn(sectionAstPath(section), cleaned);\n continue;\n }\n for (const { key, kept } of edits) {\n if (kept === undefined) deleteSlot(feature, section, key);\n // The surviving slot's value is replaced wholesale with the deep-strip:\n // a pin at slot depth could be deleted key-by-key, but a marker nested\n // on an inline channel message sits where no fixed path reaches — the\n // resolved copy is the one branch both depths share.\n else setSlotValue(feature, section, key, kept);\n }\n }\n\n if (isRecord(plain)) stripped = stripBeyondSections(feature, plain) || stripped;\n\n // The root `x-loam-baselines` record, on EVERY branch — including the\n // verbatim early return below. `stripBeyondSections` cannot do it: it\n // filters on FEATURE_ONLY_KEYS, which holds the two IN-VALUE keys and not\n // this one, so the record's own subtree (16-hex digests) carries no loam key\n // for the deep strip to find and the whole entry rides out untouched. That\n // is the create branch publishing loam's bookkeeping into a living\n // asyncapi.yaml, where nothing catches it: `removalMarkerPaths` — validate's\n // marker sweep on this axis — only ever looks for `x-loam-remove`.\n if (isRecord(plain) && ASYNCAPI_BASELINES_KEY in plain) {\n feature.deleteIn([ASYNCAPI_BASELINES_KEY]);\n stripped = true;\n }\n\n if (!stripped) return featureText;\n return writeTree(feature, asyncapiRefusal(\"feature\", service));\n}\n\n/**\n * The strip's reach OUTSIDE the three sections: the document root's own\n * keys, `info`, `servers`, and the `components` siblings of `messages` —\n * everything the section loop above deliberately does not touch. A loam key\n * here is feature-only exactly as it is inside a slot, and the create branch\n * used to publish it (an `x-loam-remove: true` at the root reached a living\n * contract with zero diagnosis). A loam key at this depth is deleted; any\n * other entry whose subtree carries one is replaced with its deep-strip,\n * through the same string-matched pair access the slot writers use.\n */\nfunction stripBeyondSections(feature: Document, plain: Record<string, unknown>): boolean {\n let stripped = false;\n const strip = (path: string[], key: string, value: unknown): void => {\n const parent = path.length === 0 ? feature.contents : feature.getIn(path);\n if (FEATURE_ONLY_KEYS.has(key)) {\n stripped = deleteKeyIn(parent, key) || stripped;\n return;\n }\n const kept = withoutFeatureKeysDeep(value);\n if (kept === value) return;\n stripped = true;\n const pair = pairAt(parent, key);\n // The section loop's fallback, one entry wide: a pair no string can find\n // is rewritten from the resolved tree rather than left carrying markers.\n if (pair !== undefined) pair.value = feature.createNode(kept);\n else feature.setIn([...path, key], kept);\n };\n for (const [key, value] of Object.entries(plain)) {\n if (key === \"channels\" || key === \"operations\") continue;\n if (key === \"components\") {\n // `components.messages` is the section loop's; the siblings are ours.\n if (!isRecord(value)) continue;\n for (const [ck, cv] of Object.entries(value)) {\n if (ck !== \"messages\") strip([\"components\"], ck, cv);\n }\n continue;\n }\n strip([], key, value);\n }\n return stripped;\n}\n"]}
@@ -18,16 +18,17 @@
18
18
  * writing through one would rewrite every other node that aliases it.
19
19
  */
20
20
  import { isDeepStrictEqual } from "node:util";
21
- import { isMap, parseDocument } from "yaml";
22
- import { classifyBaselineDigests } from "../../openapi/digest.js";
23
- import { asyncapiSlots, slotDigest } from "../digest.js";
21
+ import { isMap } from "yaml";
22
+ import { classifyBaselineDigests, withoutFeatureKeysDeep } from "../../openapi/digest.js";
23
+ import { asyncapiSlots, slotDigest, slotKey, slotsByKey } from "../digest.js";
24
24
  import { danglingRefs } from "../depth.js";
25
25
  import { messageName } from "../read.js";
26
26
  import { readBaselineRecord } from "../../openapi/baseline/record.js";
27
27
  import { asyncapiSurfaces } from "../baseline/surfaces.js";
28
- import { errorMessage, AsyncapiMergeError } from "./error.js";
28
+ import { resolveTree, writeTree } from "../../openapi/contract-tree.js";
29
+ import { asyncapiRefusal } from "./error.js";
29
30
  import { mergeAsyncapiComponents } from "./components.js";
30
- import { deleteSlot, refuseAmbiguousSlotKeys, sectionAstPath, setSlotValue, slotLabel, withoutFeatureMarkers, writableSection, } from "./markers.js";
31
+ import { deleteSlot, readEventTree, sectionAstPath, setSlotValue, slotLabel, writableSection, } from "./markers.js";
31
32
  /**
32
33
  * Merge the feature's slots into the living AsyncAPI structurally (YAML AST,
33
34
  * not text splicing). A feature document restating NOTHING — neither a slot
@@ -42,24 +43,9 @@ import { deleteSlot, refuseAmbiguousSlotKeys, sectionAstPath, setSlotValue, slot
42
43
  * published goes through the deep marker strip.
43
44
  */
44
45
  export function mergeAsyncapiSlots(livingText, featureText, service) {
45
- const feature = parseDocument(featureText);
46
- if (feature.errors.length > 0) {
47
- throw new AsyncapiMergeError("feature", service, feature.errors[0].message);
48
- }
49
- // Two pairs whose keys stringify identically (404 and "404") would make
50
- // the resolved walk and the string-matched writers disagree about which
51
- // node a slot names — refused on either side, before any verdict is taken.
52
- refuseAmbiguousSlotKeys(feature, "feature", service);
53
- let featPlain;
54
- try {
55
- // Resolve aliases once with the document's own anchor context. Calling an
56
- // individual AST node's toJSON() loses that context and can silently turn
57
- // an aliased slot into the wrong value.
58
- featPlain = feature.toJS() ?? {};
59
- }
60
- catch (error) {
61
- throw new AsyncapiMergeError("feature", service, errorMessage(error));
62
- }
46
+ // Ambiguous slot keys refused on either side, before any verdict is taken,
47
+ // and aliases resolved once with the document's own anchor context.
48
+ const { plain: featPlain } = readEventTree(featureText, "feature", service);
63
49
  const featSlots = asyncapiSlots(featPlain);
64
50
  // "Is there anything to merge?" is a question about the WHOLE delta, and it
65
51
  // was asked of the three slot sections alone — an absence test standing in
@@ -73,32 +59,22 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
73
59
  // there was no writer outside the slot loop to fall through to.
74
60
  if (featSlots.length === 0 && asyncapiSurfaces(featPlain).length === 0)
75
61
  return noop();
76
- const living = parseDocument(livingText);
77
- if (living.errors.length > 0) {
78
- throw new AsyncapiMergeError("living", service, living.errors[0].message);
79
- }
80
- refuseAmbiguousSlotKeys(living, "living", service);
81
- let livingPlain;
82
- try {
83
- livingPlain = living.toJS() ?? {};
84
- }
85
- catch (error) {
86
- throw new AsyncapiMergeError("living", service, errorMessage(error));
87
- }
88
- const livingBySlot = new Map(asyncapiSlots(livingPlain).map((slot) => [`${slot.section}\0${slot.key}`, slot]));
62
+ const { doc: living, plain: livingPlain } = readEventTree(livingText, "living", service);
63
+ const livingBySlot = slotsByKey(asyncapiSlots(livingPlain));
89
64
  // Read ONCE here, not per surface: `problems` are deliberately ignored — a
90
65
  // malformed record is the gate's diagnosis (`asyncapi.baseline-invalid`) and
91
66
  // a parser never prints, so what reaches the surface merge is whatever read
92
67
  // back cleanly, and an entry that did not is simply absent (= unpinned).
93
68
  const { record } = readBaselineRecord(featPlain);
94
69
  const modified = [];
70
+ const modifiedMessageNames = new Map();
95
71
  const removed = [];
96
72
  const quoted = [];
97
73
  const baselineStale = [];
98
74
  const touched = new Set();
99
75
  let edited = false;
100
76
  for (const slot of featSlots) {
101
- const before = livingBySlot.get(`${slot.section}\0${slot.key}`);
77
+ const before = livingBySlot.get(slotKey(slot));
102
78
  const name = slot.section === "components.messages" ? messageName(slot.node, slot.key) : undefined;
103
79
  const at = { section: slot.section, label: slotLabel(slot.section, slot.key, name) };
104
80
  if (slot.remove) {
@@ -119,8 +95,8 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
119
95
  continue;
120
96
  }
121
97
  const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, before === undefined ? undefined : slotDigest(before.node));
122
- // A QUOTE is not a merge input. The author wrote this slot down because
123
- // the document is complete, not because they changed it, so the living
98
+ // A QUOTE is not a merge input. The author wrote this slot down as a
99
+ // restatement, not because they changed it, so the living
124
100
  // contract keeps whatever it holds — including a change that landed after
125
101
  // this delta was written. Mechanical, not a judgement: `--approve` does
126
102
  // not turn it back on.
@@ -130,14 +106,22 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
130
106
  }
131
107
  if (verdict === "stale")
132
108
  baselineStale.push(at);
133
- const publish = withoutFeatureMarkers(slot.node);
109
+ // The slot's PUBLISHABLE value: every feature-only key removed, at any
110
+ // depth — the top-level pin, a nested pin or marker on an inline channel
111
+ // message, and anything an author buried deeper. What the merge writes
112
+ // and what the create branch keeps; a removal-marker slot is never
113
+ // published at all (the merge deletes its target, the strip drops it).
114
+ const publish = withoutFeatureKeysDeep(slot.node);
134
115
  // An unpinned restatement equal to the living slot has nothing to write —
135
116
  // the component closure's rule, kept here so a never-rebased delta that
136
117
  // merely restates the contract does not churn its bytes.
137
118
  if (before !== undefined && isDeepStrictEqual(before.node, publish))
138
119
  continue;
139
- if (before !== undefined)
120
+ if (before !== undefined) {
140
121
  modified.push(at);
122
+ if (slot.section === "components.messages")
123
+ modifiedMessageNames.set(at.label, messageName(before.node, slot.key));
124
+ }
141
125
  writableSection(living, livingPlain, sectionAstPath(slot.section), service);
142
126
  // Without the strip the living contract would grow a pin to a version of
143
127
  // itself — and, on this axis, a marker nested on an inline channel
@@ -175,19 +159,13 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
175
159
  // byte-identity under an all-quote delta is the whole point of the pin.
176
160
  if (!edited) {
177
161
  return {
178
- text: null, modified, removed, quoted, baselineStale,
162
+ text: null, modified, modifiedMessageNames, removed, quoted, baselineStale,
179
163
  componentsModified, componentsQuoted, componentsStale, unresolved: [],
180
164
  };
181
165
  }
182
- let mergedPlain;
183
- let text;
184
- try {
185
- mergedPlain = living.toJS() ?? {};
186
- text = living.toString();
187
- }
188
- catch (error) {
189
- throw new AsyncapiMergeError("living", service, errorMessage(error));
190
- }
166
+ const fail = asyncapiRefusal("living", service);
167
+ const mergedPlain = resolveTree(living, fail);
168
+ const text = writeTree(living, fail);
191
169
  // The ref sweep grades what the MERGE would leave dangling: a merged slot
192
170
  // pointing at something neither document defines, or a removal that took a
193
171
  // target the living document still references. Refs already dangling in the
@@ -197,7 +175,7 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
197
175
  const preexisting = new Set(danglingRefs(livingPlain));
198
176
  const unresolved = danglingRefs(mergedPlain).filter((ref) => !preexisting.has(ref));
199
177
  return {
200
- text, modified, removed, quoted, baselineStale,
178
+ text, modified, modifiedMessageNames, removed, quoted, baselineStale,
201
179
  componentsModified, componentsQuoted, componentsStale, unresolved,
202
180
  };
203
181
  }
@@ -208,7 +186,7 @@ export function mergeAsyncapiSlots(livingText, featureText, service) {
208
186
  */
209
187
  function noop() {
210
188
  return {
211
- text: null, modified: [], removed: [], quoted: [], baselineStale: [],
189
+ text: null, modified: [], modifiedMessageNames: new Map(), removed: [], quoted: [], baselineStale: [],
212
190
  componentsModified: [], componentsQuoted: [], componentsStale: [], unresolved: [],
213
191
  };
214
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/merge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The archive-time merge on the event axis: a feature's asyncapi delta folded\n * into the living contract, and every condition the caller has to surface\n * afterwards — the OpenAPI merge's shape, slot-keyed instead of path-keyed.\n *\n * The result type carries far more than the merged text on purpose. A merge\n * that quietly overwrote a slot, deleted a message, or ran against a stale\n * baseline is not an error — it is a fact the archiving human has to be told,\n * and a return value nobody can forget to read is the only place that\n * survives a refactor.\n *\n * Every SHAPE question — which slots does the feature declare, what does the\n * living contract hold at a (section, key) — is answered from the RESOLVED\n * plain trees, never from the AST node: an alias node is not a map even when\n * it resolves to one, and reading it as absent is how the OpenAPI merge once\n * dropped whole contract deltas. The WRITES walk the AST, string-key-matched,\n * and refuse aliases — one shared value backs every use of an anchor, so\n * writing through one would rewrite every other node that aliases it.\n */\nimport { isDeepStrictEqual } from \"node:util\";\nimport { isMap, parseDocument } from \"yaml\";\nimport { classifyBaselineDigests } from \"../../openapi/digest.js\";\nimport { asyncapiSlots, slotDigest, type AsyncapiSection } from \"../digest.js\";\nimport { danglingRefs } from \"../depth.js\";\nimport { messageName } from \"../read.js\";\nimport { readBaselineRecord } from \"../../openapi/baseline/record.js\";\nimport { asyncapiSurfaces } from \"../baseline/surfaces.js\";\nimport { errorMessage, AsyncapiMergeError } from \"./error.js\";\nimport { mergeAsyncapiComponents } from \"./components.js\";\nimport {\n deleteSlot,\n refuseAmbiguousSlotKeys,\n sectionAstPath,\n setSlotValue,\n slotLabel,\n withoutFeatureMarkers,\n writableSection,\n} from \"./markers.js\";\n\n/** One slot the merge acted on — the section decides which per-section code the plan reports. */\nexport interface AsyncapiSlotOutcome {\n section: AsyncapiSection;\n /** `slotLabel` of the slot — the message's join name included where it has one. */\n label: string;\n}\n\n/** What an asyncapi slot merge computed, including every condition the caller must surface. */\nexport interface AsyncapiMergeResult {\n /**\n * The merged living document, or null when nothing was written — every slot\n * and surface a quote, or the delta restating nothing at all. It used to be\n * null for a document declaring no SLOT whatever its components said, and a\n * delta whose whole change was a shared schema archived at exit 0 having\n * merged nothing.\n */\n text: string | null;\n /** Living slots overwritten with different content. */\n modified: AsyncapiSlotOutcome[];\n /** Living slots deleted by `x-loam-remove: true` markers. */\n removed: AsyncapiSlotOutcome[];\n /**\n * Slots the delta QUOTED — pinned, and equal to their own baseline — so the\n * merge left the living contract's own copy alone. Reported rather than\n * silent: \"your delta mentions this and I did not write it\" is exactly the\n * sentence whose absence made the OpenAPI revert invisible.\n */\n quoted: AsyncapiSlotOutcome[];\n /**\n * Slots whose pin matches neither the delta's own content nor the living\n * one: edited here AND changed by somebody else in between.\n * `asyncapi.baseline-stale` refuses these at the gate; the merge still\n * overwrites, because reaching it at all means `--approve` said to.\n */\n baselineStale: AsyncapiSlotOutcome[];\n /**\n * `<kind>/<name>` of living component SURFACES overwritten with different\n * content — the three fields below are the surface half's mirror of\n * `modified`, `quoted` and `baselineStale`, and they are spelled together on\n * the result and on `noop()` for the same reason those are: the plan reads\n * this object field by field, and a key present on one shape and missing\n * from the other makes \"no delta\" and \"a delta that merged nothing\"\n * structurally different answers to the same question.\n */\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 /** Local refs the MERGE left dangling: unresolved in the merged tree and not already dangling in living. */\n unresolved: string[];\n}\n\n/**\n * Merge the feature's slots into the living AsyncAPI structurally (YAML AST,\n * not text splicing). A feature document restating NOTHING — neither a slot\n * nor a component surface — is a successful no-op; one whose whole delta is a\n * `components.schemas` entry is merged like any other, through\n * `mergeAsyncapiComponents` at the end of this function. Per slot, the pin\n * decides: a QUOTE is never a merge input — not even\n * under `--approve`, because overriding a gate is a decision and reverting a\n * slot nobody edited is a bug — while `stale` still writes, since reaching\n * the merge at all means `--approve` said to. A removal marker deletes its\n * exact target and the section key when the section empties; everything\n * published goes through the deep marker strip.\n */\nexport function mergeAsyncapiSlots(\n livingText: string,\n featureText: string,\n service: string,\n): AsyncapiMergeResult {\n const feature = parseDocument(featureText);\n if (feature.errors.length > 0) {\n throw new AsyncapiMergeError(\"feature\", service, feature.errors[0]!.message);\n }\n // Two pairs whose keys stringify identically (404 and \"404\") would make\n // the resolved walk and the string-matched writers disagree about which\n // node a slot names — refused on either side, before any verdict is taken.\n refuseAmbiguousSlotKeys(feature, \"feature\", service);\n let featPlain: unknown;\n try {\n // Resolve aliases once with the document's own anchor context. Calling an\n // individual AST node's toJSON() loses that context and can silently turn\n // an aliased slot into the wrong value.\n featPlain = feature.toJS() ?? {};\n } catch (error) {\n throw new AsyncapiMergeError(\"feature\", service, errorMessage(error));\n }\n const featSlots = asyncapiSlots(featPlain);\n // \"Is there anything to merge?\" is a question about the WHOLE delta, and it\n // was asked of the three slot sections alone — an absence test standing in\n // for the decision. It returned before the living document was even parsed,\n // so `mergeAsyncapiComponents` (the last thing this function does, and the\n // only thing that writes a component surface) never ran: a feature whose\n // entire change was a `components.schemas` entry passed the gate and\n // archived at exit 0 having merged NOTHING. The surfaces answer it too now,\n // over the ONE enumeration the gate and the rebase plan already grade the\n // delta with. Deleting the early return alone would have changed nothing:\n // there was no writer outside the slot loop to fall through to.\n if (featSlots.length === 0 && asyncapiSurfaces(featPlain).length === 0) return noop();\n\n const living = parseDocument(livingText);\n if (living.errors.length > 0) {\n throw new AsyncapiMergeError(\"living\", service, living.errors[0]!.message);\n }\n refuseAmbiguousSlotKeys(living, \"living\", service);\n let livingPlain: unknown;\n try {\n livingPlain = living.toJS() ?? {};\n } catch (error) {\n throw new AsyncapiMergeError(\"living\", service, errorMessage(error));\n }\n const livingBySlot = new Map(asyncapiSlots(livingPlain).map((slot) => [`${slot.section}\\0${slot.key}`, slot]));\n // Read ONCE here, not per surface: `problems` are deliberately ignored — a\n // malformed record is the gate's diagnosis (`asyncapi.baseline-invalid`) and\n // a parser never prints, so what reaches the surface merge is whatever read\n // back cleanly, and an entry that did not is simply absent (= unpinned).\n const { record } = readBaselineRecord(featPlain);\n\n const modified: AsyncapiSlotOutcome[] = [];\n const removed: AsyncapiSlotOutcome[] = [];\n const quoted: AsyncapiSlotOutcome[] = [];\n const baselineStale: AsyncapiSlotOutcome[] = [];\n const touched = new Set<AsyncapiSection>();\n let edited = false;\n for (const slot of featSlots) {\n const before = livingBySlot.get(`${slot.section}\\0${slot.key}`);\n const name = slot.section === \"components.messages\" ? messageName(slot.node, slot.key) : undefined;\n const at = { section: slot.section, label: slotLabel(slot.section, slot.key, name) };\n if (slot.remove) {\n // Coherence validates the marker and gates absent/mismatched targets.\n // The merge remains defensive under --approve: never delete a different\n // message merely because it occupies the requested slot. Channel and\n // operation identity IS the key, so presence is the whole match there.\n if (before === undefined) continue;\n if (slot.section === \"components.messages\" && messageName(before.node, slot.key) !== name) continue;\n writableSection(living, livingPlain, sectionAstPath(slot.section), service);\n if (deleteSlot(living, slot.section, slot.key)) {\n removed.push(at);\n touched.add(slot.section);\n edited = true;\n }\n continue;\n }\n\n const verdict = classifyBaselineDigests(\n slot.basedOn,\n slot.digest,\n before === undefined ? undefined : slotDigest(before.node),\n );\n // A QUOTE is not a merge input. The author wrote this slot down because\n // the document is complete, not because they changed it, so the living\n // contract keeps whatever it holds — including a change that landed after\n // this delta was written. Mechanical, not a judgement: `--approve` does\n // not turn it back on.\n if (verdict === \"quote\") {\n quoted.push(at);\n continue;\n }\n if (verdict === \"stale\") baselineStale.push(at);\n const publish = withoutFeatureMarkers(slot.node);\n // An unpinned restatement equal to the living slot has nothing to write —\n // the component closure's rule, kept here so a never-rebased delta that\n // merely restates the contract does not churn its bytes.\n if (before !== undefined && isDeepStrictEqual(before.node, publish)) continue;\n if (before !== undefined) modified.push(at);\n writableSection(living, livingPlain, sectionAstPath(slot.section), service);\n // Without the strip the living contract would grow a pin to a version of\n // itself — and, on this axis, a marker nested on an inline channel\n // message, invisible to the slot walker (SCHEMA.md's channel-interior\n // decision: the strip works at that nested depth exactly as at slot depth).\n setSlotValue(living, slot.section, slot.key, publish);\n edited = true;\n }\n\n // Removing the last slot leaves a section the contract still spells and\n // nothing declares — delete the emptied mapping, and `components` too when\n // the messages were all it held. After the whole loop, so a removal and an\n // upsert into one section never race the cleanup.\n for (const section of touched) {\n const node = living.getIn(sectionAstPath(section));\n if (!isMap(node) || node.items.length > 0) continue;\n living.deleteIn(sectionAstPath(section));\n if (section === \"components.messages\") {\n const components = living.getIn([\"components\"]);\n if (isMap(components) && components.items.length === 0) living.deleteIn([\"components\"]);\n }\n }\n\n // The surface half, after the slot loop and its section cleanup so a write\n // outside the three sections can never race them. `edited` has to take the\n // closure's answer: without it the verdicts below would be computed and\n // reported while the `!edited` return discarded the document they were\n // computed over — the defect surviving in a new shape, with the plan naming\n // a schema that never reached the living contract.\n const surfaces = mergeAsyncapiComponents({ living, featPlain, livingPlain, record, service });\n const { componentsModified, componentsQuoted, componentsStale } = surfaces;\n if (surfaces.copied) edited = true;\n\n // Nothing written: the living document must not even be re-serialized —\n // byte-identity under an all-quote delta is the whole point of the pin.\n if (!edited) {\n return {\n text: null, modified, removed, quoted, baselineStale,\n componentsModified, componentsQuoted, componentsStale, unresolved: [],\n };\n }\n\n let mergedPlain: unknown;\n let text: string;\n try {\n mergedPlain = living.toJS() ?? {};\n text = living.toString();\n } catch (error) {\n throw new AsyncapiMergeError(\"living\", service, errorMessage(error));\n }\n // The ref sweep grades what the MERGE would leave dangling: a merged slot\n // pointing at something neither document defines, or a removal that took a\n // target the living document still references. Refs already dangling in the\n // living contract are validate's finding (asyncapi.ref-unresolved, warn),\n // not this feature's doing — gating the archive on pre-existing rot would\n // block every delta over an already-rotten contract.\n const preexisting = new Set(danglingRefs(livingPlain));\n const unresolved = danglingRefs(mergedPlain).filter((ref) => !preexisting.has(ref));\n return {\n text, modified, removed, quoted, baselineStale,\n componentsModified, componentsQuoted, componentsStale, unresolved,\n };\n}\n\n/**\n * The successful \"the feature document restates nothing at all\" answer. Every\n * key of the result is spelled here, empty, rather than left off — the reason\n * `AsyncapiMergeResult` gives for the three surface fields.\n */\nfunction noop(): AsyncapiMergeResult {\n return {\n text: null, modified: [], removed: [], quoted: [], baselineStale: [],\n componentsModified: [], componentsQuoted: [], componentsStale: [], unresolved: [],\n };\n}\n"]}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/merge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The archive-time merge on the event axis: a feature's asyncapi delta folded\n * into the living contract, and every condition the caller has to surface\n * afterwards — the OpenAPI merge's shape, slot-keyed instead of path-keyed.\n *\n * The result type carries far more than the merged text on purpose. A merge\n * that quietly overwrote a slot, deleted a message, or ran against a stale\n * baseline is not an error — it is a fact the archiving human has to be told,\n * and a return value nobody can forget to read is the only place that\n * survives a refactor.\n *\n * Every SHAPE question — which slots does the feature declare, what does the\n * living contract hold at a (section, key) — is answered from the RESOLVED\n * plain trees, never from the AST node: an alias node is not a map even when\n * it resolves to one, and reading it as absent is how the OpenAPI merge once\n * dropped whole contract deltas. The WRITES walk the AST, string-key-matched,\n * and refuse aliases — one shared value backs every use of an anchor, so\n * writing through one would rewrite every other node that aliases it.\n */\nimport { isDeepStrictEqual } from \"node:util\";\nimport { isMap } from \"yaml\";\nimport { classifyBaselineDigests, withoutFeatureKeysDeep } from \"../../openapi/digest.js\";\nimport { asyncapiSlots, slotDigest, slotKey, slotsByKey, type AsyncapiSection } from \"../digest.js\";\nimport { danglingRefs } from \"../depth.js\";\nimport { messageName } from \"../read.js\";\nimport { readBaselineRecord } from \"../../openapi/baseline/record.js\";\nimport { asyncapiSurfaces } from \"../baseline/surfaces.js\";\nimport { resolveTree, writeTree } from \"../../openapi/contract-tree.js\";\nimport { asyncapiRefusal } from \"./error.js\";\nimport { mergeAsyncapiComponents } from \"./components.js\";\nimport {\n deleteSlot,\n readEventTree,\n sectionAstPath,\n setSlotValue,\n slotLabel,\n writableSection,\n} from \"./markers.js\";\n\n/** One slot the merge acted on — the section decides which per-section code the plan reports. */\nexport interface AsyncapiSlotOutcome {\n section: AsyncapiSection;\n /** `slotLabel` of the slot — the message's join name included where it has one. */\n label: string;\n}\n\n/** What an asyncapi slot merge computed, including every condition the caller must surface. */\nexport interface AsyncapiMergeResult {\n /**\n * The merged living document, or null when nothing was written — every slot\n * and surface a quote, or the delta restating nothing at all. It used to be\n * null for a document declaring no SLOT whatever its components said, and a\n * delta whose whole change was a shared schema archived at exit 0 having\n * merged nothing.\n */\n text: string | null;\n /** Living slots overwritten with different content. */\n modified: AsyncapiSlotOutcome[];\n /**\n * The message name each overwritten `components.messages` slot carries in\n * the LIVING contract, keyed by its `modified` label — the join token\n * consumers name today (`Consumes:` lines, `consumes` edges), which\n * `loam archive` asks the fleet's consumer scan with to say who the\n * overwrite lands on. Channels and operations are absent: nothing joins to\n * them by name.\n */\n modifiedMessageNames: ReadonlyMap<string, string>;\n /** Living slots deleted by `x-loam-remove: true` markers. */\n removed: AsyncapiSlotOutcome[];\n /**\n * Slots the delta QUOTED — pinned, and equal to their own baseline — so the\n * merge left the living contract's own copy alone. Reported rather than\n * silent: \"your delta mentions this and I did not write it\" is exactly the\n * sentence whose absence made the OpenAPI revert invisible.\n */\n quoted: AsyncapiSlotOutcome[];\n /**\n * Slots whose pin matches neither the delta's own content nor the living\n * one: edited here AND changed by somebody else in between.\n * `asyncapi.baseline-stale` refuses these at the gate; the merge still\n * overwrites, because reaching it at all means `--approve` said to.\n */\n baselineStale: AsyncapiSlotOutcome[];\n /**\n * `<kind>/<name>` of living component SURFACES overwritten with different\n * content — the three fields below are the surface half's mirror of\n * `modified`, `quoted` and `baselineStale`, and they are spelled together on\n * the result and on `noop()` for the same reason those are: the plan reads\n * this object field by field, and a key present on one shape and missing\n * from the other makes \"no delta\" and \"a delta that merged nothing\"\n * structurally different answers to the same question.\n */\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 /** Local refs the MERGE left dangling: unresolved in the merged tree and not already dangling in living. */\n unresolved: string[];\n}\n\n/**\n * Merge the feature's slots into the living AsyncAPI structurally (YAML AST,\n * not text splicing). A feature document restating NOTHING — neither a slot\n * nor a component surface — is a successful no-op; one whose whole delta is a\n * `components.schemas` entry is merged like any other, through\n * `mergeAsyncapiComponents` at the end of this function. Per slot, the pin\n * decides: a QUOTE is never a merge input — not even\n * under `--approve`, because overriding a gate is a decision and reverting a\n * slot nobody edited is a bug — while `stale` still writes, since reaching\n * the merge at all means `--approve` said to. A removal marker deletes its\n * exact target and the section key when the section empties; everything\n * published goes through the deep marker strip.\n */\nexport function mergeAsyncapiSlots(\n livingText: string,\n featureText: string,\n service: string,\n): AsyncapiMergeResult {\n // Ambiguous slot keys refused on either side, before any verdict is taken,\n // and aliases resolved once with the document's own anchor context.\n const { plain: featPlain } = readEventTree(featureText, \"feature\", service);\n const featSlots = asyncapiSlots(featPlain);\n // \"Is there anything to merge?\" is a question about the WHOLE delta, and it\n // was asked of the three slot sections alone — an absence test standing in\n // for the decision. It returned before the living document was even parsed,\n // so `mergeAsyncapiComponents` (the last thing this function does, and the\n // only thing that writes a component surface) never ran: a feature whose\n // entire change was a `components.schemas` entry passed the gate and\n // archived at exit 0 having merged NOTHING. The surfaces answer it too now,\n // over the ONE enumeration the gate and the rebase plan already grade the\n // delta with. Deleting the early return alone would have changed nothing:\n // there was no writer outside the slot loop to fall through to.\n if (featSlots.length === 0 && asyncapiSurfaces(featPlain).length === 0) return noop();\n\n const { doc: living, plain: livingPlain } = readEventTree(livingText, \"living\", service);\n const livingBySlot = slotsByKey(asyncapiSlots(livingPlain));\n // Read ONCE here, not per surface: `problems` are deliberately ignored — a\n // malformed record is the gate's diagnosis (`asyncapi.baseline-invalid`) and\n // a parser never prints, so what reaches the surface merge is whatever read\n // back cleanly, and an entry that did not is simply absent (= unpinned).\n const { record } = readBaselineRecord(featPlain);\n\n const modified: AsyncapiSlotOutcome[] = [];\n const modifiedMessageNames = new Map<string, string>();\n const removed: AsyncapiSlotOutcome[] = [];\n const quoted: AsyncapiSlotOutcome[] = [];\n const baselineStale: AsyncapiSlotOutcome[] = [];\n const touched = new Set<AsyncapiSection>();\n let edited = false;\n for (const slot of featSlots) {\n const before = livingBySlot.get(slotKey(slot));\n const name = slot.section === \"components.messages\" ? messageName(slot.node, slot.key) : undefined;\n const at = { section: slot.section, label: slotLabel(slot.section, slot.key, name) };\n if (slot.remove) {\n // Coherence validates the marker and gates absent/mismatched targets.\n // The merge remains defensive under --approve: never delete a different\n // message merely because it occupies the requested slot. Channel and\n // operation identity IS the key, so presence is the whole match there.\n if (before === undefined) continue;\n if (slot.section === \"components.messages\" && messageName(before.node, slot.key) !== name) continue;\n writableSection(living, livingPlain, sectionAstPath(slot.section), service);\n if (deleteSlot(living, slot.section, slot.key)) {\n removed.push(at);\n touched.add(slot.section);\n edited = true;\n }\n continue;\n }\n\n const verdict = classifyBaselineDigests(\n slot.basedOn,\n slot.digest,\n before === undefined ? undefined : slotDigest(before.node),\n );\n // A QUOTE is not a merge input. The author wrote this slot down as a\n // restatement, not because they changed it, so the living\n // contract keeps whatever it holds — including a change that landed after\n // this delta was written. Mechanical, not a judgement: `--approve` does\n // not turn it back on.\n if (verdict === \"quote\") {\n quoted.push(at);\n continue;\n }\n if (verdict === \"stale\") baselineStale.push(at);\n // The slot's PUBLISHABLE value: every feature-only key removed, at any\n // depth — the top-level pin, a nested pin or marker on an inline channel\n // message, and anything an author buried deeper. What the merge writes\n // and what the create branch keeps; a removal-marker slot is never\n // published at all (the merge deletes its target, the strip drops it).\n const publish = withoutFeatureKeysDeep(slot.node);\n // An unpinned restatement equal to the living slot has nothing to write —\n // the component closure's rule, kept here so a never-rebased delta that\n // merely restates the contract does not churn its bytes.\n if (before !== undefined && isDeepStrictEqual(before.node, publish)) continue;\n if (before !== undefined) {\n modified.push(at);\n if (slot.section === \"components.messages\") modifiedMessageNames.set(at.label, messageName(before.node, slot.key));\n }\n writableSection(living, livingPlain, sectionAstPath(slot.section), service);\n // Without the strip the living contract would grow a pin to a version of\n // itself — and, on this axis, a marker nested on an inline channel\n // message, invisible to the slot walker (SCHEMA.md's channel-interior\n // decision: the strip works at that nested depth exactly as at slot depth).\n setSlotValue(living, slot.section, slot.key, publish);\n edited = true;\n }\n\n // Removing the last slot leaves a section the contract still spells and\n // nothing declares — delete the emptied mapping, and `components` too when\n // the messages were all it held. After the whole loop, so a removal and an\n // upsert into one section never race the cleanup.\n for (const section of touched) {\n const node = living.getIn(sectionAstPath(section));\n if (!isMap(node) || node.items.length > 0) continue;\n living.deleteIn(sectionAstPath(section));\n if (section === \"components.messages\") {\n const components = living.getIn([\"components\"]);\n if (isMap(components) && components.items.length === 0) living.deleteIn([\"components\"]);\n }\n }\n\n // The surface half, after the slot loop and its section cleanup so a write\n // outside the three sections can never race them. `edited` has to take the\n // closure's answer: without it the verdicts below would be computed and\n // reported while the `!edited` return discarded the document they were\n // computed over — the defect surviving in a new shape, with the plan naming\n // a schema that never reached the living contract.\n const surfaces = mergeAsyncapiComponents({ living, featPlain, livingPlain, record, service });\n const { componentsModified, componentsQuoted, componentsStale } = surfaces;\n if (surfaces.copied) edited = true;\n\n // Nothing written: the living document must not even be re-serialized —\n // byte-identity under an all-quote delta is the whole point of the pin.\n if (!edited) {\n return {\n text: null, modified, modifiedMessageNames, removed, quoted, baselineStale,\n componentsModified, componentsQuoted, componentsStale, unresolved: [],\n };\n }\n\n const fail = asyncapiRefusal(\"living\", service);\n const mergedPlain = resolveTree(living, fail);\n const text = writeTree(living, fail);\n // The ref sweep grades what the MERGE would leave dangling: a merged slot\n // pointing at something neither document defines, or a removal that took a\n // target the living document still references. Refs already dangling in the\n // living contract are validate's finding (asyncapi.ref-unresolved, warn),\n // not this feature's doing — gating the archive on pre-existing rot would\n // block every delta over an already-rotten contract.\n const preexisting = new Set(danglingRefs(livingPlain));\n const unresolved = danglingRefs(mergedPlain).filter((ref) => !preexisting.has(ref));\n return {\n text, modified, modifiedMessageNames, removed, quoted, baselineStale,\n componentsModified, componentsQuoted, componentsStale, unresolved,\n };\n}\n\n/**\n * The successful \"the feature document restates nothing at all\" answer. Every\n * key of the result is spelled here, empty, rather than left off — the reason\n * `AsyncapiMergeResult` gives for the three surface fields.\n */\nfunction noop(): AsyncapiMergeResult {\n return {\n text: null, modified: [], modifiedMessageNames: new Map(), removed: [], quoted: [], baselineStale: [],\n componentsModified: [], componentsQuoted: [], componentsStale: [], unresolved: [],\n };\n}\n"]}
@@ -14,10 +14,11 @@
14
14
  * spelling); an inline channel message is channel-slot interior and is never
15
15
  * pinned on its own — SCHEMA.md's recorded decision.
16
16
  */
17
- import { isMap, parseDocument } from "yaml";
18
- import { AsyncapiMergeError, errorMessage } from "./error.js";
19
- import { refuseAmbiguousSlotKeys, slotPair } from "./markers.js";
20
- import { ASYNCAPI_BASELINE_KEY, asyncapiSlots, slotDigest, } from "../digest.js";
17
+ import { isMap } from "yaml";
18
+ import { writeTree } from "../../openapi/contract-tree.js";
19
+ import { asyncapiRefusal } from "./error.js";
20
+ import { readEventTree, slotPair } from "./markers.js";
21
+ import { ASYNCAPI_BASELINE_KEY, asyncapiSlots, slotDigest, slotKey, slotsByKey, } from "../digest.js";
21
22
  /**
22
23
  * Pin every slot in a feature contract against the living one — what
23
24
  * `loam rebase` writes on this axis.
@@ -31,31 +32,9 @@ import { ASYNCAPI_BASELINE_KEY, asyncapiSlots, slotDigest, } from "../digest.js"
31
32
  * operation or message the contract does not carry yet.
32
33
  */
33
34
  export function pinAsyncapiSlots(featureText, livingText, service) {
34
- const doc = parseDocument(featureText);
35
- if (doc.errors.length > 0)
36
- throw new AsyncapiMergeError("feature", service, doc.errors[0].message);
37
- refuseAmbiguousSlotKeys(doc, "feature", service);
38
- let featPlain;
39
- let livingPlain;
40
- try {
41
- featPlain = doc.toJS() ?? {};
42
- }
43
- catch (error) {
44
- throw new AsyncapiMergeError("feature", service, errorMessage(error));
45
- }
46
- try {
47
- const parsed = parseDocument(livingText);
48
- if (parsed.errors.length > 0)
49
- throw new AsyncapiMergeError("living", service, parsed.errors[0].message);
50
- refuseAmbiguousSlotKeys(parsed, "living", service);
51
- livingPlain = parsed.toJS() ?? {};
52
- }
53
- catch (error) {
54
- if (error instanceof AsyncapiMergeError)
55
- throw error;
56
- throw new AsyncapiMergeError("living", service, errorMessage(error));
57
- }
58
- const livingBySlot = new Map(asyncapiSlots(livingPlain).map((slot) => [`${slot.section}\0${slot.key}`, slot]));
35
+ const { doc, plain: featPlain } = readEventTree(featureText, "feature", service);
36
+ const { plain: livingPlain } = readEventTree(livingText, "living", service);
37
+ const livingBySlot = slotsByKey(asyncapiSlots(livingPlain));
59
38
  const pins = [];
60
39
  let edited = false;
61
40
  for (const slot of asyncapiSlots(featPlain)) {
@@ -66,7 +45,7 @@ export function pinAsyncapiSlots(featureText, livingText, service) {
66
45
  continue;
67
46
  const at = { section: slot.section, key: slot.key };
68
47
  const from = slot.basedOn ?? null;
69
- const living = livingBySlot.get(`${slot.section}\0${slot.key}`);
48
+ const living = livingBySlot.get(slotKey(slot));
70
49
  if (living === undefined || living.node === null || typeof living.node !== "object" || Array.isArray(living.node)) {
71
50
  pins.push({ ...at, status: "unresolved", from, to: null });
72
51
  continue;
@@ -97,11 +76,6 @@ export function pinAsyncapiSlots(featureText, livingText, service) {
97
76
  }
98
77
  if (!edited)
99
78
  return { text: null, pins };
100
- try {
101
- return { text: doc.toString(), pins };
102
- }
103
- catch (error) {
104
- throw new AsyncapiMergeError("feature", service, errorMessage(error));
105
- }
79
+ return { text: writeTree(doc, asyncapiRefusal("feature", service)), pins };
106
80
  }
107
81
  //# sourceMappingURL=pin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pin.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/pin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The pin that tells a QUOTE from an EDIT, on the event axis.\n *\n * A feature's asyncapi.yaml is a complete document, so most of what it spells\n * is quotation: the author restated the living contract around the slot they\n * were changing. Without a pin the merge cannot tell the two apart and\n * upserts both — the exact silent-revert the OpenAPI axis's pin closed, with\n * no overlap between two features required, just two deltas over one service.\n *\n * Separate from `./merge.ts` because this is `loam rebase`'s half: it plans\n * the pins a delta is missing, while the merge only ever reads pins already\n * written. Slot-keyed over the three named sections exactly as the merge\n * upserts (core/asyncapi/digest.ts owns the walker and the section→path\n * spelling); an inline channel message is channel-slot interior and is never\n * pinned on its own — SCHEMA.md's recorded decision.\n */\nimport { isMap, parseDocument } from \"yaml\";\nimport { AsyncapiMergeError, errorMessage } from \"./error.js\";\nimport { refuseAmbiguousSlotKeys, slotPair } from \"./markers.js\";\nimport {\n ASYNCAPI_BASELINE_KEY,\n asyncapiSlots,\n slotDigest,\n type AsyncapiSection,\n} from \"../digest.js\";\n\n/** What happened to one slot's `x-loam-based-on`. */\nexport interface AsyncapiSlotPin {\n section: AsyncapiSection;\n key: string;\n status:\n /** It had no pin 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 slot at this (section, key): this feature is adding it. */\n | \"unresolved\"\n /** Written as a YAML alias (one shared value backs every use) or under a key no string can address — loam will not stamp through either. */\n | \"unwritable\";\n from: string | null;\n to: string | null;\n}\n\nexport interface AsyncapiPinPlan {\n /** The rewritten contract, or null when no pin changes. */\n text: string | null;\n pins: AsyncapiSlotPin[];\n}\n\n/**\n * Pin every slot in a feature contract against the living one — what\n * `loam rebase` writes on this axis.\n *\n * The pin is always `slotDigest` of the LIVING slot, never of the delta's\n * own, and that single rule produces both merge verdicts by itself: a slot\n * the author only QUOTED is byte-equal to living, so its pin equals its own\n * content and classifies as a quote; one the author EDITED differs from its\n * pin, so the merge writes it. A slot the living document lacks is\n * `unresolved` and correct — there is no living version of a channel,\n * operation or message the contract does not carry yet.\n */\nexport function pinAsyncapiSlots(featureText: string, livingText: string, service: string): AsyncapiPinPlan {\n const doc = parseDocument(featureText);\n if (doc.errors.length > 0) throw new AsyncapiMergeError(\"feature\", service, doc.errors[0]!.message);\n refuseAmbiguousSlotKeys(doc, \"feature\", service);\n let featPlain: unknown;\n let livingPlain: unknown;\n try {\n featPlain = doc.toJS() ?? {};\n } catch (error) {\n throw new AsyncapiMergeError(\"feature\", service, errorMessage(error));\n }\n try {\n const parsed = parseDocument(livingText);\n if (parsed.errors.length > 0) throw new AsyncapiMergeError(\"living\", service, parsed.errors[0]!.message);\n refuseAmbiguousSlotKeys(parsed, \"living\", service);\n livingPlain = parsed.toJS() ?? {};\n } catch (error) {\n if (error instanceof AsyncapiMergeError) throw error;\n throw new AsyncapiMergeError(\"living\", service, errorMessage(error));\n }\n\n const livingBySlot = new Map(asyncapiSlots(livingPlain).map((slot) => [`${slot.section}\\0${slot.key}`, slot]));\n const pins: AsyncapiSlotPin[] = [];\n let edited = false;\n for (const slot of asyncapiSlots(featPlain)) {\n // A removal marker asserts a slot rather than restating it, so it has\n // nothing to be based on; `asyncapi.remove-target-missing/-mismatch`\n // are what guard it against a living slot that moved under it.\n if (slot.remove) continue;\n\n const at = { section: slot.section, key: slot.key };\n const from = slot.basedOn ?? null;\n const living = livingBySlot.get(`${slot.section}\\0${slot.key}`);\n if (living === undefined || living.node === null || typeof living.node !== \"object\" || Array.isArray(living.node)) {\n pins.push({ ...at, status: \"unresolved\", from, to: null });\n continue;\n }\n const digest = slotDigest(living.node);\n if (from === digest) {\n pins.push({ ...at, status: \"unchanged\", from: digest, to: digest });\n continue;\n }\n // Located string-matched through the same door as every other slot\n // access (markers.ts's slotPair): a channel written `404:` is the YAML\n // number 404, which a raw `getIn(\"404\")` misses — the miss used to grade\n // every non-string-keyed slot `unwritable`, with an alias diagnosis\n // nothing in the document backed, and the gate's own remedy was the\n // command that had just refused. What stays refused: an aliased slot\n // value (one shared value backs every use of an anchor, so stamping\n // through it would pin every other use too — the OpenAPI pin's refusal,\n // for the same data-loss reason) and a slot whose key is not a scalar,\n // which no string can address.\n const pair = slotPair(doc, slot.section, slot.key);\n if (pair === undefined || !isMap(pair.value)) {\n pins.push({ ...at, status: \"unwritable\", from, to: null });\n continue;\n }\n pins.push({ ...at, status: from === null ? \"pinned\" : \"repinned\", from, to: digest });\n pair.value.set(ASYNCAPI_BASELINE_KEY, digest);\n edited = true;\n }\n\n if (!edited) return { text: null, pins };\n try {\n return { text: doc.toString(), pins };\n } catch (error) {\n throw new AsyncapiMergeError(\"feature\", service, errorMessage(error));\n }\n}\n"]}
1
+ {"version":3,"file":"pin.js","sourceRoot":"","sources":["../../../../src/core/asyncapi/merge/pin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The pin that tells a QUOTE from an EDIT, on the event axis.\n *\n * A feature's asyncapi.yaml is a complete document, so most of what it spells\n * is quotation: the author restated the living contract around the slot they\n * were changing. Without a pin the merge cannot tell the two apart and\n * upserts both — the exact silent-revert the OpenAPI axis's pin closed, with\n * no overlap between two features required, just two deltas over one service.\n *\n * Separate from `./merge.ts` because this is `loam rebase`'s half: it plans\n * the pins a delta is missing, while the merge only ever reads pins already\n * written. Slot-keyed over the three named sections exactly as the merge\n * upserts (core/asyncapi/digest.ts owns the walker and the section→path\n * spelling); an inline channel message is channel-slot interior and is never\n * pinned on its own — SCHEMA.md's recorded decision.\n */\nimport { isMap } from \"yaml\";\nimport { writeTree } from \"../../openapi/contract-tree.js\";\nimport { asyncapiRefusal } from \"./error.js\";\nimport { readEventTree, slotPair } from \"./markers.js\";\nimport {\n ASYNCAPI_BASELINE_KEY,\n asyncapiSlots,\n slotDigest,\n slotKey,\n slotsByKey,\n type AsyncapiSection,\n} from \"../digest.js\";\n\n/** What happened to one slot's `x-loam-based-on`. */\nexport interface AsyncapiSlotPin {\n section: AsyncapiSection;\n key: string;\n status:\n /** It had no pin 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 slot at this (section, key): this feature is adding it. */\n | \"unresolved\"\n /** Written as a YAML alias (one shared value backs every use) or under a key no string can address — loam will not stamp through either. */\n | \"unwritable\";\n from: string | null;\n to: string | null;\n}\n\nexport interface AsyncapiPinPlan {\n /** The rewritten contract, or null when no pin changes. */\n text: string | null;\n pins: AsyncapiSlotPin[];\n}\n\n/**\n * Pin every slot in a feature contract against the living one — what\n * `loam rebase` writes on this axis.\n *\n * The pin is always `slotDigest` of the LIVING slot, never of the delta's\n * own, and that single rule produces both merge verdicts by itself: a slot\n * the author only QUOTED is byte-equal to living, so its pin equals its own\n * content and classifies as a quote; one the author EDITED differs from its\n * pin, so the merge writes it. A slot the living document lacks is\n * `unresolved` and correct — there is no living version of a channel,\n * operation or message the contract does not carry yet.\n */\nexport function pinAsyncapiSlots(featureText: string, livingText: string, service: string): AsyncapiPinPlan {\n const { doc, plain: featPlain } = readEventTree(featureText, \"feature\", service);\n const { plain: livingPlain } = readEventTree(livingText, \"living\", service);\n\n const livingBySlot = slotsByKey(asyncapiSlots(livingPlain));\n const pins: AsyncapiSlotPin[] = [];\n let edited = false;\n for (const slot of asyncapiSlots(featPlain)) {\n // A removal marker asserts a slot rather than restating it, so it has\n // nothing to be based on; `asyncapi.remove-target-missing/-mismatch`\n // are what guard it against a living slot that moved under it.\n if (slot.remove) continue;\n\n const at = { section: slot.section, key: slot.key };\n const from = slot.basedOn ?? null;\n const living = livingBySlot.get(slotKey(slot));\n if (living === undefined || living.node === null || typeof living.node !== \"object\" || Array.isArray(living.node)) {\n pins.push({ ...at, status: \"unresolved\", from, to: null });\n continue;\n }\n const digest = slotDigest(living.node);\n if (from === digest) {\n pins.push({ ...at, status: \"unchanged\", from: digest, to: digest });\n continue;\n }\n // Located string-matched through the same door as every other slot\n // access (markers.ts's slotPair): a channel written `404:` is the YAML\n // number 404, which a raw `getIn(\"404\")` misses — the miss used to grade\n // every non-string-keyed slot `unwritable`, with an alias diagnosis\n // nothing in the document backed, and the gate's own remedy was the\n // command that had just refused. What stays refused: an aliased slot\n // value (one shared value backs every use of an anchor, so stamping\n // through it would pin every other use too — the OpenAPI pin's refusal,\n // for the same data-loss reason) and a slot whose key is not a scalar,\n // which no string can address.\n const pair = slotPair(doc, slot.section, slot.key);\n if (pair === undefined || !isMap(pair.value)) {\n pins.push({ ...at, status: \"unwritable\", from, to: null });\n continue;\n }\n pins.push({ ...at, status: from === null ? \"pinned\" : \"repinned\", from, to: digest });\n pair.value.set(ASYNCAPI_BASELINE_KEY, digest);\n edited = true;\n }\n\n if (!edited) return { text: null, pins };\n return { text: writeTree(doc, asyncapiRefusal(\"feature\", service)), pins };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/asyncapi/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * The read model of one AsyncAPI document — what `./read.ts`'s walk produces\n * and every consumer of the axis branches on. Split from `read.ts` when the\n * `asyncapiFromText` extraction pushed that module past the 300-line ceiling:\n * the walk and its result are two subjects (`read.ts` says HOW a document is\n * read, this file says WHAT a read answers), and the type block is the seam\n * that already existed. The doctrine comments travel with the fields they\n * explain — they are the documentation of the model, not of the walk.\n */\n\n/** One message declaration, as the reader sees it. */\nexport interface EventMessage {\n /**\n * The join token — what a landscape edge's `metadata { publishes }` and a\n * requirement's `Publishes:` line name.\n *\n * The Message Object's own `name` when it has one, otherwise the key it is\n * declared under. Both are legal AsyncAPI, and the key is what an author who\n * never wrote a `name` would reasonably expect to reference; falling back to\n * it means a minimal hand-written contract still joins to the spine.\n */\n name: string;\n /**\n * Where it was declared — `components.messages.<key>` or\n * `channels.<key>.messages.<key>`. Carried so `asyncapi.duplicate-message` can\n * name both slots instead of only saying that a name repeats, the way\n * `openapi.duplicate-operationid` names both `METHOD /path` slots.\n */\n slot: string;\n /**\n * Present when the declaration's payload defines no shape a consumer could\n * code against (`payloadUndeclared`, depth.ts). Set only when true, and\n * never set for a payload declaring a non-JSON `schemaFormat`.\n */\n payloadEmpty?: true;\n /**\n * Present when the declaration carries `x-loam-remove: true` — a FEATURE\n * delta retiring it. A marker asserts a slot rather than declaring a\n * message, so it joins nothing: excluded from sent/received and from\n * duplicate counting, the discipline `core/openapi/doc.ts` set for\n * operation markers. Set only when true, like `payloadEmpty`.\n */\n remove?: true;\n}\n\n/** The parse of one AsyncAPI document: what it declares, and whether it could be read at all. */\nexport interface AsyncapiDoc {\n /** Every message declaration in the document, in document order. */\n messages: EventMessage[];\n /** Message names reachable from an `action: send` operation — what this service PRODUCES. */\n sent: string[];\n /** Message names reachable from an `action: receive` operation — what this service CONSUMES. */\n received: string[];\n /**\n * Names declared in more than one slot. Every join on this axis is by name, so\n * two slots claiming one name is the same ambiguity `openapi.duplicate-operationid`\n * grades: the join picks one arbitrarily and the other declaration is invisible.\n */\n duplicateNames: string[];\n /**\n * True when the file EXISTS but cannot be read as an AsyncAPI document —\n * broken YAML, non-UTF-8 bytes, or a document that is not a mapping. A missing\n * file is not unreadable (absence is `service.no-asyncapi`'s question), and an\n * empty one parses to null and honestly declares nothing.\n */\n unreadable: boolean;\n /** The parser's own message, when there is one to quote back. */\n error?: string;\n /** Internal `#/` refs that resolve to nothing in this document (depth.ts). */\n danglingRefs: string[];\n /**\n * Where `x-loam-remove: true` appears — at ANY depth (digest.ts's\n * `removalMarkerPaths`): the three slot depths the format spec gives the\n * key meaning at, inline channel messages (channel-slot interior the merge\n * strips at that nested depth), and every place the key means nothing —\n * the document root, `info`, a `components` sibling. Feature-only\n * bookkeeping wherever it sits: `validate` grades any of these in a LIVING\n * contract as `asyncapi.remove-marker-living`, and the sweep is as deep as\n * the strip so a leaked marker can never be invisible to it.\n */\n markers: string[];\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/asyncapi/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * The read model of one AsyncAPI document — what `./read.ts`'s walk produces\n * and every consumer of the axis branches on. Split from `read.ts` when the\n * `asyncapiFromText` extraction pushed that module past the 300-line ceiling:\n * the walk and its result are two subjects (`read.ts` says HOW a document is\n * read, this file says WHAT a read answers), and the type block is the seam\n * that already existed. The doctrine comments travel with the fields they\n * explain — they are the documentation of the model, not of the walk.\n */\n\n/** One message declaration, as the reader sees it. */\nexport interface EventMessage {\n /**\n * The join token — what a landscape edge's `metadata { publishes }` and a\n * requirement's `Publishes:` line name.\n *\n * The Message Object's own `name` when it has one, otherwise the key it is\n * declared under. Both are legal AsyncAPI, and the key is what an author who\n * never wrote a `name` would reasonably expect to reference; falling back to\n * it means a minimal hand-written contract still joins to the spine.\n */\n name: string;\n /**\n * Where it was declared — `components.messages.<key>` or\n * `channels.<key>.messages.<key>`. Carried so `asyncapi.duplicate-message` can\n * name both slots instead of only saying that a name repeats, the way\n * `openapi.duplicate-operationid` names both `METHOD /path` slots.\n */\n slot: string;\n /**\n * The declaration plus every internal `$ref` it reaches, transitively — the\n * OpenAPI axis's `closureDigester` (core/openapi/digest.ts), so a payload\n * schema factored into `components.schemas` still moves it. Content equality\n * only, and so inside the stance read.ts states: what `loam diff` compares to\n * say a message changed, never a join on a payload field. Absent on a removal\n * marker, which declares nothing to compare.\n */\n closureDigest?: string;\n /**\n * Present when the declaration's payload defines no shape a consumer could\n * code against (`payloadUndeclared`, depth.ts). Set only when true, and\n * never set for a payload declaring a non-JSON `schemaFormat`.\n */\n payloadEmpty?: true;\n /**\n * Present when the declaration carries `x-loam-remove: true` — a FEATURE\n * delta retiring it. A marker asserts a slot rather than declaring a\n * message, so it joins nothing: excluded from sent/received and from\n * duplicate counting, the discipline `core/openapi/doc.ts` set for\n * operation markers. Set only when true, like `payloadEmpty`.\n */\n remove?: true;\n}\n\n/** The parse of one AsyncAPI document: what it declares, and whether it could be read at all. */\nexport interface AsyncapiDoc {\n /** Every message declaration in the document, in document order. */\n messages: EventMessage[];\n /** Message names reachable from an `action: send` operation — what this service PRODUCES. */\n sent: string[];\n /** Message names reachable from an `action: receive` operation — what this service CONSUMES. */\n received: string[];\n /**\n * Names declared in more than one slot. Every join on this axis is by name, so\n * two slots claiming one name is the same ambiguity `openapi.duplicate-operationid`\n * grades: the join picks one arbitrarily and the other declaration is invisible.\n */\n duplicateNames: string[];\n /**\n * True when the file EXISTS but cannot be read as an AsyncAPI document —\n * broken YAML, non-UTF-8 bytes, or a document that is not a mapping. A missing\n * file is not unreadable (absence is `service.no-asyncapi`'s question), and an\n * empty one parses to null and honestly declares nothing.\n */\n unreadable: boolean;\n /** The parser's own message, when there is one to quote back. */\n error?: string;\n /** Internal `#/` refs that resolve to nothing in this document (depth.ts). */\n danglingRefs: string[];\n /**\n * Where `x-loam-remove: true` appears — at ANY depth (digest.ts's\n * `removalMarkerPaths`): the three slot depths the format spec gives the\n * key meaning at, inline channel messages (channel-slot interior the merge\n * strips at that nested depth), and every place the key means nothing —\n * the document root, `info`, a `components` sibling. Feature-only\n * bookkeeping wherever it sits: `validate` grades any of these in a LIVING\n * contract as `asyncapi.remove-marker-living`, and the sweep is as deep as\n * the strip so a leaked marker can never be invisible to it.\n */\n markers: string[];\n}\n"]}