@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,338 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "path/filepath"
8
+ "sort"
9
+ "strings"
10
+
11
+ "github.com/shortlink-org/portolan/catalog"
12
+ "github.com/shortlink-org/portolan/plugin"
13
+ )
14
+
15
+ // LockName is what fetch-csr writes beside each vendored schema. Finding one
16
+ // is what makes a directory a subject rather than a directory that happens to
17
+ // hold a .avsc.
18
+ const LockName = "csr.lock.json"
19
+
20
+ func extract(in plugin.Input, opts Options) (plugin.Response, error) {
21
+ b := &plugin.Builder{}
22
+ root := in.Root
23
+
24
+ if opts.Context == "" {
25
+ opts.Context = filepath.Base(root)
26
+ }
27
+ if opts.Service == "" {
28
+ opts.Service = filepath.Base(root)
29
+ }
30
+ service := opts.Context + "." + opts.Service
31
+ strategy := strategyOf(opts.Strategy)
32
+
33
+ locks, err := find(root, opts.Paths)
34
+ if err != nil {
35
+ return plugin.Response{}, err
36
+ }
37
+ if len(locks) == 0 {
38
+ b.Warn(service, "no "+LockName+" under "+root+"; nothing was fetched from a schema registry, or it was fetched somewhere this step is not looking")
39
+ }
40
+
41
+ defs := map[string]catalog.TypeDef{}
42
+ channels := newChannels()
43
+
44
+ for _, at := range locks {
45
+ if err := readSubject(b, at, root, service, strategy, opts, defs, channels); err != nil {
46
+ return plugin.Response{}, err
47
+ }
48
+ }
49
+
50
+ // A field pointing at a shape nobody vendored would fail the catalog's own
51
+ // validation, and the reference is a real fact worth keeping as a label
52
+ // even when the shape behind it is not in this estate. So the label stays
53
+ // and the ref goes.
54
+ drop(defs, b, service)
55
+
56
+ fragment := catalog.Catalog{
57
+ GeneratedAt: in.GeneratedAt,
58
+ Commit: in.Commit,
59
+ Contexts: []catalog.BoundedContext{{
60
+ ID: opts.Context,
61
+ Slug: opts.Context,
62
+ // Named by whichever source knows the name, the way the AsyncAPI
63
+ // and OpenAPI fragments leave it: a registry describes what a
64
+ // service puts on a topic, not what the service is called.
65
+ Services: []catalog.Service{{
66
+ ID: service,
67
+ Slug: opts.Service,
68
+ Provides: []catalog.RpcService{},
69
+ Consumes: []catalog.RpcCall{},
70
+ Aggregates: []catalog.Aggregate{},
71
+ Channels: channels.all(),
72
+ }},
73
+ }},
74
+ Defs: defs,
75
+ Flows: []catalog.Flow{},
76
+ Adrs: []catalog.Adr{},
77
+ }
78
+
79
+ encoded, err := json.MarshalIndent(fragment, "", " ")
80
+ if err != nil {
81
+ return plugin.Response{}, err
82
+ }
83
+
84
+ b.File(firstNonEmpty(opts.Out, "schemas.json"), string(encoded)+"\n")
85
+
86
+ return b.Response(), nil
87
+ }
88
+
89
+ // readSubject reads one vendored subject: its lock, its schema, and the two
90
+ // facts they add up to.
91
+ func readSubject(
92
+ b *plugin.Builder,
93
+ at, root, service, strategy string,
94
+ opts Options,
95
+ defs map[string]catalog.TypeDef,
96
+ channels *channelSet,
97
+ ) error {
98
+ dir := filepath.Dir(at)
99
+ where := filepath.ToSlash(at)
100
+
101
+ raw, err := os.ReadFile(at)
102
+ if err != nil {
103
+ return err
104
+ }
105
+
106
+ var lock Lock
107
+ if err := json.Unmarshal(raw, &lock); err != nil {
108
+ return fmt.Errorf("%s: %w", where, err)
109
+ }
110
+ if len(lock.Subjects) != 1 {
111
+ return fmt.Errorf("%s names %d subjects; expected exactly one", where, len(lock.Subjects))
112
+ }
113
+
114
+ entry := lock.Subjects[0]
115
+ if len(entry.Files) == 0 {
116
+ b.Warn(entry.Subject, where+" names no schema file")
117
+
118
+ return nil
119
+ }
120
+
121
+ schemaAt := filepath.Join(dir, filepath.FromSlash(entry.Files[0].Path))
122
+ schema, err := os.ReadFile(schemaAt)
123
+ if err != nil {
124
+ return fmt.Errorf("%s: %w", where, err)
125
+ }
126
+ source := filepath.ToSlash(schemaAt)
127
+
128
+ declared, shapes, err := shapesOf(entry, schema, b, source)
129
+ if err != nil {
130
+ return fmt.Errorf("%s: %w", source, err)
131
+ }
132
+ for name, def := range shapes {
133
+ // Two subjects vendoring one shared record is the ordinary case - it
134
+ // is what a registry reference is for - and the two copies are the
135
+ // same bytes, so the first one wins and the second is not a conflict.
136
+ if _, held := defs[name]; !held {
137
+ defs[name] = def
138
+ }
139
+ }
140
+
141
+ registered := resolve(entry.Subject, declared, strategy)
142
+ if registered.Topic == "" {
143
+ // RecordNameStrategy says nothing about topics on purpose, so this is
144
+ // not a gap to report. The shape is still the fact worth having.
145
+ return nil
146
+ }
147
+
148
+ if registered.Key {
149
+ // A key is part of every message on the topic rather than a message on
150
+ // it, so its shape is kept and its name is not put on the channel.
151
+ return nil
152
+ }
153
+
154
+ channels.add(registered.Topic, catalog.ChannelMessage{
155
+ Name: registered.Record,
156
+ Doc: fmt.Sprintf("Registered as %s version %d.", entry.Subject, entry.Version),
157
+ Direction: directionFor(entry.Subject, opts),
158
+ }, source)
159
+
160
+ return nil
161
+ }
162
+
163
+ // shapesOf reads the schema according to its declared type.
164
+ func shapesOf(entry LockSubject, schema []byte, b *plugin.Builder, source string) (string, map[string]catalog.TypeDef, error) {
165
+ switch strings.ToUpper(entry.SchemaType) {
166
+ case "", "AVRO":
167
+ return avro(schema)
168
+
169
+ case "JSON":
170
+ return jsonSchema(schema, entry.Subject)
171
+
172
+ case "PROTOBUF":
173
+ // Reading protobuf properly is extract-proto's whole job, and a
174
+ // second, worse parser here would be a second answer to one question.
175
+ b.Warn(entry.Subject, source+" is protobuf; its topic is read but its shape is not. Point extract-proto at the vendored directory for the fields.")
176
+
177
+ return "", nil, nil
178
+ }
179
+
180
+ return "", nil, fmt.Errorf("unknown schema type %q", entry.SchemaType)
181
+ }
182
+
183
+ // directionFor is which way a subject's channel travels. A registry records no
184
+ // producer and no consumer, so this is told rather than read.
185
+ func directionFor(subject string, opts Options) catalog.ChannelDirection {
186
+ declared, named := opts.Subjects[subject]
187
+ if !named {
188
+ declared = opts.Direction
189
+ }
190
+
191
+ if declared == string(catalog.ChannelReceive) {
192
+ return catalog.ChannelReceive
193
+ }
194
+
195
+ return catalog.ChannelSend
196
+ }
197
+
198
+ // channelSet collects topics as the subjects are read. Several subjects on one
199
+ // topic - a key and a value, or two record types multiplexed onto it - are one
200
+ // channel carrying several messages.
201
+ type channelSet struct {
202
+ byTopic map[string]*catalog.Channel
203
+ seen map[string]bool
204
+ order []string
205
+ }
206
+
207
+ func newChannels() *channelSet {
208
+ return &channelSet{byTopic: map[string]*catalog.Channel{}, seen: map[string]bool{}}
209
+ }
210
+
211
+ func (c *channelSet) add(topic string, message catalog.ChannelMessage, source string) {
212
+ held, known := c.byTopic[topic]
213
+ if !known {
214
+ held = &catalog.Channel{
215
+ Address: topic,
216
+ Messages: []catalog.ChannelMessage{},
217
+ Source: source,
218
+ }
219
+ c.byTopic[topic] = held
220
+ c.order = append(c.order, topic)
221
+ }
222
+
223
+ key := topic + " " + string(message.Direction) + " " + message.Name
224
+ if c.seen[key] {
225
+ return
226
+ }
227
+ c.seen[key] = true
228
+
229
+ held.Messages = append(held.Messages, message)
230
+ }
231
+
232
+ // all is the channels in topic order, and their messages in name order. Sorted
233
+ // rather than kept in the order the tree happened to be walked in, because the
234
+ // fragment is committed and an unstable order would rewrite it every run.
235
+ func (c *channelSet) all() []catalog.Channel {
236
+ sort.Strings(c.order)
237
+
238
+ out := make([]catalog.Channel, 0, len(c.order))
239
+ for _, topic := range c.order {
240
+ held := c.byTopic[topic]
241
+ sort.Slice(held.Messages, func(i, j int) bool {
242
+ if held.Messages[i].Name != held.Messages[j].Name {
243
+ return held.Messages[i].Name < held.Messages[j].Name
244
+ }
245
+
246
+ return held.Messages[i].Direction < held.Messages[j].Direction
247
+ })
248
+ out = append(out, *held)
249
+ }
250
+
251
+ return out
252
+ }
253
+
254
+ // drop clears a field's ref when nothing in this estate declares the shape it
255
+ // names, which is the ordinary case for a record referenced across an estate
256
+ // boundary. The catalog validates that every ref resolves, so leaving one
257
+ // dangling would fail the whole run over a fact that is true.
258
+ func drop(defs map[string]catalog.TypeDef, b *plugin.Builder, service string) {
259
+ var dangling []string
260
+
261
+ for name, def := range defs {
262
+ for i, field := range def.Fields {
263
+ if field.Ref == "" {
264
+ continue
265
+ }
266
+ if _, known := defs[field.Ref]; known {
267
+ continue
268
+ }
269
+ dangling = append(dangling, field.Ref)
270
+ def.Fields[i].Ref = ""
271
+ }
272
+ defs[name] = def
273
+ }
274
+
275
+ if len(dangling) == 0 {
276
+ return
277
+ }
278
+
279
+ sort.Strings(dangling)
280
+ b.Warn(service, "these shapes are referenced but not vendored, so they are named without a shape: "+
281
+ strings.Join(unique(dangling), ", "))
282
+ }
283
+
284
+ // find locates every lock under the root, or under the declared paths.
285
+ //
286
+ // Sorted, so the fragment comes out the same way on every filesystem: WalkDir
287
+ // is ordered, but a manifest that names two paths is not.
288
+ func find(root string, paths []string) ([]string, error) {
289
+ roots := []string{root}
290
+ if len(paths) > 0 {
291
+ roots = roots[:0]
292
+ for _, at := range paths {
293
+ roots = append(roots, filepath.Join(root, filepath.FromSlash(at)))
294
+ }
295
+ }
296
+
297
+ var found []string
298
+ for _, at := range roots {
299
+ err := filepath.WalkDir(at, func(p string, entry os.DirEntry, err error) error {
300
+ if err != nil {
301
+ // A named path that is not there is worth reporting; one
302
+ // inside a walk is not this plugin's to explain.
303
+ if p == at && os.IsNotExist(err) {
304
+ return fmt.Errorf("%s does not exist", filepath.ToSlash(at))
305
+ }
306
+
307
+ return err
308
+ }
309
+ if !entry.IsDir() && entry.Name() == LockName {
310
+ found = append(found, p)
311
+ }
312
+
313
+ return nil
314
+ })
315
+ if err != nil {
316
+ return nil, err
317
+ }
318
+ }
319
+
320
+ sort.Strings(found)
321
+
322
+ return found, nil
323
+ }
324
+
325
+ func unique(values []string) []string {
326
+ out := values[:0]
327
+ seen := map[string]bool{}
328
+
329
+ for _, value := range values {
330
+ if seen[value] {
331
+ continue
332
+ }
333
+ seen[value] = true
334
+ out = append(out, value)
335
+ }
336
+
337
+ return out
338
+ }
@@ -0,0 +1,374 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "strings"
6
+ "testing"
7
+
8
+ "github.com/shortlink-org/portolan/catalog"
9
+ "github.com/shortlink-org/portolan/plugin"
10
+ )
11
+
12
+ const estate = "testdata/estate"
13
+
14
+ func fragmentFrom(t *testing.T, opts Options) (catalog.Catalog, plugin.Response) {
15
+ t.Helper()
16
+
17
+ resp, err := extract(plugin.Input{Root: estate, Commit: "abc123", GeneratedAt: "2026-09-05T00:00:00Z"}, opts)
18
+ if err != nil {
19
+ t.Fatal(err)
20
+ }
21
+ if len(resp.Files) != 1 {
22
+ t.Fatalf("the extractor named %d files, want 1", len(resp.Files))
23
+ }
24
+
25
+ var fragment catalog.Catalog
26
+ if err := json.Unmarshal([]byte(resp.Files[0].Contents), &fragment); err != nil {
27
+ t.Fatal(err)
28
+ }
29
+
30
+ return fragment, resp
31
+ }
32
+
33
+ func defaults() Options {
34
+ return Options{Context: "shop", Service: "oms", Paths: []string{"vendor/schemas"}}
35
+ }
36
+
37
+ func channelsOf(t *testing.T, fragment catalog.Catalog) []catalog.Channel {
38
+ t.Helper()
39
+
40
+ if len(fragment.Contexts) != 1 || len(fragment.Contexts[0].Services) != 1 {
41
+ t.Fatalf("the fragment does not describe one service: %+v", fragment.Contexts)
42
+ }
43
+
44
+ return fragment.Contexts[0].Services[0].Channels
45
+ }
46
+
47
+ func fieldsOf(t *testing.T, fragment catalog.Catalog, name string) []catalog.Field {
48
+ t.Helper()
49
+
50
+ def, known := fragment.Defs[name]
51
+ if !known {
52
+ var held []string
53
+ for key := range fragment.Defs {
54
+ held = append(held, key)
55
+ }
56
+ t.Fatalf("no def named %s; the fragment holds %v", name, held)
57
+ }
58
+
59
+ return def.Fields
60
+ }
61
+
62
+ func fieldNamed(t *testing.T, fields []catalog.Field, name string) catalog.Field {
63
+ t.Helper()
64
+
65
+ for _, field := range fields {
66
+ if field.Name == name {
67
+ return field
68
+ }
69
+ }
70
+ t.Fatalf("no field named %s in %+v", name, fields)
71
+
72
+ return catalog.Field{}
73
+ }
74
+
75
+ func TestTopicsComeFromSubjects(t *testing.T) {
76
+ fragment, _ := fragmentFrom(t, defaults())
77
+
78
+ channels := channelsOf(t, fragment)
79
+ if len(channels) != 3 {
80
+ t.Fatalf("channels: %+v", channels)
81
+ }
82
+
83
+ // Sorted by address, so the fragment is the same on every run.
84
+ var addresses []string
85
+ for _, channel := range channels {
86
+ addresses = append(addresses, channel.Address)
87
+ }
88
+ if got := strings.Join(addresses, ","); got != "shop.oms.order,shop.oms.route,shop.oms.shipment" {
89
+ t.Errorf("addresses %q", got)
90
+ }
91
+
92
+ orders := channels[0]
93
+ // The key subject shares the topic with the value one and adds no message:
94
+ // a key is part of every message on the channel, not a message on it.
95
+ if len(orders.Messages) != 1 {
96
+ t.Fatalf("shop.oms.order carries %+v", orders.Messages)
97
+ }
98
+ if orders.Messages[0].Name != "shop.oms.OrderPlaced" {
99
+ t.Errorf("message name %q", orders.Messages[0].Name)
100
+ }
101
+ if orders.Messages[0].Direction != catalog.ChannelSend {
102
+ t.Errorf("direction %q, want send by default", orders.Messages[0].Direction)
103
+ }
104
+ if !strings.Contains(orders.Messages[0].Doc, "version 3") {
105
+ t.Errorf("the message does not say which registration it is: %q", orders.Messages[0].Doc)
106
+ }
107
+ if orders.Source != "testdata/estate/vendor/schemas/shop.oms.order-value/v3.avsc" {
108
+ t.Errorf("source %q", orders.Source)
109
+ }
110
+ }
111
+
112
+ func TestDirectionIsToldPerSubject(t *testing.T) {
113
+ opts := defaults()
114
+ opts.Direction = "send"
115
+ opts.Subjects = map[string]string{"shop.oms.shipment-value": "receive"}
116
+
117
+ fragment, _ := fragmentFrom(t, opts)
118
+
119
+ for _, channel := range channelsOf(t, fragment) {
120
+ want := catalog.ChannelSend
121
+ if channel.Address == "shop.oms.shipment" {
122
+ want = catalog.ChannelReceive
123
+ }
124
+ for _, message := range channel.Messages {
125
+ if message.Direction != want {
126
+ t.Errorf("%s carries %s %s, want %s", channel.Address, message.Name, message.Direction, want)
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ func TestAvroShapesLandInDefs(t *testing.T) {
133
+ fragment, _ := fragmentFrom(t, defaults())
134
+
135
+ fields := fieldsOf(t, fragment, "shop.oms.OrderPlaced")
136
+
137
+ for _, want := range []catalog.Field{
138
+ {Name: "order_id", Type: "string", Doc: "The order's own id."},
139
+ {Name: "note", Type: "string?", Doc: "What the customer wrote in the box."},
140
+ {Name: "labels", Type: "map<string, string>"},
141
+ {Name: "placed_at", Type: "timestamp-millis"},
142
+ {Name: "total", Type: "decimal(12,2)"},
143
+ } {
144
+ got := fieldNamed(t, fields, want.Name)
145
+ if got.Type != want.Type || got.Doc != want.Doc {
146
+ t.Errorf("%s is %+v, want type %q doc %q", want.Name, got, want.Type, want.Doc)
147
+ }
148
+ }
149
+
150
+ // A record vendored as its own subject is a shape this one points at, and
151
+ // the ref is what makes the two one shape rather than two.
152
+ buyer := fieldNamed(t, fields, "buyer")
153
+ if buyer.Type != "shop.oms.Party" || buyer.Ref != "shop.oms.Party" {
154
+ t.Errorf("buyer is %+v", buyer)
155
+ }
156
+ if got := fieldsOf(t, fragment, "shop.oms.Party"); len(got) != 2 {
157
+ t.Errorf("shop.oms.Party: %+v", got)
158
+ }
159
+
160
+ // A record inlined in a field gets its own def, under the namespace it
161
+ // inherits rather than none.
162
+ lines := fieldNamed(t, fields, "lines")
163
+ if lines.Type != "shop.oms.Line[]" || lines.Ref != "" {
164
+ t.Errorf("lines is %+v", lines)
165
+ }
166
+ if got := fieldsOf(t, fragment, "shop.oms.Line"); len(got) != 2 {
167
+ t.Errorf("shop.oms.Line: %+v", got)
168
+ }
169
+
170
+ // Field order is the order the schema lists them in - it is the producer's
171
+ // statement about the record, and re-sorting it would lose that.
172
+ var order []string
173
+ for _, field := range fields {
174
+ order = append(order, field.Name)
175
+ }
176
+ if got := strings.Join(order, ","); got != "order_id,buyer,note,lines,labels,placed_at,total" {
177
+ t.Errorf("field order %q", got)
178
+ }
179
+ }
180
+
181
+ func TestJSONSchemaShapesLandInDefs(t *testing.T) {
182
+ fragment, _ := fragmentFrom(t, defaults())
183
+
184
+ fields := fieldsOf(t, fragment, "shop.oms.ShipmentBooked")
185
+
186
+ // The order the document writes its properties in, which decoding into a
187
+ // map would have lost.
188
+ var order []string
189
+ for _, field := range fields {
190
+ order = append(order, field.Name)
191
+ }
192
+ if got := strings.Join(order, ","); got != "shipment_id,carrier,booked_at,weight_kg,stops,address" {
193
+ t.Errorf("property order %q", got)
194
+ }
195
+
196
+ for _, want := range []catalog.Field{
197
+ {Name: "shipment_id", Type: "string", Doc: "The booking's own id."},
198
+ {Name: "booked_at", Type: "date-time?"},
199
+ {Name: "weight_kg", Type: "number?"},
200
+ {Name: "stops", Type: "string[]?"},
201
+ } {
202
+ got := fieldNamed(t, fields, want.Name)
203
+ if got.Type != want.Type || got.Doc != want.Doc {
204
+ t.Errorf("%s is %+v, want type %q doc %q", want.Name, got, want.Type, want.Doc)
205
+ }
206
+ }
207
+
208
+ // A $ref resolves to the definition the document keeps to one side.
209
+ // The label stays the name the document uses; the ref carries the key the
210
+ // catalog files it under, which is qualified because `definitions` is
211
+ // local to a document and `defs` is not.
212
+ carrier := fieldNamed(t, fields, "carrier")
213
+ if carrier.Type != "Carrier" || carrier.Ref != "shop.oms.ShipmentBooked.Carrier" {
214
+ t.Errorf("carrier is %+v", carrier)
215
+ }
216
+ if got := fieldsOf(t, fragment, "shop.oms.ShipmentBooked.Carrier"); len(got) != 2 {
217
+ t.Errorf("Carrier: %+v", got)
218
+ }
219
+
220
+ // An object inlined with no title is still a shape; it is named for where
221
+ // it sits.
222
+ address := fieldNamed(t, fields, "address")
223
+ if address.Ref != "shop.oms.ShipmentBooked.address" {
224
+ t.Errorf("address is %+v", address)
225
+ }
226
+ inner := fieldsOf(t, fragment, "shop.oms.ShipmentBooked.address")
227
+ if fieldNamed(t, inner, "line1").Type != "string" {
228
+ t.Errorf("line1 is %+v", fieldNamed(t, inner, "line1"))
229
+ }
230
+ if fieldNamed(t, inner, "postcode").Type != "string?" {
231
+ t.Errorf("postcode is not optional: %+v", fieldNamed(t, inner, "postcode"))
232
+ }
233
+ }
234
+
235
+ func TestProtobufIsNamedButNotShaped(t *testing.T) {
236
+ fragment, resp := fragmentFrom(t, defaults())
237
+
238
+ // The topic is still a fact, and it is the one thing extract-proto cannot
239
+ // say: a .proto file does not know which topic it was registered against.
240
+ var found bool
241
+ for _, channel := range channelsOf(t, fragment) {
242
+ if channel.Address == "shop.oms.route" {
243
+ found = true
244
+ if len(channel.Messages) != 1 || channel.Messages[0].Name != "shop.oms.route-value" {
245
+ t.Errorf("shop.oms.route carries %+v", channel.Messages)
246
+ }
247
+ }
248
+ }
249
+ if !found {
250
+ t.Error("the protobuf subject named no topic")
251
+ }
252
+
253
+ var said bool
254
+ for _, warning := range resp.Warnings() {
255
+ if strings.Contains(warning.Message, "extract-proto") {
256
+ said = true
257
+ }
258
+ }
259
+ if !said {
260
+ t.Errorf("nothing pointed at extract-proto for the shape: %+v", resp.Warnings())
261
+ }
262
+ }
263
+
264
+ func TestRecordStrategyNamesNoTopic(t *testing.T) {
265
+ opts := defaults()
266
+ opts.Strategy = StrategyRecord
267
+
268
+ fragment, _ := fragmentFrom(t, opts)
269
+
270
+ // RecordNameStrategy says nothing about topics on purpose - a record under
271
+ // it is reusable across many - so a fragment with no channels is the right
272
+ // answer rather than a gap.
273
+ if channels := channelsOf(t, fragment); len(channels) != 0 {
274
+ t.Errorf("channels: %+v", channels)
275
+ }
276
+ // The shapes are still there, which is the half the strategy does state.
277
+ if got := fieldsOf(t, fragment, "shop.oms.OrderPlaced"); len(got) != 7 {
278
+ t.Errorf("shop.oms.OrderPlaced: %+v", got)
279
+ }
280
+ }
281
+
282
+ func TestTopicRecordStrategySplitsOnTheSchemasOwnName(t *testing.T) {
283
+ // "shop.oms.order-value" under this strategy is topic "shop.oms.order"
284
+ // and record "value" if you split at the hyphen, and neither half is
285
+ // reliable - so the schema's own full name is what the suffix is matched
286
+ // against.
287
+ for _, c := range []struct {
288
+ subject, declared, topic, record string
289
+ }{
290
+ {"orders-com.acme.Order", "com.acme.Order", "orders", "com.acme.Order"},
291
+ {"shop.oms.order-shop.oms.OrderPlaced", "shop.oms.OrderPlaced", "shop.oms.order", "shop.oms.OrderPlaced"},
292
+ {"orders-Order", "", "orders", "Order"},
293
+ } {
294
+ got := resolve(c.subject, c.declared, StrategyTopicRecord)
295
+ if got.Topic != c.topic || got.Record != c.record {
296
+ t.Errorf("%q -> %+v, want topic %q record %q", c.subject, got, c.topic, c.record)
297
+ }
298
+ }
299
+ }
300
+
301
+ func TestTopicStrategyReadsTheSuffix(t *testing.T) {
302
+ for _, c := range []struct {
303
+ subject, declared, topic, record string
304
+ key bool
305
+ }{
306
+ {"shop.oms.order-value", "shop.oms.OrderPlaced", "shop.oms.order", "shop.oms.OrderPlaced", false},
307
+ {"shop.oms.order-key", "shop.oms.OrderKey", "shop.oms.order", "shop.oms.OrderKey", true},
308
+ // Registered some other way: it names a shape but cannot name a topic.
309
+ {"loose", "shop.oms.Loose", "", "shop.oms.Loose", false},
310
+ } {
311
+ got := resolve(c.subject, c.declared, StrategyTopic)
312
+ if got.Topic != c.topic || got.Record != c.record || got.Key != c.key {
313
+ t.Errorf("%q -> %+v", c.subject, got)
314
+ }
315
+ }
316
+ }
317
+
318
+ func TestAMissingPathIsReported(t *testing.T) {
319
+ opts := defaults()
320
+ opts.Paths = []string{"vendor/nothing"}
321
+
322
+ _, err := extract(plugin.Input{Root: estate}, opts)
323
+ if err == nil || !strings.Contains(err.Error(), "does not exist") {
324
+ t.Errorf("a path that is not there was walked silently: %v", err)
325
+ }
326
+ }
327
+
328
+ func TestAReferenceNobodyVendoredKeepsItsNameAndLosesItsRef(t *testing.T) {
329
+ // The catalog validates that every field ref resolves, so a ref pointing
330
+ // at a shape this estate does not hold would fail the whole run over a
331
+ // fact that is true: the schema really does reference it, and the shape
332
+ // really does live somewhere else.
333
+ resp, err := extract(
334
+ plugin.Input{Root: "testdata/dangling"},
335
+ Options{Context: "shop", Service: "oms", Paths: []string{"vendor/schemas"}},
336
+ )
337
+ if err != nil {
338
+ t.Fatal(err)
339
+ }
340
+
341
+ var fragment catalog.Catalog
342
+ if err := json.Unmarshal([]byte(resp.Files[0].Contents), &fragment); err != nil {
343
+ t.Fatal(err)
344
+ }
345
+
346
+ actor := fieldNamed(t, fieldsOf(t, fragment, "shop.oms.AuditWritten"), "actor")
347
+ if actor.Type != "shop.identity.Actor" {
348
+ t.Errorf("the reference lost its name: %+v", actor)
349
+ }
350
+ if actor.Ref != "" {
351
+ t.Errorf("a ref was left pointing at a shape nothing declares: %+v", actor)
352
+ }
353
+
354
+ for _, def := range fragment.Defs {
355
+ for _, field := range def.Fields {
356
+ if field.Ref == "" {
357
+ continue
358
+ }
359
+ if _, known := fragment.Defs[field.Ref]; !known {
360
+ t.Errorf("%s refs %s, which the fragment does not declare", field.Name, field.Ref)
361
+ }
362
+ }
363
+ }
364
+
365
+ var said bool
366
+ for _, warning := range resp.Warnings() {
367
+ if strings.Contains(warning.Message, "shop.identity.Actor") {
368
+ said = true
369
+ }
370
+ }
371
+ if !said {
372
+ t.Errorf("the unvendored reference was dropped silently: %+v", resp.Warnings())
373
+ }
374
+ }