@shortlink-org/portolan 0.1.0 → 0.2.1

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 +27 -10
  36. package/scripts/local-api.test.mjs +85 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +24 -3
  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 +17 -9
  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,222 +0,0 @@
1
- package main
2
-
3
- // Turning a name as written into the thing it names.
4
- //
5
- // A proto reference is relative to where it was written: `Money` inside
6
- // `shop.v1.Order` might mean `shop.v1.Order.Money`, `shop.v1.Money`, or
7
- // `Money` at the root, and proto searches outwards in that order. A leading dot
8
- // means the search is skipped and the name is already absolute.
9
- //
10
- // A name that resolves to nothing is NOT an error here. A narrowed vendored
11
- // copy importing a file nobody vendored beside it is the normal case - see the
12
- // header of parse.go - and the answer is to say the type is unresolved and
13
- // carry on, not to refuse to describe the file.
14
-
15
- import (
16
- "sort"
17
- "strings"
18
- )
19
-
20
- // scalars are proto's built-in types, which never resolve to a declaration.
21
- var scalars = map[string]bool{
22
- "double": true, "float": true, "int32": true, "int64": true,
23
- "uint32": true, "uint64": true, "sint32": true, "sint64": true,
24
- "fixed32": true, "fixed64": true, "sfixed32": true, "sfixed64": true,
25
- "bool": true, "string": true, "bytes": true,
26
- }
27
-
28
- func isScalar(name string) bool { return scalars[name] }
29
-
30
- // Index is every declaration in a set of files, by fully-qualified name.
31
- type Index struct {
32
- files []*File
33
-
34
- messages map[string]*Message
35
- enums map[string]*Enum
36
- fileOf map[string]*File
37
- fqnOf map[*Message]string
38
- }
39
-
40
- // NewIndex walks the files once and records every message and enum in them,
41
- // nested ones included, under the name the rest of the world would use.
42
- func NewIndex(files []*File) *Index {
43
- ix := &Index{
44
- files: files,
45
- messages: map[string]*Message{},
46
- enums: map[string]*Enum{},
47
- fileOf: map[string]*File{},
48
- fqnOf: map[*Message]string{},
49
- }
50
-
51
- for _, file := range files {
52
- for _, m := range file.Messages {
53
- ix.addMessage(file, file.Package, m)
54
- }
55
- for _, e := range file.Enums {
56
- ix.addEnum(file, join(file.Package, e.Name), e)
57
- }
58
- }
59
-
60
- return ix
61
- }
62
-
63
- func (ix *Index) addMessage(file *File, scope string, m *Message) {
64
- fqn := join(scope, m.Name)
65
- ix.messages[fqn] = m
66
- ix.fileOf[fqn] = file
67
- ix.fqnOf[m] = fqn
68
-
69
- for _, nested := range m.Messages {
70
- ix.addMessage(file, fqn, nested)
71
- }
72
- for _, e := range m.Enums {
73
- ix.addEnum(file, join(fqn, e.Name), e)
74
- }
75
- }
76
-
77
- func (ix *Index) addEnum(file *File, fqn string, e *Enum) {
78
- ix.enums[fqn] = e
79
- ix.fileOf[fqn] = file
80
- }
81
-
82
- // Resolve finds what `name`, written inside `scope`, refers to.
83
- //
84
- // scope is a fully-qualified name - a package, or a message inside one. The
85
- // search walks outwards from it, which is what proto itself does.
86
- func (ix *Index) Resolve(scope, name string) (string, bool) {
87
- if name == "" || isScalar(name) {
88
- return "", false
89
- }
90
-
91
- if strings.HasPrefix(name, ".") {
92
- fqn := strings.TrimPrefix(name, ".")
93
-
94
- return fqn, ix.declared(fqn)
95
- }
96
-
97
- parts := strings.Split(scope, ".")
98
- for i := len(parts); i >= 0; i-- {
99
- fqn := join(strings.Join(parts[:i], "."), name)
100
- if ix.declared(fqn) {
101
- return fqn, true
102
- }
103
- }
104
-
105
- return "", false
106
- }
107
-
108
- func (ix *Index) declared(fqn string) bool {
109
- _, message := ix.messages[fqn]
110
- _, enum := ix.enums[fqn]
111
-
112
- return message || enum
113
- }
114
-
115
- func (ix *Index) Message(fqn string) *Message { return ix.messages[fqn] }
116
- func (ix *Index) Enum(fqn string) *Enum { return ix.enums[fqn] }
117
- func (ix *Index) FileOf(fqn string) *File { return ix.fileOf[fqn] }
118
-
119
- // FQN is the name a message is known by, once it has been indexed.
120
- func (ix *Index) FQN(m *Message) string { return ix.fqnOf[m] }
121
-
122
- // Names is every declared name, sorted. Determinism is not a nicety here: the
123
- // fragment these end up in is committed and compared by `gen:check`, so a map
124
- // iterated in Go's order would produce a different file every run.
125
- func (ix *Index) Names() []string {
126
- out := make([]string, 0, len(ix.messages)+len(ix.enums))
127
- for fqn := range ix.messages {
128
- out = append(out, fqn)
129
- }
130
- for fqn := range ix.enums {
131
- out = append(out, fqn)
132
- }
133
- sort.Strings(out)
134
-
135
- return out
136
- }
137
-
138
- // TypeRef is one field's type, resolved as far as it could be.
139
- type TypeRef struct {
140
- // Written is the type as the author typed it, rendered the way the catalog
141
- // renders types: `[]LineItem`, `map[string]Money`, `Money`.
142
- Written string
143
-
144
- // FQN is what the named type resolves to, empty for a scalar or for a name
145
- // nothing in the tree declares.
146
- FQN string
147
-
148
- // Unresolved is the name that could not be found, empty when there was
149
- // nothing to find or the search succeeded. It is a diagnostic, not a
150
- // failure: a narrowed vendored copy is expected to have some.
151
- Unresolved string
152
- }
153
-
154
- // resolveType renders a field's type and resolves the name inside it.
155
- //
156
- // A map is rendered `map[K]V` and resolved on its value: the key of a proto map
157
- // is always a scalar, and the value is the half that can name a message.
158
- func (ix *Index) resolveType(scope string, f *Field) TypeRef {
159
- if key, value, ok := mapParts(f.Type); ok {
160
- inner := ix.resolveType(scope, &Field{Type: value})
161
-
162
- return TypeRef{
163
- Written: "map[" + key + "]" + inner.Written,
164
- FQN: inner.FQN,
165
- Unresolved: inner.Unresolved,
166
- }
167
- }
168
-
169
- ref := TypeRef{Written: shortName(f.Type)}
170
- if f.Label == "repeated" {
171
- ref.Written = "[]" + ref.Written
172
- }
173
-
174
- if isScalar(f.Type) {
175
- return ref
176
- }
177
-
178
- if fqn, ok := ix.Resolve(scope, f.Type); ok {
179
- ref.FQN = fqn
180
- } else {
181
- ref.Unresolved = f.Type
182
- }
183
-
184
- return ref
185
- }
186
-
187
- // mapParts splits `map<string, Money>` into its two arguments.
188
- func mapParts(typ string) (string, string, bool) {
189
- if !strings.HasPrefix(typ, "map<") || !strings.HasSuffix(typ, ">") {
190
- return "", "", false
191
- }
192
- inner := typ[len("map<") : len(typ)-1]
193
- comma := strings.Index(inner, ",")
194
- if comma < 0 {
195
- return "", "", false
196
- }
197
-
198
- return strings.TrimSpace(inner[:comma]), strings.TrimSpace(inner[comma+1:]), true
199
- }
200
-
201
- // shortName is the last segment of a dotted name.
202
- //
203
- // The catalog spells a type the way a reader says it - `Money`, not
204
- // `shop.v1.Money` - and its `defs` keys are deliberately bare for the same
205
- // reason: two sources meaning different things by `Money` should collide
206
- // visibly rather than coexist as two quietly different entries.
207
- func shortName(name string) string {
208
- name = strings.TrimPrefix(name, ".")
209
- if i := strings.LastIndex(name, "."); i >= 0 {
210
- return name[i+1:]
211
- }
212
-
213
- return name
214
- }
215
-
216
- func join(scope, name string) string {
217
- if scope == "" {
218
- return name
219
- }
220
-
221
- return scope + "." + name
222
- }
@@ -1,119 +0,0 @@
1
- package main
2
-
3
- // Name resolution, on its own. A proto reference means whatever the innermost
4
- // enclosing scope that declares it means, and a name nothing declares is a
5
- // diagnostic rather than a failure.
6
-
7
- import "testing"
8
-
9
- func indexed(t *testing.T) *Index {
10
- t.Helper()
11
-
12
- orders, _ := parseFile(t, "orders.proto")
13
- money, _ := parseFile(t, "money.proto")
14
-
15
- return NewIndex([]*File{orders, money})
16
- }
17
-
18
- func TestResolvesAcrossFiles(t *testing.T) {
19
- ix := indexed(t)
20
-
21
- fqn, ok := ix.Resolve("shop.v1", "Money")
22
- if !ok || fqn != "shop.v1.Money" {
23
- t.Errorf("Money from shop.v1: %q %v", fqn, ok)
24
- }
25
- if ix.Message(fqn) == nil {
26
- t.Error("the resolved name does not lead back to the message")
27
- }
28
- }
29
-
30
- // Proto searches outwards from where the name was written, so the nested type
31
- // wins over anything of the same name further out.
32
- func TestResolvesInnermostFirst(t *testing.T) {
33
- ix := indexed(t)
34
-
35
- fqn, ok := ix.Resolve("shop.v1.Order", "Address")
36
- if !ok || fqn != "shop.v1.Order.Address" {
37
- t.Errorf("Address from inside Order: %q %v", fqn, ok)
38
- }
39
-
40
- // The same name written one scope out finds nothing, which is the point.
41
- if _, ok := ix.Resolve("shop.v1", "Address"); ok {
42
- t.Error("a nested type resolved from outside the message that nests it")
43
- }
44
- }
45
-
46
- func TestLeadingDotIsAbsolute(t *testing.T) {
47
- ix := indexed(t)
48
-
49
- fqn, ok := ix.Resolve("shop.v1.Order", ".shop.v1.Money")
50
- if !ok || fqn != "shop.v1.Money" {
51
- t.Errorf("absolute name: %q %v", fqn, ok)
52
- }
53
- }
54
-
55
- func TestNestedEnumIsIndexed(t *testing.T) {
56
- ix := indexed(t)
57
-
58
- fqn, ok := ix.Resolve("shop.v1.Order", "Channel")
59
- if !ok || fqn != "shop.v1.Order.Channel" {
60
- t.Errorf("nested enum: %q %v", fqn, ok)
61
- }
62
- if ix.Enum(fqn) == nil {
63
- t.Error("the resolved name does not lead back to the enum")
64
- }
65
- }
66
-
67
- // The whole reason this parser is tolerant: a narrowed vendored copy imports
68
- // files nobody vendored beside it, and it must still be describable.
69
- func TestUnresolvedIsReportedNotFatal(t *testing.T) {
70
- ix := indexed(t)
71
-
72
- ref := ix.resolveType("shop.v1", &Field{Type: "Nowhere"})
73
- if ref.Unresolved != "Nowhere" {
74
- t.Errorf("an unknown type was not reported: %+v", ref)
75
- }
76
- if ref.Written != "Nowhere" {
77
- t.Errorf("an unknown type must still be shown as written: %q", ref.Written)
78
- }
79
- }
80
-
81
- // Types are rendered the way the catalog renders types, and resolved on the
82
- // half that can name a message.
83
- func TestTypeRendering(t *testing.T) {
84
- ix := indexed(t)
85
-
86
- cases := []struct {
87
- field Field
88
- written, fqn string
89
- }{
90
- {Field{Type: "string"}, "string", ""},
91
- {Field{Type: "Money"}, "Money", "shop.v1.Money"},
92
- {Field{Type: "shop.v1.Money"}, "Money", "shop.v1.Money"},
93
- {Field{Type: "LineItem", Label: "repeated"}, "[]LineItem", "shop.v1.LineItem"},
94
- {Field{Type: "map<string, string>"}, "map[string]string", ""},
95
- {Field{Type: "map<string, Money>"}, "map[string]Money", "shop.v1.Money"},
96
- }
97
-
98
- for _, c := range cases {
99
- got := ix.resolveType("shop.v1", &c.field)
100
- if got.Written != c.written || got.FQN != c.fqn {
101
- t.Errorf("%s: %q -> %q (want %q -> %q)", c.field.Type, got.Written, got.FQN, c.written, c.fqn)
102
- }
103
- }
104
- }
105
-
106
- // The fragment is committed and compared by gen:check, so anything derived from
107
- // a map has to come out in the same order every run.
108
- func TestNamesAreSorted(t *testing.T) {
109
- names := indexed(t).Names()
110
-
111
- for i := 1; i < len(names); i++ {
112
- if names[i-1] > names[i] {
113
- t.Fatalf("names are not sorted: %q before %q", names[i-1], names[i])
114
- }
115
- }
116
- if len(names) == 0 {
117
- t.Fatal("nothing was indexed")
118
- }
119
- }
@@ -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-redis",
15
- Summary: "Finds runtime construction of supported Go Redis clients and adds the service-owned Redis store to the catalog.",
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,183 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "go/ast"
6
- "strconv"
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
- type redisConstruction struct {
15
- at goscan.Source
16
- }
17
-
18
- func extract(in plugin.Input, opts Options) (plugin.Response, error) {
19
- b := &plugin.Builder{}
20
- tree, err := goscan.Read(in.Root)
21
- if err != nil {
22
- return plugin.Response{}, err
23
- }
24
-
25
- owner := opts.Context + "." + opts.Service
26
- service := catalog.Service{
27
- ID: owner,
28
- Slug: opts.Service,
29
- Provides: []catalog.RpcService{},
30
- Consumes: []catalog.RpcCall{},
31
- Aggregates: []catalog.Aggregate{},
32
- }
33
- stores := []catalog.Store{}
34
- if found := findRedisConstruction(tree); found != nil {
35
- keyspaces := scanRedisKeyspaces(tree)
36
- storeSlug := firstNonEmpty(opts.Store, "redis")
37
- storeID := owner + "." + storeSlug
38
- service.Stores = []string{storeID}
39
- stores = append(stores, catalog.Store{
40
- ID: storeID,
41
- Slug: storeSlug,
42
- Name: firstNonEmpty(opts.Name, opts.Service+" Redis"),
43
- Kind: catalog.StoreKindRedis,
44
- Owner: owner,
45
- Tables: []catalog.Table{},
46
- Keyspaces: keyspaces,
47
- Source: found.at.String(),
48
- })
49
- } else {
50
- b.Warn(owner, "no supported Redis client construction was found in non-test Go source")
51
- }
52
-
53
- fragment := catalog.Catalog{
54
- GeneratedAt: in.GeneratedAt,
55
- Commit: in.Commit,
56
- Contexts: []catalog.BoundedContext{{
57
- ID: opts.Context,
58
- Slug: opts.Context,
59
- Services: []catalog.Service{service},
60
- }},
61
- Defs: map[string]catalog.TypeDef{},
62
- Flows: []catalog.Flow{},
63
- Adrs: []catalog.Adr{},
64
- Stores: stores,
65
- }
66
- encoded, err := json.MarshalIndent(fragment, "", " ")
67
- if err != nil {
68
- return plugin.Response{}, err
69
- }
70
- b.File(firstNonEmpty(opts.Out, "redis.json"), string(encoded)+"\n")
71
-
72
- return b.Response(), nil
73
- }
74
-
75
- func findRedisConstruction(tree *goscan.Tree) *redisConstruction {
76
- for _, file := range tree.Files {
77
- var found *redisConstruction
78
- ast.Inspect(file.Node, func(node ast.Node) bool {
79
- if found != nil {
80
- return false
81
- }
82
- call, ok := node.(*ast.CallExpr)
83
- if !ok {
84
- return true
85
- }
86
- sel, ok := call.Fun.(*ast.SelectorExpr)
87
- if !ok {
88
- return true
89
- }
90
- pkg, ok := sel.X.(*ast.Ident)
91
- if !ok {
92
- return true
93
- }
94
- importPath := redisImportPath(file, pkg.Name)
95
- if !isRedisConstructor(importPath, sel.Sel.Name) {
96
- return true
97
- }
98
- found = &redisConstruction{at: tree.At(call.Pos())}
99
- return false
100
- })
101
- if found != nil {
102
- return found
103
- }
104
- }
105
-
106
- return nil
107
- }
108
-
109
- // Versioned Go module paths end in v8/v9, while the packages still call
110
- // themselves redis. goscan cannot infer an external package clause from the
111
- // import path, so fill in the stable package names of the clients understood
112
- // by this extractor. Explicit aliases are already present in file.Imports.
113
- func redisImportPath(file *goscan.File, packageName string) string {
114
- for _, spec := range file.Node.Imports {
115
- importPath, err := strconv.Unquote(spec.Path.Value)
116
- if err != nil || !isRedisClientImport(importPath) {
117
- continue
118
- }
119
- if spec.Name != nil {
120
- if spec.Name.Name == packageName {
121
- return importPath
122
- }
123
- continue
124
- }
125
- if packageName == defaultRedisPackage(importPath) {
126
- return importPath
127
- }
128
- }
129
- return ""
130
- }
131
-
132
- func isRedisConstructor(importPath, name string) bool {
133
- switch {
134
- case isGoRedisImport(importPath):
135
- switch name {
136
- case "NewClient", "NewClusterClient", "NewFailoverClient", "NewFailoverClusterClient":
137
- return true
138
- }
139
- case importPath == "github.com/redis/rueidis" || strings.HasPrefix(importPath, "github.com/redis/rueidis/"):
140
- return name == "NewClient"
141
- case importPath == "github.com/gomodule/redigo/redis":
142
- return name == "Dial" || name == "DialURL"
143
- }
144
-
145
- return false
146
- }
147
-
148
- func isRedisClientImport(importPath string) bool {
149
- return isGoRedisImport(importPath) || importPath == "github.com/redis/rueidis" ||
150
- strings.HasPrefix(importPath, "github.com/redis/rueidis/") || importPath == "github.com/gomodule/redigo/redis"
151
- }
152
-
153
- func isGoRedisImport(importPath string) bool {
154
- for _, base := range []string{"github.com/redis/go-redis", "github.com/go-redis/redis"} {
155
- if importPath == base {
156
- return true
157
- }
158
- version, ok := strings.CutPrefix(importPath, base+"/v")
159
- if !ok || version == "" {
160
- continue
161
- }
162
- if _, err := strconv.Atoi(version); err == nil {
163
- return true
164
- }
165
- }
166
- return false
167
- }
168
-
169
- func defaultRedisPackage(importPath string) string {
170
- if importPath == "github.com/redis/rueidis" || strings.HasPrefix(importPath, "github.com/redis/rueidis/") {
171
- return "rueidis"
172
- }
173
- return "redis"
174
- }
175
-
176
- func firstNonEmpty(values ...string) string {
177
- for _, value := range values {
178
- if value != "" {
179
- return value
180
- }
181
- }
182
- return ""
183
- }