@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,465 @@
1
+ // Reading TypeScript files: the few shapes the extractor looks at, pulled out
2
+ // of the syntax tree once so the modules above it work on names and strings.
3
+ //
4
+ // No type checker: everything here is resolved by name and by import - the
5
+ // resolver is oxc's, so a tsconfig `paths` alias and a workspace package
6
+ // resolve as Node and TypeScript would - which is all a layout that is the
7
+ // claim needs. The tree itself is
8
+ // oxc-parser's, through `ast.ts`.
9
+
10
+ import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
11
+ import { join, resolve } from "node:path";
12
+ import { ResolverFactory } from "oxc-resolver";
13
+ import {
14
+ parse,
15
+ docBlock,
16
+ docParams,
17
+ docReturns,
18
+ docType,
19
+ docTypedefs,
20
+ interfaceFromDoc,
21
+ isAssign,
22
+ isExprStmt,
23
+ isIdent,
24
+ isSourceFile,
25
+ jsdoc as docOf,
26
+ firstTokenOf,
27
+ isArray,
28
+ isBoolean,
29
+ isClassDecl,
30
+ isExportNamed,
31
+ isFunctionDecl,
32
+ isInterface,
33
+ isMethod,
34
+ isNew,
35
+ isNumber,
36
+ isParamProperty,
37
+ isPropertyDef,
38
+ isString,
39
+ keyName,
40
+ lineOf,
41
+ paramIdent,
42
+ parseDoc,
43
+ text as textOf,
44
+ thisMember,
45
+ typeFromDoc,
46
+ typeText,
47
+ SOURCE_EXTENSIONS,
48
+ } from "./ast.ts";
49
+ import type { BlockStatement, ClassDeclaration, DocBlock, FunctionNode, InterfaceDeclaration, MethodDefinition, Node, Parsed, Typed } from "./ast.ts";
50
+
51
+ /** What a doc comment says about the thing it sits on, beyond its prose. */
52
+ export interface Documented {
53
+ doc: string;
54
+ /** The `@deprecated` reason, "" for a bare tag, absent when not deprecated. */
55
+ deprecated?: string;
56
+ /** The `@example` bodies, when the comment had any. */
57
+ examples?: string[];
58
+ }
59
+
60
+ export interface Field extends Documented {
61
+ name: string;
62
+ type: string;
63
+ }
64
+
65
+ export interface Method {
66
+ name: string;
67
+ node: MethodDefinition;
68
+ /** The parameters, in order. */
69
+ params: Node[];
70
+ /** The body, or null for an overload signature. */
71
+ body: BlockStatement | null;
72
+ /** The return type as written, or "" when it is not. */
73
+ returns: string;
74
+ isStatic: boolean;
75
+ }
76
+
77
+ export interface ClassInfo extends Documented {
78
+ name: string;
79
+ node: ClassDeclaration;
80
+ exported: boolean;
81
+ fields: Field[];
82
+ /** Constructor parameters, in order: what a use case holds as ports. */
83
+ params: { name: string; type: string }[];
84
+ methods: Map<string, Method>;
85
+ /** `readonly name = "x"` or `static readonly name = "x"`, when present. */
86
+ nameLiteral: string | undefined;
87
+ }
88
+
89
+ export interface Import {
90
+ /** The local name, or the imported one for a namespace import. */
91
+ local: string;
92
+ /** What it is called in the module it came from; "*" for a namespace. */
93
+ imported: string;
94
+ specifier: string;
95
+ /** The file the specifier resolves to - relative, aliased or a workspace package - or undefined for a dependency. */
96
+ file: string | undefined;
97
+ typeOnly: boolean;
98
+ }
99
+
100
+ /**
101
+ * An interface with the text its nodes point into: the file's own for one
102
+ * written in the syntax, its own for one declared by a `@typedef`, which is
103
+ * parsed apart from the file it sits in.
104
+ */
105
+ export interface Iface {
106
+ p: Parsed;
107
+ node: InterfaceDeclaration;
108
+ }
109
+
110
+ export interface Source {
111
+ path: string;
112
+ parsed: Parsed;
113
+ classes: ClassInfo[];
114
+ imports: Import[];
115
+ /** Exported interfaces, by name - `interface X` in TypeScript, `@typedef {{…}} X` in JavaScript. */
116
+ interfaces: Map<string, Iface>;
117
+ /** Exported functions, by name. */
118
+ functions: Map<string, FunctionNode>;
119
+ /** `import("./x.js")` with a literal request: what the file loads lazily, resolved the same way. */
120
+ dynamicImports: { specifier: string; file: string | undefined }[];
121
+ /** Syntax errors, `file:line` and message. The tree past the first is partial, and so is what was read from it. */
122
+ errors: { at: string; message: string }[];
123
+ }
124
+
125
+ const cache = new Map<string, Source | null>();
126
+
127
+ /** Every source read so far that sits under a root, for reporting what was wrong with them once the reading is done. */
128
+ export function sourcesUnder(root: string): Source[] {
129
+ if (!existsSync(root)) return [];
130
+ const prefix = realpathSync(root) + "/";
131
+ return [...cache.values()].filter((s): s is Source => s !== null && s.path.startsWith(prefix));
132
+ }
133
+
134
+ /**
135
+ * A file, read once. Keyed by its real path, because the resolver hands back
136
+ * real paths - a workspace package is followed through its symlink - and a
137
+ * file reached by two names is still one file.
138
+ */
139
+ export function readSource(path: string): Source | null {
140
+ const asked = resolve(path);
141
+ if (!existsSync(asked)) return null;
142
+ const key = realpathSync(asked);
143
+ const hit = cache.get(key);
144
+ if (hit !== undefined) return hit;
145
+ const parsed = parse(key, readFileSync(key, "utf8"));
146
+ const source: Source = {
147
+ path: key,
148
+ parsed,
149
+ classes: [],
150
+ imports: importsOf(parsed, key),
151
+ interfaces: new Map(),
152
+ functions: new Map(),
153
+ dynamicImports: dynamicImportsOf(parsed, key),
154
+ errors: parsed.errors.map((e) => ({ at: `${key}:${lineOf(parsed, e.labels?.[0]?.start ?? 0)}`, message: e.message })),
155
+ };
156
+ for (const stmt of parsed.program.body) {
157
+ const exported = isExportNamed(stmt);
158
+ const decl = exported ? stmt.declaration : stmt;
159
+ if (!decl) continue;
160
+ if (isInterface(decl)) source.interfaces.set(decl.id.name, { p: parsed, node: decl });
161
+ else if (isFunctionDecl(decl) && decl.id) source.functions.set(decl.id.name, decl);
162
+ }
163
+ // Typedefs before classes: a class's `@param {Port}` may name a typedef,
164
+ // and the typedef's `import(...)` is what says where the port came from.
165
+ if (parsed.js) readTypedefs(source);
166
+ for (const stmt of parsed.program.body) {
167
+ const exported = isExportNamed(stmt);
168
+ const decl = exported ? stmt.declaration : stmt;
169
+ if (decl && isClassDecl(decl) && decl.id) source.classes.push(classInfo(source, decl, exported ? stmt : undefined));
170
+ }
171
+ cache.set(key, source);
172
+ return source;
173
+ }
174
+
175
+ /**
176
+ * Every `@typedef` in a JavaScript file, wherever its comment sits - a typedef
177
+ * is attached to nothing, so the comments are read rather than the tree.
178
+ * `@typedef {import("./port.js").Port} Port` is an import of a type and is
179
+ * filed as one. `@typedef {{ save(): Promise<void> }} Port`, and
180
+ * `@typedef {Object} Port` with `@property` lines under it, is an interface,
181
+ * and becomes the node `interface Port {…}` would have been. Any other
182
+ * typedef - a union, an alias of a primitive - names nothing the extractor
183
+ * follows, and is left alone.
184
+ */
185
+ function readTypedefs(src: Source): void {
186
+ for (const c of src.parsed.comments) {
187
+ if (c.type !== "Block" || !c.value.startsWith("*")) continue;
188
+ for (const def of docTypedefs(parseDoc(c.value.slice(1)))) {
189
+ const imported = /^import\(\s*["']([^"']+)["']\s*\)\.([\w$]+)$/.exec(def.type);
190
+ if (imported) {
191
+ addImport(src, { local: def.name, imported: imported[2]!, specifier: imported[1]!, file: resolveImport(src.path, imported[1]!), typeOnly: true });
192
+ continue;
193
+ }
194
+ let body: string | undefined;
195
+ if (/^\{[\s\S]*\}$/.test(def.type)) body = docTypeIn(src, def.type.slice(1, -1));
196
+ else if (/^object$/i.test(def.type)) body = def.properties.map((p) => `${p.name}: ${docTypeIn(src, p.type)};`).join(" ");
197
+ if (body === undefined) continue;
198
+ const iface = interfaceFromDoc(def.name, body);
199
+ if (iface) src.interfaces.set(def.name, iface);
200
+ }
201
+ }
202
+ }
203
+
204
+ /** An import a doc comment implies, added once: the first mention of a local name wins, as it would in the syntax. */
205
+ function addImport(src: Source, imp: Import): void {
206
+ if (!src.imports.some((i) => i.local === imp.local)) src.imports.push(imp);
207
+ }
208
+
209
+ /**
210
+ * A type expression from a doc comment, with each `import("./x.js").Y` in it
211
+ * read as the import it is and shortened to `Y`, so that what is left is a
212
+ * name the rest of the reader resolves the way it resolves any other: through
213
+ * `src.imports`. Returns the expression as the syntax would have written it.
214
+ */
215
+ export function docTypeIn(src: Source, expr: string): string {
216
+ return expr.replace(/\btypeof\s+import\(\s*["']([^"']+)["']\s*\)\.([\w$]+)|\bimport\(\s*["']([^"']+)["']\s*\)\.([\w$]+)/g, (_m, s1: string | undefined, n1: string | undefined, s2: string | undefined, n2: string | undefined) => {
217
+ const specifier = (s1 ?? s2)!;
218
+ const name = (n1 ?? n2)!;
219
+ addImport(src, { local: name, imported: name, specifier, file: resolveImport(src.path, specifier), typeOnly: true });
220
+ return s1 ? `typeof ${name}` : name;
221
+ });
222
+ }
223
+
224
+ /** A doc-comment type as the annotation it stands in for, or undefined when there is none or it does not parse. */
225
+ function docTyped(src: Source, expr: string): Typed | undefined {
226
+ return expr ? typeFromDoc(docTypeIn(src, expr)) : undefined;
227
+ }
228
+
229
+ /** The files the extractor reads in a directory, sorted: TypeScript and JavaScript alike, tests and declarations left out. */
230
+ export function sourceFiles(dir: string): string[] {
231
+ if (!existsSync(dir)) return [];
232
+ return readdirSync(dir)
233
+ .filter(isSourceFile)
234
+ .sort()
235
+ .map((f) => join(dir, f));
236
+ }
237
+
238
+ /** `<dir>/<base>.ts`, or the `.js`/`.mjs` beside where it would have been: the fixed names of the layout, in whichever language the tree is written. */
239
+ export function sourceNamed(dir: string, base: string): string {
240
+ for (const ext of SOURCE_EXTENSIONS) {
241
+ const path = join(dir, base + ext);
242
+ if (existsSync(path)) return path;
243
+ }
244
+ return join(dir, `${base}.ts`);
245
+ }
246
+
247
+ /**
248
+ * Node's resolution, with what a TypeScript tree adds to it: `./x.js` means
249
+ * `./x.ts`, a bare `./x` tries every source extension and then an index,
250
+ * `@app/domain/basket` is whatever the nearest tsconfig's `paths` say, and a
251
+ * workspace package is followed through its symlink to the source it is. The
252
+ * `types` condition comes first so a package that ships declarations resolves
253
+ * to the shape a port reader can walk rather than to compiled output.
254
+ */
255
+ const resolver = new ResolverFactory({
256
+ tsconfig: "auto",
257
+ extensions: [...SOURCE_EXTENSIONS, ".d.ts", ".mts", ".cts"],
258
+ extensionAlias: { ".js": [".ts", ".tsx", ".d.ts", ".js"], ".mjs": [".mts", ".mjs"], ".cjs": [".cts", ".cjs"], ".jsx": [".tsx", ".jsx"] },
259
+ conditionNames: ["types", "import", "default"],
260
+ mainFields: ["types", "module", "main"],
261
+ });
262
+
263
+ /**
264
+ * The file a specifier names, or undefined for a package.
265
+ *
266
+ * A package is a specifier that lands in `node_modules`: what is there is a
267
+ * dependency, and the extractor does not read dependencies - a client library
268
+ * is recognised by the name it is imported under, not by its source. A
269
+ * workspace package is the exception, and needs none: its symlink resolves
270
+ * to a directory of the repository, outside any `node_modules`, and is read
271
+ * like the rest of the tree.
272
+ */
273
+ export function resolveImport(from: string, specifier: string): string | undefined {
274
+ const found = resolver.resolveFileSync(from, specifier);
275
+ if (!found.path || /[\\/]node_modules[\\/]/.test(found.path)) return undefined;
276
+ return found.path;
277
+ }
278
+
279
+ /**
280
+ * The static imports as the parser records them: every declaration, every
281
+ * entry, with the name it is imported under, the name it had, and whether
282
+ * it is type-only - `import type { A }` and `import { type A }` alike.
283
+ * Nothing here walks the tree; the record is a by-product of parsing.
284
+ */
285
+ function importsOf(parsed: Parsed, from: string): Import[] {
286
+ const out: Import[] = [];
287
+ for (const decl of parsed.module.staticImports) {
288
+ const specifier = decl.moduleRequest.value;
289
+ const file = resolveImport(from, specifier);
290
+ for (const entry of decl.entries) {
291
+ const imported = entry.importName.kind === "Default" ? "default" : entry.importName.kind === "NamespaceObject" ? "*" : (entry.importName.name ?? entry.localName.value);
292
+ out.push({ local: entry.localName.value, imported, specifier, file, typeOnly: entry.isType });
293
+ }
294
+ }
295
+ return out;
296
+ }
297
+
298
+ /** `import("./x.js")` with a literal request, resolved; one with an expression names nothing that can be followed. */
299
+ function dynamicImportsOf(parsed: Parsed, from: string): { specifier: string; file: string | undefined }[] {
300
+ const out: { specifier: string; file: string | undefined }[] = [];
301
+ for (const d of parsed.module.dynamicImports) {
302
+ const request = parsed.text.slice(d.moduleRequest.start, d.moduleRequest.end);
303
+ const m = /^(["'])(.*)\1$/s.exec(request);
304
+ if (!m) continue;
305
+ out.push({ specifier: m[2]!, file: resolveImport(from, m[2]!) });
306
+ }
307
+ return out;
308
+ }
309
+
310
+ /** The doc comment above a node, allowing for a decorator or an `export` in front of it. */
311
+ export function jsdoc(src: Source, node: Node, exportNode?: Node): string {
312
+ return docOf(src.parsed, node, firstTokenOf(node, exportNode));
313
+ }
314
+
315
+ /** A parsed doc block as the optional fields a `Documented` carries: nothing is written down when the tag is absent. */
316
+ function withTags(block: DocBlock): Documented {
317
+ const out: Documented = { doc: block.text };
318
+ if (block.deprecated !== undefined) out.deprecated = block.deprecated;
319
+ if (block.examples.length) out.examples = block.examples;
320
+ return out;
321
+ }
322
+
323
+ /**
324
+ * The prose with the deprecation written into it, for a catalog field that
325
+ * holds only a sentence: "Deprecated: use `total` instead." is what a reader
326
+ * wants next to a struck-through name, and the boolean beside it is what the
327
+ * page strikes it through by.
328
+ */
329
+ export function docWithDeprecation(d: Documented): string {
330
+ if (d.deprecated === undefined) return d.doc;
331
+ const note = d.deprecated ? `Deprecated: ${d.deprecated.replace(/[.\s]+$/, "")}.` : "Deprecated.";
332
+ return d.doc ? `${d.doc}\n\n${note}` : note;
333
+ }
334
+
335
+ /**
336
+ * A class as the reader sees it. In TypeScript the types are in the syntax;
337
+ * in JavaScript they are in the doc comments - `@type` on a field, `@param`
338
+ * on the constructor, `@returns` on a method - and a field is what the
339
+ * constructor assigns to `this`, since there is no parameter property to
340
+ * declare it with. Each reading falls back to the next, so a TypeScript file
341
+ * with a JSDoc type on an unannotated field reads that too.
342
+ */
343
+ function classInfo(src: Source, node: ClassDeclaration, exportNode: Node | undefined): ClassInfo {
344
+ const p = src.parsed;
345
+ const info: ClassInfo = {
346
+ name: node.id!.name,
347
+ node,
348
+ ...withTags(docBlock(p, node, firstTokenOf(node, exportNode))),
349
+ exported: exportNode !== undefined,
350
+ fields: [],
351
+ params: [],
352
+ methods: new Map(),
353
+ nameLiteral: undefined,
354
+ };
355
+ for (const member of node.body.body) {
356
+ if (isPropertyDef(member) && !member.computed) {
357
+ const name = keyName(member.key);
358
+ if (name === undefined) continue;
359
+ const init = member.value;
360
+ if (name === "name" && isString(init)) {
361
+ info.nameLiteral = init.value;
362
+ continue;
363
+ }
364
+ if (member.static) continue;
365
+ const block = docBlock(p, member, firstTokenOf(member));
366
+ info.fields.push({ name, type: typeText(p, member.typeAnnotation) || docTypeIn(src, docType(block)) || inferred(p, init), ...withTags(block) });
367
+ } else if (isMethod(member) && member.kind === "constructor") {
368
+ const documented = docParams(docBlock(p, member, firstTokenOf(member)));
369
+ const params = new Map<string, string>();
370
+ for (const param of member.value.params) {
371
+ const id = paramIdent(param);
372
+ if (!id) continue;
373
+ const type = typeText(p, id.typeAnnotation) || docTypeIn(src, documented.get(id.name) ?? "");
374
+ info.params.push({ name: id.name, type });
375
+ params.set(id.name, type);
376
+ if (isParamProperty(param)) info.fields.push({ name: id.name, type, ...withTags(docBlock(p, param, firstTokenOf(param))) });
377
+ }
378
+ if (p.js && member.value.body) {
379
+ // `this.id = id`: the field is declared by being assigned, typed by
380
+ // the parameter it takes or by what it is given.
381
+ for (const stmt of member.value.body.body) {
382
+ if (!isExprStmt(stmt) || !isAssign(stmt.expression) || stmt.expression.operator !== "=") continue;
383
+ const name = thisMember(stmt.expression.left);
384
+ if (name === undefined || name === "name" || info.fields.some((f) => f.name === name)) continue;
385
+ const value = stmt.expression.right;
386
+ const block = docBlock(p, stmt);
387
+ const fromParam = isIdent(value) ? params.get(value.name) : undefined;
388
+ info.fields.push({ name, type: docTypeIn(src, docType(block)) || fromParam || inferred(p, value), ...withTags(block) });
389
+ }
390
+ }
391
+ } else if (isMethod(member) && !member.computed && member.kind === "method") {
392
+ const name = keyName(member.key);
393
+ // An overload signature has no body; the implementation that follows is
394
+ // the one read, so the last declaration of a name wins.
395
+ if (name === undefined) continue;
396
+ info.methods.set(name, {
397
+ name,
398
+ node: member,
399
+ params: member.value.params,
400
+ body: member.value.body,
401
+ returns: typeText(p, member.value.returnType) || docTypeIn(src, docReturns(docBlock(p, member, firstTokenOf(member)))),
402
+ isStatic: member.static,
403
+ });
404
+ }
405
+ }
406
+ return info;
407
+ }
408
+
409
+ /** The doc block above an exported function, found through its export statement when it has one. */
410
+ function functionDoc(src: Source, fn: FunctionNode): DocBlock {
411
+ for (const stmt of src.parsed.program.body) {
412
+ if (stmt === fn) return docBlock(src.parsed, fn);
413
+ if (isExportNamed(stmt) && stmt.declaration === fn) return docBlock(src.parsed, fn, firstTokenOf(fn, stmt));
414
+ }
415
+ return docBlock(src.parsed, fn);
416
+ }
417
+
418
+ /** What a function returns: the annotation, or the `@returns {T}` above it, with the text the node points into. */
419
+ export function returnTypeOf(src: Source, fn: FunctionNode): Typed | undefined {
420
+ if (fn.returnType) return { p: src.parsed, ann: fn.returnType };
421
+ return docTyped(src, docReturns(functionDoc(src, fn)));
422
+ }
423
+
424
+ /** The type of one of a function's parameters: the annotation, or the `@param {T} name` above the function. */
425
+ export function paramTypeOf(src: Source, fn: FunctionNode, param: Node): Typed | undefined {
426
+ const id = paramIdent(param);
427
+ if (!id) return undefined;
428
+ if (id.typeAnnotation) return { p: src.parsed, ann: id.typeAnnotation };
429
+ return docTyped(src, docParams(functionDoc(src, fn)).get(id.name) ?? "");
430
+ }
431
+
432
+ /** The return type of a function as written, or "". */
433
+ export function returnTypeText(src: Source, fn: FunctionNode): string {
434
+ const t = returnTypeOf(src, fn);
435
+ return t ? typeText(t.p, t.ann) : "";
436
+ }
437
+
438
+ function inferred(p: Parsed, init: Node | null): string {
439
+ if (!init) return "";
440
+ if (isString(init)) return "string";
441
+ if (isNumber(init)) return "number";
442
+ if (isBoolean(init)) return "boolean";
443
+ if (isArray(init)) return "[]";
444
+ if (isNew(init)) return textOf(p, init.callee);
445
+ return "";
446
+ }
447
+
448
+ /** The source of a node, as written. */
449
+ export function text(src: Source, node: Node): string {
450
+ return textOf(src.parsed, node);
451
+ }
452
+
453
+ /** `file:line` for a node, relative to the repository the way the catalog spells it. */
454
+ export function at(src: Source, node: Node, rel: (abs: string) => string): string {
455
+ return `${rel(src.path)}:${lineOf(src.parsed, node.start)}`;
456
+ }
457
+
458
+ /** The type behind `Promise<X>`, `X | undefined`, `readonly X[]` and friends, as a bare name. */
459
+ export function bareType(type: string): string {
460
+ let t = type.trim();
461
+ const promise = /^Promise<(.*)>$/s.exec(t);
462
+ if (promise) t = promise[1]!.trim();
463
+ t = t.replace(/\s*\|\s*(undefined|null)\b/g, "").replace(/^(readonly\s+)/, "");
464
+ return t;
465
+ }
@@ -0,0 +1,147 @@
1
+ // Endpoints, read off src/infrastructure/transport.
2
+ //
3
+ // Over HTTP the document's operationIds name the handlers; over gRPC the
4
+ // contract vendored beside the handler does, and an rpc is named the same on
5
+ // both sides - `planRoute` answers `PlanRoute`. Either way a handler's body
6
+ // names the use cases it runs, in order, and that is what opens a flow.
7
+
8
+ import { existsSync, readdirSync, statSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { readSpec, type Spec } from "./openapi.ts";
11
+ import { readProtos } from "./clients.ts";
12
+ import { readSource, type ClassInfo, type Source, at } from "./source.ts";
13
+ import { isCall, isMember, isSourceFile, memberName, thisMember, walk, type Node } from "./ast.ts";
14
+ import { useCaseKeyOf } from "./operations.ts";
15
+ import type { WarningSink } from "./domain.ts";
16
+
17
+ export interface Endpoint {
18
+ /** The operationId. */
19
+ id: string;
20
+ /** file:line of the handler. */
21
+ line: string;
22
+ source: string;
23
+ /** Use case keys, in the order the handler runs them. */
24
+ useCases: string[];
25
+ }
26
+
27
+ export interface Transport {
28
+ spec: Spec | undefined;
29
+ endpoints: Endpoint[];
30
+ }
31
+
32
+ /**
33
+ * The gRPC half: one directory per aggregate, the contract it answers vendored
34
+ * under it, and a handler whose methods are that contract's rpcs. The endpoint
35
+ * is called what the proto calls it, which is the name `extract-proto` puts in
36
+ * `provides`, so an operation and the interface that exposes it meet.
37
+ */
38
+ export function readGrpcTransport(grpcDir: string, rel: (abs: string) => string, b: WarningSink): Endpoint[] {
39
+ if (!existsSync(grpcDir)) return [];
40
+ const endpoints: Endpoint[] = [];
41
+
42
+ for (const pkg of readdirSync(grpcDir).sort()) {
43
+ const dir = join(grpcDir, pkg);
44
+ if (!statSync(dir).isDirectory()) continue;
45
+
46
+ const contracts = readProtos(join(dir, "proto"), rel);
47
+ if (contracts.length === 0) {
48
+ b.warn(rel(dir), "a grpc handler with no contract vendored beside it: nothing says which rpc its methods answer");
49
+ continue;
50
+ }
51
+ const rpcs = new Map<string, string>();
52
+ for (const contract of contracts) {
53
+ for (const rpc of contract.rpcs) rpcs.set(rpc.toLowerCase(), rpc);
54
+ }
55
+ const answered = new Set<string>();
56
+
57
+ for (const name of readdirSync(dir).sort()) {
58
+ if (!isSourceFile(name)) continue;
59
+ const src = readSource(join(dir, name));
60
+ if (!src) continue;
61
+ for (const cls of src.classes) {
62
+ const ports = useCasePorts(src, cls);
63
+ for (const [method, m] of cls.methods) {
64
+ const rpc = rpcs.get(method.toLowerCase());
65
+ if (!rpc || answered.has(rpc)) continue;
66
+ answered.add(rpc);
67
+ endpoints.push({
68
+ id: rpc,
69
+ line: at(src, m.node, rel),
70
+ source: rel(src.path),
71
+ useCases: useCasesRun(m.node, ports),
72
+ });
73
+ }
74
+ }
75
+ }
76
+
77
+ for (const [, rpc] of rpcs) {
78
+ if (!answered.has(rpc)) {
79
+ b.warn(rel(dir), rpc + " is declared by the contract and answered by no method here");
80
+ }
81
+ }
82
+ }
83
+
84
+ return endpoints.sort((a, c) => a.id.localeCompare(c.id));
85
+ }
86
+
87
+ export function readTransport(httpDir: string, rel: (abs: string) => string, b: WarningSink): Transport {
88
+ const specPath = join(httpDir, "gen", "openapi.yaml");
89
+ if (!existsSync(specPath)) return { spec: undefined, endpoints: [] };
90
+ const spec = readSpec(specPath);
91
+ const wanted = new Map(spec.operations.map((op) => [op.id, op]));
92
+ const endpoints: Endpoint[] = [];
93
+ const found = new Set<string>();
94
+
95
+ for (const pkg of readdirSync(httpDir).sort()) {
96
+ const dir = join(httpDir, pkg);
97
+ if (pkg === "gen" || !statSync(dir).isDirectory()) continue;
98
+ for (const name of readdirSync(dir).sort()) {
99
+ if (!isSourceFile(name)) continue;
100
+ const src = readSource(join(dir, name));
101
+ if (!src) continue;
102
+ for (const cls of src.classes) {
103
+ const ports = useCasePorts(src, cls);
104
+ for (const [method, m] of cls.methods) {
105
+ if (!wanted.has(method) || found.has(method)) continue;
106
+ found.add(method);
107
+ endpoints.push({
108
+ id: method,
109
+ line: at(src, m.node, rel),
110
+ source: rel(src.path),
111
+ useCases: useCasesRun(m.node, ports),
112
+ });
113
+ }
114
+ }
115
+ }
116
+ }
117
+ for (const id of wanted.keys()) {
118
+ if (!found.has(id)) b.warn(id, `${rel(specPath)} declares ${id} but no handler under ${rel(httpDir)} is named by it`);
119
+ }
120
+ endpoints.sort((a, c) => a.id.localeCompare(c.id));
121
+ return { spec, endpoints };
122
+ }
123
+
124
+ /** Constructor parameters whose type is a UseCase import: field name → use case key. */
125
+ export function useCasePorts(src: Source, cls: ClassInfo): Map<string, string> {
126
+ const out = new Map<string, string>();
127
+ for (const p of cls.params) {
128
+ const local = p.type.replace(/^typeof\s+/, "").trim();
129
+ const imp = src.imports.find((i) => i.local === local);
130
+ if (!imp?.file || imp.imported !== "UseCase") continue;
131
+ const key = useCaseKeyOf(imp.file);
132
+ if (key) out.set(p.name, key);
133
+ }
134
+ return out;
135
+ }
136
+
137
+ /** `this.<port>.handle(...)` calls, in source order, as use case keys. */
138
+ function useCasesRun(node: Node, ports: Map<string, string>): string[] {
139
+ const out: string[] = [];
140
+ walk(node, (n) => {
141
+ if (!isCall(n) || !isMember(n.callee) || memberName(n.callee) !== "handle") return;
142
+ const port = thisMember(n.callee.object);
143
+ const key = port === undefined ? undefined : ports.get(port);
144
+ if (key) out.push(key);
145
+ });
146
+ return out;
147
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "module": "nodenext",
6
+ "moduleResolution": "nodenext",
7
+ "allowImportingTsExtensions": true,
8
+ "types": ["node"],
9
+ "jsx": "preserve"
10
+ },
11
+ "include": ["./*.ts", "../../src/catalog.ts"],
12
+ "exclude": ["./testdata"]
13
+ }