@shortlink-org/portolan 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (826) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +387 -0
  3. package/catalog/enum_test.go +46 -0
  4. package/catalog/model.go +932 -0
  5. package/catalog/roundtrip_test.go +185 -0
  6. package/catalog/via_test.go +38 -0
  7. package/cli/portolan.mjs +368 -0
  8. package/go.mod +14 -0
  9. package/go.sum +14 -0
  10. package/index.html +13 -0
  11. package/internal/commands/cargo.go +154 -0
  12. package/internal/commands/commands.go +143 -0
  13. package/internal/commands/commands_test.go +323 -0
  14. package/internal/commands/gradle.go +77 -0
  15. package/internal/commands/justfile.go +82 -0
  16. package/internal/commands/makefile.go +106 -0
  17. package/internal/commands/maven.go +93 -0
  18. package/internal/commands/packagejson.go +159 -0
  19. package/internal/commands/pyproject.go +169 -0
  20. package/internal/commands/taskfile.go +101 -0
  21. package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
  22. package/internal/commands/testdata/estate/Makefile +33 -0
  23. package/internal/commands/testdata/estate/Taskfile.yml +21 -0
  24. package/internal/commands/testdata/estate/build.gradle.kts +21 -0
  25. package/internal/commands/testdata/estate/justfile +20 -0
  26. package/internal/commands/testdata/estate/package.json +13 -0
  27. package/internal/commands/testdata/estate/pom.xml +29 -0
  28. package/internal/commands/testdata/estate/pyproject.toml +27 -0
  29. package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
  30. package/internal/commands/testdata/golden/commands.json +305 -0
  31. package/internal/gohttp/analyze.go +2500 -0
  32. package/internal/gohttp/endpoints.go +1067 -0
  33. package/internal/gohttp/roots.go +320 -0
  34. package/internal/gohttp/typed.go +143 -0
  35. package/internal/goscan/constants.go +85 -0
  36. package/internal/goscan/goscan_test.go +227 -0
  37. package/internal/goscan/names.go +52 -0
  38. package/internal/goscan/parse_test.go +11 -0
  39. package/internal/goscan/source.go +36 -0
  40. package/internal/goscan/tree.go +155 -0
  41. package/internal/goscan/types.go +99 -0
  42. package/internal/wsdl/ids.go +127 -0
  43. package/internal/wsdl/ids_test.go +21 -0
  44. package/internal/wsdl/model.go +62 -0
  45. package/internal/wsdl/parse.go +920 -0
  46. package/internal/wsdl/parse_test.go +133 -0
  47. package/package.json +117 -0
  48. package/plugin/describe.go +107 -0
  49. package/plugin/describe_test.go +114 -0
  50. package/plugin/protocol.go +117 -0
  51. package/plugin/schematest/schematest.go +126 -0
  52. package/plugins/README.md +1160 -0
  53. package/plugins/extract-adr/describe.go +19 -0
  54. package/plugins/extract-adr/describe_test.go +11 -0
  55. package/plugins/extract-adr/extract.go +153 -0
  56. package/plugins/extract-adr/extract_test.go +350 -0
  57. package/plugins/extract-adr/history.go +99 -0
  58. package/plugins/extract-adr/main.go +65 -0
  59. package/plugins/extract-adr/options.schema.json +26 -0
  60. package/plugins/extract-adr/parse.go +642 -0
  61. package/plugins/extract-adr/parse_test.go +405 -0
  62. package/plugins/extract-asyncapi/describe.go +19 -0
  63. package/plugins/extract-asyncapi/describe_test.go +11 -0
  64. package/plugins/extract-asyncapi/extract.go +315 -0
  65. package/plugins/extract-asyncapi/extract_test.go +197 -0
  66. package/plugins/extract-asyncapi/main.go +48 -0
  67. package/plugins/extract-asyncapi/options.schema.json +26 -0
  68. package/plugins/extract-asyncapi/spec.go +151 -0
  69. package/plugins/extract-celery/README.md +169 -0
  70. package/plugins/extract-celery/extract.py +346 -0
  71. package/plugins/extract-celery/extract_test.py +192 -0
  72. package/plugins/extract-celery/main.py +66 -0
  73. package/plugins/extract-celery/options.py +31 -0
  74. package/plugins/extract-celery/options.schema.json +31 -0
  75. package/plugins/extract-celery/producers.py +156 -0
  76. package/plugins/extract-celery/routes_test.py +106 -0
  77. package/plugins/extract-commands/describe.go +19 -0
  78. package/plugins/extract-commands/describe_test.go +11 -0
  79. package/plugins/extract-commands/extract.go +79 -0
  80. package/plugins/extract-commands/extract_test.go +92 -0
  81. package/plugins/extract-commands/main.go +55 -0
  82. package/plugins/extract-commands/options.schema.json +20 -0
  83. package/plugins/extract-csr/avro.go +258 -0
  84. package/plugins/extract-csr/describe.go +19 -0
  85. package/plugins/extract-csr/describe_test.go +11 -0
  86. package/plugins/extract-csr/extract.go +338 -0
  87. package/plugins/extract-csr/extract_test.go +374 -0
  88. package/plugins/extract-csr/jsonschema.go +343 -0
  89. package/plugins/extract-csr/lock.go +25 -0
  90. package/plugins/extract-csr/main.go +84 -0
  91. package/plugins/extract-csr/options.schema.json +43 -0
  92. package/plugins/extract-csr/subject.go +102 -0
  93. package/plugins/extract-django/README.md +386 -0
  94. package/plugins/extract-django/apps.py +100 -0
  95. package/plugins/extract-django/auth.py +302 -0
  96. package/plugins/extract-django/auth_test.py +188 -0
  97. package/plugins/extract-django/clients.py +121 -0
  98. package/plugins/extract-django/contracts.py +458 -0
  99. package/plugins/extract-django/database.py +119 -0
  100. package/plugins/extract-django/domain.py +318 -0
  101. package/plugins/extract-django/events.py +86 -0
  102. package/plugins/extract-django/extract.py +601 -0
  103. package/plugins/extract-django/extract_test.py +397 -0
  104. package/plugins/extract-django/flows.py +1278 -0
  105. package/plugins/extract-django/flows_test.py +338 -0
  106. package/plugins/extract-django/ids.py +42 -0
  107. package/plugins/extract-django/ids_test.py +30 -0
  108. package/plugins/extract-django/lifecycle.py +222 -0
  109. package/plugins/extract-django/lifecycle_test.py +76 -0
  110. package/plugins/extract-django/main.py +66 -0
  111. package/plugins/extract-django/openapi.py +229 -0
  112. package/plugins/extract-django/openapi_test.py +60 -0
  113. package/plugins/extract-django/operations.py +96 -0
  114. package/plugins/extract-django/options.py +61 -0
  115. package/plugins/extract-django/options.schema.json +126 -0
  116. package/plugins/extract-django/routing.py +214 -0
  117. package/plugins/extract-django/routing_test.py +198 -0
  118. package/plugins/extract-django/serializers.py +558 -0
  119. package/plugins/extract-django/store.py +434 -0
  120. package/plugins/extract-django/transport.py +242 -0
  121. package/plugins/extract-flows/describe.go +19 -0
  122. package/plugins/extract-flows/describe_test.go +11 -0
  123. package/plugins/extract-flows/extract.go +91 -0
  124. package/plugins/extract-flows/main.go +45 -0
  125. package/plugins/extract-flows/options.schema.json +16 -0
  126. package/plugins/extract-flows/parse.go +593 -0
  127. package/plugins/extract-flows/parse_test.go +204 -0
  128. package/plugins/extract-glossary/describe.go +19 -0
  129. package/plugins/extract-glossary/describe_test.go +11 -0
  130. package/plugins/extract-glossary/extract.go +115 -0
  131. package/plugins/extract-glossary/extract_test.go +220 -0
  132. package/plugins/extract-glossary/main.go +59 -0
  133. package/plugins/extract-glossary/options.schema.json +21 -0
  134. package/plugins/extract-glossary/parse.go +214 -0
  135. package/plugins/extract-glossary/parse_test.go +203 -0
  136. package/plugins/extract-go/aggregate.go +214 -0
  137. package/plugins/extract-go/client.go +409 -0
  138. package/plugins/extract-go/client_test.go +266 -0
  139. package/plugins/extract-go/describe.go +19 -0
  140. package/plugins/extract-go/describe_test.go +11 -0
  141. package/plugins/extract-go/enum.go +195 -0
  142. package/plugins/extract-go/enum_test.go +82 -0
  143. package/plugins/extract-go/event.go +99 -0
  144. package/plugins/extract-go/extract.go +191 -0
  145. package/plugins/extract-go/extract_test.go +261 -0
  146. package/plugins/extract-go/flow.go +1441 -0
  147. package/plugins/extract-go/flow_test.go +609 -0
  148. package/plugins/extract-go/httpclient.go +174 -0
  149. package/plugins/extract-go/httpclient_test.go +296 -0
  150. package/plugins/extract-go/ids.go +92 -0
  151. package/plugins/extract-go/layout.go +225 -0
  152. package/plugins/extract-go/layout_test.go +125 -0
  153. package/plugins/extract-go/lifecycle.go +324 -0
  154. package/plugins/extract-go/lifecycle_test.go +108 -0
  155. package/plugins/extract-go/main.go +79 -0
  156. package/plugins/extract-go/operation.go +157 -0
  157. package/plugins/extract-go/options.schema.json +66 -0
  158. package/plugins/extract-go/source.go +316 -0
  159. package/plugins/extract-go/transport.go +321 -0
  160. package/plugins/extract-go/transport_test.go +145 -0
  161. package/plugins/extract-go/wiring.go +434 -0
  162. package/plugins/extract-go-nats/describe.go +19 -0
  163. package/plugins/extract-go-nats/describe_test.go +11 -0
  164. package/plugins/extract-go-nats/extract.go +177 -0
  165. package/plugins/extract-go-nats/extract_test.go +389 -0
  166. package/plugins/extract-go-nats/index.go +379 -0
  167. package/plugins/extract-go-nats/main.go +42 -0
  168. package/plugins/extract-go-nats/options.schema.json +21 -0
  169. package/plugins/extract-go-nats/resolve.go +161 -0
  170. package/plugins/extract-go-nats/sites.go +224 -0
  171. package/plugins/extract-graphql/describe.go +19 -0
  172. package/plugins/extract-graphql/describe_test.go +11 -0
  173. package/plugins/extract-graphql/extract.go +433 -0
  174. package/plugins/extract-graphql/extract_test.go +256 -0
  175. package/plugins/extract-graphql/ids.go +49 -0
  176. package/plugins/extract-graphql/lex.go +237 -0
  177. package/plugins/extract-graphql/main.go +51 -0
  178. package/plugins/extract-graphql/options.schema.json +31 -0
  179. package/plugins/extract-graphql/parse.go +621 -0
  180. package/plugins/extract-graphql/parse_test.go +122 -0
  181. package/plugins/extract-http-clients/describe.go +19 -0
  182. package/plugins/extract-http-clients/describe_test.go +11 -0
  183. package/plugins/extract-http-clients/extract.go +705 -0
  184. package/plugins/extract-http-clients/extract_test.go +1263 -0
  185. package/plugins/extract-http-clients/main.go +42 -0
  186. package/plugins/extract-http-clients/options.schema.json +33 -0
  187. package/plugins/extract-java/README.md +190 -0
  188. package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
  189. package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
  190. package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
  191. package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
  192. package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
  193. package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
  194. package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
  195. package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
  196. package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
  197. package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
  198. package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
  199. package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
  200. package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
  201. package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
  202. package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
  203. package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
  204. package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
  205. package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
  206. package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
  207. package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
  208. package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
  209. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
  210. package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
  211. package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
  212. package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
  213. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
  214. package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
  215. package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
  216. package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
  217. package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
  218. package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
  219. package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
  220. package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
  221. package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
  222. package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
  223. package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
  224. package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
  225. package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
  226. package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
  227. package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
  228. package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
  229. package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
  230. package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
  231. package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
  232. package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
  233. package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
  234. package/plugins/extract-java/options.schema.json +70 -0
  235. package/plugins/extract-openapi/describe.go +19 -0
  236. package/plugins/extract-openapi/describe_test.go +11 -0
  237. package/plugins/extract-openapi/discover.go +243 -0
  238. package/plugins/extract-openapi/extract.go +526 -0
  239. package/plugins/extract-openapi/extract_test.go +545 -0
  240. package/plugins/extract-openapi/main.go +75 -0
  241. package/plugins/extract-openapi/options.schema.json +62 -0
  242. package/plugins/extract-openapi/spec.go +350 -0
  243. package/plugins/extract-project/README.md +22 -0
  244. package/plugins/extract-project/describe.go +19 -0
  245. package/plugins/extract-project/describe_test.go +11 -0
  246. package/plugins/extract-project/extract.go +221 -0
  247. package/plugins/extract-project/extract_test.go +109 -0
  248. package/plugins/extract-project/main.go +41 -0
  249. package/plugins/extract-project/options.schema.json +48 -0
  250. package/plugins/extract-proto/ast.go +125 -0
  251. package/plugins/extract-proto/consumes.go +77 -0
  252. package/plugins/extract-proto/describe.go +19 -0
  253. package/plugins/extract-proto/extract.go +293 -0
  254. package/plugins/extract-proto/extract_test.go +459 -0
  255. package/plugins/extract-proto/ids.go +89 -0
  256. package/plugins/extract-proto/ids_test.go +57 -0
  257. package/plugins/extract-proto/lex.go +285 -0
  258. package/plugins/extract-proto/main.go +100 -0
  259. package/plugins/extract-proto/module.go +120 -0
  260. package/plugins/extract-proto/options.schema.json +51 -0
  261. package/plugins/extract-proto/parse.go +720 -0
  262. package/plugins/extract-proto/parse_test.go +307 -0
  263. package/plugins/extract-proto/provides.go +236 -0
  264. package/plugins/extract-proto/resolve.go +222 -0
  265. package/plugins/extract-proto/resolve_test.go +119 -0
  266. package/plugins/extract-python-kafka/README.md +36 -0
  267. package/plugins/extract-python-kafka/extract.py +211 -0
  268. package/plugins/extract-python-kafka/extract_test.py +68 -0
  269. package/plugins/extract-python-kafka/main.py +59 -0
  270. package/plugins/extract-python-kafka/options.py +29 -0
  271. package/plugins/extract-python-kafka/options.schema.json +31 -0
  272. package/plugins/extract-redis/README.md +11 -0
  273. package/plugins/extract-redis/describe.go +19 -0
  274. package/plugins/extract-redis/describe_test.go +11 -0
  275. package/plugins/extract-redis/extract.go +183 -0
  276. package/plugins/extract-redis/extract_test.go +168 -0
  277. package/plugins/extract-redis/keyspaces.go +469 -0
  278. package/plugins/extract-redis/main.go +44 -0
  279. package/plugins/extract-redis/options.schema.json +30 -0
  280. package/plugins/extract-river/describe.go +19 -0
  281. package/plugins/extract-river/describe_test.go +11 -0
  282. package/plugins/extract-river/extract.go +507 -0
  283. package/plugins/extract-river/extract_test.go +132 -0
  284. package/plugins/extract-river/main.go +42 -0
  285. package/plugins/extract-river/options.schema.json +21 -0
  286. package/plugins/extract-rust/Cargo.lock +133 -0
  287. package/plugins/extract-rust/Cargo.toml +23 -0
  288. package/plugins/extract-rust/README.md +204 -0
  289. package/plugins/extract-rust/options.schema.json +65 -0
  290. package/plugins/extract-rust/rustfmt.toml +1 -0
  291. package/plugins/extract-sql/ddl.go +893 -0
  292. package/plugins/extract-sql/ddl_test.go +401 -0
  293. package/plugins/extract-sql/describe.go +19 -0
  294. package/plugins/extract-sql/describe_test.go +11 -0
  295. package/plugins/extract-sql/layout.go +221 -0
  296. package/plugins/extract-sql/layout_test.go +55 -0
  297. package/plugins/extract-sql/lineage.go +107 -0
  298. package/plugins/extract-sql/main.go +141 -0
  299. package/plugins/extract-sql/maps.go +564 -0
  300. package/plugins/extract-sql/maps_java.go +117 -0
  301. package/plugins/extract-sql/maps_rust.go +333 -0
  302. package/plugins/extract-sql/maps_rust_test.go +70 -0
  303. package/plugins/extract-sql/maps_test.go +204 -0
  304. package/plugins/extract-sql/maps_ts.go +398 -0
  305. package/plugins/extract-sql/maps_ts_test.go +136 -0
  306. package/plugins/extract-sql/options.schema.json +53 -0
  307. package/plugins/extract-sql/projection.go +70 -0
  308. package/plugins/extract-sql/projection_test.go +50 -0
  309. package/plugins/extract-sql/store.go +420 -0
  310. package/plugins/extract-sql/store_test.go +233 -0
  311. package/plugins/extract-sql/view.go +295 -0
  312. package/plugins/extract-ts/README.md +306 -0
  313. package/plugins/extract-ts/ast.test.ts +178 -0
  314. package/plugins/extract-ts/ast.ts +648 -0
  315. package/plugins/extract-ts/clients.ts +185 -0
  316. package/plugins/extract-ts/domain.ts +164 -0
  317. package/plugins/extract-ts/extract.test.ts +208 -0
  318. package/plugins/extract-ts/extract.ts +200 -0
  319. package/plugins/extract-ts/flows.ts +783 -0
  320. package/plugins/extract-ts/graphql.test.ts +73 -0
  321. package/plugins/extract-ts/graphql.ts +181 -0
  322. package/plugins/extract-ts/ids.test.ts +21 -0
  323. package/plugins/extract-ts/ids.ts +52 -0
  324. package/plugins/extract-ts/lifecycle.ts +138 -0
  325. package/plugins/extract-ts/main.ts +48 -0
  326. package/plugins/extract-ts/openapi.test.ts +64 -0
  327. package/plugins/extract-ts/openapi.ts +99 -0
  328. package/plugins/extract-ts/operations.ts +93 -0
  329. package/plugins/extract-ts/options.schema.json +96 -0
  330. package/plugins/extract-ts/source.test.ts +174 -0
  331. package/plugins/extract-ts/source.ts +465 -0
  332. package/plugins/extract-ts/transport.ts +147 -0
  333. package/plugins/extract-ts/tsconfig.json +13 -0
  334. package/plugins/extract-ts/wiring.ts +190 -0
  335. package/plugins/extract-watermill/describe.go +19 -0
  336. package/plugins/extract-watermill/describe_test.go +11 -0
  337. package/plugins/extract-watermill/extract.go +1228 -0
  338. package/plugins/extract-watermill/extract_test.go +234 -0
  339. package/plugins/extract-watermill/main.go +41 -0
  340. package/plugins/extract-watermill/options.schema.json +21 -0
  341. package/plugins/extract-wsdl/describe.go +19 -0
  342. package/plugins/extract-wsdl/describe_test.go +11 -0
  343. package/plugins/extract-wsdl/extract.go +148 -0
  344. package/plugins/extract-wsdl/extract_test.go +45 -0
  345. package/plugins/extract-wsdl/main.go +53 -0
  346. package/plugins/extract-wsdl/options.schema.json +59 -0
  347. package/plugins/fetch-bsr/auth.go +114 -0
  348. package/plugins/fetch-bsr/bsr.go +240 -0
  349. package/plugins/fetch-bsr/cache.go +66 -0
  350. package/plugins/fetch-bsr/describe.go +19 -0
  351. package/plugins/fetch-bsr/fetch.go +192 -0
  352. package/plugins/fetch-bsr/fetch_test.go +465 -0
  353. package/plugins/fetch-bsr/lock.go +71 -0
  354. package/plugins/fetch-bsr/main.go +117 -0
  355. package/plugins/fetch-bsr/options.schema.json +42 -0
  356. package/plugins/fetch-csr/auth.go +95 -0
  357. package/plugins/fetch-csr/cache.go +69 -0
  358. package/plugins/fetch-csr/describe.go +19 -0
  359. package/plugins/fetch-csr/describe_test.go +11 -0
  360. package/plugins/fetch-csr/fetch.go +254 -0
  361. package/plugins/fetch-csr/fetch_test.go +494 -0
  362. package/plugins/fetch-csr/lock.go +95 -0
  363. package/plugins/fetch-csr/main.go +122 -0
  364. package/plugins/fetch-csr/options.schema.json +42 -0
  365. package/plugins/fetch-csr/registry.go +209 -0
  366. package/plugins/fetch-git/cache.go +65 -0
  367. package/plugins/fetch-git/describe.go +19 -0
  368. package/plugins/fetch-git/describe_test.go +11 -0
  369. package/plugins/fetch-git/fetch.go +181 -0
  370. package/plugins/fetch-git/fetch_test.go +332 -0
  371. package/plugins/fetch-git/git.go +169 -0
  372. package/plugins/fetch-git/lock.go +72 -0
  373. package/plugins/fetch-git/main.go +127 -0
  374. package/plugins/fetch-git/offline.go +39 -0
  375. package/plugins/fetch-git/options.schema.json +42 -0
  376. package/plugins/fetch-git/pin.go +90 -0
  377. package/plugins/fetch-git/pin_test.go +74 -0
  378. package/plugins/gen-backstage/describe.go +17 -0
  379. package/plugins/gen-backstage/main.go +22 -0
  380. package/plugins/gen-backstage/options.schema.json +30 -0
  381. package/plugins/gen-backstage/plugin.go +473 -0
  382. package/plugins/gen-backstage/plugin_test.go +145 -0
  383. package/plugins/gen-backstage.wasm +0 -0
  384. package/plugins/gen-markdown/adr.go +162 -0
  385. package/plugins/gen-markdown/aggregate.go +362 -0
  386. package/plugins/gen-markdown/canonical.go +93 -0
  387. package/plugins/gen-markdown/context.go +90 -0
  388. package/plugins/gen-markdown/coverage_test.go +89 -0
  389. package/plugins/gen-markdown/describe.go +19 -0
  390. package/plugins/gen-markdown/describe_test.go +11 -0
  391. package/plugins/gen-markdown/external.go +71 -0
  392. package/plugins/gen-markdown/flow.go +277 -0
  393. package/plugins/gen-markdown/glossary.go +76 -0
  394. package/plugins/gen-markdown/glossary_test.go +148 -0
  395. package/plugins/gen-markdown/llms.go +302 -0
  396. package/plugins/gen-markdown/main.go +22 -0
  397. package/plugins/gen-markdown/markdown.go +254 -0
  398. package/plugins/gen-markdown/markdown_test.go +100 -0
  399. package/plugins/gen-markdown/module.go +107 -0
  400. package/plugins/gen-markdown/options.schema.json +16 -0
  401. package/plugins/gen-markdown/plugin.go +39 -0
  402. package/plugins/gen-markdown/quality_test.go +176 -0
  403. package/plugins/gen-markdown/redis_test.go +38 -0
  404. package/plugins/gen-markdown/render.go +344 -0
  405. package/plugins/gen-markdown/render_test.go +186 -0
  406. package/plugins/gen-markdown/service.go +456 -0
  407. package/plugins/gen-markdown/source.go +177 -0
  408. package/plugins/gen-markdown/store.go +201 -0
  409. package/plugins/gen-markdown.wasm +0 -0
  410. package/plugins/gen-mermaid/describe.go +17 -0
  411. package/plugins/gen-mermaid/main.go +22 -0
  412. package/plugins/gen-mermaid/options.schema.json +10 -0
  413. package/plugins/gen-mermaid/plugin.go +104 -0
  414. package/plugins/gen-mermaid/plugin_test.go +33 -0
  415. package/plugins/gen-mermaid.wasm +0 -0
  416. package/plugins/openapi/ids.go +261 -0
  417. package/plugins/openapi/ids_test.go +98 -0
  418. package/plugins/pyplugin/catalog.py +209 -0
  419. package/plugins/pyplugin/celery_conf.py +436 -0
  420. package/plugins/pyplugin/celery_tasks.py +76 -0
  421. package/plugins/pyplugin/kafka.py +570 -0
  422. package/plugins/pyplugin/names.py +38 -0
  423. package/plugins/pyplugin/protocol.py +79 -0
  424. package/plugins/pyplugin/source.py +285 -0
  425. package/plugins/verify-codeowners/describe.go +19 -0
  426. package/plugins/verify-codeowners/describe_test.go +11 -0
  427. package/plugins/verify-codeowners/main.go +75 -0
  428. package/plugins/verify-codeowners/match.go +85 -0
  429. package/plugins/verify-codeowners/match_test.go +47 -0
  430. package/plugins/verify-codeowners/options.schema.json +15 -0
  431. package/plugins/verify-codeowners/owners.go +164 -0
  432. package/plugins/verify-codeowners/owners_test.go +225 -0
  433. package/plugins/verify-codeowners/parse.go +90 -0
  434. package/plugins/verify-codeowners/parse_test.go +62 -0
  435. package/plugins/verify-otel/describe.go +19 -0
  436. package/plugins/verify-otel/describe_test.go +11 -0
  437. package/plugins/verify-otel/main.go +53 -0
  438. package/plugins/verify-otel/match.go +336 -0
  439. package/plugins/verify-otel/options.schema.json +30 -0
  440. package/plugins/verify-otel/otlp.go +200 -0
  441. package/plugins/verify-otel/verify.go +734 -0
  442. package/plugins/verify-otel/verify_test.go +460 -0
  443. package/portolan.json +1073 -0
  444. package/public/favicon.svg +12 -0
  445. package/schema/portolan.schema.json +2558 -0
  446. package/scripts/README.md +53 -0
  447. package/scripts/build-report.mjs +60 -0
  448. package/scripts/build-report.test.mjs +44 -0
  449. package/scripts/builtin-plugins.mjs +49 -0
  450. package/scripts/catalog-sources.mjs +67 -0
  451. package/scripts/diff.mjs +315 -0
  452. package/scripts/diff.test.mjs +151 -0
  453. package/scripts/forge-comment.mjs +144 -0
  454. package/scripts/forge-comment.test.mjs +151 -0
  455. package/scripts/forge-release.mjs +138 -0
  456. package/scripts/forge-release.test.mjs +106 -0
  457. package/scripts/forge.mjs +61 -0
  458. package/scripts/gen-likec4.mjs +854 -0
  459. package/scripts/gen-likec4.test.mjs +258 -0
  460. package/scripts/gen.mjs +603 -0
  461. package/scripts/generated-docs.test.mjs +66 -0
  462. package/scripts/local-api.mjs +1192 -0
  463. package/scripts/local-api.test.mjs +387 -0
  464. package/scripts/manifest.mjs +214 -0
  465. package/scripts/manifest.test.mjs +148 -0
  466. package/scripts/output-path.mjs +79 -0
  467. package/scripts/output-path.test.mjs +45 -0
  468. package/scripts/package-smoke.mjs +37 -0
  469. package/scripts/plugin-host.mjs +419 -0
  470. package/scripts/plugin-host.test.mjs +109 -0
  471. package/scripts/plugin-wasm-worker.mjs +34 -0
  472. package/scripts/run-builtin.mjs +81 -0
  473. package/scripts/schema.mjs +357 -0
  474. package/scripts/site-docs.mjs +295 -0
  475. package/scripts/site-docs.test.mjs +181 -0
  476. package/scripts/vendor-lock.mjs +58 -0
  477. package/scripts/vendor-lock.test.mjs +69 -0
  478. package/src/README.md +38 -0
  479. package/src/app/App.tsx +448 -0
  480. package/src/app/BranchPicker.test.ts +46 -0
  481. package/src/app/BranchPicker.tsx +153 -0
  482. package/src/app/Breadcrumbs.test.ts +38 -0
  483. package/src/app/Breadcrumbs.tsx +223 -0
  484. package/src/app/BuildStamp.tsx +146 -0
  485. package/src/app/CommandPalette.tsx +292 -0
  486. package/src/app/HashScroll.tsx +45 -0
  487. package/src/app/Sidebar.tsx +1742 -0
  488. package/src/app/TopBar.tsx +393 -0
  489. package/src/app/density.tsx +64 -0
  490. package/src/app/forge-access.test.ts +27 -0
  491. package/src/app/forge-access.tsx +80 -0
  492. package/src/app/panels.tsx +141 -0
  493. package/src/app/pins.tsx +242 -0
  494. package/src/app/query-client.ts +24 -0
  495. package/src/app/responsive.ts +55 -0
  496. package/src/app/shortcuts.test.ts +39 -0
  497. package/src/app/shortcuts.tsx +282 -0
  498. package/src/app/theme.tsx +51 -0
  499. package/src/app/title.test.ts +14 -0
  500. package/src/app/title.ts +30 -0
  501. package/src/app/toast.tsx +59 -0
  502. package/src/app/tree-keys.test.ts +80 -0
  503. package/src/app/tree-keys.ts +87 -0
  504. package/src/app/ui-store.ts +52 -0
  505. package/src/catalog-error.test.ts +105 -0
  506. package/src/catalog-profile.test.ts +59 -0
  507. package/src/catalog-profile.ts +101 -0
  508. package/src/catalog-stores.test.ts +431 -0
  509. package/src/catalog.test.ts +1215 -0
  510. package/src/catalog.ts +3125 -0
  511. package/src/chat/Cards.tsx +408 -0
  512. package/src/chat/ChatPanel.tsx +79 -0
  513. package/src/chat/ChatSettings.tsx +219 -0
  514. package/src/chat/Composer.tsx +99 -0
  515. package/src/chat/Conversation.tsx +281 -0
  516. package/src/chat/Header.tsx +46 -0
  517. package/src/chat/Message.tsx +49 -0
  518. package/src/chat/Notice.tsx +195 -0
  519. package/src/chat/Starter.tsx +67 -0
  520. package/src/chat/Steps.tsx +45 -0
  521. package/src/chat/Waiting.tsx +87 -0
  522. package/src/chat/flags.test.ts +59 -0
  523. package/src/chat/flags.ts +155 -0
  524. package/src/chat/linkify.test.ts +30 -0
  525. package/src/chat/linkify.ts +45 -0
  526. package/src/chat/prefs.ts +56 -0
  527. package/src/chat/prompt.test.ts +43 -0
  528. package/src/chat/prompt.ts +157 -0
  529. package/src/chat/store.ts +35 -0
  530. package/src/chat/tools.ts +58 -0
  531. package/src/chat/transport.ts +70 -0
  532. package/src/components/AdrRow.tsx +29 -0
  533. package/src/components/AnchorLink.tsx +67 -0
  534. package/src/components/ApiReference.tsx +134 -0
  535. package/src/components/AsyncApiReference.tsx +120 -0
  536. package/src/components/CatalogStamp.tsx +155 -0
  537. package/src/components/ChannelRows.tsx +132 -0
  538. package/src/components/CommandRows.tsx +139 -0
  539. package/src/components/DiagramSkeleton.tsx +71 -0
  540. package/src/components/EditorLink.tsx +85 -0
  541. package/src/components/FieldTree.tsx +305 -0
  542. package/src/components/FlowTrigger.tsx +34 -0
  543. package/src/components/Ident.tsx +89 -0
  544. package/src/components/Integrations.tsx +243 -0
  545. package/src/components/LifecycleDiagram.tsx +110 -0
  546. package/src/components/MachineDocs.tsx +42 -0
  547. package/src/components/Markdown.tsx +173 -0
  548. package/src/components/MarkdownTable.tsx +206 -0
  549. package/src/components/Mermaid.tsx +211 -0
  550. package/src/components/MethodRows.tsx +309 -0
  551. package/src/components/Overlay.tsx +125 -0
  552. package/src/components/PageHeader.tsx +189 -0
  553. package/src/components/RowActions.tsx +97 -0
  554. package/src/components/SchemaDocument.tsx +89 -0
  555. package/src/components/Select.tsx +126 -0
  556. package/src/components/ShapeRows.tsx +118 -0
  557. package/src/components/SourceDoc.tsx +82 -0
  558. package/src/components/SourcePreview.tsx +441 -0
  559. package/src/components/TabRow.tsx +182 -0
  560. package/src/components/TechIcon.tsx +34 -0
  561. package/src/components/Toc.tsx +132 -0
  562. package/src/components/WhatLinksHere.tsx +273 -0
  563. package/src/components/WsdlReference.tsx +84 -0
  564. package/src/components/ddd-icons.tsx +107 -0
  565. package/src/components/kind.tsx +157 -0
  566. package/src/components/logo.tsx +61 -0
  567. package/src/components/primitives.tsx +264 -0
  568. package/src/data.test.ts +18 -0
  569. package/src/data.ts +202 -0
  570. package/src/enrich.test.ts +1150 -0
  571. package/src/enrich.ts +672 -0
  572. package/src/er/ErCanvas.tsx +533 -0
  573. package/src/er/RedisSchema.tsx +74 -0
  574. package/src/er/RelationCard.tsx +267 -0
  575. package/src/er/StoreHeader.tsx +155 -0
  576. package/src/er/TableNode.tsx +84 -0
  577. package/src/er/ViewNode.tsx +83 -0
  578. package/src/er/layout.test.ts +115 -0
  579. package/src/er/layout.ts +77 -0
  580. package/src/er/lineage.test.ts +140 -0
  581. package/src/er/lineage.ts +107 -0
  582. package/src/er/markers.tsx +79 -0
  583. package/src/er/spec.test.ts +344 -0
  584. package/src/er/spec.ts +426 -0
  585. package/src/flow/ChainList.tsx +162 -0
  586. package/src/flow/FlowTable.tsx +123 -0
  587. package/src/flow/FlowToolbar.tsx +372 -0
  588. package/src/flow/StepDetail.tsx +380 -0
  589. package/src/flow/StepRail.tsx +428 -0
  590. package/src/flow/answers.test.ts +50 -0
  591. package/src/flow/answers.ts +58 -0
  592. package/src/flow/chain.test.ts +383 -0
  593. package/src/flow/chain.ts +353 -0
  594. package/src/flow/chapters.test.ts +185 -0
  595. package/src/flow/chapters.ts +225 -0
  596. package/src/flow/continues.test.ts +102 -0
  597. package/src/flow/continues.ts +65 -0
  598. package/src/flow/cross-context.test.ts +97 -0
  599. package/src/flow/cross-context.ts +35 -0
  600. package/src/flow/mermaid.test.ts +22 -0
  601. package/src/flow/mermaid.ts +141 -0
  602. package/src/flow/outline.test.ts +212 -0
  603. package/src/flow/outline.ts +170 -0
  604. package/src/flow/paths.test.ts +129 -0
  605. package/src/flow/paths.ts +147 -0
  606. package/src/flow/prefs.test.ts +80 -0
  607. package/src/flow/prefs.ts +104 -0
  608. package/src/graph/DependencyGraph.tsx +283 -0
  609. package/src/graph/DependencyNodes.tsx +162 -0
  610. package/src/graph/EdgeLabel.tsx +61 -0
  611. package/src/graph/FocusedEventGraph.tsx +211 -0
  612. package/src/graph/GraphToolbar.tsx +187 -0
  613. package/src/graph/RoutedEdge.tsx +80 -0
  614. package/src/graph/dependency-layout.test.ts +360 -0
  615. package/src/graph/dependency-layout.ts +444 -0
  616. package/src/graph/elk.test.ts +70 -0
  617. package/src/graph/elk.ts +287 -0
  618. package/src/graph/nodes.tsx +97 -0
  619. package/src/graph/theme.ts +81 -0
  620. package/src/graph/useElkFlow.ts +125 -0
  621. package/src/index.css +1547 -0
  622. package/src/language/InLanguage.tsx +40 -0
  623. package/src/language/TermCard.tsx +132 -0
  624. package/src/language/cards.test.ts +114 -0
  625. package/src/language/cards.ts +111 -0
  626. package/src/lib/admonition.test.ts +49 -0
  627. package/src/lib/admonition.ts +48 -0
  628. package/src/lib/adr.test.ts +131 -0
  629. package/src/lib/adr.ts +68 -0
  630. package/src/lib/api.test.ts +122 -0
  631. package/src/lib/api.ts +135 -0
  632. package/src/lib/backlinks.test.ts +330 -0
  633. package/src/lib/backlinks.ts +567 -0
  634. package/src/lib/branch-compare.ts +16 -0
  635. package/src/lib/build-info.test.ts +76 -0
  636. package/src/lib/build-info.ts +80 -0
  637. package/src/lib/catalog-diff.test.ts +275 -0
  638. package/src/lib/catalog-diff.ts +655 -0
  639. package/src/lib/clipboard.ts +43 -0
  640. package/src/lib/comparison-memory.test.ts +40 -0
  641. package/src/lib/comparison-memory.ts +39 -0
  642. package/src/lib/context-color.ts +27 -0
  643. package/src/lib/context-map.test.ts +148 -0
  644. package/src/lib/context-map.ts +399 -0
  645. package/src/lib/data-model.test.ts +245 -0
  646. package/src/lib/data-model.ts +399 -0
  647. package/src/lib/data-problems.test.ts +178 -0
  648. package/src/lib/data-problems.ts +314 -0
  649. package/src/lib/derive.test.ts +251 -0
  650. package/src/lib/derive.ts +515 -0
  651. package/src/lib/editor-link.test.ts +125 -0
  652. package/src/lib/editor-link.ts +96 -0
  653. package/src/lib/editor-prefs.ts +27 -0
  654. package/src/lib/event-graph.test.ts +237 -0
  655. package/src/lib/event-graph.ts +327 -0
  656. package/src/lib/export-canvas.ts +37 -0
  657. package/src/lib/export-file.test.ts +30 -0
  658. package/src/lib/export-file.ts +20 -0
  659. package/src/lib/flow-tree.test.ts +177 -0
  660. package/src/lib/flow-tree.ts +160 -0
  661. package/src/lib/forge-refs.test.ts +50 -0
  662. package/src/lib/forge-refs.ts +60 -0
  663. package/src/lib/format.test.ts +35 -0
  664. package/src/lib/format.ts +64 -0
  665. package/src/lib/github-catalog.test.ts +301 -0
  666. package/src/lib/github-catalog.ts +610 -0
  667. package/src/lib/integrations.test.ts +119 -0
  668. package/src/lib/integrations.ts +133 -0
  669. package/src/lib/kinds.test.ts +91 -0
  670. package/src/lib/kinds.ts +204 -0
  671. package/src/lib/lifecycle.test.ts +93 -0
  672. package/src/lib/lifecycle.ts +165 -0
  673. package/src/lib/local-api.ts +194 -0
  674. package/src/lib/motion-features.ts +11 -0
  675. package/src/lib/motion.test.ts +120 -0
  676. package/src/lib/motion.tsx +269 -0
  677. package/src/lib/palette-recent.test.ts +80 -0
  678. package/src/lib/palette-recent.ts +90 -0
  679. package/src/lib/palette.test.ts +365 -0
  680. package/src/lib/palette.ts +593 -0
  681. package/src/lib/pins.test.ts +139 -0
  682. package/src/lib/pins.ts +130 -0
  683. package/src/lib/problems.test.ts +112 -0
  684. package/src/lib/project-preview.ts +5 -0
  685. package/src/lib/proto-problems.test.ts +121 -0
  686. package/src/lib/proto-problems.ts +65 -0
  687. package/src/lib/queries.test.ts +113 -0
  688. package/src/lib/queries.ts +131 -0
  689. package/src/lib/registry.test.ts +219 -0
  690. package/src/lib/registry.ts +208 -0
  691. package/src/lib/scenarios.ts +547 -0
  692. package/src/lib/setup-info.test.ts +199 -0
  693. package/src/lib/setup-info.ts +415 -0
  694. package/src/lib/shape.test.ts +289 -0
  695. package/src/lib/shape.ts +454 -0
  696. package/src/lib/sidebar-prefs.test.ts +32 -0
  697. package/src/lib/sidebar-prefs.ts +75 -0
  698. package/src/lib/source-code.test.ts +159 -0
  699. package/src/lib/source-code.ts +286 -0
  700. package/src/lib/source-doc.test.ts +131 -0
  701. package/src/lib/source-doc.ts +103 -0
  702. package/src/lib/source-highlight.test.ts +29 -0
  703. package/src/lib/source-highlight.ts +24 -0
  704. package/src/lib/source-link.test.ts +153 -0
  705. package/src/lib/source-link.ts +201 -0
  706. package/src/lib/spec-files.test.ts +24 -0
  707. package/src/lib/spec-files.ts +23 -0
  708. package/src/lib/table-directive.test.ts +139 -0
  709. package/src/lib/table-directive.ts +106 -0
  710. package/src/lib/tech.test.ts +120 -0
  711. package/src/lib/tech.ts +421 -0
  712. package/src/lib/terms.test.ts +67 -0
  713. package/src/lib/terms.ts +179 -0
  714. package/src/lib/wire-problems.test.ts +263 -0
  715. package/src/lib/wire-problems.ts +281 -0
  716. package/src/likec4/C4View.tsx +55 -0
  717. package/src/likec4/CanvasBridge.tsx +187 -0
  718. package/src/likec4/FlowView.tsx +235 -0
  719. package/src/likec4/InteractiveView.tsx +144 -0
  720. package/src/likec4/canvas-viewport.test.ts +149 -0
  721. package/src/likec4/canvas-viewport.ts +135 -0
  722. package/src/likec4/flow-edges.test.ts +51 -0
  723. package/src/likec4/flow-edges.ts +89 -0
  724. package/src/likec4/frame-theme.test.ts +76 -0
  725. package/src/likec4/frame-theme.ts +101 -0
  726. package/src/likec4/highlight-css.test.ts +49 -0
  727. package/src/likec4/highlight-css.ts +52 -0
  728. package/src/likec4/ids.test.ts +151 -0
  729. package/src/likec4/ids.ts +115 -0
  730. package/src/likec4/levels.tsx +70 -0
  731. package/src/likec4/mapping.test.ts +66 -0
  732. package/src/likec4/mapping.ts +54 -0
  733. package/src/likec4/reserved.json +141 -0
  734. package/src/likec4/view-index.ts +78 -0
  735. package/src/main.tsx +13 -0
  736. package/src/map/ContextMapGraph.tsx +274 -0
  737. package/src/map/ContextNode.tsx +70 -0
  738. package/src/map/FloatingEdge.tsx +86 -0
  739. package/src/merge.test.ts +1038 -0
  740. package/src/merge.ts +1004 -0
  741. package/src/pages/AdrDetail.tsx +242 -0
  742. package/src/pages/AdrIndex.tsx +114 -0
  743. package/src/pages/AggregatePage.tsx +727 -0
  744. package/src/pages/BlockPage.tsx +478 -0
  745. package/src/pages/CatalogFailure.tsx +111 -0
  746. package/src/pages/Changes.tsx +313 -0
  747. package/src/pages/ContextMap.tsx +534 -0
  748. package/src/pages/ContextPage.tsx +346 -0
  749. package/src/pages/EnumPage.tsx +282 -0
  750. package/src/pages/EventPage.tsx +686 -0
  751. package/src/pages/ExternalPage.tsx +204 -0
  752. package/src/pages/FlowDetail.tsx +663 -0
  753. package/src/pages/FlowIndex.tsx +303 -0
  754. package/src/pages/GraphPage.tsx +217 -0
  755. package/src/pages/Language.tsx +257 -0
  756. package/src/pages/ModulePage.tsx +366 -0
  757. package/src/pages/NotFound.tsx +54 -0
  758. package/src/pages/Overview.tsx +344 -0
  759. package/src/pages/Problems.tsx +360 -0
  760. package/src/pages/RegistryIndex.tsx +118 -0
  761. package/src/pages/ServicePage.tsx +746 -0
  762. package/src/pages/Settings.tsx +975 -0
  763. package/src/pages/StorePage.tsx +114 -0
  764. package/src/peek/PeekCard.test.ts +35 -0
  765. package/src/peek/PeekCard.tsx +118 -0
  766. package/src/peek/PeekLayer.tsx +147 -0
  767. package/src/peek/model.test.ts +156 -0
  768. package/src/peek/model.ts +253 -0
  769. package/src/routes.test.ts +222 -0
  770. package/src/routes.ts +449 -0
  771. package/src/selection/DetailPanel.tsx +1407 -0
  772. package/src/selection/hash.test.ts +73 -0
  773. package/src/selection/hash.ts +58 -0
  774. package/src/selection/model.test.ts +199 -0
  775. package/src/selection/model.ts +473 -0
  776. package/src/selection/pages.test.ts +115 -0
  777. package/src/selection/pages.ts +268 -0
  778. package/src/selection/store.test.ts +62 -0
  779. package/src/selection/store.ts +46 -0
  780. package/src/selection/sync.tsx +99 -0
  781. package/src/table/ColumnsMenu.tsx +72 -0
  782. package/src/table/DataTable.tsx +780 -0
  783. package/src/table/Facets.tsx +54 -0
  784. package/src/table/GroupMenu.tsx +69 -0
  785. package/src/table/Toolbar.tsx +190 -0
  786. package/src/table/cells.tsx +117 -0
  787. package/src/table/compare.test.ts +117 -0
  788. package/src/table/compare.ts +196 -0
  789. package/src/table/export.test.ts +88 -0
  790. package/src/table/export.ts +66 -0
  791. package/src/table/facet-groups.test.ts +74 -0
  792. package/src/table/facet-groups.ts +56 -0
  793. package/src/table/grouping.test.ts +84 -0
  794. package/src/table/grouping.ts +82 -0
  795. package/src/table/infer.test.ts +57 -0
  796. package/src/table/infer.ts +50 -0
  797. package/src/table/persist.test.ts +76 -0
  798. package/src/table/persist.ts +112 -0
  799. package/src/table/sort-url.test.ts +75 -0
  800. package/src/table/sort-url.ts +52 -0
  801. package/src/table/types.ts +80 -0
  802. package/src/table/useTableState.ts +220 -0
  803. package/src/test-catalog.ts +7 -0
  804. package/src/testing/estate/data__catalog.json +2847 -0
  805. package/src/testing/estate/data__flows.json +1724 -0
  806. package/src/testing/estate/examples__auth__portolan__api.json +212 -0
  807. package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
  808. package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
  809. package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
  810. package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
  811. package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
  812. package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
  813. package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
  814. package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
  815. package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
  816. package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
  817. package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
  818. package/src/testing/estate.ts +49 -0
  819. package/src/trail/Trail.tsx +81 -0
  820. package/src/trail/model.test.ts +157 -0
  821. package/src/trail/model.ts +189 -0
  822. package/src/trail/record.tsx +32 -0
  823. package/src/trail/store.test.ts +66 -0
  824. package/src/trail/store.ts +102 -0
  825. package/tsconfig.json +35 -0
  826. package/vite.config.ts +171 -0
