@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,195 +0,0 @@
1
- package main
2
-
3
- // The closed sets an aggregate's fields take values from.
4
- //
5
- // Go has no enum. What it has is a convention, and the convention is what is
6
- // read: a named type over a basic one - `type Reason string` - and a const
7
- // block whose constants are of that type. The set is the constants, in the
8
- // order they are written; a value's name is the constant's literal, because
9
- // that is what a consumer sees on the wire, and the constant's own name only
10
- // when the literal is not a string - an iota.
11
- //
12
- // Looked for in the aggregate's package, under vo/, and under event/: a
13
- // reason is declared beside the event that carries it, and a status beside
14
- // the root that holds it.
15
-
16
- import (
17
- "go/ast"
18
- "go/token"
19
- "path"
20
- "strings"
21
-
22
- "github.com/shortlink-org/portolan/catalog"
23
- "github.com/shortlink-org/portolan/plugin"
24
- )
25
-
26
- func extractEnums(root, domainPath, aggID string, main *pkg, b *plugin.Builder) []catalog.Enum {
27
- var out []catalog.Enum
28
-
29
- out = append(out, enumsIn(main, aggID, "")...)
30
-
31
- for _, name := range subdirs(root, path.Join(domainPath, "vo")) {
32
- if name == "rules" {
33
- continue
34
- }
35
- p, err := parsePkg(root, path.Join(domainPath, "vo", name))
36
- if err != nil {
37
- continue
38
- }
39
- // Qualified the way a value object is, for the same reason: the
40
- // field that holds it says `password.Policy`, not `Policy`.
41
- out = append(out, enumsIn(p, aggID, p.name)...)
42
- }
43
-
44
- if p, err := parsePkg(root, path.Join(domainPath, "event")); err == nil {
45
- out = append(out, enumsIn(p, aggID, "")...)
46
- }
47
-
48
- _ = b
49
-
50
- return out
51
- }
52
-
53
- // enumDecl is a named basic type and the constants declared of it.
54
- type enumDecl struct {
55
- name string
56
- doc string
57
- values []catalog.EnumValue
58
- }
59
-
60
- func enumsIn(p *pkg, aggID, qualifier string) []catalog.Enum {
61
- var out []catalog.Enum
62
-
63
- for _, decl := range p.enums() {
64
- label := decl.name
65
- if qualifier != "" {
66
- label = qualified(qualifier, decl.name)
67
- }
68
-
69
- out = append(out, catalog.Enum{
70
- ID: blockID(aggID, slug(label)),
71
- Slug: slug(label),
72
- Name: label,
73
- Doc: firstSentenceOrAll(decl.doc),
74
- Deprecated: deprecatedByDoc(decl.doc),
75
- Values: decl.values,
76
- })
77
- }
78
-
79
- return out
80
- }
81
-
82
- // enums pairs every exported `type X <basic>` with the constants of type X,
83
- // and keeps the pairs that have any. A type nothing is declared of is not a
84
- // set; it is a name for a string.
85
- func (p *pkg) enums() []enumDecl {
86
- var out []enumDecl
87
- byName := map[string]int{}
88
-
89
- for _, file := range p.files {
90
- for _, decl := range file.Decls {
91
- gen, ok := decl.(*ast.GenDecl)
92
- if !ok || gen.Tok != token.TYPE {
93
- continue
94
- }
95
- for _, spec := range gen.Specs {
96
- typeSpec, ok := spec.(*ast.TypeSpec)
97
- if !ok || !exported(typeSpec.Name.Name) || !isBasic(typeSpec.Type) {
98
- continue
99
- }
100
- comment := typeSpec.Doc
101
- if comment == nil && len(gen.Specs) == 1 {
102
- comment = gen.Doc
103
- }
104
- byName[typeSpec.Name.Name] = len(out)
105
- out = append(out, enumDecl{name: typeSpec.Name.Name, doc: comment.Text()})
106
- }
107
- }
108
- }
109
-
110
- for _, file := range p.files {
111
- for _, decl := range file.Decls {
112
- gen, ok := decl.(*ast.GenDecl)
113
- if !ok || gen.Tok != token.CONST {
114
- continue
115
- }
116
- // Inside one block the type carries over: `A Status = iota` then
117
- // bare `B` and `C` are Status too. A new explicit type resets it.
118
- current := ""
119
- for _, spec := range gen.Specs {
120
- value, ok := spec.(*ast.ValueSpec)
121
- if !ok {
122
- continue
123
- }
124
- if value.Type != nil {
125
- current = ""
126
- if ident, ok := value.Type.(*ast.Ident); ok {
127
- current = ident.Name
128
- }
129
- } else if len(value.Values) > 0 {
130
- // An untyped constant: `X = "x"` belongs to no set.
131
- current = ""
132
- }
133
- at, known := byName[current]
134
- if !known {
135
- continue
136
- }
137
- doc := value.Doc.Text()
138
- if doc == "" && value.Comment != nil {
139
- doc = value.Comment.Text()
140
- }
141
- for i, name := range value.Names {
142
- if !exported(name.Name) {
143
- continue
144
- }
145
- literal := name.Name
146
- if i < len(value.Values) {
147
- if lit, ok := value.Values[i].(*ast.BasicLit); ok && lit.Kind == token.STRING {
148
- literal = strings.Trim(lit.Value, "`\"")
149
- }
150
- }
151
- out[at].values = append(out[at].values, catalog.EnumValue{
152
- Name: literal,
153
- Doc: firstSentenceOrAll(strings.TrimSpace(doc)),
154
- Deprecated: deprecatedByDoc(doc),
155
- })
156
- }
157
- }
158
- }
159
- }
160
-
161
- kept := out[:0]
162
- for _, decl := range out {
163
- if len(decl.values) > 0 {
164
- kept = append(kept, decl)
165
- }
166
- }
167
-
168
- return kept
169
- }
170
-
171
- // isBasic is `string`, `int`, `uint8` and the like - a named type over one of
172
- // those is what a const block can be declared of.
173
- func isBasic(expr ast.Expr) bool {
174
- ident, ok := expr.(*ast.Ident)
175
- if !ok {
176
- return false
177
- }
178
- switch ident.Name {
179
- case "string", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "byte", "rune":
180
- return true
181
- }
182
-
183
- return false
184
- }
185
-
186
- // deprecatedByDoc is the Go convention: a paragraph starting "Deprecated:".
187
- func deprecatedByDoc(doc string) bool {
188
- for _, para := range strings.Split(doc, "\n\n") {
189
- if strings.HasPrefix(strings.TrimSpace(para), "Deprecated:") {
190
- return true
191
- }
192
- }
193
-
194
- return false
195
- }
@@ -1,82 +0,0 @@
1
- package main
2
-
3
- import (
4
- "strings"
5
- "testing"
6
-
7
- "github.com/shortlink-org/portolan/catalog"
8
- )
9
-
10
- const enumSrc = `package event
11
-
12
- // Reason says why a session stopped being usable. It is a closed set.
13
- type Reason string
14
-
15
- const (
16
- // ReasonLogout - the user asked.
17
- ReasonLogout Reason = "logout"
18
- // ReasonRevoked - somebody else ended it.
19
- //
20
- // Deprecated: support ends sessions through ReasonRiskBlocked now.
21
- ReasonRevoked Reason = "revoked"
22
- reasonHidden Reason = "hidden"
23
- Unrelated = "loose"
24
- )
25
-
26
- // Level is how loud the end was.
27
- type Level int
28
-
29
- const (
30
- Quiet Level = iota // barely
31
- Loud
32
- )
33
-
34
- // Alias names a string and nothing is declared of it.
35
- type Alias string
36
-
37
- type Point struct{ X int }
38
- `
39
-
40
- func TestEnumsAreTheConstantsOfANamedBasicType(t *testing.T) {
41
- p, err := parseSource("session_ended.go", enumSrc)
42
- if err != nil {
43
- t.Fatal(err)
44
- }
45
- enums := enumsIn(p, "auth.auth.session", "")
46
- if len(enums) != 2 {
47
- t.Fatalf("enums = %+v", enums)
48
- }
49
-
50
- reason := enums[0]
51
- if reason.ID != "auth.auth.session.reason" || reason.Doc != "Reason says why a session stopped being usable. It is a closed set." {
52
- t.Errorf("reason = %+v", reason)
53
- }
54
- // The literal, not the constant's name: it is what the wire carries.
55
- if got := valueNames(reason.Values); got != "logout,revoked" {
56
- t.Errorf("values = %s", got)
57
- }
58
- if !reason.Values[1].Deprecated || reason.Values[0].Deprecated {
59
- t.Errorf("deprecation not read off the doc: %+v", reason.Values)
60
- }
61
- if reason.Values[0].Doc != "ReasonLogout - the user asked." {
62
- t.Errorf("doc = %q", reason.Values[0].Doc)
63
- }
64
-
65
- // An iota has no literal to show, so the constant's name stands; the
66
- // trailing comment is its doc.
67
- level := enums[1]
68
- if got := valueNames(level.Values); got != "Quiet,Loud" {
69
- t.Errorf("level values = %s", got)
70
- }
71
- if level.Values[0].Doc != "barely" {
72
- t.Errorf("trailing comment not read: %q", level.Values[0].Doc)
73
- }
74
- }
75
-
76
- func valueNames(values []catalog.EnumValue) string {
77
- out := make([]string, 0, len(values))
78
- for _, v := range values {
79
- out = append(out, v.Name)
80
- }
81
- return strings.Join(out, ",")
82
- }
@@ -1,99 +0,0 @@
1
- package main
2
-
3
- import (
4
- "path"
5
-
6
- "github.com/shortlink-org/portolan/catalog"
7
- "github.com/shortlink-org/portolan/plugin"
8
- )
9
-
10
- // extractEvents reads event below a discovered aggregate domain package.
11
- //
12
- // An event is a struct with a `Name() string` that returns a literal - which is
13
- // both how the domain declares the event's name on the wire and how this tells
14
- // an event apart from the helper types that live beside it.
15
- func extractEvents(root, aggregateName, domainPath string, layout sourceLayout, aggID string, b *plugin.Builder) []catalog.Event {
16
- out := []catalog.Event{}
17
-
18
- pkg, err := parsePkg(root, path.Join(domainPath, "event"))
19
- if err != nil {
20
- return out
21
- }
22
-
23
- out = eventsIn(pkg, aggID, channelOf(root, aggregateName, layout))
24
- if len(out) == 0 {
25
- b.Warn(aggID, path.Join(domainPath, "event")+" declares no struct with a Name() method; the aggregate publishes nothing")
26
- }
27
-
28
- return out
29
- }
30
-
31
- // channelOf reads where an aggregate's events go: the `Topic` constant of
32
- // the discovered integration event DTO package, which may live beside a
33
- // feature or beside a legacy repository adapter. The domain names the event and the adapter
34
- // names the channel, because the channel is a fact about the transport, not
35
- // about what happened. Empty when the package or the constant is missing: a
36
- // domain nobody publishes has no channel to name, and saying so is better
37
- // than guessing one from the aggregate's name.
38
- func channelOf(root, aggregateName string, layout sourceLayout) string {
39
- dir := layout.integrationEvents[aggregateName]
40
- if dir == "" {
41
- return ""
42
- }
43
- pkg, err := parsePkg(root, dir)
44
- if err != nil {
45
- return ""
46
- }
47
-
48
- return stringConsts(pkg)["Topic"]
49
- }
50
-
51
- // eventsIn is the reading itself, kept apart from where the package was found
52
- // so it can be exercised on a package built in a test. channel is where every
53
- // event of the aggregate is published, or empty.
54
- func eventsIn(pkg *pkg, aggID, channel string) []catalog.Event {
55
- out := []catalog.Event{}
56
-
57
- for _, decl := range pkg.structs() {
58
- if !exported(decl.name) {
59
- continue
60
- }
61
-
62
- wire, named := returnedString(pkg, pkg.methods(decl.name)["Name"])
63
- if !named {
64
- // Not every struct in the package is an event. One without a Name
65
- // is a payload or a helper, and quietly documenting it as a
66
- // published fact would be worse than missing it.
67
- continue
68
- }
69
-
70
- // A Name that is not a literal names the event in a way this reader
71
- // cannot follow, and a wire with no name is no wire at all.
72
- var onWire *catalog.EventWire
73
- if wire != "" {
74
- onWire = &catalog.EventWire{Name: wire, Channel: channel}
75
- }
76
-
77
- out = append(out, catalog.Event{
78
- ID: eventID(aggID, decl.name),
79
- Slug: slug(decl.name),
80
- Name: decl.name,
81
- Wire: onWire,
82
- // One version, and it is declared rather than discovered: nothing
83
- // in the source carries a version, so saying v1 is saying "this is
84
- // what it looks like today", not "this is the first of several".
85
- Versions: []catalog.EventVersion{{
86
- Version: "v1",
87
- Doc: decl.doc,
88
- Source: decl.source,
89
- Fields: fields(decl.fields),
90
- }},
91
- // Consumers live in other services, and this extractor is reading
92
- // one. An empty list is the honest answer; the merge is where the
93
- // other side of the arrow arrives.
94
- Consumers: []catalog.EventConsumer{},
95
- })
96
- }
97
-
98
- return out
99
- }
@@ -1,191 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "os"
6
- "path/filepath"
7
- "strconv"
8
- "strings"
9
-
10
- "github.com/shortlink-org/portolan/catalog"
11
- "github.com/shortlink-org/portolan/plugin"
12
- )
13
-
14
- // extract reads a service and answers with one catalog fragment.
15
- //
16
- // A fragment, not a catalog: it carries one context and one service, names
17
- // peers it does not own, and is merged with everything else before anything
18
- // validates it. That is the whole reason the estate does not need a file
19
- // somebody has to own.
20
- func extract(in plugin.Input, opts Options) (plugin.Response, error) {
21
- b := &plugin.Builder{}
22
- root := in.Root
23
-
24
- if opts.Context == "" {
25
- opts.Context = filepath.Base(root)
26
- }
27
- if opts.Service == "" {
28
- opts.Service = filepath.Base(root)
29
- }
30
-
31
- svcID := serviceID(opts.Context, opts.Service)
32
- layout := discoverLayout(root)
33
-
34
- readme := readFile(filepath.Join(root, "README.md"))
35
- service := catalog.Service{
36
- ID: svcID,
37
- Slug: opts.Service,
38
- Name: firstNonEmpty(opts.ServiceName, markdownTitle(readme), title(opts.Service)),
39
- Repo: firstNonEmpty(opts.Repo, modulePath(root)),
40
- Path: filepath.ToSlash(root),
41
- Readme: readme,
42
- Provides: []catalog.RpcService{},
43
- Consumes: []catalog.RpcCall{},
44
- }
45
-
46
- // Operations live in the application layer and belong to the aggregate the
47
- // use case sits under, so they are gathered first and handed out below.
48
- // What exposes each one is read from the transport layer beside it, and the
49
- // endpoints that came back are where the flows start.
50
- exposures, endpoints := extractTransport(root, layout, b)
51
- operations := extractOperations(root, layout, exposures, b)
52
-
53
- for _, aggregateName := range sortedKeys(layout.domains) {
54
- aggregate, ok := extractAggregate(root, aggregateName, layout.domains[aggregateName], layout, svcID, b)
55
- if !ok {
56
- continue
57
- }
58
- aggregate.Operations = operations[aggregateName]
59
- if aggregate.Operations == nil {
60
- aggregate.Operations = []catalog.Operation{}
61
- }
62
-
63
- service.Aggregates = append(service.Aggregates, aggregate)
64
- }
65
-
66
- if len(service.Aggregates) == 0 {
67
- b.Warn(svcID, "no aggregate domain packages found; supported layouts include internal/domain/<aggregate> and internal/<aggregate>/domain")
68
- service.Aggregates = []catalog.Aggregate{}
69
- }
70
-
71
- for aggregate, ops := range operations {
72
- if !hasAggregate(service.Aggregates, aggregateID(svcID, slug(aggregate))) {
73
- b.Warn(svcID, "application slice "+aggregate+" has "+plural(len(ops))+" but there is no matching aggregate domain package")
74
- }
75
- }
76
-
77
- // Flows are read last, because a flow names the events the aggregates
78
- // declare and an event nothing reaches is worth reporting.
79
- flows, calls := extractFlows(root, flowOptions{
80
- context: opts.Context,
81
- svcID: svcID,
82
- service: opts.Service,
83
- store: opts.Store,
84
- peers: opts.Peers,
85
- externals: opts.Externals,
86
- events: opts.Events,
87
- }, layout, endpoints, eventIDs(service.Aggregates), b)
88
- // What the service calls is read off its flows: the generated client in
89
- // the tree names the rpc, and a step on it is the call being made.
90
- service.Consumes = calls
91
-
92
- fragment := catalog.Catalog{
93
- GeneratedAt: in.GeneratedAt,
94
- Commit: in.Commit,
95
- Contexts: []catalog.BoundedContext{{
96
- ID: opts.Context,
97
- Slug: opts.Context,
98
- Name: firstNonEmpty(opts.ContextName, title(opts.Context)),
99
- Summary: opts.ContextSummary,
100
- Classification: catalog.Classification(opts.Classification),
101
- Services: []catalog.Service{service},
102
- }},
103
- Defs: map[string]catalog.TypeDef{},
104
- Flows: flows,
105
- Adrs: []catalog.Adr{},
106
- }
107
-
108
- encoded, err := json.MarshalIndent(fragment, "", " ")
109
- if err != nil {
110
- return plugin.Response{}, err
111
- }
112
-
113
- b.File(firstNonEmpty(opts.Out, "domain.json"), string(encoded)+"\n")
114
-
115
- return b.Response(), nil
116
- }
117
-
118
- // eventIDs is every fact the service publishes, which is what a flow reader is
119
- // held to: an event no flow reaches is an event whose publisher this could not
120
- // follow.
121
- func eventIDs(aggregates []catalog.Aggregate) []string {
122
- var out []string
123
- for i := range aggregates {
124
- for j := range aggregates[i].Events {
125
- out = append(out, aggregates[i].Events[j].ID)
126
- }
127
- }
128
-
129
- return out
130
- }
131
-
132
- func hasAggregate(aggregates []catalog.Aggregate, id string) bool {
133
- for i := range aggregates {
134
- if aggregates[i].ID == id {
135
- return true
136
- }
137
- }
138
-
139
- return false
140
- }
141
-
142
- func readFile(path string) string {
143
- contents, err := os.ReadFile(path)
144
- if err != nil {
145
- return ""
146
- }
147
-
148
- return strings.TrimSpace(string(contents))
149
- }
150
-
151
- // modulePath is the module line of go.mod, which is the closest thing the
152
- // source has to a repository name.
153
- func modulePath(root string) string {
154
- for _, line := range strings.Split(readFile(filepath.Join(root, "go.mod")), "\n") {
155
- if rest, ok := strings.CutPrefix(strings.TrimSpace(line), "module "); ok {
156
- return strings.TrimSpace(rest)
157
- }
158
- }
159
-
160
- return ""
161
- }
162
-
163
- // markdownTitle is the first `# ` heading of a readme, which is what the
164
- // service is actually called.
165
- func markdownTitle(md string) string {
166
- for _, line := range strings.Split(md, "\n") {
167
- if heading, ok := strings.CutPrefix(strings.TrimSpace(line), "# "); ok {
168
- return strings.TrimSpace(heading)
169
- }
170
- }
171
-
172
- return ""
173
- }
174
-
175
- func firstNonEmpty(values ...string) string {
176
- for _, value := range values {
177
- if strings.TrimSpace(value) != "" {
178
- return value
179
- }
180
- }
181
-
182
- return ""
183
- }
184
-
185
- func plural(n int) string {
186
- if n == 1 {
187
- return "1 use case"
188
- }
189
-
190
- return strconv.Itoa(n) + " use cases"
191
- }