@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,398 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "regexp"
7
+ "strings"
8
+
9
+ "github.com/pgplex/pgparser/nodes"
10
+ pgsql "github.com/pgplex/pgparser/parser"
11
+ "github.com/shortlink-org/portolan/plugin"
12
+ )
13
+
14
+ // The same fact read out of a repository written in TypeScript, where the
15
+ // statement and its arguments sit side by side in one call:
16
+ //
17
+ // client.query(
18
+ // "INSERT INTO baskets (id, token, customer_id, touched_at) VALUES ($1, $2, $3, $4)",
19
+ // [basket.id, basket.token, basket.customerId, basket.touchedAt.toISOString()],
20
+ // )
21
+ //
22
+ // There is no parser for the language here, and none is needed for this
23
+ // shape: the statement is a string literal, the arguments are an array
24
+ // literal right after it, and each argument is a property chain from the
25
+ // aggregate - possibly wrapped in one conversion. Anything else is left
26
+ // unmapped, for the same reason as in the Go reader: a blank is a reader
27
+ // looking the column up, a wrong one is a reader believing something untrue.
28
+ func readMapsTS(root, repositoryDir, aggregate string, b *plugin.Builder) map[string]map[string]string {
29
+ out := map[string]map[string]string{}
30
+ dir := mapSourceDir(root, repositoryDir, aggregate)
31
+ entries, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(dir)))
32
+ if err != nil {
33
+ return out
34
+ }
35
+ root_ := title(aggregate)
36
+
37
+ for _, entry := range entries {
38
+ name := entry.Name()
39
+ if entry.IsDir() || !strings.HasSuffix(name, ".ts") || strings.HasSuffix(name, ".test.ts") || strings.HasSuffix(name, ".d.ts") {
40
+ continue
41
+ }
42
+ source, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(dir), name))
43
+ if err != nil {
44
+ continue
45
+ }
46
+ roots := rootVarsTS(string(source), root_)
47
+ aliases := loopAliasesTS(string(source))
48
+ for _, stmt := range insertCalls(string(source)) {
49
+ table, columns, values := parseInsert(stmt.sql)
50
+ if table == "" {
51
+ continue
52
+ }
53
+ mapped := out[table]
54
+ if mapped == nil {
55
+ mapped = map[string]string{}
56
+ out[table] = mapped
57
+ }
58
+ numbers := placeholderNumbers(stmt.sql)
59
+ if numbers == nil || countParams(values) != len(numbers) {
60
+ if countParams(values) > 0 {
61
+ b.Warn(table, "the insert into "+table+" has placeholders this reader cannot number; its columns are left unmapped")
62
+ }
63
+ continue
64
+ }
65
+ seen := 0
66
+ for i, column := range columns {
67
+ if i >= len(values) {
68
+ break
69
+ }
70
+ if _, ok := values[i].(*nodes.ParamRef); !ok {
71
+ continue
72
+ }
73
+ number := numbers[seen]
74
+ seen++
75
+ if number < 1 || number > len(stmt.args) {
76
+ b.Warn(table, "the insert refers to $"+itoa(number)+" but the call passes "+itoa(len(stmt.args))+" values; "+column+" is left unmapped")
77
+ continue
78
+ }
79
+ field := fieldOfTS(stmt.args[number-1], roots, aliases)
80
+ if field == "" {
81
+ continue
82
+ }
83
+ mapped[column] = root_ + "." + field
84
+ }
85
+ }
86
+ }
87
+
88
+ return out
89
+ }
90
+
91
+ // tsInsert is one statement and the array of arguments written after it.
92
+ type tsInsert struct {
93
+ sql string
94
+ args []string
95
+ }
96
+
97
+ // insertCalls walks the source one character at a time, because a string
98
+ // literal can only be found by reading from its opening quote to its closing
99
+ // one: a pattern that skips ahead to the next quote it likes will start in
100
+ // one literal and end in another, and read the code between them as SQL.
101
+ // Comments are stepped over the same way. A literal that holds an INSERT and
102
+ // is followed by a comma and an array is a statement with its arguments.
103
+ func insertCalls(source string) []tsInsert {
104
+ var out []tsInsert
105
+ for i := 0; i < len(source); i++ {
106
+ c := source[i]
107
+ switch {
108
+ case c == '/' && i+1 < len(source) && source[i+1] == '/':
109
+ for i < len(source) && source[i] != '\n' {
110
+ i++
111
+ }
112
+ case c == '/' && i+1 < len(source) && source[i+1] == '*':
113
+ end := strings.Index(source[i+2:], "*/")
114
+ if end < 0 {
115
+ return out
116
+ }
117
+ i += 2 + end + 1
118
+ case c == '"' || c == '\'' || c == '`':
119
+ end := closingQuote(source, i)
120
+ if end < 0 {
121
+ return out
122
+ }
123
+ sql := source[i+1 : end]
124
+ i = end
125
+ j := skipSpace(source, end+1)
126
+ if j >= len(source) || source[j] != ',' {
127
+ continue
128
+ }
129
+ j = skipSpace(source, j+1)
130
+ if j >= len(source) || source[j] != '[' || !strings.Contains(strings.ToUpper(sql), "INSERT INTO") {
131
+ continue
132
+ }
133
+ close := matchingBracket(source, j)
134
+ if close < 0 {
135
+ continue
136
+ }
137
+ out = append(out, tsInsert{sql: sql, args: splitTopLevel(source[j+1 : close])})
138
+ i = close
139
+ }
140
+ }
141
+
142
+ return out
143
+ }
144
+
145
+ // closingQuote finds the quote that closes the one at open, stepping over
146
+ // escapes.
147
+ func closingQuote(source string, open int) int {
148
+ quote := source[open]
149
+ for i := open + 1; i < len(source); i++ {
150
+ switch source[i] {
151
+ case '\\':
152
+ i++
153
+ case quote:
154
+ return i
155
+ }
156
+ }
157
+
158
+ return -1
159
+ }
160
+
161
+ func skipSpace(source string, i int) int {
162
+ for i < len(source) && (source[i] == ' ' || source[i] == '\t' || source[i] == '\n' || source[i] == '\r') {
163
+ i++
164
+ }
165
+
166
+ return i
167
+ }
168
+
169
+ // matchingBracket finds the `]` that closes the `[` at open, stepping over
170
+ // nested brackets, parentheses, braces and string literals.
171
+ func matchingBracket(source string, open int) int {
172
+ depth := 0
173
+ var quote byte
174
+ for i := open; i < len(source); i++ {
175
+ c := source[i]
176
+ if quote != 0 {
177
+ if c == '\\' {
178
+ i++
179
+ } else if c == quote {
180
+ quote = 0
181
+ }
182
+ continue
183
+ }
184
+ switch c {
185
+ case '"', '\'', '`':
186
+ quote = c
187
+ case '[', '(', '{':
188
+ depth++
189
+ case ']', ')', '}':
190
+ depth--
191
+ if depth == 0 {
192
+ return i
193
+ }
194
+ }
195
+ }
196
+
197
+ return -1
198
+ }
199
+
200
+ // splitTopLevel splits an argument list on the commas that are not inside a
201
+ // call, an object, an array or a string.
202
+ func splitTopLevel(list string) []string {
203
+ var out []string
204
+ depth := 0
205
+ var quote byte
206
+ start := 0
207
+ for i := 0; i < len(list); i++ {
208
+ c := list[i]
209
+ if quote != 0 {
210
+ if c == '\\' {
211
+ i++
212
+ } else if c == quote {
213
+ quote = 0
214
+ }
215
+ continue
216
+ }
217
+ switch c {
218
+ case '"', '\'', '`':
219
+ quote = c
220
+ case '[', '(', '{':
221
+ depth++
222
+ case ']', ')', '}':
223
+ depth--
224
+ case ',':
225
+ if depth == 0 {
226
+ out = append(out, strings.TrimSpace(list[start:i]))
227
+ start = i + 1
228
+ }
229
+ }
230
+ }
231
+ if last := strings.TrimSpace(list[start:]); last != "" {
232
+ out = append(out, last)
233
+ }
234
+
235
+ return out
236
+ }
237
+
238
+ var tsChain = regexp.MustCompile(`^[A-Za-z_$][\w$]*(?:\??\.[A-Za-z_$][\w$]*)+$`)
239
+
240
+ // fieldOfTS follows an argument back to the field it carries, the way fieldOf
241
+ // does for Go: `basket.customerId` is customerId, `basket.touchedAt.toISOString()`
242
+ // is touchedAt, `nullable(basket.checkedOutAt)` is checkedOutAt. A bare name,
243
+ // a literal, a call that makes its own value - `randomUUID()`, `new Date()` -
244
+ // and a call over several values carry no field.
245
+ func fieldOfTS(expr string, roots map[string]bool, aliases map[string]string) string {
246
+ expr = strings.TrimSpace(expr)
247
+ // `basket.customerId ?? null` writes the field with a default for its
248
+ // absence; the column still carries the field.
249
+ if at := strings.LastIndex(expr, "??"); at >= 0 && tsLiteral.MatchString(strings.TrimSpace(expr[at+2:])) {
250
+ expr = strings.TrimSpace(expr[:at])
251
+ }
252
+ for strings.HasSuffix(expr, ")") {
253
+ open := openingParen(expr)
254
+ if open <= 0 {
255
+ return ""
256
+ }
257
+ callee, inner := expr[:open], expr[open+1:len(expr)-1]
258
+ args := splitTopLevel(inner)
259
+ switch {
260
+ case tsChain.MatchString(callee) && (len(args) == 0 || tsChain.MatchString(strings.TrimSuffix(callee[:strings.LastIndex(callee, ".")], "?"))):
261
+ // A method on the value: the value is the chain before it. With
262
+ // no arguments it is a conversion of the value - toISOString() -
263
+ // and with some it is still the value's own method when what it
264
+ // stands on is itself a chain: basket.items.slice(1) walks the
265
+ // items, where JSON.stringify(x) stands on a bare name and is
266
+ // the conversion read below.
267
+ expr = callee[:strings.LastIndex(callee, ".")]
268
+ expr = strings.TrimSuffix(expr, "?")
269
+ case len(args) == 1:
270
+ // A conversion wrapped round the value.
271
+ expr = strings.TrimSpace(args[0])
272
+ default:
273
+ return ""
274
+ }
275
+ }
276
+ if !tsChain.MatchString(expr) {
277
+ return ""
278
+ }
279
+ parts := strings.Split(strings.ReplaceAll(expr, "?.", "."), ".")
280
+ if over, ok := aliases[parts[0]]; ok {
281
+ // The variable of a loop stands for one element of what it walks:
282
+ // `for (const item of basket.items)` makes item.sku items.sku. What
283
+ // is walked is read as an argument is, so a slice of the items is
284
+ // still the items. The alias is forgotten for the rest of the read,
285
+ // so that a loop written over its own name cannot stand for itself.
286
+ rest := make(map[string]string, len(aliases))
287
+ for name, chain := range aliases {
288
+ if name != parts[0] {
289
+ rest[name] = chain
290
+ }
291
+ }
292
+ head := fieldOfTS(over, roots, rest)
293
+ if head == "" {
294
+ return ""
295
+ }
296
+
297
+ return strings.Join(append([]string{head}, parts[1:]...), ".")
298
+ }
299
+ if roots != nil && !roots[parts[0]] {
300
+ return ""
301
+ }
302
+
303
+ return strings.Join(parts[1:], ".")
304
+ }
305
+
306
+ // tsForOf is the head of a for-of loop, up to what it walks:
307
+ // `for (const item of `. What follows runs to the parenthesis that closes
308
+ // the loop's own, found by matching rather than by pattern because a slice
309
+ // or a filter carries parentheses of its own.
310
+ var tsForOf = regexp.MustCompile(`for\s*\(\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s+of\s+`)
311
+
312
+ // loopAliasesTS reads every for-of loop of the file: the variable, and the
313
+ // expression it walks.
314
+ func loopAliasesTS(source string) map[string]string {
315
+ var aliases map[string]string
316
+ for _, m := range tsForOf.FindAllStringSubmatchIndex(source, -1) {
317
+ open := strings.IndexByte(source[m[0]:], '(') + m[0]
318
+ close := matchingBracket(source, open)
319
+ if close < 0 || close < m[1] {
320
+ continue
321
+ }
322
+ if aliases == nil {
323
+ aliases = map[string]string{}
324
+ }
325
+ aliases[source[m[2]:m[3]]] = strings.TrimSpace(source[m[1]:close])
326
+ }
327
+
328
+ return aliases
329
+ }
330
+
331
+ // tsLiteral is what may stand after `??` without changing which field the
332
+ // argument carries: nothing, or a constant.
333
+ var tsLiteral = regexp.MustCompile(`^(null|undefined|true|false|-?\d+(\.\d+)?|"[^"]*"|'[^']*')$`)
334
+
335
+ // tsParam is a name declared with the type after it: a parameter, a field, a
336
+ // variable. `route: Route` is the aggregate wherever it appears in the file.
337
+ var tsParam = regexp.MustCompile(`\b([A-Za-z_$][\w$]*)\??\s*:\s*([A-Za-z_$][\w$]*)\b`)
338
+
339
+ // rootVarsTS names the variables the file declares with the root's type, or
340
+ // nil when it declares none. Inside save(route: Route, ...) the aggregate is
341
+ // route and nothing else: the loop variable over its stops carries a stop's
342
+ // fields, and a column mapped from it onto the root would be a claim about
343
+ // the wrong type. A file that never names the type - a repository over a row
344
+ // type of its own - is read as before, any bare name standing for the root.
345
+ func rootVarsTS(source, root string) map[string]bool {
346
+ var roots map[string]bool
347
+ for _, m := range tsParam.FindAllStringSubmatch(source, -1) {
348
+ if m[2] != root {
349
+ continue
350
+ }
351
+ if roots == nil {
352
+ roots = map[string]bool{}
353
+ }
354
+ roots[m[1]] = true
355
+ }
356
+
357
+ return roots
358
+ }
359
+
360
+ // openingParen finds the `(` that the closing paren at the end of expr pairs
361
+ // with.
362
+ func openingParen(expr string) int {
363
+ depth := 0
364
+ for i := len(expr) - 1; i >= 0; i-- {
365
+ switch expr[i] {
366
+ case ')':
367
+ depth++
368
+ case '(':
369
+ depth--
370
+ if depth == 0 {
371
+ return i
372
+ }
373
+ }
374
+ }
375
+
376
+ return -1
377
+ }
378
+
379
+ // parseInsert reads the table, its column list and its values out of one
380
+ // INSERT statement.
381
+ func parseInsert(sql string) (string, []string, []nodes.Node) {
382
+ tree, err := pgsql.Parse(sql)
383
+ if err != nil || len(tree.Items) == 0 {
384
+ return "", nil, nil
385
+ }
386
+ insert, ok := tree.Items[0].(*nodes.InsertStmt)
387
+ if !ok || insert.Relation == nil {
388
+ return "", nil, nil
389
+ }
390
+ var columns []string
391
+ for _, item := range items(insert.Cols) {
392
+ if target, ok := item.(*nodes.ResTarget); ok {
393
+ columns = append(columns, target.Name)
394
+ }
395
+ }
396
+
397
+ return relationName(insert.Relation), columns, insertValues(insert)
398
+ }
@@ -0,0 +1,136 @@
1
+ package main
2
+
3
+ import (
4
+ "testing"
5
+ )
6
+
7
+ // The same rule as for Go, read off text: the chain from the aggregate, one
8
+ // conversion allowed round it, a method on the value stripped.
9
+ func TestFieldOfTSFollowsTheChainAndOneWrapper(t *testing.T) {
10
+ cases := map[string]string{
11
+ "basket.id": "id",
12
+ "basket.customerId": "customerId",
13
+ "basket.touchedAt.toISOString()": "touchedAt",
14
+ "basket.unitPrice?.amountMinor": "unitPrice.amountMinor",
15
+ "nullable(basket.checkedOutAt)": "checkedOutAt",
16
+ "String(basket.version)": "version",
17
+ "JSON.stringify(toWire(event))": "",
18
+ "randomUUID()": "",
19
+ "new Date()": "",
20
+ "combine(basket.a, basket.b)": "",
21
+ "TOPIC": "",
22
+ "'literal'": "",
23
+ "basket.items.slice(1)": "items",
24
+ "JSON.stringify(basket.shipTo)": "shipTo",
25
+ "basket.customerId ?? null": "customerId",
26
+ "basket.currency?.code ?? null": "currency.code",
27
+ "basket.version ?? 0": "version",
28
+ "basket.a ?? basket.b": "",
29
+ }
30
+ for in, want := range cases {
31
+ if got := fieldOfTS(in, nil, nil); got != want {
32
+ t.Errorf("fieldOfTS(%q) = %q, want %q", in, got, want)
33
+ }
34
+ }
35
+ }
36
+
37
+ // Once the file says which name is the aggregate, only that name maps: the
38
+ // item of a loop over its lines is not the basket.
39
+ func TestFieldOfTSReadsOnlyTheRootWhenTheFileNamesIt(t *testing.T) {
40
+ roots := rootVarsTS("async save(basket: Basket, ...events: BasketEvent[]) { for (const item of basket.items) {} }", "Basket")
41
+ if !roots["basket"] || roots["events"] || roots["item"] {
42
+ t.Fatalf("roots = %v", roots)
43
+ }
44
+ cases := map[string]string{
45
+ "basket.id": "id",
46
+ "item.sku": "",
47
+ "item.quantity": "",
48
+ }
49
+ for in, want := range cases {
50
+ if got := fieldOfTS(in, roots, nil); got != want {
51
+ t.Errorf("fieldOfTS(%q) = %q, want %q", in, got, want)
52
+ }
53
+ }
54
+ if rootVarsTS("const rows = await pool.query<Row>(sql)", "Basket") != nil {
55
+ t.Error("a file that never names the root should leave every name open")
56
+ }
57
+ }
58
+
59
+ // The variable of a for-of loop stands for one element of what it walks, so
60
+ // a column written from it carries a field of the elements of a field.
61
+ func TestFieldOfTSFollowsALoopVariable(t *testing.T) {
62
+ aliases := loopAliasesTS(`for (const item of basket.items) { for (const event of events) {} } for (const self of self.items) {}
63
+ for (const scan of basket.scans.slice(stored.rows[0]?.n ?? 0)) {}`)
64
+ if aliases["item"] != "basket.items" || aliases["event"] != "events" || aliases["self"] != "self.items" || aliases["scan"] != "basket.scans.slice(stored.rows[0]?.n ?? 0)" {
65
+ t.Fatalf("aliases = %v", aliases)
66
+ }
67
+ roots := map[string]bool{"basket": true}
68
+ cases := map[string]string{
69
+ "item.sku": "items.sku",
70
+ "item.unitPrice.currency.code": "items.unitPrice.currency.code",
71
+ "item.touchedAt.toISOString()": "items.touchedAt",
72
+ "scan.parcelId": "scans.parcelId",
73
+ "event.name": "",
74
+ "self.name": "",
75
+ }
76
+ for in, want := range cases {
77
+ if got := fieldOfTS(in, roots, aliases); got != want {
78
+ t.Errorf("fieldOfTS(%q) = %q, want %q", in, got, want)
79
+ }
80
+ }
81
+ }
82
+
83
+ func TestMapsTSAgainstTheRealService(t *testing.T) {
84
+ b := builderFor(t)
85
+ mapped := readMapsTS("../../examples/shop/cart", "src/infrastructure/repository/basket", "basket", b)
86
+
87
+ want := map[string]map[string]string{
88
+ "baskets": {
89
+ "id": "Basket.id",
90
+ "token": "Basket.token",
91
+ "customer_id": "Basket.customerId",
92
+ "currency": "Basket.currency.code",
93
+ "status": "Basket.status",
94
+ "touched_at": "Basket.touchedAt",
95
+ },
96
+ "basket_items": {
97
+ "basket_id": "Basket.id",
98
+ "sku": "Basket.items.sku",
99
+ "quantity": "Basket.items.quantity",
100
+ "unit_price_minor": "Basket.items.unitPrice.amountMinor",
101
+ "currency": "Basket.items.unitPrice.currency.code",
102
+ },
103
+ }
104
+ for table, columns := range want {
105
+ for column, field := range columns {
106
+ if got := mapped[table][column]; got != field {
107
+ t.Errorf("%s.%s -> %q, want %q", table, column, got, field)
108
+ }
109
+ }
110
+ if len(mapped[table]) != len(columns) {
111
+ t.Errorf("%s: mapped %v, want exactly %v", table, mapped[table], columns)
112
+ }
113
+ }
114
+ }
115
+
116
+ func TestInsertCallsPairAStatementWithItsArray(t *testing.T) {
117
+ source := `
118
+ await client.query("BEGIN"); // a literal before, which must not swallow what follows
119
+ await client.query(
120
+ "INSERT INTO baskets (id, token, version) VALUES ($1, $2, 1)",
121
+ [basket.id, basket.token],
122
+ );
123
+ const rows = await this.pool.query<Row>("SELECT * FROM baskets WHERE id = $1", [id]);
124
+ await client.query("INSERT INTO outbox (uuid, payload) VALUES ($1, $2)", [randomUUID(), JSON.stringify({ a: [1, 2] })]);
125
+ `
126
+ calls := insertCalls(source)
127
+ if len(calls) != 2 {
128
+ t.Fatalf("found %d inserts, want 2", len(calls))
129
+ }
130
+ if len(calls[0].args) != 2 || calls[0].args[1] != "basket.token" {
131
+ t.Fatalf("first insert args = %q", calls[0].args)
132
+ }
133
+ if len(calls[1].args) != 2 || calls[1].args[1] != "JSON.stringify({ a: [1, 2] })" {
134
+ t.Fatalf("second insert args = %q", calls[1].args)
135
+ }
136
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "properties": {
5
+ "context": {
6
+ "type": "string",
7
+ "description": "Slug of the bounded context that owns the service these migrations belong to. Defaults to the name of the input directory.",
8
+ "examples": ["auth"]
9
+ },
10
+ "service": {
11
+ "type": "string",
12
+ "description": "Slug of the service that owns the database. A store belongs to exactly one service. Defaults to the name of the input directory.",
13
+ "examples": ["auth"]
14
+ },
15
+ "store": {
16
+ "type": "string",
17
+ "description": "Slug of the database these migrations build. One service can keep state in more than one, and a slug is how the two are told apart. Give the go-domain extractor the same one.",
18
+ "default": "pg"
19
+ },
20
+ "name": {
21
+ "type": "string",
22
+ "description": "What the database is called on the page.",
23
+ "examples": ["Auth database"]
24
+ },
25
+ "kind": {
26
+ "enum": [
27
+ "postgres",
28
+ "mysql",
29
+ "sqlite",
30
+ "redis",
31
+ "mongodb",
32
+ "clickhouse",
33
+ "s3",
34
+ "other"
35
+ ],
36
+ "description": "What kind of store it is.",
37
+ "default": "postgres"
38
+ },
39
+ "repositories": {
40
+ "type": "string",
41
+ "description": "Optional adapter root relative to the input root: either one package per aggregate with migrations below each package, or one feature repository package with migrations directly below it. When omitted, Go layouts are discovered under both internal/infrastructure/repository/<aggregate> and internal/<aggregate>/infrastructure/repository. The migrations and repository writes determine which aggregate and fields the tables persist."
42
+ },
43
+ "projectors": {
44
+ "type": "string",
45
+ "description": "Optional projector root relative to the input root, using the same collection-or-feature-package convention as repositories. When omitted, horizontal and feature-sliced Go projector packages are discovered. Every table created there gets the role projection. Its aggregate is read from a `-- aggregate: <id>` line above CREATE TABLE; columns copied from elsewhere carry `-- from:`."
46
+ },
47
+ "out": {
48
+ "type": "string",
49
+ "description": "Name of the fragment file, inside the step's output directory.",
50
+ "default": "stores.json"
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,70 @@
1
+ package main
2
+
3
+ // Whose rows a projection is a picture of, when the migration says.
4
+ //
5
+ // A repository package names its aggregate by where it sits: the directory is
6
+ // the aggregate, and the first table it creates is the root. A projector
7
+ // package cannot say it that way. The projection is named after the question
8
+ // it answers, not after an aggregate, and the aggregate it is built from may
9
+ // belong to another service whose name appears nowhere in this tree.
10
+ //
11
+ // So it is declared, in the same form a copied column already uses, above the
12
+ // CREATE TABLE it speaks for:
13
+ //
14
+ // -- Planned stops, rebuilt from RoutePlanned.
15
+ // -- aggregate: shop.oms.order
16
+ // CREATE TABLE route_stops (
17
+ //
18
+ // A bare slug (`route`) is an aggregate of the service that owns the store and
19
+ // is spelled the way the repository layout would spell it: `price_list` is
20
+ // `price-list`. Anything with a dot in it is a full id and is taken as
21
+ // written. Other comment lines may sit between the note and the statement; a
22
+ // statement may not, and a note that reaches nothing is dropped.
23
+
24
+ import (
25
+ "regexp"
26
+ "strings"
27
+ )
28
+
29
+ var aggregateComment = regexp.MustCompile(`(?i)^\s*--\s*aggregate:\s*(.+?)\s*$`)
30
+
31
+ // readProjected reads the `-- aggregate:` lines of one migration, as
32
+ // unqualified table name -> aggregate id.
33
+ func readProjected(sql, owner string) map[string]string {
34
+ out := map[string]string{}
35
+ pending := ""
36
+
37
+ for _, line := range strings.Split(sql, "\n") {
38
+ if found := aggregateComment.FindStringSubmatch(line); found != nil {
39
+ pending = aggregateID(found[1], owner)
40
+
41
+ continue
42
+ }
43
+ if found := createTable.FindStringSubmatch(line); found != nil {
44
+ if pending != "" {
45
+ out[unqualified(found[1])] = pending
46
+ }
47
+ pending = ""
48
+
49
+ continue
50
+ }
51
+ trimmed := strings.TrimSpace(line)
52
+ if trimmed == "" || strings.HasPrefix(trimmed, "--") {
53
+ continue
54
+ }
55
+ // Anything else is a statement, and the note was not about it.
56
+ pending = ""
57
+ }
58
+
59
+ return out
60
+ }
61
+
62
+ // aggregateID spells the note as an id: a bare slug belongs to the owner of the
63
+ // store, a dotted name is already one.
64
+ func aggregateID(note, owner string) string {
65
+ if strings.Contains(note, ".") {
66
+ return note
67
+ }
68
+
69
+ return owner + "." + slug(note)
70
+ }