@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,89 +0,0 @@
1
- package main
2
-
3
- import (
4
- "reflect"
5
- "sort"
6
- "strings"
7
- "testing"
8
-
9
- "github.com/shortlink-org/portolan/catalog"
10
- )
11
-
12
- // Every catalog field must be acknowledged when the mirror grows. Some are
13
- // printed, others are structural (ids, slugs and union discriminators), but a
14
- // new field may not disappear from generated documentation by default.
15
- var catalogCoverage = map[reflect.Type]string{
16
- reflect.TypeOf(catalog.Catalog{}): "generatedAt commit contexts defs flows adrs stores modules terms repos externals",
17
- reflect.TypeOf(catalog.External{}): "id slug name summary url provides",
18
- reflect.TypeOf(catalog.RepoPin{}): "repo commit",
19
- reflect.TypeOf(catalog.BoundedContext{}): "id slug name summary kind classification viewId services",
20
- reflect.TypeOf(catalog.Service{}): "id slug name repo path readme kind technologies provides consumes aggregates stores modules channels owners commands",
21
- reflect.TypeOf(catalog.Command{}): "runner name run doc body source",
22
- reflect.TypeOf(catalog.RpcService{}): "id methods source messages enums module",
23
- reflect.TypeOf(catalog.RpcEnum{}): "name doc values",
24
- reflect.TypeOf(catalog.RpcEnumValue{}): "name number doc",
25
- reflect.TypeOf(catalog.RpcMethod{}): "name doc request requestRef response responseRef streaming deprecated http soap",
26
- reflect.TypeOf(catalog.HttpRoute{}): "method path",
27
- reflect.TypeOf(catalog.SoapRoute{}): "action version style endpoint binding faults headers",
28
- reflect.TypeOf(catalog.RpcMessage{}): "name fields discriminator",
29
- reflect.TypeOf(catalog.RpcDiscriminator{}): "property variants",
30
- reflect.TypeOf(catalog.RpcVariant{}): "value message",
31
- reflect.TypeOf(catalog.RpcCall{}): "id peer status source note module via",
32
- reflect.TypeOf(catalog.EdgeVia{}): "flow step",
33
- reflect.TypeOf(catalog.ProtoModule{}): "id slug name registry owner commit digest packages files deps source",
34
- reflect.TypeOf(catalog.Aggregate{}): "id slug name readme root entities valueObjects operations events enums lifecycle",
35
- reflect.TypeOf(catalog.Enum{}): "id slug name doc deprecated values",
36
- reflect.TypeOf(catalog.EnumValue{}): "name doc deprecated",
37
- reflect.TypeOf(catalog.Lifecycle{}): "states transitions",
38
- reflect.TypeOf(catalog.Transition{}): "from to on emits source",
39
- reflect.TypeOf(catalog.Operation{}): "id kind doc exposedBy",
40
- reflect.TypeOf(catalog.Block{}): "id slug name doc ref fields",
41
- reflect.TypeOf(catalog.Event{}): "id slug name versions consumers wire",
42
- reflect.TypeOf(catalog.EventWire{}): "name channel",
43
- reflect.TypeOf(catalog.Channel{}): "address kind title doc messages source",
44
- reflect.TypeOf(catalog.ChannelMessage{}): "name title doc direction",
45
- reflect.TypeOf(catalog.EventConsumer{}): "service status note via",
46
- reflect.TypeOf(catalog.EventVersion{}): "version doc source fields",
47
- reflect.TypeOf(catalog.Field{}): "name type doc ref",
48
- reflect.TypeOf(catalog.TypeDef{}): "fields",
49
- reflect.TypeOf(catalog.Store{}): "id slug name kind owner tables views keyspaces source",
50
- reflect.TypeOf(catalog.RedisKeyspace{}): "pattern operations ttl value source",
51
- reflect.TypeOf(catalog.Table{}): "id name doc columns indexes persists role",
52
- reflect.TypeOf(catalog.Persists{}): "aggregate block",
53
- reflect.TypeOf(catalog.TableIndex{}): "name columns unique",
54
- reflect.TypeOf(catalog.Column{}): "name type nullable pk fk from maps doc",
55
- reflect.TypeOf(catalog.FK{}): "table column onDelete",
56
- reflect.TypeOf(catalog.View{}): "id name doc materialized columns reads definition persists source",
57
- reflect.TypeOf(catalog.Flow{}): "id slug name summary source trigger entrypoint includes owner participants steps",
58
- reflect.TypeOf(catalog.FlowTrigger{}): "kind label confidence",
59
- reflect.TypeOf(catalog.Participant{}): "id kind context label",
60
- reflect.TypeOf(catalog.Step{}): "type id from to kind ref label status note line continuesAt reaches handoff",
61
- reflect.TypeOf(catalog.FlowHandoff{}): "kind transport channel message direction",
62
- reflect.TypeOf(catalog.Parallel{}): "type id title branches",
63
- reflect.TypeOf(catalog.Alt{}): "type id branches",
64
- reflect.TypeOf(catalog.AltBranch{}): "title steps terminal",
65
- reflect.TypeOf(catalog.Loop{}): "type id title steps",
66
- reflect.TypeOf(catalog.AdrScope{}): "kind context service",
67
- reflect.TypeOf(catalog.Adr{}): "id slug number title status date scope body note supersededBy supersedes relates source created revised",
68
- reflect.TypeOf(catalog.AdrCommit{}): "commit author date",
69
- reflect.TypeOf(catalog.AdrRelates{}): "services events flows",
70
- reflect.TypeOf(catalog.Term{}): "id slug context name definition source",
71
- }
72
-
73
- func TestCatalogFieldCoverageIsExplicit(t *testing.T) {
74
- for typ, declared := range catalogCoverage {
75
- want := strings.Fields(declared)
76
- got := make([]string, 0, typ.NumField())
77
- for i := 0; i < typ.NumField(); i++ {
78
- name := strings.Split(typ.Field(i).Tag.Get("json"), ",")[0]
79
- if name != "" && name != "-" {
80
- got = append(got, name)
81
- }
82
- }
83
- sort.Strings(want)
84
- sort.Strings(got)
85
- if strings.Join(want, " ") != strings.Join(got, " ") {
86
- t.Errorf("%s coverage changed\nfields: %s\ncovered: %s", typ.Name(), strings.Join(got, " "), strings.Join(want, " "))
87
- }
88
- }
89
- }
@@ -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: "gen-markdown",
15
- Summary: "Renders the merged catalog as a directory of markdown: a page per context, service, aggregate, store and flow.",
16
- Phases: []string{plugin.PhaseGenerate},
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,71 +0,0 @@
1
- package main
2
-
3
- import (
4
- "strings"
5
-
6
- "github.com/shortlink-org/portolan/catalog"
7
- )
8
-
9
- // renderExternals writes one page per system outside the estate. The page
10
- // says what the catalog may claim about it - what it answers on, read from
11
- // the vendored copy of its document, and who of ours calls it - and nothing
12
- // the estate would have to own the far end to know.
13
- func (s *site) renderExternals() {
14
- for i := range s.cat.Externals {
15
- s.renderExternal(&s.cat.Externals[i])
16
- }
17
- }
18
-
19
- func (s *site) renderExternal(ext *catalog.External) {
20
- self := s.pathOf[ext.ID]
21
-
22
- var b strings.Builder
23
- b.WriteString("# " + orDefault(ext.Name, ext.ID) + "\n\n")
24
- b.WriteString(s.stamp() + "\n")
25
-
26
- rows := [][]string{
27
- {"Id", code(ext.ID)},
28
- {"Where", "outside the estate"},
29
- }
30
- if ext.URL != "" {
31
- rows = append(rows, []string{"Documented at", "<" + ext.URL + ">"})
32
- }
33
- b.WriteString(defList(rows))
34
-
35
- if ext.Summary != "" {
36
- b.WriteString("\n" + ext.Summary + "\n")
37
- }
38
-
39
- section(&b, "Provides", s.providesBlock(self, ext.Provides, nil))
40
- section(&b, "Called by", s.calledByTable(self, ext.ID))
41
-
42
- s.b.file(self, b.String())
43
- }
44
-
45
- // calledByTable is every call in the catalog that lands on the external. The
46
- // service pages list their own calls outward; this is the one place the
47
- // callers of a third party stand together.
48
- func (s *site) calledByTable(from, externalID string) string {
49
- var rows [][]string
50
- for i := range s.cat.Contexts {
51
- ctx := &s.cat.Contexts[i]
52
- for j := range ctx.Services {
53
- svc := &ctx.Services[j]
54
- for k := range svc.Consumes {
55
- call := &svc.Consumes[k]
56
- if call.Peer != externalID {
57
- continue
58
- }
59
- rows = append(rows, []string{
60
- s.ref(from, svc.ID, svc.Name),
61
- code(call.ID),
62
- string(call.Status),
63
- s.source(from, call.Source, svc),
64
- s.viaRef(from, call.Via),
65
- })
66
- }
67
- }
68
- }
69
-
70
- return table([]string{"Service", "Call", "Status", "Source", "Via"}, rows)
71
- }
@@ -1,277 +0,0 @@
1
- package main
2
-
3
- import (
4
- "path"
5
- "strconv"
6
- "strings"
7
-
8
- "github.com/shortlink-org/portolan/catalog"
9
- flowmermaid "github.com/shortlink-org/portolan/render/mermaid"
10
- )
11
-
12
- func (s *site) renderFlows() {
13
- if len(s.cat.Flows) == 0 {
14
- return
15
- }
16
-
17
- const self = "flows/README.md"
18
-
19
- var b strings.Builder
20
- b.WriteString("# Flows\n\n")
21
- b.WriteString(s.stamp() + "\n")
22
-
23
- rows := make([][]string, 0, len(s.cat.Flows))
24
- for i := range s.cat.Flows {
25
- flow := &s.cat.Flows[i]
26
- owner := "—"
27
- if flow.Owner != "" {
28
- owner = s.ref(self, flow.Owner, flow.Owner)
29
- }
30
- rows = append(rows, []string{
31
- s.ref(self, flow.ID, flow.Name),
32
- owner,
33
- firstLine(flow.Summary),
34
- })
35
- }
36
- b.WriteString(table([]string{"Flow", "Owner", "Summary"}, rows))
37
-
38
- s.b.file(self, b.String())
39
-
40
- for i := range s.cat.Flows {
41
- s.renderFlow(&s.cat.Flows[i])
42
- }
43
- }
44
-
45
- func (s *site) renderFlow(flow *catalog.Flow) {
46
- self := s.pathOf[flow.ID]
47
-
48
- var b strings.Builder
49
- b.WriteString("# " + flow.Name + "\n\n")
50
- b.WriteString(s.stamp() + "\n")
51
-
52
- meta := [][]string{
53
- {"Id", code(flow.ID)},
54
- }
55
- if flow.Owner != "" {
56
- meta = append(meta, []string{"Owner", s.ref(self, flow.Owner, flow.Owner)})
57
- }
58
- if flow.Trigger != nil {
59
- trigger := code(flow.Trigger.Kind)
60
- if flow.Trigger.Label != "" {
61
- trigger += " · " + flow.Trigger.Label
62
- }
63
- meta = append(meta,
64
- []string{"Trigger", trigger},
65
- []string{"Root confidence", flow.Trigger.Confidence},
66
- )
67
- }
68
- // Source is the file the flow was read out of, which is the only thing a
69
- // reader can go and check for themselves.
70
- if flow.Source != "" {
71
- meta = append(meta, []string{"Source", s.source(self, flow.Source, s.serviceForSource(flow.Source))})
72
- }
73
- b.WriteString(defList(meta))
74
-
75
- if flow.Summary != "" {
76
- b.WriteString("\n" + flow.Summary + "\n")
77
- }
78
-
79
- participants := make([][]string, 0, len(flow.Participants))
80
- for i := range flow.Participants {
81
- p := &flow.Participants[i]
82
- context := "—"
83
- if p.Context != nil {
84
- context = s.ref(self, *p.Context, *p.Context)
85
- }
86
- participants = append(participants, []string{code(p.ID), string(p.Kind), context, p.Label})
87
- }
88
- section(&b, "Participants", table([]string{"Participant", "Kind", "Context", "Label"}, participants))
89
-
90
- section(&b, "Sequence", fence("mermaid", flowmermaid.Sequence(flow, s.labelWithAnswer)))
91
-
92
- counter := 0
93
- section(&b, "Steps", s.stepList(self, flow, flow.Steps, &counter))
94
-
95
- s.b.file(self, b.String())
96
- }
97
-
98
- // stepList is the same walk again, in prose. The diagram shows the shape and
99
- // the list carries what will not fit on an arrow: where the step was read
100
- // from, how far it is trusted, and what it refers to.
101
- //
102
- // Branches are wrapped in block quotes rather than indented. Four spaces of
103
- // indentation is a code block in markdown, and three is a continuation of
104
- // whatever list came before - a quote is the one nesting that means nesting.
105
- func (s *site) stepList(self string, flow *catalog.Flow, nodes catalog.FlowNodes, counter *int) string {
106
- var b strings.Builder
107
-
108
- for _, node := range nodes {
109
- switch n := node.(type) {
110
- case *catalog.Step:
111
- *counter++
112
- b.WriteString("<a id=\"step-" + anchorID(n.ID) + "\"></a>\n")
113
-
114
- arrow := " → "
115
- if n.From == n.To {
116
- arrow = " ↺ "
117
- }
118
- b.WriteString(strconv.Itoa(*counter) + ". **" + n.From + "**" + arrow + "**" + n.To + "** — " + s.labelWithAnswer(n) + "\n")
119
-
120
- var notes []string
121
- if n.Ref != "" {
122
- notes = append(notes, s.stepRef(self, n))
123
- }
124
- if n.Status != catalog.StatusVerified {
125
- notes = append(notes, "status: "+string(n.Status))
126
- }
127
- if n.Line != "" {
128
- location := n.Line
129
- if flow.Source != "" && path.Dir(sourceFile(n.Line)) == "." {
130
- location = path.Join(path.Dir(sourceFile(flow.Source)), n.Line)
131
- }
132
- notes = append(notes, s.source(self, location, s.serviceForSource(flow.Source)))
133
- }
134
- if n.Note != "" {
135
- notes = append(notes, n.Note)
136
- }
137
- if len(notes) > 0 {
138
- // Three spaces line the continuation up under the "1. " marker.
139
- b.WriteString(" " + strings.Join(notes, " · ") + "\n")
140
- }
141
-
142
- if n.Status == catalog.StatusUnresolved {
143
- s.b.warn(flow.ID, "%s step %q is unresolved: %s", flow.ID, n.ID, stepLabel(n))
144
- }
145
-
146
- case *catalog.Parallel:
147
- var inner strings.Builder
148
- inner.WriteString("**In parallel** — " + orDefault(n.Title, "branches that do not wait for each other") + "\n")
149
- for i, branch := range n.Branches {
150
- inner.WriteString("\n*Branch " + strconv.Itoa(i+1) + "*\n\n")
151
- inner.WriteString(s.stepList(self, flow, branch, counter))
152
- }
153
- b.WriteString("\n" + quote(inner.String()) + "\n")
154
-
155
- case *catalog.Alt:
156
- var inner strings.Builder
157
- // "One of" rather than "if": exactly one branch runs, and a reader
158
- // who takes the branches for a sequence reads the flow backwards.
159
- inner.WriteString("**One of**\n")
160
- for _, branch := range n.Branches {
161
- title := branch.Title
162
- if branch.Terminal {
163
- title += " — *ends the flow*"
164
- }
165
- inner.WriteString("\n*" + title + "*\n\n")
166
- inner.WriteString(s.stepList(self, flow, branch.Steps, counter))
167
- }
168
- b.WriteString("\n" + quote(inner.String()) + "\n")
169
-
170
- case *catalog.Loop:
171
- var inner strings.Builder
172
- inner.WriteString("**Repeats** — " + n.Title + "\n\n")
173
- inner.WriteString(s.stepList(self, flow, n.Steps, counter))
174
- b.WriteString("\n" + quote(inner.String()) + "\n")
175
- }
176
- }
177
-
178
- return b.String()
179
- }
180
-
181
- // quote wraps a block so it nests visibly, at any depth, without markdown
182
- // mistaking the indentation for something else.
183
- func quote(block string) string {
184
- lines := strings.Split(strings.TrimRight(block, "\n"), "\n")
185
- for i, line := range lines {
186
- if line == "" {
187
- lines[i] = ">"
188
-
189
- continue
190
- }
191
- lines[i] = "> " + line
192
- }
193
-
194
- return strings.Join(lines, "\n") + "\n"
195
- }
196
-
197
- // stepRef links a step to what it refers to. An event id resolves to the
198
- // aggregate page that publishes it; anything else is shown as written.
199
- func (s *site) stepRef(self string, step *catalog.Step) string {
200
- if step.Kind == catalog.StepEvent {
201
- if _, ok := s.eventPage[step.Ref]; ok {
202
- return s.eventRef(self, step.Ref, step.Ref)
203
- }
204
- if step.Status != catalog.StatusUnresolved {
205
- s.b.warn(step.Ref, "step %q refers to event %q, which no service in this catalog publishes", step.ID, step.Ref)
206
- }
207
- }
208
-
209
- return code(step.Ref)
210
- }
211
-
212
- func stepLabel(step *catalog.Step) string {
213
- switch {
214
- case step.Label != "":
215
- return step.Label
216
- case step.Ref != "":
217
- return step.Ref
218
- default:
219
- return string(step.Kind)
220
- }
221
- }
222
-
223
- // mermaidText makes a label safe on a mermaid line. A newline ends the
224
- // statement, a semicolon ends it too, and a bare # opens an entity code.
225
- func mermaidText(s string) string {
226
- return flowmermaid.Text(s)
227
- }
228
-
229
- func orDefault(s, fallback string) string {
230
- if strings.TrimSpace(s) == "" {
231
- return fallback
232
- }
233
-
234
- return s
235
- }
236
-
237
- // labelWithAnswer is the step's label, and what comes back when a contract
238
- // says so.
239
- func (s *site) labelWithAnswer(step *catalog.Step) string {
240
- if answer := s.answer(step); answer != "" {
241
- return stepLabel(step) + " → " + answer
242
- }
243
-
244
- return stepLabel(step)
245
- }
246
-
247
- // answer is what the far end of an rpc hands back, as the contract names it.
248
- // The mirror of src/flow/answers.ts, and the prose is there.
249
- //
250
- // Only an rpc has one: a call lands inside a service, which no interface
251
- // describes, and an event is a publication - a reply drawn to one would be a
252
- // lie about the bus.
253
- func (s *site) answer(step *catalog.Step) string {
254
- if step.Kind != catalog.StepRPC {
255
- return ""
256
- }
257
-
258
- // Outgoing: the step names the call, and a call id is "<interface>/<method>".
259
- if step.Ref != "" {
260
- return s.methodOf[step.Ref].Response
261
- }
262
-
263
- // Incoming: somebody called this service, and the label is the operation.
264
- service, ok := s.services[step.To]
265
- if !ok || step.Label == "" {
266
- return ""
267
- }
268
- for i := range service.Provides {
269
- for j := range service.Provides[i].Methods {
270
- if service.Provides[i].Methods[j].Name == step.Label {
271
- return service.Provides[i].Methods[j].Response
272
- }
273
- }
274
- }
275
-
276
- return ""
277
- }
@@ -1,76 +0,0 @@
1
- package main
2
-
3
- // The vocabulary of one context, as a page of its own.
4
- //
5
- // It is not folded into the context's README for the same reason the source
6
- // is its own file: the README says what the services do, the glossary says
7
- // what the words in that sentence mean, and a reader consults them at
8
- // different moments. It is also the page a model reading this estate wants
9
- // FIRST - every other page is written in these words.
10
- //
11
- // Nothing here is derived. A definition is the author's sentence, carried
12
- // through as written, and the only thing this file decides is the order it is
13
- // read in and where the words came from.
14
-
15
- import (
16
- "sort"
17
- "strconv"
18
- "strings"
19
-
20
- "github.com/shortlink-org/portolan/catalog"
21
- )
22
-
23
- const glossaryFile = "glossary.md"
24
-
25
- // glossaryPath is where a context's vocabulary is documented: beside its
26
- // README, so the two sit together in the tree the way they sit together in
27
- // the repository the terms were read from.
28
- func (s *site) glossaryPath(ctx *catalog.BoundedContext) string {
29
- return ctx.Slug + "/" + glossaryFile
30
- }
31
-
32
- func (s *site) renderGlossary(ctx *catalog.BoundedContext) {
33
- terms := s.termsOf[ctx.ID]
34
- if len(terms) == 0 {
35
- return
36
- }
37
- self := s.glossaryPath(ctx)
38
-
39
- var b strings.Builder
40
- b.WriteString("# Glossary — " + ctx.Name + "\n\n")
41
- b.WriteString(s.stamp() + "\n")
42
- b.WriteString(defList([][]string{
43
- {"Context", s.ref(self, ctx.ID, ctx.Name)},
44
- {"Terms", strconv.Itoa(len(terms))},
45
- {"Read from", strings.Join(s.termSources(self, terms), ", ")},
46
- }))
47
-
48
- b.WriteString("\nOne meaning per word inside this context, as the glossary" +
49
- " beside the code states it.\n")
50
-
51
- // A list rather than a table. A definition is a sentence or two of prose,
52
- // and a table cell is where a sentence goes to be truncated - the same
53
- // reason the source file is prose. A list also diffs by the term.
54
- var out strings.Builder
55
- for _, term := range terms {
56
- out.WriteString("- **" + term.Name + "** — " + term.Definition + "\n")
57
- }
58
- section(&b, "Terms", out.String())
59
-
60
- s.b.file(self, b.String())
61
- }
62
-
63
- func (s *site) termSources(from string, terms []*catalog.Term) []string {
64
- seen := map[string]bool{}
65
- var out []string
66
- for _, term := range terms {
67
- file := sourceFile(term.Source)
68
- if file == "" || seen[file] {
69
- continue
70
- }
71
- seen[file] = true
72
- out = append(out, s.source(from, file, s.serviceForSource(file)))
73
- }
74
- sort.Strings(out)
75
- return out
76
- }
@@ -1,148 +0,0 @@
1
- package main
2
-
3
- // What the glossary page promises, said once here rather than read out of the
4
- // golden tree: the golden proves the bytes, these prove the intent.
5
-
6
- import (
7
- "encoding/json"
8
- "os"
9
- "strings"
10
- "testing"
11
-
12
- "github.com/shortlink-org/portolan/catalog"
13
- "github.com/shortlink-org/portolan/plugin"
14
- )
15
-
16
- func rendered(t *testing.T) map[string]string {
17
- t.Helper()
18
-
19
- raw, err := os.ReadFile("testdata/catalog.json")
20
- if err != nil {
21
- t.Fatal(err)
22
- }
23
- var cat catalog.Catalog
24
- if err := json.Unmarshal(raw, &cat); err != nil {
25
- t.Fatal(err)
26
- }
27
-
28
- out := map[string]string{}
29
- for _, file := range render(plugin.Request{Catalog: cat}, Options{Title: "Golden estate"}).Files {
30
- out[file.Name] = file.Contents
31
- }
32
-
33
- return out
34
- }
35
-
36
- func page(t *testing.T, name string) string {
37
- t.Helper()
38
-
39
- pages := rendered(t)
40
- body, ok := pages[name]
41
- if !ok {
42
- t.Fatalf("no page %s", name)
43
- }
44
-
45
- return body
46
- }
47
-
48
- // A page of its own, beside the context's README: the README says what the
49
- // services do, the glossary says what the words in that sentence mean.
50
- func TestGlossaryIsItsOwnPage(t *testing.T) {
51
- body := page(t, "billing/glossary.md")
52
-
53
- if !strings.HasPrefix(body, "# Glossary — Billing\n") {
54
- t.Errorf("the page opens with %q", strings.SplitN(body, "\n", 2)[0])
55
- }
56
- for _, want := range []string{
57
- "**Invoice** — What a customer is asked to pay",
58
- "**Dunning** — Asking again, on a schedule",
59
- } {
60
- if !strings.Contains(body, want) {
61
- t.Errorf("no %q in:\n%s", want, body)
62
- }
63
- }
64
- }
65
-
66
- // Alphabetical, whatever order the sources merged in. A glossary is looked up,
67
- // not read through, and catalog order is nobody's index.
68
- func TestTermsAreAlphabetical(t *testing.T) {
69
- body := page(t, "billing/glossary.md")
70
-
71
- dunning := strings.Index(body, "**Dunning**")
72
- invoice := strings.Index(body, "**Invoice**")
73
- if dunning < 0 || invoice < 0 || dunning > invoice {
74
- t.Errorf("Dunning at %d, Invoice at %d", dunning, invoice)
75
- }
76
- }
77
-
78
- // The file the words were read from, named once on the page rather than once
79
- // per term: the reader who disagrees with a definition edits that file.
80
- func TestThePageNamesWhereTheWordsWereWritten(t *testing.T) {
81
- if body := page(t, "billing/glossary.md"); !strings.Contains(body, "examples/billing/GLOSSARY.md") {
82
- t.Errorf("the page does not say where it was read from:\n%s", body)
83
- }
84
- }
85
-
86
- func TestTheContextPageLinksItsGlossary(t *testing.T) {
87
- if body := page(t, "billing/README.md"); !strings.Contains(body, "[Glossary](glossary.md)") {
88
- t.Errorf("the context page does not link the glossary:\n%s", body)
89
- }
90
- }
91
-
92
- func TestServiceReadmeLinksUseGeneratedAdrAndGlossaryPages(t *testing.T) {
93
- body := page(t, "billing/invoices/README.md")
94
-
95
- for _, want := range []string{
96
- "[GLOSSARY.md](../glossary.md)",
97
- "[billing.0001](../../adr/billing.0001.md)",
98
- } {
99
- if !strings.Contains(body, want) {
100
- t.Errorf("the service README does not rewrite %q:\n%s", want, body)
101
- }
102
- }
103
- if strings.Contains(body, "](GLOSSARY.md)") || strings.Contains(body, "](../../docs/adr/0001.md)") {
104
- t.Errorf("the copied source links survived unchanged:\n%s", body)
105
- }
106
- }
107
-
108
- // Straight after the contexts, before the pages that are written in these
109
- // words. A model reading the estate cold has the same problem a person has.
110
- func TestLlmsListsTheGlossaries(t *testing.T) {
111
- body := page(t, "llms.txt")
112
-
113
- if !strings.Contains(body, "## Glossaries") {
114
- t.Fatalf("no Glossaries section:\n%s", body)
115
- }
116
- if !strings.Contains(body, "[Glossary of Billing](billing/glossary.md): 2 terms. Dunning, Invoice") {
117
- t.Errorf("the entry is not what it should be:\n%s", body)
118
- }
119
- if strings.Index(body, "## Glossaries") > strings.Index(body, "## Services") {
120
- t.Error("the glossaries sit after the services")
121
- }
122
- }
123
-
124
- // An estate that has written no glossary renders exactly as it did before
125
- // there was one to write.
126
- func TestAContextWithNoGlossaryGetsNoPage(t *testing.T) {
127
- raw, err := os.ReadFile("testdata/catalog.json")
128
- if err != nil {
129
- t.Fatal(err)
130
- }
131
- var cat catalog.Catalog
132
- if err := json.Unmarshal(raw, &cat); err != nil {
133
- t.Fatal(err)
134
- }
135
- cat.Terms = nil
136
-
137
- for _, file := range render(plugin.Request{Catalog: cat}, Options{}).Files {
138
- if strings.HasSuffix(file.Name, glossaryFile) {
139
- t.Errorf("rendered %s for a catalog with no terms", file.Name)
140
- }
141
- if file.Name == "llms.txt" && strings.Contains(file.Contents, "Glossaries") {
142
- t.Error("llms.txt has a Glossaries section with no glossaries")
143
- }
144
- if file.Name == "billing/README.md" && strings.Contains(file.Contents, "## Language") {
145
- t.Error("the context page has a Language section with no terms")
146
- }
147
- }
148
- }