@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,686 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { Link, useParams } from "react-router";
3
+ import { ChevronDown, ChevronRight, Minus, Plus } from "lucide-react";
4
+ import { catalog, index } from "../data";
5
+ import { plural } from "../lib/format";
6
+ import type { Field } from "../catalog";
7
+ import { backlinkCount, stepsInto } from "../lib/backlinks";
8
+ import { outboxOfService } from "../lib/data-model";
9
+ import { ctxStyle } from "../lib/context-color";
10
+ import {
11
+ eventScope,
12
+ openablePaths,
13
+ resolveShape,
14
+ schemaChanges,
15
+ } from "../lib/shape";
16
+ import type { Change, Scope } from "../lib/shape";
17
+ import {
18
+ EVENT_ANCHOR,
19
+ LINKS_HERE,
20
+ paths,
21
+ servicePath,
22
+ tablePath,
23
+ } from "../routes";
24
+ import { Empty, PageHeader, SectionTitle } from "../components/PageHeader";
25
+ import { Select } from "../components/Select";
26
+ import { Ident } from "../components/Ident";
27
+ import { ShapeBody, TypeCell } from "../components/FieldTree";
28
+ import { RowActions } from "../components/RowActions";
29
+ import { DataTable } from "../table/DataTable";
30
+ import type { ColumnSpec } from "../table/types";
31
+ import { Toc } from "../components/Toc";
32
+ import type { TocItem } from "../components/Toc";
33
+ import { StatusChip } from "../components/primitives";
34
+ import { useBacklinks, WhatLinksHere } from "../components/WhatLinksHere";
35
+ import { InLanguage } from "../language/InLanguage";
36
+ import { NotFound } from "./NotFound";
37
+ import { FocusedEventGraphPane } from "../graph/FocusedEventGraph";
38
+ import { eventChain } from "../flow/chain";
39
+ import { ChainList } from "../flow/ChainList";
40
+
41
+ /**
42
+ * One row of the schema table: a field of the version shown, or a field the
43
+ * version dropped. A dropped field is still a row because "what did v3 take
44
+ * away" is asked at the same table as "what did it add", and a table of the
45
+ * fields that survived cannot answer it.
46
+ */
47
+ interface SchemaRow extends Field {
48
+ change?: Change;
49
+ /** The type the previous version gave the field, when this one changed it. */
50
+ from?: string;
51
+ }
52
+
53
+ /** What the change column says, and in what colour. */
54
+ const CHANGE: Record<Change, { label: string; className: string; title: string }> = {
55
+ new: {
56
+ label: "new",
57
+ className: "text-verified",
58
+ title: "added in this version",
59
+ },
60
+ changed: {
61
+ label: "changed",
62
+ className: "text-declared",
63
+ title: "type changed in this version",
64
+ },
65
+ removed: {
66
+ label: "removed",
67
+ className: "text-unresolved",
68
+ title: "dropped by this version — shown as the previous version had it",
69
+ },
70
+ };
71
+
72
+ function ChangeMark({ row }: { row: SchemaRow }) {
73
+ if (!row.change) return null;
74
+ const mark = CHANGE[row.change];
75
+ const title =
76
+ row.change === "changed" && row.from
77
+ ? `was ${row.from} in the previous version`
78
+ : mark.title;
79
+ return (
80
+ <span
81
+ className={`mono inline-flex items-center gap-1 ${mark.className}`}
82
+ title={title}
83
+ >
84
+ {row.change === "new" ? <Plus size={10} aria-hidden /> : null}
85
+ {row.change === "removed" ? <Minus size={10} aria-hidden /> : null}
86
+ {mark.label}
87
+ </span>
88
+ );
89
+ }
90
+
91
+ /**
92
+ * The schema, as columns the table knows how to sort and filter. Field order
93
+ * in a proto is meaningful, so nothing is sorted until the reader asks; what
94
+ * the table adds is the ability to ask - "which fields are strings", "which
95
+ * one was the timestamp" - over a schema too long to scan.
96
+ *
97
+ * A field whose type the catalog has a shape for - a shared def by ref, or a
98
+ * value object or entity of the aggregate by name - opens under its row, and
99
+ * keeps opening as far as the shapes go.
100
+ */
101
+ function schemaColumns(
102
+ scope: Scope,
103
+ open: ReadonlySet<string>,
104
+ onToggle: (path: string) => void,
105
+ ): ColumnSpec<SchemaRow>[] {
106
+ const columns: ColumnSpec<SchemaRow>[] = [
107
+ {
108
+ id: "name",
109
+ header: "name",
110
+ type: "mono",
111
+ value: (row) => row.name,
112
+ primary: true,
113
+ cell: (row) => {
114
+ const struck = row.deprecated || row.change === "removed";
115
+ const nameClass = `mono${struck ? " line-through" : ""}${row.change === "removed" ? " text-muted" : ""}`;
116
+ const title = row.change === "removed"
117
+ ? "removed in this version"
118
+ : row.deprecated
119
+ ? "deprecated"
120
+ : undefined;
121
+ // Only a field the catalog has a shape for has anything to open.
122
+ const shape =
123
+ row.change === "removed" ? null : resolveShape(catalog, row, scope);
124
+ return shape ? (
125
+ <button
126
+ type="button"
127
+ onClick={() => onToggle(row.name)}
128
+ className="mono flex items-center gap-1 rounded-control"
129
+ aria-expanded={open.has(row.name)}
130
+ title={`${open.has(row.name) ? "collapse" : "expand"} ${shape.name}`}
131
+ >
132
+ {open.has(row.name) ? (
133
+ <ChevronDown size={11} aria-hidden className="text-muted" />
134
+ ) : (
135
+ <ChevronRight size={11} aria-hidden className="text-muted" />
136
+ )}
137
+ <span className={nameClass} title={title}>
138
+ {row.name}
139
+ </span>
140
+ </button>
141
+ ) : (
142
+ <span className={`${nameClass} pl-4`} title={title}>
143
+ {row.name}
144
+ </span>
145
+ );
146
+ },
147
+ },
148
+ {
149
+ id: "type",
150
+ header: "type",
151
+ type: "mono",
152
+ value: (row) => row.type,
153
+ cell: (row) => (
154
+ <TypeCell
155
+ field={row}
156
+ shape={
157
+ row.change === "removed" ? null : resolveShape(catalog, row, scope)
158
+ }
159
+ />
160
+ ),
161
+ },
162
+ {
163
+ id: "doc",
164
+ header: "doc",
165
+ type: "text",
166
+ value: (row) => row.doc,
167
+ cell: (row) => <span className="meta">{row.doc}</span>,
168
+ },
169
+ {
170
+ id: "change",
171
+ header: "change",
172
+ type: "text",
173
+ // Sortable and filterable like any other column: "show me what this
174
+ // version did" is a question about the schema, not a decoration.
175
+ value: (row) => row.change,
176
+ cell: (row) => <ChangeMark row={row} />,
177
+ facet: true,
178
+ enableHiding: false,
179
+ size: 88,
180
+ },
181
+ ];
182
+ return columns;
183
+ }
184
+
185
+ /** " · +2 · ~1 · −1", or nothing when the version changed nothing. */
186
+ function changeSummary(rows: SchemaRow[]): string {
187
+ const n = (change: Change) =>
188
+ rows.filter((row) => row.change === change).length;
189
+ const parts = [
190
+ [n("new"), "+"],
191
+ [n("changed"), "~"],
192
+ [n("removed"), "−"],
193
+ ] as const;
194
+ return parts
195
+ .filter(([count]) => count > 0)
196
+ .map(([count, sign]) => ` · ${sign}${count}`)
197
+ .join("");
198
+ }
199
+
200
+ export function EventPage() {
201
+ const {
202
+ context: contextId,
203
+ service: serviceSlug,
204
+ aggregate: aggSlug,
205
+ event: eventSlug,
206
+ } = useParams();
207
+ const context = catalog.contexts.find((c) => c.id === contextId);
208
+ const service = context?.services.find((s) => s.slug === serviceSlug);
209
+ const aggregate = service?.aggregates.find((a) => a.slug === aggSlug);
210
+ const event = aggregate?.events.find((e) => e.slug === eventSlug);
211
+
212
+ const latest = event?.versions[event.versions.length - 1]?.version ?? "";
213
+ const [version, setVersion] = useState(latest);
214
+ // Dotted paths of the rows opened, at every depth: "items", then
215
+ // "items.unitPrice". Kept across versions on purpose - a reader comparing
216
+ // v1 to v2 wants the same fields open in both.
217
+ const [open, setOpen] = useState<Set<string>>(new Set());
218
+
219
+ const selected = useMemo(
220
+ () =>
221
+ event?.versions.find((v) => v.version === version) ?? event?.versions[0],
222
+ [event, version],
223
+ );
224
+ // The version's rows, with what it did to each against the one before.
225
+ const rows = useMemo<SchemaRow[]>(() => {
226
+ if (!event || !selected) return [];
227
+ const { byField, removed } = schemaChanges(event, selected.version);
228
+ return [
229
+ ...selected.fields.map((field) => ({ ...field, ...byField.get(field.name) })),
230
+ ...removed.map((field) => ({ ...field, change: "removed" as const })),
231
+ ];
232
+ }, [event, selected]);
233
+ const scope = useMemo<Scope>(
234
+ () =>
235
+ event ? eventScope(index, event) : { aggregate: null, service: null },
236
+ [event],
237
+ );
238
+ // Everything the tree could open under this version, for "expand all".
239
+ const openable = useMemo(
240
+ () => (selected ? openablePaths(catalog, selected.fields, scope) : []),
241
+ [selected, scope],
242
+ );
243
+ // Flows, decisions and consumers all point AT this event, so they are one
244
+ // question with one answer; the section at the bottom is where it is given.
245
+ const links = useBacklinks({ kind: "event", id: event?.id ?? "" });
246
+
247
+ const toggle = useCallback(
248
+ (path: string) =>
249
+ setOpen((prev) => {
250
+ const next = new Set(prev);
251
+ if (next.has(path)) next.delete(path);
252
+ else next.add(path);
253
+ return next;
254
+ }),
255
+ [],
256
+ );
257
+ // Above the not-found return, with every other hook: the columns are built
258
+ // once per render of a schema, and a hook cannot sit behind a branch.
259
+ const schema = useMemo(
260
+ () => schemaColumns(scope, open, toggle),
261
+ [scope, open, toggle],
262
+ );
263
+ // What follows this event, as far as the flows say; and, for a consumer no
264
+ // source declared, the number of the step it was read from.
265
+ const eventId = event?.id ?? "";
266
+ const chain = useMemo(() => eventChain(catalog, eventId), [eventId]);
267
+ const stepNumber = useMemo(
268
+ () =>
269
+ new Map(
270
+ stepsInto(catalog, new Set([eventId])).map((s) => [
271
+ `${s.flow.slug}|${s.stepId}`,
272
+ s.number,
273
+ ]),
274
+ ),
275
+ [eventId],
276
+ );
277
+
278
+ if (!context || !service || !aggregate || !event || !selected) {
279
+ return <NotFound kind="Event" id={eventSlug} />;
280
+ }
281
+
282
+ const outbox = outboxOfService(index, service.id);
283
+ const outboxTo = outbox ? tablePath(outbox.table.id) : null;
284
+
285
+ // The four things there are to know about an event, in the order a reader
286
+ // asks them: what shape is it, how did it get that shape, who listens, and
287
+ // what else in the estate names it.
288
+ const toc: TocItem[] = [
289
+ { id: EVENT_ANCHOR.schema, label: "Schema" },
290
+ { id: EVENT_ANCHOR.versions, label: "Versions" },
291
+ { id: EVENT_ANCHOR.consumers, label: "Consumers" },
292
+ { id: EVENT_ANCHOR.then, label: "Then what" },
293
+ { id: LINKS_HERE, label: "What links here" },
294
+ ];
295
+
296
+ /** Every count on this page opens the section that holds what it counted. */
297
+ const Count = ({
298
+ n,
299
+ anchor,
300
+ unit,
301
+ }: {
302
+ n: number;
303
+ anchor: string;
304
+ unit: string;
305
+ }) => (
306
+ <a
307
+ href={`#${anchor}`}
308
+ className="mono rounded-control text-muted hover:text-ink"
309
+ title={`jump to ${unit}`}
310
+ >
311
+ <span className="tnum">{n}</span> {unit}
312
+ </a>
313
+ );
314
+
315
+ return (
316
+ <div className="h-full overflow-y-auto">
317
+ <PageHeader
318
+ kind={
319
+ <>
320
+ event ·{" "}
321
+ <Link
322
+ to={paths.aggregate(context.id, service.slug, aggregate.slug)}
323
+ className="rounded-control hover:text-ink hover:underline"
324
+ >
325
+ {aggregate.id}
326
+ </Link>
327
+ </>
328
+ }
329
+ name={event.name}
330
+ id={event.id}
331
+ contextId={context.id}
332
+ pin={{ kind: "event", id: event.id }}
333
+ right={
334
+ <span className="mono flex items-center gap-1.5 text-muted">
335
+ {selected.deprecated ? (
336
+ <span className="chip" title="this version is marked @deprecated in the source">
337
+ deprecated
338
+ </span>
339
+ ) : null}
340
+ version
341
+ <Select
342
+ value={selected.version}
343
+ onChange={setVersion}
344
+ label="Schema version"
345
+ title="Which version of this event's schema the page shows"
346
+ menuWidth={260}
347
+ options={[...event.versions].reverse().map((v) => ({
348
+ value: v.version,
349
+ label:
350
+ v.version === latest ? `${v.version} (latest)` : v.version,
351
+ note: v.doc,
352
+ }))}
353
+ />
354
+ </span>
355
+ }
356
+ >
357
+ <p className="mt-2 max-w-prose text-muted">{selected.doc}</p>
358
+ <div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1">
359
+ <Ident value={selected.source} className="text-muted" />
360
+ <span aria-hidden className="h-4 w-px bg-line-strong" />
361
+ <Count
362
+ n={selected.fields.length}
363
+ anchor={EVENT_ANCHOR.schema}
364
+ unit={plural(selected.fields.length, "field")}
365
+ />
366
+ <Count
367
+ n={event.versions.length}
368
+ anchor={EVENT_ANCHOR.versions}
369
+ unit={plural(event.versions.length, "version")}
370
+ />
371
+ <Count
372
+ n={event.consumers.length}
373
+ anchor={EVENT_ANCHOR.consumers}
374
+ unit={plural(event.consumers.length, "consumer")}
375
+ />
376
+ <Count
377
+ n={backlinkCount(links)}
378
+ anchor={LINKS_HERE}
379
+ unit={plural(backlinkCount(links), "link here", "links here")}
380
+ />
381
+ </div>
382
+ {/* What the bus sees: the name on the message and the channel it goes
383
+ on. The one line to take to a subscription or a trace search. */}
384
+ {event.wire ? (
385
+ <div className="meta mt-2">
386
+ On the wire as{" "}
387
+ <span className="mono text-ink">{event.wire.name}</span>
388
+ {event.wire.channel ? (
389
+ <>
390
+ , on <span className="mono">{event.wire.channel}</span>
391
+ </>
392
+ ) : null}
393
+ </div>
394
+ ) : null}
395
+ {/* How this event actually leaves the service. It is a fact about the
396
+ publisher, not about the schema, but it belongs here: a consumer
397
+ reading this page wants to know whether the event is committed with
398
+ the state change or published on a best effort after it. */}
399
+ {outbox ? (
400
+ <div className="meta mt-2">
401
+ Delivery: transactional outbox via{" "}
402
+ {outboxTo ? (
403
+ <Link
404
+ to={outboxTo}
405
+ className="mono rounded-control text-accent hover:underline"
406
+ >
407
+ {outbox.table.name}
408
+ </Link>
409
+ ) : (
410
+ <span className="mono text-ink">{outbox.table.name}</span>
411
+ )}{" "}
412
+ in <span className="mono">{outbox.store.slug}</span>
413
+ </div>
414
+ ) : null}
415
+ </PageHeader>
416
+
417
+ <div className="flex gap-section p-gutter">
418
+ <div className="min-w-0 flex-1">
419
+ <InLanguage id={event.id} />
420
+
421
+ {/* --- Schema ------------------------------------------------- */}
422
+ <section id={EVENT_ANCHOR.schema}>
423
+ <SectionTitle
424
+ anchor={EVENT_ANCHOR.schema}
425
+ right={
426
+ <span className="flex items-center gap-x-3">
427
+ {/* Both offered while anything is open: a reader who
428
+ opened three of eight shapes wants "the rest" as much
429
+ as "none", and either word alone guesses which. */}
430
+ {openable.length > 0 ? (
431
+ <span className="mono flex items-center gap-x-2">
432
+ {openable.some((path) => !open.has(path)) ? (
433
+ <button
434
+ type="button"
435
+ onClick={() => setOpen(new Set(openable))}
436
+ className="rounded-control text-muted hover:text-ink"
437
+ title={`open every nested shape (${openable.length})`}
438
+ >
439
+ expand all
440
+ </button>
441
+ ) : null}
442
+ {openable.some((path) => open.has(path)) ? (
443
+ <button
444
+ type="button"
445
+ onClick={() => setOpen(new Set())}
446
+ className="rounded-control text-muted hover:text-ink"
447
+ >
448
+ collapse all
449
+ </button>
450
+ ) : null}
451
+ </span>
452
+ ) : null}
453
+ <span>
454
+ {selected.fields.length}{" "}
455
+ {plural(selected.fields.length, "field")}
456
+ {changeSummary(rows)} ·{" "}
457
+ <span className="mono">{selected.version}</span>
458
+ </span>
459
+ </span>
460
+ }
461
+ >
462
+ Schema
463
+ </SectionTitle>
464
+ {/* The field list is the widest thing on the page. Its header and
465
+ its first column stay put while the rest scrolls under them. */}
466
+ <div className="max-w-table">
467
+ <DataTable
468
+ /* Per version: the schema a reader widened is the schema they
469
+ were reading, and v1 and v3 are not the same schema. */
470
+ tableId={`event-schema.${event.id}`}
471
+ caption={`Schema of ${event.id} ${selected.version}`}
472
+ columns={schema}
473
+ rows={rows}
474
+ rowId={(row) => row.name}
475
+ subRow={(row) => {
476
+ if (row.change === "removed" || !open.has(row.name)) return null;
477
+ const shape = resolveShape(catalog, row, scope);
478
+ return shape ? (
479
+ <ShapeBody
480
+ shape={shape}
481
+ scope={scope}
482
+ path={row.name}
483
+ open={open}
484
+ onToggle={toggle}
485
+ seen={new Set([shape.id])}
486
+ depth={1}
487
+ root
488
+ />
489
+ ) : null;
490
+ }}
491
+ rowActions={(row) => (
492
+ <RowActions
493
+ copy={`${event.id}@${selected.version}.${row.name}`}
494
+ label={row.name}
495
+ />
496
+ )}
497
+ />
498
+ </div>
499
+ </section>
500
+
501
+ {/* --- Versions ----------------------------------------------- */}
502
+ <section
503
+ id={EVENT_ANCHOR.versions}
504
+ className="mt-section max-w-table"
505
+ >
506
+ <SectionTitle anchor={EVENT_ANCHOR.versions} right="oldest first">
507
+ Versions
508
+ </SectionTitle>
509
+ {/* The row is a div holding a button, not a button holding
510
+ buttons: the actions are interactive too, and interactive
511
+ content does not nest. */}
512
+ <div className="flex flex-col gap-1" data-nav-list>
513
+ {event.versions.map((v) => {
514
+ const on = v.version === selected.version;
515
+ return (
516
+ <div
517
+ key={v.version}
518
+ className="row items-start gap-3"
519
+ style={{
520
+ borderColor: on ? "var(--accent)" : "var(--border)",
521
+ }}
522
+ >
523
+ <button
524
+ type="button"
525
+ data-nav-item
526
+ onClick={() => setVersion(v.version)}
527
+ aria-pressed={on}
528
+ className="flex min-w-0 flex-1 items-start gap-3 rounded-control text-left"
529
+ >
530
+ <span
531
+ className="mono shrink-0 rounded-[4px] border px-1"
532
+ style={{
533
+ borderColor: on ? "var(--accent)" : "var(--border)",
534
+ color: on ? "var(--accent)" : "var(--fg-muted)",
535
+ }}
536
+ >
537
+ {v.version}
538
+ </span>
539
+ {v.version === latest ? (
540
+ <span className="mono shrink-0 text-muted">latest</span>
541
+ ) : null}
542
+ <span className="min-w-0 flex-1 truncate" title={v.doc}>
543
+ {v.doc}
544
+ </span>
545
+ </button>
546
+ <span className="mono shrink-0 text-muted">
547
+ {v.fields.length}f
548
+ </span>
549
+ <RowActions
550
+ copy={`${event.id}@${v.version}`}
551
+ label={`${event.name} ${v.version}`}
552
+ />
553
+ </div>
554
+ );
555
+ })}
556
+ </div>
557
+ </section>
558
+
559
+ {/* --- Consumers ---------------------------------------------- */}
560
+ <section
561
+ id={EVENT_ANCHOR.consumers}
562
+ className="mt-section max-w-table"
563
+ >
564
+ <SectionTitle
565
+ anchor={EVENT_ANCHOR.consumers}
566
+ right={
567
+ <span>
568
+ published by{" "}
569
+ <Link
570
+ to={paths.service(context.id, service.slug)}
571
+ className="chip ctx"
572
+ style={ctxStyle(context.id)}
573
+ >
574
+ <span aria-hidden className="dot" />
575
+ {service.id}
576
+ </Link>
577
+ </span>
578
+ }
579
+ >
580
+ Consumers
581
+ </SectionTitle>
582
+
583
+ {/* With nobody listening there is no picture to draw: publisher
584
+ and event are already the two lines above, and half a section
585
+ of empty canvas beside one sentence says only that the layout
586
+ expected something else. */}
587
+ {event.consumers.length === 0 ? (
588
+ <Empty>nobody is listening — this event falls silent</Empty>
589
+ ) : (
590
+ /* The list and the picture stack rather than sit side by side.
591
+ The picture runs left to right - publisher, event, consumers -
592
+ and half a column is not enough width for three layers of it:
593
+ it fits by zooming out, and a node zoomed out is a grey mark
594
+ where a name was. */
595
+ <div className="flex flex-col gap-3">
596
+ <div className="flex flex-col gap-1.5" data-nav-list>
597
+ {event.consumers.map((consumer) => {
598
+ const to = servicePath(consumer.service);
599
+ return (
600
+ <div
601
+ key={consumer.service}
602
+ className="row items-start gap-2"
603
+ >
604
+ <div className="min-w-0 flex-1">
605
+ {to ? (
606
+ <Link
607
+ to={to}
608
+ data-nav-item
609
+ className="mono rounded-control text-accent"
610
+ >
611
+ {consumer.service}
612
+ </Link>
613
+ ) : (
614
+ <span className="mono text-unresolved">
615
+ {consumer.service}
616
+ </span>
617
+ )}
618
+ {consumer.note ? (
619
+ <p className="mt-0.5 text-muted">{consumer.note}</p>
620
+ ) : null}
621
+ {/* No source declared this consumer: a flow showed
622
+ the service hearing the event, and this says
623
+ which step, so the claim can be checked. */}
624
+ {consumer.via ? (
625
+ <Link
626
+ to={paths.flowStep(consumer.via.flow, consumer.via.step)}
627
+ className="chip mt-1 text-muted hover:text-ink"
628
+ title="derived from a flow step, not declared by any source"
629
+ >
630
+ from flow {consumer.via.flow}
631
+ {stepNumber.has(`${consumer.via.flow}|${consumer.via.step}`)
632
+ ? ` · step ${stepNumber.get(`${consumer.via.flow}|${consumer.via.step}`)}`
633
+ : ""}
634
+ </Link>
635
+ ) : null}
636
+ </div>
637
+ <StatusChip status={consumer.status} />
638
+ <RowActions
639
+ copy={consumer.service}
640
+ {...(to ? { reveal: consumer.service } : {})}
641
+ />
642
+ </div>
643
+ );
644
+ })}
645
+ </div>
646
+
647
+ {/* producer -> event -> consumers, the same fact as a picture */}
648
+ <FocusedEventGraphPane event={event} />
649
+ </div>
650
+ )}
651
+ </section>
652
+
653
+ {/* --- Then what -------------------------------------------- */}
654
+ {/* The consumers say who hears it; this says what they do next,
655
+ read off the flow step where each is shown hearing it. Nothing
656
+ here is a new fact, and every row links to the step it came
657
+ from. */}
658
+ <section id={EVENT_ANCHOR.then} className="mt-section max-w-table">
659
+ <SectionTitle anchor={EVENT_ANCHOR.then}>Then what</SectionTitle>
660
+ {chain.nodes.length === 0 ? (
661
+ <Empty>nothing follows an event nobody hears</Empty>
662
+ ) : (
663
+ <ChainList chain={chain} />
664
+ )}
665
+ </section>
666
+
667
+ {/* --- What links here -------------------------------------- */}
668
+ {/* Consumers keep their own section: a row can carry the name and
669
+ the status, but not the note or the picture beside them. */}
670
+ <WhatLinksHere
671
+ target={{ kind: "event", id: event.id }}
672
+ elsewhere={{
673
+ service: {
674
+ href: `#${EVENT_ANCHOR.consumers}`,
675
+ label: "Consumers",
676
+ },
677
+ }}
678
+ empty="nothing in the catalog names this event — no flow, no decision, nobody listening"
679
+ />
680
+ </div>
681
+
682
+ <Toc items={toc} label="Sections of this event" />
683
+ </div>
684
+ </div>
685
+ );
686
+ }