@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,746 @@
1
+ import { useState } from "react";
2
+ import { TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react";
3
+ import { Link, useParams, useSearchParams } from "react-router";
4
+ import { catalog } from "../data";
5
+ import {
6
+ allRepos,
7
+ commandsOf,
8
+ componentKind,
9
+ ownersOf,
10
+ technologiesOf,
11
+ } from "../catalog";
12
+ import { flowRoles } from "../lib/derive";
13
+ import { treeHref } from "../lib/source-link";
14
+ import { flowHealth } from "../lib/flow-tree";
15
+ import { adrsForService, isCurrent } from "../lib/adr";
16
+ import { ADR_ROW_COLUMNS, AdrRow } from "../components/AdrRow";
17
+ import { EVENT_ANCHOR, SERVICE_ANCHOR, aggregatePath, paths } from "../routes";
18
+ import { Markdown } from "../components/Markdown";
19
+ import { middleTruncate, plural } from "../lib/format";
20
+ import { Empty, PageHeader, SectionTitle } from "../components/PageHeader";
21
+ import { Ident } from "../components/Ident";
22
+ import { TabButton, TabCount, TabRow } from "../components/TabRow";
23
+ import { MessageList, MethodRows } from "../components/MethodRows";
24
+ import { docPathOf, pickSpec } from "../lib/source-doc";
25
+ import { ApiReference, hasSpec } from "../components/ApiReference";
26
+ import { WsdlReference } from "../components/WsdlReference";
27
+ import { Integrations } from "../components/Integrations";
28
+ import { integrationsFor } from "../lib/integrations";
29
+ import {
30
+ AsyncApiReference,
31
+ hasAsyncSpec,
32
+ } from "../components/AsyncApiReference";
33
+ import { ChannelRows } from "../components/ChannelRows";
34
+ import { CommandRows } from "../components/CommandRows";
35
+ import { ModuleSpec } from "../components/SourceDoc";
36
+ import { hasSchema, SchemaDocument } from "../components/SchemaDocument";
37
+ import { methodCount, operationsExposedBy } from "../lib/api";
38
+ import { KindIcon } from "../components/kind";
39
+ import { TechIcon } from "../components/TechIcon";
40
+ import { techGlyph } from "../lib/tech";
41
+ import { RowActions } from "../components/RowActions";
42
+ import { ContextPill, StatusChip } from "../components/primitives";
43
+ import { WhatLinksHere } from "../components/WhatLinksHere";
44
+ import { NotFound } from "./NotFound";
45
+ import { C4View } from "../likec4/C4View";
46
+ import { LevelSwitch } from "../likec4/levels";
47
+ import type { C4Level } from "../likec4/levels";
48
+ import { serviceInsideViewId, serviceViewId } from "../likec4/ids";
49
+ import { index } from "../data";
50
+ import { storesOfService } from "../lib/data-model";
51
+ import { ErCanvas } from "../er/ErCanvas";
52
+ import { StoreHeader } from "../er/StoreHeader";
53
+ import { RedisSchema } from "../er/RedisSchema";
54
+
55
+ const TABS = [
56
+ "overview",
57
+ "provides",
58
+ // The document beside the facts drawn from it. Always present rather than
59
+ // appearing only for services that have one, because every other tab on this
60
+ // page is too - a tab that comes and goes makes the page a different shape
61
+ // per service and the url stop meaning the same thing.
62
+ "spec",
63
+ "consumes",
64
+ // What the service says on the bus: the channels it declares, and the
65
+ // document they were read out of. Beside `provides` and `spec` rather than
66
+ // inside them, because a call and a message are answered differently and a
67
+ // reader is asking one question or the other.
68
+ "bus",
69
+ "data",
70
+ "flows",
71
+ "decisions",
72
+ ] as const;
73
+ type Tab = (typeof TABS)[number];
74
+
75
+ function isTab(value: string | null): value is Tab {
76
+ return value !== null && (TABS as readonly string[]).includes(value);
77
+ }
78
+
79
+ export function ServicePage() {
80
+ const { context: contextId, service: serviceSlug } = useParams();
81
+ const [params, setParams] = useSearchParams();
82
+ const [showRetired, setShowRetired] = useState(false);
83
+ /** Which C4 level the model canvas is drawn at: the service, or its parts. */
84
+ const [level, setLevel] = useState<C4Level>(2);
85
+ // Read-only stores are off by default: they belong to someone else, and the
86
+ // question this tab opens with is what THIS service is responsible for.
87
+ const [showReadOnly, setShowReadOnly] = useState(false);
88
+
89
+ // Which request and response shapes are open. Collapsed by default: the
90
+ // question the provides tab answers first is "what can I call", and a page
91
+ // that opens with six schemas answers a question nobody asked yet.
92
+ const [openShapes, setOpenShapes] = useState<ReadonlySet<string>>(new Set());
93
+ const toggleShape = (id: string) =>
94
+ setOpenShapes((open) => {
95
+ const next = new Set(open);
96
+ if (!next.delete(id)) next.add(id);
97
+
98
+ return next;
99
+ });
100
+ const context = catalog.contexts.find((c) => c.id === contextId);
101
+ const service = context?.services.find((s) => s.slug === serviceSlug);
102
+
103
+ const raw = params.get("tab");
104
+ const tab: Tab = isTab(raw) ? raw : "overview";
105
+
106
+ if (!context || !service) return <NotFound kind="Service" id={serviceSlug} />;
107
+
108
+ const events = service.aggregates.flatMap((aggregate) =>
109
+ aggregate.events.map((event) => ({ aggregate, event })),
110
+ );
111
+ const stores = storesOfService(index, service.id);
112
+ const flows = flowRoles(catalog, service.id);
113
+ const tree = treeHref(service.path, service, allRepos(catalog));
114
+ const owners = ownersOf(service);
115
+ const adrs = adrsForService(catalog, service.id, context.id);
116
+ const current = adrs.filter(isCurrent);
117
+ const retired = adrs.filter((a) => !isCurrent(a));
118
+ // What the spec tab has to show: a document this repository holds, else the
119
+ // schema module the interfaces were declared in, else nothing.
120
+ const spec = pickSpec(
121
+ service,
122
+ (source) => hasSpec(source) || hasSchema(source),
123
+ );
124
+ // The channels the service declares, and the document behind them when this
125
+ // repository holds it. A channel names its own source, so the tab does not
126
+ // have to be told where to look.
127
+ const channels = service.channels ?? [];
128
+ const busDoc = channels
129
+ .map((channel) => channel.source)
130
+ .find((source) => source !== undefined && hasAsyncSpec(source));
131
+ const showDomain =
132
+ componentKind(service) === "service" || service.aggregates.length > 0;
133
+ const integrations = integrationsFor(service, catalog);
134
+
135
+ const counts: Record<Tab, number | null> = {
136
+ overview: null,
137
+ provides: methodCount(service),
138
+ spec: null,
139
+ consumes: integrations.length,
140
+ bus: channels.length,
141
+ data: stores.length,
142
+ flows: flows.length,
143
+ decisions: adrs.length,
144
+ };
145
+
146
+ return (
147
+ /* `manual`: the arrow keys move focus and ⏎ commits, rather than switching
148
+ the panel on every keypress. Each tab here is a page - one of them draws
149
+ a C4 view - and automatic activation would render four of them on the
150
+ way to the fifth, writing four URLs behind it. */
151
+ <TabGroup
152
+ manual
153
+ as="div"
154
+ className="h-full overflow-y-auto"
155
+ selectedIndex={TABS.indexOf(tab)}
156
+ onChange={(at) => {
157
+ const next = TABS[at] ?? "overview";
158
+ setParams(next === "overview" ? {} : { tab: next }, { replace: true });
159
+ }}
160
+ >
161
+ <PageHeader
162
+ kind="service"
163
+ name={service.name}
164
+ id={service.id}
165
+ contextId={context.id}
166
+ pin={{ kind: "service", id: service.id }}
167
+ right={
168
+ <>
169
+ <span className="chip-lg">{componentKind(service)}</span>
170
+ <ContextPill id={context.id} name={context.name} />
171
+ </>
172
+ }
173
+ >
174
+ <div className="mono mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-muted">
175
+ <Ident value={service.repo}>{middleTruncate(service.repo, 32)}</Ident>
176
+ <Ident value={service.path}>{middleTruncate(service.path, 32)}</Ident>
177
+ {tree ? (
178
+ <a
179
+ href={tree}
180
+ target="_blank"
181
+ rel="noreferrer"
182
+ className="rounded-control text-accent hover:underline"
183
+ title="Open the service's directory on the forge, at the built commit"
184
+ >
185
+ open ↗
186
+ </a>
187
+ ) : null}
188
+ {/* Who to ask, read out of CODEOWNERS. A handle is copied rather
189
+ than linked: it is what a reviewer types into a request, and a
190
+ link to a team page is a page most readers cannot open. */}
191
+ {owners.length > 0 ? (
192
+ <span className="flex flex-wrap items-center gap-x-2 gap-y-1">
193
+ <span className="text-muted">owned by</span>
194
+ {owners.map((handle) => (
195
+ <Ident key={handle} value={handle}>
196
+ {middleTruncate(handle, 28)}
197
+ </Ident>
198
+ ))}
199
+ </span>
200
+ ) : null}
201
+ {/* What it is built with, read off its manifests. A brand that has
202
+ a mark shows it beside the name; one that does not is the name
203
+ alone, which is what every chip was before. */}
204
+ {technologiesOf(service).map((technology) => {
205
+ const glyph = techGlyph(technology);
206
+ return (
207
+ <span key={technology} className="chip">
208
+ {glyph ? <TechIcon glyph={glyph} /> : null}
209
+ {technology}
210
+ </span>
211
+ );
212
+ })}
213
+ <span aria-hidden className="h-4 w-px bg-line-strong" />
214
+ {/* Both counts land in the section that lists what they counted;
215
+ the tabs above do the same job for the other four. */}
216
+ {showDomain ? (
217
+ <a
218
+ href={`#${SERVICE_ANCHOR.aggregates}`}
219
+ className="rounded-control hover:text-ink"
220
+ >
221
+ <span className="tnum">{service.aggregates.length}</span>{" "}
222
+ {plural(service.aggregates.length, "aggregate")}
223
+ </a>
224
+ ) : null}
225
+ {showDomain ? (
226
+ <a
227
+ href={`#${SERVICE_ANCHOR.events}`}
228
+ className="rounded-control hover:text-ink"
229
+ >
230
+ <span className="tnum">{events.length}</span>{" "}
231
+ {plural(events.length, "event")}
232
+ </a>
233
+ ) : null}
234
+ </div>
235
+
236
+ {/* Tabs keep the underline rather than becoming a segmented box: they
237
+ switch the page under them, they do not filter a list beside them.
238
+ The 2px rule is always drawn, transparent when idle, so selecting a
239
+ tab never lifts the row.
240
+
241
+ Eight of them do not fit a narrow window, so the row scrolls inside
242
+ `TabRow` and says at its edge that it does. */}
243
+ <div className="mt-4">
244
+ <TabRow active={tab}>
245
+ <TabList className="flex w-max gap-0">
246
+ {TABS.map((t) => (
247
+ <TabButton key={t}>
248
+ {t === "consumes" ? "integrations" : t}
249
+ {counts[t] !== null ? <TabCount>{counts[t]}</TabCount> : null}
250
+ </TabButton>
251
+ ))}
252
+ </TabList>
253
+ </TabRow>
254
+ </div>
255
+ </PageHeader>
256
+
257
+ <TabPanels className="p-gutter">
258
+ <TabPanel>
259
+ <>
260
+ {/* Two scopes of the same service: the box among the ones it
261
+ touches, and the box opened up. The page already lists the
262
+ aggregates below, so it opens on the level the list cannot
263
+ give — who is on the other end. */}
264
+ <SectionTitle
265
+ right={
266
+ <LevelSwitch
267
+ level={level}
268
+ onLevel={setLevel}
269
+ levels={[
270
+ { level: 2, label: "neighbours" },
271
+ { level: 3, label: "inside" },
272
+ ]}
273
+ />
274
+ }
275
+ >
276
+ Model
277
+ </SectionTitle>
278
+ <C4View
279
+ viewId={
280
+ level === 3
281
+ ? serviceInsideViewId(service)
282
+ : serviceViewId(service)
283
+ }
284
+ height={300}
285
+ />
286
+ <div className="mt-section" />
287
+ <Markdown mermaid>{service.readme}</Markdown>
288
+ {/* What to type to build, test and run it. Under the README
289
+ rather than in a tab of its own: the list is short, it is the
290
+ first thing a reader new to the checkout wants, and the README
291
+ above is where it would have been written by hand. Absent
292
+ entirely when no runner file declares one - a heading over an
293
+ empty list would be a claim that the service cannot be built,
294
+ and nobody made it. */}
295
+ {commandsOf(service).length > 0 ? (
296
+ <section
297
+ id={SERVICE_ANCHOR.commands}
298
+ className="mt-section max-w-table"
299
+ >
300
+ <SectionTitle anchor={SERVICE_ANCHOR.commands}>
301
+ Commands
302
+ </SectionTitle>
303
+ <CommandRows
304
+ commands={commandsOf(service)}
305
+ service={service}
306
+ />
307
+ </section>
308
+ ) : null}
309
+ {showDomain ? (
310
+ <section
311
+ id={SERVICE_ANCHOR.aggregates}
312
+ className="mt-section max-w-table"
313
+ >
314
+ <SectionTitle anchor={SERVICE_ANCHOR.aggregates}>
315
+ Aggregates
316
+ </SectionTitle>
317
+ {/* icon, slug, name, event count, actions - one column each,
318
+ so the counts stack up instead of drifting with the name. */}
319
+ <div
320
+ className="rows grid-cols-[auto_auto_1fr_auto_auto]"
321
+ data-nav-list
322
+ >
323
+ {service.aggregates.map((aggregate) => {
324
+ const to = paths.aggregate(
325
+ context.id,
326
+ service.slug,
327
+ aggregate.slug,
328
+ );
329
+ return (
330
+ <div key={aggregate.id} className="row px-2 py-1.5">
331
+ <KindIcon kind="aggregate" />
332
+ <Link
333
+ to={to}
334
+ data-nav-item
335
+ className="mono rounded-control"
336
+ >
337
+ {aggregate.slug}
338
+ </Link>
339
+ <span className="meta">{aggregate.name}</span>
340
+ <Link
341
+ to={`${to}#bb-events`}
342
+ className="mono rounded-control hover:underline"
343
+ style={{
344
+ color:
345
+ aggregate.events.length === 0
346
+ ? "var(--status-declared)"
347
+ : "var(--fg-muted)",
348
+ }}
349
+ >
350
+ <span className="tnum">
351
+ {aggregate.events.length}
352
+ </span>{" "}
353
+ events
354
+ </Link>
355
+ <RowActions
356
+ copy={aggregate.id}
357
+ reveal={aggregate.id}
358
+ label={aggregate.name}
359
+ />
360
+ </div>
361
+ );
362
+ })}
363
+ </div>
364
+ </section>
365
+ ) : null}
366
+
367
+ <section
368
+ id={SERVICE_ANCHOR.events}
369
+ className="mt-section max-w-table"
370
+ >
371
+ <SectionTitle
372
+ anchor={SERVICE_ANCHOR.events}
373
+ right={<span>everything this service announces</span>}
374
+ >
375
+ Events
376
+ </SectionTitle>
377
+ {events.length === 0 ? (
378
+ <Empty>this service announces nothing — it only answers</Empty>
379
+ ) : (
380
+ <div
381
+ className="rows grid-cols-[auto_auto_1fr_auto_auto]"
382
+ data-nav-list
383
+ >
384
+ {events.map(({ aggregate, event }) => {
385
+ const to = paths.event(
386
+ context.id,
387
+ service.slug,
388
+ aggregate.slug,
389
+ event.slug,
390
+ );
391
+ return (
392
+ <div key={event.id} className="row gap-2">
393
+ <KindIcon kind="event" />
394
+ <Link
395
+ to={to}
396
+ data-nav-item
397
+ className="mono rounded-control"
398
+ style={{ color: "var(--kind-event)" }}
399
+ >
400
+ {event.name}
401
+ </Link>
402
+ <span className="mono text-muted">
403
+ {aggregate.slug}
404
+ </span>
405
+ <Link
406
+ to={`${to}#${EVENT_ANCHOR.consumers}`}
407
+ className="mono rounded-control text-muted hover:text-ink"
408
+ >
409
+ <span className="tnum">{event.consumers.length}</span>{" "}
410
+ {event.consumers.length === 1
411
+ ? "consumer"
412
+ : "consumers"}
413
+ </Link>
414
+ <RowActions
415
+ copy={event.id}
416
+ reveal={event.id}
417
+ label={event.name}
418
+ />
419
+ </div>
420
+ );
421
+ })}
422
+ </div>
423
+ )}
424
+ </section>
425
+
426
+ {/* Who calls this service is nowhere else on the page: `provides`
427
+ and `consumes` both point outwards. Flows and decisions have
428
+ tabs of their own, so they are counted here, not repeated. */}
429
+ <WhatLinksHere
430
+ target={{ kind: "service", id: service.id }}
431
+ elsewhere={{
432
+ flow: { href: "?tab=flows", label: "the flows tab" },
433
+ adr: { href: "?tab=decisions", label: "the decisions tab" },
434
+ }}
435
+ empty="nothing in the catalog calls this service or listens to it"
436
+ />
437
+ </>
438
+ </TabPanel>
439
+
440
+ <TabPanel>
441
+ <div className="flex flex-col gap-4">
442
+ {service.provides.length === 0 ? (
443
+ <Empty>this service answers nothing — it only listens</Empty>
444
+ ) : null}
445
+ {service.provides.map((provided) => {
446
+ const module = provided.module
447
+ ? index.moduleById.get(provided.module)
448
+ : undefined;
449
+
450
+ return (
451
+ <div
452
+ key={provided.id}
453
+ className="rounded-card border border-line"
454
+ >
455
+ <div className="mono flex flex-wrap items-center gap-x-3 border-b px-3 py-1.5 border-line bg-surface">
456
+ <Ident value={provided.id} className="text-ink" />
457
+ {/* The schema this interface was declared in. A link
458
+ rather than a chip that only names it: the module page
459
+ is where "who else reads this" is answered. */}
460
+ {module ? (
461
+ <Link
462
+ to={paths.module(module.slug)}
463
+ className="chip hover:text-ink"
464
+ title={module.id}
465
+ >
466
+ <KindIcon kind="module" />
467
+ {module.name}
468
+ </Link>
469
+ ) : null}
470
+ <Ident
471
+ value={docPathOf(provided.source)}
472
+ className="ml-auto"
473
+ title={provided.source}
474
+ />
475
+ </div>
476
+ {/* Drawn by the same component the module page uses, so a
477
+ method reads identically in both places. */}
478
+ <MethodRows
479
+ provided={provided}
480
+ open={openShapes}
481
+ onToggle={toggleShape}
482
+ runs={(method) => {
483
+ // What the endpoint actually runs. More than one is
484
+ // normal: a handler that resolves a token before changing
485
+ // a password has run two use cases, in two aggregates.
486
+ const runs = operationsExposedBy(service, method.name);
487
+ if (runs.length === 0) return null;
488
+
489
+ return (
490
+ <p className="mono mt-0.5 flex flex-wrap items-center gap-x-2 text-muted">
491
+ <span>runs</span>
492
+ {runs.map(({ aggregate, operation }) => {
493
+ const to = aggregatePath(aggregate.id);
494
+ const label = `${aggregate.slug}.${operation.id}`;
495
+
496
+ return to ? (
497
+ <Link
498
+ key={label}
499
+ to={to}
500
+ className="rounded-control hover:text-ink"
501
+ title={`${operation.kind} of ${aggregate.name}`}
502
+ >
503
+ {label}
504
+ </Link>
505
+ ) : (
506
+ <span key={label}>{label}</span>
507
+ );
508
+ })}
509
+ </p>
510
+ );
511
+ }}
512
+ />
513
+ <MessageList
514
+ provided={provided}
515
+ open={openShapes}
516
+ onToggle={toggleShape}
517
+ />
518
+ </div>
519
+ );
520
+ })}
521
+ </div>
522
+ </TabPanel>
523
+
524
+ <TabPanel>
525
+ {spec === null ? (
526
+ <Empty>
527
+ no api document in this catalog — nothing under this service names
528
+ one
529
+ </Empty>
530
+ ) : spec.kind === "openapi" ? (
531
+ <ApiReference source={spec.source} />
532
+ ) : spec.kind === "graphql" ? (
533
+ <SchemaDocument source={spec.source} />
534
+ ) : spec.kind === "wsdl" ? (
535
+ <WsdlReference source={spec.source} provided={service.provides} />
536
+ ) : spec.kind === "inferred-http" ? (
537
+ <div className="flex flex-col gap-4">
538
+ <div className="rounded-card border border-line bg-surface px-4 py-3">
539
+ <p className="font-medium text-ink">HTTP contract inferred from Django</p>
540
+ <p className="mt-1 text-muted">
541
+ No OpenAPI document is checked into this repository. These routes were read
542
+ statically from Django URLConf and DRF views; request and response schemas stay
543
+ unknown until the generated Swagger document is exported into the repository.
544
+ </p>
545
+ </div>
546
+ {service.provides
547
+ .filter((provided) => provided.methods.some((method) => method.http))
548
+ .map((provided) => (
549
+ <div key={provided.id} className="rounded-card border border-line">
550
+ <div className="mono flex flex-wrap items-center gap-x-3 border-b border-line bg-surface px-3 py-1.5">
551
+ <Ident value={provided.id} className="text-ink" />
552
+ <Ident value={docPathOf(provided.source)} className="ml-auto" title={provided.source} />
553
+ </div>
554
+ <MethodRows provided={provided} open={openShapes} onToggle={toggleShape} />
555
+ </div>
556
+ ))}
557
+ </div>
558
+ ) : (
559
+ /* A proto is drawn from the catalog rather than as raw text. The
560
+ rule ApiReference states - draw the document because the catalog
561
+ cannot carry its shape - does not hold here: the extractor read
562
+ exactly that shape, and a <pre> could not link a field to the
563
+ shared type it refs. */
564
+ <ModuleSpec moduleId={spec.moduleId} />
565
+ )}
566
+ </TabPanel>
567
+
568
+ <TabPanel>
569
+ <Integrations groups={integrations} />
570
+ </TabPanel>
571
+
572
+ <TabPanel>
573
+ {channels.length === 0 ? (
574
+ <Empty>
575
+ nothing says what this service puts on the bus — channels are read
576
+ from an AsyncAPI document, and none was found for this repository
577
+ </Empty>
578
+ ) : (
579
+ <ChannelRows channels={channels} service={service.id} />
580
+ )}
581
+
582
+ {busDoc ? (
583
+ <>
584
+ <div className="mt-section" />
585
+ <SectionTitle>Document</SectionTitle>
586
+ <AsyncApiReference source={busDoc} />
587
+ </>
588
+ ) : null}
589
+ </TabPanel>
590
+
591
+ <TabPanel>
592
+ {stores.length === 0 ? (
593
+ <Empty>
594
+ nothing says where this service keeps its state — no database
595
+ schema or statically readable ORM configuration was found for this repository
596
+ </Empty>
597
+ ) : null}
598
+
599
+ {stores.some((s) => s.access === "reads") ? (
600
+ <button
601
+ type="button"
602
+ onClick={() => setShowReadOnly((v) => !v)}
603
+ aria-pressed={showReadOnly}
604
+ className="mono mb-4 rounded-control border border-dashed px-2 py-1 border-line-strong text-muted hover:bg-surface"
605
+ >
606
+ {showReadOnly ? "hide" : "show"}{" "}
607
+ {stores.filter((s) => s.access === "reads").length} read-only{" "}
608
+ {plural(
609
+ stores.filter((s) => s.access === "reads").length,
610
+ "store",
611
+ )}
612
+ </button>
613
+ ) : null}
614
+
615
+ <div className="flex flex-col gap-8">
616
+ {stores
617
+ .filter((s) => showReadOnly || s.access === "owns")
618
+ .map(({ store, access }) => (
619
+ <section key={store.id}>
620
+ <StoreHeader store={store} access={access} />
621
+ <div className="mt-3">
622
+ {(store.keyspaces ?? []).length > 0 ? (
623
+ <RedisSchema store={store} />
624
+ ) : store.tables.length === 0 ? (
625
+ /* A store whose schema nobody has read is still a fact
626
+ worth drawing — a cache IS part of the picture — but an
627
+ empty canvas would say the opposite. */
628
+ <Empty>
629
+ no schema extracted — {store.name} is in the catalog by
630
+ kind and owner only
631
+ </Empty>
632
+ ) : (
633
+ <ErCanvas store={store} ghost={access === "reads"} />
634
+ )}
635
+ </div>
636
+ </section>
637
+ ))}
638
+ </div>
639
+ </TabPanel>
640
+
641
+ <TabPanel>
642
+ <div className="flex flex-col gap-1" data-nav-list>
643
+ {flows.length === 0 ? (
644
+ <Empty>no chart runs through here yet</Empty>
645
+ ) : null}
646
+ {flows.map(
647
+ ({
648
+ flow,
649
+ role,
650
+ trigger,
651
+ firstStepId,
652
+ steps,
653
+ publishes,
654
+ consumes,
655
+ }) => {
656
+ // What this service does in the flow, in one phrase, and the
657
+ // counts behind it. The link lands on the first step the
658
+ // service is on rather than the top of the rail.
659
+ const does =
660
+ role === "initiates"
661
+ ? `answers ${trigger ?? "the call in"}`
662
+ : role === "reacts"
663
+ ? `reacts to ${trigger ?? "an event"}`
664
+ : "on the way";
665
+ return (
666
+ <Link
667
+ key={flow.slug}
668
+ to={
669
+ firstStepId
670
+ ? paths.flowStep(flow.slug, firstStepId)
671
+ : paths.flow(flow.slug)
672
+ }
673
+ data-nav-item
674
+ className="row flex-wrap items-baseline gap-x-3"
675
+ >
676
+ <span className="font-semibold">{flow.name}</span>
677
+ <span className="mono text-muted">{flow.slug}</span>
678
+ <span className="mono text-muted">{does}</span>
679
+ <span className="mono ml-auto flex shrink-0 items-center gap-3 text-muted">
680
+ {publishes.length > 0 ? (
681
+ <span title={`publishes ${publishes.join(", ")}`}>
682
+ ↑ <span className="tnum">{publishes.length}</span>
683
+ </span>
684
+ ) : null}
685
+ {consumes.length > 0 ? (
686
+ <span title={`consumes ${consumes.join(", ")}`}>
687
+ ↓ <span className="tnum">{consumes.length}</span>
688
+ </span>
689
+ ) : null}
690
+ <span title="steps this service is on">
691
+ <span className="tnum">{steps}</span> step
692
+ {steps === 1 ? "" : "s"}
693
+ </span>
694
+ <StatusChip status={flowHealth(flow)} />
695
+ </span>
696
+ </Link>
697
+ );
698
+ },
699
+ )}
700
+ </div>
701
+ </TabPanel>
702
+
703
+ <TabPanel>
704
+ {adrs.length === 0 ? (
705
+ <Empty>nothing on the record names this service</Empty>
706
+ ) : (
707
+ /* number, title, status, scope, date - one column each, shared by
708
+ the rows in force and the retired ones below them, so a chip
709
+ sits under the chip above it rather than wherever the title
710
+ before it happened to end. */
711
+ <div
712
+ className={`rows max-w-table ${ADR_ROW_COLUMNS}`}
713
+ data-nav-list
714
+ >
715
+ {current.map((adr) => (
716
+ <AdrRow key={adr.id} adr={adr} />
717
+ ))}
718
+ {retired.length > 0 ? (
719
+ <>
720
+ {/* A row of its own, so the button sits in the list's grid
721
+ without being a grid itself. */}
722
+ <div>
723
+ <button
724
+ type="button"
725
+ onClick={() => setShowRetired((v) => !v)}
726
+ aria-expanded={showRetired}
727
+ className="mono col-span-full mt-1 justify-self-start border border-dashed px-2 py-1 border-line-strong text-muted hover:bg-surface"
728
+ >
729
+ {showRetired ? "hide" : "show"} {retired.length}{" "}
730
+ {retired.every((a) => a.status === "superseded")
731
+ ? "superseded"
732
+ : "no longer in force"}
733
+ </button>
734
+ </div>
735
+ {showRetired
736
+ ? retired.map((adr) => <AdrRow key={adr.id} adr={adr} />)
737
+ : null}
738
+ </>
739
+ ) : null}
740
+ </div>
741
+ )}
742
+ </TabPanel>
743
+ </TabPanels>
744
+ </TabGroup>
745
+ );
746
+ }