@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,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-commands",
15
- Summary: "Reads the make targets, npm scripts, just recipes and task-runner tasks a repository declares onto its service.",
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,79 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "errors"
6
- "path/filepath"
7
- "regexp"
8
-
9
- "github.com/shortlink-org/portolan/catalog"
10
- "github.com/shortlink-org/portolan/internal/commands"
11
- "github.com/shortlink-org/portolan/plugin"
12
- )
13
-
14
- var slug = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
15
-
16
- // extract reads the runner files at the root and answers with a fragment
17
- // that names the service and lists its commands - and nothing else about
18
- // it, because everything else is another extractor's to say.
19
- //
20
- // A runner file that cannot be read comes back as a warning rather than a
21
- // failure: a broken Taskfile is a fact about the checkout the page can carry,
22
- // and the other runners' commands are still worth listing.
23
- func extract(in plugin.Input, opts Options) (plugin.Response, error) {
24
- b := &plugin.Builder{}
25
-
26
- service := opts.Service
27
- if service == "" {
28
- service = filepath.Base(in.Root)
29
- }
30
- context := opts.Context
31
- if context == "" {
32
- context = filepath.Base(filepath.Dir(in.Root))
33
- }
34
- for _, part := range []string{context, service} {
35
- if !slug.MatchString(part) {
36
- return plugin.Response{}, errors.New(part + " is not a slug: a service's id is the context and the service, so both have to be one word the way the estate spells them")
37
- }
38
- }
39
-
40
- cmds, warnings := commands.Read(in.Root)
41
- for _, warning := range warnings {
42
- b.Warn(in.Root, warning)
43
- }
44
- if len(cmds) == 0 {
45
- b.Warn(in.Root, "no runner file declares a command here - no Makefile, justfile, Taskfile, package.json scripts or pyproject tasks - so the fragment lists none")
46
- }
47
-
48
- fragment := catalog.Catalog{
49
- GeneratedAt: in.GeneratedAt,
50
- Commit: in.Commit,
51
- Contexts: []catalog.BoundedContext{{
52
- ID: context,
53
- Slug: context,
54
- Services: []catalog.Service{{
55
- ID: context + "." + service,
56
- Slug: service,
57
- Provides: []catalog.RpcService{},
58
- Consumes: []catalog.RpcCall{},
59
- Aggregates: []catalog.Aggregate{},
60
- Commands: cmds,
61
- }},
62
- }},
63
- Defs: map[string]catalog.TypeDef{},
64
- Flows: []catalog.Flow{},
65
- Adrs: []catalog.Adr{},
66
- }
67
-
68
- encoded, err := json.MarshalIndent(fragment, "", " ")
69
- if err != nil {
70
- return plugin.Response{}, err
71
- }
72
- out := opts.Out
73
- if out == "" {
74
- out = "commands.json"
75
- }
76
- b.File(out, string(encoded)+"\n")
77
-
78
- return b.Response(), nil
79
- }
@@ -1,92 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "os"
6
- "path/filepath"
7
- "testing"
8
-
9
- "github.com/shortlink-org/portolan/catalog"
10
- "github.com/shortlink-org/portolan/plugin"
11
- )
12
-
13
- func mustWrite(t *testing.T, path, contents string) {
14
- t.Helper()
15
- if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
16
- t.Fatal(err)
17
- }
18
- if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
19
- t.Fatal(err)
20
- }
21
- }
22
-
23
- func fragment(t *testing.T, resp plugin.Response) catalog.Catalog {
24
- t.Helper()
25
- var got catalog.Catalog
26
- if err := json.Unmarshal([]byte(resp.Files[0].Contents), &got); err != nil {
27
- t.Fatal(err)
28
- }
29
-
30
- return got
31
- }
32
-
33
- func TestListsCommandsOnTheNamedService(t *testing.T) {
34
- root := filepath.Join(t.TempDir(), "shop", "cart")
35
- mustWrite(t, filepath.Join(root, "Makefile"), "test: ## Run the tests\n\tgo test ./...\n")
36
- mustWrite(t, filepath.Join(root, "package.json"), `{"scripts":{"build":"tsc"}}`)
37
-
38
- resp, err := extract(plugin.Input{Root: root, Commit: "abc", GeneratedAt: "2026-09-07T00:00:00Z"}, Options{})
39
- if err != nil {
40
- t.Fatal(err)
41
- }
42
- if resp.Files[0].Name != "commands.json" {
43
- t.Errorf("file = %q", resp.Files[0].Name)
44
- }
45
- got := fragment(t, resp)
46
- if got.Commit != "abc" || len(got.Contexts) != 1 || got.Contexts[0].ID != "shop" {
47
- t.Fatalf("fragment = %+v", got)
48
- }
49
- svc := got.Contexts[0].Services[0]
50
- if svc.ID != "shop.cart" || svc.Slug != "cart" || svc.Name != "" || svc.Readme != "" {
51
- t.Errorf("the fragment claims only the id: %+v", svc)
52
- }
53
- if len(svc.Commands) != 2 || svc.Commands[0].Run != "make test" || svc.Commands[0].Doc != "Run the tests" || svc.Commands[1].Run != "npm run build" {
54
- t.Errorf("commands = %+v", svc.Commands)
55
- }
56
- if len(resp.Warnings()) != 0 {
57
- t.Errorf("warnings = %+v", resp.Warnings())
58
- }
59
- }
60
-
61
- func TestOptionsNameTheService(t *testing.T) {
62
- root := t.TempDir()
63
- mustWrite(t, filepath.Join(root, "justfile"), "dev:\n cargo run\n")
64
-
65
- resp, err := extract(plugin.Input{Root: root}, Options{Context: "shop", Service: "oms", Out: "cmds.json"})
66
- if err != nil {
67
- t.Fatal(err)
68
- }
69
- got := fragment(t, resp)
70
- if resp.Files[0].Name != "cmds.json" || got.Contexts[0].Services[0].ID != "shop.oms" {
71
- t.Errorf("file = %q, service = %q", resp.Files[0].Name, got.Contexts[0].Services[0].ID)
72
- }
73
- }
74
-
75
- func TestNothingDeclaredWarns(t *testing.T) {
76
- resp, err := extract(plugin.Input{Root: t.TempDir()}, Options{Context: "shop", Service: "oms"})
77
- if err != nil {
78
- t.Fatal(err)
79
- }
80
- if len(resp.Warnings()) != 1 {
81
- t.Errorf("warnings = %+v", resp.Warnings())
82
- }
83
- if got := fragment(t, resp); len(got.Contexts[0].Services[0].Commands) != 0 {
84
- t.Errorf("commands = %+v", got.Contexts[0].Services[0].Commands)
85
- }
86
- }
87
-
88
- func TestRefusesANonSlug(t *testing.T) {
89
- if _, err := extract(plugin.Input{Root: t.TempDir()}, Options{Context: "Shop Front", Service: "oms"}); err == nil {
90
- t.Error("a context with a space in it is not an id")
91
- }
92
- }
@@ -1,55 +0,0 @@
1
- // Command extract-commands reads what a developer types against a checkout -
2
- // the make targets, npm scripts, just recipes and task-runner tasks the
3
- // repository declares - and answers with a catalog fragment holding them on
4
- // the service they belong to.
5
- //
6
- // It reads the runner files and nothing else. The README that says "run make
7
- // test" is a claim; the Makefile with a test target is the fact. Fragment
8
- // merging puts the list on the service the domain extractor described, so
9
- // this plugin does not need to know anything else about it - only which
10
- // service it is, which the manifest says.
11
- //
12
- // extract-project reads the same files for the component it describes, so a
13
- // repository read by that plugin does not need this one; this is for the
14
- // services a domain extractor describes instead.
15
- package main
16
-
17
- import (
18
- "fmt"
19
- "io"
20
- "os"
21
-
22
- "github.com/shortlink-org/portolan/plugin"
23
- )
24
-
25
- // Options are what the manifest tells the extractor.
26
- type Options struct {
27
- // Context is the bounded context the service is in. Left out, the name
28
- // of the input directory's parent - right for `shop/cart`, and wrong
29
- // often enough that the manifest should say.
30
- Context string `json:"context,omitempty"`
31
-
32
- // Service is the service the commands belong to, as its slug. Left out,
33
- // the name of the input directory.
34
- Service string `json:"service,omitempty"`
35
-
36
- // Out names the fragment file.
37
- Out string `json:"out,omitempty"`
38
- }
39
-
40
- func main() {
41
- if err := run(os.Stdin, os.Stdout); err != nil {
42
- fmt.Fprintln(os.Stderr, "portolan-extract-commands:", err)
43
- os.Exit(1)
44
- }
45
- }
46
-
47
- func run(stdin io.Reader, stdout io.Writer) error {
48
- return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
49
- if req.Input.Root == "" {
50
- return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
51
- }
52
-
53
- return extract(req.Input, opts)
54
- })
55
- }
@@ -1,258 +0,0 @@
1
- package main
2
-
3
- // Avro, read for the two things the catalog keeps: what a record is called and
4
- // what fields it has.
5
- //
6
- // This is deliberately not an Avro implementation. It never resolves a schema
7
- // against data, never checks compatibility and never validates - a registry
8
- // has already done all three, and a second opinion here is only a way for the
9
- // two to disagree. It reads names and shapes, and renders every type as the
10
- // short label a person reads on a page.
11
-
12
- import (
13
- "encoding/json"
14
- "fmt"
15
- "strings"
16
-
17
- "github.com/shortlink-org/portolan/catalog"
18
- )
19
-
20
- // avro reads one .avsc into the shapes it declares.
21
- //
22
- // The first return is the full name of the record at the top, which is the
23
- // name a message on the bus goes by. The rest are every named record inside
24
- // it, keyed by full name, because a nested record is a shape shared by
25
- // whatever else names it and `defs` is where a shared shape lives.
26
- func avro(raw []byte) (string, map[string]catalog.TypeDef, error) {
27
- var node any
28
- if err := json.Unmarshal(raw, &node); err != nil {
29
- return "", nil, fmt.Errorf("not an Avro schema: %w", err)
30
- }
31
-
32
- r := &avroReader{defs: map[string]catalog.TypeDef{}}
33
- label, _ := r.typeOf(node, "")
34
-
35
- // A top-level union or array is legal Avro and names no record. The label
36
- // is still the honest answer to "what is this called".
37
- return label, r.defs, nil
38
- }
39
-
40
- type avroReader struct {
41
- defs map[string]catalog.TypeDef
42
-
43
- // open guards a record that names itself inside itself - a comment thread,
44
- // a tree - which is ordinary Avro and an infinite recursion here.
45
- open map[string]bool
46
- }
47
-
48
- // typeOf renders one Avro type, and returns the defs key when it is a record
49
- // this reader has written down.
50
- //
51
- // enclosing is the namespace inherited from the schema this type sits in,
52
- // which is how Avro spells a nested name: a record with no namespace of its
53
- // own belongs to its parent's.
54
- func (r *avroReader) typeOf(node any, enclosing string) (label, ref string) {
55
- switch value := node.(type) {
56
- case string:
57
- // A bare name is either a primitive or a reference to a shape named
58
- // earlier - possibly in another subject entirely, which is what a
59
- // registry reference is for. The reference is claimed here and cleared
60
- // later if nothing in the estate turns out to declare it, because only
61
- // the whole step can say whether the other subject was vendored too.
62
- name := qualify(value, enclosing)
63
- if primitive(value) {
64
- return name, ""
65
- }
66
-
67
- return name, name
68
-
69
- case []any:
70
- return r.union(value, enclosing), ""
71
-
72
- case map[string]any:
73
- return r.object(value, enclosing)
74
- }
75
-
76
- return "", ""
77
- }
78
-
79
- // union renders ["null", "string"] as "string?" - the shape a reader wants,
80
- // rather than the shape the file has.
81
- func (r *avroReader) union(members []any, enclosing string) string {
82
- var (
83
- labels []string
84
- nullable bool
85
- )
86
-
87
- for _, member := range members {
88
- if name, isString := member.(string); isString && name == "null" {
89
- nullable = true
90
-
91
- continue
92
- }
93
- label, _ := r.typeOf(member, enclosing)
94
- labels = append(labels, label)
95
- }
96
-
97
- switch {
98
- case len(labels) == 0:
99
- return "null"
100
- case nullable:
101
- return strings.Join(labels, "|") + "?"
102
- }
103
-
104
- return strings.Join(labels, "|")
105
- }
106
-
107
- func (r *avroReader) object(node map[string]any, enclosing string) (label, ref string) {
108
- // A logical type is what the field MEANS, and it is what belongs on a
109
- // page: "timestamp-millis" says more than the "long" underneath it.
110
- if logical, _ := node["logicalType"].(string); logical != "" {
111
- return decorate(logical, node), ""
112
- }
113
-
114
- kind, _ := node["type"].(string)
115
- namespace := firstNonEmpty(text(node["namespace"]), enclosing)
116
-
117
- switch kind {
118
- case "record", "error":
119
- return r.record(node, namespace)
120
-
121
- case "enum", "fixed":
122
- // Named, but not a shape with fields, so there is nothing to put in
123
- // defs and the label is the whole answer.
124
- return qualify(text(node["name"]), namespace), ""
125
-
126
- case "array":
127
- items, _ := r.typeOf(node["items"], namespace)
128
-
129
- return items + "[]", ""
130
-
131
- case "map":
132
- values, _ := r.typeOf(node["values"], namespace)
133
-
134
- return "map<string, " + values + ">", ""
135
-
136
- case "":
137
- // `{"type": {...}}` with the real type one level down, which is how a
138
- // field wraps a schema it wants to annotate.
139
- if nested, wrapped := node["type"]; wrapped {
140
- return r.typeOf(nested, namespace)
141
- }
142
-
143
- return "", ""
144
- }
145
-
146
- return kind, ""
147
- }
148
-
149
- func (r *avroReader) record(node map[string]any, namespace string) (label, ref string) {
150
- name := qualify(text(node["name"]), namespace)
151
- if name == "" {
152
- return "", ""
153
- }
154
-
155
- if r.open[name] {
156
- // Already being read further up the stack: the name is the answer, and
157
- // the def it points at is written by the frame that opened it.
158
- return name, name
159
- }
160
- if _, written := r.defs[name]; written {
161
- return name, name
162
- }
163
-
164
- if r.open == nil {
165
- r.open = map[string]bool{}
166
- }
167
- r.open[name] = true
168
- defer delete(r.open, name)
169
-
170
- // The namespace a nested record inherits is this record's, not the one it
171
- // was reached from.
172
- inner := namespace
173
- if own := text(node["namespace"]); own != "" {
174
- inner = own
175
- } else if at := strings.LastIndex(name, "."); at > 0 {
176
- inner = name[:at]
177
- }
178
-
179
- fields := []catalog.Field{}
180
- for _, entry := range list(node["fields"]) {
181
- field, isObject := entry.(map[string]any)
182
- if !isObject {
183
- continue
184
- }
185
- fieldName := text(field["name"])
186
- if fieldName == "" {
187
- continue
188
- }
189
- fieldLabel, fieldRef := r.typeOf(field["type"], inner)
190
- fields = append(fields, catalog.Field{
191
- Name: fieldName,
192
- Type: fieldLabel,
193
- Doc: text(field["doc"]),
194
- Ref: fieldRef,
195
- })
196
- }
197
-
198
- r.defs[name] = catalog.TypeDef{Fields: fields}
199
-
200
- return name, name
201
- }
202
-
203
- // decorate spells out the parameters a logical type carries, because
204
- // "decimal" without them is not a type anybody can act on.
205
- func decorate(logical string, node map[string]any) string {
206
- if logical != "decimal" {
207
- return logical
208
- }
209
-
210
- precision, hasPrecision := number(node["precision"])
211
- if !hasPrecision {
212
- return logical
213
- }
214
- scale, hasScale := number(node["scale"])
215
- if !hasScale {
216
- scale = 0
217
- }
218
-
219
- return fmt.Sprintf("decimal(%d,%d)", precision, scale)
220
- }
221
-
222
- // qualify gives a name its namespace, unless it already carries one or is a
223
- // primitive - which is the one rule that keeps "string" from becoming
224
- // "shop.oms.string".
225
- func qualify(name, namespace string) string {
226
- if name == "" || namespace == "" || strings.Contains(name, ".") || primitive(name) {
227
- return name
228
- }
229
-
230
- return namespace + "." + name
231
- }
232
-
233
- func primitive(name string) bool {
234
- switch name {
235
- case "null", "boolean", "int", "long", "float", "double", "bytes", "string":
236
- return true
237
- }
238
-
239
- return false
240
- }
241
-
242
- func text(node any) string {
243
- value, _ := node.(string)
244
-
245
- return strings.TrimSpace(value)
246
- }
247
-
248
- func list(node any) []any {
249
- values, _ := node.([]any)
250
-
251
- return values
252
- }
253
-
254
- func number(node any) (int, bool) {
255
- value, isNumber := node.(float64)
256
-
257
- return int(value), isNumber
258
- }
@@ -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-csr",
15
- Summary: "Reads schemas vendored out of a Confluent Schema Registry and answers with the topics they are registered against and the shapes they declare.",
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
- }