@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,40 @@
1
+ // What the glossary calls the thing this page is about.
2
+ //
3
+ // One line, under the header, before the model: a reader who does not know
4
+ // what the word means in this context cannot read the page below it, and the
5
+ // sentence that says so is already written - in a file next to the code, by
6
+ // somebody who knows.
7
+ //
8
+ // It appears only where the glossary and the code spell the word the same
9
+ // way. That is most of the words worth knowing and nowhere near all of them,
10
+ // and the page says nothing at all when there is no pairing: a strip reading
11
+ // "no definition" on two thirds of the estate would be a lint nobody asked
12
+ // for, printed on every page.
13
+
14
+ import { Link } from "react-router";
15
+ import { catalog } from "../data";
16
+ import { paths } from "../routes";
17
+ import { bindTerms } from "../lib/terms";
18
+
19
+ // Built once, from the catalog the app ships, exactly as the palette builds
20
+ // its index: the pairing is a property of the estate, not of a render.
21
+ const BOUND = bindTerms(catalog);
22
+
23
+ export function InLanguage({ id }: { id: string }) {
24
+ const term = BOUND.byTarget.get(id);
25
+ if (!term) return null;
26
+
27
+ return (
28
+ <p className="mb-3 max-w-prose text-muted">
29
+ <Link
30
+ to={paths.term(term.id)}
31
+ className="rounded-control font-medium text-ink hover:underline"
32
+ title={`${term.id} — in the glossary of ${term.context}`}
33
+ >
34
+ {term.name}
35
+ </Link>
36
+ {" — "}
37
+ {term.definition}
38
+ </p>
39
+ );
40
+ }
@@ -0,0 +1,132 @@
1
+ // One entry of a glossary, as a card: the word, and what it means.
2
+ //
3
+ // The slug is not drawn. `Bus` and `bus` beside each other is the same word
4
+ // printed twice, and the id it would have carried is on the copy button that
5
+ // every row in this app has.
6
+
7
+ import { Fragment } from "react";
8
+ import { Link } from "react-router";
9
+ import type { Term } from "../catalog";
10
+ import { KindIcon } from "../components/kind";
11
+ import type { TermTarget } from "../lib/terms";
12
+ import { RowActions } from "../components/RowActions";
13
+ import { ContextPill } from "../components/primitives";
14
+ import { ctxStyle } from "../lib/context-color";
15
+ import { sourceHref } from "../lib/source-link";
16
+ import { staggerStyle } from "../lib/motion";
17
+
18
+ /**
19
+ * Backticks, and nothing else.
20
+ *
21
+ * A glossary is prose with the occasional identifier in it - `logout`, the
22
+ * name of a service - and every other mark the format allows is already gone
23
+ * by the time a term reaches the catalog. So this is a split on backticks and
24
+ * not a markdown renderer: one react-markdown per card, thirty-eight of them
25
+ * on a page, to italicise nothing.
26
+ */
27
+ function inline(text: string) {
28
+ return text.split("`").map((part, i) =>
29
+ i % 2 === 1 ? (
30
+ <code key={i} className="mono">
31
+ {part}
32
+ </code>
33
+ ) : (
34
+ <Fragment key={i}>{part}</Fragment>
35
+ ),
36
+ );
37
+ }
38
+
39
+ export function TermCard({
40
+ term,
41
+ targets = [],
42
+ index = 0,
43
+ showContext = false,
44
+ }: {
45
+ term: Term;
46
+ /**
47
+ * What the word names in the model, when the two spell it the same way.
48
+ * Empty is the ordinary case for the words worth defining - Bus, Refusal,
49
+ * Conflict name no type - so the row simply does not appear, and nothing on
50
+ * the card says anything is missing.
51
+ */
52
+ targets?: readonly TermTarget[];
53
+ /** Position in the list, for the staggered entrance. */
54
+ index?: number;
55
+ /**
56
+ * Names the context on the card. Off inside a vocabulary, whose heading has
57
+ * already said it; on where cards from several contexts stand together, and
58
+ * there the context is the only thing telling two of them apart.
59
+ */
60
+ showContext?: boolean;
61
+ }) {
62
+ const href = sourceHref(term.source, null);
63
+
64
+ return (
65
+ <div
66
+ id={`term-${term.id}`}
67
+ /* A column, so the row of cards can be one height and the line under
68
+ the definition still sits on the floor of each of them. Stretched to
69
+ its neighbours and left alone, a short entry grows a pocket of empty
70
+ card under its last sentence. */
71
+ className="card card-static stagger-in flex flex-col scroll-mt-4"
72
+ style={{ ...staggerStyle(index), ...ctxStyle(term.context), borderLeftWidth: 3 }}
73
+ >
74
+ <div className="flex items-baseline gap-2">
75
+ <h3 className="font-semibold">{term.name}</h3>
76
+ {showContext ? (
77
+ <span className="ml-auto shrink-0">
78
+ <ContextPill id={term.context} />
79
+ </span>
80
+ ) : null}
81
+ </div>
82
+
83
+ <p className="mt-1.5">{inline(term.definition)}</p>
84
+
85
+ {targets.length > 0 ? (
86
+ <div className="mt-2 flex flex-wrap items-center gap-1.5">
87
+ {targets.map((target) => (
88
+ <Link
89
+ key={target.id}
90
+ to={target.path}
91
+ className="chip hover:border-line-strong hover:text-ink"
92
+ title={`${target.kind} ${target.id}`}
93
+ >
94
+ {/* A state is not one of the app's kinds - it has no page and no
95
+ mark of its own - so it wears a dot rather than borrowing an
96
+ icon that would say it was something it is not. */}
97
+ {target.kind === "state" ? (
98
+ <span aria-hidden className="dot" />
99
+ ) : (
100
+ <KindIcon kind={target.kind} />
101
+ )}
102
+ {target.name}
103
+ </Link>
104
+ ))}
105
+ </div>
106
+ ) : null}
107
+
108
+ {/* Where the entry was written, and the button that copies its id, on one
109
+ line under the definition. The copy sat in the heading before, which
110
+ put a button between the word and the context that means it - two
111
+ things a reader compares across cards, and a control that appears
112
+ between them on hover is a control that moves them apart. */}
113
+ <div className="mt-auto flex items-baseline gap-2 pt-2 t-micro">
114
+ {href ? (
115
+ <a
116
+ href={href}
117
+ target="_blank"
118
+ rel="noreferrer"
119
+ className="mono text-faint hover:text-accent"
120
+ >
121
+ {term.source}
122
+ </a>
123
+ ) : (
124
+ /* Not a link, but still the answer to "where do I change this?" -
125
+ the same way every unlinkable path in this app stays text. */
126
+ <span className="mono text-faint">{term.source}</span>
127
+ )}
128
+ <RowActions copy={term.id} label={term.name} />
129
+ </div>
130
+ </div>
131
+ );
132
+ }
@@ -0,0 +1,114 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { catalog } from "../testing/estate";
3
+ import type { Catalog, Term } from "../catalog";
4
+ import { homonyms, matchTerms, vocabularies } from "./cards";
5
+
6
+ const term = (id: string, context: string, slug: string, name = slug): Term => ({
7
+ id,
8
+ slug,
9
+ context,
10
+ name,
11
+ definition: "one sentence.",
12
+ source: "GLOSSARY.md:1",
13
+ });
14
+
15
+ /** A catalog with only what these functions read. */
16
+ const of = (terms: Term[], contexts: string[] = ["auth", "shop"]): Catalog =>
17
+ ({
18
+ contexts: contexts.map((id) => ({ id, slug: id, name: id, services: [] })),
19
+ terms,
20
+ }) as unknown as Catalog;
21
+
22
+ describe("vocabularies", () => {
23
+ it("groups by context, in catalog order, alphabetical inside", () => {
24
+ const arranged = vocabularies(
25
+ of([
26
+ term("shop.order", "shop", "order", "Order"),
27
+ term("auth.session", "auth", "session", "Session"),
28
+ term("auth.attempt", "auth", "attempt", "Attempt"),
29
+ ]),
30
+ );
31
+
32
+ expect(arranged.map((v) => v.contextId)).toEqual(["auth", "shop"]);
33
+ expect(arranged[0]?.terms.map((t) => t.name)).toEqual([
34
+ "Attempt",
35
+ "Session",
36
+ ]);
37
+ });
38
+
39
+ it("leaves out a context that has written no glossary", () => {
40
+ const arranged = vocabularies(of([term("auth.session", "auth", "session")]));
41
+
42
+ expect(arranged).toHaveLength(1);
43
+ });
44
+
45
+ // The validator refuses this catalog, so nothing should ever render it. The
46
+ // arrangement still shows the terms rather than dropping them: a list that
47
+ // silently loses rows is the harder bug of the two.
48
+ it("still shows terms whose context the catalog does not declare", () => {
49
+ const arranged = vocabularies(of([term("oms.order", "oms", "order")]));
50
+
51
+ expect(arranged).toHaveLength(1);
52
+ expect(arranged[0]?.context).toBeNull();
53
+ expect(arranged[0]?.contextId).toBe("oms");
54
+ });
55
+ });
56
+
57
+ describe("homonyms", () => {
58
+ it("finds one word meant in two contexts", () => {
59
+ const found = homonyms(
60
+ of([
61
+ term("auth.bus", "auth", "bus", "Bus"),
62
+ term("shop.bus", "shop", "bus", "Bus"),
63
+ term("auth.session", "auth", "session", "Session"),
64
+ ]),
65
+ );
66
+
67
+ expect(found).toHaveLength(1);
68
+ expect(found[0]?.word).toBe("Bus");
69
+ expect(found[0]?.terms.map((t) => t.context)).toEqual(["auth", "shop"]);
70
+ });
71
+
72
+ it("says nothing about a word only one context uses", () => {
73
+ expect(homonyms(of([term("auth.session", "auth", "session")]))).toEqual([]);
74
+ });
75
+
76
+ // What the estate actually has, and the reason the section exists: two
77
+ // services describe the same piece of plumbing in their own words.
78
+ it("finds the words the example estate means twice", () => {
79
+ expect(homonyms(catalog).map((h) => h.slug)).toEqual(["bus", "outbox"]);
80
+ });
81
+ });
82
+
83
+ describe("matchTerms", () => {
84
+ const terms = [
85
+ {
86
+ ...term("auth.session", "auth", "session", "Session"),
87
+ definition:
88
+ "Proof that a user logged in, and the token it is presented with.",
89
+ },
90
+ {
91
+ ...term("auth.lockout", "auth", "lockout", "Lockout"),
92
+ definition: "An account refusing passwords for a while.",
93
+ },
94
+ ];
95
+
96
+ it("matches on the name", () => {
97
+ expect(matchTerms(terms, "lock").map((t) => t.name)).toEqual(["Lockout"]);
98
+ });
99
+
100
+ it("matches on the definition, not only the word", () => {
101
+ expect(matchTerms(terms, "token").map((t) => t.name)).toEqual(["Session"]);
102
+ });
103
+
104
+ it("takes every word, in any order", () => {
105
+ expect(matchTerms(terms, "passwords account").map((t) => t.name)).toEqual([
106
+ "Lockout",
107
+ ]);
108
+ expect(matchTerms(terms, "passwords token")).toEqual([]);
109
+ });
110
+
111
+ it("answers an empty query with everything", () => {
112
+ expect(matchTerms(terms, " ")).toHaveLength(2);
113
+ });
114
+ });
@@ -0,0 +1,111 @@
1
+ // The glossary, arranged for reading. Pure: a catalog in, plain values out,
2
+ // so what the page shows can be asserted in a test rather than eyeballed.
3
+ //
4
+ // Two arrangements, and the second is the one that earns the page. A context's
5
+ // vocabulary is a list anyone could have written by hand; the words that mean
6
+ // two things in two contexts are only visible once every glossary in the
7
+ // estate is in one place, and they are exactly what a reader crossing a
8
+ // boundary gets wrong.
9
+
10
+ import type { BoundedContext, Catalog, Term } from "../catalog";
11
+ import { allTerms } from "../catalog";
12
+
13
+ /** One context's words, alphabetical, however the sources were ordered. */
14
+ export interface Vocabulary {
15
+ contextId: string;
16
+ /** The context itself, when the catalog declares one. */
17
+ context: BoundedContext | null;
18
+ terms: Term[];
19
+ }
20
+
21
+ function byName(a: Term, b: Term): number {
22
+ return a.name.localeCompare(b.name, undefined, { sensitivity: "base" });
23
+ }
24
+
25
+ /**
26
+ * Every vocabulary, in catalog order - which is the order the contexts are
27
+ * drawn in everywhere else, so the page reads in the same direction as the
28
+ * sidebar and the map.
29
+ */
30
+ export function vocabularies(catalog: Catalog): Vocabulary[] {
31
+ const byContext = new Map<string, Term[]>();
32
+ for (const term of allTerms(catalog)) {
33
+ const list = byContext.get(term.context) ?? [];
34
+ list.push(term);
35
+ byContext.set(term.context, list);
36
+ }
37
+
38
+ const out: Vocabulary[] = [];
39
+ for (const context of catalog.contexts) {
40
+ const terms = byContext.get(context.id);
41
+ if (!terms) continue;
42
+ out.push({ contextId: context.id, context, terms: [...terms].sort(byName) });
43
+ byContext.delete(context.id);
44
+ }
45
+ // A glossary whose context is not in the catalog cannot happen - the
46
+ // validator refuses it - but the arrangement does not depend on that being
47
+ // true, because a list that silently drops rows is worse than one that
48
+ // shows them somewhere plain.
49
+ for (const [contextId, terms] of byContext) {
50
+ out.push({ contextId, context: null, terms: [...terms].sort(byName) });
51
+ }
52
+
53
+ return out;
54
+ }
55
+
56
+ /** One word, and the contexts that mean different things by it. */
57
+ export interface Homonym {
58
+ /** The shared slug - what makes two spellings one word. */
59
+ slug: string;
60
+ /** As the glossaries spell it, taking the first if they disagree in case. */
61
+ word: string;
62
+ terms: Term[];
63
+ }
64
+
65
+ /**
66
+ * The words that appear in more than one context.
67
+ *
68
+ * Not a problem to fix. `User` in auth is an id, an email and a hash; `User`
69
+ * in shop is a name and an address - two entries is the correct answer, and
70
+ * the layout rules say so. What is worth seeing is that a reader crossing the
71
+ * boundary is now holding two meanings of one word, which no single glossary
72
+ * can tell them.
73
+ */
74
+ export function homonyms(catalog: Catalog): Homonym[] {
75
+ const bySlug = new Map<string, Term[]>();
76
+ for (const term of allTerms(catalog)) {
77
+ const list = bySlug.get(term.slug) ?? [];
78
+ list.push(term);
79
+ bySlug.set(term.slug, list);
80
+ }
81
+
82
+ const out: Homonym[] = [];
83
+ for (const [slug, terms] of bySlug) {
84
+ // Two entries of one context cannot happen either - the merge reports it -
85
+ // so counting contexts rather than terms is the honest test of "the same
86
+ // word, twice".
87
+ if (new Set(terms.map((t) => t.context)).size < 2) continue;
88
+ out.push({
89
+ slug,
90
+ word: terms[0]?.name ?? slug,
91
+ terms: [...terms].sort((a, b) => a.context.localeCompare(b.context)),
92
+ });
93
+ }
94
+
95
+ return out.sort((a, b) => a.word.localeCompare(b.word));
96
+ }
97
+
98
+ /**
99
+ * The filter box: every word of the query has to appear somewhere in the
100
+ * entry, in any order.
101
+ */
102
+ export function matchTerms(terms: Term[], query: string): Term[] {
103
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
104
+ if (words.length === 0) return terms;
105
+
106
+ return terms.filter((term) => {
107
+ const haystack =
108
+ `${term.name} ${term.slug} ${term.definition}`.toLowerCase();
109
+ return words.every((word) => haystack.includes(word));
110
+ });
111
+ }
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ ADMONITION_KINDS,
4
+ ADMONITION_LABEL,
5
+ parseAdmonition,
6
+ } from "./admonition";
7
+
8
+ describe("GitHub admonitions", () => {
9
+ it("labels every kind it claims to know", () => {
10
+ for (const kind of ADMONITION_KINDS) {
11
+ expect(ADMONITION_LABEL[kind], kind).toBeTruthy();
12
+ }
13
+ });
14
+
15
+ it("reads the marker and hands back the body", () => {
16
+ for (const kind of ADMONITION_KINDS) {
17
+ const marker = `[!${kind.toUpperCase()}]`;
18
+ expect(parseAdmonition(`${marker}\nMind this.`)).toEqual({
19
+ kind,
20
+ rest: "Mind this.",
21
+ });
22
+ }
23
+ });
24
+
25
+ it("accepts a marker that stands alone", () => {
26
+ expect(parseAdmonition("[!NOTE]")).toEqual({ kind: "note", rest: "" });
27
+ expect(parseAdmonition("[!NOTE]\n")).toEqual({ kind: "note", rest: "" });
28
+ });
29
+
30
+ it("tolerates the case and the trailing spaces a writer leaves behind", () => {
31
+ expect(parseAdmonition("[!Warning] \nCareful.")).toEqual({
32
+ kind: "warning",
33
+ rest: "Careful.",
34
+ });
35
+ });
36
+
37
+ it("keeps the rest of a multi-line body intact", () => {
38
+ expect(parseAdmonition("[!TIP]\nfirst\nsecond")?.rest).toBe(
39
+ "first\nsecond",
40
+ );
41
+ });
42
+
43
+ it("refuses anything that is not a bare marker line", () => {
44
+ expect(parseAdmonition("[!NOTE] on the same line")).toBeNull();
45
+ expect(parseAdmonition("[!HINT]\nnot a GitHub kind")).toBeNull();
46
+ expect(parseAdmonition("A quotation.")).toBeNull();
47
+ expect(parseAdmonition("text before\n[!NOTE]\nafter")).toBeNull();
48
+ });
49
+ });
@@ -0,0 +1,48 @@
1
+ // GitHub's alert syntax: a blockquote whose first line is a bare `[!NOTE]`
2
+ // marker. It turns up in any README written on GitHub, so Portolan renders the
3
+ // five kinds GitHub knows and leaves everything else a plain quotation - the
4
+ // same rule GitHub itself applies to `[!HINT]`.
5
+
6
+ export const ADMONITION_KINDS = [
7
+ "note",
8
+ "tip",
9
+ "important",
10
+ "warning",
11
+ "caution",
12
+ ] as const;
13
+
14
+ export type AdmonitionKind = (typeof ADMONITION_KINDS)[number];
15
+
16
+ export const ADMONITION_LABEL: Record<AdmonitionKind, string> = {
17
+ note: "Note",
18
+ tip: "Tip",
19
+ important: "Important",
20
+ warning: "Warning",
21
+ caution: "Caution",
22
+ };
23
+
24
+ /**
25
+ * The marker owns its whole line: `[!NOTE] see below` is prose that happens to
26
+ * start with brackets, not an alert. Matching is case-insensitive, as it is in
27
+ * cmark-gfm, but the label always comes back canonical.
28
+ */
29
+ const MARKER = /^\[!([a-z]+)\][^\S\n]*(?:\r?\n|$)/i;
30
+
31
+ function isKind(word: string): word is AdmonitionKind {
32
+ return (ADMONITION_KINDS as readonly string[]).includes(word);
33
+ }
34
+
35
+ /**
36
+ * Reads the marker off the first text of a blockquote. `rest` is what is left
37
+ * of that text once the marker line is gone - usually the alert's first
38
+ * sentence, empty when the marker stands alone.
39
+ */
40
+ export function parseAdmonition(
41
+ text: string,
42
+ ): { kind: AdmonitionKind; rest: string } | null {
43
+ const match = MARKER.exec(text);
44
+ if (!match) return null;
45
+ const kind = match[1]?.toLowerCase() ?? "";
46
+ if (!isKind(kind)) return null;
47
+ return { kind, rest: text.slice(match[0].length) };
48
+ }
@@ -0,0 +1,131 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { describe, expect, it } from "vitest";
3
+ import { catalog } from "../testing/estate";
4
+ import {
5
+ adrCoversService,
6
+ adrNumber,
7
+ adrsForService,
8
+ isCurrent,
9
+ newestAccepted,
10
+ sortAdrs,
11
+ } from "./adr";
12
+ import type { Adr } from "../catalog";
13
+
14
+ function byId(id: string): Adr {
15
+ const adr = catalog.adrs.find((a) => a.id === id);
16
+ if (!adr) throw new Error(`no adr ${id}`);
17
+ return adr;
18
+ }
19
+
20
+ describe("adrNumber", () => {
21
+ it("pads to the four digits people write in commits", () => {
22
+ expect(adrNumber(byId("org.0001"))).toBe("ADR-0001");
23
+ expect(adrNumber(byId("shop.oms.0007"))).toBe("ADR-0007");
24
+ });
25
+ });
26
+
27
+ describe("sortAdrs", () => {
28
+ it("puts the newest decision first", () => {
29
+ expect(sortAdrs(catalog.adrs).map((a) => a.id)).toEqual([
30
+ "shop.oms.0007",
31
+ "payments.0004",
32
+ "shop.oms.0003",
33
+ "org.0002",
34
+ "org.0001",
35
+ ]);
36
+ });
37
+
38
+ it("does not mutate its input", () => {
39
+ const before = catalog.adrs.map((a) => a.id);
40
+ sortAdrs(catalog.adrs);
41
+ expect(catalog.adrs.map((a) => a.id)).toEqual(before);
42
+ });
43
+ });
44
+
45
+ describe("isCurrent", () => {
46
+ it("is false for records that no longer hold", () => {
47
+ expect(isCurrent(byId("shop.oms.0003"))).toBe(false);
48
+ expect(isCurrent(byId("shop.oms.0007"))).toBe(true);
49
+ expect(isCurrent(byId("payments.0004"))).toBe(true);
50
+ });
51
+ });
52
+
53
+ describe("adrCoversService", () => {
54
+ const org = byId("org.0002");
55
+ const context = byId("payments.0004");
56
+ const service = byId("shop.oms.0007");
57
+ const relates = byId("org.0001");
58
+
59
+ it("lets an org decision govern every service", () => {
60
+ expect(adrCoversService(org, "shop.pricing", "shop")).toBe(true);
61
+ expect(adrCoversService(org, "delivery.core", "delivery")).toBe(true);
62
+ });
63
+
64
+ it("scopes a context decision to that context only", () => {
65
+ expect(adrCoversService(context, "payments.ledger", "payments")).toBe(true);
66
+ expect(adrCoversService(context, "shop.oms", "shop")).toBe(false);
67
+ });
68
+
69
+ it("scopes a service decision to that service only", () => {
70
+ expect(adrCoversService(service, "shop.oms", "shop")).toBe(true);
71
+ expect(adrCoversService(service, "shop.pricing", "shop")).toBe(false);
72
+ });
73
+
74
+ it("also covers services named in relates", () => {
75
+ expect(relates.scope.kind).toBe("org");
76
+ const narrowed: Adr = {
77
+ ...relates,
78
+ scope: { kind: "context", context: "shop" },
79
+ };
80
+ expect(adrCoversService(narrowed, "delivery.core", "delivery")).toBe(true);
81
+ expect(adrCoversService(narrowed, "shop.pricing", "shop")).toBe(true);
82
+ expect(adrCoversService(narrowed, "payments.ledger", "payments")).toBe(
83
+ false,
84
+ );
85
+ });
86
+ });
87
+
88
+ describe("adrsForService", () => {
89
+ it("lists everything governing shop.oms, newest first", () => {
90
+ expect(
91
+ adrsForService(catalog, "shop.oms", "shop").map((a) => a.id),
92
+ ).toEqual(["shop.oms.0007", "shop.oms.0003", "org.0002", "org.0001"]);
93
+ });
94
+
95
+ it("gives shop.pricing only the org-wide decisions", () => {
96
+ expect(
97
+ adrsForService(catalog, "shop.pricing", "shop").map((a) => a.id),
98
+ ).toEqual(["org.0002", "org.0001"]);
99
+ });
100
+
101
+ it("gives payments.ledger its own context's decision too", () => {
102
+ expect(
103
+ adrsForService(catalog, "payments.ledger", "payments").map((a) => a.id),
104
+ ).toEqual(["payments.0004", "org.0002", "org.0001"]);
105
+ });
106
+ });
107
+
108
+ describe("newestAccepted", () => {
109
+ it("skips proposed and superseded records", () => {
110
+ expect(newestAccepted(catalog, 5).map((a) => a.id)).toEqual([
111
+ "shop.oms.0007",
112
+ "org.0002",
113
+ "org.0001",
114
+ ]);
115
+ });
116
+
117
+ it("honours the limit", () => {
118
+ expect(newestAccepted(catalog, 1).map((a) => a.id)).toEqual([
119
+ "shop.oms.0007",
120
+ ]);
121
+ });
122
+ });
123
+
124
+ describe("decision pages draw nothing from the current model", () => {
125
+ it("never reaches for a LikeC4 view", () => {
126
+ // A record is frozen history. Rendering a live view inside one would make
127
+ // it re-draw from a model that has moved on since the decision was taken.
128
+ const source = readFileSync("src/pages/AdrDetail.tsx", "utf8");
129
+ expect(source).not.toMatch(/LikeC4View|C4View|FlowView|likec4/);
130
+ });
131
+ });