@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,204 @@
1
+ package main
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+
7
+ "github.com/shortlink-org/portolan/catalog"
8
+ )
9
+
10
+ const sample = `# Order accepted
11
+ owner: shop
12
+ source: services/oms/test/integration/order_accepted_test.go
13
+
14
+ The narrow slice one integration test pins end to end: an order commits
15
+ with its outbox row.
16
+
17
+ Every hop here is asserted.
18
+
19
+ ## Participants
20
+ - oms-db: store in shop "oms-db (postgres)"
21
+ - psp-gateway: external "psp-gateway (external)"
22
+ - customer: actor
23
+
24
+ ## Steps
25
+ // the request
26
+ customer -> shop.oms: rpc PlaceOrder [verified] @internal/oms/http/orders.go:40
27
+ shop.oms -> oms-db: insertOrderAndOutboxRow [verified] @internal/oms/adapter/postgres/order_repo.go:141 #a1
28
+ > The order row and the OrderPlaced outbox row commit in one
29
+ > transaction.
30
+ shop.oms -> bus: event shop.oms.order.OrderPlaced [verified]
31
+ payments.ledger -> psp-gateway: rpc psp.v2.Charges/Create as "Charges.Create (auth only)" [unresolved] @internal/ledger/adapter/psp/client.go:64
32
+
33
+ alt score below 40 #alt-risk
34
+ bus -> payments.ledger: event shop.oms.order.OrderPlaced
35
+ else score at or above 40
36
+ shop.oms -> bus: event shop.oms.order.OrderCancelled
37
+ stop
38
+ else
39
+ end
40
+
41
+ par OrderPlaced fan-out #par-placed
42
+ bus -> payments.ledger: event shop.oms.order.OrderPlaced
43
+ and
44
+ bus -> delivery.core: event shop.oms.order.OrderPlaced
45
+ end
46
+
47
+ loop outbox relay, every 200 ms until the batch is empty
48
+ shop.oms -> oms-db: SELECT ... FOR UPDATE SKIP LOCKED
49
+ end
50
+ `
51
+
52
+ func parsed(t *testing.T, src string) catalog.Flow {
53
+ t.Helper()
54
+ flow, errs := parseFlow("data/flows/order-accepted.flow.md", src)
55
+ if len(errs) > 0 {
56
+ t.Fatalf("errors: %s", strings.Join(errs, "\n"))
57
+ }
58
+
59
+ return flow
60
+ }
61
+
62
+ func TestTheHeadIsReadIntoTheFlow(t *testing.T) {
63
+ flow := parsed(t, sample)
64
+ if flow.ID != "flow.order-accepted" || flow.Slug != "order-accepted" || flow.Name != "Order accepted" || flow.Owner != "shop" {
65
+ t.Errorf("flow = %+v", flow)
66
+ }
67
+ if flow.Source != "services/oms/test/integration/order_accepted_test.go" {
68
+ t.Errorf("source = %q", flow.Source)
69
+ }
70
+ want := "The narrow slice one integration test pins end to end: an order commits with its outbox row.\n\nEvery hop here is asserted."
71
+ if flow.Summary != want {
72
+ t.Errorf("summary = %q", flow.Summary)
73
+ }
74
+ }
75
+
76
+ // Declared lanes come first, in the order written; the ones a step brings in
77
+ // - services by their id, the bus by its name - follow in order of first use.
78
+ func TestLanesAreDeclaredOrInferred(t *testing.T) {
79
+ flow := parsed(t, sample)
80
+ var ids []string
81
+ for _, p := range flow.Participants {
82
+ ids = append(ids, p.ID+"/"+string(p.Kind))
83
+ }
84
+ want := "oms-db/store psp-gateway/external customer/actor shop.oms/service bus/broker payments.ledger/service delivery.core/service"
85
+ if strings.Join(ids, " ") != want {
86
+ t.Errorf("lanes = %s", strings.Join(ids, " "))
87
+ }
88
+ if store := flow.Participants[0]; store.Label != "oms-db (postgres)" || store.Context == nil || *store.Context != "shop" {
89
+ t.Errorf("store lane = %+v", store)
90
+ }
91
+ if oms := flow.Participants[3]; oms.Context == nil || *oms.Context != "shop" {
92
+ t.Errorf("service lane = %+v", oms)
93
+ }
94
+ }
95
+
96
+ func TestAHopCarriesEverythingWrittenAfterIt(t *testing.T) {
97
+ flow := parsed(t, sample)
98
+ first := flow.Steps[0].(*catalog.Step)
99
+ if first.ID != "s1" || first.Kind != catalog.StepRPC || first.Label != "PlaceOrder" || first.Ref != "" || first.Status != catalog.StatusVerified || first.Line != "internal/oms/http/orders.go:40" {
100
+ t.Errorf("first = %+v", first)
101
+ }
102
+ second := flow.Steps[1].(*catalog.Step)
103
+ if second.ID != "a1" || second.Kind != catalog.StepCall || second.Label != "insertOrderAndOutboxRow" {
104
+ t.Errorf("second = %+v", second)
105
+ }
106
+ if second.Note != "The order row and the OrderPlaced outbox row commit in one transaction." {
107
+ t.Errorf("note = %q", second.Note)
108
+ }
109
+ third := flow.Steps[2].(*catalog.Step)
110
+ if third.ID != "s2" || third.Ref != "shop.oms.order.OrderPlaced" || third.Label != "OrderPlaced" || third.From != "shop.oms" || third.To != "bus" {
111
+ t.Errorf("third = %+v; an explicit id does not use up a number", third)
112
+ }
113
+ fourth := flow.Steps[3].(*catalog.Step)
114
+ if fourth.Ref != "psp.v2.Charges/Create" || fourth.Label != "Charges.Create (auth only)" || fourth.Status != catalog.StatusUnresolved {
115
+ t.Errorf("fourth = %+v", fourth)
116
+ }
117
+ }
118
+
119
+ func TestFramesCloseWithEnd(t *testing.T) {
120
+ flow := parsed(t, sample)
121
+ alt, ok := flow.Steps[4].(*catalog.Alt)
122
+ if !ok || alt.ID != "alt-risk" || len(alt.Branches) != 3 {
123
+ t.Fatalf("alt = %+v", flow.Steps[4])
124
+ }
125
+ if alt.Branches[0].Title != "score below 40" || alt.Branches[0].Terminal || len(alt.Branches[0].Steps) != 1 {
126
+ t.Errorf("branch 0 = %+v", alt.Branches[0])
127
+ }
128
+ if alt.Branches[1].Title != "score at or above 40" || !alt.Branches[1].Terminal {
129
+ t.Errorf("branch 1 = %+v; `stop` makes it terminal", alt.Branches[1])
130
+ }
131
+ if alt.Branches[2].Title != "otherwise" || len(alt.Branches[2].Steps) != 0 {
132
+ t.Errorf("branch 2 = %+v; a bare else is otherwise, and empty is allowed", alt.Branches[2])
133
+ }
134
+
135
+ par, ok := flow.Steps[5].(*catalog.Parallel)
136
+ if !ok || par.ID != "par-placed" || par.Title != "OrderPlaced fan-out" || len(par.Branches) != 2 {
137
+ t.Fatalf("par = %+v", flow.Steps[5])
138
+ }
139
+ loop, ok := flow.Steps[6].(*catalog.Loop)
140
+ if !ok || loop.ID != "loop8" || !strings.HasPrefix(loop.Title, "outbox relay") || len(loop.Steps) != 1 {
141
+ t.Fatalf("loop = %+v", flow.Steps[6])
142
+ }
143
+ }
144
+
145
+ // An rpc with no interface behind it - a webhook arriving on a route - keeps
146
+ // the route as its label and has no ref to resolve.
147
+ func TestARouteIsALabelNotARef(t *testing.T) {
148
+ src := "# Webhook\nowner: payments\n\n## Participants\n- psp-gateway: external\n\n## Steps\npsp-gateway -> payments.ledger: rpc POST /webhooks/psp/v2 [verified]\npayments.ledger -> shop.oms: rpc shop.v1.Orders/GetOrder\n"
149
+ flow, errs := parseFlow("w.flow.md", src)
150
+ if len(errs) > 0 {
151
+ t.Fatal(errs)
152
+ }
153
+ first := flow.Steps[0].(*catalog.Step)
154
+ if first.Ref != "" || first.Label != "POST /webhooks/psp/v2" {
155
+ t.Errorf("first = %+v", first)
156
+ }
157
+ second := flow.Steps[1].(*catalog.Step)
158
+ if second.Ref != "shop.v1.Orders/GetOrder" || second.Label != "GetOrder" {
159
+ t.Errorf("second = %+v", second)
160
+ }
161
+ }
162
+
163
+ func TestTheFileIsTheSourceWhenNoneIsNamed(t *testing.T) {
164
+ src := "# Login\nowner: auth\n\n## Steps\nclient -> auth.auth: rpc login\n"
165
+ flow, errs := parseFlow("data/flows/login.flow.md", src)
166
+ if len(errs) > 0 {
167
+ t.Fatal(errs)
168
+ }
169
+ if flow.Source != "data/flows/login.flow.md" || flow.Slug != "login" {
170
+ t.Errorf("flow = %+v", flow)
171
+ }
172
+ if flow.Participants[0].Kind != catalog.ParticipantActor || flow.Participants[0].ID != "client" {
173
+ t.Errorf("client is an actor by name: %+v", flow.Participants)
174
+ }
175
+ }
176
+
177
+ // Every mistake is named with its line, and none of them is silent.
178
+ func TestMistakesAreNamedWithTheirLine(t *testing.T) {
179
+ cases := []struct{ src, want string }{
180
+ {"# X\nowner: a\n\n## Steps\na.b -> c.d: hop\nalt when\n a.b -> c.d: x\n", "alt is never closed"},
181
+ {"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\nend\n", "needs an `else`"},
182
+ {"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\n stop\nelse\n a.b -> c.d: y\n stop\nend\n", "every branch of this alt stops"},
183
+ {"# X\nowner: a\n\n## Steps\na.b -> mystery: hop\n", `"mystery" is not declared`},
184
+ {"# X\nowner: a\n\n## Steps\na.b -> c.d: hop [maybe]\n", "not one of verified"},
185
+ {"# X\nowner: a\n\n## Steps\na.b -> c.d: hop #s1\na.b -> c.d: hop #s1\n", "used twice"},
186
+ {"# X\nowner: a\n\n## Steps\n> orphan\n", "has to follow the step"},
187
+ {"# X\n\n## Steps\na.b -> c.d: hop\n", "no owner"},
188
+ {"# X\nowner: a\n\n## Steps\nloop\n a.b -> c.d: hop\nend\n", "loop needs a title"},
189
+ {"# X\nowner: a\n\n## Steps\nalt when\n a.b -> c.d: x\n stop\n a.b -> c.d: y\nelse\nend\n", "nothing runs after `stop`"},
190
+ {"# X\nowner: a\n\n## Steps\na.b -> c.d: hop [verified] wat\n", "cannot read"},
191
+ }
192
+ for _, c := range cases {
193
+ _, errs := parseFlow("f.flow.md", c.src)
194
+ if len(errs) == 0 {
195
+ t.Errorf("%q: no error, want %q", c.src, c.want)
196
+
197
+ continue
198
+ }
199
+ joined := strings.Join(errs, "\n")
200
+ if !strings.Contains(joined, c.want) || !strings.Contains(joined, "f.flow.md:") {
201
+ t.Errorf("%q: errors = %q, want %q with a line", c.src, joined, c.want)
202
+ }
203
+ }
204
+ }
@@ -0,0 +1,19 @@
1
+ package main
2
+
3
+ import (
4
+ _ "embed"
5
+
6
+ "github.com/shortlink-org/portolan/plugin"
7
+ )
8
+
9
+ //go:embed options.schema.json
10
+ var optionsSchema []byte
11
+
12
+ func descriptor() plugin.Descriptor {
13
+ return plugin.Descriptor{
14
+ Name: "extract-glossary",
15
+ Summary: "Reads a context's GLOSSARY.md - one paragraph per term, what it is and what it is not - into a catalog fragment.",
16
+ Phases: []string{plugin.PhaseExtract},
17
+ Options: optionsSchema,
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ package main
2
+
3
+ import (
4
+ "testing"
5
+
6
+ "github.com/shortlink-org/portolan/plugin/schematest"
7
+ )
8
+
9
+ func TestOptionsSchemaMatchesStruct(t *testing.T) {
10
+ schematest.Check(t, optionsSchema, Options{})
11
+ }
@@ -0,0 +1,115 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "errors"
6
+ "os"
7
+ "path/filepath"
8
+ "regexp"
9
+ "sort"
10
+ "strings"
11
+
12
+ "github.com/shortlink-org/portolan/catalog"
13
+ "github.com/shortlink-org/portolan/plugin"
14
+ )
15
+
16
+ var contextSlug = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
17
+
18
+ // extract reads every glossary the options name and answers with one fragment
19
+ // holding their terms.
20
+ //
21
+ // A file that cannot be parsed fails the whole run rather than being left out:
22
+ // a fragment silently missing half a vocabulary is a site where a word simply
23
+ // has no meaning, with nothing on the page to say a file was skipped and why.
24
+ // What is merely incomplete - an entry that never says what the term is not,
25
+ // a glossary that drifted out of alphabetical order - comes back as warnings,
26
+ // which the host prints and nobody has to act on today.
27
+ func extract(in plugin.Input, opts Options) (plugin.Response, error) {
28
+ b := &plugin.Builder{}
29
+
30
+ context := opts.Context
31
+ if context == "" {
32
+ context = filepath.Base(in.Root)
33
+ }
34
+ if !contextSlug.MatchString(context) {
35
+ return plugin.Response{}, errors.New(context + " is not a context slug: a term's id is the context and the term, so the context has to be one word the way the estate spells it")
36
+ }
37
+
38
+ patterns := opts.Files
39
+ if len(patterns) == 0 {
40
+ patterns = []string{"GLOSSARY.md"}
41
+ }
42
+ var files []string
43
+ seen := map[string]bool{}
44
+ for _, pattern := range patterns {
45
+ matches, err := filepath.Glob(filepath.Join(in.Root, pattern))
46
+ if err != nil {
47
+ return plugin.Response{}, err
48
+ }
49
+ for _, match := range matches {
50
+ if !seen[match] {
51
+ seen[match] = true
52
+ files = append(files, match)
53
+ }
54
+ }
55
+ }
56
+ sort.Strings(files)
57
+ if len(files) == 0 {
58
+ b.Warn(in.Root, "no glossary matched "+strings.Join(patterns, ", ")+"; the fragment holds no terms")
59
+ }
60
+
61
+ terms := []catalog.Term{}
62
+ ids := map[string]string{}
63
+ var problems []string
64
+ for _, file := range files {
65
+ src, err := os.ReadFile(file)
66
+ if err != nil {
67
+ return plugin.Response{}, err
68
+ }
69
+ rel := filepath.ToSlash(file)
70
+ parsed, warns, errs := parseGlossary(rel, context, string(src))
71
+ if len(errs) > 0 {
72
+ problems = append(problems, errs...)
73
+
74
+ continue
75
+ }
76
+ for _, warn := range warns {
77
+ b.Warn(rel, strings.TrimPrefix(warn, rel+": "))
78
+ }
79
+ for _, term := range parsed {
80
+ // Two files in one context defining one word is the failure the
81
+ // glossary exists to prevent, said in the glossaries themselves.
82
+ if other, taken := ids[term.ID]; taken {
83
+ problems = append(problems, term.Source+": "+term.Name+" is already defined in "+other)
84
+
85
+ continue
86
+ }
87
+ ids[term.ID] = term.Source
88
+ terms = append(terms, term)
89
+ }
90
+ }
91
+ if len(problems) > 0 {
92
+ return plugin.Response{}, errors.New(strings.Join(problems, "\n"))
93
+ }
94
+
95
+ fragment := catalog.Catalog{
96
+ GeneratedAt: in.GeneratedAt,
97
+ Commit: in.Commit,
98
+ Contexts: []catalog.BoundedContext{},
99
+ Defs: map[string]catalog.TypeDef{},
100
+ Flows: []catalog.Flow{},
101
+ Adrs: []catalog.Adr{},
102
+ Terms: terms,
103
+ }
104
+ encoded, err := json.MarshalIndent(fragment, "", " ")
105
+ if err != nil {
106
+ return plugin.Response{}, err
107
+ }
108
+ out := opts.Out
109
+ if out == "" {
110
+ out = "glossary.json"
111
+ }
112
+ b.File(out, string(encoded)+"\n")
113
+
114
+ return b.Response(), nil
115
+ }
@@ -0,0 +1,220 @@
1
+ package main
2
+
3
+ // The mapping: glossaries in a tree, a catalog fragment out.
4
+ //
5
+ // Shaped like plugins/extract-adr/extract_test.go, which is the house style
6
+ // for an extractor test - one behaviour per test, and a whole-fragment golden
7
+ // underneath so an unintended field change is a diff rather than an assertion
8
+ // nobody thought to write.
9
+
10
+ import (
11
+ "encoding/json"
12
+ "flag"
13
+ "os"
14
+ "strconv"
15
+ "strings"
16
+ "testing"
17
+
18
+ "github.com/shortlink-org/portolan/catalog"
19
+ "github.com/shortlink-org/portolan/plugin"
20
+ )
21
+
22
+ var update = flag.Bool("update", false, "rewrite the golden fragment instead of comparing against it")
23
+
24
+ const goldenPath = "testdata/golden/glossary.json"
25
+
26
+ func input(root string) plugin.Input {
27
+ // The stamp comes from the host, which derives it from git. A plugin that
28
+ // read a clock would write a different file every run.
29
+ return plugin.Input{
30
+ Root: root,
31
+ Commit: "abc1234",
32
+ GeneratedAt: "2024-01-01T00:00:00Z",
33
+ }
34
+ }
35
+
36
+ func response(t *testing.T) plugin.Response {
37
+ t.Helper()
38
+
39
+ resp, err := extract(input("testdata/estate"), Options{Context: "auth"})
40
+ if err != nil {
41
+ t.Fatal(err)
42
+ }
43
+
44
+ return resp
45
+ }
46
+
47
+ func fragment(t *testing.T) catalog.Catalog {
48
+ t.Helper()
49
+
50
+ var cat catalog.Catalog
51
+ if err := json.Unmarshal([]byte(response(t).Files[0].Contents), &cat); err != nil {
52
+ t.Fatal(err)
53
+ }
54
+
55
+ return cat
56
+ }
57
+
58
+ func TestEveryTermInTheTreeIsRead(t *testing.T) {
59
+ var ids []string
60
+ for _, term := range fragment(t).Terms {
61
+ ids = append(ids, term.ID)
62
+ }
63
+ if strings.Join(ids, " ") != "auth.email-address auth.session auth.version" {
64
+ t.Errorf("ids = %v", ids)
65
+ }
66
+ }
67
+
68
+ // The file sits beside a service and the words in it belong to a context, so
69
+ // the directory is the wrong answer whenever a context holds more than one
70
+ // service - which is the case this option exists for.
71
+ func TestTheContextIsAskedForRatherThanDerived(t *testing.T) {
72
+ resp, err := extract(input("testdata/estate"), Options{})
73
+ if err != nil {
74
+ t.Fatal(err)
75
+ }
76
+ var cat catalog.Catalog
77
+ if err := json.Unmarshal([]byte(resp.Files[0].Contents), &cat); err != nil {
78
+ t.Fatal(err)
79
+ }
80
+ if got := cat.Terms[0].ID; got != "estate.email-address" {
81
+ t.Errorf("without the option the id is %q, want the directory's name", got)
82
+ }
83
+ if got := fragment(t).Terms[0].ID; got != "auth.email-address" {
84
+ t.Errorf("with the option the id is %q", got)
85
+ }
86
+ }
87
+
88
+ // A context that is not a slug would build ids nothing else in the estate can
89
+ // match, and the term would simply never be found again.
90
+ func TestAContextThatIsNotASlugIsRefused(t *testing.T) {
91
+ _, err := extract(input("testdata/estate"), Options{Context: "shop.oms"})
92
+ if err == nil {
93
+ t.Fatal("a dotted context produced a fragment")
94
+ }
95
+ if !strings.Contains(err.Error(), "is not a context slug") {
96
+ t.Errorf("refusal is %q", err)
97
+ }
98
+ }
99
+
100
+ // The fragment is one source among several, so it carries the empty lists of
101
+ // everything it says nothing about rather than leaving the merge to guess.
102
+ func TestTheFragmentSaysNothingItDidNotRead(t *testing.T) {
103
+ cat := fragment(t)
104
+ if len(cat.Contexts) != 0 || len(cat.Flows) != 0 || len(cat.Defs) != 0 || len(cat.Adrs) != 0 {
105
+ t.Errorf("the fragment claims more than its terms: %+v", cat)
106
+ }
107
+ if cat.Commit != "abc1234" || cat.GeneratedAt != "2024-01-01T00:00:00Z" {
108
+ t.Errorf("stamp = %q %q", cat.Commit, cat.GeneratedAt)
109
+ }
110
+ }
111
+
112
+ func TestTheFileIsNamedByTheOptions(t *testing.T) {
113
+ resp, err := extract(input("testdata/estate"), Options{Context: "auth", Out: "words.json"})
114
+ if err != nil {
115
+ t.Fatal(err)
116
+ }
117
+ if resp.Files[0].Name != "words.json" {
118
+ t.Errorf("name = %q", resp.Files[0].Name)
119
+ }
120
+ if response(t).Files[0].Name != "glossary.json" {
121
+ t.Errorf("the default is not glossary.json")
122
+ }
123
+ }
124
+
125
+ // A root with no glossary is worth saying out loud. A step pointed at the
126
+ // wrong directory answers with an empty fragment either way, and the warning
127
+ // is the only thing that tells the two apart.
128
+ func TestARootWithNoGlossaryWarns(t *testing.T) {
129
+ resp, err := extract(input("testdata"), Options{Context: "auth"})
130
+ if err != nil {
131
+ t.Fatal(err)
132
+ }
133
+ if !warnedAbout(resp, "no glossary matched") {
134
+ t.Errorf("diagnostics = %+v", resp.Warnings())
135
+ }
136
+ }
137
+
138
+ // Every rule the format has is checked here, where the file that broke it can
139
+ // be named. A fragment written half-valid is a word with no meaning on a page
140
+ // later, with the reason a long way from its cause.
141
+ func TestABrokenGlossaryIsRefusedWholeRatherThanWrittenHalf(t *testing.T) {
142
+ resp, err := extract(input("testdata/broken"), Options{Context: "shop"})
143
+ if err == nil {
144
+ t.Fatal("the broken tree produced a fragment")
145
+ }
146
+ if len(resp.Files) != 0 {
147
+ t.Errorf("files were named anyway: %+v", resp.Files)
148
+ }
149
+ if !strings.Contains(err.Error(), "prose") {
150
+ t.Errorf("refusal is %q", err)
151
+ }
152
+ }
153
+
154
+ // One context, one vocabulary: two files defining the same word is the failure
155
+ // the glossary is written to prevent, and it is only visible from up here.
156
+ func TestTheSameWordInTwoFilesIsRefused(t *testing.T) {
157
+ _, err := extract(input("testdata/twice"), Options{Context: "shop", Files: []string{"*.GLOSSARY.md"}})
158
+ if err == nil {
159
+ t.Fatal("two definitions of one word produced a fragment")
160
+ }
161
+ if !strings.Contains(err.Error(), "Order is already defined in") {
162
+ t.Errorf("refusal is %q", err)
163
+ }
164
+ }
165
+
166
+ // The fragment is committed and compared by gen:check. A map iterated in Go's
167
+ // order would rewrite the file every run and turn every build into a diff.
168
+ func TestOutputIsByteIdentical(t *testing.T) {
169
+ first := response(t).Files[0].Contents
170
+ for i := 0; i < 5; i++ {
171
+ if again := response(t).Files[0].Contents; again != first {
172
+ t.Fatal("two runs over the same tree produced different fragments")
173
+ }
174
+ }
175
+ }
176
+
177
+ // The whole fragment, so a field that changes shape shows up as a diff rather
178
+ // than slipping past every assertion above.
179
+ func TestGoldenFragment(t *testing.T) {
180
+ got := response(t).Files[0].Contents
181
+
182
+ if *update {
183
+ if err := os.WriteFile(goldenPath, []byte(got), 0o644); err != nil {
184
+ t.Fatal(err)
185
+ }
186
+ t.Log("golden fragment rewritten")
187
+
188
+ return
189
+ }
190
+
191
+ want, err := os.ReadFile(goldenPath)
192
+ if err != nil {
193
+ t.Fatalf("reading the golden fragment: %v (run `go test ./plugins/extract-glossary -update`)", err)
194
+ }
195
+ if string(want) != got {
196
+ t.Errorf("the fragment differs from the golden file\n%s", firstDifference(string(want), got))
197
+ }
198
+ }
199
+
200
+ func warnedAbout(resp plugin.Response, substring string) bool {
201
+ for _, d := range resp.Warnings() {
202
+ if strings.Contains(d.Message, substring) {
203
+ return true
204
+ }
205
+ }
206
+
207
+ return false
208
+ }
209
+
210
+ func firstDifference(want, got string) string {
211
+ a := strings.Split(want, "\n")
212
+ b := strings.Split(got, "\n")
213
+ for i := 0; i < len(a) && i < len(b); i++ {
214
+ if a[i] != b[i] {
215
+ return "line " + strconv.Itoa(i+1) + ":\n want: " + a[i] + "\n got: " + b[i]
216
+ }
217
+ }
218
+
219
+ return "want " + strconv.Itoa(len(a)) + " lines, got " + strconv.Itoa(len(b))
220
+ }
@@ -0,0 +1,59 @@
1
+ // Command extract-glossary reads the vocabulary a bounded context speaks, as
2
+ // the GLOSSARY.md it is already written in, and answers with a catalog
3
+ // fragment.
4
+ //
5
+ // The glossary is the one file in a service that is written for a person and
6
+ // read by everyone: it says what a word means inside the boundary, and what it
7
+ // does NOT mean, which is where the confusion it was written to settle lives.
8
+ // Nothing generates it and nothing should - a definition is a decision about
9
+ // language, not a fact about code - so this plugin only reads.
10
+ //
11
+ // What it refuses is as much the point as what it parses. A table, a bullet
12
+ // list, a heading per term: all of them hold the same words, and every one of
13
+ // them makes the negative half of an entry either impossible to write or
14
+ // impossible to find. One shape, held to here, is what lets a page put "is"
15
+ // and "is not" on two lines everywhere in the estate. The format is described
16
+ // in plugins/README.md and held to by parse_test.go.
17
+ package main
18
+
19
+ import (
20
+ "fmt"
21
+ "io"
22
+ "os"
23
+
24
+ "github.com/shortlink-org/portolan/plugin"
25
+ )
26
+
27
+ // Options are what the manifest tells the extractor.
28
+ type Options struct {
29
+ // Context is the bounded context whose vocabulary this is. It is asked for
30
+ // rather than derived because the file sits beside a SERVICE - shop.oms
31
+ // keeps one, and the words in it belong to shop. Left out, the name of the
32
+ // input directory, which is right only when the two coincide.
33
+ Context string `json:"context,omitempty"`
34
+
35
+ // Files are the glossaries to read, as globs relative to the input root.
36
+ // Left out, `GLOSSARY.md` at the root of it, which is where the layout
37
+ // rules put it - beside the README.
38
+ Files []string `json:"files,omitempty"`
39
+
40
+ // Out names the fragment file.
41
+ Out string `json:"out,omitempty"`
42
+ }
43
+
44
+ func main() {
45
+ if err := run(os.Stdin, os.Stdout); err != nil {
46
+ fmt.Fprintln(os.Stderr, "portolan-extract-glossary:", err)
47
+ os.Exit(1)
48
+ }
49
+ }
50
+
51
+ func run(stdin io.Reader, stdout io.Writer) error {
52
+ return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
53
+ if req.Input.Root == "" {
54
+ return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
55
+ }
56
+
57
+ return extract(req.Input, opts)
58
+ })
59
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "properties": {
5
+ "context": {
6
+ "type": "string",
7
+ "description": "Slug of the bounded context whose vocabulary this is. A glossary sits beside a service, and the words in it belong to the context that service is in, so shop/oms/GLOSSARY.md is read as shop. Left out, the name of the input directory.",
8
+ "examples": ["auth", "shop"]
9
+ },
10
+ "files": {
11
+ "type": "array",
12
+ "items": { "type": "string" },
13
+ "description": "The glossaries to read, as globs relative to the input root. Left out, GLOSSARY.md at the root of it.",
14
+ "examples": [["GLOSSARY.md"], ["docs/GLOSSARY.md"]]
15
+ },
16
+ "out": {
17
+ "type": "string",
18
+ "description": "Name of the fragment file, inside the step's output directory. Left out, glossary.json."
19
+ }
20
+ }
21
+ }