@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,177 +0,0 @@
1
- package main
2
-
3
- import (
4
- "net/url"
5
- "path"
6
- "regexp"
7
- "strings"
8
- "unicode"
9
-
10
- "github.com/shortlink-org/portolan/catalog"
11
- )
12
-
13
- var sourceLine = regexp.MustCompile(`^(.+):(\d+)$`)
14
-
15
- // source renders a catalog location as a forge link when the generator has a
16
- // stable source base, or an immutable pin for an external repository.
17
- func (s *site) source(from, where string, owner *catalog.Service) string {
18
- if where == "" {
19
- return ""
20
- }
21
- label := code(where)
22
- file, line := where, ""
23
- if match := sourceLine.FindStringSubmatch(where); len(match) == 3 {
24
- file, line = match[1], match[2]
25
- }
26
- if strings.ContainsAny(file, " \t\n") || strings.HasPrefix(file, "/") {
27
- return label
28
- }
29
- directory := strings.HasSuffix(file, "/")
30
-
31
- prefix, ok := s.sourcePrefix(owner, directory)
32
- if !ok {
33
- return label
34
- }
35
- if owner != nil && owner.Path != "" && !strings.HasPrefix(file, owner.Path+"/") && file != owner.Path {
36
- file = path.Join(owner.Path, file)
37
- }
38
- file = remotePath(file, owner)
39
- if file == "." || file == "" {
40
- return label
41
- }
42
-
43
- file = strings.TrimSuffix(file, "/")
44
- href := strings.TrimSuffix(prefix, "/") + "/" + escapePath(file)
45
- if line != "" {
46
- href += "#L" + line
47
- }
48
- return "[" + label + "](" + href + ")"
49
- }
50
-
51
- func (s *site) serviceForSource(where string) *catalog.Service {
52
- file := sourceFile(where)
53
- var best *catalog.Service
54
- for _, svc := range s.services {
55
- if svc.Path != "" && (file == svc.Path || strings.HasPrefix(file, svc.Path+"/")) &&
56
- (best == nil || len(svc.Path) > len(best.Path)) {
57
- best = svc
58
- }
59
- }
60
- return best
61
- }
62
-
63
- func (s *site) sourcePrefix(owner *catalog.Service, directory bool) (string, bool) {
64
- wanted := ""
65
- if owner != nil {
66
- wanted = bareRepo(owner.Repo)
67
- }
68
- for i := range s.cat.Repos {
69
- pin := &s.cat.Repos[i]
70
- if pin.Commit != "" && bareRepo(pin.Repo) == wanted {
71
- view := "/blob/"
72
- if directory {
73
- view = "/tree/"
74
- }
75
- repo := "https://" + bareRepo(pin.Repo)
76
- if strings.Contains(strings.ToLower(repo), "gitlab") {
77
- view = "/-/" + strings.Trim(view, "/") + "/"
78
- }
79
- return repo + view + url.PathEscape(pin.Commit), true
80
- }
81
- }
82
- base := strings.TrimSuffix(s.opts.SourceBaseURL, "/")
83
- if base == "" || (wanted != "" && sourceBaseRepo(base) != wanted) {
84
- return "", false
85
- }
86
- if directory {
87
- base = strings.Replace(base, "/-/blob/", "/-/tree/", 1)
88
- base = strings.Replace(base, "/blob/", "/tree/", 1)
89
- }
90
- return base, true
91
- }
92
-
93
- func sourceBaseRepo(base string) string {
94
- for _, marker := range []string{"/-/blob/", "/-/tree/", "/blob/", "/tree/"} {
95
- if at := strings.Index(base, marker); at >= 0 {
96
- return bareRepo(base[:at])
97
- }
98
- }
99
- return bareRepo(base)
100
- }
101
-
102
- func bareRepo(value string) string {
103
- return strings.ToLower(strings.TrimSuffix(strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(value, "https://"), "http://"), "/"), ".git"))
104
- }
105
-
106
- func repoLink(repo string) string {
107
- if repo == "" {
108
- return ""
109
- }
110
- href := repo
111
- if !strings.Contains(repo, "://") {
112
- href = "https://" + repo
113
- }
114
- return "[" + code(repo) + "](" + strings.TrimSuffix(href, ".git") + ")"
115
- }
116
-
117
- func remotePath(file string, owner *catalog.Service) string {
118
- if owner == nil || owner.Repo == "" {
119
- return file
120
- }
121
- parts := strings.Split(bareRepo(owner.Repo), "/")
122
- if len(parts) < 3 {
123
- return file
124
- }
125
- prefix := "vendor/repos/" + parts[len(parts)-2] + "/" + parts[len(parts)-1] + "/"
126
- return strings.TrimPrefix(file, prefix)
127
- }
128
-
129
- func escapePath(value string) string {
130
- parts := strings.Split(value, "/")
131
- for i := range parts {
132
- parts[i] = url.PathEscape(parts[i])
133
- }
134
- return strings.Join(parts, "/")
135
- }
136
-
137
- // anchorID is independent of a renderer's heading-slug algorithm. Explicit
138
- // anchors keep deep links stable when a title changes punctuation or case.
139
- func anchorID(value string) string {
140
- var b strings.Builder
141
- dash := false
142
- for _, r := range strings.ToLower(value) {
143
- switch {
144
- case unicode.IsLetter(r), unicode.IsDigit(r):
145
- b.WriteRune(r)
146
- dash = false
147
- case b.Len() > 0 && !dash:
148
- b.WriteByte('-')
149
- dash = true
150
- }
151
- }
152
- return strings.Trim(b.String(), "-")
153
- }
154
-
155
- func (s *site) eventRef(from, eventID, text string) string {
156
- pageID := s.eventPage[eventID]
157
- page, ok := s.pathOf[pageID]
158
- if !ok {
159
- return code(text)
160
- }
161
- return "[" + code(text) + "](" + rel(from, page) + "#event-" + anchorID(eventID) + ")"
162
- }
163
-
164
- func (s *site) viaRef(from string, via *catalog.EdgeVia) string {
165
- if via == nil || via.Flow == "" {
166
- return ""
167
- }
168
- page, ok := s.pathOf[via.Flow]
169
- if !ok {
170
- return code(via.Flow + "#" + via.Step)
171
- }
172
- suffix := ""
173
- if via.Step != "" {
174
- suffix = "#step-" + anchorID(via.Step)
175
- }
176
- return "[" + code(via.Flow+"#"+via.Step) + "](" + rel(from, page) + suffix + ")"
177
- }
@@ -1,201 +0,0 @@
1
- package main
2
-
3
- import (
4
- "strings"
5
-
6
- "github.com/shortlink-org/portolan/catalog"
7
- )
8
-
9
- func (s *site) renderStore(store *catalog.Store) {
10
- self := s.pathOf[store.ID]
11
-
12
- var b strings.Builder
13
- b.WriteString("# " + store.Name + "\n\n")
14
- b.WriteString(s.stamp() + "\n")
15
-
16
- meta := [][]string{
17
- {"Id", code(store.ID)},
18
- {"Kind", string(store.Kind)},
19
- {"Owner", s.ref(self, store.Owner, store.Owner)},
20
- }
21
- if store.Source != "" {
22
- meta = append(meta, []string{"Source", s.source(self, store.Source, s.services[store.Owner])})
23
- }
24
- b.WriteString(defList(meta))
25
-
26
- keyspaces := make([][]string, 0, len(store.Keyspaces))
27
- for i := range store.Keyspaces {
28
- keyspace := &store.Keyspaces[i]
29
- source := ""
30
- if keyspace.Source != "" {
31
- source = s.source(self, keyspace.Source, s.services[store.Owner])
32
- }
33
- operations := make([]string, 0, len(keyspace.Operations))
34
- for _, operation := range keyspace.Operations {
35
- operations = append(operations, string(operation))
36
- }
37
- keyspaces = append(keyspaces, []string{
38
- code(keyspace.Pattern), strings.Join(operations, ", "), code(keyspace.Value), code(keyspace.TTL), source,
39
- })
40
- }
41
- section(&b, "Redis key patterns", table([]string{"Pattern", "Operations", "Value", "TTL", "Source"}, keyspaces))
42
-
43
- var tables strings.Builder
44
- for i := range store.Tables {
45
- s.renderTable(&tables, self, store, &store.Tables[i])
46
- }
47
- section(&b, "Tables", tables.String())
48
-
49
- var views strings.Builder
50
- for i := range store.Views {
51
- s.renderView(&views, self, store, &store.Views[i])
52
- }
53
- section(&b, "Views", views.String())
54
-
55
- s.b.file(self, b.String())
56
- }
57
-
58
- func (s *site) renderTable(b *strings.Builder, self string, store *catalog.Store, tbl *catalog.Table) {
59
- b.WriteString("<a id=\"relation-" + anchorID(tbl.ID) + "\"></a>\n")
60
- b.WriteString("### " + tbl.Name + "\n\n")
61
-
62
- meta := []string{}
63
- if tbl.Role != "" {
64
- meta = append(meta, string(tbl.Role))
65
- }
66
- if tbl.Persists != nil {
67
- if tbl.Persists.Aggregate != "" {
68
- meta = append(meta, "persists "+s.ref(self, tbl.Persists.Aggregate, tbl.Persists.Aggregate))
69
- }
70
- if tbl.Persists.Block != "" {
71
- meta = append(meta, "block "+code(tbl.Persists.Block))
72
- }
73
- }
74
- if len(meta) > 0 {
75
- b.WriteString(strings.Join(meta, " · ") + "\n\n")
76
- }
77
- if tbl.Doc != "" {
78
- b.WriteString(tbl.Doc + "\n\n")
79
- }
80
-
81
- b.WriteString(s.columnTable(self, store, tbl.Columns))
82
-
83
- indexes := make([][]string, 0, len(tbl.Indexes))
84
- for _, idx := range tbl.Indexes {
85
- kind := "index"
86
- if idx.Unique {
87
- kind = "unique"
88
- }
89
- indexes = append(indexes, []string{code(idx.Name), strings.Join(idx.Columns, ", "), kind})
90
- }
91
- if rendered := table([]string{"Index", "Columns", "Kind"}, indexes); rendered != "" {
92
- b.WriteString("\n" + rendered)
93
- }
94
- b.WriteString("\n")
95
- }
96
-
97
- func (s *site) renderView(b *strings.Builder, self string, store *catalog.Store, view *catalog.View) {
98
- b.WriteString("<a id=\"relation-" + anchorID(view.ID) + "\"></a>\n")
99
- b.WriteString("### " + view.Name + "\n\n")
100
-
101
- meta := []string{}
102
- if view.Materialized {
103
- // Worth saying first and plainly: a materialised view can be stale, and
104
- // that is the one thing a reader has to know before believing a row.
105
- meta = append(meta, "**materialized** — rows are stored, and can be stale")
106
- } else {
107
- meta = append(meta, "computed on read")
108
- }
109
- if len(view.Reads) > 0 {
110
- reads := make([]string, 0, len(view.Reads))
111
- for _, id := range view.Reads {
112
- reads = append(reads, s.relationRef(self, id))
113
- }
114
- meta = append(meta, "reads "+strings.Join(reads, ", "))
115
- }
116
- b.WriteString(strings.Join(meta, " · ") + "\n\n")
117
-
118
- if view.Doc != "" {
119
- b.WriteString(view.Doc + "\n\n")
120
- }
121
-
122
- b.WriteString(s.columnTable(self, store, view.Columns))
123
-
124
- if view.Definition != "" {
125
- b.WriteString("\n" + fence("sql", view.Definition))
126
- }
127
- if view.Source != "" {
128
- b.WriteString("\nSource: " + s.source(self, view.Source, s.services[store.Owner]) + "\n")
129
- }
130
- b.WriteString("\n")
131
- }
132
-
133
- func (s *site) columnTable(self string, store *catalog.Store, columns []catalog.Column) string {
134
- rows := make([][]string, 0, len(columns))
135
- for i := range columns {
136
- col := &columns[i]
137
-
138
- key := ""
139
- switch {
140
- case col.PK:
141
- key = "PK"
142
- case col.FK != nil:
143
- key = "→ " + s.relationRef(self, col.FK.Table) + "." + col.FK.Column
144
- if col.FK.OnDelete != "" {
145
- key += " (" + col.FK.OnDelete + ")"
146
- }
147
- }
148
-
149
- null := "not null"
150
- if col.Nullable {
151
- null = "null"
152
- }
153
-
154
- from := ""
155
- if len(col.From) > 0 {
156
- sources := make([]string, 0, len(col.From))
157
- for _, ref := range col.From {
158
- sources = append(sources, code(ref))
159
-
160
- // Lineage that leaves the store is how a service stays out of
161
- // somebody else's database - and nothing on the far side records
162
- // that the copy exists.
163
- if owner, ok := s.relationStore[relationOf(ref)]; ok && owner != store.ID {
164
- s.b.warn(store.ID, "%s.%s is copied from %q, which lives in store %q", store.ID, col.Name, ref, owner)
165
- }
166
- }
167
- from = strings.Join(sources, ", ")
168
- }
169
-
170
- rows = append(rows, []string{code(col.Name), code(col.Type), null, key, col.Maps, from, col.Doc})
171
- }
172
-
173
- return table([]string{"Column", "Type", "Null", "Key", "Maps", "From", "Doc"}, rows)
174
- }
175
-
176
- // relationRef links a table or view id to the heading that documents it,
177
- // wherever that lives - a foreign key routinely points at a table in another
178
- // store, and the point of the link is to say so.
179
- func (s *site) relationRef(from, id string) string {
180
- storeID, known := s.relationStore[id]
181
- if !known {
182
- return code(id)
183
- }
184
-
185
- page, ok := s.pathOf[storeID]
186
- if !ok {
187
- return code(id)
188
- }
189
-
190
- return "[" + code(id) + "](" + rel(from, page) + "#relation-" + anchorID(id) + ")"
191
- }
192
-
193
- // relationOf strips the trailing column from a lineage reference,
194
- // "<table or view id>.<column>", leaving the relation.
195
- func relationOf(ref string) string {
196
- if i := strings.LastIndex(ref, "."); i >= 0 {
197
- return ref[:i]
198
- }
199
-
200
- return ref
201
- }
Binary file
@@ -1,17 +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: "gen-mermaid", Summary: "Exports every flow as a standalone Mermaid sequence diagram.",
15
- Phases: []string{plugin.PhaseGenerate}, Options: optionsSchema,
16
- }
17
- }
@@ -1,22 +0,0 @@
1
- package main
2
-
3
- import (
4
- "fmt"
5
- "io"
6
- "os"
7
-
8
- "github.com/shortlink-org/portolan/plugin"
9
- )
10
-
11
- func main() {
12
- if err := run(os.Stdin, os.Stdout); err != nil {
13
- fmt.Fprintln(os.Stderr, "portolan-gen-mermaid:", err)
14
- os.Exit(1)
15
- }
16
- }
17
-
18
- func run(stdin io.Reader, stdout io.Writer) error {
19
- return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
20
- return render(req, opts), nil
21
- })
22
- }
@@ -1,104 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "sort"
6
- "strings"
7
-
8
- "github.com/shortlink-org/portolan/catalog"
9
- "github.com/shortlink-org/portolan/plugin"
10
- flowmermaid "github.com/shortlink-org/portolan/render/mermaid"
11
- )
12
-
13
- type Options struct {
14
- Title string `json:"title,omitempty"`
15
- }
16
-
17
- type indexEntry struct {
18
- ID string `json:"id"`
19
- Slug string `json:"slug"`
20
- Name string `json:"name"`
21
- Owner string `json:"owner,omitempty"`
22
- Source string `json:"source,omitempty"`
23
- Diagram string `json:"diagram"`
24
- }
25
-
26
- func render(req plugin.Request, opts Options) plugin.Response {
27
- flows := append([]catalog.Flow(nil), req.Catalog.Flows...)
28
- sort.Slice(flows, func(i, j int) bool { return flows[i].Slug < flows[j].Slug })
29
- label := labeler(req.Catalog)
30
- b := &plugin.Builder{}
31
- title := opts.Title
32
- if title == "" {
33
- title = "Architecture flows"
34
- }
35
- var readme strings.Builder
36
- readme.WriteString("# " + title + "\n\n")
37
- readme.WriteString("Standalone Mermaid sequence diagrams generated from the portolan catalog.\n\n")
38
- entries := make([]indexEntry, 0, len(flows))
39
- for i := range flows {
40
- flow := &flows[i]
41
- name := flow.Slug + ".mmd"
42
- b.File(name, flowmermaid.Sequence(flow, label))
43
- readme.WriteString("- [" + flow.Name + "](" + name + ") — `" + flow.ID + "`\n")
44
- entries = append(entries, indexEntry{ID: flow.ID, Slug: flow.Slug, Name: flow.Name, Owner: flow.Owner, Source: flow.Source, Diagram: name})
45
- }
46
- encoded, _ := json.MarshalIndent(map[string]any{"version": 1, "flows": entries}, "", " ")
47
- b.File("README.md", readme.String())
48
- b.File("index.json", string(encoded)+"\n")
49
- return b.Response()
50
- }
51
-
52
- func labeler(cat catalog.Catalog) func(*catalog.Step) string {
53
- methods := map[string]catalog.RpcMethod{}
54
- services := map[string]*catalog.Service{}
55
- for i := range cat.Contexts {
56
- for j := range cat.Contexts[i].Services {
57
- svc := &cat.Contexts[i].Services[j]
58
- services[svc.ID] = svc
59
- for k := range svc.Provides {
60
- provided := &svc.Provides[k]
61
- for m := range provided.Methods {
62
- methods[provided.ID+"/"+provided.Methods[m].Name] = provided.Methods[m]
63
- }
64
- }
65
- }
66
- }
67
- for i := range cat.Externals {
68
- for j := range cat.Externals[i].Provides {
69
- provided := &cat.Externals[i].Provides[j]
70
- for m := range provided.Methods {
71
- methods[provided.ID+"/"+provided.Methods[m].Name] = provided.Methods[m]
72
- }
73
- }
74
- }
75
- return func(step *catalog.Step) string {
76
- text := step.Label
77
- if text == "" {
78
- text = step.Ref
79
- }
80
- if text == "" {
81
- text = string(step.Kind)
82
- }
83
- if step.Kind != catalog.StepRPC {
84
- return text
85
- }
86
- answer := methods[step.Ref].Response
87
- if step.Ref == "" && step.Label != "" {
88
- if svc := services[step.To]; svc != nil {
89
- for i := range svc.Provides {
90
- for j := range svc.Provides[i].Methods {
91
- method := &svc.Provides[i].Methods[j]
92
- if method.Name == step.Label {
93
- answer = method.Response
94
- }
95
- }
96
- }
97
- }
98
- }
99
- if answer != "" {
100
- return text + " → " + answer
101
- }
102
- return text
103
- }
104
- }
@@ -1,33 +0,0 @@
1
- package main
2
-
3
- import (
4
- "encoding/json"
5
- "strings"
6
- "testing"
7
-
8
- "github.com/shortlink-org/portolan/catalog"
9
- "github.com/shortlink-org/portolan/plugin"
10
- "github.com/shortlink-org/portolan/plugin/schematest"
11
- )
12
-
13
- func TestOptionsSchema(t *testing.T) { schematest.Check(t, optionsSchema, Options{}) }
14
-
15
- func TestRenderIsSortedAndIndexed(t *testing.T) {
16
- cat := catalog.Catalog{Flows: []catalog.Flow{
17
- {ID: "flow.z", Slug: "z", Name: "Z", Participants: []catalog.Participant{{ID: "a", Kind: catalog.ParticipantActor}, {ID: "b", Kind: catalog.ParticipantService}}, Steps: catalog.FlowNodes{&catalog.Step{Type: "step", ID: "s1", From: "a", To: "b", Kind: catalog.StepCall, Label: "go"}}},
18
- {ID: "flow.a", Slug: "a", Name: "A"},
19
- }}
20
- resp := render(plugin.Request{Catalog: cat}, Options{})
21
- if resp.Files[0].Name != "a.mmd" || resp.Files[1].Name != "z.mmd" {
22
- t.Fatalf("files are not stable: %+v", resp.Files)
23
- }
24
- var index struct {
25
- Flows []indexEntry `json:"flows"`
26
- }
27
- if err := json.Unmarshal([]byte(resp.Files[len(resp.Files)-1].Contents), &index); err != nil || len(index.Flows) != 2 {
28
- t.Fatalf("bad index: %v %+v", err, index)
29
- }
30
- if !strings.Contains(resp.Files[1].Contents, "sequenceDiagram") {
31
- t.Fatalf("not Mermaid: %s", resp.Files[1].Contents)
32
- }
33
- }
Binary file