@synergenius/flow-weaver 0.33.1 → 0.44.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 (922) hide show
  1. package/LICENSE +201 -226
  2. package/NOTICE +13 -0
  3. package/README.md +152 -244
  4. package/dist/agent/agent-loop.d.ts +4 -2
  5. package/dist/agent/cli-session.d.ts +3 -3
  6. package/dist/agent/cli-session.js +511 -438
  7. package/dist/agent/cli-spawn-config.d.ts +4 -4
  8. package/dist/agent/gate.d.ts +164 -0
  9. package/dist/agent/index.d.ts +4 -4
  10. package/dist/agent/index.js +174 -28
  11. package/dist/agent/mcp-bridge.d.ts +1 -1
  12. package/dist/agent/mcp-bridge.js +114 -128
  13. package/dist/agent/mcp-tool-server.d.ts +2 -2
  14. package/dist/agent/mcp-tool-server.js +147 -189
  15. package/dist/agent/profiles.d.ts +69 -0
  16. package/dist/agent/providers/anthropic.d.ts +2 -4
  17. package/dist/agent/providers/claude-cli.d.ts +4 -5
  18. package/dist/agent/providers/openai-compat.d.ts +8 -1
  19. package/dist/agent/streaming.d.ts +2 -2
  20. package/dist/agent/types.d.ts +16 -10
  21. package/dist/api/builder.d.ts +3 -3
  22. package/dist/api/compile.d.ts +9 -4
  23. package/dist/api/durable-validation.d.ts +15 -0
  24. package/dist/api/generate-in-place.d.ts +21 -19
  25. package/dist/api/generate.d.ts +21 -16
  26. package/dist/api/generated-module/local-node-functions.d.ts +24 -0
  27. package/dist/api/generated-module/module-format.d.ts +23 -0
  28. package/dist/api/generated-module/module-writer.d.ts +34 -0
  29. package/dist/api/generated-module/node-type-imports.d.ts +39 -0
  30. package/dist/api/generated-module/preamble.d.ts +16 -0
  31. package/dist/api/generated-module/workflow-functions.d.ts +29 -0
  32. package/dist/api/graph-identity.d.ts +39 -0
  33. package/dist/api/helpers.d.ts +4 -60
  34. package/dist/api/in-place/built-in-nodes.d.ts +18 -0
  35. package/dist/api/in-place/function-body.d.ts +21 -0
  36. package/dist/api/in-place/fw-imports.d.ts +30 -0
  37. package/dist/api/in-place/macro-coverage.d.ts +14 -0
  38. package/dist/api/in-place/node-type-functions.d.ts +18 -0
  39. package/dist/api/in-place/node-type-jsdoc.d.ts +20 -0
  40. package/dist/api/in-place/orphaned-node-types.d.ts +16 -0
  41. package/dist/api/in-place/runtime-section.d.ts +16 -0
  42. package/dist/api/in-place/signature.d.ts +47 -0
  43. package/dist/api/in-place/source-file.d.ts +19 -0
  44. package/dist/api/in-place/workflow-jsdoc.d.ts +19 -0
  45. package/dist/api/index.d.ts +18 -21
  46. package/dist/api/index.js +259 -53
  47. package/dist/api/inline-engine.generated.d.ts +3 -0
  48. package/dist/api/inline-runtime.d.ts +10 -2
  49. package/dist/api/manipulation/connections.d.ts +1 -1
  50. package/dist/api/manipulation/index.d.ts +8 -8
  51. package/dist/api/manipulation/node-types.d.ts +1 -1
  52. package/dist/api/manipulation/nodes.d.ts +3 -13
  53. package/dist/api/manipulation/ports.d.ts +1 -1
  54. package/dist/api/manipulation/scopes.d.ts +1 -1
  55. package/dist/api/manipulation/workflow.d.ts +1 -1
  56. package/dist/api/modify-operation.d.ts +0 -2
  57. package/dist/api/parse.d.ts +22 -1
  58. package/dist/api/port-references.d.ts +66 -0
  59. package/dist/api/query.d.ts +18 -2
  60. package/dist/api/templates.d.ts +1 -1
  61. package/dist/api/transform.d.ts +1 -1
  62. package/dist/api/validate.d.ts +1 -1
  63. package/dist/api/validation-registry.d.ts +2 -2
  64. package/dist/artifacts/brief.d.ts +43 -0
  65. package/dist/artifacts/index.d.ts +31 -0
  66. package/dist/artifacts/pdf.d.ts +19 -0
  67. package/dist/ast/builder.d.ts +3 -3
  68. package/dist/ast/index.d.ts +4 -4
  69. package/dist/ast/index.js +60 -5
  70. package/dist/ast/serialization-node.d.ts +1 -1
  71. package/dist/ast/serialization.d.ts +1 -1
  72. package/dist/ast/types.d.ts +82 -291
  73. package/dist/ast/workflow-utils.d.ts +4 -4
  74. package/dist/built-in-nodes/coercion-types.d.ts +1 -1
  75. package/dist/built-in-nodes/delay.d.ts +3 -1
  76. package/dist/built-in-nodes/generated-registry.d.ts +1 -1
  77. package/dist/built-in-nodes/index.d.ts +6 -5
  78. package/dist/built-in-nodes/index.js +79 -5
  79. package/dist/built-in-nodes/invoke-workflow.d.ts +2 -1
  80. package/dist/built-in-nodes/mock-types.d.ts +45 -14
  81. package/dist/built-in-nodes/sleep.d.ts +12 -0
  82. package/dist/built-in-nodes/wait-for-agent.d.ts +2 -1
  83. package/dist/built-in-nodes/wait-for-event.d.ts +2 -1
  84. package/dist/chevrotain-parser/coerce-parser.d.ts +1 -1
  85. package/dist/chevrotain-parser/connect-parser.d.ts +1 -1
  86. package/dist/chevrotain-parser/grammar-diagrams.d.ts +1 -1
  87. package/dist/chevrotain-parser/index.d.ts +20 -23
  88. package/dist/chevrotain-parser/node-parser.d.ts +7 -8
  89. package/dist/chevrotain-parser/parse-line.d.ts +54 -0
  90. package/dist/chevrotain-parser/tokens.d.ts +0 -17
  91. package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +6 -1
  92. package/dist/chunks/chunk-4EUSHAY6.js +298 -0
  93. package/dist/chunks/chunk-4KDUEOFD.js +197 -0
  94. package/dist/chunks/chunk-5Y3AIUWY.js +911 -0
  95. package/dist/chunks/chunk-6KMVP6AU.js +20 -0
  96. package/dist/chunks/chunk-6PNJORFL.js +128 -0
  97. package/dist/chunks/chunk-7AAQNGRH.js +2001 -0
  98. package/dist/chunks/chunk-7YYKTPJ5.js +22705 -0
  99. package/dist/chunks/chunk-A5MBOFVY.js +33 -0
  100. package/dist/chunks/chunk-AXNQRVJB.js +18 -0
  101. package/dist/chunks/chunk-B6HQ6YS5.js +572 -0
  102. package/dist/chunks/chunk-CY3RNVML.js +296 -0
  103. package/dist/chunks/chunk-CYHYDLV4.js +653 -0
  104. package/dist/chunks/chunk-CZBCCK4Y.js +82 -0
  105. package/dist/chunks/chunk-DCMGQP5V.js +505 -0
  106. package/dist/chunks/chunk-DIAA66GL.js +72 -0
  107. package/dist/chunks/chunk-DUO6H5PR.js +20 -0
  108. package/dist/chunks/chunk-E5WN7IGO.js +125 -0
  109. package/dist/chunks/chunk-EFGFXXQF.js +184 -0
  110. package/dist/chunks/chunk-F3RTZV2A.js +2811 -0
  111. package/dist/chunks/chunk-FMTLP5ZX.js +1397 -0
  112. package/dist/chunks/chunk-HKZLQJDV.js +79 -0
  113. package/dist/chunks/chunk-INDEVYEC.js +146 -0
  114. package/dist/chunks/chunk-JQTF4R5L.js +390 -0
  115. package/dist/chunks/chunk-KMCAYNUM.js +331 -0
  116. package/dist/chunks/chunk-KMWCCOUJ.js +1444 -0
  117. package/dist/chunks/chunk-KU3CLJI2.js +338 -0
  118. package/dist/chunks/chunk-NDSTGMKR.js +3300 -0
  119. package/dist/chunks/chunk-NQNLUAPO.js +505 -0
  120. package/dist/chunks/chunk-O2U2LO57.js +466 -0
  121. package/dist/chunks/chunk-OOQG7UPP.js +457 -0
  122. package/dist/chunks/chunk-OZQAW5ZG.js +112 -0
  123. package/dist/chunks/chunk-P2CVDGHR.js +487 -0
  124. package/dist/chunks/chunk-P44RIZ7C.js +592 -0
  125. package/dist/chunks/chunk-PEZCT37T.js +1475 -0
  126. package/dist/chunks/chunk-PKWSAW6O.js +152 -0
  127. package/dist/chunks/chunk-Q6MDDZMT.js +4373 -0
  128. package/dist/chunks/chunk-QDE4Z7KW.js +6 -0
  129. package/dist/chunks/chunk-QJDAVSNY.js +8 -0
  130. package/dist/chunks/chunk-QYPORSLO.js +7552 -0
  131. package/dist/chunks/chunk-R4QGNK34.js +183 -0
  132. package/dist/chunks/chunk-REEYC73I.js +3002 -0
  133. package/dist/chunks/chunk-SEWDMQ6Q.js +46 -0
  134. package/dist/chunks/chunk-TAIUWKZB.js +3057 -0
  135. package/dist/chunks/chunk-TN4YZ3O7.js +196 -0
  136. package/dist/chunks/chunk-TRMFB43C.js +143 -0
  137. package/dist/chunks/chunk-VO4QXG2X.js +1016 -0
  138. package/dist/chunks/chunk-VP2P3BPA.js +1002 -0
  139. package/dist/chunks/chunk-VQ7H33J4.js +2176 -0
  140. package/dist/chunks/chunk-VWA7MCV2.js +105 -0
  141. package/dist/chunks/chunk-XAV22WD7.js +253 -0
  142. package/dist/chunks/chunk-YSPBPZDV.js +148 -0
  143. package/dist/chunks/chunk-ZKEGXO73.js +282 -0
  144. package/dist/chunks/chunk-ZMGN6F3S.js +4256 -0
  145. package/dist/chunks/grammar-diagrams-AI7VDXOM.js +14 -0
  146. package/dist/chunks/index.min-ECJD6GZC.js +37 -0
  147. package/dist/chunks/manifest-EPTR5MYH.js +21 -0
  148. package/dist/chunks/openapi-document-6TCGVDUW.js +69 -0
  149. package/dist/chunks/registry-K2U5KUTY.js +23 -0
  150. package/dist/chunks/serialization-node-UGY6CFH6.js +38 -0
  151. package/dist/chunks/validate-GEKSZPIN.js +24 -0
  152. package/dist/chunks/validation-registry-IQZX3UE3.js +7 -0
  153. package/dist/chunks/validator-VXFHLPDW.js +26 -0
  154. package/dist/chunks/workflow-executor-KBGFKHNR.js +36 -0
  155. package/dist/cli/commands/agents.d.ts +16 -0
  156. package/dist/cli/commands/artifact.d.ts +10 -0
  157. package/dist/cli/commands/compile.d.ts +0 -30
  158. package/dist/cli/commands/console.d.ts +32 -0
  159. package/dist/cli/commands/context.d.ts +0 -3
  160. package/dist/cli/commands/describe.js +386 -352
  161. package/dist/cli/commands/dev.d.ts +9 -4
  162. package/dist/cli/commands/diagram.d.ts +3 -5
  163. package/dist/cli/commands/doctor.d.ts +27 -4
  164. package/dist/cli/commands/doctor.js +3307 -633
  165. package/dist/cli/commands/export.d.ts +6 -4
  166. package/dist/cli/commands/grammar.d.ts +0 -3
  167. package/dist/cli/commands/implement.d.ts +1 -1
  168. package/dist/cli/commands/init/agent-handoff.d.ts +28 -0
  169. package/dist/cli/commands/init/config.d.ts +3 -0
  170. package/dist/cli/commands/init/files.d.ts +12 -0
  171. package/dist/cli/commands/init/naming.d.ts +13 -0
  172. package/dist/cli/commands/init/output.d.ts +12 -0
  173. package/dist/cli/commands/init/setup.d.ts +21 -0
  174. package/dist/cli/commands/init/types.d.ts +53 -0
  175. package/dist/cli/commands/init-personas.d.ts +9 -1
  176. package/dist/cli/commands/init.d.ts +24 -88
  177. package/dist/cli/commands/market.d.ts +1 -1
  178. package/dist/cli/commands/mcp-setup.d.ts +1 -4
  179. package/dist/cli/commands/modify.d.ts +0 -5
  180. package/dist/cli/commands/openapi.d.ts +9 -11
  181. package/dist/cli/commands/run.d.ts +0 -4
  182. package/dist/cli/commands/serve.d.ts +24 -26
  183. package/dist/cli/commands/status.d.ts +1 -1
  184. package/dist/cli/exports.d.ts +3 -4
  185. package/dist/cli/exports.js +124 -23
  186. package/dist/cli/flow-weaver-agents-H653446S.mjs +81 -0
  187. package/dist/cli/flow-weaver-artifact-WBFYTW76.mjs +58 -0
  188. package/dist/cli/flow-weaver-chunk-265CEK74.mjs +2 -0
  189. package/dist/cli/flow-weaver-chunk-2C7PZCV5.mjs +186 -0
  190. package/dist/cli/flow-weaver-chunk-3BSYCWPL.mjs +3109 -0
  191. package/dist/cli/flow-weaver-chunk-3MKUQP3D.mjs +547 -0
  192. package/dist/cli/flow-weaver-chunk-3ZLMPQTH.mjs +27 -0
  193. package/dist/cli/flow-weaver-chunk-4DVY73ML.mjs +280 -0
  194. package/dist/cli/flow-weaver-chunk-54M4KNEL.mjs +200 -0
  195. package/dist/cli/flow-weaver-chunk-55QMUZIA.mjs +256 -0
  196. package/dist/cli/flow-weaver-chunk-6ZUJ7LMX.mjs +2003 -0
  197. package/dist/cli/flow-weaver-chunk-7ET6N32V.mjs +419 -0
  198. package/dist/cli/flow-weaver-chunk-7G63RR4K.mjs +8880 -0
  199. package/dist/cli/flow-weaver-chunk-A6M5K3W7.mjs +4418 -0
  200. package/dist/cli/flow-weaver-chunk-AA3NATMX.mjs +154 -0
  201. package/dist/cli/flow-weaver-chunk-APX6MQ5A.mjs +5652 -0
  202. package/dist/cli/flow-weaver-chunk-BHCC5FE5.mjs +17 -0
  203. package/dist/cli/flow-weaver-chunk-DM4BBRRU.mjs +35 -0
  204. package/dist/cli/flow-weaver-chunk-DRN7KPWY.mjs +168 -0
  205. package/dist/cli/flow-weaver-chunk-EKWF27G6.mjs +23 -0
  206. package/dist/cli/flow-weaver-chunk-EM5HWQ4R.mjs +338 -0
  207. package/dist/cli/flow-weaver-chunk-FPPPU2DG.mjs +226 -0
  208. package/dist/cli/flow-weaver-chunk-GAQXZHNO.mjs +3005 -0
  209. package/dist/cli/flow-weaver-chunk-GBUNNP33.mjs +1329 -0
  210. package/dist/cli/flow-weaver-chunk-HMAKDE55.mjs +636 -0
  211. package/dist/cli/flow-weaver-chunk-HQ3ZAN44.mjs +367 -0
  212. package/dist/cli/flow-weaver-chunk-I4APCXYN.mjs +21442 -0
  213. package/dist/cli/flow-weaver-chunk-IJJ4JXTL.mjs +2297 -0
  214. package/dist/cli/flow-weaver-chunk-J7EPZW2I.mjs +33 -0
  215. package/dist/cli/flow-weaver-chunk-KOEMJHLG.mjs +83 -0
  216. package/dist/cli/flow-weaver-chunk-M6CBWQKJ.mjs +271 -0
  217. package/dist/cli/flow-weaver-chunk-MHJ4N652.mjs +1580 -0
  218. package/dist/cli/flow-weaver-chunk-MKAFQWLP.mjs +202 -0
  219. package/dist/cli/flow-weaver-chunk-MMV2XGD3.mjs +3291 -0
  220. package/dist/cli/flow-weaver-chunk-MSI2UJES.mjs +125 -0
  221. package/dist/cli/flow-weaver-chunk-N767YRAI.mjs +48 -0
  222. package/dist/cli/flow-weaver-chunk-NQKKWDGF.mjs +56 -0
  223. package/dist/cli/flow-weaver-chunk-NYZDQYFU.mjs +45 -0
  224. package/dist/cli/flow-weaver-chunk-OMVWSNHJ.mjs +14 -0
  225. package/dist/cli/flow-weaver-chunk-PUWZ47H5.mjs +2188 -0
  226. package/dist/cli/flow-weaver-chunk-QOPRVDES.mjs +46 -0
  227. package/dist/cli/flow-weaver-chunk-RIWE7YTR.mjs +216 -0
  228. package/dist/cli/flow-weaver-chunk-S63Q3JIU.mjs +789 -0
  229. package/dist/cli/flow-weaver-chunk-T7X5A23H.mjs +558 -0
  230. package/dist/cli/flow-weaver-chunk-TLV3VZUN.mjs +2 -0
  231. package/dist/cli/flow-weaver-chunk-TS5P3PIN.mjs +194 -0
  232. package/dist/cli/flow-weaver-chunk-U3EO3X4B.mjs +649 -0
  233. package/dist/cli/flow-weaver-chunk-UNFS3NQP.mjs +34 -0
  234. package/dist/cli/flow-weaver-chunk-V6DHWFQG.mjs +2811 -0
  235. package/dist/cli/flow-weaver-chunk-VXWNLYQU.mjs +9 -0
  236. package/dist/cli/flow-weaver-chunk-W3YNK26P.mjs +149 -0
  237. package/dist/cli/flow-weaver-chunk-XG2WKXYZ.mjs +324 -0
  238. package/dist/cli/flow-weaver-chunk-YLSRZWFK.mjs +2219 -0
  239. package/dist/cli/flow-weaver-chunk-YVPURWS6.mjs +40 -0
  240. package/dist/cli/flow-weaver-chunk-Z44JK332.mjs +582 -0
  241. package/dist/cli/flow-weaver-chunk-Z5ZJPNSY.mjs +517 -0
  242. package/dist/cli/flow-weaver-chunk-ZQALO25T.mjs +594 -0
  243. package/dist/cli/flow-weaver-compile-A2CABDSB.mjs +37 -0
  244. package/dist/cli/flow-weaver-console-6EBHDBTN.mjs +71 -0
  245. package/dist/cli/flow-weaver-context-ECLYANFF.mjs +17 -0
  246. package/dist/cli/flow-weaver-context-ZRUMW2KE.mjs +69 -0
  247. package/dist/cli/flow-weaver-create-WIP4EFNP.mjs +124 -0
  248. package/dist/cli/flow-weaver-deployment-WJJRK7NO.mjs +14 -0
  249. package/dist/cli/flow-weaver-describe-XDIFF5LI.mjs +41 -0
  250. package/dist/cli/flow-weaver-dev-LG2NSQAU.mjs +196 -0
  251. package/dist/cli/flow-weaver-diagram-3EVQARQE.mjs +52 -0
  252. package/dist/cli/flow-weaver-diagram-7LR7O4M2.mjs +44 -0
  253. package/dist/cli/flow-weaver-diff-USF7DJLU.mjs +85 -0
  254. package/dist/cli/flow-weaver-docs-QU2ZOXKH.mjs +96 -0
  255. package/dist/cli/flow-weaver-doctor-K447YWHR.mjs +43 -0
  256. package/dist/cli/flow-weaver-export-LAS6N56Y.mjs +140 -0
  257. package/dist/cli/flow-weaver-grammar-RIRUS3Q6.mjs +49 -0
  258. package/dist/cli/flow-weaver-grammar-diagrams-WXRTW5SK.mjs +15 -0
  259. package/dist/cli/flow-weaver-implement-5JG6BCFZ.mjs +114 -0
  260. package/dist/cli/flow-weaver-index.min-OR5SIHXD.mjs +39 -0
  261. package/dist/cli/flow-weaver-init-34YZNPQX.mjs +972 -0
  262. package/dist/cli/flow-weaver-init-personas-NOGZTQQ2.mjs +48 -0
  263. package/dist/cli/flow-weaver-manifest-4JNUSEB3.mjs +18 -0
  264. package/dist/cli/flow-weaver-market-WA4CKHKV.mjs +444 -0
  265. package/dist/cli/flow-weaver-mcp-setup-O26MGXSD.mjs +22 -0
  266. package/dist/cli/flow-weaver-migrate-VFVEUTPH.mjs +119 -0
  267. package/dist/cli/flow-weaver-modify-VM3JP5K6.mjs +97 -0
  268. package/dist/cli/flow-weaver-openapi-IRBWI3HM.mjs +71 -0
  269. package/dist/cli/flow-weaver-openapi-document-7N6YMPG3.mjs +30 -0
  270. package/dist/cli/flow-weaver-pack-commands-OS2QRXAK.mjs +146 -0
  271. package/dist/cli/flow-weaver-registry-5MLFRPPT.mjs +25 -0
  272. package/dist/cli/flow-weaver-run-DSTLSTHR.mjs +555 -0
  273. package/dist/cli/flow-weaver-serialization-node-LE6PCG4M.mjs +50 -0
  274. package/dist/cli/flow-weaver-serve-NVLCLSOW.mjs +192 -0
  275. package/dist/cli/flow-weaver-server-7IERLVXE.mjs +2925 -0
  276. package/dist/cli/flow-weaver-server-KWF4PXYC.mjs +19112 -0
  277. package/dist/cli/flow-weaver-status-75W2S3YB.mjs +138 -0
  278. package/dist/cli/flow-weaver-strip-AZPXUYVA.mjs +79 -0
  279. package/dist/cli/flow-weaver-templates-DSNMB6K7.mjs +72 -0
  280. package/dist/cli/flow-weaver-validate-A4UWDQN6.mjs +19 -0
  281. package/dist/cli/flow-weaver-validate-GRBZ5W4C.mjs +296 -0
  282. package/dist/cli/flow-weaver-validation-registry-BINGUNXX.mjs +9 -0
  283. package/dist/cli/flow-weaver-validator-TY56CI42.mjs +21 -0
  284. package/dist/cli/flow-weaver-watch-OPPERXL6.mjs +114 -0
  285. package/dist/cli/flow-weaver-workflow-executor-6AHYPXAN.mjs +26 -0
  286. package/dist/cli/flow-weaver.mjs +3557 -89498
  287. package/dist/cli/index.d.ts +5 -2
  288. package/dist/cli/pack-commands-needed.d.ts +12 -0
  289. package/dist/cli/program.d.ts +14 -0
  290. package/dist/cli/templates/ai/mock-provider.d.ts +1 -1
  291. package/dist/cli/templates/index.d.ts +1 -8
  292. package/dist/cli/templates/nodes/agent-router.d.ts +1 -1
  293. package/dist/cli/templates/nodes/aggregator.d.ts +1 -1
  294. package/dist/cli/templates/nodes/conversation-memory.d.ts +1 -1
  295. package/dist/cli/templates/nodes/http.d.ts +1 -1
  296. package/dist/cli/templates/nodes/human-approval.d.ts +1 -1
  297. package/dist/cli/templates/nodes/json-extractor.d.ts +1 -1
  298. package/dist/cli/templates/nodes/llm-call.d.ts +1 -1
  299. package/dist/cli/templates/nodes/prompt-template.d.ts +1 -1
  300. package/dist/cli/templates/nodes/rag-retriever.d.ts +1 -1
  301. package/dist/cli/templates/nodes/tool-executor.d.ts +1 -1
  302. package/dist/cli/templates/nodes/transformer.d.ts +1 -1
  303. package/dist/cli/templates/nodes/validator.d.ts +1 -1
  304. package/dist/cli/templates/shared/approval-types.d.ts +1 -1
  305. package/dist/cli/templates/shared/case.d.ts +14 -0
  306. package/dist/cli/templates/shared/llm-types.d.ts +4 -4
  307. package/dist/cli/templates/workflows/aggregator.d.ts +1 -1
  308. package/dist/cli/templates/workflows/ai-agent.d.ts +1 -1
  309. package/dist/cli/templates/workflows/ai-chat.d.ts +1 -1
  310. package/dist/cli/templates/workflows/ai-rag.d.ts +1 -1
  311. package/dist/cli/templates/workflows/ai-react.d.ts +1 -1
  312. package/dist/cli/templates/workflows/approval.d.ts +10 -0
  313. package/dist/cli/templates/workflows/conditional.d.ts +2 -2
  314. package/dist/cli/templates/workflows/error-handler.d.ts +2 -2
  315. package/dist/cli/templates/workflows/foreach.d.ts +1 -1
  316. package/dist/cli/templates/workflows/sequential.d.ts +1 -1
  317. package/dist/cli/templates/workflows/webhook.d.ts +1 -1
  318. package/dist/cli/utils/interactive.d.ts +6 -0
  319. package/dist/cli/utils/json-option.d.ts +8 -0
  320. package/dist/cli/utils/logger.d.ts +2 -0
  321. package/dist/compiler/durable-source-proof.d.ts +23 -0
  322. package/dist/compiler/executable-artifact.d.ts +29 -0
  323. package/dist/compiler/index.d.ts +4 -0
  324. package/dist/compiler/index.js +36 -0
  325. package/dist/console/app.js +106 -0
  326. package/dist/console/assets/synergenius.svg +18 -0
  327. package/dist/console/author.d.ts +32 -0
  328. package/dist/console/cli-catalog.d.ts +21 -0
  329. package/dist/console/cli-run.d.ts +35 -0
  330. package/dist/console/cli-run.js +46 -0
  331. package/dist/console/debug.d.ts +67 -0
  332. package/dist/console/diff-view.d.ts +96 -0
  333. package/dist/console/export.d.ts +36 -0
  334. package/dist/console/git.d.ts +27 -0
  335. package/dist/console/index.d.ts +4 -0
  336. package/dist/console/index.html +16 -0
  337. package/dist/console/index.js +203 -0
  338. package/dist/console/packs.d.ts +100 -0
  339. package/dist/console/request-guard.d.ts +10 -0
  340. package/dist/console/respond.d.ts +16 -0
  341. package/dist/console/router.d.ts +64 -0
  342. package/dist/console/routes-agents.d.ts +14 -0
  343. package/dist/console/routes-project.d.ts +3 -0
  344. package/dist/console/routes-runs.d.ts +3 -0
  345. package/dist/console/routes-services.d.ts +7 -0
  346. package/dist/console/routes-tools.d.ts +3 -0
  347. package/dist/console/runs.d.ts +69 -0
  348. package/dist/console/scan.d.ts +49 -0
  349. package/dist/console/schema.d.ts +74 -0
  350. package/dist/console/server.d.ts +34 -0
  351. package/dist/console/server.js +43264 -0
  352. package/dist/console/services.d.ts +119 -0
  353. package/dist/console/source.d.ts +23 -0
  354. package/dist/console/status.d.ts +68 -0
  355. package/dist/console/styles.css +1023 -0
  356. package/dist/console/terminals.d.ts +22 -0
  357. package/dist/console/workflow-view.d.ts +153 -0
  358. package/dist/constants.d.ts +5 -1
  359. package/dist/constants.js +56 -167
  360. package/dist/context/index.d.ts +10 -0
  361. package/dist/context/index.js +14 -191
  362. package/dist/coordinator/agent-gate.d.ts +107 -0
  363. package/dist/coordinator/bundle-digest.d.ts +20 -0
  364. package/dist/coordinator/clock-tick.d.ts +4 -0
  365. package/dist/coordinator/effect-receipts.d.ts +10 -0
  366. package/dist/coordinator/errors.d.ts +31 -0
  367. package/dist/coordinator/file-store.d.ts +3 -0
  368. package/dist/coordinator/gate-labeling.d.ts +33 -0
  369. package/dist/coordinator/gate-resolution.d.ts +28 -0
  370. package/dist/coordinator/index.d.ts +12 -0
  371. package/dist/coordinator/index.js +107 -0
  372. package/dist/coordinator/memory-store.d.ts +3 -0
  373. package/dist/coordinator/params.d.ts +18 -0
  374. package/dist/coordinator/run-annotations.d.ts +6 -0
  375. package/dist/coordinator/run-context.d.ts +11 -0
  376. package/dist/coordinator/run-ending.d.ts +5 -0
  377. package/dist/coordinator/run-outcome.d.ts +31 -0
  378. package/dist/coordinator/run-resume.d.ts +6 -0
  379. package/dist/coordinator/run-start.d.ts +4 -0
  380. package/dist/coordinator/run-store.d.ts +275 -0
  381. package/dist/coordinator/run-trace.d.ts +41 -0
  382. package/dist/coordinator/run-views.d.ts +12 -0
  383. package/dist/coordinator/runs-dir.d.ts +26 -0
  384. package/dist/coordinator/store.d.ts +67 -0
  385. package/dist/coordinator/time.d.ts +39 -0
  386. package/dist/coordinator/workflow-selection.d.ts +6 -0
  387. package/dist/deployment/index.d.ts +11 -16
  388. package/dist/deployment/index.js +541 -50
  389. package/dist/deployment/targets/base.d.ts +11 -13
  390. package/dist/deployment/targets/openapi-parts.d.ts +92 -0
  391. package/dist/diagram/arms.d.ts +70 -0
  392. package/dist/diagram/ascii-renderer.d.ts +1 -1
  393. package/dist/diagram/geometry.d.ts +22 -29
  394. package/dist/diagram/graph/bounds.d.ts +30 -0
  395. package/dist/diagram/graph/metrics.d.ts +21 -0
  396. package/dist/diagram/graph/nodes.d.ts +33 -0
  397. package/dist/diagram/graph/placement.d.ts +22 -0
  398. package/dist/diagram/graph/ports.d.ts +23 -0
  399. package/dist/diagram/graph/routing.d.ts +40 -0
  400. package/dist/diagram/graph/scopes.d.ts +30 -0
  401. package/dist/diagram/index.d.ts +15 -23
  402. package/dist/diagram/index.js +632 -111
  403. package/dist/diagram/labels.d.ts +12 -0
  404. package/dist/diagram/lanes.d.ts +93 -0
  405. package/dist/diagram/layout.d.ts +1 -1
  406. package/dist/diagram/material-symbols.d.ts +7 -0
  407. package/dist/diagram/orthogonal-router.d.ts +15 -51
  408. package/dist/diagram/process-view.d.ts +79 -0
  409. package/dist/diagram/spine.d.ts +25 -0
  410. package/dist/diagram/theme.d.ts +3 -5
  411. package/dist/diagram/types.d.ts +6 -20
  412. package/dist/diff/index.js +53 -9
  413. package/dist/diff/types.d.ts +1 -5
  414. package/dist/doc-metadata/extractors/annotations.d.ts +0 -4
  415. package/dist/doc-metadata/extractors/cli-commands.d.ts +2 -1
  416. package/dist/doc-metadata/extractors/core-metadata.d.ts +19 -4
  417. package/dist/doc-metadata/extractors/error-codes.d.ts +1 -1
  418. package/dist/doc-metadata/extractors/mcp-tools.d.ts +7 -3
  419. package/dist/doc-metadata/index.d.ts +1 -2
  420. package/dist/doc-metadata/index.js +2368 -9
  421. package/dist/doc-metadata/types.d.ts +7 -24
  422. package/dist/docs/guide.d.ts +33 -0
  423. package/dist/docs/index.d.ts +7 -4
  424. package/dist/docs/index.js +298 -298
  425. package/dist/docs/pack-topics.d.ts +18 -0
  426. package/dist/editor-completions/annotationValues.d.ts +1 -1
  427. package/dist/editor-completions/contextParser.d.ts +3 -2
  428. package/dist/editor-completions/dataTypes.d.ts +1 -1
  429. package/dist/editor-completions/goToDefinition.d.ts +1 -1
  430. package/dist/editor-completions/index.d.ts +8 -8
  431. package/dist/editor-completions/index.js +1244 -175
  432. package/dist/editor-completions/jsDocAnnotations.d.ts +1 -1
  433. package/dist/editor-completions/modifierCompletions.d.ts +1 -1
  434. package/dist/export/index.d.ts +7 -1
  435. package/dist/generated-branding.js +8 -21
  436. package/dist/generated-version.d.ts +1 -1
  437. package/dist/generated-version.js +7 -3
  438. package/dist/generator/annotation-generator.d.ts +84 -0
  439. package/dist/generator/async-detection.d.ts +1 -1
  440. package/dist/{body-generator.d.ts → generator/body-generator.d.ts} +7 -3
  441. package/dist/generator/code-utils.d.ts +15 -60
  442. package/dist/generator/connection-reads.d.ts +94 -0
  443. package/dist/generator/control-flow-plan.d.ts +74 -0
  444. package/dist/generator/control-flow.d.ts +1 -1
  445. package/dist/generator/exit-assembly.d.ts +11 -0
  446. package/dist/generator/index.d.ts +4 -5
  447. package/dist/generator/index.js +34 -11
  448. package/dist/generator/input-port-values.d.ts +22 -0
  449. package/dist/generator/{scope-function-generator.d.ts → node-arguments.d.ts} +76 -1
  450. package/dist/generator/node-invocation.d.ts +74 -0
  451. package/dist/{node-types-generator.d.ts → generator/node-types-generator.d.ts} +3 -2
  452. package/dist/generator/scope-closure.d.ts +23 -0
  453. package/dist/generator/scoped-child.d.ts +49 -0
  454. package/dist/{sugar-optimizer.d.ts → generator/sugar-optimizer.d.ts} +1 -1
  455. package/dist/generator/unified.d.ts +14 -2
  456. package/dist/generator/workflow-generator.d.ts +37 -0
  457. package/dist/index.d.ts +29 -26
  458. package/dist/index.js +1322 -92
  459. package/dist/jsdoc-port-sync/apply-diff.d.ts +15 -0
  460. package/dist/jsdoc-port-sync/callback-signature.d.ts +17 -0
  461. package/dist/jsdoc-port-sync/diff.d.ts +5 -7
  462. package/dist/jsdoc-port-sync/incomplete-lines.d.ts +41 -0
  463. package/dist/jsdoc-port-sync/index.d.ts +11 -11
  464. package/dist/jsdoc-port-sync/index.js +88 -45
  465. package/dist/jsdoc-port-sync/jsdoc-to-signature.d.ts +66 -0
  466. package/dist/jsdoc-port-sync/port-parser.d.ts +6 -24
  467. package/dist/jsdoc-port-sync/port-tag-placement.d.ts +55 -0
  468. package/dist/jsdoc-port-sync/scoped-ports.d.ts +21 -0
  469. package/dist/jsdoc-port-sync/signature-parser.d.ts +8 -1
  470. package/dist/jsdoc-port-sync/signature-to-jsdoc.d.ts +77 -0
  471. package/dist/jsdoc-port-sync/sync.d.ts +4 -1
  472. package/dist/marketplace/index.d.ts +5 -3
  473. package/dist/marketplace/index.js +62 -10
  474. package/dist/marketplace/install.d.ts +24 -0
  475. package/dist/marketplace/manifest.d.ts +1 -1
  476. package/dist/marketplace/npmrc.d.ts +24 -0
  477. package/dist/marketplace/registry.d.ts +46 -11
  478. package/dist/marketplace/types.d.ts +41 -24
  479. package/dist/marketplace/validator.d.ts +1 -1
  480. package/dist/mcp/debug-session.d.ts +2 -4
  481. package/dist/mcp/dist-imports.d.ts +12 -0
  482. package/dist/mcp/response-utils.d.ts +11 -9
  483. package/dist/mcp/server.d.ts +0 -1
  484. package/dist/mcp/tools-context.d.ts +11 -0
  485. package/dist/mcp/tools-diagram.d.ts +1 -2
  486. package/dist/mcp/tools-export.d.ts +2 -2
  487. package/dist/mcp/tools-marketplace.d.ts +1 -1
  488. package/dist/mcp/tools-run.d.ts +17 -0
  489. package/dist/mcp/tools-workflow.d.ts +3 -0
  490. package/dist/mcp/types.d.ts +4 -14
  491. package/dist/mcp/workflow-executor.d.ts +57 -19
  492. package/dist/npm-packages.d.ts +1 -1
  493. package/dist/npm-packages.js +10 -473
  494. package/dist/parser/annotation-parser.d.ts +88 -0
  495. package/dist/parser/annotation-suggestion.d.ts +23 -0
  496. package/dist/parser/durable-effect-contract.d.ts +10 -0
  497. package/dist/parser/expression-references.d.ts +101 -0
  498. package/dist/parser/external-node-types.d.ts +69 -0
  499. package/dist/{function-like.d.ts → parser/function-like.d.ts} +10 -3
  500. package/dist/parser/generated-sections.d.ts +25 -0
  501. package/dist/parser/import-resolution.d.ts +67 -0
  502. package/dist/parser/import-stub.d.ts +29 -0
  503. package/dist/parser/jsdoc/config-types.d.ts +209 -0
  504. package/dist/parser/jsdoc/graph-tags.d.ts +54 -0
  505. package/dist/parser/jsdoc/node-type-block.d.ts +23 -0
  506. package/dist/parser/jsdoc/parse-line-once.d.ts +11 -0
  507. package/dist/parser/jsdoc/port-tags.d.ts +42 -0
  508. package/dist/parser/jsdoc/runtime-tags.d.ts +65 -0
  509. package/dist/parser/jsdoc/signature-types.d.ts +52 -0
  510. package/dist/parser/jsdoc/workflow-block.d.ts +22 -0
  511. package/dist/parser/jsdoc-parser.d.ts +23 -0
  512. package/dist/parser/macro-expansion.d.ts +80 -0
  513. package/dist/parser/module-resolution.d.ts +38 -0
  514. package/dist/parser/node-inference.d.ts +39 -0
  515. package/dist/parser/npm-node-types.d.ts +24 -0
  516. package/dist/parser/pack-handlers.d.ts +12 -0
  517. package/dist/parser/path-data-resolution.d.ts +75 -0
  518. package/dist/parser/port-inference.d.ts +15 -0
  519. package/dist/{resolve-package-types.d.ts → parser/resolve-package-types.d.ts} +4 -0
  520. package/dist/parser/serialize-deploy-annotations.d.ts +19 -0
  521. package/dist/parser/tag-registry.d.ts +22 -0
  522. package/dist/parser/workflow-extraction.d.ts +39 -0
  523. package/dist/runtime/ExecutionContext.d.ts +87 -99
  524. package/dist/runtime/continuation-core.d.ts +147 -0
  525. package/dist/runtime/continuation.d.ts +48 -0
  526. package/dist/runtime/debug-controller.d.ts +7 -22
  527. package/dist/runtime/durable-execution.d.ts +176 -0
  528. package/dist/runtime/events.d.ts +16 -1
  529. package/dist/runtime/executable-module-contract.d.ts +16 -0
  530. package/dist/runtime/index.d.ts +13 -10
  531. package/dist/runtime/index.js +947 -9
  532. package/dist/runtime/strict-json.d.ts +15 -0
  533. package/dist/server/access.d.ts +12 -0
  534. package/dist/server/api.d.ts +126 -0
  535. package/dist/server/callback-delivery.d.ts +62 -0
  536. package/dist/server/callback-url.d.ts +44 -0
  537. package/dist/server/context.d.ts +35 -0
  538. package/dist/server/http-error.d.ts +10 -0
  539. package/dist/server/index.d.ts +7 -1
  540. package/dist/server/index.js +125 -6
  541. package/dist/server/openapi-document.d.ts +33 -0
  542. package/dist/server/openapi.d.ts +32 -0
  543. package/dist/server/respond.d.ts +35 -0
  544. package/dist/server/route-params.d.ts +17 -0
  545. package/dist/server/route-plan.d.ts +41 -0
  546. package/dist/server/routes-catalog.d.ts +5 -0
  547. package/dist/server/routes-declared.d.ts +12 -0
  548. package/dist/server/routes-runs.d.ts +8 -0
  549. package/dist/server/run-driver.d.ts +58 -0
  550. package/dist/server/run-view.d.ts +26 -0
  551. package/dist/server/transport.d.ts +35 -0
  552. package/dist/server/types.d.ts +143 -52
  553. package/dist/server/webhook-server.d.ts +12 -38
  554. package/dist/server/workflow-registry.d.ts +7 -4
  555. package/dist/service-registry.d.ts +49 -0
  556. package/dist/service-registry.js +123 -0
  557. package/dist/testing/assertions.d.ts +2 -2
  558. package/dist/testing/index.d.ts +14 -13
  559. package/dist/testing/index.js +582 -24
  560. package/dist/testing/mock-approval.d.ts +3 -5
  561. package/dist/testing/mock-llm.d.ts +2 -3
  562. package/dist/testing/recorder.d.ts +5 -6
  563. package/dist/testing/replayer.d.ts +6 -7
  564. package/dist/testing/run-store-check.d.ts +3 -0
  565. package/dist/testing/token-tracker.d.ts +2 -2
  566. package/dist/{type-mappings.d.ts → types/type-mappings.d.ts} +13 -1
  567. package/dist/utils/error-utils.d.ts +4 -2
  568. package/dist/utils/port-ordering.d.ts +2 -2
  569. package/dist/utils/port-tag-utils.d.ts +1 -1
  570. package/dist/validation/agent-detection.d.ts +17 -5
  571. package/dist/validation/agent-rules.d.ts +6 -7
  572. package/dist/validation/core-rules.d.ts +49 -0
  573. package/dist/validation/design-rules.d.ts +9 -3
  574. package/dist/validation/rule-registry.d.ts +3 -3
  575. package/dist/validation/rules/connections.d.ts +27 -0
  576. package/dist/validation/rules/context.d.ts +11 -0
  577. package/dist/validation/rules/cycles.d.ts +17 -0
  578. package/dist/validation/rules/data-flow.d.ts +19 -0
  579. package/dist/validation/rules/scope-topology.d.ts +35 -0
  580. package/dist/validation/rules/type-compatibility.d.ts +30 -0
  581. package/dist/validation/type-checker.d.ts +39 -0
  582. package/dist/validation/validator-helpers.d.ts +31 -0
  583. package/dist/validation/validator.d.ts +30 -0
  584. package/docs/reference/advanced-annotations.md +277 -60
  585. package/docs/reference/built-in-nodes.md +164 -94
  586. package/docs/reference/cancellation.md +44 -0
  587. package/docs/reference/cli-reference.md +252 -234
  588. package/docs/reference/compilation.md +36 -109
  589. package/docs/reference/concepts.md +80 -63
  590. package/docs/reference/console.md +169 -0
  591. package/docs/reference/debugging.md +26 -57
  592. package/docs/reference/deployment.md +217 -143
  593. package/docs/reference/durable-gates.md +445 -0
  594. package/docs/reference/error-codes.md +90 -43
  595. package/docs/reference/export-interface.md +91 -26
  596. package/docs/reference/iterative-development.md +50 -28
  597. package/docs/reference/jsdoc-grammar.md +66 -311
  598. package/docs/reference/library.md +250 -0
  599. package/docs/reference/marketplace.md +301 -152
  600. package/docs/reference/mcp-tools.md +102 -0
  601. package/docs/reference/node-conversion.md +84 -59
  602. package/docs/reference/orientation.md +68 -0
  603. package/docs/reference/scaffold.md +5 -4
  604. package/docs/reference/tutorial.md +106 -166
  605. package/docs/reference/visual-reference.md +1 -1
  606. package/package.json +85 -50
  607. package/dist/agent/agent-loop.js +0 -156
  608. package/dist/agent/cli-spawn-config.js +0 -65
  609. package/dist/agent/device-connection.d.ts +0 -79
  610. package/dist/agent/device-connection.js +0 -184
  611. package/dist/agent/env-allowlist.js +0 -60
  612. package/dist/agent/providers/anthropic.js +0 -198
  613. package/dist/agent/providers/claude-cli.js +0 -161
  614. package/dist/agent/providers/openai-compat.js +0 -205
  615. package/dist/agent/providers/platform.d.ts +0 -23
  616. package/dist/agent/providers/platform.js +0 -132
  617. package/dist/agent/streaming.js +0 -186
  618. package/dist/agent/types.js +0 -20
  619. package/dist/annotation-generator.d.ts +0 -50
  620. package/dist/annotation-generator.js +0 -743
  621. package/dist/api/builder.js +0 -345
  622. package/dist/api/command-runner.js +0 -787
  623. package/dist/api/compile.js +0 -148
  624. package/dist/api/extract-types.js +0 -57
  625. package/dist/api/generate-in-place.js +0 -1477
  626. package/dist/api/generate.js +0 -514
  627. package/dist/api/helpers.js +0 -285
  628. package/dist/api/inline-runtime.js +0 -526
  629. package/dist/api/manipulation/connections.js +0 -151
  630. package/dist/api/manipulation/index.js +0 -41
  631. package/dist/api/manipulation/node-types.js +0 -200
  632. package/dist/api/manipulation/nodes.js +0 -355
  633. package/dist/api/manipulation/ports.js +0 -228
  634. package/dist/api/manipulation/scopes.js +0 -156
  635. package/dist/api/manipulation/workflow.js +0 -116
  636. package/dist/api/modify-operation.js +0 -177
  637. package/dist/api/parse.js +0 -115
  638. package/dist/api/patterns.d.ts +0 -112
  639. package/dist/api/patterns.js +0 -303
  640. package/dist/api/query.js +0 -840
  641. package/dist/api/templates.js +0 -117
  642. package/dist/api/transform.js +0 -40
  643. package/dist/api/validate.js +0 -64
  644. package/dist/api/validation-registry.js +0 -10
  645. package/dist/api/workflow-file-operations.js +0 -180
  646. package/dist/ast/builder.js +0 -395
  647. package/dist/ast/serialization-node.js +0 -30
  648. package/dist/ast/serialization.js +0 -134
  649. package/dist/ast/types.js +0 -2
  650. package/dist/ast/workflow-utils.js +0 -114
  651. package/dist/body-generator.js +0 -35
  652. package/dist/built-in-nodes/coercion-types.js +0 -61
  653. package/dist/built-in-nodes/delay.js +0 -29
  654. package/dist/built-in-nodes/generated-registry.js +0 -299
  655. package/dist/built-in-nodes/invoke-workflow.js +0 -36
  656. package/dist/built-in-nodes/mock-types.js +0 -42
  657. package/dist/built-in-nodes/wait-for-agent.js +0 -31
  658. package/dist/built-in-nodes/wait-for-event.js +0 -25
  659. package/dist/chevrotain-parser/coerce-parser.js +0 -103
  660. package/dist/chevrotain-parser/connect-parser.js +0 -136
  661. package/dist/chevrotain-parser/fan-parser.js +0 -149
  662. package/dist/chevrotain-parser/grammar-diagrams.js +0 -273
  663. package/dist/chevrotain-parser/index.js +0 -31
  664. package/dist/chevrotain-parser/map-parser.js +0 -130
  665. package/dist/chevrotain-parser/node-parser.js +0 -558
  666. package/dist/chevrotain-parser/path-parser.js +0 -140
  667. package/dist/chevrotain-parser/port-parser.js +0 -519
  668. package/dist/chevrotain-parser/position-parser.d.ts +0 -20
  669. package/dist/chevrotain-parser/position-parser.js +0 -83
  670. package/dist/chevrotain-parser/scope-parser.js +0 -104
  671. package/dist/chevrotain-parser/tokens.js +0 -429
  672. package/dist/chevrotain-parser/trigger-cancel-parser.js +0 -291
  673. package/dist/cli/commands/account.d.ts +0 -4
  674. package/dist/cli/commands/account.js +0 -50
  675. package/dist/cli/commands/ai-credentials.d.ts +0 -12
  676. package/dist/cli/commands/ai-credentials.js +0 -95
  677. package/dist/cli/commands/apikey.d.ts +0 -4
  678. package/dist/cli/commands/apikey.js +0 -64
  679. package/dist/cli/commands/auth.d.ts +0 -9
  680. package/dist/cli/commands/auth.js +0 -255
  681. package/dist/cli/commands/compile.js +0 -331
  682. package/dist/cli/commands/connect.d.ts +0 -4
  683. package/dist/cli/commands/connect.js +0 -163
  684. package/dist/cli/commands/context.js +0 -51
  685. package/dist/cli/commands/create.js +0 -139
  686. package/dist/cli/commands/deploy.d.ts +0 -6
  687. package/dist/cli/commands/deploy.js +0 -74
  688. package/dist/cli/commands/dev.js +0 -227
  689. package/dist/cli/commands/diagram.js +0 -39
  690. package/dist/cli/commands/diff.js +0 -57
  691. package/dist/cli/commands/docs.js +0 -74
  692. package/dist/cli/commands/export.js +0 -153
  693. package/dist/cli/commands/grammar.js +0 -40
  694. package/dist/cli/commands/implement.js +0 -91
  695. package/dist/cli/commands/init-personas.js +0 -541
  696. package/dist/cli/commands/init.js +0 -797
  697. package/dist/cli/commands/market.js +0 -440
  698. package/dist/cli/commands/mcp-setup.js +0 -433
  699. package/dist/cli/commands/migrate.js +0 -91
  700. package/dist/cli/commands/modify.js +0 -58
  701. package/dist/cli/commands/openapi.js +0 -71
  702. package/dist/cli/commands/org.d.ts +0 -8
  703. package/dist/cli/commands/org.js +0 -136
  704. package/dist/cli/commands/pattern.d.ts +0 -34
  705. package/dist/cli/commands/pattern.js +0 -186
  706. package/dist/cli/commands/plugin.d.ts +0 -16
  707. package/dist/cli/commands/plugin.js +0 -176
  708. package/dist/cli/commands/run.js +0 -742
  709. package/dist/cli/commands/serve.js +0 -93
  710. package/dist/cli/commands/status.js +0 -123
  711. package/dist/cli/commands/strip.js +0 -61
  712. package/dist/cli/commands/templates.js +0 -58
  713. package/dist/cli/commands/validate.js +0 -267
  714. package/dist/cli/commands/watch.js +0 -71
  715. package/dist/cli/config/credentials.d.ts +0 -15
  716. package/dist/cli/config/credentials.js +0 -43
  717. package/dist/cli/config/platform-client.d.ts +0 -123
  718. package/dist/cli/config/platform-client.js +0 -248
  719. package/dist/cli/env-setup.js +0 -12
  720. package/dist/cli/index.js +0 -850
  721. package/dist/cli/pack-commands.js +0 -122
  722. package/dist/cli/postinstall.d.ts +0 -16
  723. package/dist/cli/postinstall.js +0 -119
  724. package/dist/cli/templates/ai/mock-provider.js +0 -64
  725. package/dist/cli/templates/ai/types.js +0 -5
  726. package/dist/cli/templates/approvals/index.d.ts +0 -15
  727. package/dist/cli/templates/approvals/index.js +0 -241
  728. package/dist/cli/templates/index.js +0 -123
  729. package/dist/cli/templates/nodes/agent-router.js +0 -114
  730. package/dist/cli/templates/nodes/aggregator.js +0 -63
  731. package/dist/cli/templates/nodes/conversation-memory.js +0 -85
  732. package/dist/cli/templates/nodes/http.js +0 -80
  733. package/dist/cli/templates/nodes/human-approval.js +0 -109
  734. package/dist/cli/templates/nodes/json-extractor.js +0 -119
  735. package/dist/cli/templates/nodes/llm-call.js +0 -106
  736. package/dist/cli/templates/nodes/prompt-template.js +0 -52
  737. package/dist/cli/templates/nodes/rag-retriever.js +0 -128
  738. package/dist/cli/templates/nodes/tool-executor.js +0 -108
  739. package/dist/cli/templates/nodes/transformer.js +0 -68
  740. package/dist/cli/templates/nodes/validator.js +0 -62
  741. package/dist/cli/templates/pack-loader.js +0 -58
  742. package/dist/cli/templates/providers/index.js +0 -239
  743. package/dist/cli/templates/shared/approval-types.js +0 -31
  744. package/dist/cli/templates/shared/llm-types.js +0 -109
  745. package/dist/cli/templates/workflows/aggregator.js +0 -101
  746. package/dist/cli/templates/workflows/ai-agent.js +0 -326
  747. package/dist/cli/templates/workflows/ai-chat.js +0 -167
  748. package/dist/cli/templates/workflows/ai-rag.js +0 -184
  749. package/dist/cli/templates/workflows/ai-react.js +0 -293
  750. package/dist/cli/templates/workflows/conditional.js +0 -139
  751. package/dist/cli/templates/workflows/error-handler.js +0 -145
  752. package/dist/cli/templates/workflows/foreach.js +0 -140
  753. package/dist/cli/templates/workflows/sequential.js +0 -200
  754. package/dist/cli/templates/workflows/webhook.js +0 -158
  755. package/dist/cli/utils/cli-helpers.d.ts +0 -44
  756. package/dist/cli/utils/cli-helpers.js +0 -99
  757. package/dist/cli/utils/logger.js +0 -126
  758. package/dist/cli/utils/parse-int-strict.js +0 -17
  759. package/dist/cli/utils/safe-write.js +0 -54
  760. package/dist/deployment/config/defaults.d.ts +0 -29
  761. package/dist/deployment/config/defaults.js +0 -98
  762. package/dist/deployment/config/loader.d.ts +0 -24
  763. package/dist/deployment/config/loader.js +0 -237
  764. package/dist/deployment/config/types.d.ts +0 -117
  765. package/dist/deployment/config/types.js +0 -5
  766. package/dist/deployment/core/adapters.d.ts +0 -66
  767. package/dist/deployment/core/adapters.js +0 -156
  768. package/dist/deployment/core/executor.d.ts +0 -62
  769. package/dist/deployment/core/executor.js +0 -197
  770. package/dist/deployment/core/formatters.d.ts +0 -42
  771. package/dist/deployment/core/formatters.js +0 -146
  772. package/dist/deployment/openapi/generator.d.ts +0 -146
  773. package/dist/deployment/openapi/generator.js +0 -347
  774. package/dist/deployment/openapi/schema-converter.d.ts +0 -49
  775. package/dist/deployment/openapi/schema-converter.js +0 -192
  776. package/dist/deployment/targets/base.js +0 -855
  777. package/dist/deployment/types.d.ts +0 -140
  778. package/dist/deployment/types.js +0 -8
  779. package/dist/diagram/ascii-renderer.js +0 -528
  780. package/dist/diagram/geometry.js +0 -1267
  781. package/dist/diagram/html-viewer.d.ts +0 -21
  782. package/dist/diagram/html-viewer.js +0 -1689
  783. package/dist/diagram/layout.js +0 -143
  784. package/dist/diagram/orthogonal-router.js +0 -568
  785. package/dist/diagram/renderer.d.ts +0 -3
  786. package/dist/diagram/renderer.js +0 -266
  787. package/dist/diagram/theme.js +0 -282
  788. package/dist/diagram/types.js +0 -2
  789. package/dist/diff/WorkflowDiffer.js +0 -429
  790. package/dist/diff/formatDiff.js +0 -239
  791. package/dist/diff/impact.js +0 -119
  792. package/dist/diff/types.js +0 -35
  793. package/dist/doc-metadata/extractors/annotations.js +0 -638
  794. package/dist/doc-metadata/extractors/cli-commands.js +0 -326
  795. package/dist/doc-metadata/extractors/core-metadata.js +0 -61
  796. package/dist/doc-metadata/extractors/error-codes.js +0 -372
  797. package/dist/doc-metadata/extractors/grammar-rules.js +0 -51
  798. package/dist/doc-metadata/extractors/mcp-tools.js +0 -705
  799. package/dist/doc-metadata/extractors/plugin-api.d.ts +0 -19
  800. package/dist/doc-metadata/extractors/plugin-api.js +0 -279
  801. package/dist/doc-metadata/types.js +0 -5
  802. package/dist/editor-completions/annotationValues.js +0 -146
  803. package/dist/editor-completions/contextParser.js +0 -410
  804. package/dist/editor-completions/dataTypes.js +0 -95
  805. package/dist/editor-completions/goToDefinition.js +0 -112
  806. package/dist/editor-completions/jsDocAnnotations.js +0 -359
  807. package/dist/editor-completions/modifierCompletions.js +0 -197
  808. package/dist/editor-completions/types.js +0 -8
  809. package/dist/export/index.js +0 -212
  810. package/dist/extensions/index.d.ts +0 -16
  811. package/dist/extensions/index.js +0 -17
  812. package/dist/friendly-errors.js +0 -761
  813. package/dist/function-like.js +0 -84
  814. package/dist/generator/async-detection.js +0 -56
  815. package/dist/generator/code-utils.js +0 -551
  816. package/dist/generator/compile-target-registry.d.ts +0 -20
  817. package/dist/generator/compile-target-registry.js +0 -20
  818. package/dist/generator/control-flow.js +0 -358
  819. package/dist/generator/dev-mode-registry.d.ts +0 -27
  820. package/dist/generator/dev-mode-registry.js +0 -20
  821. package/dist/generator/scope-function-generator.js +0 -397
  822. package/dist/generator/unified.js +0 -1831
  823. package/dist/generator.d.ts +0 -54
  824. package/dist/generator.js +0 -101
  825. package/dist/jsdoc-parser.d.ts +0 -377
  826. package/dist/jsdoc-parser.js +0 -1189
  827. package/dist/jsdoc-port-sync/constants.js +0 -103
  828. package/dist/jsdoc-port-sync/diff.js +0 -319
  829. package/dist/jsdoc-port-sync/port-parser.js +0 -579
  830. package/dist/jsdoc-port-sync/rename.js +0 -256
  831. package/dist/jsdoc-port-sync/signature-parser.js +0 -559
  832. package/dist/jsdoc-port-sync/sync.js +0 -653
  833. package/dist/marketplace/manifest.js +0 -194
  834. package/dist/marketplace/registry.js +0 -198
  835. package/dist/marketplace/types.js +0 -9
  836. package/dist/marketplace/validator.js +0 -150
  837. package/dist/mcp/agent-channel.d.ts +0 -35
  838. package/dist/mcp/agent-channel.js +0 -61
  839. package/dist/mcp/auto-registration.d.ts +0 -3
  840. package/dist/mcp/auto-registration.js +0 -62
  841. package/dist/mcp/debug-session.js +0 -25
  842. package/dist/mcp/index.d.ts +0 -11
  843. package/dist/mcp/index.js +0 -10
  844. package/dist/mcp/pack-tools.js +0 -59
  845. package/dist/mcp/prompts.js +0 -68
  846. package/dist/mcp/response-utils.js +0 -97
  847. package/dist/mcp/run-registry.d.ts +0 -29
  848. package/dist/mcp/run-registry.js +0 -24
  849. package/dist/mcp/server.js +0 -63
  850. package/dist/mcp/tools-context.js +0 -51
  851. package/dist/mcp/tools-debug.js +0 -451
  852. package/dist/mcp/tools-diagram.js +0 -97
  853. package/dist/mcp/tools-docs.js +0 -62
  854. package/dist/mcp/tools-export.js +0 -222
  855. package/dist/mcp/tools-marketplace.js +0 -132
  856. package/dist/mcp/tools-model.d.ts +0 -3
  857. package/dist/mcp/tools-model.js +0 -279
  858. package/dist/mcp/tools-pattern.d.ts +0 -3
  859. package/dist/mcp/tools-pattern.js +0 -604
  860. package/dist/mcp/tools-query.js +0 -377
  861. package/dist/mcp/tools-resources.js +0 -41
  862. package/dist/mcp/tools-template.js +0 -119
  863. package/dist/mcp/tools-workflow-run.d.ts +0 -40
  864. package/dist/mcp/tools-workflow-run.js +0 -150
  865. package/dist/mcp/types.js +0 -2
  866. package/dist/mcp/workflow-executor.js +0 -242
  867. package/dist/migration/registry.js +0 -29
  868. package/dist/node-types-generator.js +0 -139
  869. package/dist/parser/tag-registry.js +0 -57
  870. package/dist/parser.d.ts +0 -232
  871. package/dist/parser.js +0 -2500
  872. package/dist/plugin/PluginPanel.d.ts +0 -12
  873. package/dist/plugin/PluginPanel.js +0 -5
  874. package/dist/plugin/index.d.ts +0 -13
  875. package/dist/plugin/index.js +0 -14
  876. package/dist/plugin/types.d.ts +0 -75
  877. package/dist/plugin/types.js +0 -8
  878. package/dist/resolve-package-types.js +0 -123
  879. package/dist/runtime/CancellationError.js +0 -20
  880. package/dist/runtime/ExecutionContext.js +0 -280
  881. package/dist/runtime/builtin-functions.js +0 -549
  882. package/dist/runtime/checkpoint.d.ts +0 -84
  883. package/dist/runtime/checkpoint.js +0 -225
  884. package/dist/runtime/debug-controller.js +0 -247
  885. package/dist/runtime/events.js +0 -2
  886. package/dist/runtime/function-registry.js +0 -66
  887. package/dist/runtime/parameter-resolver.js +0 -113
  888. package/dist/server/types.js +0 -5
  889. package/dist/server/webhook-server.js +0 -269
  890. package/dist/server/workflow-registry.js +0 -202
  891. package/dist/shared-project.js +0 -28
  892. package/dist/sugar-optimizer.js +0 -498
  893. package/dist/testing/assertions.js +0 -127
  894. package/dist/testing/mock-approval.js +0 -98
  895. package/dist/testing/mock-llm.js +0 -119
  896. package/dist/testing/recorder.js +0 -70
  897. package/dist/testing/replayer.js +0 -143
  898. package/dist/testing/token-tracker.js +0 -94
  899. package/dist/type-checker.d.ts +0 -42
  900. package/dist/type-checker.js +0 -190
  901. package/dist/type-mappings.js +0 -131
  902. package/dist/types/branded-ports.d.ts +0 -151
  903. package/dist/types/branded-ports.js +0 -121
  904. package/dist/types/index.d.ts +0 -5
  905. package/dist/types/index.js +0 -5
  906. package/dist/types.d.ts +0 -139
  907. package/dist/types.js +0 -15
  908. package/dist/utils/error-utils.js +0 -27
  909. package/dist/utils/lru-cache.js +0 -40
  910. package/dist/utils/port-ordering.js +0 -81
  911. package/dist/utils/port-tag-utils.js +0 -41
  912. package/dist/utils/string-distance.js +0 -56
  913. package/dist/validation/agent-detection.js +0 -115
  914. package/dist/validation/agent-rules.js +0 -262
  915. package/dist/validation/design-rules.js +0 -377
  916. package/dist/validation/rule-registry.js +0 -37
  917. package/dist/validator.d.ts +0 -113
  918. package/dist/validator.js +0 -1631
  919. package/docs/reference/patterns.md +0 -159
  920. package/scripts/postinstall.cjs +0 -86
  921. /package/dist/{shared-project.d.ts → parser/shared-project.d.ts} +0 -0
  922. /package/dist/{friendly-errors.d.ts → validation/friendly-errors.d.ts} +0 -0
