@shortlink-org/portolan 0.1.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 (826) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +387 -0
  3. package/catalog/enum_test.go +46 -0
  4. package/catalog/model.go +932 -0
  5. package/catalog/roundtrip_test.go +185 -0
  6. package/catalog/via_test.go +38 -0
  7. package/cli/portolan.mjs +368 -0
  8. package/go.mod +14 -0
  9. package/go.sum +14 -0
  10. package/index.html +13 -0
  11. package/internal/commands/cargo.go +154 -0
  12. package/internal/commands/commands.go +143 -0
  13. package/internal/commands/commands_test.go +323 -0
  14. package/internal/commands/gradle.go +77 -0
  15. package/internal/commands/justfile.go +82 -0
  16. package/internal/commands/makefile.go +106 -0
  17. package/internal/commands/maven.go +93 -0
  18. package/internal/commands/packagejson.go +159 -0
  19. package/internal/commands/pyproject.go +169 -0
  20. package/internal/commands/taskfile.go +101 -0
  21. package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
  22. package/internal/commands/testdata/estate/Makefile +33 -0
  23. package/internal/commands/testdata/estate/Taskfile.yml +21 -0
  24. package/internal/commands/testdata/estate/build.gradle.kts +21 -0
  25. package/internal/commands/testdata/estate/justfile +20 -0
  26. package/internal/commands/testdata/estate/package.json +13 -0
  27. package/internal/commands/testdata/estate/pom.xml +29 -0
  28. package/internal/commands/testdata/estate/pyproject.toml +27 -0
  29. package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
  30. package/internal/commands/testdata/golden/commands.json +305 -0
  31. package/internal/gohttp/analyze.go +2500 -0
  32. package/internal/gohttp/endpoints.go +1067 -0
  33. package/internal/gohttp/roots.go +320 -0
  34. package/internal/gohttp/typed.go +143 -0
  35. package/internal/goscan/constants.go +85 -0
  36. package/internal/goscan/goscan_test.go +227 -0
  37. package/internal/goscan/names.go +52 -0
  38. package/internal/goscan/parse_test.go +11 -0
  39. package/internal/goscan/source.go +36 -0
  40. package/internal/goscan/tree.go +155 -0
  41. package/internal/goscan/types.go +99 -0
  42. package/internal/wsdl/ids.go +127 -0
  43. package/internal/wsdl/ids_test.go +21 -0
  44. package/internal/wsdl/model.go +62 -0
  45. package/internal/wsdl/parse.go +920 -0
  46. package/internal/wsdl/parse_test.go +133 -0
  47. package/package.json +117 -0
  48. package/plugin/describe.go +107 -0
  49. package/plugin/describe_test.go +114 -0
  50. package/plugin/protocol.go +117 -0
  51. package/plugin/schematest/schematest.go +126 -0
  52. package/plugins/README.md +1160 -0
  53. package/plugins/extract-adr/describe.go +19 -0
  54. package/plugins/extract-adr/describe_test.go +11 -0
  55. package/plugins/extract-adr/extract.go +153 -0
  56. package/plugins/extract-adr/extract_test.go +350 -0
  57. package/plugins/extract-adr/history.go +99 -0
  58. package/plugins/extract-adr/main.go +65 -0
  59. package/plugins/extract-adr/options.schema.json +26 -0
  60. package/plugins/extract-adr/parse.go +642 -0
  61. package/plugins/extract-adr/parse_test.go +405 -0
  62. package/plugins/extract-asyncapi/describe.go +19 -0
  63. package/plugins/extract-asyncapi/describe_test.go +11 -0
  64. package/plugins/extract-asyncapi/extract.go +315 -0
  65. package/plugins/extract-asyncapi/extract_test.go +197 -0
  66. package/plugins/extract-asyncapi/main.go +48 -0
  67. package/plugins/extract-asyncapi/options.schema.json +26 -0
  68. package/plugins/extract-asyncapi/spec.go +151 -0
  69. package/plugins/extract-celery/README.md +169 -0
  70. package/plugins/extract-celery/extract.py +346 -0
  71. package/plugins/extract-celery/extract_test.py +192 -0
  72. package/plugins/extract-celery/main.py +66 -0
  73. package/plugins/extract-celery/options.py +31 -0
  74. package/plugins/extract-celery/options.schema.json +31 -0
  75. package/plugins/extract-celery/producers.py +156 -0
  76. package/plugins/extract-celery/routes_test.py +106 -0
  77. package/plugins/extract-commands/describe.go +19 -0
  78. package/plugins/extract-commands/describe_test.go +11 -0
  79. package/plugins/extract-commands/extract.go +79 -0
  80. package/plugins/extract-commands/extract_test.go +92 -0
  81. package/plugins/extract-commands/main.go +55 -0
  82. package/plugins/extract-commands/options.schema.json +20 -0
  83. package/plugins/extract-csr/avro.go +258 -0
  84. package/plugins/extract-csr/describe.go +19 -0
  85. package/plugins/extract-csr/describe_test.go +11 -0
  86. package/plugins/extract-csr/extract.go +338 -0
  87. package/plugins/extract-csr/extract_test.go +374 -0
  88. package/plugins/extract-csr/jsonschema.go +343 -0
  89. package/plugins/extract-csr/lock.go +25 -0
  90. package/plugins/extract-csr/main.go +84 -0
  91. package/plugins/extract-csr/options.schema.json +43 -0
  92. package/plugins/extract-csr/subject.go +102 -0
  93. package/plugins/extract-django/README.md +386 -0
  94. package/plugins/extract-django/apps.py +100 -0
  95. package/plugins/extract-django/auth.py +302 -0
  96. package/plugins/extract-django/auth_test.py +188 -0
  97. package/plugins/extract-django/clients.py +121 -0
  98. package/plugins/extract-django/contracts.py +458 -0
  99. package/plugins/extract-django/database.py +119 -0
  100. package/plugins/extract-django/domain.py +318 -0
  101. package/plugins/extract-django/events.py +86 -0
  102. package/plugins/extract-django/extract.py +601 -0
  103. package/plugins/extract-django/extract_test.py +397 -0
  104. package/plugins/extract-django/flows.py +1278 -0
  105. package/plugins/extract-django/flows_test.py +338 -0
  106. package/plugins/extract-django/ids.py +42 -0
  107. package/plugins/extract-django/ids_test.py +30 -0
  108. package/plugins/extract-django/lifecycle.py +222 -0
  109. package/plugins/extract-django/lifecycle_test.py +76 -0
  110. package/plugins/extract-django/main.py +66 -0
  111. package/plugins/extract-django/openapi.py +229 -0
  112. package/plugins/extract-django/openapi_test.py +60 -0
  113. package/plugins/extract-django/operations.py +96 -0
  114. package/plugins/extract-django/options.py +61 -0
  115. package/plugins/extract-django/options.schema.json +126 -0
  116. package/plugins/extract-django/routing.py +214 -0
  117. package/plugins/extract-django/routing_test.py +198 -0
  118. package/plugins/extract-django/serializers.py +558 -0
  119. package/plugins/extract-django/store.py +434 -0
  120. package/plugins/extract-django/transport.py +242 -0
  121. package/plugins/extract-flows/describe.go +19 -0
  122. package/plugins/extract-flows/describe_test.go +11 -0
  123. package/plugins/extract-flows/extract.go +91 -0
  124. package/plugins/extract-flows/main.go +45 -0
  125. package/plugins/extract-flows/options.schema.json +16 -0
  126. package/plugins/extract-flows/parse.go +593 -0
  127. package/plugins/extract-flows/parse_test.go +204 -0
  128. package/plugins/extract-glossary/describe.go +19 -0
  129. package/plugins/extract-glossary/describe_test.go +11 -0
  130. package/plugins/extract-glossary/extract.go +115 -0
  131. package/plugins/extract-glossary/extract_test.go +220 -0
  132. package/plugins/extract-glossary/main.go +59 -0
  133. package/plugins/extract-glossary/options.schema.json +21 -0
  134. package/plugins/extract-glossary/parse.go +214 -0
  135. package/plugins/extract-glossary/parse_test.go +203 -0
  136. package/plugins/extract-go/aggregate.go +214 -0
  137. package/plugins/extract-go/client.go +409 -0
  138. package/plugins/extract-go/client_test.go +266 -0
  139. package/plugins/extract-go/describe.go +19 -0
  140. package/plugins/extract-go/describe_test.go +11 -0
  141. package/plugins/extract-go/enum.go +195 -0
  142. package/plugins/extract-go/enum_test.go +82 -0
  143. package/plugins/extract-go/event.go +99 -0
  144. package/plugins/extract-go/extract.go +191 -0
  145. package/plugins/extract-go/extract_test.go +261 -0
  146. package/plugins/extract-go/flow.go +1441 -0
  147. package/plugins/extract-go/flow_test.go +609 -0
  148. package/plugins/extract-go/httpclient.go +174 -0
  149. package/plugins/extract-go/httpclient_test.go +296 -0
  150. package/plugins/extract-go/ids.go +92 -0
  151. package/plugins/extract-go/layout.go +225 -0
  152. package/plugins/extract-go/layout_test.go +125 -0
  153. package/plugins/extract-go/lifecycle.go +324 -0
  154. package/plugins/extract-go/lifecycle_test.go +108 -0
  155. package/plugins/extract-go/main.go +79 -0
  156. package/plugins/extract-go/operation.go +157 -0
  157. package/plugins/extract-go/options.schema.json +66 -0
  158. package/plugins/extract-go/source.go +316 -0
  159. package/plugins/extract-go/transport.go +321 -0
  160. package/plugins/extract-go/transport_test.go +145 -0
  161. package/plugins/extract-go/wiring.go +434 -0
  162. package/plugins/extract-go-nats/describe.go +19 -0
  163. package/plugins/extract-go-nats/describe_test.go +11 -0
  164. package/plugins/extract-go-nats/extract.go +177 -0
  165. package/plugins/extract-go-nats/extract_test.go +389 -0
  166. package/plugins/extract-go-nats/index.go +379 -0
  167. package/plugins/extract-go-nats/main.go +42 -0
  168. package/plugins/extract-go-nats/options.schema.json +21 -0
  169. package/plugins/extract-go-nats/resolve.go +161 -0
  170. package/plugins/extract-go-nats/sites.go +224 -0
  171. package/plugins/extract-graphql/describe.go +19 -0
  172. package/plugins/extract-graphql/describe_test.go +11 -0
  173. package/plugins/extract-graphql/extract.go +433 -0
  174. package/plugins/extract-graphql/extract_test.go +256 -0
  175. package/plugins/extract-graphql/ids.go +49 -0
  176. package/plugins/extract-graphql/lex.go +237 -0
  177. package/plugins/extract-graphql/main.go +51 -0
  178. package/plugins/extract-graphql/options.schema.json +31 -0
  179. package/plugins/extract-graphql/parse.go +621 -0
  180. package/plugins/extract-graphql/parse_test.go +122 -0
  181. package/plugins/extract-http-clients/describe.go +19 -0
  182. package/plugins/extract-http-clients/describe_test.go +11 -0
  183. package/plugins/extract-http-clients/extract.go +705 -0
  184. package/plugins/extract-http-clients/extract_test.go +1263 -0
  185. package/plugins/extract-http-clients/main.go +42 -0
  186. package/plugins/extract-http-clients/options.schema.json +33 -0
  187. package/plugins/extract-java/README.md +190 -0
  188. package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
  189. package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
  190. package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
  191. package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
  192. package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
  193. package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
  194. package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
  195. package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
  196. package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
  197. package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
  198. package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
  199. package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
  200. package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
  201. package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
  202. package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
  203. package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
  204. package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
  205. package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
  206. package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
  207. package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
  208. package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
  209. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
  210. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
  211. package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
  212. package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
  213. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
  214. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
  215. package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
  216. package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
  217. package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
  218. package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
  219. package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
  220. package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
  221. package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
  222. package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
  223. package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
  224. package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
  225. package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
  226. package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
  227. package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
  228. package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
  229. package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
  230. package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
  231. package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
  232. package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
  233. package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
  234. package/plugins/extract-java/options.schema.json +70 -0
  235. package/plugins/extract-openapi/describe.go +19 -0
  236. package/plugins/extract-openapi/describe_test.go +11 -0
  237. package/plugins/extract-openapi/discover.go +243 -0
  238. package/plugins/extract-openapi/extract.go +526 -0
  239. package/plugins/extract-openapi/extract_test.go +545 -0
  240. package/plugins/extract-openapi/main.go +75 -0
  241. package/plugins/extract-openapi/options.schema.json +62 -0
  242. package/plugins/extract-openapi/spec.go +350 -0
  243. package/plugins/extract-project/README.md +22 -0
  244. package/plugins/extract-project/describe.go +19 -0
  245. package/plugins/extract-project/describe_test.go +11 -0
  246. package/plugins/extract-project/extract.go +221 -0
  247. package/plugins/extract-project/extract_test.go +109 -0
  248. package/plugins/extract-project/main.go +41 -0
  249. package/plugins/extract-project/options.schema.json +48 -0
  250. package/plugins/extract-proto/ast.go +125 -0
  251. package/plugins/extract-proto/consumes.go +77 -0
  252. package/plugins/extract-proto/describe.go +19 -0
  253. package/plugins/extract-proto/extract.go +293 -0
  254. package/plugins/extract-proto/extract_test.go +459 -0
  255. package/plugins/extract-proto/ids.go +89 -0
  256. package/plugins/extract-proto/ids_test.go +57 -0
  257. package/plugins/extract-proto/lex.go +285 -0
  258. package/plugins/extract-proto/main.go +100 -0
  259. package/plugins/extract-proto/module.go +120 -0
  260. package/plugins/extract-proto/options.schema.json +51 -0
  261. package/plugins/extract-proto/parse.go +720 -0
  262. package/plugins/extract-proto/parse_test.go +307 -0
  263. package/plugins/extract-proto/provides.go +236 -0
  264. package/plugins/extract-proto/resolve.go +222 -0
  265. package/plugins/extract-proto/resolve_test.go +119 -0
  266. package/plugins/extract-python-kafka/README.md +36 -0
  267. package/plugins/extract-python-kafka/extract.py +211 -0
  268. package/plugins/extract-python-kafka/extract_test.py +68 -0
  269. package/plugins/extract-python-kafka/main.py +59 -0
  270. package/plugins/extract-python-kafka/options.py +29 -0
  271. package/plugins/extract-python-kafka/options.schema.json +31 -0
  272. package/plugins/extract-redis/README.md +11 -0
  273. package/plugins/extract-redis/describe.go +19 -0
  274. package/plugins/extract-redis/describe_test.go +11 -0
  275. package/plugins/extract-redis/extract.go +183 -0
  276. package/plugins/extract-redis/extract_test.go +168 -0
  277. package/plugins/extract-redis/keyspaces.go +469 -0
  278. package/plugins/extract-redis/main.go +44 -0
  279. package/plugins/extract-redis/options.schema.json +30 -0
  280. package/plugins/extract-river/describe.go +19 -0
  281. package/plugins/extract-river/describe_test.go +11 -0
  282. package/plugins/extract-river/extract.go +507 -0
  283. package/plugins/extract-river/extract_test.go +132 -0
  284. package/plugins/extract-river/main.go +42 -0
  285. package/plugins/extract-river/options.schema.json +21 -0
  286. package/plugins/extract-rust/Cargo.lock +133 -0
  287. package/plugins/extract-rust/Cargo.toml +23 -0
  288. package/plugins/extract-rust/README.md +204 -0
  289. package/plugins/extract-rust/options.schema.json +65 -0
  290. package/plugins/extract-rust/rustfmt.toml +1 -0
  291. package/plugins/extract-sql/ddl.go +893 -0
  292. package/plugins/extract-sql/ddl_test.go +401 -0
  293. package/plugins/extract-sql/describe.go +19 -0
  294. package/plugins/extract-sql/describe_test.go +11 -0
  295. package/plugins/extract-sql/layout.go +221 -0
  296. package/plugins/extract-sql/layout_test.go +55 -0
  297. package/plugins/extract-sql/lineage.go +107 -0
  298. package/plugins/extract-sql/main.go +141 -0
  299. package/plugins/extract-sql/maps.go +564 -0
  300. package/plugins/extract-sql/maps_java.go +117 -0
  301. package/plugins/extract-sql/maps_rust.go +333 -0
  302. package/plugins/extract-sql/maps_rust_test.go +70 -0
  303. package/plugins/extract-sql/maps_test.go +204 -0
  304. package/plugins/extract-sql/maps_ts.go +398 -0
  305. package/plugins/extract-sql/maps_ts_test.go +136 -0
  306. package/plugins/extract-sql/options.schema.json +53 -0
  307. package/plugins/extract-sql/projection.go +70 -0
  308. package/plugins/extract-sql/projection_test.go +50 -0
  309. package/plugins/extract-sql/store.go +420 -0
  310. package/plugins/extract-sql/store_test.go +233 -0
  311. package/plugins/extract-sql/view.go +295 -0
  312. package/plugins/extract-ts/README.md +306 -0
  313. package/plugins/extract-ts/ast.test.ts +178 -0
  314. package/plugins/extract-ts/ast.ts +648 -0
  315. package/plugins/extract-ts/clients.ts +185 -0
  316. package/plugins/extract-ts/domain.ts +164 -0
  317. package/plugins/extract-ts/extract.test.ts +208 -0
  318. package/plugins/extract-ts/extract.ts +200 -0
  319. package/plugins/extract-ts/flows.ts +783 -0
  320. package/plugins/extract-ts/graphql.test.ts +73 -0
  321. package/plugins/extract-ts/graphql.ts +181 -0
  322. package/plugins/extract-ts/ids.test.ts +21 -0
  323. package/plugins/extract-ts/ids.ts +52 -0
  324. package/plugins/extract-ts/lifecycle.ts +138 -0
  325. package/plugins/extract-ts/main.ts +48 -0
  326. package/plugins/extract-ts/openapi.test.ts +64 -0
  327. package/plugins/extract-ts/openapi.ts +99 -0
  328. package/plugins/extract-ts/operations.ts +93 -0
  329. package/plugins/extract-ts/options.schema.json +96 -0
  330. package/plugins/extract-ts/source.test.ts +174 -0
  331. package/plugins/extract-ts/source.ts +465 -0
  332. package/plugins/extract-ts/transport.ts +147 -0
  333. package/plugins/extract-ts/tsconfig.json +13 -0
  334. package/plugins/extract-ts/wiring.ts +190 -0
  335. package/plugins/extract-watermill/describe.go +19 -0
  336. package/plugins/extract-watermill/describe_test.go +11 -0
  337. package/plugins/extract-watermill/extract.go +1228 -0
  338. package/plugins/extract-watermill/extract_test.go +234 -0
  339. package/plugins/extract-watermill/main.go +41 -0
  340. package/plugins/extract-watermill/options.schema.json +21 -0
  341. package/plugins/extract-wsdl/describe.go +19 -0
  342. package/plugins/extract-wsdl/describe_test.go +11 -0
  343. package/plugins/extract-wsdl/extract.go +148 -0
  344. package/plugins/extract-wsdl/extract_test.go +45 -0
  345. package/plugins/extract-wsdl/main.go +53 -0
  346. package/plugins/extract-wsdl/options.schema.json +59 -0
  347. package/plugins/fetch-bsr/auth.go +114 -0
  348. package/plugins/fetch-bsr/bsr.go +240 -0
  349. package/plugins/fetch-bsr/cache.go +66 -0
  350. package/plugins/fetch-bsr/describe.go +19 -0
  351. package/plugins/fetch-bsr/fetch.go +192 -0
  352. package/plugins/fetch-bsr/fetch_test.go +465 -0
  353. package/plugins/fetch-bsr/lock.go +71 -0
  354. package/plugins/fetch-bsr/main.go +117 -0
  355. package/plugins/fetch-bsr/options.schema.json +42 -0
  356. package/plugins/fetch-csr/auth.go +95 -0
  357. package/plugins/fetch-csr/cache.go +69 -0
  358. package/plugins/fetch-csr/describe.go +19 -0
  359. package/plugins/fetch-csr/describe_test.go +11 -0
  360. package/plugins/fetch-csr/fetch.go +254 -0
  361. package/plugins/fetch-csr/fetch_test.go +494 -0
  362. package/plugins/fetch-csr/lock.go +95 -0
  363. package/plugins/fetch-csr/main.go +122 -0
  364. package/plugins/fetch-csr/options.schema.json +42 -0
  365. package/plugins/fetch-csr/registry.go +209 -0
  366. package/plugins/fetch-git/cache.go +65 -0
  367. package/plugins/fetch-git/describe.go +19 -0
  368. package/plugins/fetch-git/describe_test.go +11 -0
  369. package/plugins/fetch-git/fetch.go +181 -0
  370. package/plugins/fetch-git/fetch_test.go +332 -0
  371. package/plugins/fetch-git/git.go +169 -0
  372. package/plugins/fetch-git/lock.go +72 -0
  373. package/plugins/fetch-git/main.go +127 -0
  374. package/plugins/fetch-git/offline.go +39 -0
  375. package/plugins/fetch-git/options.schema.json +42 -0
  376. package/plugins/fetch-git/pin.go +90 -0
  377. package/plugins/fetch-git/pin_test.go +74 -0
  378. package/plugins/gen-backstage/describe.go +17 -0
  379. package/plugins/gen-backstage/main.go +22 -0
  380. package/plugins/gen-backstage/options.schema.json +30 -0
  381. package/plugins/gen-backstage/plugin.go +473 -0
  382. package/plugins/gen-backstage/plugin_test.go +145 -0
  383. package/plugins/gen-backstage.wasm +0 -0
  384. package/plugins/gen-markdown/adr.go +162 -0
  385. package/plugins/gen-markdown/aggregate.go +362 -0
  386. package/plugins/gen-markdown/canonical.go +93 -0
  387. package/plugins/gen-markdown/context.go +90 -0
  388. package/plugins/gen-markdown/coverage_test.go +89 -0
  389. package/plugins/gen-markdown/describe.go +19 -0
  390. package/plugins/gen-markdown/describe_test.go +11 -0
  391. package/plugins/gen-markdown/external.go +71 -0
  392. package/plugins/gen-markdown/flow.go +277 -0
  393. package/plugins/gen-markdown/glossary.go +76 -0
  394. package/plugins/gen-markdown/glossary_test.go +148 -0
  395. package/plugins/gen-markdown/llms.go +302 -0
  396. package/plugins/gen-markdown/main.go +22 -0
  397. package/plugins/gen-markdown/markdown.go +254 -0
  398. package/plugins/gen-markdown/markdown_test.go +100 -0
  399. package/plugins/gen-markdown/module.go +107 -0
  400. package/plugins/gen-markdown/options.schema.json +16 -0
  401. package/plugins/gen-markdown/plugin.go +39 -0
  402. package/plugins/gen-markdown/quality_test.go +176 -0
  403. package/plugins/gen-markdown/redis_test.go +38 -0
  404. package/plugins/gen-markdown/render.go +344 -0
  405. package/plugins/gen-markdown/render_test.go +186 -0
  406. package/plugins/gen-markdown/service.go +456 -0
  407. package/plugins/gen-markdown/source.go +177 -0
  408. package/plugins/gen-markdown/store.go +201 -0
  409. package/plugins/gen-markdown.wasm +0 -0
  410. package/plugins/gen-mermaid/describe.go +17 -0
  411. package/plugins/gen-mermaid/main.go +22 -0
  412. package/plugins/gen-mermaid/options.schema.json +10 -0
  413. package/plugins/gen-mermaid/plugin.go +104 -0
  414. package/plugins/gen-mermaid/plugin_test.go +33 -0
  415. package/plugins/gen-mermaid.wasm +0 -0
  416. package/plugins/openapi/ids.go +261 -0
  417. package/plugins/openapi/ids_test.go +98 -0
  418. package/plugins/pyplugin/catalog.py +209 -0
  419. package/plugins/pyplugin/celery_conf.py +436 -0
  420. package/plugins/pyplugin/celery_tasks.py +76 -0
  421. package/plugins/pyplugin/kafka.py +570 -0
  422. package/plugins/pyplugin/names.py +38 -0
  423. package/plugins/pyplugin/protocol.py +79 -0
  424. package/plugins/pyplugin/source.py +285 -0
  425. package/plugins/verify-codeowners/describe.go +19 -0
  426. package/plugins/verify-codeowners/describe_test.go +11 -0
  427. package/plugins/verify-codeowners/main.go +75 -0
  428. package/plugins/verify-codeowners/match.go +85 -0
  429. package/plugins/verify-codeowners/match_test.go +47 -0
  430. package/plugins/verify-codeowners/options.schema.json +15 -0
  431. package/plugins/verify-codeowners/owners.go +164 -0
  432. package/plugins/verify-codeowners/owners_test.go +225 -0
  433. package/plugins/verify-codeowners/parse.go +90 -0
  434. package/plugins/verify-codeowners/parse_test.go +62 -0
  435. package/plugins/verify-otel/describe.go +19 -0
  436. package/plugins/verify-otel/describe_test.go +11 -0
  437. package/plugins/verify-otel/main.go +53 -0
  438. package/plugins/verify-otel/match.go +336 -0
  439. package/plugins/verify-otel/options.schema.json +30 -0
  440. package/plugins/verify-otel/otlp.go +200 -0
  441. package/plugins/verify-otel/verify.go +734 -0
  442. package/plugins/verify-otel/verify_test.go +460 -0
  443. package/portolan.json +1073 -0
  444. package/public/favicon.svg +12 -0
  445. package/schema/portolan.schema.json +2558 -0
  446. package/scripts/README.md +53 -0
  447. package/scripts/build-report.mjs +60 -0
  448. package/scripts/build-report.test.mjs +44 -0
  449. package/scripts/builtin-plugins.mjs +49 -0
  450. package/scripts/catalog-sources.mjs +67 -0
  451. package/scripts/diff.mjs +315 -0
  452. package/scripts/diff.test.mjs +151 -0
  453. package/scripts/forge-comment.mjs +144 -0
  454. package/scripts/forge-comment.test.mjs +151 -0
  455. package/scripts/forge-release.mjs +138 -0
  456. package/scripts/forge-release.test.mjs +106 -0
  457. package/scripts/forge.mjs +61 -0
  458. package/scripts/gen-likec4.mjs +854 -0
  459. package/scripts/gen-likec4.test.mjs +258 -0
  460. package/scripts/gen.mjs +603 -0
  461. package/scripts/generated-docs.test.mjs +66 -0
  462. package/scripts/local-api.mjs +1192 -0
  463. package/scripts/local-api.test.mjs +387 -0
  464. package/scripts/manifest.mjs +214 -0
  465. package/scripts/manifest.test.mjs +148 -0
  466. package/scripts/output-path.mjs +79 -0
  467. package/scripts/output-path.test.mjs +45 -0
  468. package/scripts/package-smoke.mjs +37 -0
  469. package/scripts/plugin-host.mjs +419 -0
  470. package/scripts/plugin-host.test.mjs +109 -0
  471. package/scripts/plugin-wasm-worker.mjs +34 -0
  472. package/scripts/run-builtin.mjs +81 -0
  473. package/scripts/schema.mjs +357 -0
  474. package/scripts/site-docs.mjs +295 -0
  475. package/scripts/site-docs.test.mjs +181 -0
  476. package/scripts/vendor-lock.mjs +58 -0
  477. package/scripts/vendor-lock.test.mjs +69 -0
  478. package/src/README.md +38 -0
  479. package/src/app/App.tsx +448 -0
  480. package/src/app/BranchPicker.test.ts +46 -0
  481. package/src/app/BranchPicker.tsx +153 -0
  482. package/src/app/Breadcrumbs.test.ts +38 -0
  483. package/src/app/Breadcrumbs.tsx +223 -0
  484. package/src/app/BuildStamp.tsx +146 -0
  485. package/src/app/CommandPalette.tsx +292 -0
  486. package/src/app/HashScroll.tsx +45 -0
  487. package/src/app/Sidebar.tsx +1742 -0
  488. package/src/app/TopBar.tsx +393 -0
  489. package/src/app/density.tsx +64 -0
  490. package/src/app/forge-access.test.ts +27 -0
  491. package/src/app/forge-access.tsx +80 -0
  492. package/src/app/panels.tsx +141 -0
  493. package/src/app/pins.tsx +242 -0
  494. package/src/app/query-client.ts +24 -0
  495. package/src/app/responsive.ts +55 -0
  496. package/src/app/shortcuts.test.ts +39 -0
  497. package/src/app/shortcuts.tsx +282 -0
  498. package/src/app/theme.tsx +51 -0
  499. package/src/app/title.test.ts +14 -0
  500. package/src/app/title.ts +30 -0
  501. package/src/app/toast.tsx +59 -0
  502. package/src/app/tree-keys.test.ts +80 -0
  503. package/src/app/tree-keys.ts +87 -0
  504. package/src/app/ui-store.ts +52 -0
  505. package/src/catalog-error.test.ts +105 -0
  506. package/src/catalog-profile.test.ts +59 -0
  507. package/src/catalog-profile.ts +101 -0
  508. package/src/catalog-stores.test.ts +431 -0
  509. package/src/catalog.test.ts +1215 -0
  510. package/src/catalog.ts +3125 -0
  511. package/src/chat/Cards.tsx +408 -0
  512. package/src/chat/ChatPanel.tsx +79 -0
  513. package/src/chat/ChatSettings.tsx +219 -0
  514. package/src/chat/Composer.tsx +99 -0
  515. package/src/chat/Conversation.tsx +281 -0
  516. package/src/chat/Header.tsx +46 -0
  517. package/src/chat/Message.tsx +49 -0
  518. package/src/chat/Notice.tsx +195 -0
  519. package/src/chat/Starter.tsx +67 -0
  520. package/src/chat/Steps.tsx +45 -0
  521. package/src/chat/Waiting.tsx +87 -0
  522. package/src/chat/flags.test.ts +59 -0
  523. package/src/chat/flags.ts +155 -0
  524. package/src/chat/linkify.test.ts +30 -0
  525. package/src/chat/linkify.ts +45 -0
  526. package/src/chat/prefs.ts +56 -0
  527. package/src/chat/prompt.test.ts +43 -0
  528. package/src/chat/prompt.ts +157 -0
  529. package/src/chat/store.ts +35 -0
  530. package/src/chat/tools.ts +58 -0
  531. package/src/chat/transport.ts +70 -0
  532. package/src/components/AdrRow.tsx +29 -0
  533. package/src/components/AnchorLink.tsx +67 -0
  534. package/src/components/ApiReference.tsx +134 -0
  535. package/src/components/AsyncApiReference.tsx +120 -0
  536. package/src/components/CatalogStamp.tsx +155 -0
  537. package/src/components/ChannelRows.tsx +132 -0
  538. package/src/components/CommandRows.tsx +139 -0
  539. package/src/components/DiagramSkeleton.tsx +71 -0
  540. package/src/components/EditorLink.tsx +85 -0
  541. package/src/components/FieldTree.tsx +305 -0
  542. package/src/components/FlowTrigger.tsx +34 -0
  543. package/src/components/Ident.tsx +89 -0
  544. package/src/components/Integrations.tsx +243 -0
  545. package/src/components/LifecycleDiagram.tsx +110 -0
  546. package/src/components/MachineDocs.tsx +42 -0
  547. package/src/components/Markdown.tsx +173 -0
  548. package/src/components/MarkdownTable.tsx +206 -0
  549. package/src/components/Mermaid.tsx +211 -0
  550. package/src/components/MethodRows.tsx +309 -0
  551. package/src/components/Overlay.tsx +125 -0
  552. package/src/components/PageHeader.tsx +189 -0
  553. package/src/components/RowActions.tsx +97 -0
  554. package/src/components/SchemaDocument.tsx +89 -0
  555. package/src/components/Select.tsx +126 -0
  556. package/src/components/ShapeRows.tsx +118 -0
  557. package/src/components/SourceDoc.tsx +82 -0
  558. package/src/components/SourcePreview.tsx +441 -0
  559. package/src/components/TabRow.tsx +182 -0
  560. package/src/components/TechIcon.tsx +34 -0
  561. package/src/components/Toc.tsx +132 -0
  562. package/src/components/WhatLinksHere.tsx +273 -0
  563. package/src/components/WsdlReference.tsx +84 -0
  564. package/src/components/ddd-icons.tsx +107 -0
  565. package/src/components/kind.tsx +157 -0
  566. package/src/components/logo.tsx +61 -0
  567. package/src/components/primitives.tsx +264 -0
  568. package/src/data.test.ts +18 -0
  569. package/src/data.ts +202 -0
  570. package/src/enrich.test.ts +1150 -0
  571. package/src/enrich.ts +672 -0
  572. package/src/er/ErCanvas.tsx +533 -0
  573. package/src/er/RedisSchema.tsx +74 -0
  574. package/src/er/RelationCard.tsx +267 -0
  575. package/src/er/StoreHeader.tsx +155 -0
  576. package/src/er/TableNode.tsx +84 -0
  577. package/src/er/ViewNode.tsx +83 -0
  578. package/src/er/layout.test.ts +115 -0
  579. package/src/er/layout.ts +77 -0
  580. package/src/er/lineage.test.ts +140 -0
  581. package/src/er/lineage.ts +107 -0
  582. package/src/er/markers.tsx +79 -0
  583. package/src/er/spec.test.ts +344 -0
  584. package/src/er/spec.ts +426 -0
  585. package/src/flow/ChainList.tsx +162 -0
  586. package/src/flow/FlowTable.tsx +123 -0
  587. package/src/flow/FlowToolbar.tsx +372 -0
  588. package/src/flow/StepDetail.tsx +380 -0
  589. package/src/flow/StepRail.tsx +428 -0
  590. package/src/flow/answers.test.ts +50 -0
  591. package/src/flow/answers.ts +58 -0
  592. package/src/flow/chain.test.ts +383 -0
  593. package/src/flow/chain.ts +353 -0
  594. package/src/flow/chapters.test.ts +185 -0
  595. package/src/flow/chapters.ts +225 -0
  596. package/src/flow/continues.test.ts +102 -0
  597. package/src/flow/continues.ts +65 -0
  598. package/src/flow/cross-context.test.ts +97 -0
  599. package/src/flow/cross-context.ts +35 -0
  600. package/src/flow/mermaid.test.ts +22 -0
  601. package/src/flow/mermaid.ts +141 -0
  602. package/src/flow/outline.test.ts +212 -0
  603. package/src/flow/outline.ts +170 -0
  604. package/src/flow/paths.test.ts +129 -0
  605. package/src/flow/paths.ts +147 -0
  606. package/src/flow/prefs.test.ts +80 -0
  607. package/src/flow/prefs.ts +104 -0
  608. package/src/graph/DependencyGraph.tsx +283 -0
  609. package/src/graph/DependencyNodes.tsx +162 -0
  610. package/src/graph/EdgeLabel.tsx +61 -0
  611. package/src/graph/FocusedEventGraph.tsx +211 -0
  612. package/src/graph/GraphToolbar.tsx +187 -0
  613. package/src/graph/RoutedEdge.tsx +80 -0
  614. package/src/graph/dependency-layout.test.ts +360 -0
  615. package/src/graph/dependency-layout.ts +444 -0
  616. package/src/graph/elk.test.ts +70 -0
  617. package/src/graph/elk.ts +287 -0
  618. package/src/graph/nodes.tsx +97 -0
  619. package/src/graph/theme.ts +81 -0
  620. package/src/graph/useElkFlow.ts +125 -0
  621. package/src/index.css +1547 -0
  622. package/src/language/InLanguage.tsx +40 -0
  623. package/src/language/TermCard.tsx +132 -0
  624. package/src/language/cards.test.ts +114 -0
  625. package/src/language/cards.ts +111 -0
  626. package/src/lib/admonition.test.ts +49 -0
  627. package/src/lib/admonition.ts +48 -0
  628. package/src/lib/adr.test.ts +131 -0
  629. package/src/lib/adr.ts +68 -0
  630. package/src/lib/api.test.ts +122 -0
  631. package/src/lib/api.ts +135 -0
  632. package/src/lib/backlinks.test.ts +330 -0
  633. package/src/lib/backlinks.ts +567 -0
  634. package/src/lib/branch-compare.ts +16 -0
  635. package/src/lib/build-info.test.ts +76 -0
  636. package/src/lib/build-info.ts +80 -0
  637. package/src/lib/catalog-diff.test.ts +275 -0
  638. package/src/lib/catalog-diff.ts +655 -0
  639. package/src/lib/clipboard.ts +43 -0
  640. package/src/lib/comparison-memory.test.ts +40 -0
  641. package/src/lib/comparison-memory.ts +39 -0
  642. package/src/lib/context-color.ts +27 -0
  643. package/src/lib/context-map.test.ts +148 -0
  644. package/src/lib/context-map.ts +399 -0
  645. package/src/lib/data-model.test.ts +245 -0
  646. package/src/lib/data-model.ts +399 -0
  647. package/src/lib/data-problems.test.ts +178 -0
  648. package/src/lib/data-problems.ts +314 -0
  649. package/src/lib/derive.test.ts +251 -0
  650. package/src/lib/derive.ts +515 -0
  651. package/src/lib/editor-link.test.ts +125 -0
  652. package/src/lib/editor-link.ts +96 -0
  653. package/src/lib/editor-prefs.ts +27 -0
  654. package/src/lib/event-graph.test.ts +237 -0
  655. package/src/lib/event-graph.ts +327 -0
  656. package/src/lib/export-canvas.ts +37 -0
  657. package/src/lib/export-file.test.ts +30 -0
  658. package/src/lib/export-file.ts +20 -0
  659. package/src/lib/flow-tree.test.ts +177 -0
  660. package/src/lib/flow-tree.ts +160 -0
  661. package/src/lib/forge-refs.test.ts +50 -0
  662. package/src/lib/forge-refs.ts +60 -0
  663. package/src/lib/format.test.ts +35 -0
  664. package/src/lib/format.ts +64 -0
  665. package/src/lib/github-catalog.test.ts +301 -0
  666. package/src/lib/github-catalog.ts +610 -0
  667. package/src/lib/integrations.test.ts +119 -0
  668. package/src/lib/integrations.ts +133 -0
  669. package/src/lib/kinds.test.ts +91 -0
  670. package/src/lib/kinds.ts +204 -0
  671. package/src/lib/lifecycle.test.ts +93 -0
  672. package/src/lib/lifecycle.ts +165 -0
  673. package/src/lib/local-api.ts +194 -0
  674. package/src/lib/motion-features.ts +11 -0
  675. package/src/lib/motion.test.ts +120 -0
  676. package/src/lib/motion.tsx +269 -0
  677. package/src/lib/palette-recent.test.ts +80 -0
  678. package/src/lib/palette-recent.ts +90 -0
  679. package/src/lib/palette.test.ts +365 -0
  680. package/src/lib/palette.ts +593 -0
  681. package/src/lib/pins.test.ts +139 -0
  682. package/src/lib/pins.ts +130 -0
  683. package/src/lib/problems.test.ts +112 -0
  684. package/src/lib/project-preview.ts +5 -0
  685. package/src/lib/proto-problems.test.ts +121 -0
  686. package/src/lib/proto-problems.ts +65 -0
  687. package/src/lib/queries.test.ts +113 -0
  688. package/src/lib/queries.ts +131 -0
  689. package/src/lib/registry.test.ts +219 -0
  690. package/src/lib/registry.ts +208 -0
  691. package/src/lib/scenarios.ts +547 -0
  692. package/src/lib/setup-info.test.ts +199 -0
  693. package/src/lib/setup-info.ts +415 -0
  694. package/src/lib/shape.test.ts +289 -0
  695. package/src/lib/shape.ts +454 -0
  696. package/src/lib/sidebar-prefs.test.ts +32 -0
  697. package/src/lib/sidebar-prefs.ts +75 -0
  698. package/src/lib/source-code.test.ts +159 -0
  699. package/src/lib/source-code.ts +286 -0
  700. package/src/lib/source-doc.test.ts +131 -0
  701. package/src/lib/source-doc.ts +103 -0
  702. package/src/lib/source-highlight.test.ts +29 -0
  703. package/src/lib/source-highlight.ts +24 -0
  704. package/src/lib/source-link.test.ts +153 -0
  705. package/src/lib/source-link.ts +201 -0
  706. package/src/lib/spec-files.test.ts +24 -0
  707. package/src/lib/spec-files.ts +23 -0
  708. package/src/lib/table-directive.test.ts +139 -0
  709. package/src/lib/table-directive.ts +106 -0
  710. package/src/lib/tech.test.ts +120 -0
  711. package/src/lib/tech.ts +421 -0
  712. package/src/lib/terms.test.ts +67 -0
  713. package/src/lib/terms.ts +179 -0
  714. package/src/lib/wire-problems.test.ts +263 -0
  715. package/src/lib/wire-problems.ts +281 -0
  716. package/src/likec4/C4View.tsx +55 -0
  717. package/src/likec4/CanvasBridge.tsx +187 -0
  718. package/src/likec4/FlowView.tsx +235 -0
  719. package/src/likec4/InteractiveView.tsx +144 -0
  720. package/src/likec4/canvas-viewport.test.ts +149 -0
  721. package/src/likec4/canvas-viewport.ts +135 -0
  722. package/src/likec4/flow-edges.test.ts +51 -0
  723. package/src/likec4/flow-edges.ts +89 -0
  724. package/src/likec4/frame-theme.test.ts +76 -0
  725. package/src/likec4/frame-theme.ts +101 -0
  726. package/src/likec4/highlight-css.test.ts +49 -0
  727. package/src/likec4/highlight-css.ts +52 -0
  728. package/src/likec4/ids.test.ts +151 -0
  729. package/src/likec4/ids.ts +115 -0
  730. package/src/likec4/levels.tsx +70 -0
  731. package/src/likec4/mapping.test.ts +66 -0
  732. package/src/likec4/mapping.ts +54 -0
  733. package/src/likec4/reserved.json +141 -0
  734. package/src/likec4/view-index.ts +78 -0
  735. package/src/main.tsx +13 -0
  736. package/src/map/ContextMapGraph.tsx +274 -0
  737. package/src/map/ContextNode.tsx +70 -0
  738. package/src/map/FloatingEdge.tsx +86 -0
  739. package/src/merge.test.ts +1038 -0
  740. package/src/merge.ts +1004 -0
  741. package/src/pages/AdrDetail.tsx +242 -0
  742. package/src/pages/AdrIndex.tsx +114 -0
  743. package/src/pages/AggregatePage.tsx +727 -0
  744. package/src/pages/BlockPage.tsx +478 -0
  745. package/src/pages/CatalogFailure.tsx +111 -0
  746. package/src/pages/Changes.tsx +313 -0
  747. package/src/pages/ContextMap.tsx +534 -0
  748. package/src/pages/ContextPage.tsx +346 -0
  749. package/src/pages/EnumPage.tsx +282 -0
  750. package/src/pages/EventPage.tsx +686 -0
  751. package/src/pages/ExternalPage.tsx +204 -0
  752. package/src/pages/FlowDetail.tsx +663 -0
  753. package/src/pages/FlowIndex.tsx +303 -0
  754. package/src/pages/GraphPage.tsx +217 -0
  755. package/src/pages/Language.tsx +257 -0
  756. package/src/pages/ModulePage.tsx +366 -0
  757. package/src/pages/NotFound.tsx +54 -0
  758. package/src/pages/Overview.tsx +344 -0
  759. package/src/pages/Problems.tsx +360 -0
  760. package/src/pages/RegistryIndex.tsx +118 -0
  761. package/src/pages/ServicePage.tsx +746 -0
  762. package/src/pages/Settings.tsx +975 -0
  763. package/src/pages/StorePage.tsx +114 -0
  764. package/src/peek/PeekCard.test.ts +35 -0
  765. package/src/peek/PeekCard.tsx +118 -0
  766. package/src/peek/PeekLayer.tsx +147 -0
  767. package/src/peek/model.test.ts +156 -0
  768. package/src/peek/model.ts +253 -0
  769. package/src/routes.test.ts +222 -0
  770. package/src/routes.ts +449 -0
  771. package/src/selection/DetailPanel.tsx +1407 -0
  772. package/src/selection/hash.test.ts +73 -0
  773. package/src/selection/hash.ts +58 -0
  774. package/src/selection/model.test.ts +199 -0
  775. package/src/selection/model.ts +473 -0
  776. package/src/selection/pages.test.ts +115 -0
  777. package/src/selection/pages.ts +268 -0
  778. package/src/selection/store.test.ts +62 -0
  779. package/src/selection/store.ts +46 -0
  780. package/src/selection/sync.tsx +99 -0
  781. package/src/table/ColumnsMenu.tsx +72 -0
  782. package/src/table/DataTable.tsx +780 -0
  783. package/src/table/Facets.tsx +54 -0
  784. package/src/table/GroupMenu.tsx +69 -0
  785. package/src/table/Toolbar.tsx +190 -0
  786. package/src/table/cells.tsx +117 -0
  787. package/src/table/compare.test.ts +117 -0
  788. package/src/table/compare.ts +196 -0
  789. package/src/table/export.test.ts +88 -0
  790. package/src/table/export.ts +66 -0
  791. package/src/table/facet-groups.test.ts +74 -0
  792. package/src/table/facet-groups.ts +56 -0
  793. package/src/table/grouping.test.ts +84 -0
  794. package/src/table/grouping.ts +82 -0
  795. package/src/table/infer.test.ts +57 -0
  796. package/src/table/infer.ts +50 -0
  797. package/src/table/persist.test.ts +76 -0
  798. package/src/table/persist.ts +112 -0
  799. package/src/table/sort-url.test.ts +75 -0
  800. package/src/table/sort-url.ts +52 -0
  801. package/src/table/types.ts +80 -0
  802. package/src/table/useTableState.ts +220 -0
  803. package/src/test-catalog.ts +7 -0
  804. package/src/testing/estate/data__catalog.json +2847 -0
  805. package/src/testing/estate/data__flows.json +1724 -0
  806. package/src/testing/estate/examples__auth__portolan__api.json +212 -0
  807. package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
  808. package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
  809. package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
  810. package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
  811. package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
  812. package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
  813. package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
  814. package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
  815. package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
  816. package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
  817. package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
  818. package/src/testing/estate.ts +49 -0
  819. package/src/trail/Trail.tsx +81 -0
  820. package/src/trail/model.test.ts +157 -0
  821. package/src/trail/model.ts +189 -0
  822. package/src/trail/record.tsx +32 -0
  823. package/src/trail/store.test.ts +66 -0
  824. package/src/trail/store.ts +102 -0
  825. package/tsconfig.json +35 -0
  826. package/vite.config.ts +171 -0
