@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
package/src/er/spec.ts ADDED
@@ -0,0 +1,426 @@
1
+ // What an ER canvas draws, worked out before anything is rendered.
2
+ //
3
+ // Pure, like lib/context-map.ts: a store in, nodes and edges out. The canvas
4
+ // component then only positions and paints what this decided, which is what
5
+ // makes "which columns does a collapsed table show?" a question with a test
6
+ // rather than a question with a screenshot.
7
+ //
8
+ // A canvas carries two kinds of card and two kinds of edge. Tables and views
9
+ // are both relations with columns, so they are drawn alike and told apart by
10
+ // their header; foreign keys and lineage are both lines between columns, so
11
+ // they are drawn alike and told apart by their direction and their dash. The
12
+ // difference that matters is what a reader can conclude: a key says a row
13
+ // points at a row, lineage says a value was copied from a value.
14
+
15
+ import type {
16
+ CatalogIndex,
17
+ Column,
18
+ Store,
19
+ Table,
20
+ View,
21
+ } from "../catalog";
22
+ import {
23
+ columnNameOfId,
24
+ relationOfColumnId,
25
+ storeViews,
26
+ viewReads,
27
+ } from "../catalog";
28
+ import { lineageEdgeId } from "./lineage";
29
+
30
+ /** Header, one row, and the "+n more" footer, in px. Layout needs the height. */
31
+ export const HEADER_H = 28;
32
+ export const ROW_H = 18;
33
+ export const MORE_H = 16;
34
+ const TABLE_W = 208;
35
+
36
+ /**
37
+ * Rows drawn before a card starts scrolling inside itself. Twelve is where a
38
+ * card stops being a shape you can take in at a glance and becomes a list you
39
+ * have to read; past that the canvas is better served by a shorter card and a
40
+ * detail panel than by a taller one.
41
+ */
42
+ export const MAX_ROWS = 12;
43
+
44
+ /** How much of a table is on show. */
45
+ export type ColumnMode = "keys" | "all";
46
+
47
+ /** A card is a table or a view; everything else about them is the same. */
48
+ export type ErNodeKind = "table" | "view";
49
+
50
+ export interface ErNode {
51
+ id: string;
52
+ kind: ErNodeKind;
53
+ name: string;
54
+ doc: string | null;
55
+ /** The table, when this card is one. Exactly one of these two is set. */
56
+ table: Table | null;
57
+ /** The view, when this card is one. */
58
+ view: View | null;
59
+ store: Store;
60
+ /** Every column the relation has, in declaration order. */
61
+ columns: Column[];
62
+ /** The columns actually drawn, in declaration order. */
63
+ rows: Column[];
64
+ /** Columns left out, summarised as "+n more". */
65
+ hidden: number;
66
+ /** True when `rows` is longer than the card can show without scrolling. */
67
+ scrolls: boolean;
68
+ /** The aggregate this relation holds or presents, if it names one. */
69
+ aggregate: string | null;
70
+ /** The context that owns that aggregate — the header tint, and nothing else. */
71
+ context: string | null;
72
+ /** A store the service reads but does not own. */
73
+ ghost: boolean;
74
+ /** The word on the header chip: "root", "outbox", "view", "matview". */
75
+ badge: string | null;
76
+ width: number;
77
+ height: number;
78
+ }
79
+
80
+ /**
81
+ * What a line between two cards means.
82
+ *
83
+ * "fk" is a constraint the database enforces and is drawn child → parent, with
84
+ * the crow's foot at the child. "lineage" is a copy the database performs and
85
+ * is drawn source → derived, with a plain arrow: it is a direction of travel,
86
+ * not a cardinality, and drawing it with the same marks would say the view
87
+ * constrains the table it reads.
88
+ */
89
+ export type ErEdgeKind = "fk" | "lineage";
90
+
91
+ export interface ErEdge {
92
+ id: string;
93
+ kind: ErEdgeKind;
94
+ /** Relation ids. For "fk", `from` holds the key, so it is the many end. */
95
+ from: string;
96
+ to: string;
97
+ /** Null on a lineage edge that only knows the relation it reads. */
98
+ fromColumn: string | null;
99
+ toColumn: string | null;
100
+ /** Printed on the edge only when it is not the database's default. */
101
+ onDelete: string | null;
102
+ /** True when the two relations are owned by different services. */
103
+ cross: boolean;
104
+ }
105
+
106
+ export interface ErSpec {
107
+ nodes: ErNode[];
108
+ edges: ErEdge[];
109
+ }
110
+
111
+ /**
112
+ * `on delete no action` is what a database does when nothing says otherwise, so
113
+ * printing it on an edge adds a word and no fact. Anything else changes what
114
+ * deleting the parent row means, and that belongs on the picture.
115
+ */
116
+ function shownOnDelete(onDelete: string | undefined): string | null {
117
+ if (!onDelete) return null;
118
+ const normalised = onDelete.trim().toLowerCase().replace(/\s+/g, " ");
119
+ return normalised === "no action" || normalised === "restrict"
120
+ ? null
121
+ : onDelete;
122
+ }
123
+
124
+ export interface ErOptions {
125
+ mode: ColumnMode;
126
+ /** Tables the reader has opened by hand; they show every column. */
127
+ expanded?: ReadonlySet<string>;
128
+ /** Stores the service reads rather than owns, drawn ghosted. */
129
+ ghost?: boolean;
130
+ /** Views on the canvas. On by default — a schema is what the reader asked for. */
131
+ views?: boolean;
132
+ /** Lineage edges. On by default, and off is a legible picture, not a broken one. */
133
+ lineage?: boolean;
134
+ }
135
+
136
+ /** Anything with columns and an id: a table or a view, asked the same question. */
137
+ type Relation = { id: string; columns: Column[] };
138
+
139
+ /**
140
+ * The columns a card shows. Keys-only is the default because a foreign key is
141
+ * the only column that says anything about the table NEXT to this one, and the
142
+ * canvas is a picture of what points at what — the rest is detail the panel
143
+ * holds. A derived column counts as a key for this purpose: it is the one thing
144
+ * on a view that joins it to anything else, and a view whose every column were
145
+ * hidden would be a card with a name and nothing under it.
146
+ *
147
+ * A relation with none of those still shows its first rows rather than an empty
148
+ * card: a lookup table with no constraints is still a shape.
149
+ */
150
+ export function visibleColumns(
151
+ relation: Relation,
152
+ options: ErOptions,
153
+ ): Column[] {
154
+ if (options.mode === "all" || options.expanded?.has(relation.id)) {
155
+ return relation.columns;
156
+ }
157
+ const joined = relation.columns.filter(
158
+ (c) => c.pk || c.fk || (c.from?.length ?? 0) > 0,
159
+ );
160
+ return joined.length > 0 ? joined : relation.columns.slice(0, 3);
161
+ }
162
+
163
+ export function nodeHeight(rows: number, hidden: number): number {
164
+ return (
165
+ HEADER_H + Math.min(rows, MAX_ROWS) * ROW_H + (hidden > 0 ? MORE_H : 0)
166
+ );
167
+ }
168
+
169
+ const ROLE_BADGE: Record<string, string> = {
170
+ "aggregate-root": "root",
171
+ child: "child",
172
+ outbox: "outbox",
173
+ projection: "projection",
174
+ lookup: "lookup",
175
+ };
176
+
177
+ /** One store's canvas. Relations in catalog order; layout decides where they land. */
178
+ export function erSpec(
179
+ index: CatalogIndex,
180
+ store: Store,
181
+ options: ErOptions,
182
+ ): ErSpec {
183
+ const showViews = options.views ?? true;
184
+ const showLineage = options.lineage ?? true;
185
+
186
+ /** The context that owns an aggregate, which is all the tint ever reads. */
187
+ const contextOf = (aggregate: string | null): string | null => {
188
+ const owner = aggregate ? index.aggregateOwner.get(aggregate) : undefined;
189
+ return owner ? (index.serviceContext.get(owner.id)?.id ?? null) : null;
190
+ };
191
+
192
+ const card = (
193
+ id: string,
194
+ kind: ErNodeKind,
195
+ name: string,
196
+ doc: string | undefined,
197
+ columns: Column[],
198
+ aggregate: string | null,
199
+ badge: string | null,
200
+ table: Table | null,
201
+ view: View | null,
202
+ ): ErNode => {
203
+ const rows = visibleColumns({ id, columns }, options);
204
+ const hidden = columns.length - rows.length;
205
+ return {
206
+ id,
207
+ kind,
208
+ name,
209
+ doc: doc ?? null,
210
+ table,
211
+ view,
212
+ store,
213
+ columns,
214
+ rows,
215
+ hidden,
216
+ scrolls: rows.length > MAX_ROWS,
217
+ aggregate,
218
+ context: contextOf(aggregate),
219
+ ghost: options.ghost ?? false,
220
+ badge,
221
+ width: TABLE_W,
222
+ height: nodeHeight(rows.length, hidden),
223
+ };
224
+ };
225
+
226
+ const nodes: ErNode[] = store.tables.map((table) =>
227
+ card(
228
+ table.id,
229
+ "table",
230
+ table.name,
231
+ table.doc,
232
+ table.columns,
233
+ table.persists?.aggregate ?? null,
234
+ ROLE_BADGE[table.role ?? "other"] ?? null,
235
+ table,
236
+ null,
237
+ ),
238
+ );
239
+
240
+ if (showViews) {
241
+ for (const view of storeViews(store)) {
242
+ nodes.push(
243
+ card(
244
+ view.id,
245
+ "view",
246
+ view.name,
247
+ view.doc,
248
+ view.columns,
249
+ view.persists?.aggregate ?? null,
250
+ view.materialized ? "matview" : "view",
251
+ null,
252
+ view,
253
+ ),
254
+ );
255
+ }
256
+ }
257
+
258
+ const own = new Set(nodes.map((n) => n.id));
259
+ const edges: ErEdge[] = [];
260
+
261
+ for (const table of store.tables) {
262
+ for (const column of table.columns) {
263
+ const fk = column.fk;
264
+ if (!fk) continue;
265
+ // An edge is drawn only when both ends are on this canvas. A key into
266
+ // another service's store is a fact about the boundary, not about this
267
+ // picture, and the Problems page is where it is answered.
268
+ if (!own.has(fk.table)) continue;
269
+ const target = index.tableById.get(fk.table);
270
+ edges.push({
271
+ id: `${table.id}.${column.name}->${fk.table}.${fk.column}`,
272
+ kind: "fk",
273
+ from: table.id,
274
+ to: fk.table,
275
+ fromColumn: column.name,
276
+ toColumn: fk.column,
277
+ onDelete: shownOnDelete(fk.onDelete),
278
+ cross: target ? target.store.owner !== store.owner : false,
279
+ });
280
+ }
281
+ }
282
+
283
+ if (showLineage) {
284
+ /** Relations already joined by a column-level edge, so `reads` can skip them. */
285
+ const joined = new Set<string>();
286
+ for (const node of nodes) {
287
+ for (const column of node.columns) {
288
+ for (const ref of column.from ?? []) {
289
+ const source = relationOfColumnId(ref);
290
+ if (!own.has(source) || source === node.id) continue;
291
+ joined.add(`${source}->${node.id}`);
292
+ const from = index.tableById.get(source) ?? index.viewById.get(source);
293
+ edges.push({
294
+ id: lineageEdgeId(ref, `${node.id}.${column.name}`),
295
+ kind: "lineage",
296
+ from: source,
297
+ to: node.id,
298
+ fromColumn: columnNameOfId(ref),
299
+ toColumn: column.name,
300
+ onDelete: null,
301
+ cross: from ? from.store.owner !== store.owner : false,
302
+ });
303
+ }
304
+ }
305
+ }
306
+
307
+ // A view that says what it reads but not which column fed which is still
308
+ // telling the reader something they cannot get anywhere else, so it gets a
309
+ // line — one per pair, drawn card to card rather than row to row.
310
+ for (const node of nodes) {
311
+ if (!node.view) continue;
312
+ for (const readId of viewReads(node.view)) {
313
+ if (!own.has(readId) || readId === node.id) continue;
314
+ if (joined.has(`${readId}->${node.id}`)) continue;
315
+ joined.add(`${readId}->${node.id}`);
316
+ edges.push({
317
+ id: lineageEdgeId(readId, node.id),
318
+ kind: "lineage",
319
+ from: readId,
320
+ to: node.id,
321
+ fromColumn: null,
322
+ toColumn: null,
323
+ onDelete: null,
324
+ cross: false,
325
+ });
326
+ }
327
+ }
328
+ }
329
+
330
+ return { nodes, edges };
331
+ }
332
+
333
+ /**
334
+ * Foreign keys leaving this store. They are not edges — there is nothing on
335
+ * the canvas to point at — so they are listed under the store header instead,
336
+ * where "this schema is not self-contained" can actually be read.
337
+ */
338
+ export interface ErOutbound {
339
+ from: string;
340
+ fromColumn: string;
341
+ to: string;
342
+ /** The service that owns the far end. */
343
+ peer: string;
344
+ }
345
+
346
+ export function outboundKeys(
347
+ index: CatalogIndex,
348
+ store: Store,
349
+ ): ErOutbound[] {
350
+ const own = new Set(store.tables.map((t) => t.id));
351
+ const out: ErOutbound[] = [];
352
+ for (const table of store.tables) {
353
+ for (const column of table.columns) {
354
+ const fk = column.fk;
355
+ if (!fk || own.has(fk.table)) continue;
356
+ const target = index.tableById.get(fk.table);
357
+ out.push({
358
+ from: table.id,
359
+ fromColumn: column.name,
360
+ to: fk.table,
361
+ peer: target?.store.owner ?? "unknown",
362
+ });
363
+ }
364
+ }
365
+ return out;
366
+ }
367
+
368
+ /**
369
+ * Columns this store copies from somewhere else. The same shape as an outbound
370
+ * key and for the same reason — the far end is not on the canvas — but a
371
+ * different sentence: a key that leaves the store is a constraint nobody can
372
+ * enforce, a value that comes in from another store is a copy nobody is told
373
+ * to refresh.
374
+ */
375
+ export function outboundLineage(
376
+ index: CatalogIndex,
377
+ store: Store,
378
+ ): ErOutbound[] {
379
+ const own = new Set<string>([
380
+ ...store.tables.map((t) => t.id),
381
+ ...storeViews(store).map((v) => v.id),
382
+ ]);
383
+ const out: ErOutbound[] = [];
384
+ const relations: { id: string; columns: Column[] }[] = [
385
+ ...store.tables,
386
+ ...storeViews(store),
387
+ ];
388
+ for (const relation of relations) {
389
+ for (const column of relation.columns) {
390
+ for (const ref of column.from ?? []) {
391
+ const source = relationOfColumnId(ref);
392
+ if (own.has(source)) continue;
393
+ const target =
394
+ index.tableById.get(source) ?? index.viewById.get(source);
395
+ out.push({
396
+ from: relation.id,
397
+ fromColumn: column.name,
398
+ to: ref,
399
+ peer: target?.store.owner ?? "unknown",
400
+ });
401
+ }
402
+ }
403
+ }
404
+ return out;
405
+ }
406
+
407
+ /**
408
+ * Cards matching a search term, by name, column name, or the aggregate they
409
+ * hold. Returns ids so the canvas can light them without re-deriving anything.
410
+ */
411
+ export function matchingNodes(spec: ErSpec, term: string): Set<string> {
412
+ const needle = term.trim().toLowerCase();
413
+ if (!needle) return new Set();
414
+ const out = new Set<string>();
415
+ for (const node of spec.nodes) {
416
+ const hay = [
417
+ node.name,
418
+ node.aggregate ?? "",
419
+ node.doc ?? "",
420
+ node.view?.definition ?? "",
421
+ ...node.columns.map((c) => c.name),
422
+ ];
423
+ if (hay.some((h) => h.toLowerCase().includes(needle))) out.add(node.id);
424
+ }
425
+ return out;
426
+ }
@@ -0,0 +1,162 @@
1
+ // The "then what" tree, drawn in the rail's own vocabulary: an indented row per
2
+ // node, a status square at the end of every row, and the corner arrow the rail
3
+ // uses for "continues in". Every row links to the thing it names, and a receipt
4
+ // or a published event links to the step it was read from.
5
+
6
+ import { Link } from "react-router";
7
+ import { CornerDownRight } from "lucide-react";
8
+
9
+ import { statusVar } from "../components/primitives";
10
+ import { ctxStyle } from "../lib/context-color";
11
+ import { plural } from "../lib/format";
12
+ import { eventPath, paths, servicePath } from "../routes";
13
+ import { CHAIN_BUDGET } from "./chain";
14
+ import type { ChainCut, ChainNode, EventChain } from "./chain";
15
+
16
+ const indent = (depth: number) => 8 + depth * 12;
17
+
18
+ export function ChainList({ chain }: { chain: EventChain }) {
19
+ return (
20
+ <div className="flex flex-col" data-nav-list>
21
+ {chain.nodes.map((node, i) => (
22
+ <Row key={`${i}`} node={node} path={`${i}`} />
23
+ ))}
24
+ {chain.truncated ? (
25
+ <div
26
+ className="mono py-0.5 text-muted"
27
+ style={{ paddingLeft: indent(0) }}
28
+ title={`the walk stops at ${CHAIN_BUDGET} rows`}
29
+ >
30
+ … and more than fits here
31
+ </div>
32
+ ) : null}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ function Row({ node, path }: { node: ChainNode; path: string }) {
38
+ return (
39
+ <>
40
+ <div
41
+ className="flex items-start gap-2 py-0.5 pr-2"
42
+ style={{ paddingLeft: indent(node.depth) }}
43
+ >
44
+ <Label node={node} />
45
+ <span
46
+ aria-hidden
47
+ className="mt-1.5 ml-auto size-1.5 shrink-0 rounded-[1px]"
48
+ style={{ background: statusVar(node.status) }}
49
+ title={
50
+ node.worst === node.status
51
+ ? node.status
52
+ : `${node.status}; ${node.worst} further down`
53
+ }
54
+ />
55
+ </div>
56
+ {node.kind === "consumer" && node.known && node.children.length === 0 ? (
57
+ <Aside depth={node.depth + 1}>no flow shows this service hearing it</Aside>
58
+ ) : null}
59
+ {node.children.map((child, i) => (
60
+ <Row key={`${path}.${i}`} node={child} path={`${path}.${i}`} />
61
+ ))}
62
+ {node.cut ? <Cut cut={node.cut} depth={node.depth + 1} /> : null}
63
+ </>
64
+ );
65
+ }
66
+
67
+ function Label({ node }: { node: ChainNode }) {
68
+ switch (node.kind) {
69
+ case "consumer": {
70
+ const to = servicePath(node.service);
71
+ return (
72
+ <span className="mono flex min-w-0 flex-wrap items-center gap-1.5">
73
+ {to ? (
74
+ <Link to={to} data-nav-item className="rounded-control text-accent">
75
+ {node.service}
76
+ </Link>
77
+ ) : (
78
+ <span className="text-unresolved" title="no service in the catalog answers to this name">
79
+ {node.service}
80
+ </span>
81
+ )}
82
+ {node.context ? <ContextChip id={node.context} /> : null}
83
+ {node.via ? (
84
+ <span
85
+ className="text-muted"
86
+ title={`read from flow ${node.via.flow}, step ${node.via.step}; no source declares it`}
87
+ >
88
+ via flow
89
+ </span>
90
+ ) : null}
91
+ </span>
92
+ );
93
+ }
94
+ case "receipt":
95
+ return (
96
+ <Link
97
+ to={paths.flowStep(node.flow, node.stepId)}
98
+ data-nav-item
99
+ className="mono flex min-w-0 items-center gap-1 rounded-control text-accent hover:underline"
100
+ title={`the step where it hears the event, in ${node.name}`}
101
+ >
102
+ <CornerDownRight size={9} aria-hidden className="shrink-0" />
103
+ <span className="truncate">
104
+ in {node.name} · step {node.number}
105
+ </span>
106
+ </Link>
107
+ );
108
+ case "event": {
109
+ const to = eventPath(node.id);
110
+ return (
111
+ <span className="mono flex min-w-0 flex-wrap items-center gap-1.5">
112
+ <span className="text-muted">publishes</span>
113
+ {to ? (
114
+ <Link to={to} data-nav-item className="rounded-control text-accent">
115
+ {node.name}
116
+ </Link>
117
+ ) : (
118
+ <span>{node.name}</span>
119
+ )}
120
+ {node.context ? <ContextChip id={node.context} /> : null}
121
+ <Link
122
+ to={paths.flowStep(node.flow, node.stepId)}
123
+ className="text-muted hover:text-ink hover:underline"
124
+ title="the step it is published at"
125
+ >
126
+ step {node.number}
127
+ </Link>
128
+ </span>
129
+ );
130
+ }
131
+ }
132
+ }
133
+
134
+ function ContextChip({ id }: { id: string }) {
135
+ return (
136
+ <span className="chip ctx" style={ctxStyle(id)}>
137
+ <span aria-hidden className="dot" />
138
+ {id}
139
+ </span>
140
+ );
141
+ }
142
+
143
+ function Aside({ depth, children }: { depth: number; children: string }) {
144
+ return (
145
+ <div className="mono py-0.5 text-muted" style={{ paddingLeft: indent(depth) }}>
146
+ {children}
147
+ </div>
148
+ );
149
+ }
150
+
151
+ function Cut({ cut, depth }: { cut: ChainCut; depth: number }) {
152
+ const n = cut.hidden;
153
+ const text =
154
+ cut.reason === "cycle"
155
+ ? "already on this path — the chain comes back around"
156
+ : cut.reason === "seen"
157
+ ? "followed above"
158
+ : cut.reason === "depth"
159
+ ? `${n} ${plural(n, "consumer")} further down, past the depth this page follows`
160
+ : `and ${n} more`;
161
+ return <Aside depth={depth}>{text}</Aside>;
162
+ }
@@ -0,0 +1,123 @@
1
+ // Compact mode: the same sequence, as a table.
2
+ //
3
+ // The rail is for reading a flow in order. This is for the other question —
4
+ // "which steps are unresolved", "what does payments touch", "give me the list"
5
+ // — and that question wants columns, sorting, filtering and an export, all of
6
+ // which the app already has exactly once. So compact mode is not a narrower
7
+ // rail; it is the flow handed to the same DataTable every index page uses.
8
+
9
+ import { useMemo } from "react";
10
+ import type { Flow, Status, Step } from "../catalog";
11
+ import { contextName } from "../lib/context-color";
12
+ import { flowStepId } from "../selection/model";
13
+ import { DataTable } from "../table/DataTable";
14
+ import type { ColumnSpec } from "../table/types";
15
+ import type { Chapter } from "./chapters";
16
+ import type { OutlineStep } from "./outline";
17
+
18
+ interface Row {
19
+ id: string;
20
+ number: number;
21
+ step: string;
22
+ route: string;
23
+ kind: string;
24
+ status: Status;
25
+ note: string | undefined;
26
+ chapter: string;
27
+ /** Not a column: what the row's selection lights up elsewhere. */
28
+ selection: string;
29
+ }
30
+
31
+ const COLUMNS: ColumnSpec<Row>[] = [
32
+ { id: "number", header: "#", type: "number", value: (r) => r.number, size: 56 },
33
+ {
34
+ id: "step",
35
+ header: "step",
36
+ type: "text",
37
+ value: (r) => r.step,
38
+ primary: true,
39
+ size: 260,
40
+ },
41
+ {
42
+ id: "route",
43
+ header: "from → to",
44
+ type: "mono",
45
+ value: (r) => r.route,
46
+ size: 300,
47
+ },
48
+ { id: "kind", header: "kind", type: "text", value: (r) => r.kind, facet: true, size: 96 },
49
+ {
50
+ id: "status",
51
+ header: "status",
52
+ type: "status",
53
+ value: (r) => r.status,
54
+ facet: true,
55
+ size: 128,
56
+ },
57
+ {
58
+ id: "chapter",
59
+ header: "chapter",
60
+ type: "text",
61
+ // Not offered as a chip-set: a chapter is named by its condition, and a
62
+ // toolbar of fifteen sentences is taller than the table it filters. The
63
+ // text filter reaches them, and the rail is where chapters are navigated.
64
+ value: (r) => r.chapter,
65
+ size: 220,
66
+ },
67
+ { id: "note", header: "note", type: "text", value: (r) => r.note, size: 320 },
68
+ ];
69
+
70
+ /** The two participants, with the context each sits in when it has one. */
71
+ function route(step: Step, contextOf: (id: string) => string | null): string {
72
+ const name = (id: string): string => {
73
+ const context = contextOf(id);
74
+ return context ? `${id} (${contextName(context)})` : id;
75
+ };
76
+ return step.from === step.to
77
+ ? `${name(step.from)} ↺`
78
+ : `${name(step.from)} → ${name(step.to)}`;
79
+ }
80
+
81
+ export function FlowTable({
82
+ flow,
83
+ rows,
84
+ chapters,
85
+ contextOf,
86
+ }: {
87
+ flow: Flow;
88
+ /** The rail's step rows, so the table shows exactly what the rail shows. */
89
+ rows: readonly OutlineStep[];
90
+ chapters: readonly Chapter[];
91
+ contextOf: (participantId: string) => string | null;
92
+ }) {
93
+ const data = useMemo<Row[]>(() => {
94
+ const chapterOf = new Map<string, string>();
95
+ for (const chapter of chapters) {
96
+ for (const stepId of chapter.stepIds) chapterOf.set(stepId, chapter.title);
97
+ }
98
+ return rows.map(({ step, number }) => ({
99
+ id: step.id,
100
+ number,
101
+ step: step.label ?? step.ref ?? step.kind,
102
+ route: route(step, contextOf),
103
+ kind: step.kind,
104
+ status: step.status,
105
+ note: step.note,
106
+ chapter: chapterOf.get(step.id) ?? "—",
107
+ selection: flowStepId(flow.slug, step.id),
108
+ }));
109
+ }, [rows, chapters, contextOf, flow.slug]);
110
+
111
+ return (
112
+ <DataTable
113
+ tableId={`flow-steps:${flow.slug}`}
114
+ columns={COLUMNS}
115
+ rows={data}
116
+ rowId={(row) => row.id}
117
+ defaultSort={[{ id: "number", desc: false }]}
118
+ selectionId={(row) => row.selection}
119
+ caption={`${flow.name} — steps`}
120
+ empty="no step survives the filters on this view"
121
+ />
122
+ );
123
+ }