@shortlink-org/portolan 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (826) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +387 -0
  3. package/catalog/enum_test.go +46 -0
  4. package/catalog/model.go +932 -0
  5. package/catalog/roundtrip_test.go +185 -0
  6. package/catalog/via_test.go +38 -0
  7. package/cli/portolan.mjs +368 -0
  8. package/go.mod +14 -0
  9. package/go.sum +14 -0
  10. package/index.html +13 -0
  11. package/internal/commands/cargo.go +154 -0
  12. package/internal/commands/commands.go +143 -0
  13. package/internal/commands/commands_test.go +323 -0
  14. package/internal/commands/gradle.go +77 -0
  15. package/internal/commands/justfile.go +82 -0
  16. package/internal/commands/makefile.go +106 -0
  17. package/internal/commands/maven.go +93 -0
  18. package/internal/commands/packagejson.go +159 -0
  19. package/internal/commands/pyproject.go +169 -0
  20. package/internal/commands/taskfile.go +101 -0
  21. package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
  22. package/internal/commands/testdata/estate/Makefile +33 -0
  23. package/internal/commands/testdata/estate/Taskfile.yml +21 -0
  24. package/internal/commands/testdata/estate/build.gradle.kts +21 -0
  25. package/internal/commands/testdata/estate/justfile +20 -0
  26. package/internal/commands/testdata/estate/package.json +13 -0
  27. package/internal/commands/testdata/estate/pom.xml +29 -0
  28. package/internal/commands/testdata/estate/pyproject.toml +27 -0
  29. package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
  30. package/internal/commands/testdata/golden/commands.json +305 -0
  31. package/internal/gohttp/analyze.go +2500 -0
  32. package/internal/gohttp/endpoints.go +1067 -0
  33. package/internal/gohttp/roots.go +320 -0
  34. package/internal/gohttp/typed.go +143 -0
  35. package/internal/goscan/constants.go +85 -0
  36. package/internal/goscan/goscan_test.go +227 -0
  37. package/internal/goscan/names.go +52 -0
  38. package/internal/goscan/parse_test.go +11 -0
  39. package/internal/goscan/source.go +36 -0
  40. package/internal/goscan/tree.go +155 -0
  41. package/internal/goscan/types.go +99 -0
  42. package/internal/wsdl/ids.go +127 -0
  43. package/internal/wsdl/ids_test.go +21 -0
  44. package/internal/wsdl/model.go +62 -0
  45. package/internal/wsdl/parse.go +920 -0
  46. package/internal/wsdl/parse_test.go +133 -0
  47. package/package.json +117 -0
  48. package/plugin/describe.go +107 -0
  49. package/plugin/describe_test.go +114 -0
  50. package/plugin/protocol.go +117 -0
  51. package/plugin/schematest/schematest.go +126 -0
  52. package/plugins/README.md +1160 -0
  53. package/plugins/extract-adr/describe.go +19 -0
  54. package/plugins/extract-adr/describe_test.go +11 -0
  55. package/plugins/extract-adr/extract.go +153 -0
  56. package/plugins/extract-adr/extract_test.go +350 -0
  57. package/plugins/extract-adr/history.go +99 -0
  58. package/plugins/extract-adr/main.go +65 -0
  59. package/plugins/extract-adr/options.schema.json +26 -0
  60. package/plugins/extract-adr/parse.go +642 -0
  61. package/plugins/extract-adr/parse_test.go +405 -0
  62. package/plugins/extract-asyncapi/describe.go +19 -0
  63. package/plugins/extract-asyncapi/describe_test.go +11 -0
  64. package/plugins/extract-asyncapi/extract.go +315 -0
  65. package/plugins/extract-asyncapi/extract_test.go +197 -0
  66. package/plugins/extract-asyncapi/main.go +48 -0
  67. package/plugins/extract-asyncapi/options.schema.json +26 -0
  68. package/plugins/extract-asyncapi/spec.go +151 -0
  69. package/plugins/extract-celery/README.md +169 -0
  70. package/plugins/extract-celery/extract.py +346 -0
  71. package/plugins/extract-celery/extract_test.py +192 -0
  72. package/plugins/extract-celery/main.py +66 -0
  73. package/plugins/extract-celery/options.py +31 -0
  74. package/plugins/extract-celery/options.schema.json +31 -0
  75. package/plugins/extract-celery/producers.py +156 -0
  76. package/plugins/extract-celery/routes_test.py +106 -0
  77. package/plugins/extract-commands/describe.go +19 -0
  78. package/plugins/extract-commands/describe_test.go +11 -0
  79. package/plugins/extract-commands/extract.go +79 -0
  80. package/plugins/extract-commands/extract_test.go +92 -0
  81. package/plugins/extract-commands/main.go +55 -0
  82. package/plugins/extract-commands/options.schema.json +20 -0
  83. package/plugins/extract-csr/avro.go +258 -0
  84. package/plugins/extract-csr/describe.go +19 -0
  85. package/plugins/extract-csr/describe_test.go +11 -0
  86. package/plugins/extract-csr/extract.go +338 -0
  87. package/plugins/extract-csr/extract_test.go +374 -0
  88. package/plugins/extract-csr/jsonschema.go +343 -0
  89. package/plugins/extract-csr/lock.go +25 -0
  90. package/plugins/extract-csr/main.go +84 -0
  91. package/plugins/extract-csr/options.schema.json +43 -0
  92. package/plugins/extract-csr/subject.go +102 -0
  93. package/plugins/extract-django/README.md +386 -0
  94. package/plugins/extract-django/apps.py +100 -0
  95. package/plugins/extract-django/auth.py +302 -0
  96. package/plugins/extract-django/auth_test.py +188 -0
  97. package/plugins/extract-django/clients.py +121 -0
  98. package/plugins/extract-django/contracts.py +458 -0
  99. package/plugins/extract-django/database.py +119 -0
  100. package/plugins/extract-django/domain.py +318 -0
  101. package/plugins/extract-django/events.py +86 -0
  102. package/plugins/extract-django/extract.py +601 -0
  103. package/plugins/extract-django/extract_test.py +397 -0
  104. package/plugins/extract-django/flows.py +1278 -0
  105. package/plugins/extract-django/flows_test.py +338 -0
  106. package/plugins/extract-django/ids.py +42 -0
  107. package/plugins/extract-django/ids_test.py +30 -0
  108. package/plugins/extract-django/lifecycle.py +222 -0
  109. package/plugins/extract-django/lifecycle_test.py +76 -0
  110. package/plugins/extract-django/main.py +66 -0
  111. package/plugins/extract-django/openapi.py +229 -0
  112. package/plugins/extract-django/openapi_test.py +60 -0
  113. package/plugins/extract-django/operations.py +96 -0
  114. package/plugins/extract-django/options.py +61 -0
  115. package/plugins/extract-django/options.schema.json +126 -0
  116. package/plugins/extract-django/routing.py +214 -0
  117. package/plugins/extract-django/routing_test.py +198 -0
  118. package/plugins/extract-django/serializers.py +558 -0
  119. package/plugins/extract-django/store.py +434 -0
  120. package/plugins/extract-django/transport.py +242 -0
  121. package/plugins/extract-flows/describe.go +19 -0
  122. package/plugins/extract-flows/describe_test.go +11 -0
  123. package/plugins/extract-flows/extract.go +91 -0
  124. package/plugins/extract-flows/main.go +45 -0
  125. package/plugins/extract-flows/options.schema.json +16 -0
  126. package/plugins/extract-flows/parse.go +593 -0
  127. package/plugins/extract-flows/parse_test.go +204 -0
  128. package/plugins/extract-glossary/describe.go +19 -0
  129. package/plugins/extract-glossary/describe_test.go +11 -0
  130. package/plugins/extract-glossary/extract.go +115 -0
  131. package/plugins/extract-glossary/extract_test.go +220 -0
  132. package/plugins/extract-glossary/main.go +59 -0
  133. package/plugins/extract-glossary/options.schema.json +21 -0
  134. package/plugins/extract-glossary/parse.go +214 -0
  135. package/plugins/extract-glossary/parse_test.go +203 -0
  136. package/plugins/extract-go/aggregate.go +214 -0
  137. package/plugins/extract-go/client.go +409 -0
  138. package/plugins/extract-go/client_test.go +266 -0
  139. package/plugins/extract-go/describe.go +19 -0
  140. package/plugins/extract-go/describe_test.go +11 -0
  141. package/plugins/extract-go/enum.go +195 -0
  142. package/plugins/extract-go/enum_test.go +82 -0
  143. package/plugins/extract-go/event.go +99 -0
  144. package/plugins/extract-go/extract.go +191 -0
  145. package/plugins/extract-go/extract_test.go +261 -0
  146. package/plugins/extract-go/flow.go +1441 -0
  147. package/plugins/extract-go/flow_test.go +609 -0
  148. package/plugins/extract-go/httpclient.go +174 -0
  149. package/plugins/extract-go/httpclient_test.go +296 -0
  150. package/plugins/extract-go/ids.go +92 -0
  151. package/plugins/extract-go/layout.go +225 -0
  152. package/plugins/extract-go/layout_test.go +125 -0
  153. package/plugins/extract-go/lifecycle.go +324 -0
  154. package/plugins/extract-go/lifecycle_test.go +108 -0
  155. package/plugins/extract-go/main.go +79 -0
  156. package/plugins/extract-go/operation.go +157 -0
  157. package/plugins/extract-go/options.schema.json +66 -0
  158. package/plugins/extract-go/source.go +316 -0
  159. package/plugins/extract-go/transport.go +321 -0
  160. package/plugins/extract-go/transport_test.go +145 -0
  161. package/plugins/extract-go/wiring.go +434 -0
  162. package/plugins/extract-go-nats/describe.go +19 -0
  163. package/plugins/extract-go-nats/describe_test.go +11 -0
  164. package/plugins/extract-go-nats/extract.go +177 -0
  165. package/plugins/extract-go-nats/extract_test.go +389 -0
  166. package/plugins/extract-go-nats/index.go +379 -0
  167. package/plugins/extract-go-nats/main.go +42 -0
  168. package/plugins/extract-go-nats/options.schema.json +21 -0
  169. package/plugins/extract-go-nats/resolve.go +161 -0
  170. package/plugins/extract-go-nats/sites.go +224 -0
  171. package/plugins/extract-graphql/describe.go +19 -0
  172. package/plugins/extract-graphql/describe_test.go +11 -0
  173. package/plugins/extract-graphql/extract.go +433 -0
  174. package/plugins/extract-graphql/extract_test.go +256 -0
  175. package/plugins/extract-graphql/ids.go +49 -0
  176. package/plugins/extract-graphql/lex.go +237 -0
  177. package/plugins/extract-graphql/main.go +51 -0
  178. package/plugins/extract-graphql/options.schema.json +31 -0
  179. package/plugins/extract-graphql/parse.go +621 -0
  180. package/plugins/extract-graphql/parse_test.go +122 -0
  181. package/plugins/extract-http-clients/describe.go +19 -0
  182. package/plugins/extract-http-clients/describe_test.go +11 -0
  183. package/plugins/extract-http-clients/extract.go +705 -0
  184. package/plugins/extract-http-clients/extract_test.go +1263 -0
  185. package/plugins/extract-http-clients/main.go +42 -0
  186. package/plugins/extract-http-clients/options.schema.json +33 -0
  187. package/plugins/extract-java/README.md +190 -0
  188. package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
  189. package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
  190. package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
  191. package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
  192. package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
  193. package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
  194. package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
  195. package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
  196. package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
  197. package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
  198. package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
  199. package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
  200. package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
  201. package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
  202. package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
  203. package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
  204. package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
  205. package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
  206. package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
  207. package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
  208. package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
  209. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
  210. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
  211. package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
  212. package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
  213. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
  214. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
  215. package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
  216. package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
  217. package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
  218. package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
  219. package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
  220. package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
  221. package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
  222. package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
  223. package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
  224. package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
  225. package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
  226. package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
  227. package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
  228. package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
  229. package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
  230. package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
  231. package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
  232. package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
  233. package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
  234. package/plugins/extract-java/options.schema.json +70 -0
  235. package/plugins/extract-openapi/describe.go +19 -0
  236. package/plugins/extract-openapi/describe_test.go +11 -0
  237. package/plugins/extract-openapi/discover.go +243 -0
  238. package/plugins/extract-openapi/extract.go +526 -0
  239. package/plugins/extract-openapi/extract_test.go +545 -0
  240. package/plugins/extract-openapi/main.go +75 -0
  241. package/plugins/extract-openapi/options.schema.json +62 -0
  242. package/plugins/extract-openapi/spec.go +350 -0
  243. package/plugins/extract-project/README.md +22 -0
  244. package/plugins/extract-project/describe.go +19 -0
  245. package/plugins/extract-project/describe_test.go +11 -0
  246. package/plugins/extract-project/extract.go +221 -0
  247. package/plugins/extract-project/extract_test.go +109 -0
  248. package/plugins/extract-project/main.go +41 -0
  249. package/plugins/extract-project/options.schema.json +48 -0
  250. package/plugins/extract-proto/ast.go +125 -0
  251. package/plugins/extract-proto/consumes.go +77 -0
  252. package/plugins/extract-proto/describe.go +19 -0
  253. package/plugins/extract-proto/extract.go +293 -0
  254. package/plugins/extract-proto/extract_test.go +459 -0
  255. package/plugins/extract-proto/ids.go +89 -0
  256. package/plugins/extract-proto/ids_test.go +57 -0
  257. package/plugins/extract-proto/lex.go +285 -0
  258. package/plugins/extract-proto/main.go +100 -0
  259. package/plugins/extract-proto/module.go +120 -0
  260. package/plugins/extract-proto/options.schema.json +51 -0
  261. package/plugins/extract-proto/parse.go +720 -0
  262. package/plugins/extract-proto/parse_test.go +307 -0
  263. package/plugins/extract-proto/provides.go +236 -0
  264. package/plugins/extract-proto/resolve.go +222 -0
  265. package/plugins/extract-proto/resolve_test.go +119 -0
  266. package/plugins/extract-python-kafka/README.md +36 -0
  267. package/plugins/extract-python-kafka/extract.py +211 -0
  268. package/plugins/extract-python-kafka/extract_test.py +68 -0
  269. package/plugins/extract-python-kafka/main.py +59 -0
  270. package/plugins/extract-python-kafka/options.py +29 -0
  271. package/plugins/extract-python-kafka/options.schema.json +31 -0
  272. package/plugins/extract-redis/README.md +11 -0
  273. package/plugins/extract-redis/describe.go +19 -0
  274. package/plugins/extract-redis/describe_test.go +11 -0
  275. package/plugins/extract-redis/extract.go +183 -0
  276. package/plugins/extract-redis/extract_test.go +168 -0
  277. package/plugins/extract-redis/keyspaces.go +469 -0
  278. package/plugins/extract-redis/main.go +44 -0
  279. package/plugins/extract-redis/options.schema.json +30 -0
  280. package/plugins/extract-river/describe.go +19 -0
  281. package/plugins/extract-river/describe_test.go +11 -0
  282. package/plugins/extract-river/extract.go +507 -0
  283. package/plugins/extract-river/extract_test.go +132 -0
  284. package/plugins/extract-river/main.go +42 -0
  285. package/plugins/extract-river/options.schema.json +21 -0
  286. package/plugins/extract-rust/Cargo.lock +133 -0
  287. package/plugins/extract-rust/Cargo.toml +23 -0
  288. package/plugins/extract-rust/README.md +204 -0
  289. package/plugins/extract-rust/options.schema.json +65 -0
  290. package/plugins/extract-rust/rustfmt.toml +1 -0
  291. package/plugins/extract-sql/ddl.go +893 -0
  292. package/plugins/extract-sql/ddl_test.go +401 -0
  293. package/plugins/extract-sql/describe.go +19 -0
  294. package/plugins/extract-sql/describe_test.go +11 -0
  295. package/plugins/extract-sql/layout.go +221 -0
  296. package/plugins/extract-sql/layout_test.go +55 -0
  297. package/plugins/extract-sql/lineage.go +107 -0
  298. package/plugins/extract-sql/main.go +141 -0
  299. package/plugins/extract-sql/maps.go +564 -0
  300. package/plugins/extract-sql/maps_java.go +117 -0
  301. package/plugins/extract-sql/maps_rust.go +333 -0
  302. package/plugins/extract-sql/maps_rust_test.go +70 -0
  303. package/plugins/extract-sql/maps_test.go +204 -0
  304. package/plugins/extract-sql/maps_ts.go +398 -0
  305. package/plugins/extract-sql/maps_ts_test.go +136 -0
  306. package/plugins/extract-sql/options.schema.json +53 -0
  307. package/plugins/extract-sql/projection.go +70 -0
  308. package/plugins/extract-sql/projection_test.go +50 -0
  309. package/plugins/extract-sql/store.go +420 -0
  310. package/plugins/extract-sql/store_test.go +233 -0
  311. package/plugins/extract-sql/view.go +295 -0
  312. package/plugins/extract-ts/README.md +306 -0
  313. package/plugins/extract-ts/ast.test.ts +178 -0
  314. package/plugins/extract-ts/ast.ts +648 -0
  315. package/plugins/extract-ts/clients.ts +185 -0
  316. package/plugins/extract-ts/domain.ts +164 -0
  317. package/plugins/extract-ts/extract.test.ts +208 -0
  318. package/plugins/extract-ts/extract.ts +200 -0
  319. package/plugins/extract-ts/flows.ts +783 -0
  320. package/plugins/extract-ts/graphql.test.ts +73 -0
  321. package/plugins/extract-ts/graphql.ts +181 -0
  322. package/plugins/extract-ts/ids.test.ts +21 -0
  323. package/plugins/extract-ts/ids.ts +52 -0
  324. package/plugins/extract-ts/lifecycle.ts +138 -0
  325. package/plugins/extract-ts/main.ts +48 -0
  326. package/plugins/extract-ts/openapi.test.ts +64 -0
  327. package/plugins/extract-ts/openapi.ts +99 -0
  328. package/plugins/extract-ts/operations.ts +93 -0
  329. package/plugins/extract-ts/options.schema.json +96 -0
  330. package/plugins/extract-ts/source.test.ts +174 -0
  331. package/plugins/extract-ts/source.ts +465 -0
  332. package/plugins/extract-ts/transport.ts +147 -0
  333. package/plugins/extract-ts/tsconfig.json +13 -0
  334. package/plugins/extract-ts/wiring.ts +190 -0
  335. package/plugins/extract-watermill/describe.go +19 -0
  336. package/plugins/extract-watermill/describe_test.go +11 -0
  337. package/plugins/extract-watermill/extract.go +1228 -0
  338. package/plugins/extract-watermill/extract_test.go +234 -0
  339. package/plugins/extract-watermill/main.go +41 -0
  340. package/plugins/extract-watermill/options.schema.json +21 -0
  341. package/plugins/extract-wsdl/describe.go +19 -0
  342. package/plugins/extract-wsdl/describe_test.go +11 -0
  343. package/plugins/extract-wsdl/extract.go +148 -0
  344. package/plugins/extract-wsdl/extract_test.go +45 -0
  345. package/plugins/extract-wsdl/main.go +53 -0
  346. package/plugins/extract-wsdl/options.schema.json +59 -0
  347. package/plugins/fetch-bsr/auth.go +114 -0
  348. package/plugins/fetch-bsr/bsr.go +240 -0
  349. package/plugins/fetch-bsr/cache.go +66 -0
  350. package/plugins/fetch-bsr/describe.go +19 -0
  351. package/plugins/fetch-bsr/fetch.go +192 -0
  352. package/plugins/fetch-bsr/fetch_test.go +465 -0
  353. package/plugins/fetch-bsr/lock.go +71 -0
  354. package/plugins/fetch-bsr/main.go +117 -0
  355. package/plugins/fetch-bsr/options.schema.json +42 -0
  356. package/plugins/fetch-csr/auth.go +95 -0
  357. package/plugins/fetch-csr/cache.go +69 -0
  358. package/plugins/fetch-csr/describe.go +19 -0
  359. package/plugins/fetch-csr/describe_test.go +11 -0
  360. package/plugins/fetch-csr/fetch.go +254 -0
  361. package/plugins/fetch-csr/fetch_test.go +494 -0
  362. package/plugins/fetch-csr/lock.go +95 -0
  363. package/plugins/fetch-csr/main.go +122 -0
  364. package/plugins/fetch-csr/options.schema.json +42 -0
  365. package/plugins/fetch-csr/registry.go +209 -0
  366. package/plugins/fetch-git/cache.go +65 -0
  367. package/plugins/fetch-git/describe.go +19 -0
  368. package/plugins/fetch-git/describe_test.go +11 -0
  369. package/plugins/fetch-git/fetch.go +181 -0
  370. package/plugins/fetch-git/fetch_test.go +332 -0
  371. package/plugins/fetch-git/git.go +169 -0
  372. package/plugins/fetch-git/lock.go +72 -0
  373. package/plugins/fetch-git/main.go +127 -0
  374. package/plugins/fetch-git/offline.go +39 -0
  375. package/plugins/fetch-git/options.schema.json +42 -0
  376. package/plugins/fetch-git/pin.go +90 -0
  377. package/plugins/fetch-git/pin_test.go +74 -0
  378. package/plugins/gen-backstage/describe.go +17 -0
  379. package/plugins/gen-backstage/main.go +22 -0
  380. package/plugins/gen-backstage/options.schema.json +30 -0
  381. package/plugins/gen-backstage/plugin.go +473 -0
  382. package/plugins/gen-backstage/plugin_test.go +145 -0
  383. package/plugins/gen-backstage.wasm +0 -0
  384. package/plugins/gen-markdown/adr.go +162 -0
  385. package/plugins/gen-markdown/aggregate.go +362 -0
  386. package/plugins/gen-markdown/canonical.go +93 -0
  387. package/plugins/gen-markdown/context.go +90 -0
  388. package/plugins/gen-markdown/coverage_test.go +89 -0
  389. package/plugins/gen-markdown/describe.go +19 -0
  390. package/plugins/gen-markdown/describe_test.go +11 -0
  391. package/plugins/gen-markdown/external.go +71 -0
  392. package/plugins/gen-markdown/flow.go +277 -0
  393. package/plugins/gen-markdown/glossary.go +76 -0
  394. package/plugins/gen-markdown/glossary_test.go +148 -0
  395. package/plugins/gen-markdown/llms.go +302 -0
  396. package/plugins/gen-markdown/main.go +22 -0
  397. package/plugins/gen-markdown/markdown.go +254 -0
  398. package/plugins/gen-markdown/markdown_test.go +100 -0
  399. package/plugins/gen-markdown/module.go +107 -0
  400. package/plugins/gen-markdown/options.schema.json +16 -0
  401. package/plugins/gen-markdown/plugin.go +39 -0
  402. package/plugins/gen-markdown/quality_test.go +176 -0
  403. package/plugins/gen-markdown/redis_test.go +38 -0
  404. package/plugins/gen-markdown/render.go +344 -0
  405. package/plugins/gen-markdown/render_test.go +186 -0
  406. package/plugins/gen-markdown/service.go +456 -0
  407. package/plugins/gen-markdown/source.go +177 -0
  408. package/plugins/gen-markdown/store.go +201 -0
  409. package/plugins/gen-markdown.wasm +0 -0
  410. package/plugins/gen-mermaid/describe.go +17 -0
  411. package/plugins/gen-mermaid/main.go +22 -0
  412. package/plugins/gen-mermaid/options.schema.json +10 -0
  413. package/plugins/gen-mermaid/plugin.go +104 -0
  414. package/plugins/gen-mermaid/plugin_test.go +33 -0
  415. package/plugins/gen-mermaid.wasm +0 -0
  416. package/plugins/openapi/ids.go +261 -0
  417. package/plugins/openapi/ids_test.go +98 -0
  418. package/plugins/pyplugin/catalog.py +209 -0
  419. package/plugins/pyplugin/celery_conf.py +436 -0
  420. package/plugins/pyplugin/celery_tasks.py +76 -0
  421. package/plugins/pyplugin/kafka.py +570 -0
  422. package/plugins/pyplugin/names.py +38 -0
  423. package/plugins/pyplugin/protocol.py +79 -0
  424. package/plugins/pyplugin/source.py +285 -0
  425. package/plugins/verify-codeowners/describe.go +19 -0
  426. package/plugins/verify-codeowners/describe_test.go +11 -0
  427. package/plugins/verify-codeowners/main.go +75 -0
  428. package/plugins/verify-codeowners/match.go +85 -0
  429. package/plugins/verify-codeowners/match_test.go +47 -0
  430. package/plugins/verify-codeowners/options.schema.json +15 -0
  431. package/plugins/verify-codeowners/owners.go +164 -0
  432. package/plugins/verify-codeowners/owners_test.go +225 -0
  433. package/plugins/verify-codeowners/parse.go +90 -0
  434. package/plugins/verify-codeowners/parse_test.go +62 -0
  435. package/plugins/verify-otel/describe.go +19 -0
  436. package/plugins/verify-otel/describe_test.go +11 -0
  437. package/plugins/verify-otel/main.go +53 -0
  438. package/plugins/verify-otel/match.go +336 -0
  439. package/plugins/verify-otel/options.schema.json +30 -0
  440. package/plugins/verify-otel/otlp.go +200 -0
  441. package/plugins/verify-otel/verify.go +734 -0
  442. package/plugins/verify-otel/verify_test.go +460 -0
  443. package/portolan.json +1073 -0
  444. package/public/favicon.svg +12 -0
  445. package/schema/portolan.schema.json +2558 -0
  446. package/scripts/README.md +53 -0
  447. package/scripts/build-report.mjs +60 -0
  448. package/scripts/build-report.test.mjs +44 -0
  449. package/scripts/builtin-plugins.mjs +49 -0
  450. package/scripts/catalog-sources.mjs +67 -0
  451. package/scripts/diff.mjs +315 -0
  452. package/scripts/diff.test.mjs +151 -0
  453. package/scripts/forge-comment.mjs +144 -0
  454. package/scripts/forge-comment.test.mjs +151 -0
  455. package/scripts/forge-release.mjs +138 -0
  456. package/scripts/forge-release.test.mjs +106 -0
  457. package/scripts/forge.mjs +61 -0
  458. package/scripts/gen-likec4.mjs +854 -0
  459. package/scripts/gen-likec4.test.mjs +258 -0
  460. package/scripts/gen.mjs +603 -0
  461. package/scripts/generated-docs.test.mjs +66 -0
  462. package/scripts/local-api.mjs +1192 -0
  463. package/scripts/local-api.test.mjs +387 -0
  464. package/scripts/manifest.mjs +214 -0
  465. package/scripts/manifest.test.mjs +148 -0
  466. package/scripts/output-path.mjs +79 -0
  467. package/scripts/output-path.test.mjs +45 -0
  468. package/scripts/package-smoke.mjs +37 -0
  469. package/scripts/plugin-host.mjs +419 -0
  470. package/scripts/plugin-host.test.mjs +109 -0
  471. package/scripts/plugin-wasm-worker.mjs +34 -0
  472. package/scripts/run-builtin.mjs +81 -0
  473. package/scripts/schema.mjs +357 -0
  474. package/scripts/site-docs.mjs +295 -0
  475. package/scripts/site-docs.test.mjs +181 -0
  476. package/scripts/vendor-lock.mjs +58 -0
  477. package/scripts/vendor-lock.test.mjs +69 -0
  478. package/src/README.md +38 -0
  479. package/src/app/App.tsx +448 -0
  480. package/src/app/BranchPicker.test.ts +46 -0
  481. package/src/app/BranchPicker.tsx +153 -0
  482. package/src/app/Breadcrumbs.test.ts +38 -0
  483. package/src/app/Breadcrumbs.tsx +223 -0
  484. package/src/app/BuildStamp.tsx +146 -0
  485. package/src/app/CommandPalette.tsx +292 -0
  486. package/src/app/HashScroll.tsx +45 -0
  487. package/src/app/Sidebar.tsx +1742 -0
  488. package/src/app/TopBar.tsx +393 -0
  489. package/src/app/density.tsx +64 -0
  490. package/src/app/forge-access.test.ts +27 -0
  491. package/src/app/forge-access.tsx +80 -0
  492. package/src/app/panels.tsx +141 -0
  493. package/src/app/pins.tsx +242 -0
  494. package/src/app/query-client.ts +24 -0
  495. package/src/app/responsive.ts +55 -0
  496. package/src/app/shortcuts.test.ts +39 -0
  497. package/src/app/shortcuts.tsx +282 -0
  498. package/src/app/theme.tsx +51 -0
  499. package/src/app/title.test.ts +14 -0
  500. package/src/app/title.ts +30 -0
  501. package/src/app/toast.tsx +59 -0
  502. package/src/app/tree-keys.test.ts +80 -0
  503. package/src/app/tree-keys.ts +87 -0
  504. package/src/app/ui-store.ts +52 -0
  505. package/src/catalog-error.test.ts +105 -0
  506. package/src/catalog-profile.test.ts +59 -0
  507. package/src/catalog-profile.ts +101 -0
  508. package/src/catalog-stores.test.ts +431 -0
  509. package/src/catalog.test.ts +1215 -0
  510. package/src/catalog.ts +3125 -0
  511. package/src/chat/Cards.tsx +408 -0
  512. package/src/chat/ChatPanel.tsx +79 -0
  513. package/src/chat/ChatSettings.tsx +219 -0
  514. package/src/chat/Composer.tsx +99 -0
  515. package/src/chat/Conversation.tsx +281 -0
  516. package/src/chat/Header.tsx +46 -0
  517. package/src/chat/Message.tsx +49 -0
  518. package/src/chat/Notice.tsx +195 -0
  519. package/src/chat/Starter.tsx +67 -0
  520. package/src/chat/Steps.tsx +45 -0
  521. package/src/chat/Waiting.tsx +87 -0
  522. package/src/chat/flags.test.ts +59 -0
  523. package/src/chat/flags.ts +155 -0
  524. package/src/chat/linkify.test.ts +30 -0
  525. package/src/chat/linkify.ts +45 -0
  526. package/src/chat/prefs.ts +56 -0
  527. package/src/chat/prompt.test.ts +43 -0
  528. package/src/chat/prompt.ts +157 -0
  529. package/src/chat/store.ts +35 -0
  530. package/src/chat/tools.ts +58 -0
  531. package/src/chat/transport.ts +70 -0
  532. package/src/components/AdrRow.tsx +29 -0
  533. package/src/components/AnchorLink.tsx +67 -0
  534. package/src/components/ApiReference.tsx +134 -0
  535. package/src/components/AsyncApiReference.tsx +120 -0
  536. package/src/components/CatalogStamp.tsx +155 -0
  537. package/src/components/ChannelRows.tsx +132 -0
  538. package/src/components/CommandRows.tsx +139 -0
  539. package/src/components/DiagramSkeleton.tsx +71 -0
  540. package/src/components/EditorLink.tsx +85 -0
  541. package/src/components/FieldTree.tsx +305 -0
  542. package/src/components/FlowTrigger.tsx +34 -0
  543. package/src/components/Ident.tsx +89 -0
  544. package/src/components/Integrations.tsx +243 -0
  545. package/src/components/LifecycleDiagram.tsx +110 -0
  546. package/src/components/MachineDocs.tsx +42 -0
  547. package/src/components/Markdown.tsx +173 -0
  548. package/src/components/MarkdownTable.tsx +206 -0
  549. package/src/components/Mermaid.tsx +211 -0
  550. package/src/components/MethodRows.tsx +309 -0
  551. package/src/components/Overlay.tsx +125 -0
  552. package/src/components/PageHeader.tsx +189 -0
  553. package/src/components/RowActions.tsx +97 -0
  554. package/src/components/SchemaDocument.tsx +89 -0
  555. package/src/components/Select.tsx +126 -0
  556. package/src/components/ShapeRows.tsx +118 -0
  557. package/src/components/SourceDoc.tsx +82 -0
  558. package/src/components/SourcePreview.tsx +441 -0
  559. package/src/components/TabRow.tsx +182 -0
  560. package/src/components/TechIcon.tsx +34 -0
  561. package/src/components/Toc.tsx +132 -0
  562. package/src/components/WhatLinksHere.tsx +273 -0
  563. package/src/components/WsdlReference.tsx +84 -0
  564. package/src/components/ddd-icons.tsx +107 -0
  565. package/src/components/kind.tsx +157 -0
  566. package/src/components/logo.tsx +61 -0
  567. package/src/components/primitives.tsx +264 -0
  568. package/src/data.test.ts +18 -0
  569. package/src/data.ts +202 -0
  570. package/src/enrich.test.ts +1150 -0
  571. package/src/enrich.ts +672 -0
  572. package/src/er/ErCanvas.tsx +533 -0
  573. package/src/er/RedisSchema.tsx +74 -0
  574. package/src/er/RelationCard.tsx +267 -0
  575. package/src/er/StoreHeader.tsx +155 -0
  576. package/src/er/TableNode.tsx +84 -0
  577. package/src/er/ViewNode.tsx +83 -0
  578. package/src/er/layout.test.ts +115 -0
  579. package/src/er/layout.ts +77 -0
  580. package/src/er/lineage.test.ts +140 -0
  581. package/src/er/lineage.ts +107 -0
  582. package/src/er/markers.tsx +79 -0
  583. package/src/er/spec.test.ts +344 -0
  584. package/src/er/spec.ts +426 -0
  585. package/src/flow/ChainList.tsx +162 -0
  586. package/src/flow/FlowTable.tsx +123 -0
  587. package/src/flow/FlowToolbar.tsx +372 -0
  588. package/src/flow/StepDetail.tsx +380 -0
  589. package/src/flow/StepRail.tsx +428 -0
  590. package/src/flow/answers.test.ts +50 -0
  591. package/src/flow/answers.ts +58 -0
  592. package/src/flow/chain.test.ts +383 -0
  593. package/src/flow/chain.ts +353 -0
  594. package/src/flow/chapters.test.ts +185 -0
  595. package/src/flow/chapters.ts +225 -0
  596. package/src/flow/continues.test.ts +102 -0
  597. package/src/flow/continues.ts +65 -0
  598. package/src/flow/cross-context.test.ts +97 -0
  599. package/src/flow/cross-context.ts +35 -0
  600. package/src/flow/mermaid.test.ts +22 -0
  601. package/src/flow/mermaid.ts +141 -0
  602. package/src/flow/outline.test.ts +212 -0
  603. package/src/flow/outline.ts +170 -0
  604. package/src/flow/paths.test.ts +129 -0
  605. package/src/flow/paths.ts +147 -0
  606. package/src/flow/prefs.test.ts +80 -0
  607. package/src/flow/prefs.ts +104 -0
  608. package/src/graph/DependencyGraph.tsx +283 -0
  609. package/src/graph/DependencyNodes.tsx +162 -0
  610. package/src/graph/EdgeLabel.tsx +61 -0
  611. package/src/graph/FocusedEventGraph.tsx +211 -0
  612. package/src/graph/GraphToolbar.tsx +187 -0
  613. package/src/graph/RoutedEdge.tsx +80 -0
  614. package/src/graph/dependency-layout.test.ts +360 -0
  615. package/src/graph/dependency-layout.ts +444 -0
  616. package/src/graph/elk.test.ts +70 -0
  617. package/src/graph/elk.ts +287 -0
  618. package/src/graph/nodes.tsx +97 -0
  619. package/src/graph/theme.ts +81 -0
  620. package/src/graph/useElkFlow.ts +125 -0
  621. package/src/index.css +1547 -0
  622. package/src/language/InLanguage.tsx +40 -0
  623. package/src/language/TermCard.tsx +132 -0
  624. package/src/language/cards.test.ts +114 -0
  625. package/src/language/cards.ts +111 -0
  626. package/src/lib/admonition.test.ts +49 -0
  627. package/src/lib/admonition.ts +48 -0
  628. package/src/lib/adr.test.ts +131 -0
  629. package/src/lib/adr.ts +68 -0
  630. package/src/lib/api.test.ts +122 -0
  631. package/src/lib/api.ts +135 -0
  632. package/src/lib/backlinks.test.ts +330 -0
  633. package/src/lib/backlinks.ts +567 -0
  634. package/src/lib/branch-compare.ts +16 -0
  635. package/src/lib/build-info.test.ts +76 -0
  636. package/src/lib/build-info.ts +80 -0
  637. package/src/lib/catalog-diff.test.ts +275 -0
  638. package/src/lib/catalog-diff.ts +655 -0
  639. package/src/lib/clipboard.ts +43 -0
  640. package/src/lib/comparison-memory.test.ts +40 -0
  641. package/src/lib/comparison-memory.ts +39 -0
  642. package/src/lib/context-color.ts +27 -0
  643. package/src/lib/context-map.test.ts +148 -0
  644. package/src/lib/context-map.ts +399 -0
  645. package/src/lib/data-model.test.ts +245 -0
  646. package/src/lib/data-model.ts +399 -0
  647. package/src/lib/data-problems.test.ts +178 -0
  648. package/src/lib/data-problems.ts +314 -0
  649. package/src/lib/derive.test.ts +251 -0
  650. package/src/lib/derive.ts +515 -0
  651. package/src/lib/editor-link.test.ts +125 -0
  652. package/src/lib/editor-link.ts +96 -0
  653. package/src/lib/editor-prefs.ts +27 -0
  654. package/src/lib/event-graph.test.ts +237 -0
  655. package/src/lib/event-graph.ts +327 -0
  656. package/src/lib/export-canvas.ts +37 -0
  657. package/src/lib/export-file.test.ts +30 -0
  658. package/src/lib/export-file.ts +20 -0
  659. package/src/lib/flow-tree.test.ts +177 -0
  660. package/src/lib/flow-tree.ts +160 -0
  661. package/src/lib/forge-refs.test.ts +50 -0
  662. package/src/lib/forge-refs.ts +60 -0
  663. package/src/lib/format.test.ts +35 -0
  664. package/src/lib/format.ts +64 -0
  665. package/src/lib/github-catalog.test.ts +301 -0
  666. package/src/lib/github-catalog.ts +610 -0
  667. package/src/lib/integrations.test.ts +119 -0
  668. package/src/lib/integrations.ts +133 -0
  669. package/src/lib/kinds.test.ts +91 -0
  670. package/src/lib/kinds.ts +204 -0
  671. package/src/lib/lifecycle.test.ts +93 -0
  672. package/src/lib/lifecycle.ts +165 -0
  673. package/src/lib/local-api.ts +194 -0
  674. package/src/lib/motion-features.ts +11 -0
  675. package/src/lib/motion.test.ts +120 -0
  676. package/src/lib/motion.tsx +269 -0
  677. package/src/lib/palette-recent.test.ts +80 -0
  678. package/src/lib/palette-recent.ts +90 -0
  679. package/src/lib/palette.test.ts +365 -0
  680. package/src/lib/palette.ts +593 -0
  681. package/src/lib/pins.test.ts +139 -0
  682. package/src/lib/pins.ts +130 -0
  683. package/src/lib/problems.test.ts +112 -0
  684. package/src/lib/project-preview.ts +5 -0
  685. package/src/lib/proto-problems.test.ts +121 -0
  686. package/src/lib/proto-problems.ts +65 -0
  687. package/src/lib/queries.test.ts +113 -0
  688. package/src/lib/queries.ts +131 -0
  689. package/src/lib/registry.test.ts +219 -0
  690. package/src/lib/registry.ts +208 -0
  691. package/src/lib/scenarios.ts +547 -0
  692. package/src/lib/setup-info.test.ts +199 -0
  693. package/src/lib/setup-info.ts +415 -0
  694. package/src/lib/shape.test.ts +289 -0
  695. package/src/lib/shape.ts +454 -0
  696. package/src/lib/sidebar-prefs.test.ts +32 -0
  697. package/src/lib/sidebar-prefs.ts +75 -0
  698. package/src/lib/source-code.test.ts +159 -0
  699. package/src/lib/source-code.ts +286 -0
  700. package/src/lib/source-doc.test.ts +131 -0
  701. package/src/lib/source-doc.ts +103 -0
  702. package/src/lib/source-highlight.test.ts +29 -0
  703. package/src/lib/source-highlight.ts +24 -0
  704. package/src/lib/source-link.test.ts +153 -0
  705. package/src/lib/source-link.ts +201 -0
  706. package/src/lib/spec-files.test.ts +24 -0
  707. package/src/lib/spec-files.ts +23 -0
  708. package/src/lib/table-directive.test.ts +139 -0
  709. package/src/lib/table-directive.ts +106 -0
  710. package/src/lib/tech.test.ts +120 -0
  711. package/src/lib/tech.ts +421 -0
  712. package/src/lib/terms.test.ts +67 -0
  713. package/src/lib/terms.ts +179 -0
  714. package/src/lib/wire-problems.test.ts +263 -0
  715. package/src/lib/wire-problems.ts +281 -0
  716. package/src/likec4/C4View.tsx +55 -0
  717. package/src/likec4/CanvasBridge.tsx +187 -0
  718. package/src/likec4/FlowView.tsx +235 -0
  719. package/src/likec4/InteractiveView.tsx +144 -0
  720. package/src/likec4/canvas-viewport.test.ts +149 -0
  721. package/src/likec4/canvas-viewport.ts +135 -0
  722. package/src/likec4/flow-edges.test.ts +51 -0
  723. package/src/likec4/flow-edges.ts +89 -0
  724. package/src/likec4/frame-theme.test.ts +76 -0
  725. package/src/likec4/frame-theme.ts +101 -0
  726. package/src/likec4/highlight-css.test.ts +49 -0
  727. package/src/likec4/highlight-css.ts +52 -0
  728. package/src/likec4/ids.test.ts +151 -0
  729. package/src/likec4/ids.ts +115 -0
  730. package/src/likec4/levels.tsx +70 -0
  731. package/src/likec4/mapping.test.ts +66 -0
  732. package/src/likec4/mapping.ts +54 -0
  733. package/src/likec4/reserved.json +141 -0
  734. package/src/likec4/view-index.ts +78 -0
  735. package/src/main.tsx +13 -0
  736. package/src/map/ContextMapGraph.tsx +274 -0
  737. package/src/map/ContextNode.tsx +70 -0
  738. package/src/map/FloatingEdge.tsx +86 -0
  739. package/src/merge.test.ts +1038 -0
  740. package/src/merge.ts +1004 -0
  741. package/src/pages/AdrDetail.tsx +242 -0
  742. package/src/pages/AdrIndex.tsx +114 -0
  743. package/src/pages/AggregatePage.tsx +727 -0
  744. package/src/pages/BlockPage.tsx +478 -0
  745. package/src/pages/CatalogFailure.tsx +111 -0
  746. package/src/pages/Changes.tsx +313 -0
  747. package/src/pages/ContextMap.tsx +534 -0
  748. package/src/pages/ContextPage.tsx +346 -0
  749. package/src/pages/EnumPage.tsx +282 -0
  750. package/src/pages/EventPage.tsx +686 -0
  751. package/src/pages/ExternalPage.tsx +204 -0
  752. package/src/pages/FlowDetail.tsx +663 -0
  753. package/src/pages/FlowIndex.tsx +303 -0
  754. package/src/pages/GraphPage.tsx +217 -0
  755. package/src/pages/Language.tsx +257 -0
  756. package/src/pages/ModulePage.tsx +366 -0
  757. package/src/pages/NotFound.tsx +54 -0
  758. package/src/pages/Overview.tsx +344 -0
  759. package/src/pages/Problems.tsx +360 -0
  760. package/src/pages/RegistryIndex.tsx +118 -0
  761. package/src/pages/ServicePage.tsx +746 -0
  762. package/src/pages/Settings.tsx +975 -0
  763. package/src/pages/StorePage.tsx +114 -0
  764. package/src/peek/PeekCard.test.ts +35 -0
  765. package/src/peek/PeekCard.tsx +118 -0
  766. package/src/peek/PeekLayer.tsx +147 -0
  767. package/src/peek/model.test.ts +156 -0
  768. package/src/peek/model.ts +253 -0
  769. package/src/routes.test.ts +222 -0
  770. package/src/routes.ts +449 -0
  771. package/src/selection/DetailPanel.tsx +1407 -0
  772. package/src/selection/hash.test.ts +73 -0
  773. package/src/selection/hash.ts +58 -0
  774. package/src/selection/model.test.ts +199 -0
  775. package/src/selection/model.ts +473 -0
  776. package/src/selection/pages.test.ts +115 -0
  777. package/src/selection/pages.ts +268 -0
  778. package/src/selection/store.test.ts +62 -0
  779. package/src/selection/store.ts +46 -0
  780. package/src/selection/sync.tsx +99 -0
  781. package/src/table/ColumnsMenu.tsx +72 -0
  782. package/src/table/DataTable.tsx +780 -0
  783. package/src/table/Facets.tsx +54 -0
  784. package/src/table/GroupMenu.tsx +69 -0
  785. package/src/table/Toolbar.tsx +190 -0
  786. package/src/table/cells.tsx +117 -0
  787. package/src/table/compare.test.ts +117 -0
  788. package/src/table/compare.ts +196 -0
  789. package/src/table/export.test.ts +88 -0
  790. package/src/table/export.ts +66 -0
  791. package/src/table/facet-groups.test.ts +74 -0
  792. package/src/table/facet-groups.ts +56 -0
  793. package/src/table/grouping.test.ts +84 -0
  794. package/src/table/grouping.ts +82 -0
  795. package/src/table/infer.test.ts +57 -0
  796. package/src/table/infer.ts +50 -0
  797. package/src/table/persist.test.ts +76 -0
  798. package/src/table/persist.ts +112 -0
  799. package/src/table/sort-url.test.ts +75 -0
  800. package/src/table/sort-url.ts +52 -0
  801. package/src/table/types.ts +80 -0
  802. package/src/table/useTableState.ts +220 -0
  803. package/src/test-catalog.ts +7 -0
  804. package/src/testing/estate/data__catalog.json +2847 -0
  805. package/src/testing/estate/data__flows.json +1724 -0
  806. package/src/testing/estate/examples__auth__portolan__api.json +212 -0
  807. package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
  808. package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
  809. package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
  810. package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
  811. package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
  812. package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
  813. package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
  814. package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
  815. package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
  816. package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
  817. package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
  818. package/src/testing/estate.ts +49 -0
  819. package/src/trail/Trail.tsx +81 -0
  820. package/src/trail/model.test.ts +157 -0
  821. package/src/trail/model.ts +189 -0
  822. package/src/trail/record.tsx +32 -0
  823. package/src/trail/store.test.ts +66 -0
  824. package/src/trail/store.ts +102 -0
  825. package/tsconfig.json +35 -0
  826. package/vite.config.ts +171 -0
