@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,436 @@
1
+ """Where a task goes: the app's configuration, read as syntax.
2
+
3
+ A Django project configures Celery in one of two places, and usually both: the
4
+ app module, `app = Celery("billing")` followed by `app.conf.update(...)` or
5
+ `app.conf.task_routes = ...`, and the settings module the app is told to read
6
+ with `config_from_object("django.conf:settings", namespace="CELERY")`, where
7
+ every key is spelled in upper case under that prefix. Both are read here, the
8
+ settings first and the app's own assignments over them, which is the order
9
+ Celery applies them in.
10
+
11
+ Three keys decide a queue: `task_routes`, `task_default_queue` and
12
+ `broker_url`. A fourth, `beat_schedule`, says what the clock sets off - and
13
+ so does `app.add_periodic_task(...)`, wherever it is called. A value that is
14
+ not a literal is read as far as it goes - `os.environ.get("X", default)` is
15
+ its default - and past that it is unknown, which is said rather than guessed.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import ast
21
+ from dataclasses import dataclass, field
22
+ from fnmatch import fnmatchcase
23
+ from typing import Dict, List, Optional, Tuple
24
+
25
+ from source import Module, Project, assigned, const_str, dotted, keyword, keyword_str
26
+
27
+ # Celery's own default queue, used when nothing in the tree says otherwise.
28
+ DEFAULT_QUEUE = "celery"
29
+
30
+ # The pre-4.0 spellings, still accepted by Celery and still found in settings.
31
+ LEGACY = {
32
+ "CELERY_ROUTES": "task_routes",
33
+ "CELERY_DEFAULT_QUEUE": "task_default_queue",
34
+ "BROKER_URL": "broker_url",
35
+ "CELERYBEAT_SCHEDULE": "beat_schedule",
36
+ }
37
+
38
+ KNOWN = {"task_routes", "task_default_queue", "broker_url", "beat_schedule"}
39
+
40
+ # The application that keeps the schedule in the database instead of the tree.
41
+ BEAT_IN_DATABASE = "django_celery_beat"
42
+
43
+
44
+ @dataclass
45
+ class App:
46
+ """A `Celery(...)` instance: the module it lives in and the name it is
47
+ bound to there, which is what `@app.task` is resolved against."""
48
+
49
+ module: Module
50
+ name: str
51
+ node: ast.Call
52
+ namespace: str = ""
53
+
54
+
55
+ @dataclass
56
+ class Schedule:
57
+ """One thing the clock sets off: a `beat_schedule` entry, or one call of
58
+ `add_periodic_task`. The task is named on the wire (`task`) or as the
59
+ signature spells it (`ref`, resolved by whoever holds the task index)."""
60
+
61
+ name: str # the entry's key, or the `name=` the call gave
62
+ when: str # "cron 0 9 * * *", "every 6h" - or the expression as written, when opaque
63
+ module: Module # where it is declared
64
+ node: ast.AST
65
+ task: str = "" # the wire name, when the entry says one
66
+ ref: str = "" # the task as `add_periodic_task` names it, when it is a signature
67
+ queue: str = "" # `options={"queue": ...}` or a `queue=` on the call
68
+ opaque: bool = False # the schedule was not readable as syntax
69
+
70
+ @property
71
+ def where(self) -> str:
72
+ return self.module.where(self.node)
73
+
74
+
75
+ @dataclass
76
+ class Config:
77
+ routes: List[Tuple[str, str]] = field(default_factory=list) # pattern -> queue, in declaration order
78
+ default_queue: str = ""
79
+ broker: str = ""
80
+ settings: str = "" # the settings module, as looked for
81
+ settings_found: bool = False
82
+ opaque: List[str] = field(default_factory=list) # where a value could not be read
83
+ apps: List[App] = field(default_factory=list)
84
+ schedules: List[Schedule] = field(default_factory=list) # in declaration order
85
+ opaque_schedules: List[str] = field(default_factory=list) # where a beat_schedule was not a mapping
86
+ beat_in_database: bool = False # django_celery_beat is installed
87
+
88
+ @property
89
+ def broker_scheme(self) -> str:
90
+ return self.broker.split("://", 1)[0] if "://" in self.broker else ""
91
+
92
+
93
+ def find_apps(project: Project) -> List[App]:
94
+ """Every module-level `X = Celery(...)`, in path order."""
95
+ out = []
96
+ for module in sorted(project.modules.values(), key=lambda m: m.rel):
97
+ for name, value, _ in assigned(module.tree):
98
+ if isinstance(value, ast.Call) and dotted(value.func).split(".")[-1] == "Celery":
99
+ out.append(App(module, name, value))
100
+ return out
101
+
102
+
103
+ def settings_module_name(project: Project) -> str:
104
+ """What `manage.py` sets `DJANGO_SETTINGS_MODULE` to, or the app module
105
+ does; the settings module is otherwise nowhere written down."""
106
+ for module in sorted(project.modules.values(), key=lambda m: m.rel):
107
+ if module.dotted.split(".")[-1] not in ("manage", "celery", "wsgi", "asgi"):
108
+ continue
109
+ for node in ast.walk(module.tree):
110
+ if isinstance(node, ast.Call) and dotted(node.func).split(".")[-1] == "setdefault":
111
+ if len(node.args) == 2 and const_str(node.args[0]) == "DJANGO_SETTINGS_MODULE":
112
+ return const_str(node.args[1])
113
+ if isinstance(node, ast.Assign) and len(node.targets) == 1:
114
+ target = node.targets[0]
115
+ if isinstance(target, ast.Subscript) and dotted(target.value) == "os.environ" and const_str(target.slice) == "DJANGO_SETTINGS_MODULE":
116
+ return const_str(node.value)
117
+ return ""
118
+
119
+
120
+ def read_config(project: Project, settings: str) -> Config:
121
+ cfg = Config(apps=find_apps(project))
122
+
123
+ # The app first, for the namespace the settings are read under.
124
+ for app in cfg.apps:
125
+ for node in ast.walk(app.module.tree):
126
+ if isinstance(node, ast.Call) and dotted(node.func) == app.name + ".config_from_object":
127
+ app.namespace = const_str(keyword(node, "namespace"))
128
+ broker = keyword(app.node, "broker")
129
+ if broker is not None:
130
+ cfg.broker = str_value(broker, app.module) or cfg.broker
131
+
132
+ cfg.settings = settings or settings_module_name(project)
133
+ module = project.module(cfg.settings) if cfg.settings else None
134
+ if module is not None:
135
+ cfg.settings_found = True
136
+ namespaces = [app.namespace for app in cfg.apps if app.namespace]
137
+ for name, value, _ in assigned(module.tree):
138
+ key = settings_key(name, namespaces)
139
+ if key:
140
+ apply(cfg, key, value, module)
141
+ if name == "INSTALLED_APPS":
142
+ installed = follow(value, module)
143
+ if isinstance(installed, (ast.List, ast.Tuple)) and any(const_str(e) == BEAT_IN_DATABASE for e in installed.elts):
144
+ cfg.beat_in_database = True
145
+
146
+ # Then what the app module assigns itself, over the settings.
147
+ for app in cfg.apps:
148
+ for node in app.module.tree.body:
149
+ if isinstance(node, ast.Assign) and len(node.targets) == 1:
150
+ target = dotted(node.targets[0])
151
+ prefix = app.name + ".conf."
152
+ if target.startswith(prefix):
153
+ apply(cfg, target[len(prefix):].lower(), node.value, app.module)
154
+ call = node.value if isinstance(node, ast.Expr) else None
155
+ if isinstance(call, ast.Call) and dotted(call.func) == app.name + ".conf.update":
156
+ for kw in call.keywords:
157
+ if kw.arg:
158
+ apply(cfg, kw.arg.lower(), kw.value, app.module)
159
+
160
+ # `add_periodic_task` is the other way to say what the clock sets off,
161
+ # usually under `@app.on_after_configure.connect`, where the app arrives
162
+ # as `sender`; so it is read by the method's name, wherever it is called.
163
+ for module in sorted(project.modules.values(), key=lambda m: m.rel):
164
+ for node in ast.walk(module.tree):
165
+ if isinstance(node, ast.Call) and dotted(node.func).split(".")[-1] == "add_periodic_task" and len(node.args) >= 2:
166
+ cfg.schedules.append(periodic_task(node, module))
167
+ return cfg
168
+
169
+
170
+ def periodic_task(call: ast.Call, module: Module) -> Schedule:
171
+ """`sender.add_periodic_task(crontab(hour=3), close_stale_drafts.s(), name="...")`."""
172
+ when, readable = schedule_text(call.args[0], module)
173
+ signature = call.args[1]
174
+ ref = ""
175
+ if isinstance(signature, ast.Call) and isinstance(signature.func, ast.Attribute) and signature.func.attr in ("s", "si", "signature", "subtask"):
176
+ ref = dotted(signature.func.value)
177
+ else:
178
+ ref = dotted(signature)
179
+ name = keyword_str(call, "name") or ref.split(".")[-1]
180
+ return Schedule(name, when, module, call, ref=ref, queue=keyword_str(call, "queue"), opaque=not readable)
181
+
182
+
183
+ def settings_key(name: str, namespaces: List[str]) -> str:
184
+ """`CELERY_TASK_ROUTES` under the `CELERY` namespace is `task_routes`;
185
+ without a namespace the new-style lower-case keys and the legacy upper-case
186
+ ones are what Celery itself accepts."""
187
+ for namespace in namespaces:
188
+ prefix = namespace + "_"
189
+ if name.startswith(prefix):
190
+ key = name[len(prefix):].lower()
191
+ return key if key in KNOWN else ""
192
+ if namespaces:
193
+ return ""
194
+ if name in LEGACY:
195
+ return LEGACY[name]
196
+ return name if name in KNOWN else ""
197
+
198
+
199
+ def apply(cfg: Config, key: str, value: ast.AST, module: Module) -> None:
200
+ if key == "task_routes":
201
+ routes = read_routes(value, module)
202
+ if routes is None:
203
+ cfg.opaque.append(module.where(value))
204
+ else:
205
+ cfg.routes = routes
206
+ elif key == "task_default_queue":
207
+ cfg.default_queue = str_value(value, module) or cfg.default_queue
208
+ elif key == "broker_url":
209
+ cfg.broker = str_value(value, module) or cfg.broker
210
+ elif key == "beat_schedule":
211
+ read_beat(cfg, value, module)
212
+
213
+
214
+ def read_beat(cfg: Config, node: ast.AST, module: Module) -> None:
215
+ """`{"nightly": {"task": "pkg.tasks.nightly", "schedule": crontab(hour=2),
216
+ "options": {"queue": "slow"}}}`, entry by entry, in the order written."""
217
+ node = follow(node, module)
218
+ if not isinstance(node, ast.Dict):
219
+ cfg.opaque_schedules.append(module.where(node) if node is not None else module.rel)
220
+ return
221
+ for key, spec in zip(node.keys, node.values):
222
+ name = const_str(key) if key is not None else ""
223
+ spec = follow(spec, module)
224
+ if not name or not isinstance(spec, ast.Dict):
225
+ cfg.opaque_schedules.append(module.where(spec if spec is not None else node))
226
+ continue
227
+ fields = {const_str(k): v for k, v in zip(spec.keys, spec.values) if k is not None and const_str(k)}
228
+ when, readable = schedule_text(fields.get("schedule"), module)
229
+ cfg.schedules.append(
230
+ Schedule(
231
+ name,
232
+ when,
233
+ module,
234
+ spec,
235
+ task=str_value(fields.get("task"), module),
236
+ queue=route_queue(fields["options"], module) if "options" in fields else "",
237
+ opaque=not readable,
238
+ )
239
+ )
240
+
241
+
242
+ # --- a schedule, as text ----------------------------------------------------
243
+ #
244
+ # Celery's schedule is an object - a crontab, a timedelta, a number of seconds
245
+ # - and the page wants a sentence. The forms the documentation gives are read
246
+ # into the spellings a reader already knows: `cron 0 9 * * *` in crontab's own
247
+ # field order, `every 6h` for an interval. Anything else is kept as written and
248
+ # marked opaque, which the caller says out loud.
249
+
250
+ CRON_FIELDS = ("minute", "hour", "day_of_month", "month_of_year", "day_of_week")
251
+
252
+
253
+ def schedule_text(node: Optional[ast.AST], module: Module) -> Tuple[str, bool]:
254
+ """(text, readable): the schedule as a sentence, and whether syntax
255
+ actually said so."""
256
+ node = follow(node, module)
257
+ if node is None:
258
+ return "", False
259
+ if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)) and not isinstance(node.value, bool):
260
+ return "every " + interval(float(node.value)), True
261
+ if isinstance(node, ast.Call):
262
+ last = dotted(node.func).split(".")[-1]
263
+ if last == "crontab":
264
+ given = {kw.arg: cron_field(kw.value) for kw in node.keywords if kw.arg in CRON_FIELDS}
265
+ for position, arg in enumerate(node.args[: len(CRON_FIELDS)]):
266
+ given.setdefault(CRON_FIELDS[position], cron_field(arg))
267
+ if any(value is None for value in given.values()):
268
+ return unparse(node), False
269
+ return "cron " + " ".join(given.get(field_, "*") or "*" for field_ in CRON_FIELDS), True
270
+ if last == "timedelta":
271
+ seconds = timedelta_seconds(node)
272
+ if seconds is None:
273
+ return unparse(node), False
274
+ return "every " + interval(seconds), True
275
+ if last == "schedule":
276
+ inner = keyword(node, "run_every") if keyword(node, "run_every") is not None else (node.args[0] if node.args else None)
277
+ return schedule_text(inner, module)
278
+ if last == "solar" and node.args and const_str(node.args[0]):
279
+ return "solar " + const_str(node.args[0]), True
280
+ return unparse(node), False
281
+
282
+
283
+ def cron_field(node: ast.AST) -> Optional[str]:
284
+ if isinstance(node, ast.Constant) and isinstance(node.value, (str, int)) and not isinstance(node.value, bool):
285
+ return str(node.value)
286
+ return None
287
+
288
+
289
+ TIMEDELTA_UNITS = {"weeks": 604800.0, "days": 86400.0, "hours": 3600.0, "minutes": 60.0, "seconds": 1.0, "milliseconds": 0.001}
290
+ TIMEDELTA_POSITIONS = ("days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks")
291
+
292
+
293
+ def timedelta_seconds(call: ast.Call) -> Optional[float]:
294
+ total = 0.0
295
+ parts = [(TIMEDELTA_POSITIONS[i], arg) for i, arg in enumerate(call.args[: len(TIMEDELTA_POSITIONS)])]
296
+ parts += [(kw.arg or "", kw.value) for kw in call.keywords]
297
+ for unit, value in parts:
298
+ if unit not in TIMEDELTA_UNITS or not (isinstance(value, ast.Constant) and isinstance(value.value, (int, float)) and not isinstance(value.value, bool)):
299
+ return None
300
+ total += float(value.value) * TIMEDELTA_UNITS[unit]
301
+ return total
302
+
303
+
304
+ def interval(seconds: float) -> str:
305
+ """90 -> "1m30s", 21600 -> "6h", 0.5 -> "0.5s"."""
306
+ if seconds != int(seconds):
307
+ return "%gs" % seconds
308
+ whole = int(seconds)
309
+ out = ""
310
+ for unit, size in (("d", 86400), ("h", 3600), ("m", 60)):
311
+ if whole >= size:
312
+ out += "%d%s" % (whole // size, unit)
313
+ whole %= size
314
+ if whole or not out:
315
+ out += "%ds" % whole
316
+ return out
317
+
318
+
319
+ def unparse(node: ast.AST) -> str:
320
+ try:
321
+ return ast.unparse(node)
322
+ except Exception: # a node ast.unparse does not know
323
+ return type(node).__name__
324
+
325
+
326
+ def read_routes(node: ast.AST, module: Module) -> Optional[List[Tuple[str, str]]]:
327
+ """`{"pkg.tasks.send": {"queue": "mail"}}`, or the same as a list of pairs.
328
+ A router function, or anything else that is not a literal, is None: the
329
+ routes are decided at run time and this reader does not run anything."""
330
+ node = follow(node, module)
331
+ pairs: List[Tuple[ast.AST, ast.AST]] = []
332
+ if isinstance(node, ast.Dict):
333
+ pairs = [(k, v) for k, v in zip(node.keys, node.values) if k is not None]
334
+ elif isinstance(node, (ast.List, ast.Tuple)):
335
+ for element in node.elts:
336
+ if isinstance(element, ast.Tuple) and len(element.elts) == 2:
337
+ pairs.append((element.elts[0], element.elts[1]))
338
+ elif isinstance(element, ast.Dict):
339
+ pairs += [(k, v) for k, v in zip(element.keys, element.values) if k is not None]
340
+ else:
341
+ return None
342
+ else:
343
+ return None
344
+ out = []
345
+ for key, spec in pairs:
346
+ pattern = const_str(key)
347
+ queue = route_queue(spec, module)
348
+ if pattern and queue:
349
+ out.append((pattern, queue))
350
+ return out
351
+
352
+
353
+ def route_queue(spec: ast.AST, module: Module) -> str:
354
+ spec = follow(spec, module)
355
+ if isinstance(spec, ast.Dict):
356
+ for key, value in zip(spec.keys, spec.values):
357
+ if key is not None and const_str(key) == "queue":
358
+ return str_value(value, module)
359
+ return ""
360
+ return str_value(spec, module)
361
+
362
+
363
+ def str_value(node: Optional[ast.AST], module: Module) -> str:
364
+ """A string as far as syntax carries it: the literal, the default of an
365
+ environment lookup, or a module-level constant, followed once."""
366
+ node = follow(node, module)
367
+ if node is None:
368
+ return ""
369
+ if isinstance(node, ast.Constant) and isinstance(node.value, str):
370
+ return node.value
371
+ if isinstance(node, ast.Call):
372
+ name = dotted(node.func)
373
+ last = name.split(".")[-1]
374
+ if last in ("get", "getenv") and (name.startswith("os.environ") or name == "os.getenv" or name == "getenv"):
375
+ default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
376
+ return str_value(default, module)
377
+ if last in ("env", "str") and name.split(".")[0] in ("env", "environ", "config"):
378
+ default = node.args[1] if len(node.args) > 1 else keyword(node, "default")
379
+ return str_value(default, module)
380
+ return ""
381
+
382
+
383
+ def follow(node: Optional[ast.AST], module: Module) -> Optional[ast.AST]:
384
+ """A bare name is the module-level assignment it refers to, once."""
385
+ if isinstance(node, ast.Name):
386
+ for name, value, _ in assigned(module.tree):
387
+ if name == node.id:
388
+ return value
389
+ return node
390
+
391
+
392
+ def apps_by_module(cfg: Config) -> Dict[Tuple[str, str], App]:
393
+ return {(app.module.dotted, app.name): app for app in cfg.apps}
394
+
395
+
396
+ # --- which queue a task lands on, decided the way Celery decides it ---------
397
+ #
398
+ # The call comes first, `apply_async(queue=...)`; then the decorator's `queue=`;
399
+ # then `task_routes`, an exact name before a glob, in the order they are
400
+ # written; then `task_default_queue`; and past all of those Celery's own
401
+ # `celery`. Every answer says which rule gave it, so a page can say "routed by
402
+ # task_routes" rather than leave a reader to work it out.
403
+
404
+ from typing import List, Tuple
405
+
406
+
407
+ AT_CALL = "named at the call"
408
+ ON_TASK = "named on the task"
409
+ BY_ROUTES = "routed by task_routes"
410
+ BY_DEFAULT = "the default queue"
411
+ BY_CELERY = "Celery's own default"
412
+
413
+
414
+ def queue_for(wire: str, at_call: str, on_task: str, cfg: Config) -> Tuple[str, str]:
415
+ if at_call:
416
+ return at_call, AT_CALL
417
+ if on_task:
418
+ return on_task, ON_TASK
419
+ for pattern, queue in cfg.routes:
420
+ if matches(pattern, wire):
421
+ return queue, BY_ROUTES
422
+ if cfg.default_queue:
423
+ return cfg.default_queue, BY_DEFAULT
424
+ return DEFAULT_QUEUE, BY_CELERY
425
+
426
+
427
+ def matches(pattern: str, wire: str) -> bool:
428
+ """Celery takes a glob - `invoices.tasks.*` - and a compiled regex; only
429
+ the glob is syntax, so only the glob is matched here."""
430
+ return pattern == wire or fnmatchcase(wire, pattern)
431
+
432
+
433
+ def unmatched(cfg: Config, wires: List[str]) -> List[str]:
434
+ """Route patterns that name no task in the tree: a route to nowhere is
435
+ stale, or the task moved."""
436
+ return [pattern for pattern, _ in cfg.routes if not any(matches(pattern, wire) for wire in wires)]
@@ -0,0 +1,76 @@
1
+ """The tasks: every function the tree hands to Celery.
2
+
3
+ A task is a module-level function decorated `@shared_task`, `@app.task` or
4
+ `@<anything>.task`, with or without arguments. Its wire name is the `name=` it
5
+ was given, or what Celery composes when it was not: the module's dotted path
6
+ and the function's name, `invoices.tasks.send_invoice_email`. A `queue=` on
7
+ the decorator is where it goes unless a call says otherwise; the docstring is
8
+ what the page says about it. A class-based task is not read.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import ast
14
+ from dataclasses import dataclass
15
+ from typing import Dict, List, Tuple
16
+
17
+ from source import Module, Project, decorator_named, doc, keyword_str
18
+
19
+ DECORATORS = ("task", "shared_task")
20
+
21
+
22
+ def package_of(module: Module) -> str:
23
+ """The module as an import spells it: a package's `__init__` is the package."""
24
+ return module.dotted[: -len(".__init__")] if module.dotted.endswith(".__init__") else module.dotted
25
+
26
+
27
+ @dataclass
28
+ class Task:
29
+ module: Module
30
+ node: ast.AST
31
+ name: str # the wire name
32
+ queue: str # the decorator's, "" when it has none
33
+
34
+ @property
35
+ def short(self) -> str:
36
+ return self.node.name # type: ignore[attr-defined]
37
+
38
+ @property
39
+ def key(self) -> Tuple[str, str]:
40
+ return package_of(self.module), self.short
41
+
42
+ @property
43
+ def line(self) -> str:
44
+ return self.module.where(self.node)
45
+
46
+ @property
47
+ def doc(self) -> str:
48
+ return doc(self.node)
49
+
50
+ @property
51
+ def entrypoint(self) -> str:
52
+ """Stable source seam shared by the task transport and body readers."""
53
+ return "python:%s:%s" % (package_of(self.module), self.short)
54
+
55
+
56
+ def read_tasks(project: Project) -> List[Task]:
57
+ out = []
58
+ for module in sorted(project.modules.values(), key=lambda m: m.rel):
59
+ for fn in module.functions():
60
+ dec = decorator_named(fn, *DECORATORS)
61
+ if dec is None:
62
+ continue
63
+ name = queue = ""
64
+ if isinstance(dec, ast.Call):
65
+ name = keyword_str(dec, "name")
66
+ queue = keyword_str(dec, "queue")
67
+ out.append(Task(module, fn, name or package_of(module) + "." + fn.name, queue))
68
+ return out
69
+
70
+
71
+ def index(tasks: List[Task]) -> Tuple[Dict[Tuple[str, str], Task], Dict[str, Task]]:
72
+ """By where the function is, for a call on it; by wire name, for
73
+ `send_task`."""
74
+ by_key = {task.key: task for task in tasks}
75
+ by_name = {task.name: task for task in tasks}
76
+ return by_key, by_name