package/dist/parser.js DELETED
@@ -1,2500 +0,0 @@
1
- import { extractFunctionLikes } from './function-like.js';
2
- import * as path from 'node:path';
3
- import * as fs from 'node:fs';
4
- import { createHash } from 'node:crypto';
5
- import { jsdocParser } from './jsdoc-parser.js';
6
- import { EXECUTION_STRATEGIES, RESERVED_PORT_NAMES, isControlFlowPort } from './constants.js';
7
- import { getErrorMessage } from './utils/error-utils.js';
8
- import { assignImplicitPortOrders } from './utils/port-ordering.js';
9
- import { stripGeneratedSections, hasInPlaceMarkers } from './api/generate-in-place.js';
10
- import { inferDataTypeFromTS } from './type-mappings.js';
11
- import { generateJSDocPortTag } from './annotation-generator.js';
12
- import { resolvePackageTypesPath } from './resolve-package-types.js';
13
- import { getPackageExports } from './npm-packages.js';
14
- import { getSharedProject } from './shared-project.js';
15
- import { LRUCache } from './utils/lru-cache.js';
16
- import { COERCION_NODE_TYPES, COERCE_TYPE_MAP } from './built-in-nodes/coercion-types.js';
17
- import { BUILT_IN_NODE_TYPES } from './built-in-nodes/generated-registry.js';
18
- import { tagHandlerRegistry } from './parser/tag-registry.js';
19
- /**
20
- * Convert a TExternalNodeType to a TNodeTypeAST with sensible defaults.
21
- * Used to merge runtime-loaded node types into the parser's available types.
22
- */
23
- function externalToAST(ext) {
24
- const inputs = {};
25
- const outputs = {};
26
- if (ext.ports) {
27
- for (const port of ext.ports) {
28
- const def = {
29
- dataType: port.type || 'ANY',
30
- ...(port.defaultLabel && { label: port.defaultLabel }),
31
- };
32
- if (port.direction === 'OUTPUT') {
33
- outputs[port.name] = def;
34
- }
35
- else {
36
- inputs[port.name] = def;
37
- }
38
- }
39
- }
40
- // Ensure mandatory ports exist
41
- if (!inputs.execute) {
42
- inputs.execute = { dataType: 'STEP', label: 'Execute' };
43
- }
44
- if (!outputs.onSuccess) {
45
- outputs.onSuccess = { dataType: 'STEP', label: 'On Success', isControlFlow: true };
46
- }
47
- if (!outputs.onFailure) {
48
- outputs.onFailure = {
49
- dataType: 'STEP',
50
- label: 'On Failure',
51
- isControlFlow: true,
52
- failure: true,
53
- };
54
- }
55
- return {
56
- type: 'NodeType',
57
- name: ext.name,
58
- functionName: ext.functionName || ext.name,
59
- inputs,
60
- outputs,
61
- hasSuccessPort: 'onSuccess' in outputs,
62
- hasFailurePort: 'onFailure' in outputs,
63
- isAsync: false,
64
- executeWhen: EXECUTION_STRATEGIES.CONJUNCTION,
65
- variant: 'FUNCTION',
66
- };
67
- }
68
- // Port ordering functions imported from ./utils/port-ordering
69
- /** Exposed for tests that need direct access to the shared ts-morph Project */
70
- export function getParserProject() {
71
- return getSharedProject();
72
- }
73
- export class AnnotationParser {
74
- project;
75
- importCache = new LRUCache(200);
76
- importStack = new Set();
77
- parseCache = new LRUCache(100);
78
- /** Tag handler registry. Defaults to the global singleton (pre-populated by extensions). */
79
- tagRegistry = tagHandlerRegistry;
80
- /** Tracks which projectDirs have already had their pack handlers loaded. */
81
- loadedPackDirs = new Set();
82
- constructor() {
83
- this.project = getSharedProject();
84
- }
85
- /**
86
- * Discover and register tag handlers from installed marketplace packs.
87
- * Results are cached per projectDir so repeated parse calls skip the scan.
88
- */
89
- async loadPackHandlers(projectDir) {
90
- if (this.loadedPackDirs.has(projectDir))
91
- return;
92
- this.loadedPackDirs.add(projectDir);
93
- const { discoverTagHandlers, discoverValidationRuleSets } = await import('./marketplace/registry.js');
94
- const { pathToFileURL } = await import('node:url');
95
- // Load tag handlers
96
- const handlers = await discoverTagHandlers(projectDir);
97
- for (const discovered of handlers) {
98
- // Skip if these tags are already registered (e.g. by side-effect imports)
99
- if (discovered.tags.every((t) => this.tagRegistry.has(t)))
100
- continue;
101
- try {
102
- const mod = await import(pathToFileURL(discovered.absoluteFile).href);
103
- const handlerFn = discovered.exportName ? mod[discovered.exportName] : mod.default;
104
- if (typeof handlerFn === 'function') {
105
- this.tagRegistry.register(discovered.tags, discovered.namespace, discovered.scope, handlerFn);
106
- }
107
- }
108
- catch {
109
- // Skip handlers that fail to load (pack may not be built)
110
- }
111
- }
112
- // Load validation rule sets
113
- const { validationRuleRegistry } = await import('./api/validation-registry.js');
114
- const ruleSets = await discoverValidationRuleSets(projectDir);
115
- for (const ruleSet of ruleSets) {
116
- try {
117
- const mod = await import(pathToFileURL(ruleSet.absoluteFile).href);
118
- const detectFn = mod[ruleSet.detectExport ?? 'detect'];
119
- const getRulesFn = mod[ruleSet.rulesExport ?? 'getRules'];
120
- if (typeof detectFn === 'function' && typeof getRulesFn === 'function') {
121
- validationRuleRegistry.register({
122
- name: ruleSet.name,
123
- namespace: ruleSet.namespace,
124
- detect: detectFn,
125
- getRules: getRulesFn,
126
- });
127
- }
128
- }
129
- catch {
130
- // Skip rule sets that fail to load
131
- }
132
- }
133
- }
134
- computeHash(content) {
135
- return createHash('sha256').update(content).digest('hex').slice(0, 16);
136
- }
137
- detectMinorEdit(original, updated) {
138
- let start = 0;
139
- const minLen = Math.min(original.length, updated.length);
140
- while (start < minLen && original[start] === updated[start])
141
- start++;
142
- let endOrig = original.length;
143
- let endNew = updated.length;
144
- while (endOrig > start && endNew > start && original[endOrig - 1] === updated[endNew - 1]) {
145
- endOrig--;
146
- endNew--;
147
- }
148
- const changedRegion = updated.slice(start, endNew);
149
- // Structural patterns require full re-parse
150
- const structural = /import\b|export\b|@flowWeaver|@input\b|@output\b|function\s+\w+\s*\(|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=|@node\b|@connect\b/;
151
- if (structural.test(changedRegion)) {
152
- return { isMinor: false, affectedFunctions: [] };
153
- }
154
- // For now, return isMinor: true but no affected functions (conservative approach)
155
- // This means we'll still do a full parse but the infrastructure is in place
156
- return { isMinor: true, affectedFunctions: [] };
157
- }
158
- patchAST(filePath, cached, newContent, _affectedFunctions) {
159
- try {
160
- const sourceFile = this.project.getSourceFile(filePath);
161
- if (!sourceFile)
162
- return null;
163
- sourceFile.replaceWithText(newContent);
164
- // Re-extract all node types (conservative approach for now)
165
- const warnings = [];
166
- const nodeTypes = this.extractNodeTypes(sourceFile, warnings);
167
- const result = {
168
- ...cached.result,
169
- nodeTypes,
170
- warnings: [...cached.result.warnings, ...warnings],
171
- };
172
- this.parseCache.set(filePath, {
173
- mtime: fs.statSync(filePath).mtimeMs,
174
- contentHash: this.computeHash(newContent),
175
- result,
176
- });
177
- return result;
178
- }
179
- catch {
180
- return null;
181
- }
182
- }
183
- parse(filePath, externalNodeTypes) {
184
- const stats = fs.statSync(filePath);
185
- const hasExternalTypes = externalNodeTypes && externalNodeTypes.length > 0;
186
- // Skip cache when external node types are provided — cache was built without them
187
- if (!hasExternalTypes) {
188
- const cached = this.parseCache.get(filePath);
189
- // FAST PATH 1: mtime unchanged
190
- if (cached && cached.mtime === stats.mtimeMs) {
191
- return cached.result;
192
- }
193
- const rawContent = fs.readFileSync(filePath, 'utf-8');
194
- const content = hasInPlaceMarkers(rawContent)
195
- ? stripGeneratedSections(rawContent)
196
- : rawContent;
197
- const hash = this.computeHash(content);
198
- // FAST PATH 2: content hash unchanged (save without edit)
199
- if (cached && cached.contentHash === hash) {
200
- cached.mtime = stats.mtimeMs;
201
- return cached.result;
202
- }
203
- // FAST PATH 3: Incremental patching disabled — re-enable when detectMinorEdit
204
- // returns affected functions. Infrastructure preserved in detectMinorEdit/patchAST.
205
- // FALLBACK: Full parse
206
- return this.fullParse(filePath, content, hash, stats.mtimeMs);
207
- }
208
- // External types provided — always do a full parse without caching the result
209
- const rawContent = fs.readFileSync(filePath, 'utf-8');
210
- const content = hasInPlaceMarkers(rawContent) ? stripGeneratedSections(rawContent) : rawContent;
211
- const hash = this.computeHash(content);
212
- return this.fullParse(filePath, content, hash, stats.mtimeMs, externalNodeTypes);
213
- }
214
- fullParse(filePath, content, hash, mtimeMs, externalNodeTypes) {
215
- // Reset import tracking for new parse
216
- this.importStack.clear();
217
- const errors = [];
218
- const warnings = [];
219
- const sourceFile = this.project.createSourceFile(filePath, content, { overwrite: true });
220
- // Add current file to import stack BEFORE processing imports
221
- this.importStack.add(filePath);
222
- const localNodeTypes = this.extractNodeTypes(sourceFile, warnings);
223
- const importedNodeTypes = this.extractImportedNodeTypes(sourceFile, filePath);
224
- // First pass: extract workflow signatures to enable same-file workflow invocation
225
- const workflowSignatures = this.extractWorkflowSignatures(sourceFile, filePath, warnings);
226
- const sameFileWorkflowNodeTypes = workflowSignatures.map((wf) => this.workflowToNodeType(wf));
227
- const nodeTypes = [...localNodeTypes, ...importedNodeTypes, ...sameFileWorkflowNodeTypes];
228
- // Merge external (runtime-loaded) node types so the parser can validate references
229
- if (externalNodeTypes?.length) {
230
- for (const ext of externalNodeTypes) {
231
- const alreadyKnown = nodeTypes.some((nt) => nt.name === ext.name || nt.functionName === ext.name);
232
- if (!alreadyKnown) {
233
- nodeTypes.push(externalToAST(ext));
234
- }
235
- }
236
- }
237
- // Auto-infer node types from unannotated functions referenced by @node,
238
- // and lazily inject built-in nodes (delay, waitForEvent, etc.) when referenced
239
- const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes, localNodeTypes, warnings);
240
- nodeTypes.push(...inferredNodeTypes);
241
- const workflows = this.extractWorkflows(sourceFile, nodeTypes, filePath, errors, warnings);
242
- const patterns = this.extractPatterns(sourceFile, nodeTypes, filePath, errors, warnings);
243
- // Deduplicate warnings (extractWorkflowSignatures + extractWorkflows both parse JSDoc)
244
- const dedupedWarnings = [...new Set(warnings)];
245
- const result = { workflows, nodeTypes, patterns, errors, warnings: dedupedWarnings };
246
- // Clean up source file to prevent ts-morph Project bloat
247
- // (results are captured in the returned AST, source file is no longer needed)
248
- this.project.removeSourceFile(sourceFile);
249
- // Only cache when no external types were used (cache should reflect file-only state)
250
- if (!externalNodeTypes?.length) {
251
- this.parseCache.set(filePath, {
252
- mtime: mtimeMs,
253
- contentHash: hash,
254
- result,
255
- });
256
- }
257
- return result;
258
- }
259
- /**
260
- * Parse workflow from a string instead of a file path.
261
- * Useful for testing and in-memory operations.
262
- *
263
- * Note: Imports from other workflow files are NOT supported in this mode
264
- * since there's no filesystem context. Use parse() for files with imports.
265
- *
266
- * @param code - TypeScript source code containing workflow definitions
267
- * @param virtualPath - Virtual file path for error messages (default: 'virtual.ts')
268
- * @returns ParseResult with workflows and nodeTypes
269
- */
270
- parseFromString(code, virtualPath = 'virtual.ts') {
271
- // Reset import tracking
272
- this.importStack.clear();
273
- // Remove existing virtual file if present
274
- const existingFile = this.project.getSourceFile(virtualPath);
275
- if (existingFile) {
276
- this.project.removeSourceFile(existingFile);
277
- }
278
- // Create source file from string
279
- const sourceFile = this.project.createSourceFile(virtualPath, code, { overwrite: true });
280
- const errors = [];
281
- const warnings = [];
282
- const localNodeTypes = this.extractNodeTypes(sourceFile, warnings);
283
- // First pass: extract workflow signatures to enable same-file workflow invocation
284
- const workflowSignatures = this.extractWorkflowSignatures(sourceFile, virtualPath, warnings);
285
- const sameFileWorkflowNodeTypes = workflowSignatures.map((wf) => this.workflowToNodeType(wf));
286
- const nodeTypes = [...localNodeTypes, ...sameFileWorkflowNodeTypes];
287
- // Auto-infer node types from unannotated functions referenced by @node,
288
- // and lazily inject built-in nodes (delay, waitForEvent, etc.) when referenced
289
- const inferredNodeTypes = this.inferNodeTypesFromUnannotated(sourceFile, nodeTypes, localNodeTypes, warnings);
290
- nodeTypes.push(...inferredNodeTypes);
291
- // Note: imports not supported for virtual files - would need filesystem access
292
- const workflows = this.extractWorkflows(sourceFile, nodeTypes, virtualPath, errors, warnings);
293
- const patterns = this.extractPatterns(sourceFile, nodeTypes, virtualPath, errors, warnings);
294
- // Clean up virtual source file to prevent memory bloat
295
- // (tests create many unique virtual paths that accumulate)
296
- this.project.removeSourceFile(sourceFile);
297
- // Deduplicate warnings (extractWorkflowSignatures + extractWorkflows both parse JSDoc)
298
- const dedupedWarnings = [...new Set(warnings)];
299
- return {
300
- workflows,
301
- nodeTypes,
302
- patterns,
303
- errors,
304
- warnings: dedupedWarnings,
305
- };
306
- }
307
- clearCache() {
308
- this.importCache.clear();
309
- this.parseCache.clear();
310
- }
311
- /** Clear only the parse result cache, keeping the import/node-type cache intact. */
312
- clearParseCache() {
313
- this.parseCache.clear();
314
- }
315
- resolveModulePath(moduleSpecifier, currentDir) {
316
- const extensions = ['.ts', '.tsx', '.js', '.jsx'];
317
- // If already has extension, check if exists (with ESM .js → .ts fallback)
318
- const hasExtension = extensions.some((ext) => moduleSpecifier.endsWith(ext));
319
- if (hasExtension) {
320
- const fullPath = path.resolve(currentDir, moduleSpecifier);
321
- if (fs.existsSync(fullPath))
322
- return fullPath;
323
- // ESM convention: TypeScript files use .js extensions in imports.
324
- // If the .js file doesn't exist, try the .ts/.tsx equivalent.
325
- if (moduleSpecifier.endsWith('.js')) {
326
- const tsPath = fullPath.replace(/\.js$/, '.ts');
327
- if (fs.existsSync(tsPath))
328
- return tsPath;
329
- const tsxPath = fullPath.replace(/\.js$/, '.tsx');
330
- if (fs.existsSync(tsxPath))
331
- return tsxPath;
332
- }
333
- else if (moduleSpecifier.endsWith('.jsx')) {
334
- const tsxPath = fullPath.replace(/\.jsx$/, '.tsx');
335
- if (fs.existsSync(tsxPath))
336
- return tsxPath;
337
- }
338
- return null;
339
- }
340
- // Try each extension in order
341
- for (const ext of extensions) {
342
- const fullPath = path.resolve(currentDir, moduleSpecifier + ext);
343
- if (fs.existsSync(fullPath)) {
344
- return fullPath;
345
- }
346
- }
347
- // Try as directory with package.json main field or index file
348
- const dirPath = path.resolve(currentDir, moduleSpecifier);
349
- if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
350
- // Check package.json main field
351
- const pkgPath = path.join(dirPath, 'package.json');
352
- if (fs.existsSync(pkgPath)) {
353
- try {
354
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
355
- if (pkg.main) {
356
- const mainPath = path.resolve(dirPath, pkg.main);
357
- if (fs.existsSync(mainPath)) {
358
- return mainPath;
359
- }
360
- }
361
- }
362
- catch (e) {
363
- console.warn(`Failed to parse package.json at ${pkgPath}: ${getErrorMessage(e)}`);
364
- }
365
- }
366
- // Try index files
367
- for (const ext of extensions) {
368
- const indexPath = path.join(dirPath, `index${ext}`);
369
- if (fs.existsSync(indexPath)) {
370
- return indexPath;
371
- }
372
- }
373
- }
374
- return null;
375
- }
376
- extractImportedNodeTypes(sourceFile, currentFilePath) {
377
- const importedNodeTypes = [];
378
- const imports = sourceFile.getImportDeclarations();
379
- for (const importDecl of imports) {
380
- const moduleSpecifier = importDecl.getModuleSpecifierValue();
381
- // Skip if module specifier is undefined or not a relative import
382
- // Any .ts file with @flowWeaver annotations can be imported.
383
- if (!moduleSpecifier) {
384
- continue;
385
- }
386
- if (!moduleSpecifier.startsWith('.')) {
387
- const packageNodeTypes = this.resolveNpmPackageTypes(importDecl, moduleSpecifier, currentFilePath);
388
- importedNodeTypes.push(...packageNodeTypes);
389
- continue;
390
- }
391
- const currentDir = path.dirname(currentFilePath);
392
- const importedFilePath = this.resolveModulePath(moduleSpecifier, currentDir);
393
- // Validate import path exists
394
- if (!importedFilePath) {
395
- throw new Error(`Import error: File not found for "${moduleSpecifier}"\n` +
396
- ` Imported from: ${currentFilePath}\n` +
397
- ` Searched extensions: .ts, .tsx, .js, .jsx`);
398
- }
399
- // Check for circular dependencies
400
- if (this.importStack.has(importedFilePath)) {
401
- const cycle = Array.from(this.importStack).concat(importedFilePath);
402
- throw new Error(`Circular dependency detected:\n ${cycle.join('\n -> ')}`);
403
- }
404
- try {
405
- // Check cache first — validate mtime to detect file changes
406
- let nodeTypes;
407
- const importStats = fs.statSync(importedFilePath);
408
- const cached = this.importCache.get(importedFilePath);
409
- if (cached && cached.mtime === importStats.mtimeMs) {
410
- nodeTypes = cached.nodeTypes;
411
- }
412
- else {
413
- // Add to import stack for circular dependency detection
414
- this.importStack.add(importedFilePath);
415
- try {
416
- const importedRaw = fs.readFileSync(importedFilePath, 'utf-8');
417
- const importedContent = hasInPlaceMarkers(importedRaw)
418
- ? stripGeneratedSections(importedRaw)
419
- : importedRaw;
420
- const importedFile = this.project.createSourceFile(importedFilePath, importedContent, {
421
- overwrite: true,
422
- });
423
- const importWarnings = [];
424
- const localNodeTypes = this.extractNodeTypes(importedFile, importWarnings);
425
- // Recursively process imports (enables circular dependency detection)
426
- const importedFromFile = this.extractImportedNodeTypes(importedFile, importedFilePath);
427
- // Also extract workflows and convert them to node types
428
- const workflows = this.extractWorkflows(importedFile, [...localNodeTypes, ...importedFromFile], importedFilePath, [], importWarnings);
429
- const workflowAsNodeTypes = workflows.map((wf) => this.workflowToNodeType(wf));
430
- nodeTypes = [...localNodeTypes, ...importedFromFile, ...workflowAsNodeTypes];
431
- // Pre-infer all unannotated functions so the named-import filter can resolve them
432
- const inferredFromImport = this.inferAllUnannotatedFunctions(importedFile, nodeTypes);
433
- nodeTypes.push(...inferredFromImport);
434
- // Clean up imported source file to prevent Project bloat
435
- this.project.removeSourceFile(importedFile);
436
- // Cache the parsed node types with mtime for invalidation
437
- this.importCache.set(importedFilePath, { mtime: importStats.mtimeMs, nodeTypes });
438
- }
439
- finally {
440
- // Remove from stack after processing
441
- this.importStack.delete(importedFilePath);
442
- }
443
- }
444
- // Extract only the named imports
445
- const importedNames = new Set();
446
- importDecl.getNamedImports().forEach((namedImport) => {
447
- importedNames.add(namedImport.getName());
448
- });
449
- // Only include imports that are actually node types
450
- // (other imports may be regular TypeScript exports like types, constants, etc.)
451
- nodeTypes.forEach((nodeType) => {
452
- if (importedNames.has(nodeType.functionName)) {
453
- importedNodeTypes.push({
454
- ...nodeType,
455
- sourceLocation: {
456
- file: importedFilePath,
457
- line: nodeType.sourceLocation?.line || 0,
458
- column: nodeType.sourceLocation?.column || 0,
459
- },
460
- });
461
- }
462
- });
463
- }
464
- catch (error) {
465
- // Re-throw with better context
466
- if (error instanceof Error) {
467
- throw new Error(`Failed to process import from ${importedFilePath}:\n ${error.message}`);
468
- }
469
- throw error;
470
- }
471
- }
472
- return importedNodeTypes;
473
- }
474
- /**
475
- * Resolve npm package imports to node types by reading `.d.ts` declarations.
476
- * Only named imports of exported functions are resolved.
477
- */
478
- resolveNpmPackageTypes(importDecl, moduleSpecifier, currentFilePath) {
479
- // Only handle named imports
480
- const namedImports = importDecl.getNamedImports();
481
- if (namedImports.length === 0)
482
- return [];
483
- const importedNames = new Set();
484
- namedImports.forEach((ni) => importedNames.add(ni.getName()));
485
- // Check cache (npm imports use package path mtime for invalidation)
486
- const cacheKey = `npm:${moduleSpecifier}`;
487
- const npmCached = this.importCache.get(cacheKey);
488
- if (npmCached) {
489
- // For npm packages, check mtime of the resolved .d.ts file
490
- const currentDir = path.dirname(currentFilePath);
491
- const resolvedDts = resolvePackageTypesPath(moduleSpecifier, currentDir);
492
- if (resolvedDts) {
493
- try {
494
- const dtsStats = fs.statSync(resolvedDts);
495
- if (npmCached.mtime === dtsStats.mtimeMs) {
496
- return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
497
- }
498
- }
499
- catch { /* file gone — re-parse */ }
500
- }
501
- else {
502
- return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
503
- }
504
- }
505
- // Resolve .d.ts path
506
- const currentDir = path.dirname(currentFilePath);
507
- const dtsPath = resolvePackageTypesPath(moduleSpecifier, currentDir);
508
- if (!dtsPath)
509
- return [];
510
- try {
511
- const dtsContent = fs.readFileSync(dtsPath, 'utf-8');
512
- const dtsFile = this.project.createSourceFile(`__npm_dts__/${moduleSpecifier}.d.ts`, dtsContent, { overwrite: true });
513
- const fns = extractFunctionLikes(dtsFile);
514
- const allNodeTypes = [];
515
- const seenNames = new Set();
516
- for (const fn of fns) {
517
- const fnName = fn.getName();
518
- if (!fnName)
519
- continue;
520
- // Skip duplicate function names (overloaded declarations in .d.ts)
521
- if (seenNames.has(fnName))
522
- continue;
523
- seenNames.add(fnName);
524
- const nodeType = this.inferNodeTypeFromFunction(fn, fnName, dtsPath);
525
- // Mark as npm package import and prevent inlining
526
- nodeType.importSource = moduleSpecifier;
527
- nodeType.functionText = undefined;
528
- allNodeTypes.push(nodeType);
529
- }
530
- // Clean up the temporary source file
531
- this.project.removeSourceFile(dtsFile);
532
- // Cache all node types from this package (with mtime of the .d.ts file)
533
- const dtsMtime = fs.statSync(dtsPath).mtimeMs;
534
- this.importCache.set(cacheKey, { mtime: dtsMtime, nodeTypes: allNodeTypes });
535
- // Return only the ones in the import statement
536
- return allNodeTypes.filter((nt) => importedNames.has(nt.functionName));
537
- }
538
- catch {
539
- // Silently skip packages whose .d.ts can't be parsed
540
- return [];
541
- }
542
- }
543
- /**
544
- * Resolve an @fwImport annotation to a properly inferred node type.
545
- * Supports both npm packages (e.g., "lodash") and relative paths (e.g., "./utils").
546
- *
547
- * @param imp - The import annotation from JSDoc
548
- * @param currentFilePath - Path of the workflow file containing the @fwImport
549
- * @param warnings - Array to collect warnings
550
- * @returns Inferred TNodeTypeAST, or a stub if inference fails
551
- */
552
- resolveImportAnnotation(imp, currentFilePath, warnings) {
553
- const currentDir = path.dirname(currentFilePath);
554
- // Determine if this is a relative path import or an npm package
555
- if (imp.importSource.startsWith('.')) {
556
- // Relative path import - resolve local file and infer
557
- return this.resolveLocalImportAnnotation(imp, currentDir, warnings);
558
- }
559
- else {
560
- // npm package import - use .d.ts inference
561
- return this.resolveNpmImportAnnotation(imp, currentDir, warnings);
562
- }
563
- }
564
- /**
565
- * Resolve a relative path @fwImport to a node type by reading the local file.
566
- * Includes circular dependency detection using importStack.
567
- */
568
- resolveLocalImportAnnotation(imp, currentDir, warnings) {
569
- const importedFilePath = this.resolveModulePath(imp.importSource, currentDir);
570
- if (!importedFilePath) {
571
- // Gap 3: Warn when relative path doesn't resolve
572
- warnings.push(`@fwImport: Could not resolve "${imp.importSource}" from ${currentDir}`);
573
- return this.createImportStub(imp);
574
- }
575
- // Gap 1: Circular dependency detection
576
- if (this.importStack.has(importedFilePath)) {
577
- const cycle = Array.from(this.importStack).concat(importedFilePath);
578
- warnings.push(`@fwImport: Circular dependency detected:\n ${cycle.join('\n -> ')}`);
579
- return this.createImportStub(imp);
580
- }
581
- // Add to import stack before processing
582
- this.importStack.add(importedFilePath);
583
- try {
584
- const importedContent = fs.readFileSync(importedFilePath, 'utf-8');
585
- const importedFile = this.project.createSourceFile(importedFilePath, importedContent, {
586
- overwrite: true,
587
- });
588
- const fns = extractFunctionLikes(importedFile);
589
- const fn = fns.find((f) => f.getName() === imp.functionName);
590
- if (!fn) {
591
- // Function not found in file - return stub
592
- this.project.removeSourceFile(importedFile);
593
- return this.createImportStub(imp);
594
- }
595
- // Infer BEFORE removing the source file (ts-morph needs it)
596
- const nodeType = this.inferNodeTypeFromFunction(fn, imp.name, importedFilePath);
597
- nodeType.importSource = imp.importSource;
598
- nodeType.functionText = undefined; // Don't inline external code
599
- // Clean up after inference is complete
600
- this.project.removeSourceFile(importedFile);
601
- return nodeType;
602
- }
603
- catch {
604
- // Graceful fallback on any error
605
- return this.createImportStub(imp);
606
- }
607
- finally {
608
- // Always remove from import stack
609
- this.importStack.delete(importedFilePath);
610
- }
611
- }
612
- /**
613
- * Resolve an npm package @fwImport to a node type by reading .d.ts declarations.
614
- */
615
- resolveNpmImportAnnotation(imp, currentDir, warnings) {
616
- // Check cache (with mtime validation — same pattern as resolveNpmImports)
617
- const cacheKey = `npm:${imp.importSource}`;
618
- if (this.importCache.has(cacheKey)) {
619
- const cached = this.importCache.get(cacheKey);
620
- const resolvedDts = resolvePackageTypesPath(imp.importSource, currentDir);
621
- let cacheValid = false;
622
- if (resolvedDts) {
623
- try {
624
- const dtsStats = fs.statSync(resolvedDts);
625
- cacheValid = cached.mtime === dtsStats.mtimeMs;
626
- }
627
- catch { /* file gone — re-parse */ }
628
- }
629
- else {
630
- // No .d.ts found — trust cache (package may have been removed)
631
- cacheValid = true;
632
- }
633
- if (cacheValid) {
634
- const found = cached.nodeTypes.find((nt) => nt.functionName === imp.functionName);
635
- if (found) {
636
- return { ...found, name: imp.name, importSource: imp.importSource };
637
- }
638
- }
639
- }
640
- // Resolve .d.ts path
641
- const dtsPath = resolvePackageTypesPath(imp.importSource, currentDir);
642
- if (!dtsPath) {
643
- warnings.push(`@fwImport: Package "${imp.importSource}" has no type declarations (.d.ts). ` +
644
- `Install @types/${imp.importSource} or add a local wrapper with @flowWeaver nodeType annotations.`);
645
- return this.createImportStub(imp);
646
- }
647
- try {
648
- const dtsContent = fs.readFileSync(dtsPath, 'utf-8');
649
- const dtsFile = this.project.createSourceFile(`__npm_dts__/${imp.importSource}.d.ts`, dtsContent, { overwrite: true });
650
- const fns = extractFunctionLikes(dtsFile);
651
- const allNodeTypes = [];
652
- const seenNames = new Set();
653
- for (const fn of fns) {
654
- const fnName = fn.getName();
655
- if (!fnName)
656
- continue;
657
- // Skip duplicate function names (overloaded declarations in .d.ts)
658
- if (seenNames.has(fnName))
659
- continue;
660
- seenNames.add(fnName);
661
- const nodeType = this.inferNodeTypeFromFunction(fn, fnName, dtsPath);
662
- nodeType.importSource = imp.importSource;
663
- nodeType.functionText = undefined;
664
- allNodeTypes.push(nodeType);
665
- }
666
- this.project.removeSourceFile(dtsFile);
667
- // Cache all node types from this package (with mtime of the .d.ts file)
668
- const dtsMtime2 = fs.statSync(dtsPath).mtimeMs;
669
- this.importCache.set(cacheKey, { mtime: dtsMtime2, nodeTypes: allNodeTypes });
670
- // Find the specific function we need
671
- const found = allNodeTypes.find((nt) => nt.functionName === imp.functionName);
672
- if (found) {
673
- return { ...found, name: imp.name, importSource: imp.importSource };
674
- }
675
- // Function not found in .d.ts
676
- warnings.push(`@fwImport: Function "${imp.functionName}" not found in type declarations for "${imp.importSource}". ` +
677
- `Available exports: ${allNodeTypes.map((nt) => nt.functionName).join(', ') || '(none)'}.`);
678
- }
679
- catch (err) {
680
- warnings.push(`@fwImport: Failed to parse type declarations for "${imp.importSource}": ${err.message}. Node "${imp.name}" will use a generic stub.`);
681
- }
682
- return this.createImportStub(imp);
683
- }
684
- /**
685
- * Create a stub node type for @fwImport when proper inference fails.
686
- * This provides graceful degradation rather than failing completely.
687
- */
688
- createImportStub(imp) {
689
- return {
690
- type: 'NodeType',
691
- name: imp.name,
692
- functionName: imp.functionName,
693
- importSource: imp.importSource,
694
- variant: 'FUNCTION',
695
- inputs: {},
696
- outputs: { result: { dataType: 'ANY' } },
697
- hasSuccessPort: true,
698
- hasFailurePort: true,
699
- executeWhen: 'CONJUNCTION',
700
- isAsync: false,
701
- // Mark as expression since most npm functions are pure
702
- // This is a reasonable default for stubs
703
- expression: true,
704
- };
705
- }
706
- extractNodeTypes(sourceFile, warnings) {
707
- const nodeTypes = [];
708
- extractFunctionLikes(sourceFile).forEach((fn) => {
709
- // Parse JSDoc comments
710
- const config = jsdocParser.parseNodeType(fn, warnings, this.tagRegistry);
711
- if (!config) {
712
- const jsdocText = fn.getJsDocs().map((d) => d.getFullText()).join('');
713
- if (jsdocText.includes('@flowWeaver nodeType')) {
714
- warnings.push(`Function "${fn.getName() || 'anonymous'}" has @flowWeaver annotation but could not be parsed. ` +
715
- `Check for special characters (---) or malformed JSDoc syntax.`);
716
- }
717
- return;
718
- }
719
- const functionName = fn.getName() || 'anonymous';
720
- const nodeTypeName = config.name || functionName;
721
- const inputs = {};
722
- if (config.inputs) {
723
- for (const [portName, portDef] of Object.entries(config.inputs)) {
724
- inputs[portName] = {
725
- dataType: portDef.type,
726
- default: portDef.defaultValue,
727
- optional: portDef.optional,
728
- label: portDef.label,
729
- expression: portDef.expression,
730
- ...(portDef.scope && { scope: portDef.scope }),
731
- ...(portDef.hidden && { hidden: portDef.hidden }),
732
- ...(portDef.metadata && { metadata: portDef.metadata }),
733
- ...(portDef.tsType && { tsType: portDef.tsType }),
734
- };
735
- }
736
- }
737
- const outputs = {};
738
- if (config.outputs) {
739
- for (const [portName, portDef] of Object.entries(config.outputs)) {
740
- outputs[portName] = {
741
- dataType: portDef.type,
742
- label: portDef.label,
743
- ...(portDef.scope && { scope: portDef.scope }),
744
- ...(portDef.hidden && { hidden: portDef.hidden }),
745
- ...(portDef.metadata && { metadata: portDef.metadata }),
746
- ...(portDef.tsType && { tsType: portDef.tsType }),
747
- };
748
- }
749
- }
750
- // Ambient declarations (declare function) are stub nodes — interface only, no implementation.
751
- // Force expression mode so ports are inferred from the TypeScript signature.
752
- const isStub = fn.isAmbient?.() ?? false;
753
- if (isStub) {
754
- config.expression = true;
755
- }
756
- // Auto-infer ports for @expression nodes when @input/@output are missing.
757
- // If the function has @expression but no explicit port annotations, infer
758
- // data ports from the TypeScript function signature (same logic as unannotated functions).
759
- if (config.expression) {
760
- const hasExplicitDataInputs = Object.keys(inputs).some((k) => k !== 'execute');
761
- const hasExplicitDataOutputs = Object.keys(outputs).some((k) => k !== 'onSuccess' && k !== 'onFailure');
762
- if (!hasExplicitDataInputs || !hasExplicitDataOutputs) {
763
- const inferred = this.inferNodeTypeFromFunction(fn, nodeTypeName, fn.getSourceFile().getFilePath());
764
- if (!hasExplicitDataInputs) {
765
- // Copy inferred data inputs (skip control flow ports)
766
- for (const [portName, portDef] of Object.entries(inferred.inputs)) {
767
- if (portName === 'execute')
768
- continue;
769
- inputs[portName] = portDef;
770
- }
771
- }
772
- if (!hasExplicitDataOutputs) {
773
- // Copy inferred data outputs (skip control flow ports)
774
- for (const [portName, portDef] of Object.entries(inferred.outputs)) {
775
- if (portName === 'onSuccess' || portName === 'onFailure')
776
- continue;
777
- outputs[portName] = portDef;
778
- }
779
- }
780
- }
781
- }
782
- // ALL nodes must have execute input and onSuccess/onFailure outputs
783
- // Execute port is visible by default so users can connect execution flow
784
- // Merge user-defined ports with mandatory defaults to preserve special properties
785
- inputs.execute = {
786
- label: 'Execute', // Default label
787
- ...inputs.execute, // User can override label
788
- dataType: 'STEP', // But dataType is mandatory
789
- };
790
- outputs.onSuccess = {
791
- label: 'On Success', // Default label
792
- ...outputs.onSuccess, // User can override label
793
- dataType: 'STEP', // But dataType is mandatory
794
- isControlFlow: true, // Always a control flow port
795
- };
796
- outputs.onFailure = {
797
- label: 'On Failure', // Default label
798
- ...outputs.onFailure, // User can override label
799
- dataType: 'STEP', // But dataType is mandatory
800
- failure: true, // Always a failure port
801
- isControlFlow: true, // Always a control flow port
802
- };
803
- // Assign implicit port orders with mandatory port precedence
804
- assignImplicitPortOrders(inputs);
805
- assignImplicitPortOrders(outputs);
806
- // Get function text (JSDoc comment + function). Stubs have no body to capture.
807
- const jsDocs = fn.getJsDocs();
808
- const jsDocText = jsDocs.map((doc) => doc.getText()).join('\n');
809
- const functionText = isStub ? undefined : (jsDocText ? `${jsDocText}\n${fn.getText()}` : fn.getText());
810
- // Detect async keyword on function declaration
811
- const isAsync = fn.isAsync();
812
- // Convert defaultConfig
813
- let defaultConfig = undefined;
814
- if (config.defaultConfig) {
815
- defaultConfig = {
816
- label: config.defaultConfig.label,
817
- description: config.defaultConfig.description,
818
- pullExecution: config.defaultConfig.pullExecution,
819
- };
820
- }
821
- // Extract unique scope names from ports (per-port scoped architecture)
822
- const portScopes = new Set();
823
- Object.values(inputs).forEach((port) => {
824
- if (port.scope)
825
- portScopes.add(port.scope);
826
- });
827
- Object.values(outputs).forEach((port) => {
828
- if (port.scope)
829
- portScopes.add(port.scope);
830
- });
831
- // Determine scopes array:
832
- // - If node has node-level scope: use that (old architecture)
833
- // - Otherwise if ports have scopes: use unique port scopes ordered by function parameter position
834
- // - Otherwise: undefined (no scopes)
835
- let scopes;
836
- if (config.scope) {
837
- scopes = [config.scope];
838
- }
839
- else if (portScopes.size > 0) {
840
- // Order scopes by function parameter position (callback params whose name matches a scope)
841
- const orderedScopes = [];
842
- try {
843
- const params = fn.getParameters();
844
- for (const param of params) {
845
- const paramName = param.getName();
846
- if (portScopes.has(paramName)) {
847
- orderedScopes.push(paramName);
848
- }
849
- }
850
- }
851
- catch {
852
- // Fall back to Set order if parameter extraction fails
853
- }
854
- // Add any scopes not found as params (e.g. from JSDoc-only scope declarations)
855
- for (const scope of portScopes) {
856
- if (!orderedScopes.includes(scope)) {
857
- orderedScopes.push(scope);
858
- }
859
- }
860
- scopes = orderedScopes;
861
- }
862
- nodeTypes.push({
863
- type: 'NodeType',
864
- name: nodeTypeName,
865
- functionName,
866
- variant: isStub ? 'STUB' : 'FUNCTION',
867
- inputs,
868
- outputs,
869
- hasSuccessPort: RESERVED_PORT_NAMES.ON_SUCCESS in outputs,
870
- hasFailurePort: RESERVED_PORT_NAMES.ON_FAILURE in outputs,
871
- isAsync,
872
- functionText,
873
- executeWhen: config.executeWhen || EXECUTION_STRATEGIES.CONJUNCTION,
874
- defaultConfig,
875
- scope: config.scope,
876
- scopes,
877
- ...(config.expression && { expression: true }),
878
- ...(fn.getDeclarationKind?.() && { declarationKind: fn.getDeclarationKind() }),
879
- label: config.label,
880
- description: config.description,
881
- visuals: config.color || config.icon || config.tags
882
- ? {
883
- color: config.color,
884
- icon: config.icon,
885
- tags: config.tags,
886
- }
887
- : undefined,
888
- ...(config.deploy && { deploy: config.deploy }),
889
- sourceLocation: {
890
- file: sourceFile.getFilePath(),
891
- line: fn.getStartLineNumber(false),
892
- column: 0,
893
- },
894
- });
895
- });
896
- return nodeTypes;
897
- }
898
- /**
899
- * Extract workflow signatures (metadata only) without validating instances.
900
- * This enables same-file workflow invocation by making workflow ports available
901
- * before the full workflow extraction pass.
902
- */
903
- extractWorkflowSignatures(sourceFile, filePath, warnings) {
904
- const workflows = [];
905
- extractFunctionLikes(sourceFile).forEach((fn) => {
906
- const config = jsdocParser.parseWorkflow(fn, warnings, this.tagRegistry);
907
- if (!config)
908
- return;
909
- const functionName = fn.getName() || 'anonymous';
910
- const startPorts = this.parseStartPorts(fn, config);
911
- const exitPorts = this.parseExitPorts(fn, config);
912
- const userSpecifiedAsync = fn.isAsync();
913
- workflows.push({
914
- type: 'Workflow',
915
- sourceFile: filePath,
916
- name: config.name || functionName,
917
- functionName,
918
- nodeTypes: [],
919
- instances: [],
920
- connections: [],
921
- startPorts,
922
- exitPorts,
923
- imports: [],
924
- description: config.description,
925
- userSpecifiedAsync,
926
- });
927
- });
928
- return workflows;
929
- }
930
- /**
931
- * Convert a workflow to a node type.
932
- * This allows workflows to be used as nodes in other workflows.
933
- */
934
- workflowToNodeType(workflow) {
935
- return {
936
- type: 'NodeType',
937
- name: workflow.name,
938
- functionName: workflow.functionName,
939
- variant: 'IMPORTED_WORKFLOW',
940
- path: workflow.sourceFile,
941
- inputs: { ...workflow.startPorts },
942
- outputs: { ...workflow.exitPorts },
943
- hasSuccessPort: 'onSuccess' in workflow.exitPorts,
944
- hasFailurePort: 'onFailure' in workflow.exitPorts,
945
- isAsync: workflow.userSpecifiedAsync || false,
946
- executeWhen: EXECUTION_STRATEGIES.CONJUNCTION,
947
- description: workflow.description,
948
- sourceLocation: {
949
- file: workflow.sourceFile,
950
- line: 0,
951
- column: 0,
952
- },
953
- };
954
- }
955
- extractWorkflows(sourceFile, availableNodeTypes, filePath, errors, warnings) {
956
- const workflows = [];
957
- const allFunctions = extractFunctionLikes(sourceFile);
958
- // Collect all function names in the file for unannotated-function hints in validator
959
- const allFunctionNames = allFunctions
960
- .map((fn) => fn.getName())
961
- .filter((name) => !!name);
962
- allFunctions.forEach((fn) => {
963
- // Parse JSDoc comments
964
- const config = jsdocParser.parseWorkflow(fn, warnings, this.tagRegistry);
965
- if (!config) {
966
- const jsdocText = fn.getJsDocs().map((d) => d.getFullText()).join('');
967
- if (jsdocText.includes('@flowWeaver workflow')) {
968
- warnings.push(`Function "${fn.getName() || 'anonymous'}" has @flowWeaver annotation but could not be parsed. ` +
969
- `Check for special characters (---) or malformed JSDoc syntax.`);
970
- }
971
- return;
972
- }
973
- const functionName = fn.getName() || 'anonymous';
974
- // Validate no IN/OUT pseudo-nodes in workflows (they're only for patterns)
975
- if (config.connections) {
976
- for (const conn of config.connections) {
977
- if (conn.from.node === 'IN' || conn.from.node === 'OUT') {
978
- errors.push(`Workflow "${functionName}" uses "${conn.from.node}" pseudo-node which is only valid in patterns. Use "Start" or "Exit" instead.`);
979
- }
980
- if (conn.to.node === 'IN' || conn.to.node === 'OUT') {
981
- errors.push(`Workflow "${functionName}" uses "${conn.to.node}" pseudo-node which is only valid in patterns. Use "Start" or "Exit" instead.`);
982
- }
983
- }
984
- }
985
- // Detect async keyword on workflow function declaration
986
- const userSpecifiedAsync = fn.isAsync();
987
- const startPorts = this.parseStartPorts(fn, config);
988
- const exitPorts = this.parseExitPorts(fn, config);
989
- // Convert @fwImport annotations to properly inferred node types
990
- // These are persisted in JSDoc so they survive file re-parsing
991
- // Uses the same inference logic as TS imports for consistency
992
- const importedNpmNodeTypes = (config.imports || []).map((imp) => this.resolveImportAnnotation(imp, filePath, warnings));
993
- // Post-resolution check: warn when inferred node type has zero data ports
994
- // (excluding control-flow ports). This usually means the .d.ts inference
995
- // couldn't extract meaningful port info and a local wrapper is needed.
996
- for (const nt of importedNpmNodeTypes) {
997
- const dataInputs = Object.keys(nt.inputs).filter((p) => p !== 'execute');
998
- const nonControlOutputs = Object.keys(nt.outputs).filter((p) => p !== 'onSuccess' && p !== 'onFailure');
999
- // Stub fallback has only result: ANY — if that's all we have with zero
1000
- // data inputs, inference likely failed
1001
- const isStubOnly = nonControlOutputs.length === 1 &&
1002
- nonControlOutputs[0] === 'result' &&
1003
- nt.outputs.result?.dataType === 'ANY';
1004
- if (dataInputs.length === 0 && isStubOnly) {
1005
- warnings.push(`Could not infer ports for "${nt.functionName}" from "${nt.importSource}". ` +
1006
- `Wrap it in a local function with @flowWeaver nodeType annotations instead.`);
1007
- }
1008
- }
1009
- // Combine available node types with imported npm types for validation
1010
- const allAvailableNodeTypes = [...availableNodeTypes, ...importedNpmNodeTypes];
1011
- // Convert instances to NodeInstanceAST
1012
- const instances = (config.instances || []).map((inst) => {
1013
- // Validate node type exists — push error instead of throwing so that
1014
- // partial parse results remain usable (defense-in-depth for race conditions)
1015
- const nodeTypeExists = allAvailableNodeTypes.some((nt) => nt.name === inst.type || nt.functionName === inst.type);
1016
- if (!nodeTypeExists) {
1017
- errors.push(`Node type "${inst.type}" not found in workflow "${functionName}". ` +
1018
- `Available types: ${allAvailableNodeTypes.map((nt) => nt.functionName).join(', ') || '(none)'}`);
1019
- }
1020
- const position = config.positions?.[inst.id];
1021
- // Convert parentScope string "nodeName.scope" to parent object
1022
- let parent;
1023
- if (inst.parentScope) {
1024
- const dotIndex = inst.parentScope.indexOf('.');
1025
- if (dotIndex > 0) {
1026
- parent = {
1027
- id: inst.parentScope.substring(0, dotIndex),
1028
- scope: inst.parentScope.substring(dotIndex + 1),
1029
- };
1030
- }
1031
- }
1032
- // portConfigs are direction-agnostic (annotations don't carry direction info).
1033
- // Matching code handles undefined direction by matching any direction.
1034
- const portConfigs = inst.portConfigs;
1035
- return {
1036
- type: 'NodeInstance',
1037
- id: inst.id,
1038
- nodeType: inst.type,
1039
- ...(parent && { parent }),
1040
- config: {
1041
- ...(inst.label && { label: inst.label }),
1042
- ...(inst.x !== undefined && inst.y !== undefined && { x: inst.x, y: inst.y }),
1043
- ...(position && { x: position.x, y: position.y }),
1044
- ...(portConfigs && portConfigs.length > 0 && { portConfigs }),
1045
- ...(inst.pullExecution && { pullExecution: inst.pullExecution }),
1046
- ...(inst.minimized && { minimized: inst.minimized }),
1047
- ...(inst.color && { color: inst.color }),
1048
- ...(inst.icon && { icon: inst.icon }),
1049
- ...(inst.tags && inst.tags.length > 0 && { tags: inst.tags }),
1050
- ...(inst.width && { width: inst.width }),
1051
- ...(inst.height && { height: inst.height }),
1052
- ...(inst.suppressWarnings?.length && { suppressWarnings: inst.suppressWarnings }),
1053
- },
1054
- ...(inst.sourceLocation && {
1055
- sourceLocation: { file: filePath, ...inst.sourceLocation },
1056
- }),
1057
- ...(inst.job && { job: inst.job }),
1058
- ...(inst.environment && { environment: inst.environment }),
1059
- };
1060
- });
1061
- // Convert connections to ConnectionAST
1062
- const connections = (config.connections || []).map((conn) => ({
1063
- type: 'Connection',
1064
- from: conn.from,
1065
- to: conn.to,
1066
- ...(conn.sourceLocation && { sourceLocation: { file: filePath, ...conn.sourceLocation } }),
1067
- }));
1068
- // Auto-connect: when @autoConnect is set and no explicit @connect annotations exist,
1069
- // auto-wire linear connections between nodes in declaration order
1070
- if (config.autoConnect && connections.length === 0 && instances.length > 0) {
1071
- const autoConnections = this.generateAutoConnections(instances, allAvailableNodeTypes, startPorts, exitPorts);
1072
- connections.push(...autoConnections);
1073
- }
1074
- // Expand @map macros into synthetic node types, instances, connections, and scopes
1075
- const scopes = config.scopes || {};
1076
- const macros = [];
1077
- if (config.maps && config.maps.length > 0) {
1078
- for (const mapConfig of config.maps) {
1079
- this.expandMapMacro(mapConfig, instances, connections, scopes, allAvailableNodeTypes, macros, errors, warnings);
1080
- }
1081
- }
1082
- // Expand @path macros into multi-step execution routes with scope walking
1083
- if (config.paths && config.paths.length > 0) {
1084
- this.expandPathMacros(config.paths, instances, connections, allAvailableNodeTypes, startPorts, exitPorts, macros, errors, warnings);
1085
- }
1086
- // Expand @fanOut macros into 1-to-N connections
1087
- if (config.fanOuts && config.fanOuts.length > 0) {
1088
- this.expandFanOutMacros(config.fanOuts, instances, connections, startPorts, exitPorts, macros, errors);
1089
- }
1090
- // Expand @fanIn macros into N-to-1 connections
1091
- if (config.fanIns && config.fanIns.length > 0) {
1092
- this.expandFanInMacros(config.fanIns, instances, connections, startPorts, exitPorts, macros, errors);
1093
- }
1094
- // Expand @coerce macros into synthetic coercion nodes + connections
1095
- if (config.coercions && config.coercions.length > 0) {
1096
- this.expandCoerceMacros(config.coercions, instances, connections, startPorts, exitPorts, macros, errors);
1097
- }
1098
- // Include ALL available nodeTypes in the workflow AST, plus imported npm types.
1099
- // Previously this filtered to only nodeTypes used by instances, but that caused
1100
- // a bug: when creating a new nodeType and then adding its first instance,
1101
- // the second operation would re-parse the file, see no instances using the
1102
- // nodeType yet, filter it out, and then removeOrphanedNodeTypeFunctions would
1103
- // delete the nodeType function that was just written.
1104
- // NPM types come from @import annotations in JSDoc (persisted to survive re-parsing).
1105
- // Deduplicate: @fwImport types take precedence over external/runtime types with the same name.
1106
- // Without this, each parse+generate cycle adds one more duplicate @fwImport entry.
1107
- const importedNames = new Set(importedNpmNodeTypes.map((nt) => nt.name));
1108
- // Use allAvailableNodeTypes (includes synthetic MAP_ITERATOR types from @map macros)
1109
- const dedupedAvailableTypes = allAvailableNodeTypes.filter((nt) => !importedNames.has(nt.name));
1110
- const workflowNodeTypes = [...dedupedAvailableTypes, ...importedNpmNodeTypes];
1111
- // Inject synthetic coercion node types for any __fw_ instances
1112
- for (const inst of instances) {
1113
- if (inst.nodeType.startsWith('__fw_') && COERCION_NODE_TYPES[inst.nodeType]) {
1114
- if (!workflowNodeTypes.some(nt => nt.functionName === inst.nodeType)) {
1115
- workflowNodeTypes.push(COERCION_NODE_TYPES[inst.nodeType]);
1116
- }
1117
- }
1118
- }
1119
- // Extract Start/Exit positions from config.positions
1120
- const ui = {};
1121
- const startPosition = config.positions?.['Start'];
1122
- if (startPosition) {
1123
- ui.startNode = { x: startPosition.x, y: startPosition.y };
1124
- }
1125
- const exitPosition = config.positions?.['Exit'];
1126
- if (exitPosition) {
1127
- ui.exitNode = { x: exitPosition.x, y: exitPosition.y };
1128
- }
1129
- workflows.push({
1130
- type: 'Workflow',
1131
- sourceFile: filePath,
1132
- name: config.name || functionName,
1133
- functionName: functionName,
1134
- nodeTypes: workflowNodeTypes,
1135
- instances,
1136
- connections,
1137
- scopes,
1138
- startPorts,
1139
- exitPorts,
1140
- imports: [],
1141
- description: config.description,
1142
- userSpecifiedAsync,
1143
- availableFunctionNames: allFunctionNames,
1144
- ...(macros.length > 0 && { macros }),
1145
- ...(Object.keys(ui).length > 0 && { ui }),
1146
- ...((config.strictTypes !== undefined || config.autoConnect ||
1147
- config.trigger || config.cancelOn || config.retries !== undefined ||
1148
- config.timeout || config.throttle || config.deploy) && {
1149
- options: {
1150
- ...(config.strictTypes !== undefined && { strictTypes: config.strictTypes }),
1151
- ...(config.autoConnect && { autoConnect: true }),
1152
- ...(config.trigger && { trigger: config.trigger }),
1153
- ...(config.cancelOn && { cancelOn: config.cancelOn }),
1154
- ...(config.retries !== undefined && { retries: config.retries }),
1155
- ...(config.timeout && { timeout: config.timeout }),
1156
- ...(config.throttle && { throttle: config.throttle }),
1157
- // CI/CD data comes from deploy['cicd'] (populated by extension tag handler)
1158
- ...(config.deploy?.['cicd'] && {
1159
- cicd: config.deploy['cicd'],
1160
- }),
1161
- // Per-target deployment config
1162
- ...(config.deploy && { deploy: config.deploy }),
1163
- },
1164
- }),
1165
- });
1166
- });
1167
- return workflows;
1168
- }
1169
- /**
1170
- * Extract patterns from a source file.
1171
- * Patterns are defined with @flowWeaver pattern annotation.
1172
- */
1173
- extractPatterns(sourceFile, availableNodeTypes, filePath, errors, warnings) {
1174
- const patterns = [];
1175
- const seenNames = new Set();
1176
- extractFunctionLikes(sourceFile).forEach((fn) => {
1177
- // Parse JSDoc comments for pattern
1178
- const config = jsdocParser.parsePattern(fn, warnings);
1179
- if (!config) {
1180
- const jsdocText = fn.getJsDocs().map((d) => d.getFullText()).join('');
1181
- if (jsdocText.includes('@flowWeaver pattern')) {
1182
- warnings.push(`Function "${fn.getName() || 'anonymous'}" has @flowWeaver annotation but could not be parsed. ` +
1183
- `Check for special characters (---) or malformed JSDoc syntax.`);
1184
- }
1185
- return;
1186
- }
1187
- // Validate required @name
1188
- if (!config.name) {
1189
- errors.push(`Pattern is missing required @name tag in function "${fn.getName()}"`);
1190
- return;
1191
- }
1192
- // Check for duplicate names
1193
- if (seenNames.has(config.name)) {
1194
- errors.push(`Duplicate pattern name "${config.name}" in file`);
1195
- return;
1196
- }
1197
- seenNames.add(config.name);
1198
- // Extract node types used by this pattern
1199
- const patternNodeTypes = availableNodeTypes.filter((nt) => config.instances?.some((inst) => inst.nodeType === nt.name));
1200
- // Build connections from config
1201
- const connections = (config.connections || []).map((conn) => ({
1202
- type: 'Connection',
1203
- from: conn.from,
1204
- to: conn.to,
1205
- }));
1206
- // Extract input/output ports from @port declarations
1207
- const inputPorts = {};
1208
- const outputPorts = {};
1209
- if (config.ports) {
1210
- for (const port of config.ports) {
1211
- if (port.direction === 'IN') {
1212
- inputPorts[port.name] = { description: port.description };
1213
- }
1214
- else if (port.direction === 'OUT') {
1215
- outputPorts[port.name] = { description: port.description };
1216
- }
1217
- }
1218
- }
1219
- // Build instances from config
1220
- const instances = (config.instances || []).map((inst) => ({
1221
- type: 'NodeInstance',
1222
- id: inst.id,
1223
- nodeType: inst.nodeType,
1224
- config: inst.config || {},
1225
- }));
1226
- patterns.push({
1227
- type: 'Pattern',
1228
- sourceFile: filePath,
1229
- name: config.name,
1230
- description: config.description,
1231
- nodeTypes: patternNodeTypes,
1232
- instances,
1233
- connections,
1234
- inputPorts,
1235
- outputPorts,
1236
- });
1237
- });
1238
- return patterns;
1239
- }
1240
- /**
1241
- * Infer a TNodeTypeAST from a single function's TypeScript signature.
1242
- * Shared helper used by both same-file and cross-file inference.
1243
- */
1244
- inferNodeTypeFromFunction(fn, name, filePath) {
1245
- // Infer inputs from parameters
1246
- const inputs = {};
1247
- const params = fn.getParameters();
1248
- const firstParamIsExecute = params.length > 0 && params[0].getName() === 'execute';
1249
- for (const param of params) {
1250
- const paramName = param.getName();
1251
- const tsType = param.getType().getText(param);
1252
- const dataType = inferDataTypeFromTS(tsType);
1253
- const optional = param.isOptional() || param.hasInitializer();
1254
- inputs[paramName] = {
1255
- dataType,
1256
- optional: optional || undefined,
1257
- label: this.capitalize(paramName),
1258
- tsType,
1259
- };
1260
- }
1261
- // Infer outputs from return type
1262
- const outputs = {};
1263
- let returnType = fn.getReturnType();
1264
- const returnTypeText = returnType.getText();
1265
- // Unwrap Promise<T>
1266
- if (returnTypeText.startsWith('Promise<')) {
1267
- const typeArgs = returnType.getTypeArguments();
1268
- if (typeArgs && typeArgs.length > 0) {
1269
- returnType = typeArgs[0];
1270
- }
1271
- }
1272
- const unwrappedText = returnType.getText();
1273
- if (unwrappedText !== 'void' && unwrappedText !== 'undefined') {
1274
- const primitiveTypes = new Set(['string', 'number', 'boolean', 'any', 'unknown', 'never']);
1275
- const isPrimitive = primitiveTypes.has(unwrappedText);
1276
- const isArray = unwrappedText.endsWith('[]') || unwrappedText.startsWith('Array<');
1277
- const properties = returnType.getProperties();
1278
- const isObjectLike = !isPrimitive && !isArray && returnType.isObject() && properties.length > 0;
1279
- if (isObjectLike) {
1280
- for (const prop of properties) {
1281
- const propName = prop.getName();
1282
- if (propName === 'onSuccess' || propName === 'onFailure')
1283
- continue;
1284
- const propType = prop.getTypeAtLocation(fn.getTypeResolutionNode());
1285
- const propTypeText = propType.getText();
1286
- const dataType = inferDataTypeFromTS(propTypeText);
1287
- outputs[propName] = {
1288
- dataType,
1289
- label: this.capitalize(propName),
1290
- tsType: propTypeText,
1291
- };
1292
- }
1293
- }
1294
- else {
1295
- const dataType = inferDataTypeFromTS(unwrappedText);
1296
- outputs.result = {
1297
- dataType,
1298
- label: 'Result',
1299
- tsType: unwrappedText,
1300
- };
1301
- }
1302
- }
1303
- // Add mandatory ports
1304
- inputs.execute = { dataType: 'STEP', label: 'Execute' };
1305
- outputs.onSuccess = { dataType: 'STEP', label: 'On Success', isControlFlow: true };
1306
- outputs.onFailure = {
1307
- dataType: 'STEP',
1308
- label: 'On Failure',
1309
- failure: true,
1310
- isControlFlow: true,
1311
- };
1312
- // Assign implicit port orders
1313
- assignImplicitPortOrders(inputs);
1314
- assignImplicitPortOrders(outputs);
1315
- // Build TNodeTypeAST
1316
- const jsDocs = fn.getJsDocs();
1317
- const jsDocText = jsDocs.map((doc) => doc.getText()).join('\n');
1318
- const functionText = jsDocText ? `${jsDocText}\n${fn.getText()}` : fn.getText();
1319
- return {
1320
- type: 'NodeType',
1321
- name,
1322
- functionName: name,
1323
- variant: 'FUNCTION',
1324
- inputs,
1325
- outputs,
1326
- hasSuccessPort: true,
1327
- hasFailurePort: true,
1328
- isAsync: fn.isAsync() || returnTypeText.startsWith('Promise<'),
1329
- executeWhen: EXECUTION_STRATEGIES.CONJUNCTION,
1330
- expression: !firstParamIsExecute, // Expression only if original function lacks execute as first param
1331
- inferred: true,
1332
- functionText,
1333
- ...(fn.getDeclarationKind?.() && {
1334
- declarationKind: fn.getDeclarationKind(),
1335
- }),
1336
- sourceLocation: {
1337
- file: filePath,
1338
- line: fn.getStartLineNumber(false),
1339
- column: 0,
1340
- },
1341
- };
1342
- }
1343
- /**
1344
- * Pre-infer ALL unannotated functions from a source file.
1345
- * Used for imported files so the named-import filter can scope them.
1346
- */
1347
- inferAllUnannotatedFunctions(sourceFile, existingNodeTypes) {
1348
- const allFunctions = extractFunctionLikes(sourceFile);
1349
- const existingNames = new Set();
1350
- for (const nt of existingNodeTypes) {
1351
- existingNames.add(nt.name);
1352
- existingNames.add(nt.functionName);
1353
- }
1354
- const inferred = [];
1355
- for (const fn of allFunctions) {
1356
- const fnName = fn.getName();
1357
- if (!fnName)
1358
- continue;
1359
- // Skip if already known (annotated or from another source)
1360
- if (existingNames.has(fnName))
1361
- continue;
1362
- // Must NOT have a valid @flowWeaver annotation
1363
- if (this.hasFlowWeaverAnnotation(fn))
1364
- continue;
1365
- inferred.push(this.inferNodeTypeFromFunction(fn, fnName, sourceFile.getFilePath()));
1366
- existingNames.add(fnName);
1367
- }
1368
- return inferred;
1369
- }
1370
- /**
1371
- * Auto-infer node types from unannotated functions referenced by @node.
1372
- *
1373
- * When a workflow references a function via @node that has no @flowWeaver
1374
- * nodeType annotation, we infer an expression node type from its TypeScript
1375
- * signature. Phase 1: same-file functions only.
1376
- */
1377
- inferNodeTypesFromUnannotated(sourceFile, existingNodeTypes, localNodeTypes, warnings) {
1378
- const allFunctions = extractFunctionLikes(sourceFile);
1379
- // 1. Pre-scan workflows for @node references to collect referenced type names
1380
- const referencedTypes = new Set();
1381
- for (const fn of allFunctions) {
1382
- const config = jsdocParser.parseWorkflow(fn, []);
1383
- if (!config)
1384
- continue;
1385
- for (const inst of config.instances || []) {
1386
- referencedTypes.add(inst.type);
1387
- }
1388
- }
1389
- // 2. Find unresolved types: referenced but not in existingNodeTypes
1390
- const existingNames = new Set();
1391
- for (const nt of existingNodeTypes) {
1392
- existingNames.add(nt.name);
1393
- existingNames.add(nt.functionName);
1394
- }
1395
- const unresolvedTypes = new Set();
1396
- for (const typeName of referencedTypes) {
1397
- if (!existingNames.has(typeName)) {
1398
- unresolvedTypes.add(typeName);
1399
- }
1400
- }
1401
- if (unresolvedTypes.size === 0)
1402
- return [];
1403
- // 3. Match unresolved types to unannotated functions OR built-in nodes
1404
- const inferredNodeTypes = [];
1405
- const alreadyInferred = new Set();
1406
- const builtInByName = new Map(BUILT_IN_NODE_TYPES.map((nt) => [nt.name, nt]));
1407
- const annotatedNames = new Set(localNodeTypes.map((nt) => nt.functionName));
1408
- for (const unresolvedType of unresolvedTypes) {
1409
- if (alreadyInferred.has(unresolvedType))
1410
- continue;
1411
- // 3a. Check built-in nodes first
1412
- const builtIn = builtInByName.get(unresolvedType);
1413
- if (builtIn) {
1414
- inferredNodeTypes.push(builtIn);
1415
- alreadyInferred.add(unresolvedType);
1416
- // Warn if a local unannotated function has the same name (it will be shadowed)
1417
- if (!annotatedNames.has(unresolvedType)) {
1418
- const shadowFn = allFunctions.find((fn) => fn.getName() === unresolvedType && !this.hasFlowWeaverAnnotation(fn));
1419
- if (shadowFn) {
1420
- warnings.push(`Function '${unresolvedType}' exists in this file but is not annotated with @flowWeaver nodeType. ` +
1421
- `The built-in '${unresolvedType}' will be used instead. Add @flowWeaver nodeType to use your version.`);
1422
- }
1423
- }
1424
- continue;
1425
- }
1426
- // 3b. Match unannotated local function
1427
- const matchedFn = allFunctions.find((fn) => {
1428
- if (fn.getName() !== unresolvedType)
1429
- return false;
1430
- return !this.hasFlowWeaverAnnotation(fn);
1431
- });
1432
- if (!matchedFn)
1433
- continue;
1434
- inferredNodeTypes.push(this.inferNodeTypeFromFunction(matchedFn, unresolvedType, sourceFile.getFilePath()));
1435
- alreadyInferred.add(unresolvedType);
1436
- }
1437
- return inferredNodeTypes;
1438
- }
1439
- /**
1440
- * Expand a @map macro into synthetic node type, instances, connections, and scope.
1441
- *
1442
- * @map loop proc over scan.files
1443
- *
1444
- * Expands to:
1445
- * - A synthetic MAP_ITERATOR node type for "loop" with scoped ports
1446
- * - An instance "loop" of that synthetic type
1447
- * - The child instance "proc" placed inside loop.iterate scope
1448
- * - All scoped connections auto-generated
1449
- * - Upstream connection from scan.files -> loop.items
1450
- */
1451
- expandMapMacro(mapConfig, instances, connections, scopes, availableNodeTypes, macros, errors, _warnings) {
1452
- const { instanceId, childId, sourceNode, sourcePort } = mapConfig;
1453
- const scopeName = 'iterate';
1454
- // Find the child node instance (must already be declared via @node)
1455
- const childInstance = instances.find((inst) => inst.id === childId);
1456
- if (!childInstance) {
1457
- errors.push(`@map "${instanceId}": child node "${childId}" not found. Declare it with @node before using @map.`);
1458
- return;
1459
- }
1460
- // Find the child's node type to determine ports
1461
- const childNodeType = availableNodeTypes.find((nt) => nt.name === childInstance.nodeType || nt.functionName === childInstance.nodeType);
1462
- if (!childNodeType) {
1463
- errors.push(`@map "${instanceId}": node type "${childInstance.nodeType}" for child "${childId}" not found.`);
1464
- return;
1465
- }
1466
- // Determine input/output ports on the child
1467
- // Auto-infer: first non-execute data input, first non-control-flow data output
1468
- let inputPort = mapConfig.inputPort;
1469
- let outputPort = mapConfig.outputPort;
1470
- if (!inputPort) {
1471
- const dataInputs = Object.entries(childNodeType.inputs).filter(([name, def]) => name !== 'execute' && def.dataType !== 'STEP');
1472
- if (dataInputs.length === 0) {
1473
- errors.push(`@map "${instanceId}": child node "${childId}" has no data input ports to receive items.`);
1474
- return;
1475
- }
1476
- inputPort = dataInputs[0][0];
1477
- }
1478
- if (!outputPort) {
1479
- const dataOutputs = Object.entries(childNodeType.outputs).filter(([name, def]) => name !== 'onSuccess' && name !== 'onFailure' && def.dataType !== 'STEP' && !def.isControlFlow && !def.failure);
1480
- if (dataOutputs.length === 0) {
1481
- errors.push(`@map "${instanceId}": child node "${childId}" has no data output ports for results.`);
1482
- return;
1483
- }
1484
- outputPort = dataOutputs[0][0];
1485
- }
1486
- // Get type info from child ports for the synthetic type
1487
- const childInputDef = childNodeType.inputs[inputPort];
1488
- const childOutputDef = childNodeType.outputs[outputPort];
1489
- // Create synthetic MAP_ITERATOR node type
1490
- const syntheticTypeName = `__map_${instanceId}__`;
1491
- const syntheticNodeType = {
1492
- type: 'NodeType',
1493
- name: syntheticTypeName,
1494
- functionName: syntheticTypeName,
1495
- variant: 'MAP_ITERATOR',
1496
- isAsync: true,
1497
- executeWhen: 'CONJUNCTION',
1498
- hasSuccessPort: true,
1499
- hasFailurePort: true,
1500
- scope: scopeName,
1501
- scopes: [scopeName],
1502
- inputs: {
1503
- execute: { dataType: 'STEP', label: 'Execute' },
1504
- items: {
1505
- dataType: 'ARRAY',
1506
- label: 'Items',
1507
- tsType: childInputDef?.tsType ? `(${childInputDef.tsType})[]` : 'unknown[]',
1508
- },
1509
- // Scoped INPUT ports (receive from children)
1510
- success: { dataType: 'STEP', scope: scopeName },
1511
- failure: { dataType: 'STEP', scope: scopeName },
1512
- processed: {
1513
- dataType: childOutputDef?.dataType || 'ANY',
1514
- scope: scopeName,
1515
- ...(childOutputDef?.tsType && { tsType: childOutputDef.tsType }),
1516
- },
1517
- },
1518
- outputs: {
1519
- onSuccess: { dataType: 'STEP', label: 'On Success', isControlFlow: true },
1520
- onFailure: { dataType: 'STEP', label: 'On Failure', isControlFlow: true, failure: true },
1521
- results: {
1522
- dataType: 'ARRAY',
1523
- label: 'Results',
1524
- tsType: childOutputDef?.tsType ? `(${childOutputDef.tsType})[]` : 'unknown[]',
1525
- },
1526
- // Scoped OUTPUT ports (send to children)
1527
- start: { dataType: 'STEP', scope: scopeName },
1528
- item: {
1529
- dataType: childInputDef?.dataType || 'ANY',
1530
- scope: scopeName,
1531
- ...(childInputDef?.tsType && { tsType: childInputDef.tsType }),
1532
- },
1533
- },
1534
- };
1535
- // Add synthetic type to available types
1536
- availableNodeTypes.push(syntheticNodeType);
1537
- // Create instance for the map iterator
1538
- const mapInstance = {
1539
- type: 'NodeInstance',
1540
- id: instanceId,
1541
- nodeType: syntheticTypeName,
1542
- };
1543
- instances.push(mapInstance);
1544
- // Move child instance into the scope
1545
- childInstance.parent = { id: instanceId, scope: scopeName };
1546
- // Generate scoped connections
1547
- // loop.start:iterate -> proc.execute
1548
- connections.push({
1549
- type: 'Connection',
1550
- from: { node: instanceId, port: 'start', scope: scopeName },
1551
- to: { node: childId, port: 'execute', scope: scopeName },
1552
- });
1553
- // loop.item:iterate -> proc.<inputPort>
1554
- connections.push({
1555
- type: 'Connection',
1556
- from: { node: instanceId, port: 'item', scope: scopeName },
1557
- to: { node: childId, port: inputPort, scope: scopeName },
1558
- });
1559
- // proc.<outputPort> -> loop.processed:iterate
1560
- connections.push({
1561
- type: 'Connection',
1562
- from: { node: childId, port: outputPort, scope: scopeName },
1563
- to: { node: instanceId, port: 'processed', scope: scopeName },
1564
- });
1565
- // proc.onSuccess -> loop.success:iterate
1566
- connections.push({
1567
- type: 'Connection',
1568
- from: { node: childId, port: 'onSuccess', scope: scopeName },
1569
- to: { node: instanceId, port: 'success', scope: scopeName },
1570
- });
1571
- // proc.onFailure -> loop.failure:iterate
1572
- connections.push({
1573
- type: 'Connection',
1574
- from: { node: childId, port: 'onFailure', scope: scopeName },
1575
- to: { node: instanceId, port: 'failure', scope: scopeName },
1576
- });
1577
- // Generate upstream connection: source.port -> loop.items
1578
- connections.push({
1579
- type: 'Connection',
1580
- from: { node: sourceNode, port: sourcePort },
1581
- to: { node: instanceId, port: 'items' },
1582
- });
1583
- // Register scope
1584
- scopes[`${instanceId}.${scopeName}`] = [childId];
1585
- // Store macro for round-trip preservation
1586
- macros.push({
1587
- type: 'map',
1588
- instanceId,
1589
- childId,
1590
- sourcePort: `${sourceNode}.${sourcePort}`,
1591
- ...(mapConfig.inputPort && { inputPort: mapConfig.inputPort }),
1592
- ...(mapConfig.outputPort && { outputPort: mapConfig.outputPort }),
1593
- });
1594
- }
1595
- /**
1596
- * Expand @path macros into multi-step execution routes with scope walking.
1597
- * Processes all paths together for shared deduplication.
1598
- */
1599
- expandPathMacros(pathConfigs, instances, connections, availableNodeTypes, startPorts, exitPorts, macros, errors, warnings) {
1600
- // Helper to find a node type by name or functionName
1601
- const findNodeType = (nodeTypeName) => availableNodeTypes.find((nt) => nt.name === nodeTypeName || nt.functionName === nodeTypeName);
1602
- // Helper to resolve instance → node type
1603
- const getNodeType = (nodeId) => {
1604
- const instance = instances.find((inst) => inst.id === nodeId);
1605
- if (!instance)
1606
- return undefined;
1607
- return findNodeType(instance.nodeType);
1608
- };
1609
- // Helper to get output ports for a node (handling Start/Exit)
1610
- const getOutputPorts = (nodeId) => {
1611
- if (nodeId === 'Start')
1612
- return startPorts;
1613
- if (nodeId === 'Exit')
1614
- return exitPorts;
1615
- const nodeType = getNodeType(nodeId);
1616
- return nodeType?.outputs || {};
1617
- };
1618
- // Helper to get input ports for a node (handling Start/Exit)
1619
- const getInputPorts = (nodeId) => {
1620
- if (nodeId === 'Exit')
1621
- return exitPorts;
1622
- if (nodeId === 'Start')
1623
- return startPorts;
1624
- const nodeType = getNodeType(nodeId);
1625
- return nodeType?.inputs || {};
1626
- };
1627
- // Build a set of existing connection keys for deduplication
1628
- const existingKeys = new Set();
1629
- for (const conn of connections) {
1630
- existingKeys.add(`${conn.from.node}.${conn.from.port}->${conn.to.node}.${conn.to.port}`);
1631
- }
1632
- const addConnection = (fromNode, fromPort, toNode, toPort) => {
1633
- const key = `${fromNode}.${fromPort}->${toNode}.${toPort}`;
1634
- if (existingKeys.has(key))
1635
- return;
1636
- existingKeys.add(key);
1637
- connections.push({
1638
- type: 'Connection',
1639
- from: { node: fromNode, port: fromPort },
1640
- to: { node: toNode, port: toPort },
1641
- });
1642
- };
1643
- for (const pathConfig of pathConfigs) {
1644
- const { steps } = pathConfig;
1645
- if (steps.length < 2) {
1646
- errors.push(`@path requires at least 2 steps, got ${steps.length}.`);
1647
- continue;
1648
- }
1649
- // Validate all node references exist
1650
- let valid = true;
1651
- for (const step of steps) {
1652
- if (step.node === 'Start' || step.node === 'Exit')
1653
- continue;
1654
- const instance = instances.find((inst) => inst.id === step.node);
1655
- if (!instance) {
1656
- errors.push(`@path: node "${step.node}" not found. Declare it with @node before using @path.`);
1657
- valid = false;
1658
- }
1659
- }
1660
- if (!valid)
1661
- continue;
1662
- // Generate connections for each consecutive pair
1663
- for (let i = 0; i < steps.length - 1; i++) {
1664
- const current = steps[i];
1665
- const next = steps[i + 1];
1666
- const currentId = current.node;
1667
- const nextId = next.node;
1668
- const route = current.route || 'ok';
1669
- // Control flow connection
1670
- if (currentId === 'Start') {
1671
- addConnection('Start', 'execute', nextId, 'execute');
1672
- }
1673
- else if (nextId === 'Exit') {
1674
- if (route === 'fail') {
1675
- addConnection(currentId, 'onFailure', 'Exit', 'onFailure');
1676
- }
1677
- else {
1678
- addConnection(currentId, 'onSuccess', 'Exit', 'onSuccess');
1679
- }
1680
- }
1681
- else {
1682
- if (route === 'fail') {
1683
- addConnection(currentId, 'onFailure', nextId, 'execute');
1684
- }
1685
- else {
1686
- addConnection(currentId, 'onSuccess', nextId, 'execute');
1687
- }
1688
- }
1689
- // Data port scope walking (skip Exit — no data inputs to wire)
1690
- if (nextId === 'Exit')
1691
- continue;
1692
- const nextInputs = getInputPorts(nextId);
1693
- for (const [inputName] of Object.entries(nextInputs)) {
1694
- if (isControlFlowPort(inputName))
1695
- continue;
1696
- // Skip auto-wiring if a manual @connect already targets this input port
1697
- const alreadyConnected = connections.some(c => c.to.node === nextId && c.to.port === inputName);
1698
- if (alreadyConnected)
1699
- continue;
1700
- // Walk backward through path steps to find nearest ancestor with same-name output
1701
- for (let j = i; j >= 0; j--) {
1702
- const ancestorId = steps[j].node;
1703
- const ancestorOutputs = getOutputPorts(ancestorId);
1704
- if (inputName in ancestorOutputs && !isControlFlowPort(inputName)) {
1705
- addConnection(ancestorId, inputName, nextId, inputName);
1706
- break;
1707
- }
1708
- }
1709
- }
1710
- }
1711
- // Store macro for round-trip preservation
1712
- macros.push({
1713
- type: 'path',
1714
- steps: steps.map(s => s.route ? { node: s.node, route: s.route } : { node: s.node }),
1715
- });
1716
- }
1717
- }
1718
- /**
1719
- * Expand @fanOut macros into 1-to-N connections.
1720
- */
1721
- expandFanOutMacros(fanOutConfigs, instances, connections, startPorts, exitPorts, macros, errors) {
1722
- const instanceIds = new Set(instances.map(i => i.id));
1723
- instanceIds.add('Start');
1724
- instanceIds.add('Exit');
1725
- for (const config of fanOutConfigs) {
1726
- const { source, targets } = config;
1727
- // Validate source node exists
1728
- if (!instanceIds.has(source.node)) {
1729
- errors.push(`@fanOut: source node "${source.node}" does not exist`);
1730
- continue;
1731
- }
1732
- let valid = true;
1733
- for (const target of targets) {
1734
- if (!instanceIds.has(target.node)) {
1735
- errors.push(`@fanOut: target node "${target.node}" does not exist`);
1736
- valid = false;
1737
- }
1738
- }
1739
- if (!valid)
1740
- continue;
1741
- // Create connections
1742
- for (const target of targets) {
1743
- const targetPort = target.port ?? source.port;
1744
- const conn = {
1745
- type: 'Connection',
1746
- from: { node: source.node, port: source.port },
1747
- to: { node: target.node, port: targetPort },
1748
- };
1749
- // Deduplicate
1750
- const exists = connections.some(c => c.from.node === conn.from.node && c.from.port === conn.from.port &&
1751
- c.to.node === conn.to.node && c.to.port === conn.to.port);
1752
- if (!exists) {
1753
- connections.push(conn);
1754
- }
1755
- }
1756
- // Store macro for round-trip preservation
1757
- macros.push({
1758
- type: 'fanOut',
1759
- source: { node: source.node, port: source.port },
1760
- targets: targets.map(t => t.port ? { node: t.node, port: t.port } : { node: t.node }),
1761
- });
1762
- }
1763
- }
1764
- /**
1765
- * Expand @fanIn macros into N-to-1 connections.
1766
- */
1767
- expandFanInMacros(fanInConfigs, instances, connections, startPorts, exitPorts, macros, errors) {
1768
- const instanceIds = new Set(instances.map(i => i.id));
1769
- instanceIds.add('Start');
1770
- instanceIds.add('Exit');
1771
- for (const config of fanInConfigs) {
1772
- const { sources, target } = config;
1773
- // Validate target node exists
1774
- if (!instanceIds.has(target.node)) {
1775
- errors.push(`@fanIn: target node "${target.node}" does not exist`);
1776
- continue;
1777
- }
1778
- let valid = true;
1779
- for (const source of sources) {
1780
- if (!instanceIds.has(source.node)) {
1781
- errors.push(`@fanIn: source node "${source.node}" does not exist`);
1782
- valid = false;
1783
- }
1784
- }
1785
- if (!valid)
1786
- continue;
1787
- // Create connections
1788
- for (const source of sources) {
1789
- const sourcePort = source.port ?? target.port;
1790
- const conn = {
1791
- type: 'Connection',
1792
- from: { node: source.node, port: sourcePort },
1793
- to: { node: target.node, port: target.port },
1794
- };
1795
- // Deduplicate
1796
- const exists = connections.some(c => c.from.node === conn.from.node && c.from.port === conn.from.port &&
1797
- c.to.node === conn.to.node && c.to.port === conn.to.port);
1798
- if (!exists) {
1799
- connections.push(conn);
1800
- }
1801
- }
1802
- // Store macro for round-trip preservation
1803
- macros.push({
1804
- type: 'fanIn',
1805
- sources: sources.map(s => s.port ? { node: s.node, port: s.port } : { node: s.node }),
1806
- target: { node: target.node, port: target.port },
1807
- });
1808
- }
1809
- }
1810
- /**
1811
- * Expand @coerce macros into synthetic coercion node instances + connections.
1812
- */
1813
- expandCoerceMacros(coerceConfigs, instances, connections, startPorts, exitPorts, macros, errors) {
1814
- const instanceIds = new Set(instances.map(i => i.id));
1815
- instanceIds.add('Start');
1816
- instanceIds.add('Exit');
1817
- for (const config of coerceConfigs) {
1818
- const { instanceId, source, target, targetType } = config;
1819
- // Validate source and target nodes exist
1820
- if (!instanceIds.has(source.node)) {
1821
- errors.push(`@coerce: source node "${source.node}" does not exist`);
1822
- continue;
1823
- }
1824
- if (!instanceIds.has(target.node)) {
1825
- errors.push(`@coerce: target node "${target.node}" does not exist`);
1826
- continue;
1827
- }
1828
- // Check for duplicate instance ID
1829
- if (instanceIds.has(instanceId)) {
1830
- errors.push(`@coerce: instance ID "${instanceId}" already exists`);
1831
- continue;
1832
- }
1833
- const nodeTypeName = COERCE_TYPE_MAP[targetType];
1834
- if (!nodeTypeName) {
1835
- errors.push(`@coerce: unknown target type "${targetType}"`);
1836
- continue;
1837
- }
1838
- // Add synthetic instance
1839
- instances.push({
1840
- type: 'NodeInstance',
1841
- id: instanceId,
1842
- nodeType: nodeTypeName,
1843
- });
1844
- instanceIds.add(instanceId);
1845
- // Add connections: source -> coercion.value, coercion.result -> target
1846
- connections.push({
1847
- type: 'Connection',
1848
- from: { node: source.node, port: source.port },
1849
- to: { node: instanceId, port: 'value' },
1850
- });
1851
- connections.push({
1852
- type: 'Connection',
1853
- from: { node: instanceId, port: 'result' },
1854
- to: { node: target.node, port: target.port },
1855
- });
1856
- // Store macro for round-trip preservation
1857
- macros.push({
1858
- type: 'coerce',
1859
- instanceId,
1860
- source: { node: source.node, port: source.port },
1861
- target: { node: target.node, port: target.port },
1862
- targetType,
1863
- });
1864
- }
1865
- }
1866
- /**
1867
- * Generate automatic connections for @autoConnect workflows.
1868
- * Wires nodes in declaration order as a linear pipeline:
1869
- * Start -> first node -> second node -> ... -> last node -> Exit
1870
- *
1871
- * For each consecutive pair:
1872
- * - Connect execute flow: previous.onSuccess -> next.execute
1873
- * - Connect data ports where output name matches input name
1874
- * For first node: Start.execute -> first.execute + match Start data ports to first inputs
1875
- * For last node: last.onSuccess -> Exit.execute + match last outputs to Exit ports
1876
- */
1877
- generateAutoConnections(instances, availableNodeTypes, startPorts, exitPorts) {
1878
- const connections = [];
1879
- // Helper to find a node type by name or functionName
1880
- const findNodeType = (nodeTypeName) => availableNodeTypes.find((nt) => nt.name === nodeTypeName || nt.functionName === nodeTypeName);
1881
- // Connect Start -> first node
1882
- if (instances.length > 0) {
1883
- const firstInstance = instances[0];
1884
- const firstNodeType = findNodeType(firstInstance.nodeType);
1885
- // Start.execute -> first.execute (execution flow)
1886
- connections.push({
1887
- type: 'Connection',
1888
- from: { node: 'Start', port: 'execute' },
1889
- to: { node: firstInstance.id, port: 'execute' },
1890
- });
1891
- // Match Start data ports to first node's data inputs
1892
- if (firstNodeType) {
1893
- for (const [portName, portDef] of Object.entries(startPorts)) {
1894
- if (portDef.dataType === 'STEP')
1895
- continue; // Skip control flow
1896
- if (portName in firstNodeType.inputs && !isControlFlowPort(portName)) {
1897
- connections.push({
1898
- type: 'Connection',
1899
- from: { node: 'Start', port: portName },
1900
- to: { node: firstInstance.id, port: portName },
1901
- });
1902
- }
1903
- }
1904
- }
1905
- }
1906
- // Connect consecutive nodes
1907
- for (let i = 0; i < instances.length - 1; i++) {
1908
- const current = instances[i];
1909
- const next = instances[i + 1];
1910
- const currentNodeType = findNodeType(current.nodeType);
1911
- const nextNodeType = findNodeType(next.nodeType);
1912
- // current.onSuccess -> next.execute (execution flow)
1913
- connections.push({
1914
- type: 'Connection',
1915
- from: { node: current.id, port: 'onSuccess' },
1916
- to: { node: next.id, port: 'execute' },
1917
- });
1918
- // Match data ports: current outputs -> next inputs (by matching port names)
1919
- if (currentNodeType && nextNodeType) {
1920
- for (const [outputName, outputDef] of Object.entries(currentNodeType.outputs)) {
1921
- if (outputDef.dataType === 'STEP' || isControlFlowPort(outputName))
1922
- continue;
1923
- if (outputName in nextNodeType.inputs && !isControlFlowPort(outputName)) {
1924
- connections.push({
1925
- type: 'Connection',
1926
- from: { node: current.id, port: outputName },
1927
- to: { node: next.id, port: outputName },
1928
- });
1929
- }
1930
- }
1931
- }
1932
- }
1933
- // Connect last node -> Exit
1934
- if (instances.length > 0) {
1935
- const lastInstance = instances[instances.length - 1];
1936
- const lastNodeType = findNodeType(lastInstance.nodeType);
1937
- // last.onSuccess -> Exit.onSuccess (execution flow)
1938
- connections.push({
1939
- type: 'Connection',
1940
- from: { node: lastInstance.id, port: 'onSuccess' },
1941
- to: { node: 'Exit', port: 'onSuccess' },
1942
- });
1943
- // Match last node's data outputs to Exit data ports
1944
- if (lastNodeType) {
1945
- for (const [portName, portDef] of Object.entries(exitPorts)) {
1946
- if (portDef.dataType === 'STEP' || portDef.isControlFlow)
1947
- continue;
1948
- if (lastNodeType.outputs[portName] && !isControlFlowPort(portName)) {
1949
- connections.push({
1950
- type: 'Connection',
1951
- from: { node: lastInstance.id, port: portName },
1952
- to: { node: 'Exit', port: portName },
1953
- });
1954
- }
1955
- }
1956
- }
1957
- }
1958
- return connections;
1959
- }
1960
- parseStartPorts(fn, config) {
1961
- const ports = {};
1962
- const params = fn.getParameters();
1963
- // New architecture: first parameter should be execute: boolean
1964
- // Second parameter is the params object with data
1965
- if (params.length === 0) {
1966
- // No parameters - just return execute port
1967
- ports.execute = { dataType: 'STEP', label: 'Execute' };
1968
- return ports;
1969
- }
1970
- // Check if first parameter is execute: boolean
1971
- const firstParam = params[0];
1972
- const firstParamName = firstParam.getName();
1973
- const firstParamType = firstParam.getType();
1974
- const firstParamTypeText = firstParamType.getText();
1975
- // Accept "execute: any" (from compiled .js files where types are stripped)
1976
- // when JSDoc @param confirms it as a step port.
1977
- const isExecutePort = firstParamName === 'execute' &&
1978
- (firstParamTypeText === 'boolean' || (firstParamTypeText === 'any' && config?.startPorts?.['execute']));
1979
- if (isExecutePort) {
1980
- // Correct new format: first param is execute
1981
- // Check if JSDoc has explicit metadata override for execute port (from @param annotation)
1982
- if (config?.startPorts && config.startPorts['execute']) {
1983
- ports.execute = {
1984
- dataType: 'STEP',
1985
- tsType: 'boolean',
1986
- ...config.startPorts['execute'],
1987
- };
1988
- }
1989
- else {
1990
- ports.execute = { dataType: 'STEP', tsType: 'boolean', label: 'Execute' };
1991
- }
1992
- // Extract data ports from parameters beyond execute
1993
- if (params.length > 1) {
1994
- // Filter out __abortSignal__ which is injected by generateInPlace and
1995
- // is not a user-visible port.
1996
- const dataParams = params.slice(1).filter(p => p.getName() !== '__abortSignal__');
1997
- // Multiple separate params: each becomes its own port
1998
- // Single param with object type: expand its properties into ports
1999
- const shouldExpandProperties = dataParams.length === 1 && this.isExpandableObjectType(dataParams[0].getType());
2000
- if (shouldExpandProperties) {
2001
- const dataParam = dataParams[0];
2002
- const dataParamType = dataParam.getType();
2003
- const properties = dataParamType.getProperties();
2004
- properties.forEach((prop) => {
2005
- const propName = prop.getName();
2006
- const propType = prop.getTypeAtLocation(dataParam);
2007
- const portType = this.inferPortType(propType);
2008
- const propTypeText = propType.getText();
2009
- const tsSchema = portType === 'OBJECT' ? this.extractTypeSchema(propType) : undefined;
2010
- const startPortConfig = config?.startPorts?.[propName];
2011
- ports[propName] = {
2012
- dataType: portType,
2013
- label: startPortConfig?.label || this.capitalize(propName),
2014
- ...(startPortConfig?.metadata && { metadata: startPortConfig.metadata }),
2015
- ...(propTypeText && { tsType: propTypeText }),
2016
- ...(tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }),
2017
- };
2018
- });
2019
- }
2020
- else {
2021
- // Each parameter becomes its own port
2022
- for (const param of dataParams) {
2023
- const paramName = param.getName();
2024
- const paramType = param.getType();
2025
- const portType = this.inferPortType(paramType);
2026
- const paramTypeText = paramType.getText(param);
2027
- const tsSchema = portType === 'OBJECT' ? this.extractTypeSchema(paramType) : undefined;
2028
- const startPortConfig = config?.startPorts?.[paramName];
2029
- ports[paramName] = {
2030
- dataType: portType,
2031
- label: startPortConfig?.label || this.capitalize(paramName),
2032
- ...(startPortConfig?.metadata && { metadata: startPortConfig.metadata }),
2033
- ...(paramTypeText && { tsType: paramTypeText }),
2034
- ...(tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }),
2035
- };
2036
- }
2037
- }
2038
- }
2039
- }
2040
- else {
2041
- // Old format detected - reject it
2042
- throw new Error(`Invalid node type function signature for "${fn.getName()}". ` +
2043
- `Expected first parameter to be "execute: boolean", but got "${firstParamName}: ${firstParamTypeText}". ` +
2044
- `Correct format: function ${fn.getName()}(execute: boolean, data: {...}) { ... }`);
2045
- }
2046
- // Assign implicit port orders with mandatory port precedence
2047
- assignImplicitPortOrders(ports);
2048
- return ports;
2049
- }
2050
- parseExitPorts(fn, config) {
2051
- const ports = {};
2052
- let returnType = fn.getReturnType();
2053
- // If return type is a Promise, extract the type parameter
2054
- const typeText = returnType?.getText();
2055
- const sourceFile = fn.getSourceFile();
2056
- const filePath = sourceFile?.getFilePath() || 'unknown file';
2057
- const fileName = path.basename(filePath);
2058
- if (!typeText || typeText === 'void') {
2059
- console.warn(`[PARSER] Could not determine return type for function "${fn.getName()}" in ${fileName}.\n` +
2060
- ` Add an explicit return type like: Promise<{ onSuccess: boolean; onFailure: boolean }>`);
2061
- return ports;
2062
- }
2063
- if (typeText.startsWith('Promise<')) {
2064
- const typeArgs = returnType.getTypeArguments();
2065
- if (typeArgs && typeArgs.length > 0) {
2066
- returnType = typeArgs[0];
2067
- }
2068
- }
2069
- const properties = returnType.getProperties();
2070
- properties.forEach((prop) => {
2071
- const propName = prop.getName();
2072
- // Always infer from TypeScript signature, use @returns annotation only for metadata
2073
- // onSuccess/onFailure are always STEP ports regardless of annotations
2074
- if (propName === 'onSuccess' || propName === 'onFailure') {
2075
- const returnPortConfig = config?.returnPorts?.[propName];
2076
- ports[propName] = {
2077
- dataType: 'STEP',
2078
- label: returnPortConfig?.label || (propName === 'onSuccess' ? 'On Success' : 'On Failure'),
2079
- isControlFlow: true,
2080
- ...(propName === 'onFailure' && { failure: true }),
2081
- ...(returnPortConfig?.metadata && { metadata: returnPortConfig.metadata }),
2082
- };
2083
- }
2084
- else {
2085
- // Auto-infer type from TypeScript signature (more accurate than JSDoc regex)
2086
- const propType = prop.getTypeAtLocation(fn.getTypeResolutionNode());
2087
- const portType = this.inferPortType(propType);
2088
- const propTypeText = propType.getText();
2089
- // Extract schema for complex types (interfaces/objects)
2090
- const tsSchema = portType === 'OBJECT' ? this.extractTypeSchema(propType) : undefined;
2091
- const returnPortConfig = config?.returnPorts?.[propName];
2092
- ports[propName] = {
2093
- dataType: portType,
2094
- label: returnPortConfig?.label || this.capitalize(propName),
2095
- ...(returnPortConfig?.metadata && { metadata: returnPortConfig.metadata }),
2096
- // Include original TS type for rich type display (e.g., "ResearchReport" instead of "object")
2097
- ...(propTypeText && propTypeText !== portType.toLowerCase() && { tsType: propTypeText }),
2098
- // Include schema breakdown for complex types
2099
- ...(tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }),
2100
- };
2101
- }
2102
- });
2103
- // Assign implicit port orders with mandatory port precedence
2104
- assignImplicitPortOrders(ports);
2105
- return ports;
2106
- }
2107
- /**
2108
- * Extract schema breakdown for complex types (interfaces/objects).
2109
- * Returns a map of property names to their TypeScript type strings.
2110
- */
2111
- extractTypeSchema(tsType) {
2112
- const schema = {};
2113
- const properties = tsType.getProperties();
2114
- if (!properties || properties.length === 0) {
2115
- return undefined;
2116
- }
2117
- for (const prop of properties) {
2118
- const propName = prop.getName();
2119
- // Skip internal/private properties
2120
- if (propName.startsWith('_'))
2121
- continue;
2122
- const propType = prop.getValueDeclaration()?.getType();
2123
- if (propType) {
2124
- schema[propName] = propType.getText();
2125
- }
2126
- else {
2127
- // Fallback: try to get type from declarations
2128
- const declarations = prop.getDeclarations();
2129
- if (declarations && declarations.length > 0) {
2130
- const decl = declarations[0];
2131
- schema[propName] = decl.getType().getText();
2132
- }
2133
- }
2134
- }
2135
- return Object.keys(schema).length > 0 ? schema : undefined;
2136
- }
2137
- /**
2138
- * Check if a type should be expanded into individual ports via getProperties().
2139
- * Returns true for object literals and interfaces, false for primitives, arrays,
2140
- * and built-in types whose properties are prototype methods (string, number, etc.).
2141
- */
2142
- isExpandableObjectType(tsType) {
2143
- const typeText = tsType.getText();
2144
- const primitiveTypes = new Set(['string', 'number', 'boolean', 'any', 'unknown', 'never', 'object', 'Object']);
2145
- if (primitiveTypes.has(typeText))
2146
- return false;
2147
- if (typeText.endsWith('[]') || typeText.startsWith('Array<'))
2148
- return false;
2149
- return tsType.isObject() && tsType.getProperties().length > 0;
2150
- }
2151
- inferPortType(tsType) {
2152
- const typeText = tsType.getText();
2153
- // Delegate to inferDataTypeFromTS for consistent type mapping
2154
- // This handles all cases: primitives, any, unknown, never, arrays, functions, etc.
2155
- return inferDataTypeFromTS(typeText);
2156
- }
2157
- /**
2158
- * Check if a function has a valid @flowWeaver annotation (nodeType, workflow, or pattern).
2159
- * Avoids false positives from file-level JSDoc that mentions @flowWeaver in description text.
2160
- */
2161
- hasFlowWeaverAnnotation(fn) {
2162
- const validTypes = new Set(['nodeType', 'workflow', 'pattern']);
2163
- return fn.getJsDocs().some((doc) => doc.getTags().some((t) => {
2164
- if (t.getTagName() !== 'flowWeaver')
2165
- return false;
2166
- const comment = t.getCommentText?.()?.trim() || '';
2167
- return validTypes.has(comment.split(/\s/)[0]);
2168
- }));
2169
- }
2170
- /**
2171
- * Generate an annotation suggestion for ghost-text autocomplete.
2172
- * Analyzes the function nearest to cursorLine, diffs inferred ports against
2173
- * any existing @flowWeaver annotation, and returns only the missing lines.
2174
- */
2175
- generateAnnotationSuggestion(content, cursorLine, virtualPath = 'virtual.ts') {
2176
- // Create virtual SourceFile
2177
- const existingFile = this.project.getSourceFile(virtualPath);
2178
- if (existingFile) {
2179
- this.project.removeSourceFile(existingFile);
2180
- }
2181
- const sourceFile = this.project.createSourceFile(virtualPath, content, { overwrite: true });
2182
- try {
2183
- const allFunctions = extractFunctionLikes(sourceFile);
2184
- if (allFunctions.length === 0)
2185
- return null;
2186
- // Find the function nearest to cursorLine (below or containing the cursor)
2187
- // cursorLine is 0-based; getStartLineNumber() is 1-based
2188
- let targetFn = null;
2189
- let bestDistance = Infinity;
2190
- for (const fn of allFunctions) {
2191
- const fnLine = fn.getStartLineNumber(false) - 1; // 0-based
2192
- // Prefer functions at or below the cursor
2193
- const distance = fnLine >= cursorLine ? fnLine - cursorLine : (cursorLine - fnLine) + 1000;
2194
- if (distance < bestDistance) {
2195
- bestDistance = distance;
2196
- targetFn = fn;
2197
- }
2198
- }
2199
- if (!targetFn)
2200
- return null;
2201
- const fnName = targetFn.getName() || 'anonymous';
2202
- const fnStartLine = targetFn.getStartLineNumber(false) - 1; // 0-based
2203
- // Don't suggest if cursor is too far from the function (more than 30 lines above)
2204
- if (cursorLine < fnStartLine - 30)
2205
- return null;
2206
- // Check existing JSDoc state
2207
- const hasAnnotation = this.hasFlowWeaverAnnotation(targetFn);
2208
- const hasAnyJsDoc = targetFn.getJsDocs().length > 0;
2209
- // If function has a JSDoc but NOT a @flowWeaver annotation, don't suggest
2210
- // a competing JSDoc block — the user has an intentional regular JSDoc
2211
- if (hasAnyJsDoc && !hasAnnotation)
2212
- return null;
2213
- // Infer full node type from function signature
2214
- const inferred = this.inferNodeTypeFromFunction(targetFn, fnName, virtualPath);
2215
- // Extract @param descriptions from existing JSDoc (if any)
2216
- const paramDescriptions = new Map();
2217
- for (const doc of targetFn.getJsDocs()) {
2218
- for (const tag of doc.getTags()) {
2219
- if (tag.getTagName() === 'param') {
2220
- const comment = tag.getCommentText?.()?.trim() || '';
2221
- // Extract param name and description: "{type} name - desc" or "name - desc" or "name desc"
2222
- const paramMatch = comment.match(/^(?:\{[^}]*\}\s+)?(\w+)(?:\s*-\s*|\s+)(.+)/);
2223
- if (paramMatch) {
2224
- paramDescriptions.set(paramMatch[1], paramMatch[2]);
2225
- }
2226
- }
2227
- }
2228
- }
2229
- // Merge @param descriptions into inferred port labels
2230
- for (const [portName, portDef] of Object.entries(inferred.inputs)) {
2231
- const desc = paramDescriptions.get(portName);
2232
- if (desc) {
2233
- portDef.label = desc;
2234
- }
2235
- }
2236
- // Parse existing JSDoc to find what's already annotated
2237
- const existingPorts = this.extractExistingAnnotatedPorts(targetFn);
2238
- // Build missing port lines
2239
- const missingLines = [];
2240
- // Filter out mandatory ports (execute, onSuccess, onFailure) from suggestions
2241
- for (const [portName, portDef] of Object.entries(inferred.inputs)) {
2242
- if (isControlFlowPort(portName))
2243
- continue;
2244
- if (existingPorts.inputs.has(portName))
2245
- continue;
2246
- missingLines.push(` * ${generateJSDocPortTag(portName, portDef, 'input')}`);
2247
- }
2248
- for (const [portName, portDef] of Object.entries(inferred.outputs)) {
2249
- if (isControlFlowPort(portName))
2250
- continue;
2251
- if (existingPorts.outputs.has(portName))
2252
- continue;
2253
- missingLines.push(` * ${generateJSDocPortTag(portName, portDef, 'output')}`);
2254
- }
2255
- if (hasAnnotation) {
2256
- // Check if this is a workflow block — if so, suggest missing connections
2257
- const isWorkflow = this.isWorkflowBlock(targetFn);
2258
- if (isWorkflow) {
2259
- const connectionLines = this.generateWorkflowStructureSuggestion(targetFn, sourceFile);
2260
- missingLines.push(...connectionLines);
2261
- }
2262
- // Partial JSDoc: suggest only missing ports / connections
2263
- if (missingLines.length === 0)
2264
- return null;
2265
- // Find the insertion point: just before the closing */
2266
- const lines = content.split(/\r?\n/);
2267
- let jsDocEndLine = -1;
2268
- for (let i = fnStartLine - 1; i >= 0; i--) {
2269
- if (lines[i].includes('*/')) {
2270
- jsDocEndLine = i;
2271
- break;
2272
- }
2273
- }
2274
- if (jsDocEndLine < 0)
2275
- return null;
2276
- const text = missingLines.join('\n') + '\n';
2277
- return {
2278
- text,
2279
- insertLine: jsDocEndLine,
2280
- replaceLinesCount: 0,
2281
- };
2282
- }
2283
- // No @flowWeaver JSDoc — check if user just typed "/**" on the cursor line
2284
- const lines = content.split(/\r?\n/);
2285
- const cursorLineText = lines[cursorLine] || '';
2286
- if (/^\s*\/\*\*\s*$/.test(cursorLineText)) {
2287
- // User typed "/**" — generate only the continuation lines after it
2288
- const continuationLines = [
2289
- ` * @flowWeaver nodeType ${fnName}`,
2290
- ...(inferred.expression ? [' * @expression'] : []),
2291
- ...missingLines,
2292
- ' */',
2293
- ];
2294
- const text = continuationLines.join('\n') + '\n';
2295
- return {
2296
- text,
2297
- insertLine: cursorLine + 1,
2298
- replaceLinesCount: 0,
2299
- };
2300
- }
2301
- // Generate full annotation block
2302
- const allLines = [
2303
- '/**',
2304
- ` * @flowWeaver nodeType ${fnName}`,
2305
- ...(inferred.expression ? [' * @expression'] : []),
2306
- ...missingLines,
2307
- ' */',
2308
- ];
2309
- const text = allLines.join('\n') + '\n';
2310
- // Insert on the line above the function
2311
- return {
2312
- text,
2313
- insertLine: fnStartLine,
2314
- replaceLinesCount: 0,
2315
- };
2316
- }
2317
- finally {
2318
- // Clean up virtual source file
2319
- const sf = this.project.getSourceFile(virtualPath);
2320
- if (sf)
2321
- this.project.removeSourceFile(sf);
2322
- }
2323
- }
2324
- /**
2325
- * Check if a function's JSDoc marks it as a workflow (vs nodeType).
2326
- */
2327
- isWorkflowBlock(fn) {
2328
- for (const doc of fn.getJsDocs()) {
2329
- for (const tag of doc.getTags()) {
2330
- if (tag.getTagName() !== 'flowWeaver')
2331
- continue;
2332
- const comment = tag.getCommentText?.()?.trim() || '';
2333
- const firstWord = comment.split(/\s/)[0];
2334
- // 'workflow' explicitly, or bare @flowWeaver (no qualifier), or named workflow
2335
- if (firstWord === 'workflow' || firstWord === '' || (firstWord !== 'nodeType' && firstWord !== 'pattern')) {
2336
- return true;
2337
- }
2338
- }
2339
- }
2340
- return false;
2341
- }
2342
- /**
2343
- * Generate missing @connect suggestions for a workflow block.
2344
- * Finds @node declarations, resolves their types, and suggests connections
2345
- * for matching port names that aren't already wired.
2346
- */
2347
- generateWorkflowStructureSuggestion(fn, sourceFile) {
2348
- // Extract @node declarations: { nodeId -> nodeTypeName }
2349
- const nodeDecls = new Map();
2350
- // Extract existing @connect lines: set of "sourceNode.sourcePort->targetNode.targetPort"
2351
- const existingConnections = new Set();
2352
- for (const doc of fn.getJsDocs()) {
2353
- for (const tag of doc.getTags()) {
2354
- const tagName = tag.getTagName();
2355
- const comment = tag.getCommentText?.()?.trim() || '';
2356
- if (tagName === 'node') {
2357
- const nodeMatch = comment.match(/^(\w+)\s+(\w+)/);
2358
- if (nodeMatch) {
2359
- nodeDecls.set(nodeMatch[1], nodeMatch[2]);
2360
- }
2361
- }
2362
- else if (tagName === 'connect') {
2363
- const connMatch = comment.match(/^(\w+)\.(\w+)\s*->\s*(\w+)\.(\w+)/);
2364
- if (connMatch) {
2365
- existingConnections.add(`${connMatch[1]}.${connMatch[2]}->${connMatch[3]}.${connMatch[4]}`);
2366
- }
2367
- }
2368
- }
2369
- }
2370
- if (nodeDecls.size < 2)
2371
- return [];
2372
- // Resolve node types from the same file
2373
- const allFunctions = extractFunctionLikes(sourceFile);
2374
- const resolvedTypes = new Map();
2375
- for (const [nodeId, typeName] of nodeDecls) {
2376
- const matchedFn = allFunctions.find((f) => f.getName() === typeName);
2377
- if (matchedFn) {
2378
- resolvedTypes.set(nodeId, this.inferNodeTypeFromFunction(matchedFn, typeName, sourceFile.getFilePath()));
2379
- }
2380
- }
2381
- // Find matching unconnected port pairs
2382
- const suggestions = [];
2383
- const nodeIds = [...nodeDecls.keys()];
2384
- for (let i = 0; i < nodeIds.length; i++) {
2385
- for (let j = 0; j < nodeIds.length; j++) {
2386
- if (i === j)
2387
- continue;
2388
- const srcId = nodeIds[i];
2389
- const tgtId = nodeIds[j];
2390
- const srcType = resolvedTypes.get(srcId);
2391
- const tgtType = resolvedTypes.get(tgtId);
2392
- if (!srcType || !tgtType)
2393
- continue;
2394
- for (const [outputName, outputDef] of Object.entries(srcType.outputs)) {
2395
- if (isControlFlowPort(outputName))
2396
- continue;
2397
- if (outputDef.dataType === 'STEP')
2398
- continue;
2399
- // Check if target has a matching input with the same name
2400
- if (outputName in tgtType.inputs && !isControlFlowPort(outputName)) {
2401
- const connKey = `${srcId}.${outputName}->${tgtId}.${outputName}`;
2402
- if (!existingConnections.has(connKey)) {
2403
- suggestions.push(` * @connect ${srcId}.${outputName} -> ${tgtId}.${outputName}`);
2404
- existingConnections.add(connKey); // prevent duplicates
2405
- }
2406
- }
2407
- }
2408
- }
2409
- }
2410
- return suggestions;
2411
- }
2412
- /**
2413
- * Extract port names that are already annotated in a function's JSDoc.
2414
- * Returns sets of input and output port names found in existing annotations.
2415
- */
2416
- extractExistingAnnotatedPorts(fn) {
2417
- const inputs = new Set();
2418
- const outputs = new Set();
2419
- for (const doc of fn.getJsDocs()) {
2420
- for (const tag of doc.getTags()) {
2421
- const tagName = tag.getTagName();
2422
- const comment = tag.getCommentText?.()?.trim() || '';
2423
- // Extract port name: first word, possibly wrapped in brackets [name] or [name=default]
2424
- const nameMatch = comment.match(/^\[?(\w+)/);
2425
- if (!nameMatch)
2426
- continue;
2427
- const portName = nameMatch[1];
2428
- if (tagName === 'input' || tagName === 'step') {
2429
- inputs.add(portName);
2430
- }
2431
- else if (tagName === 'output') {
2432
- outputs.add(portName);
2433
- }
2434
- }
2435
- }
2436
- return { inputs, outputs };
2437
- }
2438
- capitalize(str) {
2439
- return str.charAt(0).toUpperCase() + str.slice(1);
2440
- }
2441
- }
2442
- export const parser = new AnnotationParser();
2443
- /**
2444
- * Resolve npm node types by re-reading their .d.ts files.
2445
- * This fills in the full port information that isn't stored in @fwImport annotations.
2446
- *
2447
- * When workflows are parsed, npm node types from @fwImport annotations only contain
2448
- * minimal stub information (name, functionName, importSource). This function re-resolves
2449
- * the full port definitions from the actual .d.ts files of the npm packages.
2450
- *
2451
- * @param ast - The workflow AST with potentially stub npm node types
2452
- * @param workdir - Directory to search for node_modules (typically the workflow file's directory)
2453
- * @returns Updated AST with fully resolved npm node types
2454
- */
2455
- export function resolveNpmNodeTypes(ast, workdir) {
2456
- if (!ast.nodeTypes || ast.nodeTypes.length === 0) {
2457
- return ast;
2458
- }
2459
- const resolvedNodeTypes = ast.nodeTypes.map((nodeType) => {
2460
- // Only resolve npm node types (those with importSource)
2461
- if (!nodeType.importSource) {
2462
- return nodeType;
2463
- }
2464
- // Get the full node type from the .d.ts file
2465
- const packageExports = getPackageExports(nodeType.importSource, workdir);
2466
- const matchingExport = packageExports.find((exp) => exp.name === nodeType.name || exp.function === nodeType.functionName);
2467
- if (!matchingExport) {
2468
- // Can't resolve - keep stub (will show only result port)
2469
- return nodeType;
2470
- }
2471
- // Convert TNpmNodeType ports to TNodeTypeAST inputs/outputs
2472
- const inputs = {};
2473
- const outputs = {};
2474
- for (const port of matchingExport.ports) {
2475
- if (port.direction === 'INPUT') {
2476
- inputs[port.name] = {
2477
- dataType: port.type,
2478
- label: port.defaultLabel,
2479
- };
2480
- }
2481
- else if (port.direction === 'OUTPUT') {
2482
- outputs[port.name] = {
2483
- dataType: port.type,
2484
- label: port.defaultLabel,
2485
- };
2486
- }
2487
- }
2488
- return {
2489
- ...nodeType,
2490
- inputs,
2491
- outputs,
2492
- isAsync: matchingExport.synchronicity === 'ASYNC',
2493
- };
2494
- });
2495
- return {
2496
- ...ast,
2497
- nodeTypes: resolvedNodeTypes,
2498
- };
2499
- }
2500
- //# sourceMappingURL=parser.js.map