@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,419 @@
1
+ // Runs a portolan generator plugin and returns what it wants written.
2
+ //
3
+ // A plugin never touches the tree (portolan.0001). It is handed a request on stdin and answers
4
+ // with named files on stdout; writing them - and refusing a name that climbs
5
+ // out of the output directory - is this file's job. That split is what lets the
6
+ // same plugin run as a sandboxed wasm module with no directory preopened at
7
+ // all, and it is why `--check` can compare a render against disk without the
8
+ // plugin knowing there is a disk.
9
+
10
+ import { spawn } from "node:child_process";
11
+ import { createHash } from "node:crypto";
12
+ import {
13
+ mkdirSync,
14
+ readFileSync,
15
+ rmSync,
16
+ statSync,
17
+ writeFileSync,
18
+ } from "node:fs";
19
+ import { mkdtemp } from "node:fs/promises";
20
+ import { tmpdir } from "node:os";
21
+ import { isAbsolute, join } from "node:path";
22
+ import { Worker } from "node:worker_threads";
23
+
24
+ const PORTOLAN_VERSION = "0.1.0";
25
+ const HOST_LIMITS = Object.freeze({
26
+ requestBytes: 4 * 1024 * 1024,
27
+ responseBytes: 16 * 1024 * 1024,
28
+ stderrBytes: 1024 * 1024,
29
+ wasmBytes: 64 * 1024 * 1024,
30
+ timeoutMs: 120_000,
31
+ });
32
+
33
+ /**
34
+ * Runs one plugin.
35
+ *
36
+ * @param {{name: string, wasm?: {url: string, sha256?: string}, process?: {command: string, args?: string[]}}} plugin
37
+ * @param {unknown} request
38
+ * @returns {Promise<{files: {name: string, contents: string}[], describe?: object}>}
39
+ */
40
+ export async function runPlugin(plugin, request, requestedLimits = {}) {
41
+ const limits = lowerLimits(requestedLimits);
42
+ let payload;
43
+ try {
44
+ payload = JSON.stringify(request);
45
+ } catch (cause) {
46
+ throw new Error(`plugin ${plugin.name}: request is not JSON-serializable: ${cause.message}`);
47
+ }
48
+ if (typeof payload !== "string") {
49
+ throw new Error(`plugin ${plugin.name}: request is not a JSON value`);
50
+ }
51
+ if (Buffer.byteLength(payload) > limits.requestBytes) {
52
+ throw new Error(`plugin ${plugin.name}: request exceeds ${limits.requestBytes} bytes`);
53
+ }
54
+
55
+ const result = plugin.wasm
56
+ ? await runWasm(plugin, payload, limits)
57
+ : await runProcess(plugin, payload, limits);
58
+ if (result.stderr) process.stderr.write(result.stderr);
59
+
60
+ let response;
61
+ try {
62
+ response = JSON.parse(result.stdout);
63
+ } catch (cause) {
64
+ throw new Error(
65
+ `plugin ${plugin.name} did not answer with JSON: ${cause.message}`,
66
+ );
67
+ }
68
+
69
+ return { ...validateResponse(plugin.name, response), warnings: warningsIn(result.stderr) };
70
+ }
71
+
72
+ /**
73
+ * The warnings a plugin wrote to stderr, one per `warning: ` line, without the
74
+ * prefix.
75
+ *
76
+ * They are not part of the response - the protocol has no advisory property a
77
+ * caller may accidentally ignore - but for a service whose tree the extractor
78
+ * only half understood they are the whole story: which packages were not read
79
+ * as aggregates, which record was left out and why. Printed once as they
80
+ * arrive, they scroll off; kept beside the step in the build report, they are
81
+ * the list of what to fix.
82
+ *
83
+ * @param {string} stderr
84
+ * @returns {string[]}
85
+ */
86
+ export function warningsIn(stderr) {
87
+ const warnings = [];
88
+ for (const line of String(stderr ?? "").split(/\r?\n/)) {
89
+ const warning = line.startsWith("warning: ") ? line.slice("warning: ".length).trim() : "";
90
+ if (warning) warnings.push(warning);
91
+ }
92
+ return warnings;
93
+ }
94
+
95
+ function lowerLimits(requested) {
96
+ const limits = {};
97
+ for (const [name, maximum] of Object.entries(HOST_LIMITS)) {
98
+ const value = requested[name];
99
+ limits[name] = Number.isSafeInteger(value) && value > 0
100
+ ? Math.min(value, maximum)
101
+ : maximum;
102
+ }
103
+ return limits;
104
+ }
105
+
106
+ /** Reject malformed or over-broad responses before they reach the filesystem. */
107
+ export function validateResponse(name, response) {
108
+ if (!response || typeof response !== "object" || Array.isArray(response)) {
109
+ throw new Error(`plugin ${name} answered with a non-object response`);
110
+ }
111
+ const unexpected = Object.keys(response).filter((key) => !["files", "describe"].includes(key));
112
+ if (unexpected.length > 0) {
113
+ throw new Error(`plugin ${name} answered with unsupported properties: ${unexpected.join(", ")}`);
114
+ }
115
+ if (!Array.isArray(response.files)) {
116
+ throw new Error(`plugin ${name} answered without a files list`);
117
+ }
118
+
119
+ const seen = new Set();
120
+ const files = response.files.map((file, index) => {
121
+ if (!file || typeof file !== "object" || Array.isArray(file)) {
122
+ throw new Error(`plugin ${name}: files[${index}] is not an object`);
123
+ }
124
+ const keys = Object.keys(file);
125
+ if (keys.some((key) => !["name", "contents"].includes(key))) {
126
+ throw new Error(`plugin ${name}: files[${index}] has unsupported properties`);
127
+ }
128
+ if (typeof file.name !== "string" || !safeFileName(file.name)) {
129
+ throw new Error(`plugin ${name}: files[${index}] has an unsafe name`);
130
+ }
131
+ if (typeof file.contents !== "string") {
132
+ throw new Error(`plugin ${name}: files[${index}].contents is not a string`);
133
+ }
134
+ if (seen.has(file.name)) {
135
+ throw new Error(`plugin ${name}: duplicate output file ${file.name}`);
136
+ }
137
+ seen.add(file.name);
138
+ return { name: file.name, contents: file.contents };
139
+ });
140
+
141
+ if (response.describe !== undefined) validateDescriptor(name, response.describe);
142
+ return { files, ...(response.describe === undefined ? {} : { describe: response.describe }) };
143
+ }
144
+
145
+ function safeFileName(name) {
146
+ if (!name || name.includes("\0") || isAbsolute(name) || /^[A-Za-z]:[\\/]/.test(name)) return false;
147
+ const parts = name.split(/[\\/]/);
148
+ return parts.every((part) => part !== "" && part !== "." && part !== "..");
149
+ }
150
+
151
+ function validateDescriptor(pluginName, descriptor) {
152
+ if (!descriptor || typeof descriptor !== "object" || Array.isArray(descriptor)) {
153
+ throw new Error(`plugin ${pluginName}: describe is not an object`);
154
+ }
155
+ if (typeof descriptor.name !== "string" || descriptor.name.length === 0) {
156
+ throw new Error(`plugin ${pluginName}: describe.name is missing`);
157
+ }
158
+ if (typeof descriptor.summary !== "string") {
159
+ throw new Error(`plugin ${pluginName}: describe.summary is missing`);
160
+ }
161
+ if (!Array.isArray(descriptor.phases) || descriptor.phases.some((phase) => !["extract", "verify", "generate"].includes(phase))) {
162
+ throw new Error(`plugin ${pluginName}: describe.phases is invalid`);
163
+ }
164
+ if (!descriptor.options || typeof descriptor.options !== "object" || Array.isArray(descriptor.options)) {
165
+ throw new Error(`plugin ${pluginName}: describe.options is invalid`);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Asks a plugin what it is and what it can be told.
171
+ *
172
+ * The answer is a descriptor with a JSON Schema for its options, which is what
173
+ * schema/portolan.schema.json is composed from. A plugin written against an
174
+ * older protocol answers without one; that comes back as null rather than as
175
+ * an error, because a manifest naming it is not wrong, it is just a manifest
176
+ * nothing can check.
177
+ *
178
+ * @param {{name: string, wasm?: {url: string, sha256?: string}, process?: {command: string, args?: string[]}}} plugin
179
+ */
180
+ export async function describePlugin(plugin) {
181
+ const { describe } = await runPlugin(plugin, {
182
+ portolanVersion: PORTOLAN_VERSION,
183
+ kind: "describe",
184
+ });
185
+
186
+ return describe ?? null;
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // wasm
191
+ //
192
+ // Node's WASI takes file descriptors rather than pipes, so the request and the
193
+ // response go through a scratch directory. That is the only place the sandbox
194
+ // leaks, and it leaks into a temporary directory the plugin is not told about:
195
+ // `preopens` stays empty, so the module cannot open a path even if it tries.
196
+ // ---------------------------------------------------------------------------
197
+
198
+ async function runWasm(plugin, payload, limits) {
199
+ const bytes = await loadWasm(plugin, limits);
200
+
201
+ const dir = await mkdtemp(join(tmpdir(), "portolan-plugin-"));
202
+ const inPath = join(dir, "request.json");
203
+ const outPath = join(dir, "response.json");
204
+ const errPath = join(dir, "stderr.txt");
205
+
206
+ writeFileSync(inPath, payload);
207
+ writeFileSync(outPath, "");
208
+ writeFileSync(errPath, "");
209
+
210
+ try {
211
+ const worker = new Worker(new URL("./plugin-wasm-worker.mjs", import.meta.url), {
212
+ workerData: { name: plugin.name, bytes, inPath, outPath, errPath },
213
+ });
214
+ const code = await new Promise((resolve, reject) => {
215
+ let settled = false;
216
+ const finish = (cause, value) => {
217
+ if (settled) return;
218
+ settled = true;
219
+ clearTimeout(timer);
220
+ cause ? reject(cause) : resolve(value);
221
+ };
222
+ const timer = setTimeout(async () => {
223
+ if (settled) return;
224
+ settled = true;
225
+ clearTimeout(timer);
226
+ try {
227
+ await worker.terminate();
228
+ } finally {
229
+ reject(new Error(`plugin ${plugin.name}: timed out after ${limits.timeoutMs} ms`));
230
+ }
231
+ }, limits.timeoutMs);
232
+ worker.once("message", (message) => {
233
+ if (message.error) finish(new Error(`plugin ${plugin.name}: ${message.error}`));
234
+ else finish(null, message.code);
235
+ });
236
+ worker.once("error", (cause) => finish(new Error(`plugin ${plugin.name}: ${cause.message}`)));
237
+ worker.once("exit", (exitCode) => {
238
+ finish(new Error(`plugin ${plugin.name}: wasm worker exited ${exitCode} without a response`));
239
+ });
240
+ });
241
+
242
+ if (code !== 0 && code !== undefined) {
243
+ const message = readFileSync(errPath, "utf8").trim();
244
+ throw new Error(
245
+ `plugin ${plugin.name} exited ${code}${message ? `: ${message}` : ""}`,
246
+ );
247
+ }
248
+
249
+ if (statSync(outPath).size > limits.responseBytes) {
250
+ throw new Error(`plugin ${plugin.name}: response exceeds ${limits.responseBytes} bytes`);
251
+ }
252
+ if (statSync(errPath).size > limits.stderrBytes) {
253
+ throw new Error(`plugin ${plugin.name}: stderr exceeds ${limits.stderrBytes} bytes`);
254
+ }
255
+ const stdoutText = readFileSync(outPath, "utf8");
256
+ const stderrText = readFileSync(errPath, "utf8");
257
+ return { stdout: stdoutText, stderr: stderrText };
258
+ } finally {
259
+ rmSync(dir, { recursive: true, force: true });
260
+ }
261
+ }
262
+
263
+ async function loadWasm(plugin, limits) {
264
+ const { url, sha256 } = plugin.wasm;
265
+
266
+ if (url.startsWith("file://")) {
267
+ // Repo-relative, not a URL host. `file://plugins/x.wasm` means the file at
268
+ // plugins/x.wasm next to the manifest, which is the only thing anyone
269
+ // writing that line means.
270
+ const bytes = readFileSync(url.slice("file://".length));
271
+ // A checksum on a local file is optional: it protects a download, not a
272
+ // module that was just built from the source next to it.
273
+ if (sha256) verifyDigest(plugin.name, bytes, sha256);
274
+
275
+ verifyWasmSize(plugin.name, bytes, limits.wasmBytes);
276
+ return bytes;
277
+ }
278
+
279
+ if (!url.startsWith("https://")) {
280
+ throw new Error(
281
+ `plugin ${plugin.name}: wasm url must be https:// or file://, got ${url}`,
282
+ );
283
+ }
284
+ if (!sha256) {
285
+ throw new Error(
286
+ `plugin ${plugin.name}: a downloaded plugin must declare its sha256`,
287
+ );
288
+ }
289
+
290
+ const cacheDir = join("node_modules", ".cache", "portolan-plugins");
291
+ const cached = join(cacheDir, `${sha256}.wasm`);
292
+ try {
293
+ const bytes = readFileSync(cached);
294
+ verifyDigest(plugin.name, bytes, sha256);
295
+
296
+ verifyWasmSize(plugin.name, bytes, limits.wasmBytes);
297
+ return bytes;
298
+ } catch {
299
+ // Not cached yet, or cached wrongly. Either way, fetch it again.
300
+ }
301
+
302
+ const response = await fetch(url, {
303
+ redirect: "error",
304
+ signal: AbortSignal.timeout(limits.timeoutMs),
305
+ });
306
+ if (!response.ok) {
307
+ throw new Error(
308
+ `plugin ${plugin.name}: ${url} answered ${response.status}`,
309
+ );
310
+ }
311
+
312
+ const declaredSize = Number(response.headers.get("content-length"));
313
+ if (Number.isFinite(declaredSize) && declaredSize > limits.wasmBytes) {
314
+ throw new Error(`plugin ${plugin.name}: wasm module exceeds ${limits.wasmBytes} bytes`);
315
+ }
316
+ const chunks = [];
317
+ let size = 0;
318
+ for await (const chunk of response.body) {
319
+ size += chunk.byteLength;
320
+ if (size > limits.wasmBytes) {
321
+ throw new Error(`plugin ${plugin.name}: wasm module exceeds ${limits.wasmBytes} bytes`);
322
+ }
323
+ chunks.push(Buffer.from(chunk));
324
+ }
325
+ const bytes = Buffer.concat(chunks);
326
+ verifyWasmSize(plugin.name, bytes, limits.wasmBytes);
327
+ verifyDigest(plugin.name, bytes, sha256);
328
+
329
+ mkdirSync(cacheDir, { recursive: true });
330
+ writeFileSync(cached, bytes);
331
+
332
+ return bytes;
333
+ }
334
+
335
+ function verifyWasmSize(name, bytes, maximum) {
336
+ if (bytes.byteLength > maximum) {
337
+ throw new Error(`plugin ${name}: wasm module exceeds ${maximum} bytes`);
338
+ }
339
+ }
340
+
341
+ function verifyDigest(name, bytes, expected) {
342
+ const actual = createHash("sha256").update(bytes).digest("hex");
343
+ if (actual !== expected) {
344
+ throw new Error(
345
+ `plugin ${name}: sha256 is ${actual}, the manifest says ${expected}`,
346
+ );
347
+ }
348
+ }
349
+
350
+ // ---------------------------------------------------------------------------
351
+ // process
352
+ //
353
+ // The escape hatch for a generator that needs a toolchain - one reading Go
354
+ // source has to run `go list`, which no wasm module can. It gets the same
355
+ // protocol and none of the sandbox, which is the trade being made and the
356
+ // reason it is not the default.
357
+ // ---------------------------------------------------------------------------
358
+
359
+ function runProcess(plugin, payload, limits) {
360
+ const { command, args = [] } = plugin.process ?? {};
361
+ if (typeof command !== "string" || command.length === 0 || !Array.isArray(args) || args.some((arg) => typeof arg !== "string")) {
362
+ throw new Error(`plugin ${plugin.name}: process must declare command and string args`);
363
+ }
364
+
365
+ return new Promise((resolve, reject) => {
366
+ const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
367
+
368
+ const stdout = [];
369
+ const stderr = [];
370
+ let stdoutBytes = 0;
371
+ let stderrBytes = 0;
372
+ let settled = false;
373
+
374
+ const finish = (cause, value) => {
375
+ if (settled) return;
376
+ settled = true;
377
+ clearTimeout(timer);
378
+ cause ? reject(cause) : resolve(value);
379
+ };
380
+ const stopForLimit = (stream, limit) => {
381
+ child.kill("SIGKILL");
382
+ finish(new Error(`plugin ${plugin.name}: ${stream} exceeds ${limit} bytes`));
383
+ };
384
+
385
+ child.stdout.on("data", (chunk) => {
386
+ stdoutBytes += chunk.length;
387
+ if (stdoutBytes > limits.responseBytes) return stopForLimit("response", limits.responseBytes);
388
+ stdout.push(chunk);
389
+ });
390
+ child.stderr.on("data", (chunk) => {
391
+ stderrBytes += chunk.length;
392
+ if (stderrBytes > limits.stderrBytes) return stopForLimit("stderr", limits.stderrBytes);
393
+ stderr.push(chunk);
394
+ });
395
+
396
+ const timer = setTimeout(() => {
397
+ child.kill("SIGKILL");
398
+ finish(new Error(`plugin ${plugin.name}: timed out after ${limits.timeoutMs} ms`));
399
+ }, limits.timeoutMs);
400
+
401
+ child.on("error", (cause) => finish(new Error(`plugin ${plugin.name} did not start: ${cause.message}`)));
402
+ child.on("close", (code) => {
403
+ const stdoutText = Buffer.concat(stdout).toString("utf8");
404
+ const stderrText = Buffer.concat(stderr).toString("utf8");
405
+ if (code !== 0) {
406
+ finish(
407
+ new Error(
408
+ `plugin ${plugin.name} exited ${code}${stderrText ? `: ${stderrText.trim()}` : ""}`,
409
+ ),
410
+ );
411
+
412
+ return;
413
+ }
414
+ finish(null, { stdout: stdoutText, stderr: stderrText });
415
+ });
416
+
417
+ child.stdin.end(payload);
418
+ });
419
+ }
@@ -0,0 +1,109 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { join } from "node:path";
3
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+
6
+ import { runPlugin, validateResponse, warningsIn } from "./plugin-host.mjs";
7
+
8
+ describe("plugin response validation", () => {
9
+ it("accepts only named text files", () => {
10
+ expect(validateResponse("fixture", { files: [{ name: "nested/result.json", contents: "{}" }] })).toEqual({
11
+ files: [{ name: "nested/result.json", contents: "{}" }],
12
+ });
13
+ });
14
+
15
+ it.each(["../secret", "/tmp/result", "C:\\tmp\\result", "a/../../secret", "./result"])(
16
+ "rejects unsafe output name %s",
17
+ (name) => {
18
+ expect(() => validateResponse("fixture", { files: [{ name, contents: "" }] })).toThrow("unsafe name");
19
+ },
20
+ );
21
+
22
+ it("rejects duplicate files and removed protocol properties", () => {
23
+ expect(() =>
24
+ validateResponse("fixture", {
25
+ files: [
26
+ { name: "same", contents: "one" },
27
+ { name: "same", contents: "two" },
28
+ ],
29
+ }),
30
+ ).toThrow("duplicate output file");
31
+ expect(() => validateResponse("fixture", { files: [], diagnostics: [] })).toThrow("unsupported properties");
32
+ });
33
+ });
34
+
35
+ describe("process plugins", () => {
36
+ it("passes arguments directly and parses a bounded response", async () => {
37
+ const script = `
38
+ process.stdin.resume();
39
+ process.stdin.on("end", () => process.stdout.write(JSON.stringify({files:[{name:process.argv[1],contents:"ok"}]})));
40
+ `;
41
+ const response = await runPlugin(
42
+ { name: "fixture", process: { command: process.execPath, args: ["-e", script, "name with spaces.txt"] } },
43
+ { portolanVersion: "0.1.0" },
44
+ );
45
+ expect(response.files).toEqual([{ name: "name with spaces.txt", contents: "ok" }]);
46
+ expect(response.warnings).toEqual([]);
47
+ });
48
+
49
+ it("keeps the warnings a plugin wrote to stderr beside its response", async () => {
50
+ const script = `
51
+ process.stderr.write("warning: shop.cart: internal/domain/errors has no struct called Errors\\n");
52
+ process.stderr.write("a note that is not a warning\\n");
53
+ process.stderr.write("warning: docs/adr/0004.md: left out of the fragment\\n");
54
+ process.stdin.resume();
55
+ process.stdin.on("end", () => process.stdout.write(JSON.stringify({files:[]})));
56
+ `;
57
+ const response = await runPlugin(
58
+ { name: "fixture", process: { command: process.execPath, args: ["-e", script] } },
59
+ { portolanVersion: "0.1.0" },
60
+ );
61
+ expect(response.warnings).toEqual([
62
+ "shop.cart: internal/domain/errors has no struct called Errors",
63
+ "docs/adr/0004.md: left out of the fragment",
64
+ ]);
65
+ });
66
+
67
+ it("kills a zip-bomb-like response before buffering it", async () => {
68
+ const script = `process.stdout.write("x".repeat(4096));`;
69
+ await expect(
70
+ runPlugin(
71
+ { name: "bomb", process: { command: process.execPath, args: ["-e", script] } },
72
+ {},
73
+ { responseBytes: 1024 },
74
+ ),
75
+ ).rejects.toThrow("response exceeds 1024 bytes");
76
+ });
77
+ });
78
+
79
+ describe("wasm plugins", () => {
80
+ it("terminates a module whose _start never returns", async () => {
81
+ const dir = mkdtempSync(join(tmpdir(), "portolan-plugin-host-"));
82
+ const path = join(dir, "hang.wasm");
83
+ // (module (memory (export "memory") 1)
84
+ // (func (export "_start") (loop br 0)))
85
+ writeFileSync(path, Buffer.from(
86
+ "0061736d01000000010401600000030201000503010001071302066d656d6f72790200065f737461727400000a0901070003400c000b0b",
87
+ "hex",
88
+ ));
89
+ try {
90
+ await expect(
91
+ runPlugin(
92
+ { name: "hang", wasm: { url: `file://${path}` } },
93
+ {},
94
+ { timeoutMs: 100 },
95
+ ),
96
+ ).rejects.toThrow("timed out after 100 ms");
97
+ } finally {
98
+ rmSync(dir, { recursive: true, force: true });
99
+ }
100
+ });
101
+ });
102
+
103
+ describe("warningsIn", () => {
104
+ it("reads only the warning lines, and nothing from an empty stderr", () => {
105
+ expect(warningsIn("")).toEqual([]);
106
+ expect(warningsIn(undefined)).toEqual([]);
107
+ expect(warningsIn("warning: one\r\nnote\nwarning: two \nwarning: \n")).toEqual(["one", "two"]);
108
+ });
109
+ });
@@ -0,0 +1,34 @@
1
+ import { closeSync, openSync } from "node:fs";
2
+ import { parentPort, workerData } from "node:worker_threads";
3
+ import { WASI } from "node:wasi";
4
+
5
+ async function run() {
6
+ const stdin = openSync(workerData.inPath, "r");
7
+ const stdout = openSync(workerData.outPath, "w");
8
+ const stderr = openSync(workerData.errPath, "w");
9
+ try {
10
+ const wasi = new WASI({
11
+ version: "preview1",
12
+ args: [workerData.name],
13
+ env: {},
14
+ preopens: {},
15
+ stdin,
16
+ stdout,
17
+ stderr,
18
+ });
19
+ const module = await WebAssembly.compile(workerData.bytes);
20
+ const instance = await WebAssembly.instantiate(module, wasi.getImportObject());
21
+ return wasi.start(instance);
22
+ } finally {
23
+ closeSync(stdin);
24
+ closeSync(stdout);
25
+ closeSync(stderr);
26
+ }
27
+ }
28
+
29
+ try {
30
+ const code = await run();
31
+ parentPort.postMessage({ code });
32
+ } catch (cause) {
33
+ parentPort.postMessage({ error: cause instanceof Error ? cause.message : String(cause) });
34
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Process adapter for plugins bundled in the npm package. It keeps the
4
+ // plugin's cwd in the user's workspace while resolving source and toolchain
5
+ // files from the installation. Go plugins are compiled into the ignored
6
+ // `.portolan/bin` directory because `go run` changes the child cwd to the
7
+ // module containing the plugin.
8
+
9
+ import { spawn, spawnSync } from "node:child_process";
10
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
11
+ import { dirname, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ const installRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
15
+ const workspace = process.cwd();
16
+ const name = process.argv[2];
17
+ const manifest = JSON.parse(readFileSync(resolve(installRoot, "portolan.json"), "utf8"));
18
+ const plugin = (manifest.plugins ?? []).find((candidate) => candidate.name === name);
19
+
20
+ if (!plugin?.process) {
21
+ console.error(`portolan: built-in process plugin ${JSON.stringify(name)} does not exist`);
22
+ process.exit(2);
23
+ }
24
+
25
+ const command = plugin.process.command;
26
+ const args = [...(plugin.process.args ?? [])];
27
+
28
+ if (command === "go" && args[0] === "run" && args[1]) {
29
+ const binDir = resolve(workspace, ".portolan", "bin");
30
+ const suffix = process.platform === "win32" ? ".exe" : "";
31
+ const binary = resolve(binDir, `${name}${suffix}`);
32
+ mkdirSync(binDir, { recursive: true });
33
+
34
+ // Rebuilding is deliberately left to Go's build cache. Package upgrades can
35
+ // change shared extractor code without changing this plugin directory, so a
36
+ // timestamp shortcut here would make an old binary look current.
37
+ const built = spawnSync(
38
+ "go",
39
+ ["build", "-o", binary, args[1]],
40
+ { cwd: installRoot, stdio: ["ignore", "ignore", "inherit"] },
41
+ );
42
+ if (built.error) {
43
+ console.error(`portolan: could not start Go to build ${name}: ${built.error.message}`);
44
+ process.exit(1);
45
+ }
46
+ if (built.status !== 0 || !existsSync(binary)) process.exit(built.status ?? 1);
47
+ run(binary, []);
48
+ } else if (command === "cargo" && existsSync(resolve(installRoot, "plugins/extract-rust/target/release/portolan-extract-rust"))) {
49
+ run(resolve(installRoot, "plugins/extract-rust/target/release/portolan-extract-rust"), []);
50
+ } else {
51
+ if (command === "cargo") {
52
+ process.env.CARGO_TARGET_DIR = resolve(workspace, ".portolan", "bin", "cargo");
53
+ }
54
+ const resolved = args.map((arg, index) => {
55
+ if (!arg || arg.startsWith("-") || resolveArgument(command, index) === false) return arg;
56
+ return resolve(installRoot, arg);
57
+ });
58
+ run(command, resolved);
59
+ }
60
+
61
+ function resolveArgument(executable, index) {
62
+ if (["node", "python", "python3"].includes(executable)) return index === 0;
63
+ if (executable === "java") return index === 1;
64
+ if (executable === "cargo") return index === 3;
65
+ return false;
66
+ }
67
+
68
+ function run(executable, argv) {
69
+ const child = spawn(executable, argv, { cwd: workspace, stdio: "inherit" });
70
+ child.once("error", (cause) => {
71
+ console.error(`portolan: built-in ${name} did not start: ${cause.message}`);
72
+ process.exitCode = 1;
73
+ });
74
+ child.once("exit", (code, signal) => {
75
+ if (signal) {
76
+ process.kill(process.pid, signal);
77
+ return;
78
+ }
79
+ process.exitCode = code ?? 1;
80
+ });
81
+ }