@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,222 @@
1
+ package main
2
+
3
+ // Turning a name as written into the thing it names.
4
+ //
5
+ // A proto reference is relative to where it was written: `Money` inside
6
+ // `shop.v1.Order` might mean `shop.v1.Order.Money`, `shop.v1.Money`, or
7
+ // `Money` at the root, and proto searches outwards in that order. A leading dot
8
+ // means the search is skipped and the name is already absolute.
9
+ //
10
+ // A name that resolves to nothing is NOT an error here. A narrowed vendored
11
+ // copy importing a file nobody vendored beside it is the normal case - see the
12
+ // header of parse.go - and the answer is to say the type is unresolved and
13
+ // carry on, not to refuse to describe the file.
14
+
15
+ import (
16
+ "sort"
17
+ "strings"
18
+ )
19
+
20
+ // scalars are proto's built-in types, which never resolve to a declaration.
21
+ var scalars = map[string]bool{
22
+ "double": true, "float": true, "int32": true, "int64": true,
23
+ "uint32": true, "uint64": true, "sint32": true, "sint64": true,
24
+ "fixed32": true, "fixed64": true, "sfixed32": true, "sfixed64": true,
25
+ "bool": true, "string": true, "bytes": true,
26
+ }
27
+
28
+ func isScalar(name string) bool { return scalars[name] }
29
+
30
+ // Index is every declaration in a set of files, by fully-qualified name.
31
+ type Index struct {
32
+ files []*File
33
+
34
+ messages map[string]*Message
35
+ enums map[string]*Enum
36
+ fileOf map[string]*File
37
+ fqnOf map[*Message]string
38
+ }
39
+
40
+ // NewIndex walks the files once and records every message and enum in them,
41
+ // nested ones included, under the name the rest of the world would use.
42
+ func NewIndex(files []*File) *Index {
43
+ ix := &Index{
44
+ files: files,
45
+ messages: map[string]*Message{},
46
+ enums: map[string]*Enum{},
47
+ fileOf: map[string]*File{},
48
+ fqnOf: map[*Message]string{},
49
+ }
50
+
51
+ for _, file := range files {
52
+ for _, m := range file.Messages {
53
+ ix.addMessage(file, file.Package, m)
54
+ }
55
+ for _, e := range file.Enums {
56
+ ix.addEnum(file, join(file.Package, e.Name), e)
57
+ }
58
+ }
59
+
60
+ return ix
61
+ }
62
+
63
+ func (ix *Index) addMessage(file *File, scope string, m *Message) {
64
+ fqn := join(scope, m.Name)
65
+ ix.messages[fqn] = m
66
+ ix.fileOf[fqn] = file
67
+ ix.fqnOf[m] = fqn
68
+
69
+ for _, nested := range m.Messages {
70
+ ix.addMessage(file, fqn, nested)
71
+ }
72
+ for _, e := range m.Enums {
73
+ ix.addEnum(file, join(fqn, e.Name), e)
74
+ }
75
+ }
76
+
77
+ func (ix *Index) addEnum(file *File, fqn string, e *Enum) {
78
+ ix.enums[fqn] = e
79
+ ix.fileOf[fqn] = file
80
+ }
81
+
82
+ // Resolve finds what `name`, written inside `scope`, refers to.
83
+ //
84
+ // scope is a fully-qualified name - a package, or a message inside one. The
85
+ // search walks outwards from it, which is what proto itself does.
86
+ func (ix *Index) Resolve(scope, name string) (string, bool) {
87
+ if name == "" || isScalar(name) {
88
+ return "", false
89
+ }
90
+
91
+ if strings.HasPrefix(name, ".") {
92
+ fqn := strings.TrimPrefix(name, ".")
93
+
94
+ return fqn, ix.declared(fqn)
95
+ }
96
+
97
+ parts := strings.Split(scope, ".")
98
+ for i := len(parts); i >= 0; i-- {
99
+ fqn := join(strings.Join(parts[:i], "."), name)
100
+ if ix.declared(fqn) {
101
+ return fqn, true
102
+ }
103
+ }
104
+
105
+ return "", false
106
+ }
107
+
108
+ func (ix *Index) declared(fqn string) bool {
109
+ _, message := ix.messages[fqn]
110
+ _, enum := ix.enums[fqn]
111
+
112
+ return message || enum
113
+ }
114
+
115
+ func (ix *Index) Message(fqn string) *Message { return ix.messages[fqn] }
116
+ func (ix *Index) Enum(fqn string) *Enum { return ix.enums[fqn] }
117
+ func (ix *Index) FileOf(fqn string) *File { return ix.fileOf[fqn] }
118
+
119
+ // FQN is the name a message is known by, once it has been indexed.
120
+ func (ix *Index) FQN(m *Message) string { return ix.fqnOf[m] }
121
+
122
+ // Names is every declared name, sorted. Determinism is not a nicety here: the
123
+ // fragment these end up in is committed and compared by `gen:check`, so a map
124
+ // iterated in Go's order would produce a different file every run.
125
+ func (ix *Index) Names() []string {
126
+ out := make([]string, 0, len(ix.messages)+len(ix.enums))
127
+ for fqn := range ix.messages {
128
+ out = append(out, fqn)
129
+ }
130
+ for fqn := range ix.enums {
131
+ out = append(out, fqn)
132
+ }
133
+ sort.Strings(out)
134
+
135
+ return out
136
+ }
137
+
138
+ // TypeRef is one field's type, resolved as far as it could be.
139
+ type TypeRef struct {
140
+ // Written is the type as the author typed it, rendered the way the catalog
141
+ // renders types: `[]LineItem`, `map[string]Money`, `Money`.
142
+ Written string
143
+
144
+ // FQN is what the named type resolves to, empty for a scalar or for a name
145
+ // nothing in the tree declares.
146
+ FQN string
147
+
148
+ // Unresolved is the name that could not be found, empty when there was
149
+ // nothing to find or the search succeeded. It is a diagnostic, not a
150
+ // failure: a narrowed vendored copy is expected to have some.
151
+ Unresolved string
152
+ }
153
+
154
+ // resolveType renders a field's type and resolves the name inside it.
155
+ //
156
+ // A map is rendered `map[K]V` and resolved on its value: the key of a proto map
157
+ // is always a scalar, and the value is the half that can name a message.
158
+ func (ix *Index) resolveType(scope string, f *Field) TypeRef {
159
+ if key, value, ok := mapParts(f.Type); ok {
160
+ inner := ix.resolveType(scope, &Field{Type: value})
161
+
162
+ return TypeRef{
163
+ Written: "map[" + key + "]" + inner.Written,
164
+ FQN: inner.FQN,
165
+ Unresolved: inner.Unresolved,
166
+ }
167
+ }
168
+
169
+ ref := TypeRef{Written: shortName(f.Type)}
170
+ if f.Label == "repeated" {
171
+ ref.Written = "[]" + ref.Written
172
+ }
173
+
174
+ if isScalar(f.Type) {
175
+ return ref
176
+ }
177
+
178
+ if fqn, ok := ix.Resolve(scope, f.Type); ok {
179
+ ref.FQN = fqn
180
+ } else {
181
+ ref.Unresolved = f.Type
182
+ }
183
+
184
+ return ref
185
+ }
186
+
187
+ // mapParts splits `map<string, Money>` into its two arguments.
188
+ func mapParts(typ string) (string, string, bool) {
189
+ if !strings.HasPrefix(typ, "map<") || !strings.HasSuffix(typ, ">") {
190
+ return "", "", false
191
+ }
192
+ inner := typ[len("map<") : len(typ)-1]
193
+ comma := strings.Index(inner, ",")
194
+ if comma < 0 {
195
+ return "", "", false
196
+ }
197
+
198
+ return strings.TrimSpace(inner[:comma]), strings.TrimSpace(inner[comma+1:]), true
199
+ }
200
+
201
+ // shortName is the last segment of a dotted name.
202
+ //
203
+ // The catalog spells a type the way a reader says it - `Money`, not
204
+ // `shop.v1.Money` - and its `defs` keys are deliberately bare for the same
205
+ // reason: two sources meaning different things by `Money` should collide
206
+ // visibly rather than coexist as two quietly different entries.
207
+ func shortName(name string) string {
208
+ name = strings.TrimPrefix(name, ".")
209
+ if i := strings.LastIndex(name, "."); i >= 0 {
210
+ return name[i+1:]
211
+ }
212
+
213
+ return name
214
+ }
215
+
216
+ func join(scope, name string) string {
217
+ if scope == "" {
218
+ return name
219
+ }
220
+
221
+ return scope + "." + name
222
+ }
@@ -0,0 +1,119 @@
1
+ package main
2
+
3
+ // Name resolution, on its own. A proto reference means whatever the innermost
4
+ // enclosing scope that declares it means, and a name nothing declares is a
5
+ // diagnostic rather than a failure.
6
+
7
+ import "testing"
8
+
9
+ func indexed(t *testing.T) *Index {
10
+ t.Helper()
11
+
12
+ orders, _ := parseFile(t, "orders.proto")
13
+ money, _ := parseFile(t, "money.proto")
14
+
15
+ return NewIndex([]*File{orders, money})
16
+ }
17
+
18
+ func TestResolvesAcrossFiles(t *testing.T) {
19
+ ix := indexed(t)
20
+
21
+ fqn, ok := ix.Resolve("shop.v1", "Money")
22
+ if !ok || fqn != "shop.v1.Money" {
23
+ t.Errorf("Money from shop.v1: %q %v", fqn, ok)
24
+ }
25
+ if ix.Message(fqn) == nil {
26
+ t.Error("the resolved name does not lead back to the message")
27
+ }
28
+ }
29
+
30
+ // Proto searches outwards from where the name was written, so the nested type
31
+ // wins over anything of the same name further out.
32
+ func TestResolvesInnermostFirst(t *testing.T) {
33
+ ix := indexed(t)
34
+
35
+ fqn, ok := ix.Resolve("shop.v1.Order", "Address")
36
+ if !ok || fqn != "shop.v1.Order.Address" {
37
+ t.Errorf("Address from inside Order: %q %v", fqn, ok)
38
+ }
39
+
40
+ // The same name written one scope out finds nothing, which is the point.
41
+ if _, ok := ix.Resolve("shop.v1", "Address"); ok {
42
+ t.Error("a nested type resolved from outside the message that nests it")
43
+ }
44
+ }
45
+
46
+ func TestLeadingDotIsAbsolute(t *testing.T) {
47
+ ix := indexed(t)
48
+
49
+ fqn, ok := ix.Resolve("shop.v1.Order", ".shop.v1.Money")
50
+ if !ok || fqn != "shop.v1.Money" {
51
+ t.Errorf("absolute name: %q %v", fqn, ok)
52
+ }
53
+ }
54
+
55
+ func TestNestedEnumIsIndexed(t *testing.T) {
56
+ ix := indexed(t)
57
+
58
+ fqn, ok := ix.Resolve("shop.v1.Order", "Channel")
59
+ if !ok || fqn != "shop.v1.Order.Channel" {
60
+ t.Errorf("nested enum: %q %v", fqn, ok)
61
+ }
62
+ if ix.Enum(fqn) == nil {
63
+ t.Error("the resolved name does not lead back to the enum")
64
+ }
65
+ }
66
+
67
+ // The whole reason this parser is tolerant: a narrowed vendored copy imports
68
+ // files nobody vendored beside it, and it must still be describable.
69
+ func TestUnresolvedIsReportedNotFatal(t *testing.T) {
70
+ ix := indexed(t)
71
+
72
+ ref := ix.resolveType("shop.v1", &Field{Type: "Nowhere"})
73
+ if ref.Unresolved != "Nowhere" {
74
+ t.Errorf("an unknown type was not reported: %+v", ref)
75
+ }
76
+ if ref.Written != "Nowhere" {
77
+ t.Errorf("an unknown type must still be shown as written: %q", ref.Written)
78
+ }
79
+ }
80
+
81
+ // Types are rendered the way the catalog renders types, and resolved on the
82
+ // half that can name a message.
83
+ func TestTypeRendering(t *testing.T) {
84
+ ix := indexed(t)
85
+
86
+ cases := []struct {
87
+ field Field
88
+ written, fqn string
89
+ }{
90
+ {Field{Type: "string"}, "string", ""},
91
+ {Field{Type: "Money"}, "Money", "shop.v1.Money"},
92
+ {Field{Type: "shop.v1.Money"}, "Money", "shop.v1.Money"},
93
+ {Field{Type: "LineItem", Label: "repeated"}, "[]LineItem", "shop.v1.LineItem"},
94
+ {Field{Type: "map<string, string>"}, "map[string]string", ""},
95
+ {Field{Type: "map<string, Money>"}, "map[string]Money", "shop.v1.Money"},
96
+ }
97
+
98
+ for _, c := range cases {
99
+ got := ix.resolveType("shop.v1", &c.field)
100
+ if got.Written != c.written || got.FQN != c.fqn {
101
+ t.Errorf("%s: %q -> %q (want %q -> %q)", c.field.Type, got.Written, got.FQN, c.written, c.fqn)
102
+ }
103
+ }
104
+ }
105
+
106
+ // The fragment is committed and compared by gen:check, so anything derived from
107
+ // a map has to come out in the same order every run.
108
+ func TestNamesAreSorted(t *testing.T) {
109
+ names := indexed(t).Names()
110
+
111
+ for i := 1; i < len(names); i++ {
112
+ if names[i-1] > names[i] {
113
+ t.Fatalf("names are not sorted: %q before %q", names[i-1], names[i])
114
+ }
115
+ }
116
+ if len(names) == 0 {
117
+ t.Fatal("nothing was indexed")
118
+ }
119
+ }
@@ -0,0 +1,36 @@
1
+ # Python Kafka extractor
2
+
3
+ `extract-python-kafka` reads Kafka client construction and calls from Python
4
+ syntax. It does not import or execute the project and it does not require a
5
+ Django, Flask, or other framework layout.
6
+
7
+ Supported clients:
8
+
9
+ - `confluent_kafka.Producer.produce` and `Consumer.subscribe`;
10
+ - `kafka.KafkaProducer.send` and constructor-based `KafkaConsumer` topics;
11
+ - `aiokafka.AIOKafkaProducer.send` / `send_and_wait` and constructor-based
12
+ `AIOKafkaConsumer` topics.
13
+
14
+ Literal topics, module constants, imported constants, environment defaults and
15
+ optional `settings.NAME` references are followed. Local producer factories are
16
+ followed to a supported constructor, so a singleton wrapper does not hide the
17
+ client. Topics that remain dynamic are reported and no channel is guessed.
18
+
19
+ The output uses generic `message` channels rather than domain events. It keeps
20
+ source-backed broker, security mode, idempotence, retry, compression, client
21
+ id, consumer group, key, headers and serializer facts. Authentication values
22
+ are never emitted. Partitions, replication and retention are broker-side facts
23
+ and remain explicitly unknown unless another catalog source declares them.
24
+
25
+ ```json
26
+ {
27
+ "plugin": "python-kafka",
28
+ "in": "checkout",
29
+ "out": ".portolan/fragments/checkout-kafka",
30
+ "options": {
31
+ "context": "shop",
32
+ "service": "checkout",
33
+ "settings": "config.settings"
34
+ }
35
+ }
36
+ ```
@@ -0,0 +1,211 @@
1
+ """Python Kafka client calls in, generic message channels and flows out."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from collections import defaultdict
8
+ from typing import Any, Dict, List
9
+
10
+ import catalog
11
+ import kafka
12
+ from names import slug, title
13
+ from options import Options
14
+ from protocol import Builder, File, Input
15
+ from source import Project
16
+
17
+ MESSAGE = "message"
18
+
19
+
20
+ def extract(input_: Input, opts: Options, b: Builder, cwd: str = "") -> None:
21
+ cwd = cwd or os.getcwd()
22
+ root = os.path.abspath(os.path.join(cwd, input_.root))
23
+
24
+ def rel(path: str) -> str:
25
+ return os.path.relpath(path, cwd).replace(os.sep, "/")
26
+
27
+ source = os.path.normpath(os.path.join(root, opts.source or "."))
28
+ context = opts.context or os.path.basename(root)
29
+ service = opts.service or os.path.basename(root)
30
+ svc_id = context + "." + service
31
+ project = Project(root, source, rel)
32
+ for path, message in project.broken:
33
+ b.warn(path, "cannot be parsed, so nothing in it is read: %s" % message)
34
+
35
+ found = kafka.scan(project, opts.settings)
36
+ if not found.publishes and not found.subscriptions:
37
+ b.warn(svc_id, "no supported Python Kafka producer or consumer calls under %s" % rel(source))
38
+ for where, expr in sorted(set(found.dynamic_topics)):
39
+ b.warn(where, "Kafka topic is dynamic%s, so no channel is invented" % ((": `%s`" % expr) if expr else ""))
40
+ clients = [item.client for item in found.publishes] + [item.client for item in found.subscriptions]
41
+ for source_line in sorted({client.source for client in clients if "brokers" not in client.config}):
42
+ b.warn(source_line or svc_id, "Kafka broker endpoints are resolved at runtime, so none are claimed in the catalog")
43
+
44
+ sends = defaultdict(list)
45
+ receives = defaultdict(list)
46
+ for item in found.publishes:
47
+ sends[item.topic].append(item)
48
+ for item in found.subscriptions:
49
+ receives[item.topic].append(item)
50
+ for topic in sorted(set(sends) - set(receives)):
51
+ b.warn(svc_id, "publishes Kafka topic `%s`, but no consumer for it is proven in this source tree" % topic)
52
+
53
+ channels = [channel(topic, sends[topic], receives[topic]) for topic in sorted(set(sends) | set(receives))]
54
+ flows = []
55
+ for topic in sorted(sends):
56
+ for index, item in enumerate(sends[topic], 1):
57
+ flows.append(publish_flow(context, service, svc_id, item, index))
58
+ for topic in sorted(receives):
59
+ received_message = sends[topic][0].message if len({item.message for item in sends[topic]}) == 1 else "message"
60
+ for index, item in enumerate(receives[topic], 1):
61
+ flows.append(receive_flow(context, service, svc_id, item, received_message, index))
62
+ flows.sort(key=lambda flow: flow["slug"])
63
+
64
+ fragment: Dict[str, Any] = {
65
+ "generatedAt": input_.generated_at,
66
+ "commit": input_.commit,
67
+ "contexts": [
68
+ {
69
+ "id": context,
70
+ "slug": context,
71
+ "name": "",
72
+ "summary": "",
73
+ "services": [
74
+ {
75
+ "id": svc_id,
76
+ "slug": service,
77
+ "name": "",
78
+ "repo": "",
79
+ "path": "",
80
+ "readme": "",
81
+ "provides": [],
82
+ "consumes": [],
83
+ "aggregates": [],
84
+ "channels": channels,
85
+ }
86
+ ],
87
+ }
88
+ ],
89
+ "defs": {},
90
+ "flows": flows,
91
+ "adrs": [],
92
+ }
93
+ b.files.append(File(name=opts.out or "kafka.json", contents=json.dumps(fragment, indent=2, ensure_ascii=False) + "\n"))
94
+
95
+
96
+ def channel(topic: str, sends: List[kafka.Publish], receives: List[kafka.Subscription]) -> Dict[str, Any]:
97
+ messages = []
98
+ seen = set()
99
+ for item in sends:
100
+ key = (item.message, "send")
101
+ if key not in seen:
102
+ messages.append(catalog.message(item.message, title(item.message), message_doc(item), "send"))
103
+ seen.add(key)
104
+ for item in receives:
105
+ # Kafka subscriptions dispatch records, and source often does not prove
106
+ # their schema. Match a single proven producer name; otherwise stay
107
+ # honest and call the record a message.
108
+ name = sends[0].message if len({sent.message for sent in sends}) == 1 else "message"
109
+ key = (name, "receive")
110
+ if key not in seen:
111
+ messages.append(catalog.message(name, title(name), consumer_doc(item), "receive"))
112
+ seen.add(key)
113
+ clients = []
114
+ for item in list(sends) + list(receives):
115
+ note = kafka.config_note(item.client)
116
+ if note not in clients:
117
+ clients.append(note)
118
+ doc = "Kafka message stream. " + ". ".join(clients) + ". Broker-side partitions, replication and retention are not declared by client code."
119
+ source = (sends[0].line if sends else receives[0].line) if sends or receives else ""
120
+ return catalog.channel(topic, MESSAGE, "Kafka · " + topic, doc, messages, source)
121
+
122
+
123
+ def message_doc(item: kafka.Publish) -> str:
124
+ bits = ["Published with %s" % item.client.library]
125
+ if item.message_expression:
126
+ bits.append("payload `%s`" % item.message_expression)
127
+ if item.key:
128
+ bits.append("key `%s`" % item.key)
129
+ if item.headers:
130
+ bits.append("headers `%s`" % item.headers)
131
+ return "; ".join(bits) + "."
132
+
133
+
134
+ def consumer_doc(item: kafka.Subscription) -> str:
135
+ group = item.client.config.get("consumer group")
136
+ suffix = "; consumer group `%s`" % group if group else ""
137
+ return "Subscribed with %s%s." % (item.client.library, suffix)
138
+
139
+
140
+ def handoff(topic: str, message: str, direction: str) -> Dict[str, str]:
141
+ return {
142
+ "kind": "message",
143
+ "transport": "kafka",
144
+ "channel": topic,
145
+ "message": message,
146
+ "direction": direction,
147
+ }
148
+
149
+
150
+ def publish_flow(context: str, service: str, svc_id: str, item: kafka.Publish, index: int) -> Dict[str, Any]:
151
+ flow_slug = slug("%s-kafka-publish-%s-%s-%d" % (service, item.topic, item.message, index))
152
+ broker = "kafka-" + slug(item.topic)
153
+ return catalog.flow(
154
+ "flow." + flow_slug,
155
+ flow_slug,
156
+ "Publish %s" % title(item.message),
157
+ "Publishes `%s` to Kafka topic `%s`." % (item.message, item.topic),
158
+ item.module.rel,
159
+ context,
160
+ [
161
+ catalog.participant(svc_id, "service", context),
162
+ catalog.participant(broker, "broker", None, "Kafka · " + item.topic),
163
+ ],
164
+ [
165
+ catalog.step(
166
+ "publish",
167
+ svc_id,
168
+ broker,
169
+ "call",
170
+ "publish " + item.message,
171
+ catalog.DECLARED,
172
+ note=kafka.config_note(item.client),
173
+ line=item.line,
174
+ handoff=handoff(item.topic, item.message, "send"),
175
+ )
176
+ ],
177
+ trigger={"kind": "unproven", "label": "source caller not proven", "confidence": "low"},
178
+ entrypoint=item.entrypoint,
179
+ )
180
+
181
+
182
+ def receive_flow(context: str, service: str, svc_id: str, item: kafka.Subscription, message: str, index: int) -> Dict[str, Any]:
183
+ flow_slug = slug("%s-kafka-consume-%s-%d" % (service, item.topic, index))
184
+ broker = "kafka-" + slug(item.topic)
185
+ return catalog.flow(
186
+ "flow." + flow_slug,
187
+ flow_slug,
188
+ "Consume from %s" % item.topic,
189
+ "Consumes Kafka messages from `%s`." % item.topic,
190
+ item.module.rel,
191
+ context,
192
+ [
193
+ catalog.participant(broker, "broker", None, "Kafka · " + item.topic),
194
+ catalog.participant(svc_id, "service", context),
195
+ ],
196
+ [
197
+ catalog.step(
198
+ "receive",
199
+ broker,
200
+ svc_id,
201
+ "call",
202
+ "receive message",
203
+ catalog.DECLARED,
204
+ note=consumer_doc(item),
205
+ line=item.line,
206
+ handoff=handoff(item.topic, message, "receive"),
207
+ )
208
+ ],
209
+ trigger={"kind": "message", "label": "Kafka · " + item.topic, "confidence": "high"},
210
+ entrypoint=item.entrypoint,
211
+ )
@@ -0,0 +1,68 @@
1
+ """Framework-neutral Kafka extraction contracts."""
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ import unittest
7
+
8
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
9
+ sys.path.insert(1, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "pyplugin"))
10
+
11
+ from extract import extract # noqa: E402
12
+ from options import Options # noqa: E402
13
+ from protocol import Builder, Input # noqa: E402
14
+
15
+ HERE = os.path.dirname(os.path.abspath(__file__))
16
+ ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
17
+
18
+
19
+ class PythonKafka(unittest.TestCase):
20
+ def setUp(self):
21
+ fixture = os.path.join(HERE, "testdata", "service")
22
+ builder = Builder()
23
+ extract(
24
+ Input(root=os.path.relpath(fixture, ROOT), commit="abc1234", generated_at="2026-09-08T00:00:00Z"),
25
+ Options.of({"context": "shop", "service": "orders", "settings": "config.settings"}),
26
+ builder,
27
+ cwd=ROOT,
28
+ )
29
+ self.warnings = builder.warnings
30
+ self.contents = builder.files[0].contents
31
+ self.fragment = json.loads(self.contents)
32
+ self.service = self.fragment["contexts"][0]["services"][0]
33
+
34
+ def test_three_standard_clients_form_message_channels(self):
35
+ channels = {item["address"]: item for item in self.service["channels"]}
36
+ self.assertEqual(sorted(channels), ["audit.records", "orders.created", "payments.accepted"])
37
+ self.assertTrue(all(item["kind"] == "message" for item in channels.values()))
38
+ self.assertEqual(
39
+ [message["direction"] for message in channels["orders.created"]["messages"]],
40
+ ["send", "receive"],
41
+ )
42
+ self.assertIn("consumer group `orders-indexer`", channels["orders.created"]["messages"][1]["doc"])
43
+
44
+ def test_client_configuration_is_kept_but_credentials_never_are(self):
45
+ orders = next(item for item in self.service["channels"] if item["address"] == "orders.created")
46
+ self.assertIn("brokers: kafka:9092", orders["doc"])
47
+ self.assertIn("idempotence: enabled", orders["doc"])
48
+ self.assertIn("security protocol: SASL_SSL", orders["doc"])
49
+ self.assertNotIn("must-not-leak", self.contents)
50
+ self.assertNotIn("sasl.password", self.contents)
51
+ self.assertNotIn("sasl.username", self.contents)
52
+
53
+ def test_publish_and_receive_flows_have_kafka_handoffs_and_source(self):
54
+ steps = [step for flow in self.fragment["flows"] for step in flow["steps"]]
55
+ handoffs = [step["handoff"] for step in steps]
56
+ self.assertTrue(any(item == {"kind": "message", "transport": "kafka", "channel": "payments.accepted", "message": "payment", "direction": "send"} for item in handoffs))
57
+ self.assertTrue(any(item == {"kind": "message", "transport": "kafka", "channel": "payments.accepted", "message": "payment", "direction": "receive"} for item in handoffs))
58
+ self.assertTrue(any(item["channel"] == "orders.created" and item["direction"] == "receive" for item in handoffs))
59
+ self.assertTrue(all(step["line"].endswith(tuple(str(n) for n in range(1, 100))) for step in steps))
60
+
61
+ def test_a_dynamic_topic_is_reported_and_not_invented(self):
62
+ self.assertEqual(len(self.warnings), 1)
63
+ self.assertIn("Kafka topic is dynamic", self.warnings[0].message)
64
+ self.assertIn("runtime_topic", self.warnings[0].message)
65
+
66
+
67
+ if __name__ == "__main__":
68
+ unittest.main()