@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,332 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "os/exec"
6
+ "path/filepath"
7
+ "sort"
8
+ "strings"
9
+ "testing"
10
+
11
+ "github.com/shortlink-org/portolan/plugin"
12
+ )
13
+
14
+ // A repository to fetch from, made on the spot: git itself is the fake, and
15
+ // what it serves is what a forge would serve.
16
+ type remote struct {
17
+ url string
18
+ commit string
19
+ }
20
+
21
+ func repository(t *testing.T) remote {
22
+ t.Helper()
23
+ if _, err := exec.LookPath("git"); err != nil {
24
+ t.Skip("no git on PATH")
25
+ }
26
+ dir := t.TempDir()
27
+ run := func(args ...string) string {
28
+ t.Helper()
29
+ cmd := exec.Command("git", append([]string{
30
+ "-c", "user.name=test", "-c", "user.email=test@example.com",
31
+ "-c", "commit.gpgsign=false", "-c", "init.defaultBranch=main",
32
+ }, args...)...)
33
+ cmd.Dir = dir
34
+ out, err := cmd.CombinedOutput()
35
+ if err != nil {
36
+ t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
37
+ }
38
+
39
+ return strings.TrimSpace(string(out))
40
+ }
41
+ write := func(rel, contents string) {
42
+ t.Helper()
43
+ if err := os.MkdirAll(filepath.Dir(filepath.Join(dir, rel)), 0o755); err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ if err := os.WriteFile(filepath.Join(dir, rel), []byte(contents), 0o644); err != nil {
47
+ t.Fatal(err)
48
+ }
49
+ }
50
+
51
+ run("init", "--quiet")
52
+ write("services/oms/internal/domain/order/order.go", "package order\n")
53
+ write("services/oms/README.md", "# OMS\n")
54
+ write("proto/shop/v1/orders.proto", "syntax = \"proto3\";\n")
55
+ write("README.md", "# shop\n")
56
+ run("add", ".")
57
+ run("commit", "--quiet", "-m", "the estate")
58
+ // A forge lets a client fetch a commit by name; a plain file remote does
59
+ // not unless told to, and the fallback to branches is tested separately.
60
+ run("config", "uploadpack.allowAnySHA1InWant", "true")
61
+
62
+ return remote{url: "file://" + dir, commit: run("rev-parse", "HEAD")}
63
+ }
64
+
65
+ func options(r remote, cache string, paths ...string) Options {
66
+ return Options{
67
+ Cache: cache,
68
+ Repos: []Repo{{Repo: r.url, Commit: r.commit, Paths: paths}},
69
+ }
70
+ }
71
+
72
+ // Every test decides for itself whether it is online.
73
+ func isolate(t *testing.T) {
74
+ t.Helper()
75
+ t.Setenv(OfflineEnv, "")
76
+ t.Setenv("CI", "")
77
+ }
78
+
79
+ func names(resp plugin.Response) []string {
80
+ out := make([]string, 0, len(resp.Files))
81
+ for _, f := range resp.Files {
82
+ out = append(out, f.Name)
83
+ }
84
+ sort.Strings(out)
85
+
86
+ return out
87
+ }
88
+
89
+ func contentsOf(resp plugin.Response, name string) string {
90
+ for _, f := range resp.Files {
91
+ if f.Name == name {
92
+ return f.Contents
93
+ }
94
+ }
95
+
96
+ return ""
97
+ }
98
+
99
+ // write does what the host does with a response.
100
+ func write(t *testing.T, dir string, resp plugin.Response) {
101
+ t.Helper()
102
+ for _, f := range resp.Files {
103
+ at := filepath.Join(dir, filepath.FromSlash(f.Name))
104
+ if err := os.MkdirAll(filepath.Dir(at), 0o755); err != nil {
105
+ t.Fatal(err)
106
+ }
107
+ if err := os.WriteFile(at, []byte(f.Contents), 0o644); err != nil {
108
+ t.Fatal(err)
109
+ }
110
+ }
111
+ }
112
+
113
+ func TestFetchWritesTheNarrowedCopyAndALock(t *testing.T) {
114
+ isolate(t)
115
+ r := repository(t)
116
+
117
+ resp, err := fetch(options(r, t.TempDir(), "services/oms", "proto"))
118
+ if err != nil {
119
+ t.Fatal(err)
120
+ }
121
+ // The directory is owner/name, read off the URL; the paths inside are
122
+ // the repository's own, so an extractor can read the copy as a checkout.
123
+ dir := filepath.Base(filepath.Dir(strings.TrimPrefix(r.url, "file://"))) + "/" + filepath.Base(strings.TrimPrefix(r.url, "file://"))
124
+ want := []string{
125
+ dir + "/git.lock.json",
126
+ dir + "/git.repo.json",
127
+ dir + "/proto/shop/v1/orders.proto",
128
+ dir + "/services/oms/README.md",
129
+ dir + "/services/oms/internal/domain/order/order.go",
130
+ }
131
+ if got := names(resp); strings.Join(got, "\n") != strings.Join(want, "\n") {
132
+ t.Fatalf("files = %v, want %v", got, want)
133
+ }
134
+ lock := contentsOf(resp, dir+"/git.lock.json")
135
+ if !strings.Contains(lock, `"commit": "`+r.commit+`"`) || !strings.Contains(lock, `"sha256"`) || !strings.Contains(lock, `"services/oms"`) {
136
+ t.Errorf("lock = %s", lock)
137
+ }
138
+ if len(resp.Warnings()) != 0 {
139
+ t.Errorf("diagnostics = %+v", resp.Warnings())
140
+ }
141
+
142
+ // The fragment is the lock's commit said in the shape the catalog reads,
143
+ // and it is a catalog: empty lists rather than absent ones, so the merge
144
+ // never has to tell "no contexts" from "not a source".
145
+ fragment := contentsOf(resp, dir+"/git.repo.json")
146
+ if !strings.Contains(fragment, `"commit": "`+r.commit+`"`) {
147
+ t.Errorf("fragment does not pin the fetched commit: %s", fragment)
148
+ }
149
+ for _, empty := range []string{`"contexts": []`, `"defs": {}`, `"flows": []`, `"adrs": []`} {
150
+ if !strings.Contains(fragment, empty) {
151
+ t.Errorf("fragment is missing %s: %s", empty, fragment)
152
+ }
153
+ }
154
+ }
155
+
156
+ func TestOfflineReplayIsByteIdentical(t *testing.T) {
157
+ isolate(t)
158
+ r := repository(t)
159
+ cache := t.TempDir()
160
+
161
+ online, err := fetch(options(r, cache, "services/oms"))
162
+ if err != nil {
163
+ t.Fatal(err)
164
+ }
165
+ write(t, cache, online)
166
+
167
+ t.Setenv(OfflineEnv, "1")
168
+ replayed, err := fetch(options(r, cache, "services/oms"))
169
+ if err != nil {
170
+ t.Fatal(err)
171
+ }
172
+ if strings.Join(names(online), "\n") != strings.Join(names(replayed), "\n") {
173
+ t.Fatalf("offline names = %v, online %v", names(replayed), names(online))
174
+ }
175
+ for _, f := range online.Files {
176
+ if contentsOf(replayed, f.Name) != f.Contents {
177
+ t.Errorf("%s differs offline", f.Name)
178
+ }
179
+ }
180
+ if len(replayed.Warnings()) != 1 || !strings.Contains(replayed.Warnings()[0].Message, "offline") {
181
+ t.Errorf("diagnostics = %+v, want the one saying the copy was used", replayed.Warnings())
182
+ }
183
+ }
184
+
185
+ func TestCiImpliesOffline(t *testing.T) {
186
+ isolate(t)
187
+ r := repository(t)
188
+ cache := t.TempDir()
189
+ online, err := fetch(options(r, cache, "proto"))
190
+ if err != nil {
191
+ t.Fatal(err)
192
+ }
193
+ write(t, cache, online)
194
+
195
+ t.Setenv("CI", "true")
196
+ replayed, err := fetch(options(r, cache, "proto"))
197
+ if err != nil {
198
+ t.Fatal(err)
199
+ }
200
+ if len(replayed.Warnings()) != 1 {
201
+ t.Errorf("CI should replay and say so: %+v", replayed.Warnings())
202
+ }
203
+ }
204
+
205
+ func TestEditedCacheIsReportedByPath(t *testing.T) {
206
+ isolate(t)
207
+ r := repository(t)
208
+ cache := t.TempDir()
209
+ online, err := fetch(options(r, cache, "services/oms"))
210
+ if err != nil {
211
+ t.Fatal(err)
212
+ }
213
+ write(t, cache, online)
214
+
215
+ var edited string
216
+ for _, name := range names(online) {
217
+ if strings.HasSuffix(name, "order.go") {
218
+ edited = name
219
+ }
220
+ }
221
+ if err := os.WriteFile(filepath.Join(cache, filepath.FromSlash(edited)), []byte("package order // edited\n"), 0o644); err != nil {
222
+ t.Fatal(err)
223
+ }
224
+
225
+ t.Setenv(OfflineEnv, "1")
226
+ _, err = fetch(options(r, cache, "services/oms"))
227
+ if err == nil || !strings.Contains(err.Error(), "order.go") || !strings.Contains(err.Error(), "edited by hand") {
228
+ t.Fatalf("err = %v, want the edited file named", err)
229
+ }
230
+ }
231
+
232
+ func TestOfflineWithNoCacheFailsRatherThanEmittingNothing(t *testing.T) {
233
+ isolate(t)
234
+ t.Setenv(OfflineEnv, "1")
235
+ resp, err := fetch(options(remote{url: "file:///nowhere/acme/shop", commit: "0000000000000000000000000000000000000000"}, t.TempDir()))
236
+ if err == nil || len(resp.Files) != 0 {
237
+ t.Fatalf("err = %v, files = %v", err, names(resp))
238
+ }
239
+ }
240
+
241
+ func TestFailedFetchWithNoCacheFails(t *testing.T) {
242
+ isolate(t)
243
+ _, err := fetch(options(remote{url: "file://" + filepath.Join(t.TempDir(), "acme", "gone"), commit: "0000000000000000000000000000000000000000"}, t.TempDir()))
244
+ if err == nil || !strings.Contains(err.Error(), "no usable copy") {
245
+ t.Fatalf("err = %v", err)
246
+ }
247
+ }
248
+
249
+ func TestFailedFetchFallsBackToTheCommittedCopy(t *testing.T) {
250
+ isolate(t)
251
+ r := repository(t)
252
+ cache := t.TempDir()
253
+ online, err := fetch(options(r, cache, "proto"))
254
+ if err != nil {
255
+ t.Fatal(err)
256
+ }
257
+ write(t, cache, online)
258
+
259
+ // The forge is gone; the URL is the same, so the copy is in the same place.
260
+ if err := os.RemoveAll(strings.TrimPrefix(r.url, "file://")); err != nil {
261
+ t.Fatal(err)
262
+ }
263
+ fallback, err := fetch(options(r, cache, "proto"))
264
+ if err != nil {
265
+ t.Fatal(err)
266
+ }
267
+ if strings.Join(names(fallback), "\n") != strings.Join(names(online), "\n") {
268
+ t.Errorf("fallback = %v", names(fallback))
269
+ }
270
+ if len(fallback.Warnings()) != 1 || !strings.Contains(fallback.Warnings()[0].Message, "not fetched") {
271
+ t.Errorf("diagnostics = %+v", fallback.Warnings())
272
+ }
273
+ }
274
+
275
+ func TestUnpinnedRepositoryIsResolvedOnlineAndRefusedOffline(t *testing.T) {
276
+ isolate(t)
277
+ r := repository(t)
278
+ opts := Options{Cache: t.TempDir(), Repos: []Repo{{Repo: r.url, Ref: "main", Paths: []string{"proto"}}}}
279
+
280
+ resp, err := fetch(opts)
281
+ if err != nil {
282
+ t.Fatal(err)
283
+ }
284
+ if len(resp.Warnings()) != 1 || !strings.Contains(resp.Warnings()[0].Message, "not pinned") || !strings.Contains(resp.Warnings()[0].Message, r.commit) {
285
+ t.Errorf("diagnostics = %+v", resp.Warnings())
286
+ }
287
+
288
+ t.Setenv(OfflineEnv, "1")
289
+ if _, err := fetch(opts); err == nil || !strings.Contains(err.Error(), "not pinned") {
290
+ t.Fatalf("err = %v", err)
291
+ }
292
+ }
293
+
294
+ // A remote that does not serve commits by name still serves its branches,
295
+ // and the commit is found among them.
296
+ func TestACommitIsFetchedThroughItsBranchWhenTheRemoteRefusesIt(t *testing.T) {
297
+ isolate(t)
298
+ r := repository(t)
299
+ cmd := exec.Command("git", "config", "--unset", "uploadpack.allowAnySHA1InWant")
300
+ cmd.Dir = strings.TrimPrefix(r.url, "file://")
301
+ if out, err := cmd.CombinedOutput(); err != nil {
302
+ t.Fatalf("%v: %s", err, out)
303
+ }
304
+
305
+ resp, err := fetch(options(r, t.TempDir(), "README.md"))
306
+ if err != nil {
307
+ t.Fatal(err)
308
+ }
309
+ // The file, the lock, and the fragment naming the commit.
310
+ if got := names(resp); len(got) != 3 {
311
+ t.Errorf("files = %v", got)
312
+ }
313
+ }
314
+
315
+ func TestSplitRepo(t *testing.T) {
316
+ cases := map[string]string{
317
+ "github.com/acme/shop": "https://github.com/acme/shop|acme/shop",
318
+ "https://gitlab.com/acme/shop.git": "https://gitlab.com/acme/shop.git|acme/shop",
319
+ "git@github.com:acme/shop.git": "git@github.com:acme/shop.git|acme/shop",
320
+ "ssh://git@github.com/acme/shop": "ssh://git@github.com/acme/shop|acme/shop",
321
+ "https://gitlab.com/org/group/shop": "https://gitlab.com/org/group/shop|group/shop",
322
+ }
323
+ for name, want := range cases {
324
+ url, dir, err := splitRepo(name)
325
+ if err != nil || url+"|"+dir != want {
326
+ t.Errorf("%s = %s|%s %v, want %s", name, url, dir, err, want)
327
+ }
328
+ }
329
+ if _, _, err := splitRepo("shop"); err == nil {
330
+ t.Error("a bare name has no owner and should be refused")
331
+ }
332
+ }
@@ -0,0 +1,169 @@
1
+ package main
2
+
3
+ // The only file that runs git.
4
+ //
5
+ // The host already needs a git binary to stamp fragments, so this leans on
6
+ // the same one rather than on a library: whatever git is configured to do
7
+ // about credentials and hosts, it does here too, and this plugin has no
8
+ // opinion about it. Everything happens in a directory that exists for one
9
+ // call: a fetch of the one commit, and an archive of the paths wanted, read
10
+ // straight into memory.
11
+
12
+ import (
13
+ "archive/tar"
14
+ "bytes"
15
+ "errors"
16
+ "fmt"
17
+ "io"
18
+ "os"
19
+ "os/exec"
20
+ "path"
21
+ "strings"
22
+ )
23
+
24
+ // splitRepo turns the name the manifest uses into a URL git can fetch and
25
+ // the directory the copy lives in: owner/name, the same shape as fetch-bsr.
26
+ func splitRepo(name string) (url, dir string, err error) {
27
+ name = strings.TrimSpace(name)
28
+ if name == "" {
29
+ return "", "", fmt.Errorf("a repo entry names no repository")
30
+ }
31
+
32
+ var where string
33
+ switch {
34
+ case strings.HasPrefix(name, "git@"):
35
+ // git@host:owner/name
36
+ url = name
37
+ _, where, _ = strings.Cut(name, ":")
38
+ case strings.Contains(name, "://"):
39
+ url = name
40
+ _, where, _ = strings.Cut(name, "://")
41
+ if i := strings.Index(where, "/"); i >= 0 {
42
+ where = where[i+1:]
43
+ }
44
+ default:
45
+ // host/owner/name, the way go.mod spells it.
46
+ url = "https://" + name
47
+ if i := strings.Index(name, "/"); i >= 0 {
48
+ where = name[i+1:]
49
+ }
50
+ }
51
+
52
+ where = strings.TrimSuffix(strings.Trim(where, "/"), ".git")
53
+ segments := strings.Split(where, "/")
54
+ if len(segments) < 2 {
55
+ return "", "", fmt.Errorf("%q does not name an owner and a repository", name)
56
+ }
57
+ dir = path.Join(segments[len(segments)-2], segments[len(segments)-1])
58
+
59
+ return url, dir, nil
60
+ }
61
+
62
+ // git runs one command and answers with its output, or with what it printed
63
+ // on stderr when it failed. Prompts are off: a fetch that needs a credential
64
+ // git does not have is a failure to report, not a question to hang on.
65
+ func git(dir string, args ...string) ([]byte, error) {
66
+ cmd := exec.Command("git", args...)
67
+ cmd.Dir = dir
68
+ cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
69
+ var stderr bytes.Buffer
70
+ cmd.Stderr = &stderr
71
+ out, err := cmd.Output()
72
+ if err != nil {
73
+ msg := strings.TrimSpace(stderr.String())
74
+ if msg == "" {
75
+ msg = err.Error()
76
+ }
77
+
78
+ return nil, errors.New("git " + args[0] + ": " + firstLine(msg))
79
+ }
80
+
81
+ return out, nil
82
+ }
83
+
84
+ func firstLine(s string) string {
85
+ if i := strings.IndexByte(s, '\n'); i >= 0 {
86
+ return s[:i]
87
+ }
88
+
89
+ return s
90
+ }
91
+
92
+ // resolve asks the remote what a ref points at right now.
93
+ func resolve(url, ref string) (string, error) {
94
+ args := []string{"ls-remote", "--quiet", url}
95
+ if ref != "" {
96
+ args = append(args, ref)
97
+ } else {
98
+ args = append(args, "HEAD")
99
+ }
100
+ out, err := git("", args...)
101
+ if err != nil {
102
+ return "", err
103
+ }
104
+ for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
105
+ fields := strings.Fields(line)
106
+ if len(fields) >= 1 && len(fields[0]) == 40 {
107
+ return fields[0], nil
108
+ }
109
+ }
110
+
111
+ return "", fmt.Errorf("%s has no ref %q", url, firstNonEmpty(ref, "HEAD"))
112
+ }
113
+
114
+ // download fetches one commit and reads the wanted paths out of it.
115
+ //
116
+ // The commit is fetched by name where the forge allows it - GitHub and GitLab
117
+ // do - and by its branches where it does not, which is the case for a plain
118
+ // file:// remote; either way what is read is verified to be that commit.
119
+ func download(url, commit string, paths []string) (map[string][]byte, error) {
120
+ tmp, err := os.MkdirTemp("", "portolan-fetch-git-")
121
+ if err != nil {
122
+ return nil, err
123
+ }
124
+ defer os.RemoveAll(tmp)
125
+
126
+ if _, err := git(tmp, "init", "--quiet"); err != nil {
127
+ return nil, err
128
+ }
129
+ if _, err := git(tmp, "fetch", "--quiet", "--depth", "1", url, commit); err != nil {
130
+ if _, fallback := git(tmp, "fetch", "--quiet", "--depth", "1", url, "+refs/heads/*:refs/remotes/src/*"); fallback != nil {
131
+ return nil, err
132
+ }
133
+ }
134
+ if _, err := git(tmp, "cat-file", "-e", commit+"^{commit}"); err != nil {
135
+ return nil, fmt.Errorf("%s is not a commit %s has, or not one reachable from a branch", commit, url)
136
+ }
137
+
138
+ args := []string{"archive", "--format=tar", commit}
139
+ if len(paths) > 0 {
140
+ args = append(args, "--")
141
+ args = append(args, paths...)
142
+ }
143
+ archive, err := git(tmp, args...)
144
+ if err != nil {
145
+ return nil, err
146
+ }
147
+
148
+ files := map[string][]byte{}
149
+ reader := tar.NewReader(bytes.NewReader(archive))
150
+ for {
151
+ header, err := reader.Next()
152
+ if errors.Is(err, io.EOF) {
153
+ break
154
+ }
155
+ if err != nil {
156
+ return nil, err
157
+ }
158
+ if header.Typeflag != tar.TypeReg {
159
+ continue
160
+ }
161
+ content, err := io.ReadAll(reader)
162
+ if err != nil {
163
+ return nil, err
164
+ }
165
+ files[path.Clean(header.Name)] = content
166
+ }
167
+
168
+ return files, nil
169
+ }
@@ -0,0 +1,72 @@
1
+ package main
2
+
3
+ // What was fetched, so it can be checked without fetching again.
4
+ //
5
+ // One lock per repository directory rather than one for the whole step, so a
6
+ // vendored copy is self-describing: the files and the statement of where they
7
+ // came from sit together.
8
+
9
+ import (
10
+ "crypto/sha256"
11
+ "encoding/hex"
12
+ "encoding/json"
13
+ "sort"
14
+ )
15
+
16
+ // LockName is the file written beside each repository's copy.
17
+ const LockName = "git.lock.json"
18
+
19
+ type Lock struct {
20
+ Repos []LockRepo `json:"repos"`
21
+ }
22
+
23
+ type LockRepo struct {
24
+ Repo string `json:"repo"`
25
+
26
+ // Commit is what was actually fetched. A commit is immutable, so a pinned
27
+ // fetch is byte-reproducible and this is what makes replaying from disk
28
+ // equivalent to fetching again.
29
+ Commit string `json:"commit"`
30
+
31
+ // Paths is the narrowing the copy was made with, so a wider or narrower
32
+ // manifest is a change to the copy rather than a mystery.
33
+ Paths []string `json:"paths,omitempty"`
34
+
35
+ Files []LockEntry `json:"files"`
36
+ }
37
+
38
+ type LockEntry struct {
39
+ Path string `json:"path"`
40
+
41
+ // SHA256 is over the bytes as written. It is what turns a cache into
42
+ // something checkable: a vendored copy someone edited by hand stops
43
+ // matching, which is exactly the drift docs/adr/org.0001.md wants visible.
44
+ SHA256 string `json:"sha256"`
45
+ Size int `json:"size"`
46
+ }
47
+
48
+ func digestOf(content []byte) string {
49
+ sum := sha256.Sum256(content)
50
+
51
+ return hex.EncodeToString(sum[:])
52
+ }
53
+
54
+ // encode writes the lock the way every generated file in this repository is
55
+ // written: sorted, indented, newline-terminated. It is committed and compared
56
+ // by `gen:check`, so an unstable order would rewrite it on every run.
57
+ func (l Lock) encode() (string, error) {
58
+ for i := range l.Repos {
59
+ sort.Slice(l.Repos[i].Files, func(a, b int) bool {
60
+ return l.Repos[i].Files[a].Path < l.Repos[i].Files[b].Path
61
+ })
62
+ sort.Strings(l.Repos[i].Paths)
63
+ }
64
+ sort.Slice(l.Repos, func(a, b int) bool { return l.Repos[a].Repo < l.Repos[b].Repo })
65
+
66
+ out, err := json.MarshalIndent(l, "", " ")
67
+ if err != nil {
68
+ return "", err
69
+ }
70
+
71
+ return string(out) + "\n", nil
72
+ }
@@ -0,0 +1,127 @@
1
+ // Package main is portolan-fetch-git: a git repository on one side, a narrowed
2
+ // copy of it committed to this repository on the other.
3
+ //
4
+ // It exists for the estate whose services live in repositories of their own.
5
+ // An extractor reads one tree, and reads it as a pure function of what is on
6
+ // disk; fetching is the step that can fail, need a credential, or come back
7
+ // with something different than it did yesterday. Keeping the two apart is
8
+ // what lets CI verify an estate it never clones.
9
+ //
10
+ // The fetched files are the plugin's Response.Files, not a side effect. The
11
+ // host writes them, so they get a manifest entry, are compared by `gen:check`
12
+ // like any other generated file, and are removed when the step stops naming
13
+ // them. Paths inside the copy are the repository's own, so the extract step
14
+ // that follows points its `in` at the vendored service and reads it exactly
15
+ // as it would read the service's own checkout. Refreshing a pin then produces
16
+ // ONE pull request holding the pin bump, the source diff, the lock diff and
17
+ // the fragment diff - which is the review worth having.
18
+ //
19
+ // Two files land beside each copy. `git.lock.json` is for the next run of this
20
+ // step - the commit and every file's digest, which is what makes replaying it
21
+ // equivalent to fetching again. `git.repo.json` is for the estate: a catalog
22
+ // fragment naming the repository and the commit, which is the only way that
23
+ // fact reaches a page. Nothing downstream can work it out - a service says
24
+ // which repository it lives in, and an extractor reads a directory as a pure
25
+ // function of what is on disk - so without it a vendored service's source
26
+ // paths are text nobody can open. It has to be matched by `sources` in the
27
+ // manifest, and by SOURCE_GLOBS in src/data.ts, to be read.
28
+ //
29
+ // It is not wired into portolan.json until there is a second repository. The
30
+ // manifest it expects:
31
+ //
32
+ // {
33
+ // "sources": ["data/*.json", "vendor/repos/*/*/git.repo.json"],
34
+ // "plugins": [
35
+ // { "name": "git", "process": { "command": "go", "args": ["run", "./plugins/fetch-git"] } }
36
+ // ],
37
+ // "extract": [
38
+ // {
39
+ // "plugin": "git",
40
+ // "in": "vendor",
41
+ // "out": "vendor/repos",
42
+ // "options": {
43
+ // "cache": "vendor/repos",
44
+ // "repos": [
45
+ // {
46
+ // "repo": "github.com/acme/shop",
47
+ // "commit": "c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0",
48
+ // "paths": ["services/oms", "proto"]
49
+ // }
50
+ // ]
51
+ // }
52
+ // },
53
+ // {
54
+ // "plugin": "go-domain",
55
+ // "in": "vendor/repos/acme/shop/services/oms",
56
+ // "out": "data/shop",
57
+ // "options": { "context": "shop", "service": "oms", "store": "pg" }
58
+ // }
59
+ // ]
60
+ // }
61
+ //
62
+ // The git step is declared FIRST: extract steps run in list order, so the copy
63
+ // and its lock are on disk by the time the extractors read them.
64
+ //
65
+ // In CI, set PORTOLAN_OFFLINE=1. The step then replays the committed copies,
66
+ // checks them against their locks and emits an identical file list, so a fork's
67
+ // pull request needs no credential and a forge being down cannot turn the tree
68
+ // red.
69
+ package main
70
+
71
+ import (
72
+ "fmt"
73
+ "io"
74
+ "os"
75
+
76
+ "github.com/shortlink-org/portolan/plugin"
77
+ )
78
+
79
+ // Options are what the manifest tells the fetcher.
80
+ //
81
+ // No credential, ever. portolan.json is committed; git reaches the forge with
82
+ // whatever it is configured with - a credential helper, a netrc entry, an ssh
83
+ // agent - and this plugin reads none of it.
84
+ type Options struct {
85
+ Repos []Repo `json:"repos"`
86
+
87
+ // Cache is where the last run's files were written, repo-relative, and
88
+ // should be the same path as the step's `out`.
89
+ //
90
+ // Stated twice because a plugin is never told where its output goes. That
91
+ // is the whole shape of the protocol - a plugin returns files and the host
92
+ // decides what to do with them - and not an oversight to work around.
93
+ Cache string `json:"cache"`
94
+ }
95
+
96
+ type Repo struct {
97
+ // Repo is where the repository is: "github.com/acme/shop", or any URL git
98
+ // accepts - https://, ssh://, git@host:owner/name.
99
+ Repo string `json:"repo"`
100
+
101
+ // Commit pins it. A commit is immutable, so a pinned fetch is
102
+ // byte-reproducible - which is the only reason replaying from disk is
103
+ // equivalent to fetching again. Leaving it out is allowed and warned about;
104
+ // offline it is refused, because there is nothing to replay against.
105
+ Commit string `json:"commit,omitempty"`
106
+
107
+ // Ref is the branch or tag to resolve when there is no commit. Defaults to
108
+ // the remote's HEAD.
109
+ Ref string `json:"ref,omitempty"`
110
+
111
+ // Paths narrows the copy to the directories actually read, which is how a
112
+ // vendored repository stays small. Left out, the whole tree.
113
+ Paths []string `json:"paths,omitempty"`
114
+ }
115
+
116
+ func main() {
117
+ if err := run(os.Stdin, os.Stdout); err != nil {
118
+ fmt.Fprintln(os.Stderr, "portolan-fetch-git:", err)
119
+ os.Exit(1)
120
+ }
121
+ }
122
+
123
+ func run(stdin io.Reader, stdout io.Writer) error {
124
+ return plugin.Serve(stdin, stdout, descriptor(), func(_ plugin.Request, opts Options) (plugin.Response, error) {
125
+ return fetch(opts)
126
+ })
127
+ }