@@ -0,0 +1,338 @@
1
+ """Project-call traversal used to turn Django handlers into useful flows."""
2
+
3
+ import ast
4
+ import os
5
+ import sys
6
+ import unittest
7
+ from types import SimpleNamespace
8
+
9
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
10
+ sys.path.insert(1, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "pyplugin"))
11
+
12
+ import flows # noqa: E402
13
+ from domain import ModelDef # noqa: E402
14
+ from source import Module, node_name, read_imports # noqa: E402
15
+
16
+
17
+ def module(name, source):
18
+ found = Module(path="/" + name + ".py", rel=name + ".py", dotted=name, tree=ast.parse(source))
19
+ found.imports = read_imports(found)
20
+ return found
21
+
22
+
23
+ class Project:
24
+ def __init__(self, modules):
25
+ self.modules = {item.dotted: item for item in modules}
26
+
27
+ def module(self, name):
28
+ return self.modules.get(name) or self.modules.get(name + ".__init__")
29
+
30
+ def resolve(self, owner, name):
31
+ imported = owner.imports.get(name)
32
+ if imported is None:
33
+ if any(node_name(node) == name for node in owner.tree.body):
34
+ return owner, name
35
+ return None
36
+ target = self.module(imported.module)
37
+ if target is None:
38
+ return None
39
+ return target, name if imported.name == "*" else imported.name
40
+
41
+
42
+ class Warnings:
43
+ def warn(self, *_args):
44
+ pass
45
+
46
+
47
+ class FlowCalls(unittest.TestCase):
48
+ def test_self_and_imported_helpers_reach_http_and_cycles_are_cut(self):
49
+ views = module(
50
+ "app.views",
51
+ """
52
+ from app.helpers import send
53
+
54
+ class Tools:
55
+ @staticmethod
56
+ def deliver():
57
+ return send()
58
+
59
+ class BaseView:
60
+ @classmethod
61
+ def dispatch(cls):
62
+ return Tools.deliver()
63
+
64
+ class StatusView(BaseView):
65
+ def get(self, request):
66
+ return self.forward()
67
+
68
+ def forward(self):
69
+ return super().dispatch()
70
+ """,
71
+ )
72
+ helpers = module(
73
+ "app.helpers",
74
+ """
75
+ import os
76
+ from requests import post
77
+
78
+ BASE = os.environ.get("PEER_URL", "https://peer.example")
79
+
80
+ def send():
81
+ post(BASE + "/v1/status")
82
+ again()
83
+
84
+ def again():
85
+ send()
86
+ """,
87
+ )
88
+ project = Project([views, helpers])
89
+ reader = flows.FlowReader(
90
+ flows.Options(context="app", svc_id="app.web", service="web", store=""),
91
+ project,
92
+ [],
93
+ [],
94
+ [],
95
+ [],
96
+ {},
97
+ lambda path: path,
98
+ Warnings(),
99
+ )
100
+ view = next(node for node in views.classes() if node.name == "StatusView")
101
+ handler = next(node for node in view.body if getattr(node, "name", "") == "get")
102
+ endpoint = SimpleNamespace(id="status_get", module=views, node=handler, view="StatusView", doc="", use_cases=[])
103
+
104
+ flow = reader.endpoint_flow(endpoint)
105
+
106
+ self.assertEqual([step["label"] for step in flow["steps"]], ["status_get", "POST /v1/status"])
107
+ self.assertEqual(flow["steps"][1]["status"], "unresolved")
108
+ self.assertEqual(flow["steps"][1]["line"], "app.helpers.py:8")
109
+ self.assertEqual([lane["label"] for lane in flow["participants"] if lane.get("label")], ["peer.example"])
110
+
111
+ def test_one_sided_condition_keeps_an_explicit_otherwise_branch(self):
112
+ views = module(
113
+ "app.views",
114
+ """
115
+ from requests import get
116
+
117
+ class StatusView:
118
+ def get(self, request):
119
+ if request.ready:
120
+ get("https://peer.example/ready")
121
+ """,
122
+ )
123
+ project = Project([views])
124
+ reader = flows.FlowReader(
125
+ flows.Options(context="app", svc_id="app.web", service="web", store=""),
126
+ project,
127
+ [],
128
+ [],
129
+ [],
130
+ [],
131
+ {},
132
+ lambda path: path,
133
+ Warnings(),
134
+ )
135
+ view = views.classes()[0]
136
+ handler = next(node for node in view.body if getattr(node, "name", "") == "get")
137
+ endpoint = SimpleNamespace(id="status_get", module=views, node=handler, view="StatusView", doc="", use_cases=[])
138
+
139
+ flow = reader.endpoint_flow(endpoint)
140
+
141
+ alternative = flow["steps"][1]
142
+ self.assertEqual([branch["title"] for branch in alternative["branches"]], ["request.ready", "otherwise"])
143
+ self.assertEqual(alternative["branches"][1]["steps"], [])
144
+
145
+ def test_routed_models_are_visible_without_an_aggregate(self):
146
+ models = module(
147
+ "app.models",
148
+ """
149
+ class Widget:
150
+ pass
151
+ """,
152
+ )
153
+ views = module(
154
+ "app.views",
155
+ """
156
+ from .models import Widget
157
+
158
+ class WidgetView:
159
+ def get(self, request):
160
+ return Widget.objects.get(id=request.id)
161
+ """,
162
+ )
163
+ app = SimpleNamespace(dotted="app")
164
+ model = ModelDef(name="Widget", node=models.classes()[0], module=models, app=app)
165
+ project = Project([models, views])
166
+ reader = flows.FlowReader(
167
+ flows.Options(context="app", svc_id="app.web", service="web", store=""),
168
+ project,
169
+ [],
170
+ [model],
171
+ [],
172
+ [],
173
+ {},
174
+ lambda path: path,
175
+ Warnings(),
176
+ )
177
+ view = views.classes()[0]
178
+ handler = next(node for node in view.body if getattr(node, "name", "") == "get")
179
+ endpoint = SimpleNamespace(id="widget_get", module=views, node=handler, view="WidgetView", doc="", use_cases=[])
180
+
181
+ flow = reader.endpoint_flow(endpoint)
182
+
183
+ self.assertEqual([step["label"] for step in flow["steps"]], ["widget_get", "Widget.objects.get"])
184
+
185
+ def test_configured_wrappers_are_semantic_and_technical_ones_are_counted(self):
186
+ views = module(
187
+ "app.views",
188
+ """
189
+ from app.helpers import record_log, send_mail
190
+
191
+ class StatusView:
192
+ def post(self, request):
193
+ record_log("started")
194
+ send_mail(request.email)
195
+ record_log("finished")
196
+ """,
197
+ )
198
+ helpers = module(
199
+ "app.helpers",
200
+ """
201
+ from requests import post
202
+
203
+ def record_log(message):
204
+ post("https://logger.example/logs", json={"message": message})
205
+
206
+ def send_mail(email):
207
+ post("https://mailer.example/messages", json={"email": email})
208
+ """,
209
+ )
210
+ reader = flows.FlowReader(
211
+ flows.Options(
212
+ context="app",
213
+ svc_id="app.web",
214
+ service="web",
215
+ store="",
216
+ flow_wrappers={
217
+ "app.helpers.record_log": {"label": "Record application log", "technical": True},
218
+ "app.helpers.send_mail": {"label": "Send email", "target": "messaging.mailer"},
219
+ },
220
+ ),
221
+ Project([views, helpers]),
222
+ [],
223
+ [],
224
+ [],
225
+ [],
226
+ {},
227
+ lambda path: path,
228
+ Warnings(),
229
+ )
230
+ view = views.classes()[0]
231
+ handler = next(node for node in view.body if getattr(node, "name", "") == "post")
232
+ endpoint = SimpleNamespace(id="status_post", module=views, node=handler, view="StatusView", doc="", use_cases=[])
233
+
234
+ flow = reader.endpoint_flow(endpoint)
235
+
236
+ self.assertEqual(
237
+ [step["label"] for step in flow["steps"]],
238
+ ["status_post", "Record application log ×2", "Send email"],
239
+ )
240
+ self.assertEqual(flow["steps"][1]["from"], "app.web")
241
+ self.assertEqual(flow["steps"][1]["to"], "app.web")
242
+ self.assertIn("2 call sites are collapsed", flow["steps"][1]["note"])
243
+ self.assertEqual(flow["steps"][2]["to"], "messaging.mailer")
244
+ self.assertNotIn("logger.example", [lane.get("label") for lane in flow["participants"]])
245
+
246
+ def test_task_body_is_a_source_backed_continuation(self):
247
+ tasks = module(
248
+ "app.tasks",
249
+ """
250
+ from celery import shared_task
251
+ from requests import post
252
+
253
+ class Refresher:
254
+ def run(self):
255
+ for item in self.items():
256
+ pass
257
+
258
+ def items(self):
259
+ return post("https://peer.example/refresh")
260
+
261
+ @shared_task(name="app.refresh")
262
+ def refresh():
263
+ refresher = Refresher()
264
+ refresher.run()
265
+ """,
266
+ )
267
+ reader = flows.FlowReader(
268
+ flows.Options(context="app", svc_id="app.web", service="web", store=""),
269
+ Project([tasks]),
270
+ [],
271
+ [],
272
+ [],
273
+ [],
274
+ {},
275
+ lambda path: path,
276
+ Warnings(),
277
+ )
278
+
279
+ found = reader.task_flows()
280
+
281
+ self.assertEqual(len(found), 1)
282
+ self.assertEqual(found[0]["entrypoint"], "python:app.tasks:refresh")
283
+ self.assertEqual([step["label"] for step in found[0]["steps"]], ["POST /refresh"])
284
+
285
+ def test_kafka_factory_inside_a_task_is_a_message_handoff(self):
286
+ settings = module(
287
+ "app.settings",
288
+ """
289
+ import os
290
+ TOPIC = os.environ.get("TOPIC", "records.changed")
291
+ """,
292
+ )
293
+ tasks = module(
294
+ "app.tasks",
295
+ """
296
+ from celery import shared_task
297
+ from confluent_kafka import Producer
298
+ from django.conf import settings
299
+
300
+ class Factory:
301
+ @classmethod
302
+ def get(cls):
303
+ config = {"bootstrap.servers": "kafka:9092", "enable.idempotence": True}
304
+ return Producer(config)
305
+
306
+ def publish(payload):
307
+ producer = Factory.get()
308
+ producer.produce(topic=settings.TOPIC, value=payload, headers=[("schema", b"v1")])
309
+
310
+ @shared_task(name="app.deliver")
311
+ def deliver(payload):
312
+ publish(payload)
313
+ """,
314
+ )
315
+ reader = flows.FlowReader(
316
+ flows.Options(context="app", svc_id="app.web", service="web", store="", settings="app.settings"),
317
+ Project([tasks, settings]),
318
+ [],
319
+ [],
320
+ [],
321
+ [],
322
+ {},
323
+ lambda path: path,
324
+ Warnings(),
325
+ )
326
+
327
+ found = reader.task_flows()
328
+
329
+ self.assertEqual(len(found), 1)
330
+ step = found[0]["steps"][0]
331
+ self.assertEqual(step["to"], "kafka-records-changed")
332
+ self.assertEqual(step["handoff"], {"kind": "message", "transport": "kafka", "channel": "records.changed", "message": "payload", "direction": "send"})
333
+ self.assertIn("idempotence: enabled", step["note"])
334
+ self.assertEqual(step["line"], "app.tasks.py:14")
335
+
336
+
337
+ if __name__ == "__main__":
338
+ unittest.main()
@@ -0,0 +1,42 @@
1
+ """How a name in the source becomes an id in the catalog.
2
+
3
+ The spelling itself lives in `pyplugin/names.py`, shared with every Python
4
+ plugin; what is here is the Django half - the plural an application is named
5
+ in, and the ids the fragment composes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from names import camel, slug, title # noqa: F401
11
+
12
+
13
+ def pascal(name: str) -> str:
14
+ """A directory name in PascalCase, which is what its root model is called."""
15
+ return camel(name)
16
+
17
+
18
+ def singular(name: str) -> str:
19
+ """invoices -> invoice: a Django app is named for many of the thing it holds."""
20
+ if name.endswith("ies") and len(name) > 3:
21
+ return name[:-3] + "y"
22
+ if name.endswith("sses") or name.endswith("shes") or name.endswith("ches") or name.endswith("xes"):
23
+ return name[:-2]
24
+ if name.endswith("s") and not name.endswith("ss"):
25
+ return name[:-1]
26
+ return name
27
+
28
+
29
+ def service_id(context: str, service: str) -> str:
30
+ return context + "." + service
31
+
32
+
33
+ def aggregate_id(service: str, aggregate: str) -> str:
34
+ return service + "." + aggregate
35
+
36
+
37
+ def block_id(aggregate: str, block: str) -> str:
38
+ return aggregate + "." + block
39
+
40
+
41
+ def event_id(aggregate: str, name: str) -> str:
42
+ return aggregate + "." + name
@@ -0,0 +1,30 @@
1
+ import os
2
+ import sys
3
+ import unittest
4
+
5
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
6
+ sys.path.insert(1, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "pyplugin"))
7
+
8
+ import ids # noqa: E402
9
+
10
+
11
+ class Ids(unittest.TestCase):
12
+ def test_slug_is_the_one_extract_go_makes(self):
13
+ self.assertEqual(ids.slug("PriceList"), "price-list")
14
+ self.assertEqual(ids.slug("Address"), "address")
15
+ self.assertEqual(ids.slug("ID"), "id")
16
+ self.assertEqual(ids.slug("email.Address"), "email-address")
17
+ self.assertEqual(ids.slug("InvoiceLine"), "invoice-line")
18
+
19
+ def test_camel_and_title(self):
20
+ self.assertEqual(ids.camel("issue_invoice"), "IssueInvoice")
21
+ self.assertEqual(ids.title("price_list"), "Price List")
22
+
23
+ def test_an_application_is_named_for_many_of_the_thing_it_holds(self):
24
+ self.assertEqual(ids.singular("invoices"), "invoice")
25
+ self.assertEqual(ids.singular("policies"), "policy")
26
+ self.assertEqual(ids.singular("billing"), "billing")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ unittest.main()
@@ -0,0 +1,222 @@
1
+ """The root's states, read off the table the code keeps.
2
+
3
+ Never off the branches of the methods: the table is the claim and the methods
4
+ are held to it. In Django the table is either a `TRANSITIONS` mapping on the
5
+ model, beside the `TextChoices` that names the states, or the `@transition`
6
+ decorators of django-fsm, which are a table written one edge at a time. An edge
7
+ nothing makes, a move into a state the table lacks, and a status assigned
8
+ anywhere but a mover are each reported.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import ast
14
+ from dataclasses import dataclass, field as dc_field
15
+ from typing import Dict, List, Optional, Tuple
16
+
17
+ import catalog
18
+ from domain import Aggregate, ModelDef
19
+ from source import assigned, bases, const_str, dotted, inner_class, keyword, methods
20
+
21
+
22
+ @dataclass
23
+ class Move:
24
+ to: str
25
+ on: str
26
+ emits: str
27
+ source: str
28
+ sources: List[str] = dc_field(default_factory=list) # the states it may be made from, when the decorator says
29
+
30
+
31
+ def choices_of(model: ModelDef, name: str) -> Dict[str, str]:
32
+ """A TextChoices class, as member name to the value stored in the column."""
33
+ node = inner_class(model.node, name)
34
+ if node is None:
35
+ for other in model.module.classes():
36
+ if other.name == name:
37
+ node = other
38
+ break
39
+ if node is None or not any(b.split(".")[-1].endswith("Choices") for b in bases(node)):
40
+ return {}
41
+ out = {}
42
+ for member, value, _ in assigned(node):
43
+ if isinstance(value, ast.Constant) and isinstance(value.value, str):
44
+ out[member] = value.value
45
+ elif isinstance(value, ast.Tuple) and value.elts and isinstance(value.elts[0], ast.Constant):
46
+ out[member] = value.elts[0].value
47
+ return out
48
+
49
+
50
+ def state_of(node: ast.AST, choices: Dict[str, Dict[str, str]]) -> str:
51
+ """`Status.DRAFT` or `"draft"`, either way the value in the column."""
52
+ literal = const_str(node)
53
+ if literal:
54
+ return literal
55
+ name = dotted(node)
56
+ if "." in name:
57
+ holder, member = name.rsplit(".", 1)
58
+ table = choices.get(holder.split(".")[-1], {})
59
+ if member in table:
60
+ return table[member]
61
+ return ""
62
+
63
+
64
+ def status_field(model: ModelDef) -> Optional[str]:
65
+ """The field whose choices are the states. `status` by convention, or the
66
+ one field whose choices name a Choices class."""
67
+ for f in model.fields:
68
+ value = keyword(f.call, "choices")
69
+ if value is not None and dotted(value):
70
+ return f.name
71
+ return "status" if model.field("status") else None
72
+
73
+
74
+ def choice_tables(model: ModelDef) -> Dict[str, Dict[str, str]]:
75
+ out = {}
76
+ for node in list(model.node.body) + list(model.module.tree.body):
77
+ if isinstance(node, ast.ClassDef) and any(b.split(".")[-1].endswith("Choices") for b in bases(node)):
78
+ out[node.name] = choices_of(model, node.name)
79
+ return out
80
+
81
+
82
+ def declared_table(model: ModelDef, choices: Dict[str, Dict[str, str]]) -> Dict[str, List[str]]:
83
+ """`TRANSITIONS = {Status.DRAFT: [Status.ISSUED], ...}`, in the order written."""
84
+ for name, value, _ in assigned(model.node):
85
+ if name != "TRANSITIONS" or not isinstance(value, ast.Dict):
86
+ continue
87
+ table: Dict[str, List[str]] = {}
88
+ for key, targets in zip(value.keys, value.values):
89
+ state = state_of(key, choices)
90
+ if not state:
91
+ continue
92
+ listed = []
93
+ if isinstance(targets, (ast.List, ast.Tuple, ast.Set)):
94
+ for item in targets.elts:
95
+ to = state_of(item, choices)
96
+ if to:
97
+ listed.append(to)
98
+ table[state] = listed
99
+ return table
100
+ return {}
101
+
102
+
103
+ def emits_of(node: ast.AST, events: Dict[str, object]) -> str:
104
+ """The event a mover hands back, as its return annotation names it, by the
105
+ id the catalog knows it under."""
106
+ annotation = getattr(node, "returns", None)
107
+ if annotation is None:
108
+ return ""
109
+ found = ""
110
+ for name in ast.walk(annotation):
111
+ if isinstance(name, ast.Name) and name.id in events:
112
+ found = events[name.id].id
113
+ elif isinstance(name, ast.Constant) and isinstance(name.value, str) and name.value in events:
114
+ found = events[name.value].id
115
+ return found
116
+
117
+
118
+ def movers(model: ModelDef, status: str, choices: Dict[str, Dict[str, str]], events: Dict[str, object]) -> Tuple[List[Move], List[Tuple[str, str]]]:
119
+ """Every method that moves the root: the ones assigning the status field,
120
+ and the ones django-fsm decorates. Also what they assign that no table
121
+ knows, which the caller reports."""
122
+ out: List[Move] = []
123
+ loose: List[Tuple[str, str]] = []
124
+ for node in methods(model.node):
125
+ if node.name.startswith("_"):
126
+ continue
127
+ emits = emits_of(node, events)
128
+ where = model.module.where(node)
129
+ decorator = None
130
+ for dec in getattr(node, "decorator_list", []):
131
+ if dotted(dec).split(".")[-1] == "transition" and isinstance(dec, ast.Call):
132
+ decorator = dec
133
+ if decorator is not None:
134
+ target = state_of(keyword(decorator, "target"), choices)
135
+ source = keyword(decorator, "source")
136
+ froms = []
137
+ if isinstance(source, (ast.List, ast.Tuple, ast.Set)):
138
+ froms = [state_of(item, choices) for item in source.elts]
139
+ elif source is not None:
140
+ one = state_of(source, choices)
141
+ froms = [one] if one else []
142
+ if target:
143
+ out.append(Move(to=target, on=node.name, emits=emits, source=where, sources=[f for f in froms if f]))
144
+ continue
145
+ for assignment in ast.walk(node):
146
+ if not isinstance(assignment, ast.Assign):
147
+ continue
148
+ for target in assignment.targets:
149
+ if not isinstance(target, ast.Attribute) or target.attr != status:
150
+ continue
151
+ if dotted(target.value) != "self":
152
+ continue
153
+ to = state_of(assignment.value, choices)
154
+ if to:
155
+ out.append(Move(to=to, on=node.name, emits=emits, source=model.module.where(assignment)))
156
+ else:
157
+ loose.append((node.name, model.module.where(assignment)))
158
+ return out, loose
159
+
160
+
161
+ def read(agg: Aggregate, events: Dict[str, object], b) -> Optional[Dict[str, object]]:
162
+ model = agg.root
163
+ status = status_field(model)
164
+ if status is None:
165
+ return None
166
+ choices = choice_tables(model)
167
+ table = declared_table(model, choices)
168
+ moves, loose = movers(model, status, choices, events)
169
+
170
+ if not table:
171
+ if not moves:
172
+ return None
173
+ # django-fsm writes the table one edge at a time; a model that only
174
+ # assigns its status has no table at all, and a lifecycle read off the
175
+ # assignments would be the branches talking.
176
+ if not any(m.sources for m in moves):
177
+ b.warn(agg.id, "%s moves its %s but declares no TRANSITIONS: the table is the claim, the methods are held to it" % (model.name, status))
178
+ return None
179
+ table = {}
180
+ for move in moves:
181
+ for source in move.sources:
182
+ table.setdefault(source, [])
183
+ if move.to not in table[source]:
184
+ table[source].append(move.to)
185
+ table.setdefault(move.to, [])
186
+
187
+ states = list(table.keys())
188
+ # The order the Choices class writes them in is the order a reader expects,
189
+ # and the first is where a new root starts.
190
+ declared = next(iter(choices.values()), {})
191
+ if declared:
192
+ ordered = [value for value in declared.values() if value in table or any(value in tos for tos in table.values())]
193
+ for state in states:
194
+ if state not in ordered:
195
+ ordered.append(state)
196
+ states = ordered
197
+ for move in moves:
198
+ if move.to not in states:
199
+ states.append(move.to)
200
+ b.warn(agg.id, "%s moves to %r, which the table does not list" % (move.on, move.to))
201
+
202
+ transitions = []
203
+ made = set()
204
+ for move in moves:
205
+ froms = [state for state, tos in table.items() if move.to in tos]
206
+ if move.sources:
207
+ froms = [f for f in move.sources if f in states] or move.sources
208
+ if not froms:
209
+ continue
210
+ for state in froms:
211
+ transitions.append(catalog.transition(state, move.to, move.on, move.emits, move.source))
212
+ made.add((state, move.to))
213
+ for state, tos in table.items():
214
+ for to in tos:
215
+ if (state, to) not in made:
216
+ b.warn(agg.id, "the table has %s -> %s and no method of %s makes it" % (state, to, model.name))
217
+ for name, where in loose:
218
+ b.warn(agg.id, "%s assigns %s to something the states do not name (%s)" % (name, status, where))
219
+
220
+ if not transitions:
221
+ return None
222
+ return {"states": states, "transitions": transitions}