@@ -0,0 +1,26 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "properties": {
5
+ "files": {
6
+ "type": "array",
7
+ "items": { "type": "string" },
8
+ "description": "The record files to read, as globs relative to the input root. A README.md among them is an index rather than a record and is skipped. Left out, every docs/adr/*.md.",
9
+ "examples": [["docs/adr/*.md"], ["adr/*.md"]]
10
+ },
11
+ "scope": {
12
+ "type": "string",
13
+ "description": "What a record is about when it does not say so itself: \"org\", a context slug, or \"<context>.<service>\". A record written by adr-tools has no scope of its own and takes its id's prefix from here; a MADR record with a Scope bullet keeps its own.",
14
+ "examples": ["avia.aviasupp", "payments", "org"]
15
+ },
16
+ "history": {
17
+ "enum": ["git", "none"],
18
+ "description": "Where a record's author and dates come from. \"git\", the default, reads the file's commits: who first added it and who last touched it. \"none\" reads nothing, for a tree that is not a checkout or a fragment that must not change when the history does.",
19
+ "default": "git"
20
+ },
21
+ "out": {
22
+ "type": "string",
23
+ "description": "Name of the fragment file, inside the step's output directory. Left out, adr.json."
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,642 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "path"
6
+ "regexp"
7
+ "strconv"
8
+ "strings"
9
+ "time"
10
+
11
+ "github.com/shortlink-org/portolan/catalog"
12
+ )
13
+
14
+ // The format, in one place.
15
+ //
16
+ // # auth.0003 — Session expiry publishes no event
17
+ //
18
+ // - **Status:** accepted
19
+ // - **Date:** 2026-08-22
20
+ // - **Scope:** auth.auth
21
+ // - **Superseded by:** auth.0007 optional
22
+ // - **Supersedes:** auth.0001, auth.0002 optional
23
+ // - **Relates:** auth.auth.session.SessionEnded, shop.cart, checkout
24
+ // - **Note:** prose the catalog has no other field for
25
+ //
26
+ // ## Context and Problem Statement
27
+ // …
28
+ //
29
+ // The title carries the id and, after an em dash, the title itself. The id's
30
+ // prefix is whatever the record is about - a service (`auth`), a context
31
+ // (`payments`) or the organisation (`org`) - and the four digits after it are
32
+ // the record's number, which the file's own name repeats.
33
+ //
34
+ // The bullets are everything the markdown knows that the prose does not say
35
+ // in a form anything can read. Status, Date and Scope are required; the rest
36
+ // are written when there is something to write. `Relates` names events,
37
+ // services and flows in one list and they are told apart by their shape,
38
+ // because a record's author should not have to remember which of three lists
39
+ // a name belongs in.
40
+ //
41
+ // Everything from the first `##` onward is the record. It is frozen history:
42
+ // it goes into the catalog exactly as written and comes out onto the page the
43
+ // same way, and nothing in it is ever regenerated from the model as it stands
44
+ // now.
45
+ //
46
+ // The other format read here is the one adr-tools writes, because a tree of
47
+ // records kept that way for years should not have to be retyped to be read:
48
+ //
49
+ // # 2. Integration with external suppliers
50
+ //
51
+ // Date: 2024-09-04
52
+ //
53
+ // ## Status
54
+ //
55
+ // Superseded by [5. Use schemas](0005-use-schemas.md)
56
+ //
57
+ // ## Context
58
+ // …
59
+ //
60
+ // The title carries the number and the title; the id's prefix, which the
61
+ // format has no place for, comes from the step's scope option, as does the
62
+ // scope itself. The date is a line of its own above the record, and the status
63
+ // is the first section of the record, where adr-tools writes it - a word, or
64
+ // "Superseded by" and a link to the record that replaced this one, and
65
+ // "Supersedes" and a link the other way. The record is the same as above:
66
+ // everything from the first `##`, the status section included.
67
+
68
+ var (
69
+ titleLine = regexp.MustCompile(`^#\s+(\S+)\s+—\s+(.+?)\s*$`)
70
+ toolsTitle = regexp.MustCompile(`^#\s+(\d+)\.\s+(.+?)\s*$`)
71
+ plainTitle = regexp.MustCompile(`^#\s+(.+?)\s*$`)
72
+ toolsDate = regexp.MustCompile(`^Date:\s*(.+?)\s*$`)
73
+ toolsLink = regexp.MustCompile(`\[\s*(\d+)\.[^\]]*\]\([^)]*\)`)
74
+ toolsBare = regexp.MustCompile(`(?i)^(?:adr[-\s]?)?0*(\d+)$`)
75
+ statusHead = regexp.MustCompile(`^##\s+Status\s*$`)
76
+ sectionAny = regexp.MustCompile(`^#{1,2}\s`)
77
+ bulletLine = regexp.MustCompile(`^-\s+\*\*([^*:]+):\*\*\s*(.*?)\s*$`)
78
+ bodyStart = regexp.MustCompile(`^##\s`)
79
+ fileName = regexp.MustCompile(`^(\d{4})-([a-z0-9]+(?:-[a-z0-9]+)*)$`)
80
+ adrID = regexp.MustCompile(`^([a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*)\.(\d+)$`)
81
+ scopeValue = regexp.MustCompile(`^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)?$`)
82
+ flowSlug = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
83
+ eventID = regexp.MustCompile(`^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[A-Za-z][A-Za-z0-9]*$`)
84
+ serviceID = regexp.MustCompile(`^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$`)
85
+ )
86
+
87
+ var statuses = map[string]catalog.AdrStatus{
88
+ "proposed": catalog.AdrProposed,
89
+ "accepted": catalog.AdrAccepted,
90
+ "superseded": catalog.AdrSuperseded,
91
+ "deprecated": catalog.AdrDeprecated,
92
+ "rejected": catalog.AdrRejected,
93
+ }
94
+
95
+ // defaults are what the manifest says about a tree of records, for the
96
+ // records that do not say it themselves.
97
+ type defaults struct {
98
+ // Scope is what a record is about when it has no Scope of its own: "org",
99
+ // a context, or "<context>.<service>". It also lends an adr-tools record
100
+ // the prefix of its id, which is the last segment: the service, the
101
+ // context, or "org".
102
+ Scope string
103
+ }
104
+
105
+ type parser struct {
106
+ file string
107
+ lines []string
108
+ errs []string
109
+ d defaults
110
+
111
+ // tools is set when the title is the one adr-tools writes, and picks the
112
+ // way the rest of the file is read.
113
+ tools bool
114
+
115
+ adr catalog.Adr
116
+ }
117
+
118
+ // parseAdr reads one record. Every mistake it can find is collected rather
119
+ // than returned at the first one, because a file with two typos in its header
120
+ // should be fixed once.
121
+ func parseAdr(file, src string, d defaults) (catalog.Adr, []string) {
122
+ p := &parser{
123
+ file: file,
124
+ lines: strings.Split(strings.ReplaceAll(src, "\r\n", "\n"), "\n"),
125
+ d: d,
126
+ }
127
+ p.read()
128
+ if len(p.errs) > 0 {
129
+ return catalog.Adr{}, p.errs
130
+ }
131
+
132
+ return p.adr, nil
133
+ }
134
+
135
+ func (p *parser) fail(line int, msg string) {
136
+ p.errs = append(p.errs, p.file+":"+strconv.Itoa(line+1)+": "+msg)
137
+ }
138
+
139
+ func (p *parser) read() {
140
+ p.adr.Source = p.file
141
+ p.adr.Relates = catalog.AdrRelates{}
142
+
143
+ head := p.title()
144
+ if head < 0 {
145
+ return
146
+ }
147
+ p.checkFileName()
148
+
149
+ body := -1
150
+ if p.tools {
151
+ body = p.toolsMeta(head + 1)
152
+ } else {
153
+ body = p.meta(head + 1)
154
+ }
155
+ if body < 0 {
156
+ return
157
+ }
158
+ p.adr.Body = strings.TrimSpace(strings.Join(p.lines[body:], "\n")) + "\n"
159
+ if p.tools {
160
+ p.toolsStatus(body)
161
+ p.checkSupersession(head)
162
+ }
163
+ }
164
+
165
+ // title reads the one line the record has to open with and answers with its
166
+ // index, or -1 when there is nothing to go on.
167
+ func (p *parser) title() int {
168
+ for i, line := range p.lines {
169
+ if strings.TrimSpace(line) == "" {
170
+ continue
171
+ }
172
+
173
+ match := titleLine.FindStringSubmatch(line)
174
+ if match == nil {
175
+ if tools := toolsTitle.FindStringSubmatch(line); tools != nil {
176
+ return p.toolsTitleLine(i, tools)
177
+ }
178
+ // A title with no number at all is numbered by its file, unless
179
+ // it opens with something shaped like an id: that is a MADR
180
+ // title with the wrong dash, and reading it as a plain one would
181
+ // turn a typo into a record under another name.
182
+ if plain := plainTitle.FindStringSubmatch(line); plain != nil && !adrID.MatchString(strings.Fields(plain[1])[0]) {
183
+ return p.plainTitleLine(i, plain[1])
184
+ }
185
+ p.fail(i, `a record opens with "# <id> — <title>", an em dash between the two, or with "# <n>. <title>" as adr-tools writes it`)
186
+
187
+ return -1
188
+ }
189
+
190
+ id := adrID.FindStringSubmatch(match[1])
191
+ if id == nil {
192
+ p.fail(i, "the id "+strconv.Quote(match[1])+` is not a prefix and a number, as in "auth.0003"`)
193
+
194
+ return -1
195
+ }
196
+ number, err := strconv.Atoi(id[2])
197
+ if err != nil || fmt.Sprintf("%04d", number) != id[2] {
198
+ // The app fails the whole catalog on an id that does not end with
199
+ // its own number, so the digits are the number and are written
200
+ // the one way that round-trips.
201
+ p.fail(i, "the number in "+strconv.Quote(match[1])+" is not four padded digits")
202
+
203
+ return -1
204
+ }
205
+
206
+ p.adr.ID = match[1]
207
+ p.adr.Number = number
208
+ p.adr.Title = strings.TrimSpace(match[2])
209
+
210
+ return i
211
+ }
212
+
213
+ p.fail(0, "the file is empty")
214
+
215
+ return -1
216
+ }
217
+
218
+ // toolsTitleLine reads the title adr-tools writes: the record's number, a
219
+ // full stop, the title. The id is built from the number and the prefix the
220
+ // scope lends, because the format keeps no id of its own.
221
+ func (p *parser) toolsTitleLine(i int, match []string) int {
222
+ number, err := strconv.Atoi(match[1])
223
+ if err != nil || number <= 0 {
224
+ p.fail(i, "the number "+strconv.Quote(match[1])+" is not a record's number")
225
+
226
+ return -1
227
+ }
228
+
229
+ p.tools = true
230
+ p.adr.ID = p.toolsID(number)
231
+ p.adr.Number = number
232
+ p.adr.Title = strings.TrimSpace(match[2])
233
+
234
+ return i
235
+ }
236
+
237
+ // plainTitleLine reads a title that carries no number, which adr-tools trees
238
+ // collect when a record is written by hand: the file's own name numbers it,
239
+ // and the heading is the title.
240
+ func (p *parser) plainTitleLine(i int, title string) int {
241
+ match := fileName.FindStringSubmatch(strings.TrimSuffix(path.Base(p.file), ".md"))
242
+ if match == nil {
243
+ p.fail(i, `the title carries no number and the file is not named "NNNN-kebab-slug.md", so nothing numbers the record`)
244
+
245
+ return -1
246
+ }
247
+ number, _ := strconv.Atoi(match[1])
248
+ if number <= 0 {
249
+ p.fail(i, "the file is numbered "+match[1]+", which is not a record's number")
250
+
251
+ return -1
252
+ }
253
+
254
+ p.tools = true
255
+ p.adr.ID = p.toolsID(number)
256
+ p.adr.Number = number
257
+ p.adr.Title = strings.TrimSpace(title)
258
+
259
+ return i
260
+ }
261
+
262
+ // toolsID is the id an adr-tools record gets: the prefix the scope lends and
263
+ // the number, padded the one way that round-trips.
264
+ func (p *parser) toolsID(number int) string {
265
+ return p.prefix() + "." + fmt.Sprintf("%04d", number)
266
+ }
267
+
268
+ // prefix is what an id opens with, taken from the scope the way the MADR
269
+ // records of the estate do it: the service for a service, the context for a
270
+ // context, and "org" for the organisation.
271
+ func (p *parser) prefix() string {
272
+ scope := strings.TrimSpace(p.d.Scope)
273
+ if scope == "" || scope == "org" {
274
+ return "org"
275
+ }
276
+ if _, service, ok := strings.Cut(scope, "."); ok {
277
+ return service
278
+ }
279
+
280
+ return scope
281
+ }
282
+
283
+ // toolsMeta reads what adr-tools keeps above the record - the date, on a line
284
+ // of its own - and answers with the index the record starts at, or -1 when
285
+ // there is none.
286
+ func (p *parser) toolsMeta(from int) int {
287
+ for i := from; i < len(p.lines); i++ {
288
+ line := p.lines[i]
289
+ switch {
290
+ case bodyStart.MatchString(line):
291
+ if p.adr.Date == "" {
292
+ p.fail(from-1, `the record says no "Date:"`)
293
+ }
294
+ p.scope(from-1, p.d.Scope)
295
+
296
+ return i
297
+
298
+ case strings.TrimSpace(line) == "":
299
+
300
+ case toolsDate.MatchString(line):
301
+ value := toolsDate.FindStringSubmatch(line)[1]
302
+ if _, err := time.Parse("2006-01-02", value); err != nil {
303
+ p.fail(i, strconv.Quote(value)+" is not a date, as in 2026-08-22")
304
+
305
+ continue
306
+ }
307
+ p.adr.Date = value
308
+
309
+ default:
310
+ p.fail(i, "only \"Date:\" belongs between the title and the first `##`")
311
+
312
+ return -1
313
+ }
314
+ }
315
+
316
+ p.fail(len(p.lines)-1, "the record has no body: nothing here is under a `##`")
317
+
318
+ return -1
319
+ }
320
+
321
+ // toolsStatus reads the status from the record's own Status section, which is
322
+ // where adr-tools keeps it. Each non-empty line there is one statement: a
323
+ // status word, or "Superseded by" or "Supersedes" and the record it means. A
324
+ // later statement wins over an earlier one, because a record that was accepted
325
+ // and then superseded has both written down, in that order.
326
+ func (p *parser) toolsStatus(body int) {
327
+ head := -1
328
+ for i := body; i < len(p.lines); i++ {
329
+ if statusHead.MatchString(p.lines[i]) {
330
+ head = i
331
+
332
+ break
333
+ }
334
+ }
335
+ if head < 0 {
336
+ p.fail(body, "the record has no `## Status` section")
337
+
338
+ return
339
+ }
340
+
341
+ statements := 0
342
+ for i := head + 1; i < len(p.lines) && !sectionAny.MatchString(p.lines[i]); i++ {
343
+ statement := strings.TrimSpace(p.lines[i])
344
+ if statement == "" {
345
+ continue
346
+ }
347
+ statements++
348
+ p.toolsStatement(i, statement)
349
+ }
350
+ if statements == 0 {
351
+ p.fail(head, "the Status section says nothing")
352
+ }
353
+ if p.adr.Status == "" && len(p.errs) == 0 {
354
+ p.fail(head, "the Status section names no status: proposed, accepted, superseded, deprecated or rejected")
355
+ }
356
+ }
357
+
358
+ func (p *parser) toolsStatement(at int, statement string) {
359
+ lower := strings.ToLower(strings.TrimRight(statement, ". "))
360
+ if status, ok := statuses[lower]; ok {
361
+ p.adr.Status = status
362
+
363
+ return
364
+ }
365
+
366
+ switch {
367
+ case strings.HasPrefix(lower, "superseded by"):
368
+ ids := p.toolsRecords(statement[len("superseded by"):])
369
+ if len(ids) != 1 {
370
+ p.fail(at, "the record is superseded and says by which one record")
371
+
372
+ return
373
+ }
374
+ p.adr.Status = catalog.AdrSuperseded
375
+ p.adr.SupersededBy = ids[0]
376
+
377
+ case strings.HasPrefix(lower, "supersedes"):
378
+ ids := p.toolsRecords(statement[len("supersedes"):])
379
+ if len(ids) == 0 {
380
+ p.fail(at, "the record supersedes something and says which record")
381
+
382
+ return
383
+ }
384
+ p.adr.Supersedes = append(p.adr.Supersedes, ids...)
385
+
386
+ case strings.HasPrefix(lower, "amends"), strings.HasPrefix(lower, "amended by"):
387
+ // An amendment is a fact the catalog has no field for. It stays in the
388
+ // body, which is on the page as written.
389
+
390
+ default:
391
+ p.fail(at, strconv.Quote(statement)+` is not a status: proposed, accepted, superseded, deprecated or rejected, or "Superseded by" a record`)
392
+ }
393
+ }
394
+
395
+ // toolsRecords finds the records a statement names, as the links adr-tools
396
+ // writes - "[3. Use DTO](0003-use-dto.md)" - or as bare numbers.
397
+ func (p *parser) toolsRecords(rest string) []string {
398
+ var ids []string
399
+ for _, match := range toolsLink.FindAllStringSubmatch(rest, -1) {
400
+ number, err := strconv.Atoi(match[1])
401
+ if err != nil {
402
+ continue
403
+ }
404
+ ids = append(ids, p.toolsID(number))
405
+ }
406
+ if len(ids) > 0 {
407
+ return ids
408
+ }
409
+ for _, word := range strings.Fields(strings.NewReplacer(",", " ", "and", " ").Replace(rest)) {
410
+ if match := toolsBare.FindStringSubmatch(word); match != nil {
411
+ number, _ := strconv.Atoi(match[1])
412
+ ids = append(ids, p.toolsID(number))
413
+ }
414
+ }
415
+
416
+ return ids
417
+ }
418
+
419
+ // checkFileName ties the record to the file it is in. The slug is built from
420
+ // both - the id says which record this is, the file's name says what it was
421
+ // about - so a file renamed away from its record would silently change the
422
+ // URL of a decision somebody linked to.
423
+ func (p *parser) checkFileName() {
424
+ base := strings.TrimSuffix(path.Base(p.file), ".md")
425
+
426
+ match := fileName.FindStringSubmatch(base)
427
+ if match == nil {
428
+ p.fail(0, "the file is named "+strconv.Quote(base+".md")+`, not "NNNN-kebab-slug.md"`)
429
+
430
+ return
431
+ }
432
+ if number, _ := strconv.Atoi(match[1]); number != p.adr.Number {
433
+ p.fail(0, "the file is numbered "+match[1]+" and the record inside it is "+p.adr.ID)
434
+
435
+ return
436
+ }
437
+
438
+ p.adr.Slug = strings.ReplaceAll(p.adr.ID, ".", "-") + "-" + match[2]
439
+ }
440
+
441
+ // meta reads the bullets between the title and the record, and answers with
442
+ // the index the record starts at, or -1 when there is none.
443
+ func (p *parser) meta(from int) int {
444
+ seen := map[string]bool{}
445
+ key, value, at := "", "", 0
446
+ flush := func() {
447
+ if key != "" {
448
+ p.bullet(at, key, value)
449
+ }
450
+ key, value = "", ""
451
+ }
452
+
453
+ for i := from; i < len(p.lines); i++ {
454
+ line := p.lines[i]
455
+ switch {
456
+ case bodyStart.MatchString(line):
457
+ flush()
458
+ p.require(from-1, seen)
459
+
460
+ return i
461
+
462
+ case strings.TrimSpace(line) == "":
463
+ flush()
464
+
465
+ case bulletLine.MatchString(line):
466
+ flush()
467
+ match := bulletLine.FindStringSubmatch(line)
468
+ key, value, at = strings.TrimSpace(match[1]), match[2], i
469
+ if seen[key] {
470
+ p.fail(i, strconv.Quote(key)+" is written twice")
471
+ }
472
+ seen[key] = true
473
+
474
+ case key != "" && (strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t")):
475
+ // A bullet wraps onto the next line, indented under itself. The
476
+ // break is the author's line width and means nothing, so it
477
+ // closes up into a space.
478
+ value = strings.TrimSpace(value + " " + strings.TrimSpace(line))
479
+
480
+ default:
481
+ p.fail(i, "only meta bullets belong between the title and the first `##`")
482
+
483
+ return -1
484
+ }
485
+ }
486
+
487
+ flush()
488
+ p.fail(len(p.lines)-1, "the record has no body: nothing here is under a `##`")
489
+
490
+ return -1
491
+ }
492
+
493
+ // require reports the three bullets a record cannot be read without. They are
494
+ // reported against the title rather than against the missing line, because
495
+ // there is no missing line to point at.
496
+ func (p *parser) require(at int, seen map[string]bool) {
497
+ for _, key := range []string{"Status", "Date"} {
498
+ if !seen[key] {
499
+ p.fail(at, "the record says no "+strconv.Quote(key))
500
+ }
501
+ }
502
+ // A record that does not say what it is about is about what the step says
503
+ // its tree is about; only when the step says nothing either is that a
504
+ // mistake.
505
+ switch {
506
+ case seen["Scope"]:
507
+ case strings.TrimSpace(p.d.Scope) != "":
508
+ p.scope(at, p.d.Scope)
509
+ default:
510
+ p.fail(at, `the record says no "Scope"`)
511
+ }
512
+
513
+ p.checkSupersession(at)
514
+ }
515
+
516
+ // checkSupersession holds the two things a record says about being replaced
517
+ // against each other. Supersession is a two-way fact and half of it recorded
518
+ // is a bug. The other half - that the record named actually names this one
519
+ // back - can only be checked once every record is read, so it is checked
520
+ // there.
521
+ func (p *parser) checkSupersession(at int) {
522
+ if p.adr.Status == catalog.AdrSuperseded && p.adr.SupersededBy == "" {
523
+ p.fail(at, "the record is superseded and says by what")
524
+ }
525
+ if p.adr.SupersededBy != "" && p.adr.Status != catalog.AdrSuperseded {
526
+ p.fail(at, "the record is superseded by "+p.adr.SupersededBy+` but its status is "`+string(p.adr.Status)+`"`)
527
+ }
528
+ }
529
+
530
+ func (p *parser) bullet(at int, key, value string) {
531
+ switch key {
532
+ case "Status":
533
+ status, ok := statuses[value]
534
+ if !ok {
535
+ p.fail(at, strconv.Quote(value)+" is not a status: proposed, accepted, superseded, deprecated or rejected")
536
+
537
+ return
538
+ }
539
+ p.adr.Status = status
540
+
541
+ case "Date":
542
+ if _, err := time.Parse("2006-01-02", value); err != nil {
543
+ p.fail(at, strconv.Quote(value)+" is not a date, as in 2026-08-22")
544
+
545
+ return
546
+ }
547
+ p.adr.Date = value
548
+
549
+ case "Scope":
550
+ p.scope(at, value)
551
+
552
+ case "Superseded by":
553
+ if ids := p.ids(at, key, value); len(ids) > 1 {
554
+ p.fail(at, "a record is superseded by one record, not "+strconv.Itoa(len(ids)))
555
+ } else if len(ids) == 1 {
556
+ p.adr.SupersededBy = ids[0]
557
+ }
558
+
559
+ case "Supersedes":
560
+ p.adr.Supersedes = p.ids(at, key, value)
561
+
562
+ case "Relates":
563
+ p.relates(at, value)
564
+
565
+ case "Note":
566
+ p.adr.Note = value
567
+
568
+ default:
569
+ p.fail(at, strconv.Quote(key)+" is not one of the bullets a record carries")
570
+ }
571
+ }
572
+
573
+ // scope says what the record is about, and the number of segments says which
574
+ // of the three kinds it is: none for the organisation, one for a context, two
575
+ // for a service. Whether the thing named exists is not a question this side
576
+ // can answer - an extractor sees one service's tree - so it is left to the
577
+ // validator, which sees the merged catalog.
578
+ func (p *parser) scope(at int, value string) {
579
+ value = strings.TrimSpace(value)
580
+ if value == "" || value == "org" {
581
+ p.adr.Scope = catalog.AdrScope{Kind: "org"}
582
+
583
+ return
584
+ }
585
+ if !scopeValue.MatchString(value) {
586
+ p.fail(at, strconv.Quote(value)+` is not a scope: "org", a context, or "<context>.<service>"`)
587
+
588
+ return
589
+ }
590
+ if strings.Contains(value, ".") {
591
+ p.adr.Scope = catalog.AdrScope{Kind: "service", Service: value}
592
+
593
+ return
594
+ }
595
+ p.adr.Scope = catalog.AdrScope{Kind: "context", Context: value}
596
+ }
597
+
598
+ // relates names events, services and flows in one comma-separated list and
599
+ // they are told apart by their shape: a flow by its slug, which has no dots; a
600
+ // service by `<context>.<service>`; an event by the aggregate and Name after
601
+ // that. A name matching none of the three is a typo, and reported here rather
602
+ // than left to come back from the validator as a reference to something that
603
+ // does not exist.
604
+ func (p *parser) relates(at int, value string) {
605
+ for _, id := range split(value) {
606
+ switch {
607
+ case eventID.MatchString(id):
608
+ p.adr.Relates.Events = append(p.adr.Relates.Events, id)
609
+ case serviceID.MatchString(id):
610
+ p.adr.Relates.Services = append(p.adr.Relates.Services, id)
611
+ case flowSlug.MatchString(id):
612
+ p.adr.Relates.Flows = append(p.adr.Relates.Flows, id)
613
+ default:
614
+ p.fail(at, strconv.Quote(id)+" is not the shape of an event, a service or a flow")
615
+ }
616
+ }
617
+ }
618
+
619
+ func (p *parser) ids(at int, key, value string) []string {
620
+ var ids []string
621
+ for _, id := range split(value) {
622
+ if !adrID.MatchString(id) {
623
+ p.fail(at, strconv.Quote(id)+" in "+strconv.Quote(key)+" is not a record's id")
624
+
625
+ continue
626
+ }
627
+ ids = append(ids, id)
628
+ }
629
+
630
+ return ids
631
+ }
632
+
633
+ func split(value string) []string {
634
+ var out []string
635
+ for _, part := range strings.Split(value, ",") {
636
+ if part = strings.TrimSpace(part); part != "" {
637
+ out = append(out, part)
638
+ }
639
+ }
640
+
641
+ return out
642
+ }