@shortlink-org/portolan 0.1.0 → 0.2.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 (381) hide show
  1. package/README.md +69 -205
  2. package/cli/init.mjs +285 -0
  3. package/cli/init.test.mjs +158 -0
  4. package/cli/portolan.mjs +37 -73
  5. package/index.html +19 -0
  6. package/package.json +5 -10
  7. package/plugins/README.md +66 -27
  8. package/plugins/portolan-go.wasm +0 -0
  9. package/portolan.json +52 -73
  10. package/public/landing/cats/bug-hunter.webp +0 -0
  11. package/public/landing/cats/compass-nap.webp +0 -0
  12. package/public/landing/cats/diagrammer.webp +0 -0
  13. package/public/landing/cats/docs-reader.webp +0 -0
  14. package/public/landing/cats/laptop.webp +0 -0
  15. package/public/landing/cats/map-inspector.webp +0 -0
  16. package/public/landing/cats/server-break.webp +0 -0
  17. package/public/landing/cats/star-mapper.webp +0 -0
  18. package/public/landing/cats/system-builder.webp +0 -0
  19. package/public/landing/cats/thread-tangle.webp +0 -0
  20. package/public/og.png +0 -0
  21. package/public/readme-header.webp +0 -0
  22. package/schema/portolan.schema.json +180 -0
  23. package/scripts/builtin-plugins.mjs +14 -0
  24. package/scripts/delivery-presets.mjs +353 -0
  25. package/scripts/gen.mjs +37 -4
  26. package/scripts/history.mjs +126 -0
  27. package/scripts/history.test.mjs +110 -0
  28. package/scripts/host-plugins/fetch-bsr.mjs +351 -0
  29. package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
  30. package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
  31. package/scripts/host-plugins/fetch-csr.mjs +386 -0
  32. package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
  33. package/scripts/host-plugins/fetch-git.mjs +379 -0
  34. package/scripts/host-plugins/fetch-git.test.mjs +210 -0
  35. package/scripts/local-api.mjs +22 -4
  36. package/scripts/local-api.test.mjs +84 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +6 -2
  39. package/scripts/plugin-host.mjs +77 -18
  40. package/scripts/plugin-host.test.mjs +50 -1
  41. package/scripts/plugin-wasm-worker.mjs +6 -3
  42. package/scripts/run-builtin.mjs +6 -26
  43. package/scripts/schema.mjs +6 -1
  44. package/src/app/App.tsx +24 -429
  45. package/src/app/Breadcrumbs.test.ts +7 -0
  46. package/src/app/Breadcrumbs.tsx +15 -2
  47. package/src/app/CatalogApp.tsx +436 -0
  48. package/src/app/Sidebar.tsx +0 -1
  49. package/src/chat/ChatPanel.tsx +98 -41
  50. package/src/chat/Composer.tsx +4 -2
  51. package/src/chat/Conversation.tsx +79 -15
  52. package/src/chat/Header.tsx +43 -9
  53. package/src/chat/Starter.tsx +64 -3
  54. package/src/chat/Steps.tsx +1 -1
  55. package/src/chat/flags.test.ts +51 -14
  56. package/src/chat/flags.ts +8 -2
  57. package/src/chat/page-context.test.ts +51 -0
  58. package/src/chat/page-context.ts +128 -0
  59. package/src/chat/prompt.test.ts +49 -1
  60. package/src/chat/prompt.ts +42 -1
  61. package/src/chat/tools.ts +7 -2
  62. package/src/chat/transport.ts +16 -4
  63. package/src/components/ProblemRow.tsx +182 -0
  64. package/src/data.ts +20 -5
  65. package/src/er/ErCanvas.tsx +1 -1
  66. package/src/graph/DependencyGraph.tsx +10 -1
  67. package/src/graph/FocusedEventGraph.tsx +1 -1
  68. package/src/graph/GraphToolbar.tsx +2 -4
  69. package/src/index.css +220 -2
  70. package/src/landing/DraggableReveal.tsx +168 -0
  71. package/src/landing/EstateGraph.tsx +28 -0
  72. package/src/landing/FlowPlayback.tsx +282 -0
  73. package/src/landing/HeroMap.tsx +128 -0
  74. package/src/landing/LandingPage.tsx +757 -0
  75. package/src/landing/ProductFrame.tsx +68 -0
  76. package/src/landing/ProductTour.tsx +371 -0
  77. package/src/landing/catalog.ts +25 -0
  78. package/src/landing/motion.tsx +52 -0
  79. package/src/lib/all-problems.ts +28 -0
  80. package/src/lib/local-api.ts +35 -1
  81. package/src/lib/setup-info.test.ts +8 -0
  82. package/src/lib/setup-info.ts +3 -2
  83. package/src/lib/source-code.ts +2 -1
  84. package/src/likec4/C4View.tsx +5 -0
  85. package/src/likec4/InteractiveView.tsx +5 -1
  86. package/src/map/ContextMapGraph.tsx +47 -20
  87. package/src/pages/ContextMap.tsx +5 -1
  88. package/src/pages/FlowDetail.tsx +24 -7
  89. package/src/pages/Overview.tsx +68 -12
  90. package/src/pages/Problems.tsx +6 -193
  91. package/src/pages/Settings.tsx +133 -108
  92. package/src/pages/settings/DeliverySettings.tsx +229 -0
  93. package/src/pages/settings/PreferencesSettings.tsx +101 -0
  94. package/src/routes.test.ts +20 -0
  95. package/src/routes.ts +13 -1
  96. package/vite.config.ts +12 -8
  97. package/catalog/enum_test.go +0 -46
  98. package/catalog/model.go +0 -932
  99. package/catalog/roundtrip_test.go +0 -185
  100. package/catalog/via_test.go +0 -38
  101. package/go.mod +0 -14
  102. package/go.sum +0 -14
  103. package/internal/commands/cargo.go +0 -154
  104. package/internal/commands/commands.go +0 -143
  105. package/internal/commands/commands_test.go +0 -323
  106. package/internal/commands/gradle.go +0 -77
  107. package/internal/commands/justfile.go +0 -82
  108. package/internal/commands/makefile.go +0 -106
  109. package/internal/commands/maven.go +0 -93
  110. package/internal/commands/packagejson.go +0 -159
  111. package/internal/commands/pyproject.go +0 -169
  112. package/internal/commands/taskfile.go +0 -101
  113. package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
  114. package/internal/commands/testdata/estate/Makefile +0 -33
  115. package/internal/commands/testdata/estate/Taskfile.yml +0 -21
  116. package/internal/commands/testdata/estate/build.gradle.kts +0 -21
  117. package/internal/commands/testdata/estate/justfile +0 -20
  118. package/internal/commands/testdata/estate/package.json +0 -13
  119. package/internal/commands/testdata/estate/pom.xml +0 -29
  120. package/internal/commands/testdata/estate/pyproject.toml +0 -27
  121. package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
  122. package/internal/commands/testdata/golden/commands.json +0 -305
  123. package/internal/gohttp/analyze.go +0 -2500
  124. package/internal/gohttp/endpoints.go +0 -1067
  125. package/internal/gohttp/roots.go +0 -320
  126. package/internal/gohttp/typed.go +0 -143
  127. package/internal/goscan/constants.go +0 -85
  128. package/internal/goscan/goscan_test.go +0 -227
  129. package/internal/goscan/names.go +0 -52
  130. package/internal/goscan/parse_test.go +0 -11
  131. package/internal/goscan/source.go +0 -36
  132. package/internal/goscan/tree.go +0 -155
  133. package/internal/goscan/types.go +0 -99
  134. package/internal/wsdl/ids.go +0 -127
  135. package/internal/wsdl/ids_test.go +0 -21
  136. package/internal/wsdl/model.go +0 -62
  137. package/internal/wsdl/parse.go +0 -920
  138. package/internal/wsdl/parse_test.go +0 -133
  139. package/plugin/describe.go +0 -107
  140. package/plugin/describe_test.go +0 -114
  141. package/plugin/protocol.go +0 -117
  142. package/plugin/schematest/schematest.go +0 -126
  143. package/plugins/extract-adr/describe.go +0 -19
  144. package/plugins/extract-adr/describe_test.go +0 -11
  145. package/plugins/extract-adr/extract.go +0 -153
  146. package/plugins/extract-adr/extract_test.go +0 -350
  147. package/plugins/extract-adr/history.go +0 -99
  148. package/plugins/extract-adr/main.go +0 -65
  149. package/plugins/extract-adr/parse.go +0 -642
  150. package/plugins/extract-adr/parse_test.go +0 -405
  151. package/plugins/extract-asyncapi/describe.go +0 -19
  152. package/plugins/extract-asyncapi/describe_test.go +0 -11
  153. package/plugins/extract-asyncapi/extract.go +0 -315
  154. package/plugins/extract-asyncapi/extract_test.go +0 -197
  155. package/plugins/extract-asyncapi/main.go +0 -48
  156. package/plugins/extract-asyncapi/spec.go +0 -151
  157. package/plugins/extract-commands/describe.go +0 -19
  158. package/plugins/extract-commands/describe_test.go +0 -11
  159. package/plugins/extract-commands/extract.go +0 -79
  160. package/plugins/extract-commands/extract_test.go +0 -92
  161. package/plugins/extract-commands/main.go +0 -55
  162. package/plugins/extract-csr/avro.go +0 -258
  163. package/plugins/extract-csr/describe.go +0 -19
  164. package/plugins/extract-csr/describe_test.go +0 -11
  165. package/plugins/extract-csr/extract.go +0 -338
  166. package/plugins/extract-csr/extract_test.go +0 -374
  167. package/plugins/extract-csr/jsonschema.go +0 -343
  168. package/plugins/extract-csr/lock.go +0 -25
  169. package/plugins/extract-csr/main.go +0 -84
  170. package/plugins/extract-csr/subject.go +0 -102
  171. package/plugins/extract-flows/describe.go +0 -19
  172. package/plugins/extract-flows/describe_test.go +0 -11
  173. package/plugins/extract-flows/extract.go +0 -91
  174. package/plugins/extract-flows/main.go +0 -45
  175. package/plugins/extract-flows/parse.go +0 -593
  176. package/plugins/extract-flows/parse_test.go +0 -204
  177. package/plugins/extract-glossary/describe.go +0 -19
  178. package/plugins/extract-glossary/describe_test.go +0 -11
  179. package/plugins/extract-glossary/extract.go +0 -115
  180. package/plugins/extract-glossary/extract_test.go +0 -220
  181. package/plugins/extract-glossary/main.go +0 -59
  182. package/plugins/extract-glossary/parse.go +0 -214
  183. package/plugins/extract-glossary/parse_test.go +0 -203
  184. package/plugins/extract-go/aggregate.go +0 -214
  185. package/plugins/extract-go/client.go +0 -409
  186. package/plugins/extract-go/client_test.go +0 -266
  187. package/plugins/extract-go/describe.go +0 -19
  188. package/plugins/extract-go/describe_test.go +0 -11
  189. package/plugins/extract-go/enum.go +0 -195
  190. package/plugins/extract-go/enum_test.go +0 -82
  191. package/plugins/extract-go/event.go +0 -99
  192. package/plugins/extract-go/extract.go +0 -191
  193. package/plugins/extract-go/extract_test.go +0 -261
  194. package/plugins/extract-go/flow.go +0 -1441
  195. package/plugins/extract-go/flow_test.go +0 -609
  196. package/plugins/extract-go/httpclient.go +0 -174
  197. package/plugins/extract-go/httpclient_test.go +0 -296
  198. package/plugins/extract-go/ids.go +0 -92
  199. package/plugins/extract-go/layout.go +0 -225
  200. package/plugins/extract-go/layout_test.go +0 -125
  201. package/plugins/extract-go/lifecycle.go +0 -324
  202. package/plugins/extract-go/lifecycle_test.go +0 -108
  203. package/plugins/extract-go/main.go +0 -79
  204. package/plugins/extract-go/operation.go +0 -157
  205. package/plugins/extract-go/source.go +0 -316
  206. package/plugins/extract-go/transport.go +0 -321
  207. package/plugins/extract-go/transport_test.go +0 -145
  208. package/plugins/extract-go/wiring.go +0 -434
  209. package/plugins/extract-go-nats/describe.go +0 -19
  210. package/plugins/extract-go-nats/describe_test.go +0 -11
  211. package/plugins/extract-go-nats/extract.go +0 -177
  212. package/plugins/extract-go-nats/extract_test.go +0 -389
  213. package/plugins/extract-go-nats/index.go +0 -379
  214. package/plugins/extract-go-nats/main.go +0 -42
  215. package/plugins/extract-go-nats/resolve.go +0 -161
  216. package/plugins/extract-go-nats/sites.go +0 -224
  217. package/plugins/extract-graphql/describe.go +0 -19
  218. package/plugins/extract-graphql/describe_test.go +0 -11
  219. package/plugins/extract-graphql/extract.go +0 -433
  220. package/plugins/extract-graphql/extract_test.go +0 -256
  221. package/plugins/extract-graphql/ids.go +0 -49
  222. package/plugins/extract-graphql/lex.go +0 -237
  223. package/plugins/extract-graphql/main.go +0 -51
  224. package/plugins/extract-graphql/parse.go +0 -621
  225. package/plugins/extract-graphql/parse_test.go +0 -122
  226. package/plugins/extract-http-clients/describe.go +0 -19
  227. package/plugins/extract-http-clients/describe_test.go +0 -11
  228. package/plugins/extract-http-clients/extract.go +0 -705
  229. package/plugins/extract-http-clients/extract_test.go +0 -1263
  230. package/plugins/extract-http-clients/main.go +0 -42
  231. package/plugins/extract-openapi/describe.go +0 -19
  232. package/plugins/extract-openapi/describe_test.go +0 -11
  233. package/plugins/extract-openapi/discover.go +0 -243
  234. package/plugins/extract-openapi/extract.go +0 -526
  235. package/plugins/extract-openapi/extract_test.go +0 -545
  236. package/plugins/extract-openapi/main.go +0 -75
  237. package/plugins/extract-openapi/spec.go +0 -350
  238. package/plugins/extract-project/describe.go +0 -19
  239. package/plugins/extract-project/describe_test.go +0 -11
  240. package/plugins/extract-project/extract.go +0 -221
  241. package/plugins/extract-project/extract_test.go +0 -109
  242. package/plugins/extract-project/main.go +0 -41
  243. package/plugins/extract-proto/ast.go +0 -125
  244. package/plugins/extract-proto/consumes.go +0 -77
  245. package/plugins/extract-proto/describe.go +0 -19
  246. package/plugins/extract-proto/extract.go +0 -293
  247. package/plugins/extract-proto/extract_test.go +0 -459
  248. package/plugins/extract-proto/ids.go +0 -89
  249. package/plugins/extract-proto/ids_test.go +0 -57
  250. package/plugins/extract-proto/lex.go +0 -285
  251. package/plugins/extract-proto/main.go +0 -100
  252. package/plugins/extract-proto/module.go +0 -120
  253. package/plugins/extract-proto/parse.go +0 -720
  254. package/plugins/extract-proto/parse_test.go +0 -307
  255. package/plugins/extract-proto/provides.go +0 -236
  256. package/plugins/extract-proto/resolve.go +0 -222
  257. package/plugins/extract-proto/resolve_test.go +0 -119
  258. package/plugins/extract-redis/describe.go +0 -19
  259. package/plugins/extract-redis/describe_test.go +0 -11
  260. package/plugins/extract-redis/extract.go +0 -183
  261. package/plugins/extract-redis/extract_test.go +0 -168
  262. package/plugins/extract-redis/keyspaces.go +0 -469
  263. package/plugins/extract-redis/main.go +0 -44
  264. package/plugins/extract-river/describe.go +0 -19
  265. package/plugins/extract-river/describe_test.go +0 -11
  266. package/plugins/extract-river/extract.go +0 -507
  267. package/plugins/extract-river/extract_test.go +0 -132
  268. package/plugins/extract-river/main.go +0 -42
  269. package/plugins/extract-sql/ddl.go +0 -893
  270. package/plugins/extract-sql/ddl_test.go +0 -401
  271. package/plugins/extract-sql/describe.go +0 -19
  272. package/plugins/extract-sql/describe_test.go +0 -11
  273. package/plugins/extract-sql/layout.go +0 -221
  274. package/plugins/extract-sql/layout_test.go +0 -55
  275. package/plugins/extract-sql/lineage.go +0 -107
  276. package/plugins/extract-sql/main.go +0 -141
  277. package/plugins/extract-sql/maps.go +0 -564
  278. package/plugins/extract-sql/maps_java.go +0 -117
  279. package/plugins/extract-sql/maps_rust.go +0 -333
  280. package/plugins/extract-sql/maps_rust_test.go +0 -70
  281. package/plugins/extract-sql/maps_test.go +0 -204
  282. package/plugins/extract-sql/maps_ts.go +0 -398
  283. package/plugins/extract-sql/maps_ts_test.go +0 -136
  284. package/plugins/extract-sql/projection.go +0 -70
  285. package/plugins/extract-sql/projection_test.go +0 -50
  286. package/plugins/extract-sql/store.go +0 -420
  287. package/plugins/extract-sql/store_test.go +0 -233
  288. package/plugins/extract-sql/view.go +0 -295
  289. package/plugins/extract-watermill/describe.go +0 -19
  290. package/plugins/extract-watermill/describe_test.go +0 -11
  291. package/plugins/extract-watermill/extract.go +0 -1228
  292. package/plugins/extract-watermill/extract_test.go +0 -234
  293. package/plugins/extract-watermill/main.go +0 -41
  294. package/plugins/extract-wsdl/describe.go +0 -19
  295. package/plugins/extract-wsdl/describe_test.go +0 -11
  296. package/plugins/extract-wsdl/extract.go +0 -148
  297. package/plugins/extract-wsdl/extract_test.go +0 -45
  298. package/plugins/extract-wsdl/main.go +0 -53
  299. package/plugins/fetch-bsr/auth.go +0 -114
  300. package/plugins/fetch-bsr/bsr.go +0 -240
  301. package/plugins/fetch-bsr/cache.go +0 -66
  302. package/plugins/fetch-bsr/describe.go +0 -19
  303. package/plugins/fetch-bsr/fetch.go +0 -192
  304. package/plugins/fetch-bsr/fetch_test.go +0 -465
  305. package/plugins/fetch-bsr/lock.go +0 -71
  306. package/plugins/fetch-bsr/main.go +0 -117
  307. package/plugins/fetch-csr/auth.go +0 -95
  308. package/plugins/fetch-csr/cache.go +0 -69
  309. package/plugins/fetch-csr/describe.go +0 -19
  310. package/plugins/fetch-csr/describe_test.go +0 -11
  311. package/plugins/fetch-csr/fetch.go +0 -254
  312. package/plugins/fetch-csr/fetch_test.go +0 -494
  313. package/plugins/fetch-csr/lock.go +0 -95
  314. package/plugins/fetch-csr/main.go +0 -122
  315. package/plugins/fetch-csr/registry.go +0 -209
  316. package/plugins/fetch-git/cache.go +0 -65
  317. package/plugins/fetch-git/describe.go +0 -19
  318. package/plugins/fetch-git/describe_test.go +0 -11
  319. package/plugins/fetch-git/fetch.go +0 -181
  320. package/plugins/fetch-git/fetch_test.go +0 -332
  321. package/plugins/fetch-git/git.go +0 -169
  322. package/plugins/fetch-git/lock.go +0 -72
  323. package/plugins/fetch-git/main.go +0 -127
  324. package/plugins/fetch-git/offline.go +0 -39
  325. package/plugins/fetch-git/pin.go +0 -90
  326. package/plugins/fetch-git/pin_test.go +0 -74
  327. package/plugins/gen-backstage/describe.go +0 -17
  328. package/plugins/gen-backstage/main.go +0 -22
  329. package/plugins/gen-backstage/plugin.go +0 -473
  330. package/plugins/gen-backstage/plugin_test.go +0 -145
  331. package/plugins/gen-backstage.wasm +0 -0
  332. package/plugins/gen-markdown/adr.go +0 -162
  333. package/plugins/gen-markdown/aggregate.go +0 -362
  334. package/plugins/gen-markdown/canonical.go +0 -93
  335. package/plugins/gen-markdown/context.go +0 -90
  336. package/plugins/gen-markdown/coverage_test.go +0 -89
  337. package/plugins/gen-markdown/describe.go +0 -19
  338. package/plugins/gen-markdown/describe_test.go +0 -11
  339. package/plugins/gen-markdown/external.go +0 -71
  340. package/plugins/gen-markdown/flow.go +0 -277
  341. package/plugins/gen-markdown/glossary.go +0 -76
  342. package/plugins/gen-markdown/glossary_test.go +0 -148
  343. package/plugins/gen-markdown/llms.go +0 -302
  344. package/plugins/gen-markdown/main.go +0 -22
  345. package/plugins/gen-markdown/markdown.go +0 -254
  346. package/plugins/gen-markdown/markdown_test.go +0 -100
  347. package/plugins/gen-markdown/module.go +0 -107
  348. package/plugins/gen-markdown/plugin.go +0 -39
  349. package/plugins/gen-markdown/quality_test.go +0 -176
  350. package/plugins/gen-markdown/redis_test.go +0 -38
  351. package/plugins/gen-markdown/render.go +0 -344
  352. package/plugins/gen-markdown/render_test.go +0 -186
  353. package/plugins/gen-markdown/service.go +0 -456
  354. package/plugins/gen-markdown/source.go +0 -177
  355. package/plugins/gen-markdown/store.go +0 -201
  356. package/plugins/gen-markdown.wasm +0 -0
  357. package/plugins/gen-mermaid/describe.go +0 -17
  358. package/plugins/gen-mermaid/main.go +0 -22
  359. package/plugins/gen-mermaid/plugin.go +0 -104
  360. package/plugins/gen-mermaid/plugin_test.go +0 -33
  361. package/plugins/gen-mermaid.wasm +0 -0
  362. package/plugins/openapi/ids.go +0 -261
  363. package/plugins/openapi/ids_test.go +0 -98
  364. package/plugins/verify-codeowners/describe.go +0 -19
  365. package/plugins/verify-codeowners/describe_test.go +0 -11
  366. package/plugins/verify-codeowners/main.go +0 -75
  367. package/plugins/verify-codeowners/match.go +0 -85
  368. package/plugins/verify-codeowners/match_test.go +0 -47
  369. package/plugins/verify-codeowners/owners.go +0 -164
  370. package/plugins/verify-codeowners/owners_test.go +0 -225
  371. package/plugins/verify-codeowners/parse.go +0 -90
  372. package/plugins/verify-codeowners/parse_test.go +0 -62
  373. package/plugins/verify-otel/describe.go +0 -19
  374. package/plugins/verify-otel/describe_test.go +0 -11
  375. package/plugins/verify-otel/main.go +0 -53
  376. package/plugins/verify-otel/match.go +0 -336
  377. package/plugins/verify-otel/otlp.go +0 -200
  378. package/plugins/verify-otel/verify.go +0 -734
  379. package/plugins/verify-otel/verify_test.go +0 -460
  380. /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
  381. /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
