@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,570 @@
1
+ """Kafka clients in Python source, read without importing the project.
2
+
3
+ The module deliberately knows client APIs, not framework or repository
4
+ conventions. It is shared by the standalone Kafka extractor and framework
5
+ extractors that want to add a proven publish to a larger source-backed flow.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ from dataclasses import dataclass, field
12
+ from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
13
+
14
+ from source import Module, Project, assigned, dotted, keyword, methods
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class Spec:
19
+ library: str
20
+ role: str
21
+
22
+
23
+ CONSTRUCTORS = {
24
+ "confluent_kafka.Producer": Spec("confluent-kafka", "producer"),
25
+ "confluent_kafka.Consumer": Spec("confluent-kafka", "consumer"),
26
+ "kafka.KafkaProducer": Spec("kafka-python", "producer"),
27
+ "kafka.KafkaConsumer": Spec("kafka-python", "consumer"),
28
+ "aiokafka.AIOKafkaProducer": Spec("aiokafka", "producer"),
29
+ "aiokafka.AIOKafkaConsumer": Spec("aiokafka", "consumer"),
30
+ }
31
+
32
+ PUBLISH_METHODS = {
33
+ "confluent-kafka": {"produce"},
34
+ "kafka-python": {"send"},
35
+ "aiokafka": {"send", "send_and_wait"},
36
+ }
37
+
38
+ # Only operational facts safe and useful in generated documentation. Auth
39
+ # values are intentionally absent: recognizing a constructor must never turn
40
+ # source credentials into generated output.
41
+ CONFIG_KEYS = {
42
+ "bootstrap.servers": "brokers",
43
+ "bootstrap_servers": "brokers",
44
+ "security.protocol": "security protocol",
45
+ "security_protocol": "security protocol",
46
+ "sasl.mechanism": "SASL mechanism",
47
+ "sasl_mechanism": "SASL mechanism",
48
+ "enable.idempotence": "idempotence",
49
+ "enable_idempotence": "idempotence",
50
+ "retries": "retries",
51
+ "compression.type": "compression",
52
+ "compression_type": "compression",
53
+ "compression.level": "compression level",
54
+ "compression_level": "compression level",
55
+ "client.id": "client id",
56
+ "client_id": "client id",
57
+ "group.id": "consumer group",
58
+ "group_id": "consumer group",
59
+ "auto.offset.reset": "offset reset",
60
+ "auto_offset_reset": "offset reset",
61
+ "enable.auto.commit": "auto commit",
62
+ "enable_auto_commit": "auto commit",
63
+ "key_serializer": "key serializer",
64
+ "value_serializer": "value serializer",
65
+ }
66
+
67
+
68
+ @dataclass
69
+ class Client:
70
+ library: str
71
+ role: str
72
+ config: Dict[str, Any] = field(default_factory=dict)
73
+ source: str = ""
74
+ constructor_topics: List[str] = field(default_factory=list)
75
+
76
+
77
+ @dataclass
78
+ class Publish:
79
+ module: Module
80
+ node: ast.Call
81
+ entrypoint: str
82
+ client: Client
83
+ topic: str
84
+ topic_expression: str
85
+ message: str
86
+ message_expression: str
87
+ key: str = ""
88
+ headers: str = ""
89
+
90
+ @property
91
+ def line(self) -> str:
92
+ return self.module.where(self.node)
93
+
94
+
95
+ @dataclass
96
+ class Subscription:
97
+ module: Module
98
+ node: ast.AST
99
+ entrypoint: str
100
+ client: Client
101
+ topic: str
102
+ topic_expression: str
103
+
104
+ @property
105
+ def line(self) -> str:
106
+ return self.module.where(self.node)
107
+
108
+
109
+ @dataclass
110
+ class Result:
111
+ publishes: List[Publish] = field(default_factory=list)
112
+ subscriptions: List[Subscription] = field(default_factory=list)
113
+ dynamic_topics: List[Tuple[str, str]] = field(default_factory=list)
114
+
115
+
116
+ def external_name(module: Module, name: str) -> str:
117
+ """Resolve the imported first segment of a dotted expression."""
118
+ if not name:
119
+ return ""
120
+ parts = name.split(".")
121
+ imported = module.imports.get(parts[0])
122
+ if imported is None:
123
+ return name
124
+ base = imported.module if imported.name == "*" else imported.module + "." + imported.name
125
+ return ".".join([base] + parts[1:])
126
+
127
+
128
+ def expression(node: Optional[ast.AST]) -> str:
129
+ if node is None:
130
+ return ""
131
+ try:
132
+ return " ".join(ast.unparse(node).split()) # type: ignore[attr-defined]
133
+ except Exception:
134
+ return dotted(node)
135
+
136
+
137
+ def _module_assignment(module: Module, name: str) -> Optional[ast.AST]:
138
+ return next((value for candidate, value, _ in assigned(module.tree) if candidate == name), None)
139
+
140
+
141
+ def value(
142
+ project: Project,
143
+ module: Module,
144
+ node: Optional[ast.AST],
145
+ settings: str = "",
146
+ variables: Optional[Dict[str, Tuple[str, object]]] = None,
147
+ depth: int = 0,
148
+ ) -> Any:
149
+ """Resolve safe scalar/list/dict values as far as syntax proves them."""
150
+ if node is None or depth > 8:
151
+ return None
152
+ variables = variables or {}
153
+ if isinstance(node, ast.Constant) and isinstance(node.value, (str, int, float, bool)):
154
+ return node.value
155
+ if isinstance(node, ast.Name):
156
+ bound = variables.get(node.id)
157
+ if bound is not None and bound[0] == "value":
158
+ return bound[1]
159
+ assigned_node = _module_assignment(module, node.id)
160
+ if assigned_node is not None and assigned_node is not node:
161
+ return value(project, module, assigned_node, settings, variables, depth + 1)
162
+ hit = project.resolve(module, node.id)
163
+ if hit is not None and hit[0] is not module:
164
+ return value(project, hit[0], ast.Name(id=hit[1]), settings, variables, depth + 1)
165
+ return None
166
+ if isinstance(node, ast.Attribute):
167
+ name = dotted(node)
168
+ bound = variables.get(name)
169
+ if bound is not None and bound[0] == "value":
170
+ return bound[1]
171
+ if name.startswith("settings.") and name.count(".") == 1:
172
+ settings_module = project.module(settings) if settings else None
173
+ if settings_module is not None:
174
+ return value(project, settings_module, ast.Name(id=name.split(".")[1]), settings, {}, depth + 1)
175
+ return None
176
+ if isinstance(node, (ast.List, ast.Tuple, ast.Set)):
177
+ items = [value(project, module, item, settings, variables, depth + 1) for item in node.elts]
178
+ return items if all(item is not None for item in items) else None
179
+ if isinstance(node, ast.Dict):
180
+ out: Dict[str, Any] = {}
181
+ for key_node, val_node in zip(node.keys, node.values):
182
+ key = value(project, module, key_node, settings, variables, depth + 1)
183
+ val = value(project, module, val_node, settings, variables, depth + 1)
184
+ if not isinstance(key, str) or val is None:
185
+ continue
186
+ out[key] = val
187
+ return out
188
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
189
+ left = value(project, module, node.left, settings, variables, depth + 1)
190
+ right = value(project, module, node.right, settings, variables, depth + 1)
191
+ if isinstance(left, str) and isinstance(right, str):
192
+ return left + right
193
+ return None
194
+ if isinstance(node, ast.JoinedStr):
195
+ chunks: List[str] = []
196
+ for item in node.values:
197
+ if isinstance(item, ast.Constant) and isinstance(item.value, str):
198
+ chunks.append(item.value)
199
+ elif isinstance(item, ast.FormattedValue):
200
+ inner = value(project, module, item.value, settings, variables, depth + 1)
201
+ if inner is None:
202
+ return None
203
+ chunks.append(str(inner))
204
+ return "".join(chunks)
205
+ if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
206
+ inner = value(project, module, node.operand, settings, variables, depth + 1)
207
+ return -inner if isinstance(inner, (int, float)) else None
208
+ if isinstance(node, ast.IfExp):
209
+ condition = value(project, module, node.test, settings, variables, depth + 1)
210
+ if isinstance(condition, bool):
211
+ branch = node.body if condition else node.orelse
212
+ return value(project, module, branch, settings, variables, depth + 1)
213
+ left = value(project, module, node.body, settings, variables, depth + 1)
214
+ right = value(project, module, node.orelse, settings, variables, depth + 1)
215
+ return left if left == right else None
216
+ if isinstance(node, ast.Call):
217
+ name = external_name(module, dotted(node.func))
218
+ last = name.split(".")[-1]
219
+ if last in ("get", "getenv") and (name.startswith("os.environ") or name == "os.getenv"):
220
+ default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
221
+ return value(project, module, default, settings, variables, depth + 1)
222
+ if last in ("env", "str", "bool", "int") and dotted(node.func).split(".")[0] in ("env", "environ", "config"):
223
+ default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
224
+ return value(project, module, default, settings, variables, depth + 1)
225
+ if name in ("str", "int", "float", "bool") and node.args:
226
+ inner = value(project, module, node.args[0], settings, variables, depth + 1)
227
+ if inner is None:
228
+ return None
229
+ try:
230
+ return {"str": str, "int": int, "float": float, "bool": bool}[name](inner)
231
+ except (TypeError, ValueError):
232
+ return None
233
+ return None
234
+
235
+
236
+ def _config_values(project: Project, module: Module, call: ast.Call, settings: str, variables) -> Dict[str, Any]:
237
+ raw: Dict[str, Any] = {}
238
+ if call.args:
239
+ first = value(project, module, call.args[0], settings, variables)
240
+ if isinstance(first, dict):
241
+ raw.update(first)
242
+ for kw in call.keywords:
243
+ if not kw.arg:
244
+ continue
245
+ resolved = value(project, module, kw.value, settings, variables)
246
+ if resolved is not None:
247
+ raw[kw.arg] = resolved
248
+ elif kw.arg in ("key_serializer", "value_serializer"):
249
+ raw[kw.arg] = expression(kw.value)
250
+ out: Dict[str, Any] = {}
251
+ for key, val in raw.items():
252
+ normalized = CONFIG_KEYS.get(key)
253
+ if normalized:
254
+ out[normalized] = val
255
+ return out
256
+
257
+
258
+ def constructed(
259
+ project: Project,
260
+ module: Module,
261
+ node: ast.AST,
262
+ settings: str = "",
263
+ variables: Optional[Dict[str, Tuple[str, object]]] = None,
264
+ depth: int = 0,
265
+ ) -> Optional[Client]:
266
+ """A supported Kafka constructor, including a local zero-cost factory."""
267
+ if not isinstance(node, ast.Call) or depth > 5:
268
+ return None
269
+ spec = CONSTRUCTORS.get(external_name(module, dotted(node.func)))
270
+ if spec is not None:
271
+ topics: List[str] = []
272
+ if spec.role == "consumer" and spec.library in ("kafka-python", "aiokafka"):
273
+ for arg in node.args:
274
+ resolved = value(project, module, arg, settings, variables)
275
+ if isinstance(resolved, str):
276
+ topics.append(resolved)
277
+ elif isinstance(resolved, list):
278
+ topics.extend(str(item) for item in resolved if isinstance(item, str))
279
+ return Client(
280
+ spec.library,
281
+ spec.role,
282
+ _config_values(project, module, node, settings, variables or {}),
283
+ module.where(node),
284
+ topics,
285
+ )
286
+
287
+ # A project factory is still proof of the library when one supported
288
+ # constructor is reachable in its return path. This catches singleton
289
+ # factories without attaching meaning to their class or method names.
290
+ target = _callable(project, module, node.func)
291
+ if target is None:
292
+ return None
293
+ target_module, fn = target
294
+ local: Dict[str, Tuple[str, object]] = {}
295
+ parameters = list(getattr(fn.args, "posonlyargs", [])) + list(fn.args.args)
296
+ if parameters and parameters[0].arg in ("self", "cls"):
297
+ parameters = parameters[1:]
298
+ for parameter, arg in zip(parameters, node.args):
299
+ resolved = value(project, module, arg, settings, variables)
300
+ if resolved is not None:
301
+ local[parameter.arg] = ("value", resolved)
302
+ for child in ast.walk(fn):
303
+ if isinstance(child, ast.Assign):
304
+ resolved = value(project, target_module, child.value, settings, local)
305
+ if resolved is not None:
306
+ for assignment_target in child.targets:
307
+ assignment_name = dotted(assignment_target)
308
+ if assignment_name:
309
+ local[assignment_name] = ("value", resolved)
310
+ elif isinstance(child, ast.AnnAssign) and child.value is not None:
311
+ resolved = value(project, target_module, child.value, settings, local)
312
+ assignment_name = dotted(child.target)
313
+ if resolved is not None and assignment_name:
314
+ local[assignment_name] = ("value", resolved)
315
+ if isinstance(child, ast.Call):
316
+ found = constructed(project, target_module, child, settings, local, depth + 1)
317
+ if found is not None:
318
+ return found
319
+ return None
320
+
321
+
322
+ def _callable(project: Project, module: Module, func: ast.AST) -> Optional[Tuple[Module, ast.AST]]:
323
+ name = dotted(func)
324
+ parts = name.split(".") if name else []
325
+ if not parts:
326
+ return None
327
+ if len(parts) == 1:
328
+ hit = project.resolve(module, name)
329
+ if hit is None:
330
+ return None
331
+ fn = next((item for item in hit[0].functions() if item.name == hit[1]), None)
332
+ return (hit[0], fn) if fn is not None else None
333
+ # Local/imported class method.
334
+ class_name, method_name = parts[-2], parts[-1]
335
+ class_module = module
336
+ resolved_class = class_name
337
+ imported = module.imports.get(parts[0])
338
+ if imported is not None:
339
+ if len(parts) == 2 and imported.name != "*":
340
+ class_module = project.module(imported.module) or module
341
+ resolved_class = imported.name
342
+ else:
343
+ base = imported.module if imported.name == "*" else imported.module + "." + imported.name
344
+ class_module = project.module(".".join([base] + parts[1:-2])) or module
345
+ cls = next((item for item in class_module.classes() if item.name == resolved_class), None)
346
+ if cls is not None:
347
+ fn = next((item for item in methods(cls) if item.name == method_name), None)
348
+ return (class_module, fn) if fn is not None else None
349
+ # Imported module function: pkg.helper(...).
350
+ if imported is not None:
351
+ base = imported.module if imported.name == "*" else imported.module + "." + imported.name
352
+ target_module = project.module(".".join([base] + parts[1:-1]))
353
+ if target_module is not None:
354
+ fn = next((item for item in target_module.functions() if item.name == method_name), None)
355
+ return (target_module, fn) if fn is not None else None
356
+ return None
357
+
358
+
359
+ def topic_node(call: ast.Call) -> Optional[ast.AST]:
360
+ return keyword(call, "topic") or (call.args[0] if call.args else None)
361
+
362
+
363
+ def payload_node(call: ast.Call) -> Optional[ast.AST]:
364
+ return keyword(call, "value") or (call.args[1] if len(call.args) > 1 else None)
365
+
366
+
367
+ def payload_name(node: Optional[ast.AST], variables: Optional[Dict[str, Tuple[str, object]]] = None, depth: int = 0) -> str:
368
+ if node is None or depth > 6:
369
+ return "message"
370
+ variables = variables or {}
371
+ if isinstance(node, ast.Name):
372
+ bound = variables.get(node.id)
373
+ if bound is not None and bound[0] == "expr" and isinstance(bound[1], ast.AST):
374
+ return payload_name(bound[1], variables, depth + 1)
375
+ if bound is not None and bound[0] == "event":
376
+ return str(bound[1]).rsplit(".", 1)[-1]
377
+ return node.id
378
+ if isinstance(node, ast.Call):
379
+ last = dotted(node.func).split(".")[-1]
380
+ if last in ("dumps", "dump", "encode", "serialize", "SerializeToString", "asdict", "dict", "model_dump") and node.args:
381
+ return payload_name(node.args[0], variables, depth + 1)
382
+ return last or "message"
383
+ if isinstance(node, ast.Attribute):
384
+ return node.attr
385
+ if isinstance(node, ast.Dict):
386
+ return "message"
387
+ return "message"
388
+
389
+
390
+ def _text(node: Optional[ast.AST]) -> str:
391
+ text = expression(node)
392
+ return text if len(text) <= 100 else text[:97] + "..."
393
+
394
+
395
+ class Scanner:
396
+ def __init__(self, project: Project, settings: str = ""):
397
+ self.project = project
398
+ self.settings = settings
399
+ self.result = Result()
400
+ self._seen_publish: Set[Tuple[str, int, str]] = set()
401
+ self._seen_subscription: Set[Tuple[str, int, str]] = set()
402
+
403
+ def scan(self) -> Result:
404
+ for module in sorted(self.project.modules.values(), key=lambda item: item.rel):
405
+ module_env = self._initial(module, module.tree)
406
+ self._walk(module, module.tree.body, module_env, "python:%s" % module.dotted)
407
+ for fn in module.functions():
408
+ self._walk(module, fn.body, dict(module_env), "python:%s:%s" % (module.dotted, fn.name))
409
+ for cls in module.classes():
410
+ class_env = dict(module_env)
411
+ class_env.update(self._initial(module, cls))
412
+ for fn in methods(cls):
413
+ self._walk(module, fn.body, dict(class_env), "python:%s:%s.%s" % (module.dotted, cls.name, fn.name))
414
+ self.result.publishes.sort(key=lambda item: (item.module.rel, item.node.lineno, item.topic))
415
+ self.result.subscriptions.sort(key=lambda item: (item.module.rel, getattr(item.node, "lineno", 0), item.topic))
416
+ return self.result
417
+
418
+ def _initial(self, module: Module, owner: ast.AST) -> Dict[str, Tuple[str, object]]:
419
+ env: Dict[str, Tuple[str, object]] = {}
420
+ for name, node, _ in assigned(owner):
421
+ client = constructed(self.project, module, node, self.settings, env)
422
+ if client is not None:
423
+ env[name] = ("kafka", client)
424
+ if isinstance(owner, ast.ClassDef):
425
+ env["self." + name] = ("kafka", client)
426
+ env["cls." + name] = ("kafka", client)
427
+ continue
428
+ resolved = value(self.project, module, node, self.settings, env)
429
+ env[name] = ("value", resolved) if resolved is not None else ("expr", node)
430
+ return env
431
+
432
+ def _bind(self, env: Dict[str, Tuple[str, object]], target: ast.AST, binding: Optional[Tuple[str, object]]) -> None:
433
+ if binding is None:
434
+ return
435
+ name = dotted(target)
436
+ if name:
437
+ env[name] = binding
438
+
439
+ def _binding(self, module: Module, node: ast.AST, env) -> Optional[Tuple[str, object]]:
440
+ if isinstance(node, ast.Await):
441
+ return self._binding(module, node.value, env)
442
+ if isinstance(node, (ast.Name, ast.Attribute)):
443
+ name = dotted(node)
444
+ return env.get(name) or env.get(name.split(".")[0])
445
+ client = constructed(self.project, module, node, self.settings, env)
446
+ if client is not None:
447
+ return ("kafka", client)
448
+ resolved = value(self.project, module, node, self.settings, env)
449
+ return ("value", resolved) if resolved is not None else ("expr", node)
450
+
451
+ def _walk(self, module: Module, body: Iterable[ast.AST], env, entrypoint: str) -> None:
452
+ for stmt in body:
453
+ if isinstance(stmt, ast.Assign):
454
+ binding = self._binding(module, stmt.value, env)
455
+ for target in stmt.targets:
456
+ self._bind(env, target, binding)
457
+ self._call(module, stmt.value, env, entrypoint)
458
+ elif isinstance(stmt, ast.AnnAssign) and stmt.value is not None:
459
+ self._bind(env, stmt.target, self._binding(module, stmt.value, env))
460
+ self._call(module, stmt.value, env, entrypoint)
461
+ elif isinstance(stmt, ast.Expr):
462
+ self._call(module, stmt.value, env, entrypoint)
463
+ elif isinstance(stmt, ast.Return) and stmt.value is not None:
464
+ self._call(module, stmt.value, env, entrypoint)
465
+ elif isinstance(stmt, (ast.If, ast.While, ast.For, ast.AsyncFor)):
466
+ if isinstance(stmt, ast.If):
467
+ self._call(module, stmt.test, env, entrypoint)
468
+ elif isinstance(stmt, ast.While):
469
+ self._call(module, stmt.test, env, entrypoint)
470
+ else:
471
+ self._call(module, stmt.iter, env, entrypoint)
472
+ self._walk(module, stmt.body, dict(env), entrypoint)
473
+ self._walk(module, stmt.orelse, dict(env), entrypoint)
474
+ elif isinstance(stmt, (ast.With, ast.AsyncWith)):
475
+ for item in stmt.items:
476
+ self._call(module, item.context_expr, env, entrypoint)
477
+ self._walk(module, stmt.body, dict(env), entrypoint)
478
+ elif isinstance(stmt, ast.Try):
479
+ self._walk(module, stmt.body, dict(env), entrypoint)
480
+ self._walk(module, stmt.orelse, dict(env), entrypoint)
481
+ self._walk(module, stmt.finalbody, dict(env), entrypoint)
482
+ for handler in stmt.handlers:
483
+ self._walk(module, handler.body, dict(env), entrypoint)
484
+
485
+ def _call(self, module: Module, node: ast.AST, env, entrypoint: str) -> None:
486
+ if isinstance(node, ast.Await):
487
+ self._call(module, node.value, env, entrypoint)
488
+ return
489
+ if not isinstance(node, ast.Call):
490
+ return
491
+ holder = self._binding(module, node.func.value, env) if isinstance(node.func, ast.Attribute) else None
492
+ if holder is not None and holder[0] == "kafka":
493
+ client: Client = holder[1] # type: ignore[assignment]
494
+ method = node.func.attr
495
+ if client.role == "producer" and method in PUBLISH_METHODS.get(client.library, set()):
496
+ self._publish(module, node, env, entrypoint, client)
497
+ elif client.role == "consumer" and method == "subscribe":
498
+ topics_node = node.args[0] if node.args else keyword(node, "topics")
499
+ topics = value(self.project, module, topics_node, self.settings, env)
500
+ if isinstance(topics, str):
501
+ topics = [topics]
502
+ if isinstance(topics, list):
503
+ for topic in topics:
504
+ if isinstance(topic, str):
505
+ self._subscription(module, node, entrypoint, client, topic, _text(topics_node))
506
+ else:
507
+ self.result.dynamic_topics.append((module.where(node), _text(topics_node)))
508
+ client = constructed(self.project, module, node, self.settings, env)
509
+ if client is not None and client.role == "consumer":
510
+ for topic in client.constructor_topics:
511
+ self._subscription(module, node, entrypoint, client, topic, topic)
512
+ # Calls nested in chains and arguments can themselves construct or use
513
+ # a client; visit them without interpreting arbitrary project code.
514
+ if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Call):
515
+ self._call(module, node.func.value, env, entrypoint)
516
+ for arg in node.args:
517
+ self._call(module, arg, env, entrypoint)
518
+ for kw in node.keywords:
519
+ self._call(module, kw.value, env, entrypoint)
520
+
521
+ def _publish(self, module: Module, call: ast.Call, env, entrypoint: str, client: Client) -> None:
522
+ topic_ast = topic_node(call)
523
+ resolved = value(self.project, module, topic_ast, self.settings, env)
524
+ if not isinstance(resolved, str) or not resolved:
525
+ self.result.dynamic_topics.append((module.where(call), _text(topic_ast)))
526
+ return
527
+ key = (module.rel, call.lineno, resolved)
528
+ if key in self._seen_publish:
529
+ return
530
+ self._seen_publish.add(key)
531
+ payload = payload_node(call)
532
+ self.result.publishes.append(
533
+ Publish(
534
+ module,
535
+ call,
536
+ entrypoint,
537
+ client,
538
+ resolved,
539
+ _text(topic_ast),
540
+ payload_name(payload, env),
541
+ _text(payload),
542
+ _text(keyword(call, "key")),
543
+ _text(keyword(call, "headers")),
544
+ )
545
+ )
546
+
547
+ def _subscription(self, module: Module, node: ast.AST, entrypoint: str, client: Client, topic: str, topic_expr: str) -> None:
548
+ key = (module.rel, getattr(node, "lineno", 0), topic)
549
+ if key in self._seen_subscription:
550
+ return
551
+ self._seen_subscription.add(key)
552
+ self.result.subscriptions.append(Subscription(module, node, entrypoint, client, topic, topic_expr))
553
+
554
+
555
+ def scan(project: Project, settings: str = "") -> Result:
556
+ return Scanner(project, settings).scan()
557
+
558
+
559
+ def config_note(client: Client) -> str:
560
+ parts = ["client: %s" % client.library]
561
+ for key in sorted(client.config):
562
+ val = client.config[key]
563
+ if isinstance(val, list):
564
+ text = ", ".join(str(item) for item in val)
565
+ elif isinstance(val, bool):
566
+ text = "enabled" if val else "disabled"
567
+ else:
568
+ text = str(val)
569
+ parts.append("%s: %s" % (key, text))
570
+ return "; ".join(parts)
@@ -0,0 +1,38 @@
1
+ """How a name in the source becomes an id in the catalog.
2
+
3
+ These are the rules `extract-go` lives by and `extract-ts` repeats, spelled
4
+ once for every Python plugin, so a Django service and a Go service with the
5
+ same aggregate get the same id.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+
12
+
13
+ def slug(name: str) -> str:
14
+ """PriceList -> price-list, Address -> address, ID -> id, email.Address -> email-address."""
15
+ out = []
16
+ chars = list(name)
17
+ for i, c in enumerate(chars):
18
+ upper = "A" <= c <= "Z"
19
+ if upper and i > 0:
20
+ prev = chars[i - 1]
21
+ nxt = chars[i + 1] if i + 1 < len(chars) else ""
22
+ if ("a" <= prev <= "z") or ("a" <= nxt <= "z"):
23
+ out.append("-")
24
+ r = c.lower() if upper else c
25
+ if r in ("_", "."):
26
+ r = "-"
27
+ out.append(r)
28
+ return re.sub("-+", "-", "".join(out)).strip("-")
29
+
30
+
31
+ def camel(name: str) -> str:
32
+ """issue_invoice -> IssueInvoice: the operation id a function name becomes."""
33
+ return "".join(w[0].upper() + w[1:] for w in re.split(r"[_\-]+", name) if w)
34
+
35
+
36
+ def title(name: str) -> str:
37
+ """price_list -> Price List: the human name for a directory."""
38
+ return " ".join(w[0].upper() + w[1:] for w in re.split(r"[_\-]+", name) if w)
@@ -0,0 +1,79 @@
1
+ """The plugin protocol, as `plugin/protocol.go` spells it: one JSON request on
2
+ stdin, one JSON response on stdout, and a `describe` that answers with what the
3
+ plugin is and what it can be told. Shared by every Python plugin under
4
+ `plugins/`, the way `plugin/` is shared by the Go ones; each plugin keeps its
5
+ own options dataclass and hands it to `read_options`.
6
+
7
+ Nothing here reads the environment or the clock. Every fact about the estate
8
+ arrives in the request, which is what lets the same tree produce the same
9
+ fragment on any machine.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+ from typing import Any, Dict, List
16
+
17
+
18
+ @dataclass
19
+ class Input:
20
+ """Where the source is, and the stamp the host put on this run."""
21
+
22
+ root: str = ""
23
+ output: str = ""
24
+ commit: str = ""
25
+ generated_at: str = ""
26
+
27
+ @staticmethod
28
+ def of(raw: Any) -> "Input":
29
+ raw = raw or {}
30
+ return Input(
31
+ root=raw.get("root", ""),
32
+ output=raw.get("output", ""),
33
+ commit=raw.get("commit", ""),
34
+ generated_at=raw.get("generatedAt", ""),
35
+ )
36
+
37
+
38
+ def read_options(opts: Any, keys: Dict[str, str], raw: Any) -> Any:
39
+ """Fills an options dataclass from what the manifest sent: `keys` is the
40
+ option as the manifest spells it against the field that holds it. A key
41
+ that is not there is refused rather than dropped, the way
42
+ `deny_unknown_fields` refuses one in the Rust extractor: an option nobody
43
+ reads is a page that comes out blank with nothing saying why."""
44
+ raw = raw or {}
45
+ for key, value in raw.items():
46
+ attr = keys.get(key)
47
+ if attr is None:
48
+ raise ValueError("unknown option %r" % key)
49
+ setattr(opts, attr, value)
50
+ return opts
51
+
52
+
53
+ @dataclass
54
+ class File:
55
+ name: str
56
+ contents: str
57
+
58
+
59
+ @dataclass
60
+ class Warning:
61
+ severity: str
62
+ message: str
63
+ ref: str
64
+
65
+
66
+ class Builder:
67
+ """Collects output files and internal extraction warnings."""
68
+
69
+ def __init__(self) -> None:
70
+ self.files: List[File] = []
71
+ self.warnings: List[Warning] = []
72
+
73
+ def warn(self, ref: str, message: str) -> None:
74
+ self.warnings.append(Warning("warning", message, ref))
75
+
76
+ def response(self) -> Dict[str, Any]:
77
+ return {
78
+ "files": [{"name": f.name, "contents": f.contents} for f in self.files],
79
+ }