@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,122 @@
1
+ // Package main is portolan-fetch-csr: a Confluent Schema Registry on one side,
2
+ // schema files committed to this repository on the other.
3
+ //
4
+ // It is the second plugin here that opens a socket, and it is split from
5
+ // portolan-extract-csr for the reason portolan-fetch-bsr is split from
6
+ // portolan-extract-proto. Extraction stays a pure function of the tree;
7
+ // fetching is the step that can fail, need a credential, or come back with
8
+ // something different than it did yesterday. Keeping them apart is what lets
9
+ // CI verify an estate whose schemas live in a registry it never talks to.
10
+ //
11
+ // The fetched schemas are the plugin's Response.Files, not a side effect. The
12
+ // host writes them, so they get a manifest entry, are compared by `gen:check`
13
+ // like any other generated file, and are removed when the step stops naming
14
+ // them. Refreshing a pin then produces ONE pull request holding the version
15
+ // bump, the schema diff, the lock diff and the fragment diff - which is the
16
+ // review worth having, and the one a registry UI cannot give you.
17
+ //
18
+ // A registered version is immutable: subject `orders-value` at version 3 is
19
+ // the same bytes today and next year, and re-registering a changed schema
20
+ // makes version 4. That is the same promise a BSR commit makes, and it is the
21
+ // only reason replaying from disk is equivalent to fetching again. So a
22
+ // subject is PINNED to a version here, exactly as a module is pinned to a
23
+ // commit there.
24
+ //
25
+ // The shape of the two steps, fetch and then read:
26
+ //
27
+ // {
28
+ // "plugins": [
29
+ // { "name": "csr", "process": { "command": "go", "args": ["run", "./plugins/fetch-csr"] } },
30
+ // { "name": "csr-schemas", "process": { "command": "go", "args": ["run", "./plugins/extract-csr"] } }
31
+ // ],
32
+ // "extract": [
33
+ // {
34
+ // "plugin": "csr",
35
+ // "in": "examples/shop/oms",
36
+ // "out": "examples/shop/oms/vendor/schemas",
37
+ // "options": {
38
+ // "registry": "https://psrc-00000.eu-central-1.aws.confluent.cloud",
39
+ // "cache": "examples/shop/oms/vendor/schemas",
40
+ // "subjects": [{ "subject": "shop.oms.order-value", "version": 3 }]
41
+ // }
42
+ // },
43
+ // {
44
+ // "plugin": "csr-schemas",
45
+ // "in": "examples/shop/oms",
46
+ // "out": "examples/shop/oms/portolan",
47
+ // "options": {
48
+ // "context": "shop",
49
+ // "service": "oms",
50
+ // "paths": ["vendor/schemas"],
51
+ // "out": "schemas.json"
52
+ // }
53
+ // }
54
+ // ]
55
+ // }
56
+ //
57
+ // The csr step is declared FIRST: extract steps run in list order, so its
58
+ // schemas and locks are on disk by the time the reader looks for them.
59
+ //
60
+ // In CI, set PORTOLAN_OFFLINE=1. The step then replays the committed copies,
61
+ // checks them against their locks and emits an identical file list, so a
62
+ // fork's pull request needs no secret and the registry being down cannot turn
63
+ // the tree red.
64
+ package main
65
+
66
+ import (
67
+ "fmt"
68
+ "io"
69
+ "os"
70
+
71
+ "github.com/shortlink-org/portolan/plugin"
72
+ )
73
+
74
+ // Options are what the manifest tells the fetcher.
75
+ //
76
+ // No credential, ever. portolan.json is committed; the API key comes from the
77
+ // environment, and only auth.go looks at it. The registry URL is not a
78
+ // credential - it is a fact about the estate, the same kind of fact as a
79
+ // module's registry-global name - so it is written down here.
80
+ type Options struct {
81
+ // Registry is the base URL of the schema registry, without a trailing
82
+ // path: "https://psrc-00000.eu-central-1.aws.confluent.cloud", or
83
+ // "http://localhost:8081" for one running beside the tests.
84
+ Registry string `json:"registry"`
85
+
86
+ Subjects []Subject `json:"subjects"`
87
+
88
+ // Cache is where the last run's files were written, repo-relative, and
89
+ // should be the same path as the step's `out`.
90
+ //
91
+ // Stated twice because a plugin is never told where its output goes. That
92
+ // is the whole shape of the protocol - a plugin returns files and the host
93
+ // decides what to do with them - and not an oversight to work around.
94
+ Cache string `json:"cache"`
95
+ }
96
+
97
+ // Subject is one pinned registration.
98
+ type Subject struct {
99
+ // Subject is the registry's own name for it: "shop.oms.order-value" under
100
+ // TopicNameStrategy, "shop.oms.OrderPlaced" under RecordNameStrategy.
101
+ Subject string `json:"subject"`
102
+
103
+ // Version pins it. A registered version is immutable, so a pinned
104
+ // download is byte-reproducible - which is the only reason replaying from
105
+ // disk is equivalent to fetching again. Leaving it out resolves `latest`
106
+ // and is warned about; offline it is refused, because there is nothing to
107
+ // replay against.
108
+ Version int `json:"version,omitempty"`
109
+ }
110
+
111
+ func main() {
112
+ if err := run(os.Stdin, os.Stdout); err != nil {
113
+ fmt.Fprintln(os.Stderr, "portolan-fetch-csr:", err)
114
+ os.Exit(1)
115
+ }
116
+ }
117
+
118
+ func run(stdin io.Reader, stdout io.Writer) error {
119
+ return plugin.Serve(stdin, stdout, descriptor(), func(_ plugin.Request, opts Options) (plugin.Response, error) {
120
+ return fetch(opts)
121
+ })
122
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "required": ["registry", "subjects", "cache"],
5
+ "properties": {
6
+ "registry": {
7
+ "type": "string",
8
+ "description": "Base URL of the schema registry. The credential for it is never written here: it comes from the environment, so this file stays committable.",
9
+ "examples": [
10
+ "http://localhost:8081",
11
+ "https://psrc-00000.eu-central-1.aws.confluent.cloud"
12
+ ]
13
+ },
14
+ "subjects": {
15
+ "type": "array",
16
+ "minItems": 1,
17
+ "description": "The subjects to fetch, each pinned to a version. A subject a fetched schema references is fetched too, pinned by the reference itself, and needs no entry here.",
18
+ "items": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": ["subject"],
22
+ "properties": {
23
+ "subject": {
24
+ "type": "string",
25
+ "description": "The registry's own name for the registration: \"<topic>-value\" under TopicNameStrategy, the record's full name under RecordNameStrategy.",
26
+ "examples": ["shop.oms.order-value", "shop.oms.OrderPlaced"]
27
+ },
28
+ "version": {
29
+ "type": "integer",
30
+ "minimum": 1,
31
+ "description": "The version to fetch. A registered version is immutable, so a pinned fetch is byte-reproducible, which is what lets an offline run replay it from the tree. Left out, `latest` is resolved online and warned about, and refused offline."
32
+ }
33
+ }
34
+ }
35
+ },
36
+ "cache": {
37
+ "type": "string",
38
+ "description": "Where the last run's files were written, repo-relative: the same path as the step's out. An offline run replays what is there and checks it against the lock beside it.",
39
+ "examples": ["examples/shop/oms/vendor/schemas"]
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,209 @@
1
+ package main
2
+
3
+ // The registry's REST API, and only the two calls this plugin makes.
4
+ //
5
+ // A schema registry has a large surface - compatibility levels, modes, global
6
+ // configuration, deletion. None of it belongs here. This plugin reads
7
+ // registrations and writes nothing, which is what makes it safe to point at a
8
+ // production registry from a laptop.
9
+
10
+ import (
11
+ "bytes"
12
+ "encoding/json"
13
+ "fmt"
14
+ "io"
15
+ "net/http"
16
+ "net/url"
17
+ "strconv"
18
+ "strings"
19
+ "time"
20
+ )
21
+
22
+ // Accept names the registry's own media type first and plain JSON second, so
23
+ // the same client works against Confluent's registry and the several
24
+ // API-compatible ones that only answer application/json.
25
+ const accept = "application/vnd.schemaregistry.v1+json, application/json"
26
+
27
+ type client struct {
28
+ base string
29
+ auth string
30
+ http *http.Client
31
+ }
32
+
33
+ func newClient(base, auth string) *client {
34
+ return &client{
35
+ base: strings.TrimRight(base, "/"),
36
+ auth: auth,
37
+ // A registry that has stopped answering must fail the step rather than
38
+ // hang a build until somebody notices.
39
+ http: &http.Client{Timeout: 30 * time.Second},
40
+ }
41
+ }
42
+
43
+ // registration is one version of one subject, as the registry answers it.
44
+ type registration struct {
45
+ Subject string `json:"subject"`
46
+ Version int `json:"version"`
47
+
48
+ // ID is the registry-global schema id - the four bytes a Kafka message
49
+ // carries in its wire header. It identifies the SCHEMA, not the
50
+ // registration: two subjects registering identical bytes share one id.
51
+ ID int `json:"id"`
52
+
53
+ // GUID is the newer, wider identifier for the same thing. Registries that
54
+ // predate it leave it out, so it is never required.
55
+ GUID string `json:"guid,omitempty"`
56
+
57
+ // SchemaType is "AVRO", "PROTOBUF" or "JSON". Absent means AVRO: the
58
+ // registry only started saying so when it stopped being the only option.
59
+ SchemaType string `json:"schemaType,omitempty"`
60
+
61
+ // Schema is the schema itself, as text. For AVRO and JSON that text is
62
+ // itself JSON, which is why it arrives escaped inside a JSON field.
63
+ Schema string `json:"schema"`
64
+
65
+ References []Reference `json:"references,omitempty"`
66
+ }
67
+
68
+ // Reference is one subject a schema depends on, pinned by the referring schema
69
+ // rather than by the manifest. That is what makes a reference safe to follow
70
+ // without asking: it names a version, and a version is immutable.
71
+ type Reference struct {
72
+ // Name is how the referring schema spells it - an Avro full name, a
73
+ // protobuf import path, a JSON Schema `$ref` URL.
74
+ Name string `json:"name"`
75
+ Subject string `json:"subject"`
76
+ Version int `json:"version"`
77
+ }
78
+
79
+ // apiError is the registry's own error shape. Read rather than ignored,
80
+ // because "Subject not found" and "Version not found" are the two mistakes a
81
+ // manifest actually makes, and the HTTP status alone says neither.
82
+ type apiError struct {
83
+ Code int `json:"error_code"`
84
+ Message string `json:"message"`
85
+ }
86
+
87
+ // version fetches one registration. A zero version asks for `latest`.
88
+ func (c *client) version(subject string, version int) (*registration, error) {
89
+ which := "latest"
90
+ if version > 0 {
91
+ which = strconv.Itoa(version)
92
+ }
93
+
94
+ // Escaped, because a subject under RecordNameStrategy is a full name and
95
+ // may hold anything the producer's language allows in one.
96
+ at := c.base + "/subjects/" + url.PathEscape(subject) + "/versions/" + which
97
+
98
+ var got registration
99
+ if err := c.get(at, &got); err != nil {
100
+ return nil, fmt.Errorf("%s at version %s: %w", subject, which, err)
101
+ }
102
+ if strings.TrimSpace(got.Schema) == "" {
103
+ return nil, fmt.Errorf("%s at version %s: the registry answered with no schema", subject, which)
104
+ }
105
+ // A registry that answers `latest` says which version that was; one that
106
+ // does not has told us nothing we could pin.
107
+ if got.Version <= 0 {
108
+ return nil, fmt.Errorf("%s at version %s: the registry did not say which version it answered with", subject, which)
109
+ }
110
+
111
+ return &got, nil
112
+ }
113
+
114
+ func (c *client) get(at string, into any) error {
115
+ req, err := http.NewRequest(http.MethodGet, at, nil)
116
+ if err != nil {
117
+ return err
118
+ }
119
+ req.Header.Set("Accept", accept)
120
+ if c.auth != "" {
121
+ req.Header.Set("Authorization", c.auth)
122
+ }
123
+
124
+ resp, err := c.http.Do(req)
125
+ if err != nil {
126
+ return err
127
+ }
128
+ defer resp.Body.Close()
129
+
130
+ body, err := io.ReadAll(io.LimitReader(resp.Body, 32<<20))
131
+ if err != nil {
132
+ return err
133
+ }
134
+
135
+ if resp.StatusCode != http.StatusOK {
136
+ var fault apiError
137
+ if json.Unmarshal(body, &fault) == nil && fault.Message != "" {
138
+ return fmt.Errorf("the registry answered %s: %s (error_code %d)",
139
+ resp.Status, fault.Message, fault.Code)
140
+ }
141
+
142
+ return fmt.Errorf("the registry answered %s", resp.Status)
143
+ }
144
+
145
+ if err := json.Unmarshal(body, into); err != nil {
146
+ return fmt.Errorf("the registry's answer is not the shape this plugin reads: %w", err)
147
+ }
148
+
149
+ return nil
150
+ }
151
+
152
+ // schemaType normalizes the type. The empty answer is AVRO by the registry's
153
+ // own rule, and normalizing it here means nothing downstream has to know that.
154
+ func schemaType(declared string) string {
155
+ if strings.TrimSpace(declared) == "" {
156
+ return "AVRO"
157
+ }
158
+
159
+ return strings.ToUpper(strings.TrimSpace(declared))
160
+ }
161
+
162
+ // extensionFor is what the vendored file is called, so an editor opens it with
163
+ // the right syntax and a reviewer reads a diff rather than a wall of escapes.
164
+ func extensionFor(kind string) (string, error) {
165
+ switch schemaType(kind) {
166
+ case "AVRO":
167
+ return ".avsc", nil
168
+ case "PROTOBUF":
169
+ return ".proto", nil
170
+ case "JSON":
171
+ return ".json", nil
172
+ }
173
+
174
+ return "", fmt.Errorf("unknown schema type %q", kind)
175
+ }
176
+
177
+ // body is the schema as it should be written to the tree.
178
+ //
179
+ // AVRO and JSON arrive as JSON minified onto one line, which makes a version
180
+ // bump a one-line diff nobody can read. Indenting it is done with json.Indent,
181
+ // which reformats the bytes without reordering them, so the file still says
182
+ // what the registry said, in the order it said it. PROTOBUF arrives as source
183
+ // and is left exactly as it is.
184
+ //
185
+ // The digest is taken over the bytes as written, so the reformatting is inside
186
+ // what the lock checks rather than something that has to be redone to verify.
187
+ func body(kind, schema string) string {
188
+ if schemaType(kind) == "PROTOBUF" {
189
+ return ensureNewline(schema)
190
+ }
191
+
192
+ var pretty bytes.Buffer
193
+ if err := json.Indent(&pretty, []byte(schema), "", " "); err != nil {
194
+ // Not an error: a registry that answered with something that is not
195
+ // JSON has told us something, and the honest thing is to keep it so
196
+ // the reader can see what arrived.
197
+ return ensureNewline(schema)
198
+ }
199
+
200
+ return ensureNewline(pretty.String())
201
+ }
202
+
203
+ func ensureNewline(s string) string {
204
+ if strings.HasSuffix(s, "\n") {
205
+ return s
206
+ }
207
+
208
+ return s + "\n"
209
+ }
@@ -0,0 +1,65 @@
1
+ package main
2
+
3
+ // Replaying a fetch from what is already on disk.
4
+ //
5
+ // The cache is not a second copy of anything: it IS the tree. The files this
6
+ // plugin fetched were written by the host through the ordinary Response.Files
7
+ // path, so they are committed, reviewable, and removed when the step stops
8
+ // naming them. Reading them back and checking them against the lock is what
9
+ // lets a build that never opens a socket produce byte-identical output.
10
+
11
+ import (
12
+ "encoding/json"
13
+ "fmt"
14
+ "os"
15
+ "path/filepath"
16
+ )
17
+
18
+ // cached is one repository's files, read back from the tree.
19
+ type cached struct {
20
+ lock LockRepo
21
+ files map[string][]byte
22
+ }
23
+
24
+ // replay reads a repository's committed copy and checks it against its lock.
25
+ //
26
+ // An incomplete or edited cache is reported rather than patched over. The two
27
+ // failures it distinguishes matter: nothing on disk means there is nothing to
28
+ // fall back to, and bytes that no longer match their digest mean somebody
29
+ // edited a vendored copy - the drift docs/adr/org.0001.md wants visible.
30
+ func replay(dir string) (*cached, error) {
31
+ at := filepath.Join(dir, LockName)
32
+ raw, err := os.ReadFile(at)
33
+ if os.IsNotExist(err) {
34
+ return nil, fmt.Errorf("no %s in %s", LockName, filepath.ToSlash(dir))
35
+ }
36
+ if err != nil {
37
+ return nil, err
38
+ }
39
+
40
+ var lock Lock
41
+ if err := json.Unmarshal(raw, &lock); err != nil {
42
+ return nil, fmt.Errorf("%s: %w", filepath.ToSlash(at), err)
43
+ }
44
+ if len(lock.Repos) != 1 {
45
+ return nil, fmt.Errorf("%s names %d repositories; expected exactly one", filepath.ToSlash(at), len(lock.Repos))
46
+ }
47
+
48
+ entry := lock.Repos[0]
49
+ files := make(map[string][]byte, len(entry.Files))
50
+ for _, want := range entry.Files {
51
+ content, err := os.ReadFile(filepath.Join(dir, filepath.FromSlash(want.Path)))
52
+ if os.IsNotExist(err) {
53
+ return nil, fmt.Errorf("%s is in the lock but not on disk", want.Path)
54
+ }
55
+ if err != nil {
56
+ return nil, err
57
+ }
58
+ if got := digestOf(content); got != want.SHA256 {
59
+ return nil, fmt.Errorf("%s does not match its digest; the vendored copy was edited by hand", want.Path)
60
+ }
61
+ files[want.Path] = content
62
+ }
63
+
64
+ return &cached{lock: entry, files: files}, nil
65
+ }
@@ -0,0 +1,19 @@
1
+ package main
2
+
3
+ import (
4
+ _ "embed"
5
+
6
+ "github.com/shortlink-org/portolan/plugin"
7
+ )
8
+
9
+ //go:embed options.schema.json
10
+ var optionsSchema []byte
11
+
12
+ func descriptor() plugin.Descriptor {
13
+ return plugin.Descriptor{
14
+ Name: "fetch-git",
15
+ Summary: "Fetches pinned directories of another git repository into the tree, with a lock, so extractors can read a service that lives elsewhere.",
16
+ Phases: []string{plugin.PhaseExtract},
17
+ Options: optionsSchema,
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ package main
2
+
3
+ import (
4
+ "testing"
5
+
6
+ "github.com/shortlink-org/portolan/plugin/schematest"
7
+ )
8
+
9
+ func TestOptionsSchemaMatchesStruct(t *testing.T) {
10
+ schematest.Check(t, optionsSchema, Options{})
11
+ }
@@ -0,0 +1,181 @@
1
+ package main
2
+
3
+ // Deciding whether to fetch, and what to do when the fetch cannot happen.
4
+ //
5
+ // The rules below are the design, not error handling, and they are the rules
6
+ // fetch-bsr lives by. The one that matters most is the third: a failed fetch
7
+ // with no cache is a NON-ZERO EXIT, never a short file list. The host deletes
8
+ // files a step stops naming, and dropping a vendored service because a laptop
9
+ // went offline is worse than a red build.
10
+
11
+ import (
12
+ "fmt"
13
+ "path"
14
+ "path/filepath"
15
+ "sort"
16
+ "strings"
17
+
18
+ "github.com/shortlink-org/portolan/plugin"
19
+ )
20
+
21
+ // fetch reads nothing out of the tree being described. Its `cache` is
22
+ // repo-relative, like every other path in the manifest, and everything else
23
+ // it needs is in the options.
24
+ func fetch(opts Options) (plugin.Response, error) {
25
+ b := &plugin.Builder{}
26
+
27
+ if len(opts.Repos) == 0 {
28
+ return plugin.Response{}, fmt.Errorf("no repositories to fetch: name at least one in the manifest")
29
+ }
30
+ if opts.Cache == "" {
31
+ return plugin.Response{}, fmt.Errorf(
32
+ "no cache directory: set `cache` to the same path as the step's `out`, " +
33
+ "so an offline run can replay what the last online one wrote")
34
+ }
35
+
36
+ // Sorted, so the file list and the diagnostics come out the same way every
37
+ // run whatever order the manifest happens to list them in.
38
+ wanted := append([]Repo(nil), opts.Repos...)
39
+ sort.Slice(wanted, func(i, j int) bool { return wanted[i].Repo < wanted[j].Repo })
40
+
41
+ skip := offline()
42
+
43
+ for _, want := range wanted {
44
+ url, dir, err := splitRepo(want.Repo)
45
+ if err != nil {
46
+ return plugin.Response{}, err
47
+ }
48
+ at := filepath.Join(filepath.FromSlash(opts.Cache), filepath.FromSlash(dir))
49
+
50
+ if skip {
51
+ if err := emitCached(b, dir, at, want, "offline"); err != nil {
52
+ return plugin.Response{}, err
53
+ }
54
+
55
+ continue
56
+ }
57
+
58
+ commit, files, err := live(url, want, b)
59
+ if err != nil {
60
+ // Rule 2: the tree still holds a good copy, so the output is
61
+ // unchanged and `--check` stays clean.
62
+ if cacheErr := emitCached(b, dir, at, want, err.Error()); cacheErr != nil {
63
+ // Rule 3: nothing to fall back to.
64
+ return plugin.Response{}, fmt.Errorf(
65
+ "%s could not be fetched (%w) and there is no usable copy in the tree (%v)",
66
+ want.Repo, err, cacheErr)
67
+ }
68
+
69
+ continue
70
+ }
71
+
72
+ if err := emitFetched(b, dir, want, commit, files); err != nil {
73
+ return plugin.Response{}, err
74
+ }
75
+ }
76
+
77
+ return b.Response(), nil
78
+ }
79
+
80
+ // live fetches one repository, pinning it first if the manifest did not.
81
+ func live(url string, want Repo, b *plugin.Builder) (string, map[string][]byte, error) {
82
+ commit := want.Commit
83
+ if commit == "" {
84
+ // Rule: pin, or every run is a lottery. Resolving a ref still works,
85
+ // but it is said out loud, because two runs a day apart would then
86
+ // produce two different trees from one manifest.
87
+ resolved, err := resolve(url, want.Ref)
88
+ if err != nil {
89
+ return "", nil, err
90
+ }
91
+ b.Warn(want.Repo, fmt.Sprintf(
92
+ "is not pinned; %q resolved to %s. Pin it in portolan.json or every run is a lottery.",
93
+ firstNonEmpty(want.Ref, "HEAD"), resolved))
94
+ commit = resolved
95
+ }
96
+
97
+ files, err := download(url, commit, want.Paths)
98
+ if err != nil {
99
+ return "", nil, err
100
+ }
101
+ if len(files) == 0 {
102
+ return "", nil, fmt.Errorf("%s at %s holds no files under %s", want.Repo, commit, strings.Join(want.Paths, ", "))
103
+ }
104
+
105
+ return commit, files, nil
106
+ }
107
+
108
+ // emitFetched writes what came back, plus the lock that makes it checkable.
109
+ func emitFetched(b *plugin.Builder, dir string, want Repo, commit string, files map[string][]byte) error {
110
+ entry := LockRepo{Repo: want.Repo, Commit: commit, Paths: append([]string(nil), want.Paths...)}
111
+
112
+ paths := make([]string, 0, len(files))
113
+ for at := range files {
114
+ paths = append(paths, at)
115
+ }
116
+ sort.Strings(paths)
117
+
118
+ for _, at := range paths {
119
+ content := files[at]
120
+ b.File(path.Join(dir, at), string(content))
121
+ entry.Files = append(entry.Files, LockEntry{Path: at, SHA256: digestOf(content), Size: len(content)})
122
+ }
123
+
124
+ lock, err := Lock{Repos: []LockRepo{entry}}.encode()
125
+ if err != nil {
126
+ return err
127
+ }
128
+ b.File(path.Join(dir, LockName), lock)
129
+
130
+ return emitPin(b, dir, want.Repo, commit)
131
+ }
132
+
133
+ // emitCached re-emits the committed copy unchanged.
134
+ //
135
+ // Byte-identical to what the last online run wrote, which is the whole point:
136
+ // `gen:check` sees no drift, so CI can verify an estate whose services live in
137
+ // repositories it never clones.
138
+ func emitCached(b *plugin.Builder, dir, at string, want Repo, why string) error {
139
+ if want.Commit == "" {
140
+ return fmt.Errorf("%s is not pinned to a commit, so there is nothing to replay", want.Repo)
141
+ }
142
+
143
+ held, err := replay(at)
144
+ if err != nil {
145
+ return err
146
+ }
147
+ if held.lock.Commit != want.Commit {
148
+ return fmt.Errorf("%s holds commit %s but the manifest pins %s; fetch it", want.Repo, held.lock.Commit, want.Commit)
149
+ }
150
+
151
+ for _, entry := range held.lock.Files {
152
+ b.File(path.Join(dir, entry.Path), string(held.files[entry.Path]))
153
+ }
154
+
155
+ lock, err := Lock{Repos: []LockRepo{held.lock}}.encode()
156
+ if err != nil {
157
+ return err
158
+ }
159
+ b.File(path.Join(dir, LockName), lock)
160
+
161
+ // From the LOCK's commit, not the manifest's. They are equal by the check
162
+ // above, and taking it from the copy is what keeps the fragment describing
163
+ // what is actually on disk rather than what was asked for.
164
+ if err := emitPin(b, dir, want.Repo, held.lock.Commit); err != nil {
165
+ return err
166
+ }
167
+
168
+ b.Warn(want.Repo, "not fetched ("+why+"); the copy committed in this repository is used unchanged")
169
+
170
+ return nil
171
+ }
172
+
173
+ func firstNonEmpty(values ...string) string {
174
+ for _, v := range values {
175
+ if v != "" {
176
+ return v
177
+ }
178
+ }
179
+
180
+ return ""
181
+ }