@@ -1,434 +0,0 @@
1
- package main
2
-
3
- import (
4
- "go/ast"
5
- "go/types"
6
- "strings"
7
- )
8
-
9
- // portBindings reads internal/di/provider for the one thing a use case cannot
10
- // say about itself: which other use case fills a port it declares.
11
- //
12
- // A use case states its need as an interface of its own - login.Authenticator -
13
- // precisely so that it does not import the package that satisfies it. The
14
- // binding therefore exists nowhere in either of them; it exists in assembly,
15
- // as a provider whose result is the port and whose parameter is the use case:
16
- //
17
- // func ProvideAuthenticator(uc *authenticate.UseCase) login.Authenticator
18
- //
19
- // Result and parameter are both written in the signature, so for a port filled
20
- // by one use case this reads the declaration and never the body.
21
- //
22
- // A port can also be filled by several use cases at once - one per method,
23
- // with an adapter in between:
24
- //
25
- // func ProvideLockout(check *check.UseCase, failed *record_failure.UseCase, ...) authenticate.Lockout {
26
- // return lockoutAdapter{check: check, failed: failed, ...}
27
- // }
28
- // func (l lockoutAdapter) Failed(ctx context.Context, id string) error {
29
- // return l.failed.Handle(ctx, ...)
30
- // }
31
- //
32
- // The signature alone cannot say which method reaches which use case, so
33
- // these are read off the adapter: the struct the provider returns, and which
34
- // field's Handle each of its methods calls. Such a binding is keyed by port
35
- // and method, "user/authenticate.Lockout.Failed", beside the port-level key
36
- // the single-use-case case produces. An adapter this cannot read falls back to
37
- // the first use case in the signature, which is the reading it always had.
38
- //
39
- // A provider that builds its port out of something else is not a binding
40
- // between use cases and does not appear here.
41
- func portBindings(root string, layouts ...sourceLayout) map[string]string {
42
- out := map[string]string{}
43
-
44
- packages := goPackageDirs(root, "internal")
45
- if len(layouts) > 0 {
46
- packages = layouts[0].packages
47
- }
48
-
49
- for _, dir := range packages {
50
- pkg, err := parsePkg(root, dir)
51
- if err != nil {
52
- continue
53
- }
54
- for _, file := range pkg.files {
55
- useCases := useCaseImports(file, layouts...)
56
- if len(useCases) == 0 {
57
- continue
58
- }
59
-
60
- for _, decl := range file.Decls {
61
- fn, ok := decl.(*ast.FuncDecl)
62
- if !ok || fn.Recv != nil || fn.Type.Results == nil {
63
- continue
64
- }
65
- if len(fn.Type.Results.List) != 1 {
66
- continue
67
- }
68
-
69
- port, ok := portName(fn.Type.Results.List[0].Type, useCases)
70
- if !ok {
71
- continue
72
- }
73
-
74
- var bound []string
75
- for _, param := range params(fn) {
76
- if useCase, ok := useCaseParam(param, useCases); ok {
77
- bound = append(bound, useCase)
78
- }
79
- }
80
- if len(bound) == 0 {
81
- continue
82
- }
83
-
84
- if len(bound) > 1 {
85
- perMethod := methodBindings(file, fn, useCases)
86
- for method, useCase := range perMethod {
87
- out[port+"."+method] = useCase
88
- }
89
- if len(perMethod) > 0 {
90
- continue
91
- }
92
- }
93
-
94
- out[port] = bound[0]
95
- }
96
- }
97
- }
98
-
99
- return out
100
- }
101
-
102
- // methodBindings reads which use case each method of a provider's adapter
103
- // reaches: the struct the provider returns, its fields' types, and the field
104
- // whose Handle each method calls. Empty when any of those cannot be read.
105
- func methodBindings(file *ast.File, provider *ast.FuncDecl, useCases map[string]string) map[string]string {
106
- adapter := returnedType(provider)
107
- if adapter == "" {
108
- return nil
109
- }
110
-
111
- fields := adapterFields(file, adapter)
112
- out := map[string]string{}
113
-
114
- for method, field := range handleCalls(file, adapter) {
115
- if useCase, ok := useCaseParam(fields[field], useCases); ok {
116
- out[method] = useCase
117
- }
118
- }
119
-
120
- return out
121
- }
122
-
123
- // returnedType names the struct a provider returns as a composite literal,
124
- // `return lockoutAdapter{...}` or `return &lockoutAdapter{...}`. Anything else
125
- // - a call, a variable - is not read.
126
- func returnedType(fn *ast.FuncDecl) string {
127
- if fn.Body == nil {
128
- return ""
129
- }
130
-
131
- name := ""
132
- ast.Inspect(fn.Body, func(n ast.Node) bool {
133
- ret, ok := n.(*ast.ReturnStmt)
134
- if !ok || len(ret.Results) != 1 || name != "" {
135
- return true
136
- }
137
-
138
- expr := ret.Results[0]
139
- if unary, ok := expr.(*ast.UnaryExpr); ok {
140
- expr = unary.X
141
- }
142
- if lit, ok := expr.(*ast.CompositeLit); ok {
143
- if ident, ok := lit.Type.(*ast.Ident); ok {
144
- name = ident.Name
145
- }
146
- }
147
-
148
- return true
149
- })
150
-
151
- return name
152
- }
153
-
154
- // adapterFields reads the field types of a struct declared in the file.
155
- func adapterFields(file *ast.File, typeName string) map[string]ast.Expr {
156
- out := map[string]ast.Expr{}
157
-
158
- for _, decl := range file.Decls {
159
- gen, ok := decl.(*ast.GenDecl)
160
- if !ok {
161
- continue
162
- }
163
- for _, spec := range gen.Specs {
164
- ts, ok := spec.(*ast.TypeSpec)
165
- if !ok || ts.Name.Name != typeName {
166
- continue
167
- }
168
- st, ok := ts.Type.(*ast.StructType)
169
- if !ok {
170
- continue
171
- }
172
- for _, field := range st.Fields.List {
173
- for _, name := range field.Names {
174
- out[name.Name] = field.Type
175
- }
176
- }
177
- }
178
- }
179
-
180
- return out
181
- }
182
-
183
- // handleCalls reads, for every method on typeName in the file, the field whose
184
- // Handle it calls: `l.failed.Handle(ctx, ...)` gives Failed -> "failed". A
185
- // method that calls Handle on two fields is read as the first; none here does.
186
- func handleCalls(file *ast.File, typeName string) map[string]string {
187
- out := map[string]string{}
188
-
189
- for _, decl := range file.Decls {
190
- fn, ok := decl.(*ast.FuncDecl)
191
- if !ok || fn.Recv == nil || fn.Body == nil || receiverTypeName(fn) != typeName {
192
- continue
193
- }
194
- recv := receiverIdent(fn)
195
- if recv == "" {
196
- continue
197
- }
198
-
199
- ast.Inspect(fn.Body, func(n ast.Node) bool {
200
- call, ok := n.(*ast.CallExpr)
201
- if !ok {
202
- return true
203
- }
204
- sel, ok := call.Fun.(*ast.SelectorExpr)
205
- if !ok || sel.Sel.Name != "Handle" {
206
- return true
207
- }
208
- field, ok := sel.X.(*ast.SelectorExpr)
209
- if !ok {
210
- return true
211
- }
212
- base, ok := field.X.(*ast.Ident)
213
- if !ok || base.Name != recv {
214
- return true
215
- }
216
- if _, seen := out[fn.Name.Name]; !seen {
217
- out[fn.Name.Name] = field.Sel.Name
218
- }
219
-
220
- return true
221
- })
222
- }
223
-
224
- return out
225
- }
226
-
227
- // receiverTypeName reads `lockoutAdapter` off `(l lockoutAdapter)` and
228
- // `(l *lockoutAdapter)` alike.
229
- func receiverTypeName(fn *ast.FuncDecl) string {
230
- if fn.Recv == nil || len(fn.Recv.List) == 0 {
231
- return ""
232
- }
233
-
234
- expr := fn.Recv.List[0].Type
235
- if star, ok := expr.(*ast.StarExpr); ok {
236
- expr = star.X
237
- }
238
- if ident, ok := expr.(*ast.Ident); ok {
239
- return ident.Name
240
- }
241
-
242
- return ""
243
- }
244
-
245
- // portName turns the result type `login.Authenticator` into the key the use
246
- // case's own field carries: "session/login.Authenticator". The field is written
247
- // unqualified there - it is declared in that package - so the use case has to
248
- // be part of the key or two ports of the same name would collide.
249
- func portName(expr ast.Expr, useCases map[string]string) (string, bool) {
250
- selector, name, found := strings.Cut(types.ExprString(expr), ".")
251
- if !found {
252
- return "", false
253
- }
254
-
255
- useCase, known := useCases[selector]
256
- if !known {
257
- return "", false
258
- }
259
-
260
- return useCase + "." + name, true
261
- }
262
-
263
- // useCaseParam reads `*authenticate.UseCase` back to "user/authenticate".
264
- func useCaseParam(expr ast.Expr, useCases map[string]string) (string, bool) {
265
- selector, name, found := strings.Cut(strings.TrimPrefix(types.ExprString(expr), "*"), ".")
266
- if !found || name != "UseCase" {
267
- return "", false
268
- }
269
-
270
- useCase, known := useCases[selector]
271
-
272
- return useCase, known
273
- }
274
-
275
- // adapterDecl is a provider whose result is a port of a use case and whose
276
- // parameters are not another use case: whatever it builds the port out of,
277
- // the flow reader will have to follow.
278
- type adapterDecl struct {
279
- pkg *pkg
280
- fn *ast.FuncDecl
281
- imports map[string]string
282
- }
283
-
284
- // adapterBindings reads the same directory for the other kind of binding: a
285
- // port filled by an adapter over something that is not a use case - a
286
- // generated client, say. What that something is, and what the adapter does
287
- // with it, is left to the reader that follows it; here the declaration is
288
- // enough to know which provider to follow.
289
- func adapterBindings(root string, layouts ...sourceLayout) map[string]adapterDecl {
290
- out := map[string]adapterDecl{}
291
- packages := goPackageDirs(root, "internal")
292
- if len(layouts) > 0 {
293
- packages = layouts[0].packages
294
- }
295
-
296
- for _, dir := range packages {
297
- pkg, err := parsePkg(root, dir)
298
- if err != nil {
299
- continue
300
- }
301
- for _, file := range pkg.files {
302
- useCases := useCaseImports(file, layouts...)
303
- if len(useCases) == 0 {
304
- continue
305
- }
306
- imports := map[string]string{}
307
- for _, spec := range file.Imports {
308
- importPath := strings.Trim(spec.Path.Value, `"`)
309
- name := importPath[strings.LastIndex(importPath, "/")+1:]
310
- if spec.Name != nil {
311
- name = spec.Name.Name
312
- }
313
- imports[name] = importPath
314
- }
315
-
316
- for _, decl := range file.Decls {
317
- fn, ok := decl.(*ast.FuncDecl)
318
- if !ok || fn.Recv != nil || fn.Type.Results == nil || len(fn.Type.Results.List) != 1 {
319
- continue
320
- }
321
- port, ok := portName(fn.Type.Results.List[0].Type, useCases)
322
- if !ok {
323
- continue
324
- }
325
- bound := false
326
- for _, param := range params(fn) {
327
- if _, ok := useCaseParam(param, useCases); ok {
328
- bound = true
329
- }
330
- }
331
- if bound {
332
- continue
333
- }
334
- out[port] = adapterDecl{pkg: pkg, fn: fn, imports: imports}
335
- }
336
- }
337
- }
338
-
339
- return out
340
- }
341
-
342
- // wireBoundPorts reads direct Wire bindings from a use case's local interface
343
- // to a concrete adapter:
344
- //
345
- // wire.Bind(new(register.PasswordHasher), new(*password.Hasher))
346
- //
347
- // Unlike a provider function, this binding has no body to follow. It still
348
- // proves that the port is deliberate local work rather than a missing domain
349
- // or use-case binding, so the flow reader can omit the call without warning.
350
- func wireBoundPorts(root string, layouts ...sourceLayout) map[string]bool {
351
- out := map[string]bool{}
352
- packages := goPackageDirs(root, "internal")
353
- if len(layouts) > 0 {
354
- packages = layouts[0].packages
355
- }
356
-
357
- for _, dir := range packages {
358
- pkg, err := parsePkg(root, dir)
359
- if err != nil {
360
- continue
361
- }
362
- for _, file := range pkg.files {
363
- useCases := useCaseImports(file, layouts...)
364
- if len(useCases) == 0 {
365
- continue
366
- }
367
-
368
- wireNames := map[string]bool{}
369
- for _, spec := range file.Imports {
370
- if strings.Trim(spec.Path.Value, `"`) != "github.com/google/wire" {
371
- continue
372
- }
373
- name := "wire"
374
- if spec.Name != nil {
375
- name = spec.Name.Name
376
- }
377
- wireNames[name] = true
378
- }
379
-
380
- ast.Inspect(file, func(node ast.Node) bool {
381
- call, ok := node.(*ast.CallExpr)
382
- if !ok || len(call.Args) < 2 {
383
- return true
384
- }
385
- sel, ok := call.Fun.(*ast.SelectorExpr)
386
- if !ok || sel.Sel.Name != "Bind" {
387
- return true
388
- }
389
- owner, ok := sel.X.(*ast.Ident)
390
- if !ok || !wireNames[owner.Name] {
391
- return true
392
- }
393
-
394
- bound, ok := newArgument(call.Args[0])
395
- if !ok {
396
- return true
397
- }
398
- if port, ok := portName(bound, useCases); ok {
399
- out[port] = true
400
- }
401
-
402
- return true
403
- })
404
- }
405
- }
406
-
407
- return out
408
- }
409
-
410
- func newArgument(expr ast.Expr) (ast.Expr, bool) {
411
- call, ok := expr.(*ast.CallExpr)
412
- if !ok || len(call.Args) != 1 {
413
- return nil, false
414
- }
415
- name, ok := call.Fun.(*ast.Ident)
416
- if !ok || name.Name != "new" {
417
- return nil, false
418
- }
419
-
420
- return call.Args[0], true
421
- }
422
-
423
- func params(fn *ast.FuncDecl) []ast.Expr {
424
- if fn.Type.Params == nil {
425
- return nil
426
- }
427
-
428
- var out []ast.Expr
429
- for _, param := range fn.Type.Params.List {
430
- out = append(out, param.Type)
431
- }
432
-
433
- return out
434
- }
@@ -1,19 +0,0 @@
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-nats",
15
- Summary: "Reads nats.go and JetStream calls, through the port that wraps them, into the subjects a service listens on and publishes to.",
16
- Phases: []string{plugin.PhaseExtract},
17
- Options: optionsSchema,
18
- }
19
- }
@@ -1,11 +0,0 @@
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
- }
@@ -1,177 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "go/ast"
6
- "sort"
7
- "strings"
8
-
9
- "github.com/shortlink-org/portolan/catalog"
10
- "github.com/shortlink-org/portolan/internal/goscan"
11
- "github.com/shortlink-org/portolan/plugin"
12
- )
13
-
14
- func extract(in plugin.Input, opts Options) (plugin.Response, error) {
15
- b := &plugin.Builder{}
16
- tree, err := goscan.Read(in.Root)
17
- if err != nil {
18
- return plugin.Response{}, err
19
- }
20
- s := newScanner(tree)
21
- s.index()
22
-
23
- sites := s.sites()
24
- if len(sites) == 0 {
25
- b.Warn(in.Root, "no nats.go or JetStream call was found")
26
- }
27
- channels := s.catalog(sites, b)
28
-
29
- serviceID := opts.Context + "." + opts.Service
30
- fragment := catalog.Catalog{
31
- GeneratedAt: in.GeneratedAt,
32
- Commit: in.Commit,
33
- Contexts: []catalog.BoundedContext{{
34
- ID: opts.Context,
35
- Slug: opts.Context,
36
- Services: []catalog.Service{{
37
- ID: serviceID,
38
- Slug: opts.Service,
39
- Provides: []catalog.RpcService{},
40
- Consumes: []catalog.RpcCall{},
41
- Aggregates: []catalog.Aggregate{},
42
- Channels: channels,
43
- }},
44
- }},
45
- Defs: map[string]catalog.TypeDef{},
46
- Flows: []catalog.Flow{},
47
- Adrs: []catalog.Adr{},
48
- }
49
- encoded, err := json.MarshalIndent(fragment, "", " ")
50
- if err != nil {
51
- return plugin.Response{}, err
52
- }
53
- b.File(goscan.FirstNonEmpty(opts.Out, "nats.json"), string(encoded)+"\n")
54
- return b.Response(), nil
55
- }
56
-
57
- // channelState is one subject as the sites describe it, gathered before it
58
- // is written out: which way messages go from where, and what they are
59
- // called when a port said.
60
- type channelState struct {
61
- address string
62
- api string
63
- sources []string
64
- notes map[string]bool
65
- messages map[string]catalog.ChannelMessage
66
- }
67
-
68
- // catalog turns the sites into channels. A site whose subject cannot be
69
- // followed to a string is a warning at the call, not a channel with a hole
70
- // in it.
71
- func (s *scanner) catalog(sites []site, b *plugin.Builder) []catalog.Channel {
72
- states := map[string]*channelState{}
73
- for _, found := range sites {
74
- if len(found.subjects) == 0 {
75
- why := "the message was not built with nats.NewMsg or &nats.Msg{Subject: ...} in this function"
76
- if found.configured {
77
- why = "a consumer with no filter subject reads whatever its stream holds"
78
- }
79
- b.Warn(found.at.String(), found.method+" names no subject this reader can see: "+why)
80
- continue
81
- }
82
- for _, subject := range found.subjects {
83
- values := s.resolve(subject, found.fn, 0, map[string]bool{})
84
- if len(values) == 0 {
85
- b.Warn(found.at.String(), "subject of "+found.method+" could not be resolved to a literal, a constant, a config default or a caller's argument")
86
- continue
87
- }
88
- for _, value := range values {
89
- state := states[value.value]
90
- if state == nil {
91
- state = &channelState{address: value.value, api: found.api, notes: map[string]bool{}, messages: map[string]catalog.ChannelMessage{}}
92
- states[value.value] = state
93
- }
94
- state.sources = append(state.sources, value.at.String())
95
- state.notes[s.note(found)] = true
96
- if value.name != "" {
97
- state.messages[string(found.direction)+" "+value.name] = catalog.ChannelMessage{Name: value.name, Title: value.name, Direction: found.direction}
98
- }
99
- }
100
- }
101
- }
102
-
103
- addresses := make([]string, 0, len(states))
104
- for address := range states {
105
- addresses = append(addresses, address)
106
- }
107
- sort.Strings(addresses)
108
- out := make([]catalog.Channel, 0, len(addresses))
109
- for _, address := range addresses {
110
- state := states[address]
111
- sort.Strings(state.sources)
112
- notes := make([]string, 0, len(state.notes))
113
- for note := range state.notes {
114
- notes = append(notes, note)
115
- }
116
- sort.Strings(notes)
117
- keys := make([]string, 0, len(state.messages))
118
- for key := range state.messages {
119
- keys = append(keys, key)
120
- }
121
- sort.Strings(keys)
122
- messages := make([]catalog.ChannelMessage, 0, len(keys))
123
- for _, key := range keys {
124
- messages = append(messages, state.messages[key])
125
- }
126
- out = append(out, catalog.Channel{
127
- Address: address,
128
- Kind: catalog.ChannelKindEvent,
129
- Title: state.api + " subject",
130
- Doc: "Read through " + natsPkg + ". " + strings.Join(notes, " "),
131
- Messages: messages,
132
- Source: state.sources[0],
133
- })
134
- }
135
- return out
136
- }
137
-
138
- // note is one sentence on a site: which way, from where, and what JetStream
139
- // was told about the consumer.
140
- func (s *scanner) note(found site) string {
141
- verb := "Subscribed"
142
- if found.direction == catalog.ChannelSend {
143
- verb = "Published"
144
- }
145
- text := verb + " by `" + goscan.LastSegment(found.fn.receiver) + dot(found.fn.receiver) + found.fn.name + "`"
146
- if found.api == "JetStream" {
147
- text += " over JetStream"
148
- }
149
- if durable := s.literal(found.durable, found.fn); durable != "" {
150
- text += ", durable consumer `" + durable + "`"
151
- }
152
- if queue := s.literal(found.queue, found.fn); queue != "" {
153
- text += ", queue group `" + queue + "`"
154
- }
155
- return text + "."
156
- }
157
-
158
- func dot(receiver string) string {
159
- if receiver == "" {
160
- return ""
161
- }
162
- return "."
163
- }
164
-
165
- // literal is a string an expression is worth without leaving the function:
166
- // a durable name or queue group is documentation, and following it up the
167
- // callers would be more than it is worth.
168
- func (s *scanner) literal(expr ast.Expr, fn *function) string {
169
- if expr == nil {
170
- return ""
171
- }
172
- values := s.resolve(expr, fn, hops, map[string]bool{})
173
- if len(values) != 1 {
174
- return ""
175
- }
176
- return values[0].value
177
- }