@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,409 @@
1
+ package main
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/token"
6
+ "go/types"
7
+ "strconv"
8
+ "strings"
9
+
10
+ "github.com/shortlink-org/portolan/catalog"
11
+ )
12
+
13
+ // A call to another service is read off the generated client, because that is
14
+ // where the fact is written down: protoc-gen-go-grpc emits one constant per
15
+ // rpc holding the full method name, and the client interface beside it;
16
+ // oapi-codegen emits a request builder per operation with its route, beside
17
+ // the document it was generated from (httpclient.go). The use case never
18
+ // names either - it holds a port - so the chain is port → provider → adapter
19
+ // → client, and every link of it is in the tree.
20
+
21
+ // client is one generated client and the calls it carries.
22
+ type client struct {
23
+ // pkg is what the manifest's peers map is keyed by: the proto package,
24
+ // "risk.v1", or the api id, "auth.v1".
25
+ pkg string
26
+ // methods maps a Go method name to the call id: "risk.v1.RiskService/Assess",
27
+ // "auth.v1.Sessions/login".
28
+ methods map[string]string
29
+ // source is the generated file, or the document, the names were read from.
30
+ source string
31
+ // external is the system the document beside an HTTP client is titled
32
+ // after, as an id: what the far end is called when no peers line says it
33
+ // is one of ours. Empty for a gRPC client, whose proto names no system.
34
+ external string
35
+ }
36
+
37
+ // readClients finds every client a package declares, keyed by the type a port
38
+ // would hold it as: "RiskServiceClient" for gRPC, "ClientWithResponses" and
39
+ // its three siblings for HTTP. The problem, when there is one, says why an
40
+ // HTTP client could not be named.
41
+ func readClients(pkg *pkg) (map[string]client, string) {
42
+ if isHTTPClient(pkg) {
43
+ return readHTTPClient(pkg)
44
+ }
45
+ out := map[string]client{}
46
+
47
+ for _, file := range pkg.files {
48
+ for _, decl := range file.Decls {
49
+ gen, ok := decl.(*ast.GenDecl)
50
+ if !ok || gen.Tok != token.CONST {
51
+ continue
52
+ }
53
+ for _, spec := range gen.Specs {
54
+ value, ok := spec.(*ast.ValueSpec)
55
+ if !ok {
56
+ continue
57
+ }
58
+ for i, name := range value.Names {
59
+ if i >= len(value.Values) {
60
+ continue
61
+ }
62
+ lit, ok := value.Values[i].(*ast.BasicLit)
63
+ if !ok || lit.Kind != token.STRING {
64
+ continue
65
+ }
66
+ full, err := strconv.Unquote(lit.Value)
67
+ if err != nil {
68
+ continue
69
+ }
70
+ goService, service, method, ok := splitFullMethod(name.Name, full)
71
+ if !ok {
72
+ continue
73
+ }
74
+
75
+ key := goService + "Client"
76
+ c, known := out[key]
77
+ if !known {
78
+ c = client{
79
+ pkg: lastDotPrefix(service),
80
+ methods: map[string]string{},
81
+ source: pkg.paths[file],
82
+ }
83
+ }
84
+ c.methods[method] = service + "/" + method
85
+ out[key] = c
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ return out, ""
92
+ }
93
+
94
+ // splitFullMethod reads `RiskService_Assess_FullMethodName = "/risk.v1.RiskService/Assess"`
95
+ // into the Go service name, the proto service name and the rpc.
96
+ func splitFullMethod(constName, full string) (goService, service, method string, ok bool) {
97
+ if !strings.HasSuffix(constName, "_FullMethodName") || !strings.HasPrefix(full, "/") {
98
+ return "", "", "", false
99
+ }
100
+ service, method, found := strings.Cut(full[1:], "/")
101
+ if !found || service == "" || method == "" || strings.Contains(method, "/") {
102
+ return "", "", "", false
103
+ }
104
+ goService, ok = strings.CutSuffix(constName, "_"+method+"_FullMethodName")
105
+ if !ok || goService == "" {
106
+ return "", "", "", false
107
+ }
108
+
109
+ return goService, service, method, true
110
+ }
111
+
112
+ // lastDotPrefix is "risk.v1" for "risk.v1.RiskService".
113
+ func lastDotPrefix(name string) string {
114
+ if i := strings.LastIndex(name, "."); i >= 0 {
115
+ return name[:i]
116
+ }
117
+
118
+ return name
119
+ }
120
+
121
+ // clientPkg reads the clients of a package by import path, once.
122
+ func (r *flowReader) clientPkg(importPath string) map[string]client {
123
+ if cached, ok := r.clients[importPath]; ok {
124
+ return cached
125
+ }
126
+
127
+ var out map[string]client
128
+ if rel, ok := r.relDir(importPath); ok {
129
+ if pkg, err := parsePkg(r.root, rel); err == nil {
130
+ var problem string
131
+ out, problem = readClients(pkg)
132
+ if problem != "" {
133
+ r.b.Warn(r.opts.svcID, problem)
134
+ }
135
+ }
136
+ }
137
+ r.clients[importPath] = out
138
+
139
+ return out
140
+ }
141
+
142
+ // relDir maps an import path inside this module to a directory under root.
143
+ // Anything outside the module is not in the tree, and this reads the tree.
144
+ func (r *flowReader) relDir(importPath string) (string, bool) {
145
+ if r.module == "" {
146
+ return "", false
147
+ }
148
+
149
+ return strings.CutPrefix(importPath, r.module+"/")
150
+ }
151
+
152
+ // clientOf resolves a type written as `riskpb.RiskServiceClient` to the client
153
+ // it names, or says the type is not a generated client.
154
+ func (r *flowReader) clientOf(declared string, imports map[string]string) (client, bool) {
155
+ selector, name, found := strings.Cut(strings.TrimPrefix(declared, "*"), ".")
156
+ if !found {
157
+ return client{}, false
158
+ }
159
+ client, ok := r.clientPkg(imports[selector])[name]
160
+
161
+ return client, ok
162
+ }
163
+
164
+ // rpcHop is one call on a port that turns out to be another service's rpc.
165
+ type rpcHop struct {
166
+ client client
167
+ method string
168
+ }
169
+
170
+ // clientCalls says which rpcs a call on a port amounts to: none when the port
171
+ // is not a client, one when the port IS the generated client, and whatever the
172
+ // adapter's method calls when the port is a local interface bound to one in
173
+ // assembly.
174
+ func (r *flowReader) clientCalls(s *scope, declared, method string) []rpcHop {
175
+ if client, ok := r.clientOf(declared, s.imports); ok {
176
+ if _, known := client.methods[method]; !known {
177
+ r.b.Warn(s.key, "port `"+declared+"` is a generated client with no rpc named "+method+"; the call is left out of the flow")
178
+
179
+ return nil
180
+ }
181
+
182
+ return []rpcHop{{client: client, method: method}}
183
+ }
184
+
185
+ binding, ok := r.adapters[s.key+"."+declared]
186
+ if !ok {
187
+ return nil
188
+ }
189
+
190
+ return r.adapterCalls(s.key, declared, binding, method)
191
+ }
192
+
193
+ // adapterCalls reads the provider that binds a port to a client, follows it to
194
+ // the adapter type it builds, and reads which rpcs the adapter's method runs.
195
+ func (r *flowReader) adapterCalls(key, declared string, binding adapterDecl, method string) []rpcHop {
196
+ var client client
197
+ var clientType string
198
+ for _, param := range params(binding.fn) {
199
+ written := types.ExprString(param)
200
+ if c, ok := r.clientOf(written, binding.imports); ok {
201
+ client, clientType = c, strings.TrimPrefix(written, "*")
202
+
203
+ break
204
+ }
205
+ }
206
+ if clientType == "" {
207
+ // A provider that builds the port out of something other than a
208
+ // client is a binding of a different kind, and not one this can
209
+ // follow to another service.
210
+ return nil
211
+ }
212
+
213
+ adapter, adapterPkg, ok := r.adapterOf(binding)
214
+ if !ok {
215
+ r.b.Warn(key, "port `"+declared+"` is bound to a client of "+client.pkg+" but the provider's result could not be followed to a type; its calls are left out of the flow")
216
+
217
+ return nil
218
+ }
219
+
220
+ fn := adapterPkg.methods(adapter)[method]
221
+ if fn == nil {
222
+ r.b.Warn(key, "port `"+declared+"` is adapted by "+adapter+", which has no method "+method+"; the call is left out of the flow")
223
+
224
+ return nil
225
+ }
226
+
227
+ // The field of the adapter that holds the client, by name, so a call on it
228
+ // can be told apart from a call on anything else the adapter holds.
229
+ fields := map[string]string{}
230
+ for _, decl := range adapterPkg.structs() {
231
+ if decl.name == adapter {
232
+ fields = structFields(decl.fields)
233
+ }
234
+ }
235
+ imports := importsOf(adapterPkg)
236
+ recv := receiverIdent(fn)
237
+
238
+ var out []rpcHop
239
+ seen := map[string]bool{}
240
+ for _, site := range callSites(fn) {
241
+ selector, ok := site.call.Fun.(*ast.SelectorExpr)
242
+ if !ok {
243
+ continue
244
+ }
245
+ inner, ok := selector.X.(*ast.SelectorExpr)
246
+ if !ok {
247
+ continue
248
+ }
249
+ owner, ok := inner.X.(*ast.Ident)
250
+ if !ok || owner.Name != recv {
251
+ continue
252
+ }
253
+ held, ok := r.clientOf(fields[inner.Sel.Name], imports)
254
+ if !ok || held.pkg != client.pkg {
255
+ continue
256
+ }
257
+ rpc := selector.Sel.Name
258
+ if _, known := held.methods[rpc]; !known || seen[rpc] {
259
+ continue
260
+ }
261
+ seen[rpc] = true
262
+ out = append(out, rpcHop{client: held, method: rpc})
263
+ }
264
+
265
+ if len(out) == 0 {
266
+ r.b.Warn(key, "port `"+declared+"` is adapted by "+adapter+"."+method+", which calls no rpc of the client; the call is left out of the flow")
267
+ }
268
+
269
+ return out
270
+ }
271
+
272
+ // adapterOf follows a provider's return to the type it builds: a literal of a
273
+ // type declared beside it, or a constructor in another package of this module.
274
+ func (r *flowReader) adapterOf(binding adapterDecl) (string, *pkg, bool) {
275
+ expr := returnedExpr(binding.fn)
276
+ if expr == nil {
277
+ return "", nil, false
278
+ }
279
+ if unary, ok := expr.(*ast.UnaryExpr); ok && unary.Op == token.AND {
280
+ expr = unary.X
281
+ }
282
+
283
+ switch x := expr.(type) {
284
+ case *ast.CompositeLit:
285
+ return r.typeIn(x.Type, binding)
286
+ case *ast.CallExpr:
287
+ selector, ok := x.Fun.(*ast.SelectorExpr)
288
+ if !ok {
289
+ return "", nil, false
290
+ }
291
+ owner, ok := selector.X.(*ast.Ident)
292
+ if !ok {
293
+ return "", nil, false
294
+ }
295
+ rel, ok := r.relDir(binding.imports[owner.Name])
296
+ if !ok {
297
+ return "", nil, false
298
+ }
299
+ pkg, err := parsePkg(r.root, rel)
300
+ if err != nil {
301
+ return "", nil, false
302
+ }
303
+ for _, file := range pkg.files {
304
+ for _, decl := range file.Decls {
305
+ fn, ok := decl.(*ast.FuncDecl)
306
+ if !ok || fn.Recv != nil || fn.Name.Name != selector.Sel.Name {
307
+ continue
308
+ }
309
+ if fn.Type.Results == nil || len(fn.Type.Results.List) == 0 {
310
+ return "", nil, false
311
+ }
312
+ name := strings.TrimPrefix(types.ExprString(fn.Type.Results.List[0].Type), "*")
313
+ if strings.Contains(name, ".") {
314
+ return "", nil, false
315
+ }
316
+
317
+ return name, pkg, true
318
+ }
319
+ }
320
+ }
321
+
322
+ return "", nil, false
323
+ }
324
+
325
+ // typeIn resolves the type of a composite literal: `adapter{}` is in the
326
+ // provider's own package, `risk.Client{}` in an imported one.
327
+ func (r *flowReader) typeIn(expr ast.Expr, binding adapterDecl) (string, *pkg, bool) {
328
+ switch t := expr.(type) {
329
+ case *ast.Ident:
330
+ return t.Name, binding.pkg, true
331
+ case *ast.SelectorExpr:
332
+ owner, ok := t.X.(*ast.Ident)
333
+ if !ok {
334
+ return "", nil, false
335
+ }
336
+ rel, ok := r.relDir(binding.imports[owner.Name])
337
+ if !ok {
338
+ return "", nil, false
339
+ }
340
+ pkg, err := parsePkg(r.root, rel)
341
+ if err != nil {
342
+ return "", nil, false
343
+ }
344
+
345
+ return t.Sel.Name, pkg, true
346
+ }
347
+
348
+ return "", nil, false
349
+ }
350
+
351
+ // returnedExpr is the single value of the function's last return.
352
+ func returnedExpr(fn *ast.FuncDecl) ast.Expr {
353
+ if fn == nil || fn.Body == nil || len(fn.Body.List) == 0 {
354
+ return nil
355
+ }
356
+ ret, ok := fn.Body.List[len(fn.Body.List)-1].(*ast.ReturnStmt)
357
+ if !ok || len(ret.Results) != 1 {
358
+ return nil
359
+ }
360
+
361
+ return ret.Results[0]
362
+ }
363
+
364
+ // peerLane is the lane a call to another service lands in, and what the step
365
+ // can claim about it. The manifest says which service answers to a proto
366
+ // package; without that line the far end is a package name, which is not a
367
+ // thing the catalog has, and the step says so rather than guessing.
368
+ func (r *flowReader) peerLane(d *flowDraft, client client) (lane, peer string, status catalog.Status) {
369
+ if service, ok := r.opts.peers[client.pkg]; ok {
370
+ context, _, _ := strings.Cut(service, ".")
371
+
372
+ return d.lane(catalog.Participant{ID: service, Kind: catalog.ParticipantService, Context: &context}), service, catalog.StatusDeclared
373
+ }
374
+
375
+ // A system outside the estate, with a contract: the call lands on an
376
+ // operation its document declares, so the step is declared, and the lane
377
+ // says what the far end is rather than pretending it is ours. The manifest
378
+ // may name the system; failing that, the document beside the client does,
379
+ // because a service that vendors a document and generates a client from
380
+ // it is calling whatever that document is titled, and nothing in this
381
+ // tree implements it.
382
+ external := r.opts.externals[client.pkg]
383
+ if external == "" && client.external != "" {
384
+ external = client.external
385
+ if !r.warnedPeer[client.pkg] {
386
+ r.b.Warn(r.opts.svcID, "calls "+client.pkg+", which no peers line claims; read as calling "+external+", the system the document beside the client is titled after - add the package under `peers` if it is one of ours")
387
+ r.warnedPeer[client.pkg] = true
388
+ }
389
+ }
390
+ if external != "" {
391
+ return d.lane(catalog.Participant{ID: external, Kind: catalog.ParticipantExternal}), external, catalog.StatusDeclared
392
+ }
393
+
394
+ if !r.warnedPeer[client.pkg] {
395
+ r.b.Warn(r.opts.svcID, "calls "+client.pkg+" and the manifest names no peer for that package; add it under `peers` to say which service answers, or under `externals` when the far end is outside the estate, until then the calls are unresolved")
396
+ r.warnedPeer[client.pkg] = true
397
+ }
398
+
399
+ // A bare name for the lane, as for the store: a participant that is not
400
+ // a service must not carry a dot, which the diagram model reads as
401
+ // containment. The package stays on the label, and on the call.
402
+ lane = d.lane(catalog.Participant{
403
+ ID: strings.ReplaceAll(client.pkg, ".", "-"),
404
+ Kind: catalog.ParticipantUnknown,
405
+ Label: client.pkg,
406
+ })
407
+
408
+ return lane, client.pkg, catalog.StatusUnresolved
409
+ }
@@ -0,0 +1,266 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "testing"
7
+
8
+ "github.com/shortlink-org/portolan/catalog"
9
+ "github.com/shortlink-org/portolan/plugin"
10
+ )
11
+
12
+ const generatedClient = `package riskpb
13
+
14
+ import "google.golang.org/grpc"
15
+
16
+ const (
17
+ RiskService_Assess_FullMethodName = "/risk.v1.RiskService/Assess"
18
+ RiskService_Report_FullMethodName = "/risk.v1.RiskService/Report"
19
+ unrelated = "/not/a/method/name"
20
+ )
21
+
22
+ type RiskServiceClient interface {
23
+ Assess(ctx context.Context, in *AssessRequest, opts ...grpc.CallOption) (*AssessResponse, error)
24
+ Report(ctx context.Context, in *ReportRequest, opts ...grpc.CallOption) (*ReportResponse, error)
25
+ }
26
+ `
27
+
28
+ // The rpc is read off the constant protoc-gen-go-grpc writes for it, which is
29
+ // the one place in the tree the full name appears as a string.
30
+ func TestClientsAreReadOffTheGeneratedNames(t *testing.T) {
31
+ pkg, err := parseSource("risk_grpc.pb.go", generatedClient)
32
+ if err != nil {
33
+ t.Fatal(err)
34
+ }
35
+
36
+ clients, problem := readClients(pkg)
37
+ if problem != "" {
38
+ t.Fatal(problem)
39
+ }
40
+ client, ok := clients["RiskServiceClient"]
41
+ if !ok {
42
+ t.Fatalf("clients = %v", clients)
43
+ }
44
+ if client.pkg != "risk.v1" {
45
+ t.Errorf("pkg = %q", client.pkg)
46
+ }
47
+ if client.methods["Assess"] != "risk.v1.RiskService/Assess" || client.methods["Report"] != "risk.v1.RiskService/Report" {
48
+ t.Errorf("methods = %v", client.methods)
49
+ }
50
+ if len(client.methods) != 2 {
51
+ t.Errorf("methods = %v, want the two rpcs and not the unrelated constant", client.methods)
52
+ }
53
+ if client.source != "risk_grpc.pb.go" {
54
+ t.Errorf("source = %q", client.source)
55
+ }
56
+ }
57
+
58
+ // The peer is the manifest's to name. With a line for the package the step
59
+ // lands on a service; without one the far end is a package name, which the
60
+ // catalog does not have, and the step says so.
61
+ func TestThePeerIsTheManifestsToName(t *testing.T) {
62
+ client := client{pkg: "risk.v1", methods: map[string]string{"Assess": "risk.v1.RiskService/Assess"}, source: "gen/risk_grpc.pb.go"}
63
+
64
+ named := newFlowReader(t.TempDir(), flowOptions{svcID: "auth.auth", peers: map[string]string{"risk.v1": "shop.risk"}}, &plugin.Builder{})
65
+ d := newDraft()
66
+ named.rpcHop(d, rpcHop{client: client, method: "Assess"}, "usecase.go:12")
67
+
68
+ step := d.steps[0].(*catalog.Step)
69
+ if step.To != "shop.risk" || step.Kind != catalog.StepRPC || step.Ref != "risk.v1.RiskService/Assess" || step.Status != catalog.StatusDeclared {
70
+ t.Errorf("step = %+v", step)
71
+ }
72
+ if lane := d.lanes[0]; lane.Kind != catalog.ParticipantService || lane.Context == nil || *lane.Context != "shop" {
73
+ t.Errorf("lane = %+v", lane)
74
+ }
75
+ calls := named.consumes()
76
+ if len(calls) != 1 || calls[0].Peer != "shop.risk" || calls[0].Source != "gen/risk_grpc.pb.go" || calls[0].Status != catalog.StatusDeclared {
77
+ t.Errorf("consumes = %+v", calls)
78
+ }
79
+
80
+ b := &plugin.Builder{}
81
+ unnamed := newFlowReader(t.TempDir(), flowOptions{svcID: "auth.auth"}, b)
82
+ d = newDraft()
83
+ unnamed.rpcHop(d, rpcHop{client: client, method: "Assess"}, "")
84
+ unnamed.rpcHop(d, rpcHop{client: client, method: "Assess"}, "")
85
+
86
+ step = d.steps[0].(*catalog.Step)
87
+ if step.To != "risk-v1" || step.Status != catalog.StatusUnresolved {
88
+ t.Errorf("step = %+v", step)
89
+ }
90
+ if lane := d.lanes[0]; lane.Kind != catalog.ParticipantUnknown || lane.ID != "risk-v1" || lane.Label != "risk.v1" {
91
+ t.Errorf("lane = %+v, want a bare id with the package on the label", lane)
92
+ }
93
+ if calls := unnamed.consumes(); len(calls) != 1 || calls[0].Status != catalog.StatusUnresolved || calls[0].Peer != "risk.v1" {
94
+ t.Errorf("consumes = %+v", calls)
95
+ }
96
+ if n := len(b.Response().Warnings()); n != 1 {
97
+ t.Errorf("diagnostics = %d, want the one warning about the unnamed peer, said once", n)
98
+ }
99
+ }
100
+
101
+ // A policy on somebody else's event is still a policy. Its trigger has an id
102
+ // this tree cannot form, so the step names the type and resolves to nothing.
103
+ func TestAPolicyOnAForeignEventIsKeptAndUnresolved(t *testing.T) {
104
+ const policy = `package policy
105
+
106
+ import (
107
+ shopevents "github.com/acme/shop/events/v1"
108
+ "github.com/example/auth/internal/domain/user/event"
109
+ "github.com/example/auth/internal/pkg/helper"
110
+ )
111
+
112
+ type OnOrderPlaced struct{}
113
+
114
+ func (p OnOrderPlaced) Handle(ctx context.Context, e any) error {
115
+ placed, ok := e.(shopevents.OrderPlaced)
116
+ _ = placed
117
+ return nil
118
+ }
119
+
120
+ type OnPasswordChanged struct{}
121
+
122
+ func (p OnPasswordChanged) Handle(ctx context.Context, e any) error {
123
+ _, ok := e.(event.PasswordChanged)
124
+ return nil
125
+ }
126
+
127
+ type OnNothing struct{}
128
+
129
+ func (p OnNothing) Handle(ctx context.Context, e any) error {
130
+ _, ok := e.(helper.Thing)
131
+ return nil
132
+ }
133
+ `
134
+ pkg, err := parseSource("policy.go", policy)
135
+ if err != nil {
136
+ t.Fatal(err)
137
+ }
138
+ r := &flowReader{opts: flowOptions{svcID: "auth.auth"}, module: "github.com/example/auth"}
139
+ imports := importsOf(pkg)
140
+
141
+ foreign, ok := r.assertedEvent(pkg.methods("OnOrderPlaced")["Handle"], imports)
142
+ if !ok || foreign.foreign != "github.com/acme/shop/events/v1" || foreign.name != "OrderPlaced" || foreign.id != "" {
143
+ t.Errorf("foreign = %+v %v", foreign, ok)
144
+ }
145
+
146
+ own, ok := r.assertedEvent(pkg.methods("OnPasswordChanged")["Handle"], imports)
147
+ if !ok || own.foreign != "" || own.id != "auth.auth.user.PasswordChanged" {
148
+ t.Errorf("own = %+v %v", own, ok)
149
+ }
150
+
151
+ if ref, ok := r.assertedEvent(pkg.methods("OnNothing")["Handle"], imports); ok {
152
+ t.Errorf("a helper type of this module was read as an event: %+v", ref)
153
+ }
154
+ }
155
+
156
+ // The whole chain, on a tree: a use case holds a port of its own, assembly
157
+ // fills it with an adapter over a generated client, and the adapter's method
158
+ // is what says which rpc the port's method amounts to.
159
+ func TestAPortBoundToAClientIsARpcHop(t *testing.T) {
160
+ root := t.TempDir()
161
+ write := func(rel, contents string) {
162
+ t.Helper()
163
+ if err := os.MkdirAll(filepath.Dir(filepath.Join(root, rel)), 0o755); err != nil {
164
+ t.Fatal(err)
165
+ }
166
+ if err := os.WriteFile(filepath.Join(root, rel), []byte(contents), 0o644); err != nil {
167
+ t.Fatal(err)
168
+ }
169
+ }
170
+
171
+ write("go.mod", "module github.com/example/auth\n\ngo 1.27\n")
172
+ write("internal/infrastructure/risk/gen/riskpb/risk_grpc.pb.go", generatedClient)
173
+ write("internal/infrastructure/risk/client.go", `package risk
174
+
175
+ import (
176
+ "context"
177
+
178
+ "github.com/example/auth/internal/infrastructure/risk/gen/riskpb"
179
+ )
180
+
181
+ type Client struct {
182
+ rpc riskpb.RiskServiceClient
183
+ }
184
+
185
+ func New(rpc riskpb.RiskServiceClient) *Client { return &Client{rpc: rpc} }
186
+
187
+ func (c *Client) Verdict(ctx context.Context, userID string) (bool, error) {
188
+ out, err := c.rpc.Assess(ctx, &riskpb.AssessRequest{UserId: userID})
189
+ if err != nil {
190
+ return false, err
191
+ }
192
+ return out.Block, nil
193
+ }
194
+ `)
195
+ write("internal/di/provider/risk.go", `package provider
196
+
197
+ import (
198
+ "github.com/example/auth/internal/application/session/usecases/login"
199
+ "github.com/example/auth/internal/infrastructure/risk"
200
+ "github.com/example/auth/internal/infrastructure/risk/gen/riskpb"
201
+ )
202
+
203
+ func ProvideRisk(rpc riskpb.RiskServiceClient) login.Risk {
204
+ return risk.New(rpc)
205
+ }
206
+ `)
207
+ write("internal/application/session/usecases/login/usecase.go", `package login
208
+
209
+ import (
210
+ "context"
211
+
212
+ "github.com/example/auth/internal/domain/session"
213
+ )
214
+
215
+ type Risk interface {
216
+ Verdict(ctx context.Context, userID string) (bool, error)
217
+ }
218
+
219
+ type UseCase struct {
220
+ repo session.Repository
221
+ risk Risk
222
+ }
223
+
224
+ func (uc *UseCase) Handle(ctx context.Context, in dto.Input) error {
225
+ block, err := uc.risk.Verdict(ctx, in.UserID)
226
+ if err != nil {
227
+ return err
228
+ }
229
+ if block {
230
+ if err := uc.repo.Save(ctx, nil); err != nil {
231
+ return err
232
+ }
233
+ return ErrBlocked
234
+ }
235
+ return uc.repo.Save(ctx, nil)
236
+ }
237
+ `)
238
+
239
+ b := &plugin.Builder{}
240
+ r := newFlowReader(root, flowOptions{context: "auth", svcID: "auth.auth", service: "auth", store: "pg", peers: map[string]string{"risk.v1": "shop.risk"}}, b)
241
+ d := newDraft()
242
+ d.lane(r.serviceLane())
243
+ r.walkUseCase(d, "session/login", 0)
244
+
245
+ if got := dump(d.steps); got != "s1:Assess alt3{s2:Save } s4:Save " {
246
+ t.Fatalf("nodes = %q", got)
247
+ }
248
+ rpc := d.steps[0].(*catalog.Step)
249
+ if rpc.Kind != catalog.StepRPC || rpc.To != "shop.risk" || rpc.Ref != "risk.v1.RiskService/Assess" || rpc.Status != catalog.StatusDeclared {
250
+ t.Errorf("rpc step = %+v", rpc)
251
+ }
252
+ if rpc.Line != "internal/application/session/usecases/login/usecase.go:19" && filepath.Base(rpc.Line) == "" {
253
+ t.Errorf("line = %q", rpc.Line)
254
+ }
255
+ alt := d.steps[1].(*catalog.Alt)
256
+ if alt.Branches[0].Title != "block" || !alt.Branches[0].Terminal {
257
+ t.Errorf("arm = %+v", alt.Branches[0])
258
+ }
259
+ calls := r.consumes()
260
+ if len(calls) != 1 || calls[0].ID != "risk.v1.RiskService/Assess" || calls[0].Peer != "shop.risk" {
261
+ t.Errorf("consumes = %+v", calls)
262
+ }
263
+ if diags := b.Response().Warnings(); len(diags) != 0 {
264
+ t.Errorf("diagnostics = %+v", diags)
265
+ }
266
+ }
@@ -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-go",
15
+ Summary: "Reads a Go service with go/parser alone and answers with its domain: aggregates, events, use cases and the flows they imply.",
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
+ }