@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,214 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "regexp"
6
+ "strings"
7
+
8
+ "github.com/shortlink-org/portolan/catalog"
9
+ )
10
+
11
+ // The format, in one place.
12
+ //
13
+ // # Glossary — auth
14
+ //
15
+ // One meaning per word inside this context.
16
+ //
17
+ // **Session.** Proof that a user logged in, how long that proof is good for,
18
+ // and whether it has been taken away.
19
+ //
20
+ // A title, an optional line or two saying what the vocabulary covers, then one
21
+ // paragraph per term in alphabetical order. The paragraph opens with the term
22
+ // in bold and the full stop inside the bold, so `**Email address.**` names a
23
+ // two-word term and nothing has to guess where the name ends. Everything after
24
+ // it is the definition, carried through as written.
25
+ //
26
+ // That is the whole format. Nothing here reads the prose for structure: a
27
+ // glossary is a person explaining a word to another person, and a parser that
28
+ // went looking for shapes inside the explanation would be a parser telling an
29
+ // estate how to phrase itself.
30
+ var (
31
+ titleLine = regexp.MustCompile(`^#\s+Glossary\b`)
32
+ entryOpen = regexp.MustCompile(`^\*\*(.+?)\.\*\*\s*(.*)$`)
33
+ listItem = regexp.MustCompile(`^([-*+]|\d+\.)\s`)
34
+ )
35
+
36
+ // A paragraph of the file, flattened to one line, and where it started.
37
+ //
38
+ // Flattening is what makes the rest of this file about words rather than about
39
+ // line breaks: a glossary is hard-wrapped at whatever width its author likes,
40
+ // and a soft break inside a paragraph is a space in every markdown renderer
41
+ // there is. The line number survives because it is the only thing an error
42
+ // message can point at.
43
+ type paragraph struct {
44
+ line int
45
+ text string
46
+ }
47
+
48
+ // blocks splits a file into paragraphs on blank lines.
49
+ func blocks(src string) []paragraph {
50
+ var out []paragraph
51
+
52
+ lines := strings.Split(strings.ReplaceAll(src, "\r\n", "\n"), "\n")
53
+ start := 0
54
+ var held []string
55
+ flush := func() {
56
+ if len(held) == 0 {
57
+ return
58
+ }
59
+ out = append(out, paragraph{line: start, text: strings.Join(held, " ")})
60
+ held = nil
61
+ }
62
+ for i, line := range lines {
63
+ trimmed := strings.TrimSpace(line)
64
+ if trimmed == "" {
65
+ flush()
66
+
67
+ continue
68
+ }
69
+ if len(held) == 0 {
70
+ start = i + 1
71
+ }
72
+ held = append(held, trimmed)
73
+ }
74
+ flush()
75
+
76
+ return out
77
+ }
78
+
79
+ // slug turns a term into the kebab-case form ids and urls use: "Email address"
80
+ // becomes email-address, "Password policy" becomes password-policy.
81
+ func slug(name string) string {
82
+ var b strings.Builder
83
+
84
+ dash := false
85
+ for _, r := range strings.ToLower(name) {
86
+ switch {
87
+ case r >= 'a' && r <= 'z', r >= '0' && r <= '9':
88
+ b.WriteRune(r)
89
+ dash = false
90
+ default:
91
+ if !dash && b.Len() > 0 {
92
+ b.WriteRune('-')
93
+ dash = true
94
+ }
95
+ }
96
+ }
97
+
98
+ return strings.Trim(b.String(), "-")
99
+ }
100
+
101
+ func where(file string, line int) string {
102
+ return fmt.Sprintf("%s:%d: ", file, line)
103
+ }
104
+
105
+ // short quotes the head of a paragraph, for an error that has to say what it
106
+ // found instead of what it wanted.
107
+ func short(text string) string {
108
+ runes := []rune(text)
109
+ if len(runes) > 40 {
110
+ return `"` + string(runes[:40]) + `…"`
111
+ }
112
+
113
+ return `"` + text + `"`
114
+ }
115
+
116
+ // refuse names what a paragraph is, when it is not an entry. A paragraph
117
+ // before the first entry is the preamble and is allowed to be anything; after
118
+ // one, everything is an entry, and the shapes worth naming are the shapes a
119
+ // glossary is otherwise written in.
120
+ func refuse(text string, started bool) string {
121
+ switch {
122
+ case strings.HasPrefix(text, "|"):
123
+ return "a table: a glossary is prose, one paragraph per term, so that an entry has room to say what the term is not"
124
+ case strings.HasPrefix(text, "#"):
125
+ return "a heading under the title: a term is a paragraph, not a section of its own"
126
+ case listItem.MatchString(text):
127
+ return "a list: a term is a paragraph opening with **Term.**"
128
+ case started:
129
+ return "a paragraph naming no term; after the first entry every paragraph is one, and it opens with **Term.**"
130
+ }
131
+
132
+ return ""
133
+ }
134
+
135
+ // parseGlossary reads one file into terms, the notes worth making about it,
136
+ // and the reasons it cannot be read at all.
137
+ //
138
+ // Errors and warnings are kept apart all the way up: an error is something the
139
+ // catalog cannot hold, a warning is something a person should go and write.
140
+ func parseGlossary(file, context, src string) (terms []catalog.Term, warns, errs []string) {
141
+ paras := blocks(src)
142
+ if len(paras) == 0 {
143
+ return nil, nil, []string{file + ": the file is empty"}
144
+ }
145
+ if !titleLine.MatchString(paras[0].text) {
146
+ return nil, nil, []string{where(file, paras[0].line) +
147
+ "a glossary opens with `# Glossary — <context>`, and this file opens with " + short(paras[0].text)}
148
+ }
149
+
150
+ seen := map[string]int{}
151
+ previous := ""
152
+ started := false
153
+ for _, p := range paras[1:] {
154
+ if !strings.HasPrefix(p.text, "**") {
155
+ if msg := refuse(p.text, started); msg != "" {
156
+ errs = append(errs, where(file, p.line)+msg)
157
+ }
158
+
159
+ continue
160
+ }
161
+ started = true
162
+
163
+ m := entryOpen.FindStringSubmatch(p.text)
164
+ if m == nil {
165
+ errs = append(errs, where(file, p.line)+
166
+ "an entry opens with the term in bold and the full stop inside it, `**Session.**`")
167
+
168
+ continue
169
+ }
170
+ name := strings.TrimSpace(m[1])
171
+ body := strings.TrimSpace(m[2])
172
+ id := slug(name)
173
+ if id == "" {
174
+ errs = append(errs, where(file, p.line)+"a term with no name")
175
+
176
+ continue
177
+ }
178
+ if body == "" {
179
+ errs = append(errs, where(file, p.line)+name+" says nothing")
180
+
181
+ continue
182
+ }
183
+
184
+ if at, taken := seen[id]; taken {
185
+ errs = append(errs, where(file, p.line)+name+fmt.Sprintf(" is already defined on line %d", at))
186
+
187
+ continue
188
+ }
189
+ seen[id] = p.line
190
+
191
+ // Order is a warning because it costs a reader a moment and costs the
192
+ // catalog nothing: every page sorts what it draws anyway.
193
+ lower := strings.ToLower(name)
194
+ if previous != "" && lower < previous {
195
+ warns = append(warns, where(file, p.line)+name+" sits after "+previous+"; the glossary is alphabetical")
196
+ }
197
+ previous = lower
198
+
199
+ terms = append(terms, catalog.Term{
200
+ ID: context + "." + id,
201
+ Slug: id,
202
+ Context: context,
203
+ Name: name,
204
+ Definition: body,
205
+ Source: fmt.Sprintf("%s:%d", file, p.line),
206
+ })
207
+ }
208
+
209
+ if !started && len(errs) == 0 {
210
+ warns = append(warns, file+": a glossary with no terms in it")
211
+ }
212
+
213
+ return terms, warns, errs
214
+ }
@@ -0,0 +1,203 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "strings"
6
+ "testing"
7
+
8
+ "github.com/shortlink-org/portolan/catalog"
9
+ )
10
+
11
+ const sample = `# Glossary — auth
12
+
13
+ One meaning per word inside this context. The code, the events and the API
14
+ spell these the same way.
15
+
16
+ **Email address.** The address a user logs in with, normalised on creation.
17
+
18
+ **Session.** Proof that a user logged in, and how long that proof is good for,
19
+ and whether it has been taken away.
20
+
21
+ **Version.** The number the store compares before writing an aggregate. Zero
22
+ means never stored.
23
+ `
24
+
25
+ const file = "examples/auth/GLOSSARY.md"
26
+
27
+ func parsed(t *testing.T, src string) []catalog.Term {
28
+ t.Helper()
29
+
30
+ terms, _, errs := parseGlossary(file, "auth", src)
31
+ if len(errs) > 0 {
32
+ t.Fatalf("errors: %s", strings.Join(errs, "\n"))
33
+ }
34
+
35
+ return terms
36
+ }
37
+
38
+ func refused(t *testing.T, src string) string {
39
+ t.Helper()
40
+
41
+ _, _, errs := parseGlossary(file, "auth", src)
42
+ if len(errs) == 0 {
43
+ t.Fatal("parsed without complaint")
44
+ }
45
+
46
+ return strings.Join(errs, "\n")
47
+ }
48
+
49
+ func warned(t *testing.T, src string) string {
50
+ t.Helper()
51
+
52
+ _, warns, errs := parseGlossary(file, "auth", src)
53
+ if len(errs) > 0 {
54
+ t.Fatalf("errors: %s", strings.Join(errs, "\n"))
55
+ }
56
+
57
+ return strings.Join(warns, "\n")
58
+ }
59
+
60
+ func term(t *testing.T, terms []catalog.Term, name string) catalog.Term {
61
+ t.Helper()
62
+
63
+ for _, candidate := range terms {
64
+ if candidate.Name == name {
65
+ return candidate
66
+ }
67
+ }
68
+ t.Fatalf("no term called %q", name)
69
+
70
+ return catalog.Term{}
71
+ }
72
+
73
+ func TestTheDefinitionIsTheParagraph(t *testing.T) {
74
+ session := term(t, parsed(t, sample), "Session")
75
+
76
+ want := "Proof that a user logged in, and how long that proof is good for, and whether it has been taken away."
77
+ if session.Definition != want {
78
+ t.Errorf("definition is %q, want %q", session.Definition, want)
79
+ }
80
+ }
81
+
82
+ // The hard wrap in the file is a soft break in markdown, and a definition that
83
+ // carried it would be a definition nothing could put on a card.
84
+ func TestParagraphIsOneLine(t *testing.T) {
85
+ for _, term := range parsed(t, sample) {
86
+ if strings.ContainsAny(term.Definition, "\n") {
87
+ t.Errorf("%s carries a line break", term.Name)
88
+ }
89
+ }
90
+ }
91
+
92
+ func TestTwoWordTerm(t *testing.T) {
93
+ address := term(t, parsed(t, sample), "Email address")
94
+
95
+ if address.Slug != "email-address" {
96
+ t.Errorf("slug is %q", address.Slug)
97
+ }
98
+ if address.ID != "auth.email-address" {
99
+ t.Errorf("id is %q", address.ID)
100
+ }
101
+ if address.Context != "auth" {
102
+ t.Errorf("context is %q", address.Context)
103
+ }
104
+ }
105
+
106
+ func TestSourceIsTheLineTheEntryStartsOn(t *testing.T) {
107
+ if got := term(t, parsed(t, sample), "Session").Source; got != file+":8" {
108
+ t.Errorf("source is %q, want %s:8", got, file)
109
+ }
110
+ }
111
+
112
+ func TestPreambleIsNotATerm(t *testing.T) {
113
+ if terms := parsed(t, sample); len(terms) != 3 {
114
+ names := []string{}
115
+ for _, term := range terms {
116
+ names = append(names, term.Name)
117
+ }
118
+ t.Errorf("read %d terms: %s", len(terms), strings.Join(names, ", "))
119
+ }
120
+ }
121
+
122
+ func TestTableIsRefused(t *testing.T) {
123
+ src := "# Glossary — auth\n\n| Term | Meaning |\n| --- | --- |\n| Session | Proof. |\n"
124
+
125
+ if got := refused(t, src); !strings.Contains(got, "prose") {
126
+ t.Errorf("refusal is %q", got)
127
+ }
128
+ }
129
+
130
+ func TestHeadingPerTermIsRefused(t *testing.T) {
131
+ src := "# Glossary — auth\n\n## Session\n\nProof that a user logged in.\n"
132
+
133
+ if got := refused(t, src); !strings.Contains(got, "a term is a paragraph") {
134
+ t.Errorf("refusal is %q", got)
135
+ }
136
+ }
137
+
138
+ func TestListIsRefused(t *testing.T) {
139
+ src := "# Glossary — auth\n\n- **Session.** Proof that a user logged in.\n"
140
+
141
+ if got := refused(t, src); !strings.Contains(got, "a list") {
142
+ t.Errorf("refusal is %q", got)
143
+ }
144
+ }
145
+
146
+ func TestAFileThatIsNotAGlossaryIsRefused(t *testing.T) {
147
+ src := "# Sessions\n\n**Session.** Proof that a user logged in.\n"
148
+
149
+ if got := refused(t, src); !strings.Contains(got, "opens with `# Glossary") {
150
+ t.Errorf("refusal is %q", got)
151
+ }
152
+ }
153
+
154
+ // A word with two meanings inside one context is the failure the glossary is
155
+ // written to prevent, so it is the one duplication that cannot be a warning.
156
+ func TestTheSameWordTwiceIsRefused(t *testing.T) {
157
+ src := sample + "\n**Session.** Something else entirely.\n"
158
+
159
+ if got := refused(t, src); !strings.Contains(got, "already defined on line 8") {
160
+ t.Errorf("refusal is %q", got)
161
+ }
162
+ }
163
+
164
+ func TestOutOfOrderIsAWarning(t *testing.T) {
165
+ src := "# Glossary — auth\n\n**Lockout.** An account refusing passwords.\n\n**Locked.** The state of a lockout while it refuses.\n"
166
+
167
+ if got := warned(t, src); !strings.Contains(got, "alphabetical") {
168
+ t.Errorf("warnings are %q", got)
169
+ }
170
+ }
171
+
172
+ func TestAnEntryThatNamesNothing(t *testing.T) {
173
+ src := "# Glossary — auth\n\n**Session** proof that a user logged in.\n"
174
+
175
+ if got := refused(t, src); !strings.Contains(got, "the full stop inside it") {
176
+ t.Errorf("refusal is %q", got)
177
+ }
178
+ }
179
+
180
+ // The estate's own glossaries parse, and are held to the format they document.
181
+ func TestTheEstatesOwnGlossaries(t *testing.T) {
182
+ for _, tt := range []struct{ path, context string }{
183
+ {"../../examples/auth/GLOSSARY.md", "auth"},
184
+ {"../../examples/shop/oms/GLOSSARY.md", "shop"},
185
+ } {
186
+ t.Run(tt.path, func(t *testing.T) {
187
+ src, err := os.ReadFile(tt.path)
188
+ if err != nil {
189
+ t.Fatal(err)
190
+ }
191
+ terms, warns, errs := parseGlossary(tt.path, tt.context, string(src))
192
+ if len(errs) > 0 {
193
+ t.Fatalf("errors: %s", strings.Join(errs, "\n"))
194
+ }
195
+ if len(warns) > 0 {
196
+ t.Errorf("warnings: %s", strings.Join(warns, "\n"))
197
+ }
198
+ if len(terms) == 0 {
199
+ t.Error("no terms read")
200
+ }
201
+ })
202
+ }
203
+ }
@@ -0,0 +1,214 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "path"
6
+ "path/filepath"
7
+ "strings"
8
+
9
+ "github.com/shortlink-org/portolan/catalog"
10
+ "github.com/shortlink-org/portolan/plugin"
11
+ )
12
+
13
+ // extractAggregate reads one discovered aggregate domain package.
14
+ //
15
+ // The layout is the claim: a directory there is an aggregate, the struct named
16
+ // after it is the root, `vo/` holds its value objects and `event/` the facts it
17
+ // publishes. Nothing is inferred from a comment or a marker interface, so a
18
+ // package that does not follow the layout produces a diagnostic instead of a
19
+ // half-right aggregate.
20
+ func extractAggregate(root, aggregateName, domainPath string, layout sourceLayout, svcID string, b *plugin.Builder) (catalog.Aggregate, bool) {
21
+ pkg, err := parsePkg(root, domainPath)
22
+ if err != nil {
23
+ if !os.IsNotExist(err) {
24
+ b.Warn(aggregateID(svcID, aggregateName), domainPath+" could not be parsed: "+err.Error())
25
+ }
26
+
27
+ return catalog.Aggregate{}, false
28
+ }
29
+
30
+ id := aggregateID(svcID, slug(aggregateName))
31
+ name := title(pkg.name)
32
+
33
+ aggregate := catalog.Aggregate{
34
+ ID: id,
35
+ Slug: slug(aggregateName),
36
+ Name: name,
37
+ Readme: aggregateReadme(root, domainPath, pkg.name, name, pkg.doc()),
38
+ Entities: []catalog.Block{},
39
+ ValueObjects: []catalog.Block{},
40
+ Operations: []catalog.Operation{},
41
+ Events: []catalog.Event{},
42
+ }
43
+
44
+ for _, decl := range pkg.structs() {
45
+ if !exported(decl.name) {
46
+ continue
47
+ }
48
+
49
+ aggregate.Entities = append(aggregate.Entities, catalog.Block{
50
+ ID: blockID(id, slug(decl.name)),
51
+ Slug: slug(decl.name),
52
+ Name: decl.name,
53
+ Doc: decl.doc,
54
+ Fields: fields(decl.fields),
55
+ })
56
+ }
57
+
58
+ // The root is the entity named after its package: package user holds User,
59
+ // and package price_list holds PriceList. It is a rule rather than a guess,
60
+ // and a package that breaks it is not read as an aggregate at all. Taking
61
+ // whichever struct came first instead would put a package of error types
62
+ // on the page as a model with a root and seven fields, and a reader
63
+ // believes a table; a warning and no page is the honest answer.
64
+ aggregate.Root = pascal(pkg.name)
65
+ if !hasBlock(aggregate.Entities, aggregate.Root) {
66
+ if len(aggregate.Entities) == 0 {
67
+ b.Warn(id, domainPath+" declares no exported struct, so the aggregate has no root")
68
+
69
+ return catalog.Aggregate{}, false
70
+ }
71
+
72
+ b.Warn(id, domainPath+" has no struct called "+pascal(pkg.name)+", so it is not read as an aggregate; the root is the struct named after its package")
73
+
74
+ return catalog.Aggregate{}, false
75
+ }
76
+
77
+ aggregate.ValueObjects = extractValueObjects(root, domainPath, id, b)
78
+ aggregate.Enums = extractEnums(root, domainPath, id, pkg, b)
79
+ aggregate.Events = extractEvents(root, aggregateName, domainPath, layout, id, b)
80
+ aggregate.Lifecycle = readLifecycle(pkg, aggregate.Root, aggregate.Events, id, b)
81
+
82
+ return aggregate, true
83
+ }
84
+
85
+ // extractValueObjects reads vo/* below a discovered aggregate domain package.
86
+ //
87
+ // Each directory there is one value object, and the exported struct inside it
88
+ // is its shape. `rules/` is skipped: a validation specification is how the
89
+ // value object refuses a value, not part of what it holds.
90
+ func extractValueObjects(root, domainPath, aggID string, b *plugin.Builder) []catalog.Block {
91
+ out := []catalog.Block{}
92
+
93
+ for _, name := range subdirs(root, path.Join(domainPath, "vo")) {
94
+ if name == "rules" {
95
+ continue
96
+ }
97
+
98
+ pkg, err := parsePkg(root, path.Join(domainPath, "vo", name))
99
+ if err != nil {
100
+ continue
101
+ }
102
+
103
+ found := false
104
+ for _, decl := range pkg.structs() {
105
+ if !exported(decl.name) {
106
+ continue
107
+ }
108
+
109
+ doc := decl.doc
110
+ if doc == "" {
111
+ doc = firstSentenceOrAll(pkg.doc())
112
+ }
113
+
114
+ // Qualified the way Go refers to it, so that the value object and
115
+ // the field that holds it are visibly the same thing: User.Password
116
+ // is a `password.Hash`, and a block called just `Hash` says nothing
117
+ // about which one.
118
+ label := qualified(pkg.name, decl.name)
119
+
120
+ out = append(out, catalog.Block{
121
+ ID: blockID(aggID, slug(label)),
122
+ Slug: slug(label),
123
+ Name: label,
124
+ Doc: doc,
125
+ Fields: fields(decl.fields),
126
+ })
127
+ found = true
128
+ }
129
+
130
+ if !found {
131
+ b.Warn(aggID, path.Join(domainPath, "vo", name)+" declares no exported struct; skipped")
132
+ }
133
+ }
134
+
135
+ return out
136
+ }
137
+
138
+ func hasBlock(blocks []catalog.Block, name string) bool {
139
+ for i := range blocks {
140
+ if blocks[i].Name == name {
141
+ return true
142
+ }
143
+ }
144
+
145
+ return false
146
+ }
147
+
148
+ // qualified is pkg.Name, unless the package is named after the type it holds -
149
+ // token.Token adds nothing over Token.
150
+ func qualified(pkg, name string) string {
151
+ if strings.EqualFold(pkg, name) {
152
+ return name
153
+ }
154
+
155
+ return pkg + "." + name
156
+ }
157
+
158
+ func exported(name string) bool {
159
+ return name != "" && name[0] >= 'A' && name[0] <= 'Z'
160
+ }
161
+
162
+ // aggregateReadme prefers the package's README.md, which is written for a
163
+ // reader and can draw the states, over the package comment, which is written
164
+ // for whoever opens the file next. Same preference as operationDoc, for the
165
+ // same reason. The comment is a fallback, not a second source: a package that
166
+ // has both is described by its README alone.
167
+ func aggregateReadme(root, domainPath, pkgName, name, doc string) string {
168
+ if md := readFile(filepath.Join(root, filepath.FromSlash(domainPath), "README.md")); md != "" {
169
+ return md
170
+ }
171
+
172
+ return readme(pkgName, name, doc)
173
+ }
174
+
175
+ // readme turns a package comment into the markdown the catalog carries. The
176
+ // heading is added because every other readme in a catalog has one, and a page
177
+ // that starts mid-paragraph reads as a fragment of something else.
178
+ func readme(pkgName, name, doc string) string {
179
+ doc = withoutPackagePrefix(pkgName, strings.TrimSpace(doc))
180
+ if doc == "" {
181
+ return ""
182
+ }
183
+
184
+ return "# " + name + "\n\n" + doc
185
+ }
186
+
187
+ // withoutPackagePrefix drops the "Package user " that Go doc convention puts at
188
+ // the front of a package comment.
189
+ //
190
+ // It is a convention for godoc, where the sentence is read next to the package
191
+ // clause. On a page headed "User" it reads as a leftover from somewhere else,
192
+ // and the sentence works perfectly well without it.
193
+ func withoutPackagePrefix(pkgName, doc string) string {
194
+ return withoutLeading("Package "+pkgName+" ", doc)
195
+ }
196
+
197
+ // withoutLeading is the same for anything else Go doc convention names before
198
+ // it says anything: a type comment opens with the type.
199
+ func withoutLeading(prefix, doc string) string {
200
+ rest, ok := strings.CutPrefix(doc, prefix)
201
+ if !ok {
202
+ return doc
203
+ }
204
+
205
+ runes := []rune(rest)
206
+ if len(runes) == 0 {
207
+ return doc
208
+ }
209
+ if runes[0] >= 'a' && runes[0] <= 'z' {
210
+ runes[0] = runes[0] - 'a' + 'A'
211
+ }
212
+
213
+ return string(runes)
214
+ }