@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,154 @@
1
+ package commands
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "regexp"
7
+ "slices"
8
+ "strings"
9
+
10
+ "github.com/shortlink-org/portolan/catalog"
11
+ )
12
+
13
+ var cargoAliasTable = regexp.MustCompile(`(?m)^\[alias\]\s*$`)
14
+ var xtaskArm = regexp.MustCompile(`(?m)^[ \t]*(?:Some\()?"[a-z][a-z0-9-]*"\)?[ \t]*(?:\|[ \t]*(?:Some\()?"[a-z][a-z0-9-]*"\)?[ \t]*)*=>`)
15
+ var xtaskLiteral = regexp.MustCompile(`"([a-z][a-z0-9-]*)"`)
16
+ var xtaskVariant = regexp.MustCompile(`^[ \t]*([A-Z][A-Za-z0-9]*)\s*(?:\{[^}]*\}|\([^)]*\))?\s*,?\s*$`)
17
+ var xtaskEnum = regexp.MustCompile(`(?s)#\[derive\([^)]*Subcommand[^)]*\)\][^{]*\benum\s+\w+\s*\{(.*?)\n\}`)
18
+
19
+ // readCargoAliases lists the aliases a checkout gives cargo in
20
+ // `.cargo/config.toml`. An alias is the one place a Cargo project declares a
21
+ // command of its own - `cargo xtask`, `cargo gen` - and its expansion is the
22
+ // body. When an alias runs the `xtask` package, that package's subcommands
23
+ // are listed under it as well, because `cargo xtask` on its own does nothing.
24
+ func readCargoAliases(root, file, src string) []catalog.Command {
25
+ loc := cargoAliasTable.FindStringIndex(src)
26
+ if loc == nil {
27
+ return nil
28
+ }
29
+ lines := strings.Split(src, "\n")
30
+ start := strings.Count(src[:loc[0]], "\n") + 1
31
+
32
+ var out []catalog.Command
33
+ for i := start; i < len(lines); i++ {
34
+ text := strings.TrimSpace(lines[i])
35
+ if tomlHeader.MatchString(text) {
36
+ break
37
+ }
38
+ m := tomlKey.FindStringSubmatch(text)
39
+ if m == nil {
40
+ continue
41
+ }
42
+ name := strings.Trim(m[1], `"`)
43
+ if !typeable(name) {
44
+ continue
45
+ }
46
+ body, _ := tomlTask(strings.TrimSpace(m[2]))
47
+ out = append(out, catalog.Command{Runner: "cargo", Name: name, Run: "cargo " + name, Body: body, Source: at(file, i+1)})
48
+
49
+ if pkg := runsPackage(body); pkg != "" {
50
+ out = append(out, readXtask(root, name, pkg)...)
51
+ }
52
+ }
53
+
54
+ return out
55
+ }
56
+
57
+ // runsPackage is the package an alias expansion runs, when it is `run
58
+ // --package <name>` or `run -p <name>` - the xtask convention - and the
59
+ // alias leaves the subcommand to the caller. `gen = "run -p xtask -- gen"`
60
+ // names one subcommand already, and is listed as the one command it is.
61
+ func runsPackage(expansion string) string {
62
+ fields := strings.Fields(expansion)
63
+ if len(fields) == 0 || fields[0] != "run" {
64
+ return ""
65
+ }
66
+ if i := slices.Index(fields, "--"); i >= 0 && i+1 < len(fields) {
67
+ return ""
68
+ }
69
+ for i, f := range fields {
70
+ if (f == "--package" || f == "-p") && i+1 < len(fields) {
71
+ return fields[i+1]
72
+ }
73
+ if rest, ok := strings.CutPrefix(f, "--package="); ok {
74
+ return rest
75
+ }
76
+ }
77
+
78
+ return ""
79
+ }
80
+
81
+ // readXtask lists the subcommands of an xtask-style package by reading its
82
+ // main.rs for the two shapes they are written in: a match on the first
83
+ // argument (`"dist" => ...`, `Some("dist") => ...`), and a clap enum
84
+ // deriving Subcommand, whose variants are the commands in kebab-case. A
85
+ // `///` comment over a variant, or its `about`, is the doc.
86
+ func readXtask(root, alias, pkg string) []catalog.Command {
87
+ for _, candidate := range []string{filepath.Join(pkg, "src", "main.rs"), filepath.Join("xtask", "src", "main.rs")} {
88
+ raw, err := os.ReadFile(filepath.Join(root, candidate))
89
+ if err != nil {
90
+ continue
91
+ }
92
+ src := string(raw)
93
+ file := filepath.ToSlash(filepath.Join(root, candidate))
94
+ var out []catalog.Command
95
+ seen := map[string]bool{}
96
+ add := func(name, doc string, line int) {
97
+ if seen[name] || !typeable(name) {
98
+ return
99
+ }
100
+ seen[name] = true
101
+ out = append(out, catalog.Command{Runner: "cargo", Name: alias + " " + name, Run: "cargo " + alias + " " + name, Doc: doc, Source: at(file, line)})
102
+ }
103
+
104
+ if e := xtaskEnum.FindStringSubmatchIndex(src); e != nil {
105
+ body := src[e[2]:e[3]]
106
+ lines := strings.Split(body, "\n")
107
+ base := strings.Count(src[:e[2]], "\n") + 1
108
+ for i, line := range lines {
109
+ m := xtaskVariant.FindStringSubmatch(line)
110
+ if m == nil {
111
+ continue
112
+ }
113
+ doc := commentAbove(lines, i, "///")
114
+ if doc == "" {
115
+ if a := regexp.MustCompile(`about\s*=\s*"([^"]*)"`).FindStringSubmatch(strings.Join(lines[max(0, i-3):i], "\n")); a != nil {
116
+ doc = a[1]
117
+ }
118
+ }
119
+ add(kebab(m[1]), doc, base+i)
120
+ }
121
+ }
122
+ lines := strings.Split(src, "\n")
123
+ for _, m := range xtaskArm.FindAllStringIndex(src, -1) {
124
+ // `Some("bench") | Some("perf") =>` is one arm and two names, and
125
+ // the `//` comment over the arm is what both are for.
126
+ line := strings.Count(src[:m[0]], "\n")
127
+ doc := commentAbove(lines, line, "//")
128
+ for _, literal := range xtaskLiteral.FindAllStringSubmatch(src[m[0]:m[1]], -1) {
129
+ add(literal[1], doc, line+1)
130
+ }
131
+ }
132
+
133
+ return out
134
+ }
135
+
136
+ return nil
137
+ }
138
+
139
+ func kebab(name string) string {
140
+ var b strings.Builder
141
+ for i, r := range name {
142
+ if r >= 'A' && r <= 'Z' {
143
+ if i > 0 {
144
+ b.WriteByte('-')
145
+ }
146
+ b.WriteRune(r + 'a' - 'A')
147
+
148
+ continue
149
+ }
150
+ b.WriteRune(r)
151
+ }
152
+
153
+ return b.String()
154
+ }
@@ -0,0 +1,143 @@
1
+ // Package commands reads what a developer types against a checkout - the make
2
+ // targets, npm scripts, just recipes and task-runner tasks a repository
3
+ // declares - into catalog.Command values.
4
+ //
5
+ // It reads the runner files and nothing else. A README that says "run make
6
+ // test" is a claim; a Makefile with a test target is the fact, and the two
7
+ // drift apart exactly when the README stops being read. Every parser here is
8
+ // deliberately shallow: it knows the file well enough to list its entries and
9
+ // their descriptions, and it does not evaluate anything. A make target hidden
10
+ // behind an `ifeq` is listed; a target spelled through a variable is not,
11
+ // because the name it would have depends on a value nobody here can know.
12
+ package commands
13
+
14
+ import (
15
+ "os"
16
+ "path/filepath"
17
+ "strconv"
18
+ "strings"
19
+
20
+ "github.com/shortlink-org/portolan/catalog"
21
+ )
22
+
23
+ // Read lists the commands declared at root, in a fixed order of files - make,
24
+ // just, Taskfile, the package manifest, pyproject, pom, the Gradle script,
25
+ // cargo's aliases - and in file order inside each. Source is spelled root-relative with the line, the way the rest of the
26
+ // catalog spells one.
27
+ //
28
+ // Warnings name what was seen and not read: a Taskfile that is not YAML, a
29
+ // package.json whose scripts are not an object.
30
+ func Read(root string) ([]catalog.Command, []string) {
31
+ var out []catalog.Command
32
+ var warnings []string
33
+
34
+ add := func(cmds []catalog.Command, warns []string) {
35
+ out = append(out, cmds...)
36
+ warnings = append(warnings, warns...)
37
+ }
38
+
39
+ if file, src, ok := first(root, "Makefile", "makefile", "GNUmakefile"); ok {
40
+ add(readMakefile(file, src), nil)
41
+ }
42
+ if file, src, ok := first(root, "justfile", "Justfile", ".justfile"); ok {
43
+ add(readJustfile(file, src), nil)
44
+ }
45
+ if file, src, ok := first(root, "Taskfile.yml", "Taskfile.yaml", "taskfile.yml", "taskfile.yaml", "Taskfile.dist.yml", "Taskfile.dist.yaml"); ok {
46
+ add(readTaskfile(file, src))
47
+ }
48
+ if file, src, ok := first(root, "package.json"); ok {
49
+ add(readPackageJSON(file, src, nodeRunner(root)))
50
+ }
51
+ if file, src, ok := first(root, "pyproject.toml"); ok {
52
+ add(readPyproject(file, src), nil)
53
+ }
54
+ if file, src, ok := first(root, "pom.xml"); ok {
55
+ add(readPom(root, file, src), nil)
56
+ }
57
+ if file, src, ok := first(root, "build.gradle.kts", "build.gradle"); ok {
58
+ add(readGradle(root, file, src), nil)
59
+ }
60
+ if file, src, ok := first(root, ".cargo/config.toml", ".cargo/config"); ok {
61
+ add(readCargoAliases(root, file, src), nil)
62
+ }
63
+
64
+ return out, warnings
65
+ }
66
+
67
+ // first opens the first of names that exists under root, answering its
68
+ // root-relative path and contents.
69
+ func first(root string, names ...string) (string, string, bool) {
70
+ for _, name := range names {
71
+ raw, err := os.ReadFile(filepath.Join(root, name))
72
+ if err != nil {
73
+ continue
74
+ }
75
+
76
+ return filepath.ToSlash(filepath.Join(root, name)), string(raw), true
77
+ }
78
+
79
+ return "", "", false
80
+ }
81
+
82
+ // nodeRunner is the package manager a checkout is set up for, read off the
83
+ // lockfile it keeps - the one file that says which tool the last person used.
84
+ func nodeRunner(root string) string {
85
+ for _, lock := range []struct{ file, runner string }{
86
+ {"pnpm-lock.yaml", "pnpm"},
87
+ {"yarn.lock", "yarn"},
88
+ {"bun.lockb", "bun"},
89
+ {"bun.lock", "bun"},
90
+ } {
91
+ if _, err := os.Stat(filepath.Join(root, lock.file)); err == nil {
92
+ return lock.runner
93
+ }
94
+ }
95
+
96
+ return "npm"
97
+ }
98
+
99
+ func at(file string, line int) string {
100
+ return file + ":" + strconv.Itoa(line)
101
+ }
102
+
103
+ // hooks drops the pre/post entries of a name that is itself listed, the way
104
+ // npm and pdm run them: `pretest` is part of `test`, not a second command.
105
+ func hooks(cmds []catalog.Command, prefixes ...string) []catalog.Command {
106
+ named := map[string]bool{}
107
+ for _, cmd := range cmds {
108
+ named[cmd.Name] = true
109
+ }
110
+ kept := cmds[:0]
111
+ for _, cmd := range cmds {
112
+ hook := false
113
+ for _, prefix := range prefixes {
114
+ if rest, ok := strings.CutPrefix(cmd.Name, prefix); ok && rest != "" && named[rest] {
115
+ hook = true
116
+ }
117
+ }
118
+ if !hook {
119
+ kept = append(kept, cmd)
120
+ }
121
+ }
122
+
123
+ return kept
124
+ }
125
+
126
+ // commentAbove is the contiguous block of `#` lines ending at line i-1, as one
127
+ // paragraph, or "" when the line above is not a comment. A blank line breaks
128
+ // the block: a comment two lines up is about something else.
129
+ func commentAbove(lines []string, i int, marker string) string {
130
+ var block []string
131
+ for j := i - 1; j >= 0; j-- {
132
+ text := strings.TrimSpace(lines[j])
133
+ if !strings.HasPrefix(text, marker) {
134
+ break
135
+ }
136
+ text = strings.TrimSpace(strings.TrimPrefix(text, marker))
137
+ // A `## help` line in a block is help; a `##` divider is not a word.
138
+ text = strings.TrimSpace(strings.TrimLeft(text, "#"))
139
+ block = append([]string{text}, block...)
140
+ }
141
+
142
+ return strings.TrimSpace(strings.Join(block, " "))
143
+ }
@@ -0,0 +1,323 @@
1
+ package commands
2
+
3
+ import (
4
+ "encoding/json"
5
+ "flag"
6
+ "os"
7
+ "path/filepath"
8
+ "strings"
9
+ "testing"
10
+
11
+ "github.com/shortlink-org/portolan/catalog"
12
+ )
13
+
14
+ var update = flag.Bool("update", false, "rewrite the golden list instead of comparing against it")
15
+
16
+ const goldenPath = "testdata/golden/commands.json"
17
+
18
+ func read(t *testing.T) []catalog.Command {
19
+ t.Helper()
20
+ cmds, warnings := Read("testdata/estate")
21
+ if len(warnings) != 0 {
22
+ t.Fatalf("warnings: %v", warnings)
23
+ }
24
+
25
+ return cmds
26
+ }
27
+
28
+ func find(t *testing.T, cmds []catalog.Command, run string) catalog.Command {
29
+ t.Helper()
30
+ for _, cmd := range cmds {
31
+ if cmd.Run == run {
32
+ return cmd
33
+ }
34
+ }
35
+ t.Fatalf("no command %q in %s", run, names(cmds))
36
+
37
+ return catalog.Command{}
38
+ }
39
+
40
+ func names(cmds []catalog.Command) string {
41
+ out := make([]string, 0, len(cmds))
42
+ for _, cmd := range cmds {
43
+ out = append(out, cmd.Run)
44
+ }
45
+
46
+ return strings.Join(out, ", ")
47
+ }
48
+
49
+ func absent(t *testing.T, cmds []catalog.Command, run string) {
50
+ t.Helper()
51
+ for _, cmd := range cmds {
52
+ if cmd.Run == run {
53
+ t.Errorf("%q is listed and should not be", run)
54
+ }
55
+ }
56
+ }
57
+
58
+ func TestMakefileTargetsAndDocs(t *testing.T) {
59
+ cmds := read(t)
60
+
61
+ gen := find(t, cmds, "make gen")
62
+ if gen.Doc != "Regenerate the gRPC stubs" {
63
+ t.Errorf("gen doc = %q", gen.Doc)
64
+ }
65
+ if !strings.HasPrefix(gen.Body, "buf generate $(GRPC)/quote/proto") || strings.Count(gen.Body, "\n") != 1 {
66
+ t.Errorf("gen body = %q", gen.Body)
67
+ }
68
+ if gen.Source != "testdata/estate/Makefile:10" {
69
+ t.Errorf("gen source = %q", gen.Source)
70
+ }
71
+
72
+ build := find(t, cmds, "make build")
73
+ if build.Doc != "Compiles every package. Nothing is installed." {
74
+ t.Errorf("comment over a target is its doc; got %q", build.Doc)
75
+ }
76
+
77
+ // Two targets on one rule are two commands.
78
+ find(t, cmds, "make test")
79
+ find(t, cmds, "make lint")
80
+ // Inside a conditional is still a target.
81
+ find(t, cmds, "make ci")
82
+
83
+ for _, hidden := range []string{"make .PHONY", "make _internal", "make $(BIN)", "make %.pb.go", "make GRPC", "make BIN", "make ifeq"} {
84
+ absent(t, cmds, hidden)
85
+ }
86
+ }
87
+
88
+ func TestJustfileRecipes(t *testing.T) {
89
+ cmds := read(t)
90
+
91
+ dev := find(t, cmds, "just dev")
92
+ if dev.Doc != "Run the service against the compose stack" || dev.Body != "docker compose up -d\ncargo run" {
93
+ t.Errorf("dev = %+v", dev)
94
+ }
95
+ test := find(t, cmds, "just test")
96
+ if test.Doc != "Run the tests, unit and integration" {
97
+ t.Errorf("doc attribute beats the comment; got %q", test.Doc)
98
+ }
99
+ for _, hidden := range []string{"just release", "just _setup", "just alias", "just set", "just port"} {
100
+ absent(t, cmds, hidden)
101
+ }
102
+ }
103
+
104
+ func TestTaskfileTasks(t *testing.T) {
105
+ cmds := read(t)
106
+
107
+ build := find(t, cmds, "task build")
108
+ if build.Doc != "Compile the binary" || build.Body != "go build -o bin/svc ./cmd/svc" {
109
+ t.Errorf("build = %+v", build)
110
+ }
111
+ if migrate := find(t, cmds, "task migrate"); migrate.Doc != "Apply pending migrations" || migrate.Body != "goose up" {
112
+ t.Errorf("migrate = %+v", migrate)
113
+ }
114
+ if def := find(t, cmds, "task default"); def.Body != "task build" {
115
+ t.Errorf("default = %+v", def)
116
+ }
117
+ if fmt := find(t, cmds, "task fmt"); fmt.Body != "gofmt -w ." {
118
+ t.Errorf("fmt = %+v", fmt)
119
+ }
120
+ absent(t, cmds, "task lint")
121
+ absent(t, cmds, "task _shared")
122
+ }
123
+
124
+ func TestPackageScriptsInFileOrder(t *testing.T) {
125
+ cmds := read(t)
126
+
127
+ var npm []string
128
+ for _, cmd := range cmds {
129
+ if cmd.Runner == "npm" {
130
+ npm = append(npm, cmd.Run)
131
+ }
132
+ }
133
+ want := "npm run build, npm start, npm test, npm run build:watch, npm run generate, npm run generate:api"
134
+ if got := strings.Join(npm, ", "); got != want {
135
+ t.Errorf("scripts = %s\n want %s", got, want)
136
+ }
137
+ if build := find(t, cmds, "npm run build"); build.Body != "tsc -p tsconfig.build.json" || build.Source != "testdata/estate/package.json:4" {
138
+ t.Errorf("build = %+v", build)
139
+ }
140
+ if watch := find(t, cmds, "npm run build:watch"); watch.Source != "testdata/estate/package.json:8" {
141
+ t.Errorf("a name containing another name has its own line; got %q", watch.Source)
142
+ }
143
+ }
144
+
145
+ func TestPackageRunnerFollowsTheLockfile(t *testing.T) {
146
+ root := t.TempDir()
147
+ if err := os.WriteFile(filepath.Join(root, "package.json"), []byte(`{"scripts":{"test":"vitest"}}`), 0o644); err != nil {
148
+ t.Fatal(err)
149
+ }
150
+ if err := os.WriteFile(filepath.Join(root, "pnpm-lock.yaml"), nil, 0o644); err != nil {
151
+ t.Fatal(err)
152
+ }
153
+ cmds, _ := Read(root)
154
+ if len(cmds) != 1 || cmds[0].Run != "pnpm test" || cmds[0].Runner != "pnpm" {
155
+ t.Errorf("commands = %+v", cmds)
156
+ }
157
+ }
158
+
159
+ func TestPyprojectTasks(t *testing.T) {
160
+ cmds := read(t)
161
+
162
+ if test := find(t, cmds, "poe test"); test.Body != "pytest" {
163
+ t.Errorf("test = %+v", test)
164
+ }
165
+ if lint := find(t, cmds, "poe lint"); lint.Body != "ruff check ." {
166
+ t.Errorf("lint = %+v", lint)
167
+ }
168
+ if serve := find(t, cmds, "poe serve"); serve.Doc != "Run the dev server" || serve.Body != "python manage.py runserver" {
169
+ t.Errorf("serve = %+v", serve)
170
+ }
171
+ migrate := find(t, cmds, "poe migrate")
172
+ if migrate.Doc != "Apply pending migrations" || migrate.Body != "python manage.py migrate" || migrate.Source != "testdata/estate/pyproject.toml:14" {
173
+ t.Errorf("migrate = %+v", migrate)
174
+ }
175
+ if check := find(t, cmds, "poe check"); check.Body != "lint\ntest" {
176
+ t.Errorf("check = %+v", check)
177
+ }
178
+ if worker := find(t, cmds, "pdm run worker"); worker.Doc != "Run the celery worker" || worker.Body != "celery -A billing worker" {
179
+ t.Errorf("worker = %+v", worker)
180
+ }
181
+ find(t, cmds, "pdm run test")
182
+ absent(t, cmds, "pdm run pre_test")
183
+ absent(t, cmds, "poe _hidden")
184
+ // Entry points are programs the package installs, not tasks.
185
+ absent(t, cmds, "poe billing")
186
+ }
187
+
188
+ func TestPomPhasesAndPluginGoals(t *testing.T) {
189
+ cmds := read(t)
190
+
191
+ if test := find(t, cmds, "mvn test"); test.Source != "testdata/estate/pom.xml:5" || test.Doc == "" {
192
+ t.Errorf("test = %+v", test)
193
+ }
194
+ find(t, cmds, "mvn package")
195
+ if run := find(t, cmds, "mvn spring-boot:run"); run.Doc != "Run the application" || run.Source != "testdata/estate/pom.xml:22" {
196
+ t.Errorf("spring-boot:run = %+v", run)
197
+ }
198
+ // A plugin bound to a phase is run by the phase, not typed; a plugin
199
+ // under pluginManagement or in dependencies is not part of the build.
200
+ for _, hidden := range []string{"mvn protobuf:compile", "mvn jib:dockerBuild", "mvn flyway:migrate"} {
201
+ absent(t, cmds, hidden)
202
+ }
203
+ }
204
+
205
+ func TestPomIsTypedAtTheWrapper(t *testing.T) {
206
+ root := t.TempDir()
207
+ if err := os.WriteFile(filepath.Join(root, "pom.xml"), []byte("<project><packaging>jar</packaging></project>"), 0o644); err != nil {
208
+ t.Fatal(err)
209
+ }
210
+ if err := os.WriteFile(filepath.Join(root, "mvnw"), []byte("#!/bin/sh\n"), 0o755); err != nil {
211
+ t.Fatal(err)
212
+ }
213
+ cmds, _ := Read(root)
214
+ if len(cmds) != 2 || cmds[0].Run != "./mvnw test" || cmds[0].Runner != "./mvnw" {
215
+ t.Errorf("commands = %+v", cmds)
216
+ }
217
+ }
218
+
219
+ func TestGradleTasks(t *testing.T) {
220
+ cmds := read(t)
221
+
222
+ find(t, cmds, "gradle build")
223
+ find(t, cmds, "gradle test")
224
+ if run := find(t, cmds, "gradle run"); run.Source != "testdata/estate/build.gradle.kts:3" {
225
+ t.Errorf("run = %+v", run)
226
+ }
227
+ if boot := find(t, cmds, "gradle bootRun"); boot.Source != "testdata/estate/build.gradle.kts:2" {
228
+ t.Errorf("bootRun = %+v", boot)
229
+ }
230
+ if gen := find(t, cmds, "gradle generateProto"); gen.Doc != "Regenerate the gRPC stubs" || gen.Source != "testdata/estate/build.gradle.kts:6" {
231
+ t.Errorf("generateProto = %+v", gen)
232
+ }
233
+ if smoke := find(t, cmds, "gradle smokeTest"); smoke.Doc != "Run the tests that need the compose stack" {
234
+ t.Errorf("smokeTest = %+v", smoke)
235
+ }
236
+ find(t, cmds, "gradle legacyTask")
237
+ absent(t, cmds, "gradle _shared")
238
+ }
239
+
240
+ func TestCargoAliasesAndXtask(t *testing.T) {
241
+ cmds := read(t)
242
+
243
+ if xtask := find(t, cmds, "cargo xtask"); xtask.Body != "run --package xtask --" || xtask.Source != "testdata/estate/.cargo/config.toml:5" {
244
+ t.Errorf("xtask = %+v", xtask)
245
+ }
246
+ if gen := find(t, cmds, "cargo gen"); gen.Body != "run --package xtask -- gen" {
247
+ t.Errorf("gen = %+v", gen)
248
+ }
249
+ absent(t, cmds, "cargo _secret")
250
+
251
+ if gen := find(t, cmds, "cargo xtask gen"); gen.Doc != "Regenerate the gRPC stubs from the vendored protos" || gen.Source != "testdata/estate/xtask/src/main.rs:12" {
252
+ t.Errorf("xtask gen = %+v", gen)
253
+ }
254
+ if ci := find(t, cmds, "cargo xtask ci"); ci.Doc != "Run fmt, clippy and the tests the way CI does" {
255
+ t.Errorf("xtask ci = %+v", ci)
256
+ }
257
+ if dist := find(t, cmds, "cargo xtask dist"); dist.Doc != "Build the release artefacts" {
258
+ t.Errorf("xtask dist = %+v", dist)
259
+ }
260
+ // Match arms add what the enum does not name, once each.
261
+ if bench := find(t, cmds, "cargo xtask bench"); bench.Doc != "Run the benchmarks against the compose stack" {
262
+ t.Errorf("bench = %+v", bench)
263
+ }
264
+ if perf := find(t, cmds, "cargo xtask perf"); perf.Doc != "Run the benchmarks against the compose stack" {
265
+ t.Errorf("perf = %+v", perf)
266
+ }
267
+ count := 0
268
+ for _, cmd := range cmds {
269
+ if cmd.Run == "cargo xtask gen" {
270
+ count++
271
+ }
272
+ }
273
+ if count != 1 {
274
+ t.Errorf("cargo xtask gen listed %d times", count)
275
+ }
276
+ }
277
+
278
+ func TestNothingToReadIsNothing(t *testing.T) {
279
+ cmds, warnings := Read(t.TempDir())
280
+ if len(cmds) != 0 || len(warnings) != 0 {
281
+ t.Errorf("commands = %+v, warnings = %v", cmds, warnings)
282
+ }
283
+ }
284
+
285
+ func TestBrokenFilesWarnRatherThanFail(t *testing.T) {
286
+ root := t.TempDir()
287
+ if err := os.WriteFile(filepath.Join(root, "Taskfile.yml"), []byte("tasks: [\n"), 0o644); err != nil {
288
+ t.Fatal(err)
289
+ }
290
+ if err := os.WriteFile(filepath.Join(root, "package.json"), []byte(`{"scripts": "nope"}`), 0o644); err != nil {
291
+ t.Fatal(err)
292
+ }
293
+ cmds, warnings := Read(root)
294
+ if len(cmds) != 0 || len(warnings) != 2 {
295
+ t.Errorf("commands = %+v, warnings = %v", cmds, warnings)
296
+ }
297
+ }
298
+
299
+ func TestGolden(t *testing.T) {
300
+ cmds := read(t)
301
+ got, err := json.MarshalIndent(cmds, "", " ")
302
+ if err != nil {
303
+ t.Fatal(err)
304
+ }
305
+ got = append(got, '\n')
306
+ if *update {
307
+ if err := os.MkdirAll(filepath.Dir(goldenPath), 0o755); err != nil {
308
+ t.Fatal(err)
309
+ }
310
+ if err := os.WriteFile(goldenPath, got, 0o644); err != nil {
311
+ t.Fatal(err)
312
+ }
313
+
314
+ return
315
+ }
316
+ want, err := os.ReadFile(goldenPath)
317
+ if err != nil {
318
+ t.Fatalf("%v (run with -update to write it)", err)
319
+ }
320
+ if string(want) != string(got) {
321
+ t.Errorf("golden differs from what is read now; run with -update and review the diff\n%s", got)
322
+ }
323
+ }
@@ -0,0 +1,77 @@
1
+ package commands
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "regexp"
7
+ "strings"
8
+
9
+ "github.com/shortlink-org/portolan/catalog"
10
+ )
11
+
12
+ var gradleRegister = regexp.MustCompile(`(?m)^[ \t]*(?:tasks\.register(?:<[^>]+>)?\(\s*["']([A-Za-z][\w-]*)["']|task\s+([A-Za-z][\w-]*)\s*[({]|val\s+([A-Za-z][\w-]*)\s+by\s+tasks\.(?:registering|creating))`)
13
+ var gradleDescription = regexp.MustCompile(`description\s*(?:=|\.set\()\s*["']([^"']*)["']`)
14
+ var gradlePlugin = regexp.MustCompile(`(?m)^[ \t]*(?:id\s*\(?\s*["']([\w.-]+)["']\)?|apply\s+plugin:\s*["']([\w.-]+)["']|\b(application|java|java-library|kotlin)\b\s*$)`)
15
+
16
+ // readGradle lists what a Gradle build answers to: `build` and `test`, which
17
+ // every JVM build has; `run` when the application plugin is applied and
18
+ // `bootRun` for Spring Boot; and the tasks the script registers itself, with
19
+ // the description it sets on them. The line is typed at the wrapper when the
20
+ // checkout keeps one.
21
+ //
22
+ // A build script is a program, and this reads it as text: a task registered
23
+ // inside a loop or by a plugin the script does not name is not here.
24
+ func readGradle(root, file, src string) []catalog.Command {
25
+ runner, prefix := "gradle", "gradle "
26
+ if _, err := os.Stat(filepath.Join(root, "gradlew")); err == nil {
27
+ runner, prefix = "./gradlew", "./gradlew "
28
+ }
29
+
30
+ out := []catalog.Command{
31
+ {Runner: runner, Name: "build", Run: prefix + "build", Doc: "Assemble and test the project", Source: file},
32
+ {Runner: runner, Name: "test", Run: prefix + "test", Doc: "Run the tests", Source: file},
33
+ }
34
+
35
+ plugins := map[string]int{}
36
+ for _, m := range gradlePlugin.FindAllStringSubmatchIndex(src, -1) {
37
+ for _, group := range [][2]int{{m[2], m[3]}, {m[4], m[5]}, {m[6], m[7]}} {
38
+ if group[0] >= 0 {
39
+ plugins[src[group[0]:group[1]]] = 1 + strings.Count(src[:group[0]], "\n")
40
+ }
41
+ }
42
+ }
43
+ if line, ok := plugins["application"]; ok {
44
+ out = append(out, catalog.Command{Runner: runner, Name: "run", Run: prefix + "run", Doc: "Run the application", Source: at(file, line)})
45
+ }
46
+ if line, ok := plugins["org.springframework.boot"]; ok {
47
+ out = append(out, catalog.Command{Runner: runner, Name: "bootRun", Run: prefix + "bootRun", Doc: "Run the Spring Boot application", Source: at(file, line)})
48
+ }
49
+
50
+ matches := gradleRegister.FindAllStringSubmatchIndex(src, -1)
51
+ for i, m := range matches {
52
+ name := ""
53
+ for _, group := range [][2]int{{m[2], m[3]}, {m[4], m[5]}, {m[6], m[7]}} {
54
+ if group[0] >= 0 {
55
+ name = src[group[0]:group[1]]
56
+ }
57
+ }
58
+ if !typeable(name) {
59
+ continue
60
+ }
61
+ // The description is read from the task's own block: from its
62
+ // registration up to the next one. A script that sets one task's
63
+ // description after registering the next is read wrong, and that
64
+ // is the price of not running Gradle.
65
+ end := len(src)
66
+ if i+1 < len(matches) {
67
+ end = matches[i+1][0]
68
+ }
69
+ doc := ""
70
+ if d := gradleDescription.FindStringSubmatch(src[m[0]:end]); d != nil {
71
+ doc = d[1]
72
+ }
73
+ out = append(out, catalog.Command{Runner: runner, Name: name, Run: prefix + name, Doc: doc, Source: at(file, 1+strings.Count(src[:m[0]], "\n"))})
74
+ }
75
+
76
+ return out
77
+ }