@@ -0,0 +1,302 @@
1
+ package main
2
+
3
+ import (
4
+ "strings"
5
+
6
+ "github.com/shortlink-org/portolan/catalog"
7
+ "github.com/shortlink-org/portolan/plugin"
8
+ )
9
+
10
+ // The site for a language model, in the shape llmstxt.org asks for.
11
+ //
12
+ // llms.txt is the index page again as flat lists: one H1, one blockquote that
13
+ // says what the estate is, then a link and a line of context per entity. A
14
+ // reader that fetches pages on demand starts here.
15
+ //
16
+ // llms-full.txt is every page of the site in one file, for a reader that has a
17
+ // context window and not a filesystem. It is assembled from the pages already
18
+ // rendered, not rendered again, so it cannot disagree with them.
19
+ //
20
+ // Neither takes the estate's public URL: the links are the same relative
21
+ // paths every other page uses, correct wherever the docs directory is mounted.
22
+ const (
23
+ llmsPage = "llms.txt"
24
+ llmsFullPage = "llms-full.txt"
25
+ )
26
+
27
+ // renderLlms runs last. Everything the builder holds at that point is the
28
+ // site, and llms-full.txt is that, in that order.
29
+ func (s *site) renderLlms() {
30
+ pages := append([]plugin.File(nil), s.b.Files...)
31
+
32
+ s.b.file(llmsPage, s.llmsIndex())
33
+ s.b.file(llmsFullPage, s.llmsFull(pages))
34
+ }
35
+
36
+ func (s *site) llmsIndex() string {
37
+ const self = llmsPage
38
+
39
+ var b strings.Builder
40
+ b.WriteString("# " + s.title() + "\n\n")
41
+ b.WriteString("> " + s.llmsSummary() + "\n\n")
42
+ b.WriteString(s.stamp() + "\n")
43
+ b.WriteString("Every link below is a markdown page beside this file. " +
44
+ "[" + llmsFullPage + "](" + llmsFullPage + ") is all of those pages in one file, " +
45
+ "in the order the site lists them.\n")
46
+
47
+ var contexts, services []string
48
+ for i := range s.cat.Contexts {
49
+ ctx := &s.cat.Contexts[i]
50
+ contexts = append(contexts, entry(s.ref(self, ctx.ID, ctx.Name), string(ctx.Classification), firstLine(ctx.Summary)))
51
+
52
+ for j := range ctx.Services {
53
+ svc := &ctx.Services[j]
54
+ names := make([]string, 0, len(svc.Aggregates))
55
+ for k := range svc.Aggregates {
56
+ names = append(names, svc.Aggregates[k].Name)
57
+ }
58
+ aggregates := ""
59
+ if len(names) > 0 {
60
+ aggregates = "Aggregates: " + strings.Join(names, ", ")
61
+ }
62
+ services = append(services, entry(s.ref(self, svc.ID, svc.Name), "in "+ctx.Name, aggregates))
63
+ }
64
+ }
65
+ section(&b, "Contexts", list(contexts))
66
+
67
+ // Straight after the contexts and before anything that uses the words. A
68
+ // model reading this estate cold has the same problem a person has: every
69
+ // page below is written in a vocabulary it has not been told yet.
70
+ var glossaries []string
71
+ for i := range s.cat.Contexts {
72
+ ctx := &s.cat.Contexts[i]
73
+ terms := s.termsOf[ctx.ID]
74
+ if len(terms) == 0 {
75
+ continue
76
+ }
77
+ names := make([]string, 0, len(terms))
78
+ for _, term := range terms {
79
+ names = append(names, term.Name)
80
+ }
81
+ glossaries = append(glossaries, entry(
82
+ link("Glossary of "+ctx.Name, self, s.glossaryPath(ctx)),
83
+ plural(len(terms), "term"),
84
+ strings.Join(names, ", "),
85
+ ))
86
+ }
87
+ section(&b, "Glossaries", list(glossaries))
88
+
89
+ section(&b, "Services", list(services))
90
+
91
+ // The pages a model has most to learn from: the shape of the data, the
92
+ // commands and the events of the estate all live here, and nowhere else
93
+ // on the site is the readme of an aggregate printed in full.
94
+ var aggregates []string
95
+ for i := range s.cat.Contexts {
96
+ ctx := &s.cat.Contexts[i]
97
+ for j := range ctx.Services {
98
+ svc := &ctx.Services[j]
99
+ for k := range svc.Aggregates {
100
+ agg := &svc.Aggregates[k]
101
+ commands, queries := 0, 0
102
+ for _, op := range agg.Operations {
103
+ if op.Kind == catalog.OperationQuery {
104
+ queries++
105
+ } else {
106
+ commands++
107
+ }
108
+ }
109
+ shape := []string{plural(commands, "command")}
110
+ if queries > 0 {
111
+ shape = append(shape, plural(queries, "query", "queries"))
112
+ }
113
+ shape = append(shape, plural(len(agg.Events), "event"))
114
+ aggregates = append(aggregates, entry(
115
+ s.ref(self, agg.ID, agg.Name),
116
+ "in "+s.ref(self, svc.ID, svc.Name),
117
+ "root "+code(agg.Root),
118
+ strings.Join(shape, ", "),
119
+ firstSentence(body(agg.Readme, agg.Name)),
120
+ ))
121
+ }
122
+ }
123
+ }
124
+ section(&b, "Aggregates", list(aggregates))
125
+
126
+ externals := make([]string, 0, len(s.cat.Externals))
127
+ for i := range s.cat.Externals {
128
+ ext := &s.cat.Externals[i]
129
+ interfaces := make([]string, 0, len(ext.Provides))
130
+ for j := range ext.Provides {
131
+ interfaces = append(interfaces, ext.Provides[j].ID)
132
+ }
133
+ externals = append(externals, entry(s.ref(self, ext.ID, orDefault(ext.Name, ext.ID)),
134
+ "outside the estate", "Answers on "+strings.Join(interfaces, ", "), firstLine(ext.Summary)))
135
+ }
136
+ section(&b, "Outside the estate", list(externals))
137
+
138
+ modules := make([]string, 0, len(s.cat.Modules))
139
+ for i := range s.cat.Modules {
140
+ module := &s.cat.Modules[i]
141
+ modules = append(modules, entry(
142
+ s.ref(self, module.ID, orDefault(module.Name, module.ID)),
143
+ "schema module", "published by "+s.ref(self, module.Owner, module.Owner),
144
+ strings.Join(module.Packages, ", "),
145
+ ))
146
+ }
147
+ section(&b, "Schema modules", list(modules))
148
+
149
+ flows := make([]string, 0, len(s.cat.Flows))
150
+ for i := range s.cat.Flows {
151
+ flow := &s.cat.Flows[i]
152
+ owner := ""
153
+ if flow.Owner != "" {
154
+ owner = "owned by " + s.ref(self, flow.Owner, flow.Owner)
155
+ }
156
+ flows = append(flows, entry(s.ref(self, flow.ID, flow.Name), owner, firstLine(flow.Summary)))
157
+ }
158
+ section(&b, "Flows", list(flows))
159
+
160
+ adrs := make([]string, 0, len(s.cat.Adrs))
161
+ for i := range s.cat.Adrs {
162
+ adr := &s.cat.Adrs[i]
163
+ var when []string
164
+ if adr.Status != "" {
165
+ when = append(when, string(adr.Status))
166
+ }
167
+ if adr.Date != "" {
168
+ when = append(when, adr.Date)
169
+ }
170
+ title := adr.Title
171
+ if len(when) > 0 {
172
+ title += " (" + strings.Join(when, ", ") + ")"
173
+ }
174
+ adrs = append(adrs, entry(s.ref(self, adr.ID, adr.ID), title))
175
+ }
176
+ section(&b, "Decisions", list(adrs))
177
+
178
+ // What llmstxt.org calls Optional: pages a reader short on context can
179
+ // skip. The shape of a shared type or a table matters once a page above
180
+ // has named it, and not before.
181
+ var optional []string
182
+ if len(s.cat.Defs) > 0 {
183
+ optional = append(optional, entry(link("Shared types", self, typesPage),
184
+ "types named by more than one aggregate, event or message"))
185
+ }
186
+ for i := range s.cat.Stores {
187
+ store := &s.cat.Stores[i]
188
+ note := string(store.Kind) + " store"
189
+ if owner, ok := s.services[store.Owner]; ok {
190
+ note += " of " + s.ref(self, owner.ID, owner.Name)
191
+ }
192
+ schema := plural(len(store.Tables), "table")
193
+ if len(store.Keyspaces) > 0 {
194
+ schema = plural(len(store.Keyspaces), "key pattern")
195
+ }
196
+ optional = append(optional, entry(s.ref(self, store.ID, store.Name), note, schema))
197
+ }
198
+ section(&b, "Optional", list(optional))
199
+
200
+ return b.String()
201
+ }
202
+
203
+ // llmsSummary is the blockquote: what kind of thing this is, and how much of
204
+ // it there is.
205
+ func (s *site) llmsSummary() string {
206
+ services, aggregates := 0, 0
207
+ for i := range s.cat.Contexts {
208
+ services += len(s.cat.Contexts[i].Services)
209
+ for j := range s.cat.Contexts[i].Services {
210
+ aggregates += len(s.cat.Contexts[i].Services[j].Aggregates)
211
+ }
212
+ }
213
+
214
+ parts := []string{
215
+ plural(len(s.cat.Contexts), "bounded context"),
216
+ plural(services, "service"),
217
+ plural(aggregates, "aggregate"),
218
+ plural(len(s.cat.Stores), "store"),
219
+ }
220
+ if len(s.cat.Modules) > 0 {
221
+ parts = append(parts, plural(len(s.cat.Modules), "schema module"))
222
+ }
223
+ // Named only when there is one: an estate that calls nobody outside has
224
+ // nothing to count, and "0 systems outside" reads as a claim about the
225
+ // world rather than about the catalog.
226
+ if len(s.cat.Externals) > 0 {
227
+ parts = append(parts, plural(len(s.cat.Externals), "system outside the estate", "systems outside the estate"))
228
+ }
229
+ parts = append(parts,
230
+ plural(len(s.cat.Flows), "flow"),
231
+ plural(len(s.cat.Adrs), "decision record"),
232
+ )
233
+
234
+ return "Architecture catalog of " + s.title() + ": " + strings.Join(parts[:len(parts)-1], ", ") +
235
+ " and " + parts[len(parts)-1] + ", read from the code, the specs and the traces of the estate."
236
+ }
237
+
238
+ func (s *site) llmsFull(pages []plugin.File) string {
239
+ var b strings.Builder
240
+ b.WriteString("*" + llmsFullPage + ": every page of " + s.title() + ", one after another. " +
241
+ "A page begins with a comment naming its file, and the links inside it are " +
242
+ "relative to that file's directory.*\n\n")
243
+ b.WriteString(s.stamp())
244
+
245
+ for _, page := range pages {
246
+ b.WriteString("\n<!-- " + page.Name + " -->\n\n")
247
+ b.WriteString(strings.TrimRight(page.Contents, "\n") + "\n")
248
+ }
249
+
250
+ return b.String()
251
+ }
252
+
253
+ // entry is one line of an llms.txt list: the link, then whatever notes are
254
+ // not empty, sentence by sentence.
255
+ func entry(link string, notes ...string) string {
256
+ kept := make([]string, 0, len(notes))
257
+ for _, note := range notes {
258
+ note = strings.TrimSpace(note)
259
+ if note == "" {
260
+ continue
261
+ }
262
+ kept = append(kept, strings.TrimSuffix(note, "."))
263
+ }
264
+ if len(kept) == 0 {
265
+ return "- " + link
266
+ }
267
+
268
+ return "- " + link + ": " + strings.Join(kept, ". ")
269
+ }
270
+
271
+ // firstSentence is the opening of a readme as one line: the first paragraph
272
+ // unwrapped, cut at the end of its first sentence. A readme is wrapped at
273
+ // some column by whoever wrote it, so the first line alone stops mid-phrase.
274
+ func firstSentence(md string) string {
275
+ md = strings.TrimSpace(md)
276
+ if md == "" {
277
+ return ""
278
+ }
279
+ para := md
280
+ if i := strings.Index(md, "\n\n"); i >= 0 {
281
+ para = md[:i]
282
+ }
283
+ if strings.HasPrefix(para, "#") {
284
+ return ""
285
+ }
286
+ para = strings.Join(strings.Fields(para), " ")
287
+ for i := 0; i < len(para); i++ {
288
+ if para[i] == '.' && (i+1 == len(para) || para[i+1] == ' ') {
289
+ return para[:i]
290
+ }
291
+ }
292
+
293
+ return para
294
+ }
295
+
296
+ func list(items []string) string {
297
+ if len(items) == 0 {
298
+ return ""
299
+ }
300
+
301
+ return strings.Join(items, "\n") + "\n"
302
+ }
@@ -0,0 +1,22 @@
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-markdown:", 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
+ }
@@ -0,0 +1,254 @@
1
+ package main
2
+
3
+ import (
4
+ "path"
5
+ "strings"
6
+ )
7
+
8
+ // Markdown primitives. Small on purpose: a template engine would put the shape
9
+ // of a page somewhere other than the code that decides what goes on it.
10
+
11
+ // table renders a GFM table, or nothing at all when there are no rows. An
12
+ // empty table with a header is a heading that promises content and delivers a
13
+ // horizontal rule.
14
+ func table(headers []string, rows [][]string) string {
15
+ if len(rows) == 0 {
16
+ return ""
17
+ }
18
+
19
+ // A column nobody filled in is dropped. The same renderer draws a table of
20
+ // columns with lineage and a table of columns without any, and a column of
21
+ // nine dashes is a question the reader keeps re-asking.
22
+ keep := make([]int, 0, len(headers))
23
+ for i := range headers {
24
+ // The first column identifies the row, so it stays even when empty.
25
+ if i == 0 || anyFilled(rows, i) {
26
+ keep = append(keep, i)
27
+ }
28
+ }
29
+
30
+ var b strings.Builder
31
+ head := make([]string, len(keep))
32
+ for i, column := range keep {
33
+ head[i] = headers[column]
34
+ }
35
+ b.WriteString("| " + strings.Join(head, " | ") + " |\n")
36
+ b.WriteString("|" + strings.Repeat(" --- |", len(keep)) + "\n")
37
+
38
+ for _, row := range rows {
39
+ cells := make([]string, len(keep))
40
+ for i, column := range keep {
41
+ if column < len(row) {
42
+ cells[i] = cell(row[column])
43
+ } else {
44
+ cells[i] = cell("")
45
+ }
46
+ }
47
+ b.WriteString("| " + strings.Join(cells, " | ") + " |\n")
48
+ }
49
+
50
+ return b.String()
51
+ }
52
+
53
+ func anyFilled(rows [][]string, column int) bool {
54
+ for _, row := range rows {
55
+ if column < len(row) && strings.TrimSpace(row[column]) != "" {
56
+ return true
57
+ }
58
+ }
59
+
60
+ return false
61
+ }
62
+
63
+ // cell makes a string safe to sit between two pipes. A doc comment routinely
64
+ // contains both a newline and a pipe, and either one silently ends the row.
65
+ func cell(s string) string {
66
+ s = strings.ReplaceAll(s, "|", "\\|")
67
+ s = strings.ReplaceAll(s, "\r\n", "\n")
68
+ s = strings.ReplaceAll(s, "\n", " ")
69
+ s = strings.TrimSpace(s)
70
+ if s == "" {
71
+ return "—"
72
+ }
73
+
74
+ return s
75
+ }
76
+
77
+ func code(s string) string {
78
+ if s == "" {
79
+ return ""
80
+ }
81
+ longest, run := 0, 0
82
+ for _, r := range s {
83
+ if r == '`' {
84
+ run++
85
+ if run > longest {
86
+ longest = run
87
+ }
88
+ } else {
89
+ run = 0
90
+ }
91
+ }
92
+ marks := strings.Repeat("`", longest+1)
93
+ if longest > 0 || strings.HasPrefix(s, " ") || strings.HasSuffix(s, " ") {
94
+ return marks + " " + s + " " + marks
95
+ }
96
+ return marks + s + marks
97
+ }
98
+
99
+ func fence(lang, body string) string {
100
+ return "```" + lang + "\n" + strings.TrimRight(body, "\n") + "\n```\n"
101
+ }
102
+
103
+ // link is a relative markdown link from one generated page to another. The
104
+ // paths are the ones the host will write, so the link is correct wherever the
105
+ // docs directory is mounted.
106
+ func link(text, from, to string) string {
107
+ return "[" + text + "](" + rel(from, to) + ")"
108
+ }
109
+
110
+ // rel is filepath.Rel for slash paths, between two FILES: the result is
111
+ // relative to the directory the first one sits in.
112
+ //
113
+ // It is written out rather than taken from path/filepath because a plugin
114
+ // compiled for wasip1 and one compiled for the host must agree, and
115
+ // filepath.Rel agrees with whatever separator it was built for.
116
+ func rel(from, to string) string {
117
+ fromDir := strings.Split(path.Dir(from), "/")
118
+ if path.Dir(from) == "." {
119
+ fromDir = nil
120
+ }
121
+
122
+ toParts := strings.Split(to, "/")
123
+
124
+ common := 0
125
+ for common < len(fromDir) && common < len(toParts)-1 && fromDir[common] == toParts[common] {
126
+ common++
127
+ }
128
+
129
+ var out []string
130
+ for range fromDir[common:] {
131
+ out = append(out, "..")
132
+ }
133
+ out = append(out, toParts[common:]...)
134
+
135
+ result := strings.Join(out, "/")
136
+ if result == "" {
137
+ return path.Base(to)
138
+ }
139
+
140
+ return result
141
+ }
142
+
143
+ // shiftHeadings pushes every ATX heading down by `by` levels, leaving fenced
144
+ // code alone.
145
+ //
146
+ // Readmes and ADR bodies are whole documents that start at `#`. Pasting one
147
+ // under a generated `#` gives a page with two top-level headings, which every
148
+ // table-of-contents generator then reads as two documents.
149
+ func shiftHeadings(md string, by int) string {
150
+ if md == "" {
151
+ return ""
152
+ }
153
+
154
+ lines := strings.Split(strings.ReplaceAll(md, "\r\n", "\n"), "\n")
155
+ fenced := false
156
+ for i, line := range lines {
157
+ trimmed := strings.TrimSpace(line)
158
+ if strings.HasPrefix(trimmed, "```") || strings.HasPrefix(trimmed, "~~~") {
159
+ fenced = !fenced
160
+
161
+ continue
162
+ }
163
+ if fenced || !strings.HasPrefix(line, "#") {
164
+ continue
165
+ }
166
+
167
+ level := 0
168
+ for level < len(line) && line[level] == '#' {
169
+ level++
170
+ }
171
+ // A run of hashes with no space after it is not a heading.
172
+ if level >= len(line) || line[level] != ' ' {
173
+ continue
174
+ }
175
+ // Markdown has no h7. Anything that would go past six stays where it is,
176
+ // because a heading rendered as literal hashes is worse than a deep one.
177
+ if level+by > 6 {
178
+ continue
179
+ }
180
+
181
+ lines[i] = strings.Repeat("#", by) + line
182
+ }
183
+
184
+ return strings.Join(lines, "\n")
185
+ }
186
+
187
+ // section appends a titled block, and nothing at all when the block is empty.
188
+ // Every page here is a list of optional sections, so this is what keeps a
189
+ // service with no consumers from growing a "Consumes" heading over a blank.
190
+ func section(b *strings.Builder, title, body string) {
191
+ if strings.TrimSpace(body) == "" {
192
+ return
193
+ }
194
+
195
+ b.WriteString("\n## " + title + "\n\n")
196
+ b.WriteString(strings.TrimRight(body, "\n") + "\n")
197
+ }
198
+
199
+ func subsection(b *strings.Builder, title, body string) {
200
+ if strings.TrimSpace(body) == "" {
201
+ return
202
+ }
203
+
204
+ b.WriteString("\n### " + title + "\n\n")
205
+ b.WriteString(strings.TrimRight(body, "\n") + "\n")
206
+ }
207
+
208
+ // defList renders a page's metadata.
209
+ //
210
+ // It is a bullet list rather than a two-column table because a table needs a
211
+ // header row, and the header over an id and a path is two empty cells that
212
+ // every renderer draws a box around.
213
+ func defList(rows [][]string) string {
214
+ var b strings.Builder
215
+ for _, row := range rows {
216
+ if len(row) < 2 || strings.TrimSpace(row[1]) == "" {
217
+ continue
218
+ }
219
+ b.WriteString("- **" + row[0] + ":** " + strings.TrimSpace(row[1]) + "\n")
220
+ }
221
+
222
+ return b.String()
223
+ }
224
+
225
+ // body prepares a readme or an ADR body to sit under a generated `# Title`.
226
+ //
227
+ // A document that opens with its own title repeating the page's gets that line
228
+ // dropped, and everything below it is already at the right depth. One that
229
+ // starts lower is pushed down a level so its sections nest under the page
230
+ // instead of beside it.
231
+ func body(md, title string) string {
232
+ md = strings.TrimSpace(md)
233
+ if md == "" {
234
+ return ""
235
+ }
236
+
237
+ lines := strings.Split(strings.ReplaceAll(md, "\r\n", "\n"), "\n")
238
+ for i, line := range lines {
239
+ if strings.TrimSpace(line) == "" {
240
+ continue
241
+ }
242
+ if heading, ok := strings.CutPrefix(line, "# "); ok && sameTitle(heading, title) {
243
+ return strings.TrimSpace(strings.Join(lines[i+1:], "\n"))
244
+ }
245
+
246
+ break
247
+ }
248
+
249
+ return shiftHeadings(md, 1)
250
+ }
251
+
252
+ func sameTitle(a, b string) bool {
253
+ return strings.EqualFold(strings.TrimSpace(a), strings.TrimSpace(b))
254
+ }
@@ -0,0 +1,100 @@
1
+ package main
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ func TestRel(t *testing.T) {
9
+ cases := []struct{ from, to, want string }{
10
+ {"README.md", "types.md", "types.md"},
11
+ {"README.md", "shop/oms/README.md", "shop/oms/README.md"},
12
+ {"shop/oms/README.md", "shop/oms/aggregates/order.md", "aggregates/order.md"},
13
+ {"shop/oms/aggregates/order.md", "types.md", "../../../types.md"},
14
+ {"shop/oms/aggregates/order.md", "shop/pricing/README.md", "../../pricing/README.md"},
15
+ {"flows/checkout.md", "shop/oms/README.md", "../shop/oms/README.md"},
16
+ {"adr/org.0001.md", "adr/org.0002.md", "org.0002.md"},
17
+ }
18
+
19
+ for _, c := range cases {
20
+ if got := rel(c.from, c.to); got != c.want {
21
+ t.Errorf("rel(%q, %q) = %q, want %q", c.from, c.to, got, c.want)
22
+ }
23
+ }
24
+ }
25
+
26
+ func TestShiftHeadingsLeavesCodeAlone(t *testing.T) {
27
+ in := "# Title\n\n```sh\n# not a heading\n```\n\n## Section\n"
28
+ want := "## Title\n\n```sh\n# not a heading\n```\n\n### Section\n"
29
+
30
+ if got := shiftHeadings(in, 1); got != want {
31
+ t.Errorf("shiftHeadings:\n got: %q\nwant: %q", got, want)
32
+ }
33
+ }
34
+
35
+ func TestShiftHeadingsStopsAtSix(t *testing.T) {
36
+ if got := shiftHeadings("###### deep", 1); got != "###### deep" {
37
+ t.Errorf("a sixth-level heading must stay put, got %q", got)
38
+ }
39
+ if got := shiftHeadings("#hashtag", 1); got != "#hashtag" {
40
+ t.Errorf("a hash with no space is not a heading, got %q", got)
41
+ }
42
+ }
43
+
44
+ // A readme opening with the page's own title would give the page two h1s, so
45
+ // that line is dropped and the rest keeps its depth.
46
+ func TestBodyDropsRepeatedTitle(t *testing.T) {
47
+ got := body("# Basket\n\n## Lifetime\n\nShort.", "Basket")
48
+ if got != "## Lifetime\n\nShort." {
49
+ t.Errorf("body kept the repeated title: %q", got)
50
+ }
51
+
52
+ got = body("Just prose.\n\n# Later heading", "Basket")
53
+ if got != "Just prose.\n\n## Later heading" {
54
+ t.Errorf("body should shift a document that does not open with the title: %q", got)
55
+ }
56
+ }
57
+
58
+ func TestCellSurvivesPipesAndNewlines(t *testing.T) {
59
+ if got := cell("a | b\nc"); got != "a \\| b c" {
60
+ t.Errorf("cell = %q", got)
61
+ }
62
+ if got := cell(" "); got != "—" {
63
+ t.Errorf("an empty cell should be a dash, got %q", got)
64
+ }
65
+ }
66
+
67
+ func TestMermaidTextEscapesHashBeforeSemicolons(t *testing.T) {
68
+ // The escape for # ends in a semicolon, so the order of the two
69
+ // replacements is the whole of this function's correctness.
70
+ if got := mermaidText("markPaid #1; twice"); got != "markPaid #35;1, twice" {
71
+ t.Errorf("mermaidText = %q", got)
72
+ }
73
+ }
74
+
75
+ func TestAnchor(t *testing.T) {
76
+ if got := anchor("Gateway Ref"); got != "gateway-ref" {
77
+ t.Errorf("anchor = %q", got)
78
+ }
79
+ }
80
+
81
+ func TestCodeUsesALongerFenceAroundBackticks(t *testing.T) {
82
+ if got := code("run `gen`"); got != "`` run `gen` ``" {
83
+ t.Fatalf("code() = %q", got)
84
+ }
85
+ }
86
+
87
+ func FuzzMarkdownPrimitives(f *testing.F) {
88
+ f.Add("a | b\nnext `tick`")
89
+ f.Add("обычный текст")
90
+ f.Fuzz(func(t *testing.T, value string) {
91
+ got := cell(value)
92
+ if strings.ContainsAny(got, "\r\n") {
93
+ t.Fatalf("cell kept a line break: %q", got)
94
+ }
95
+ wrapped := code(value)
96
+ if value != "" && !strings.HasPrefix(wrapped, "`") {
97
+ t.Fatalf("code is not fenced: %q", wrapped)
98
+ }
99
+ })
100
+ }