@synergenius/flow-weaver 0.2.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 (466) hide show
  1. package/LICENSE +122 -0
  2. package/README.md +315 -0
  3. package/dist/annotation-generator.d.ts +45 -0
  4. package/dist/annotation-generator.js +557 -0
  5. package/dist/api/builder.d.ts +223 -0
  6. package/dist/api/builder.js +345 -0
  7. package/dist/api/compile.d.ts +92 -0
  8. package/dist/api/compile.js +149 -0
  9. package/dist/api/extract-types.d.ts +29 -0
  10. package/dist/api/extract-types.js +57 -0
  11. package/dist/api/generate-in-place.d.ts +73 -0
  12. package/dist/api/generate-in-place.js +1353 -0
  13. package/dist/api/generate.d.ts +83 -0
  14. package/dist/api/generate.js +510 -0
  15. package/dist/api/helpers.d.ts +248 -0
  16. package/dist/api/helpers.js +285 -0
  17. package/dist/api/index.d.ts +46 -0
  18. package/dist/api/index.js +45 -0
  19. package/dist/api/inline-runtime.d.ts +27 -0
  20. package/dist/api/inline-runtime.js +551 -0
  21. package/dist/api/manipulation/connections.d.ts +79 -0
  22. package/dist/api/manipulation/connections.js +151 -0
  23. package/dist/api/manipulation/index.d.ts +34 -0
  24. package/dist/api/manipulation/index.js +41 -0
  25. package/dist/api/manipulation/node-types.d.ts +123 -0
  26. package/dist/api/manipulation/node-types.js +200 -0
  27. package/dist/api/manipulation/nodes.d.ts +144 -0
  28. package/dist/api/manipulation/nodes.js +333 -0
  29. package/dist/api/manipulation/ports.d.ts +59 -0
  30. package/dist/api/manipulation/ports.js +228 -0
  31. package/dist/api/manipulation/scopes.d.ts +52 -0
  32. package/dist/api/manipulation/scopes.js +156 -0
  33. package/dist/api/manipulation/validation.d.ts +6 -0
  34. package/dist/api/manipulation/validation.js +6 -0
  35. package/dist/api/manipulation/workflow.d.ts +81 -0
  36. package/dist/api/manipulation/workflow.js +116 -0
  37. package/dist/api/manipulation.d.ts +8 -0
  38. package/dist/api/manipulation.js +8 -0
  39. package/dist/api/parse.d.ts +48 -0
  40. package/dist/api/parse.js +110 -0
  41. package/dist/api/patterns.d.ts +112 -0
  42. package/dist/api/patterns.js +306 -0
  43. package/dist/api/query.d.ts +429 -0
  44. package/dist/api/query.js +816 -0
  45. package/dist/api/templates.d.ts +98 -0
  46. package/dist/api/templates.js +117 -0
  47. package/dist/api/transform.d.ts +31 -0
  48. package/dist/api/transform.js +40 -0
  49. package/dist/api/validate.d.ts +25 -0
  50. package/dist/api/validate.js +39 -0
  51. package/dist/api/workflow-file-operations.d.ts +29 -0
  52. package/dist/api/workflow-file-operations.js +180 -0
  53. package/dist/ast/builder.d.ts +210 -0
  54. package/dist/ast/builder.js +395 -0
  55. package/dist/ast/index.d.ts +5 -0
  56. package/dist/ast/index.js +5 -0
  57. package/dist/ast/serialization-node.d.ts +6 -0
  58. package/dist/ast/serialization-node.js +30 -0
  59. package/dist/ast/serialization.d.ts +43 -0
  60. package/dist/ast/serialization.js +134 -0
  61. package/dist/ast/types.d.ts +852 -0
  62. package/dist/ast/types.js +2 -0
  63. package/dist/ast/workflow-utils.d.ts +54 -0
  64. package/dist/ast/workflow-utils.js +114 -0
  65. package/dist/body-generator.d.ts +31 -0
  66. package/dist/body-generator.js +35 -0
  67. package/dist/built-in-nodes/delay.d.ts +11 -0
  68. package/dist/built-in-nodes/delay.js +29 -0
  69. package/dist/built-in-nodes/index.d.ts +5 -0
  70. package/dist/built-in-nodes/index.js +4 -0
  71. package/dist/built-in-nodes/invoke-workflow.d.ts +13 -0
  72. package/dist/built-in-nodes/invoke-workflow.js +25 -0
  73. package/dist/built-in-nodes/mock-types.d.ts +18 -0
  74. package/dist/built-in-nodes/mock-types.js +12 -0
  75. package/dist/built-in-nodes/wait-for-event.d.ts +13 -0
  76. package/dist/built-in-nodes/wait-for-event.js +25 -0
  77. package/dist/chevrotain-parser/connect-parser.d.ts +24 -0
  78. package/dist/chevrotain-parser/connect-parser.js +98 -0
  79. package/dist/chevrotain-parser/grammar-diagrams.d.ts +29 -0
  80. package/dist/chevrotain-parser/grammar-diagrams.js +264 -0
  81. package/dist/chevrotain-parser/index.d.ts +25 -0
  82. package/dist/chevrotain-parser/index.js +27 -0
  83. package/dist/chevrotain-parser/map-parser.d.ts +33 -0
  84. package/dist/chevrotain-parser/map-parser.js +130 -0
  85. package/dist/chevrotain-parser/node-parser.d.ts +36 -0
  86. package/dist/chevrotain-parser/node-parser.js +466 -0
  87. package/dist/chevrotain-parser/path-parser.d.ts +28 -0
  88. package/dist/chevrotain-parser/path-parser.js +118 -0
  89. package/dist/chevrotain-parser/port-parser.d.ts +36 -0
  90. package/dist/chevrotain-parser/port-parser.js +442 -0
  91. package/dist/chevrotain-parser/position-parser.d.ts +20 -0
  92. package/dist/chevrotain-parser/position-parser.js +83 -0
  93. package/dist/chevrotain-parser/scope-parser.d.ts +19 -0
  94. package/dist/chevrotain-parser/scope-parser.js +104 -0
  95. package/dist/chevrotain-parser/tokens.d.ts +78 -0
  96. package/dist/chevrotain-parser/tokens.js +384 -0
  97. package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +50 -0
  98. package/dist/chevrotain-parser/trigger-cancel-parser.js +282 -0
  99. package/dist/cli/commands/changelog.d.ts +13 -0
  100. package/dist/cli/commands/changelog.js +135 -0
  101. package/dist/cli/commands/compile.d.ts +64 -0
  102. package/dist/cli/commands/compile.js +278 -0
  103. package/dist/cli/commands/create.d.ts +33 -0
  104. package/dist/cli/commands/create.js +147 -0
  105. package/dist/cli/commands/describe.d.ts +68 -0
  106. package/dist/cli/commands/describe.js +377 -0
  107. package/dist/cli/commands/dev.d.ts +32 -0
  108. package/dist/cli/commands/dev.js +384 -0
  109. package/dist/cli/commands/diagram.d.ts +13 -0
  110. package/dist/cli/commands/diagram.js +33 -0
  111. package/dist/cli/commands/diff.d.ts +11 -0
  112. package/dist/cli/commands/diff.js +59 -0
  113. package/dist/cli/commands/doctor.d.ts +57 -0
  114. package/dist/cli/commands/doctor.js +719 -0
  115. package/dist/cli/commands/export.d.ts +57 -0
  116. package/dist/cli/commands/export.js +163 -0
  117. package/dist/cli/commands/grammar.d.ts +9 -0
  118. package/dist/cli/commands/grammar.js +39 -0
  119. package/dist/cli/commands/init.d.ts +59 -0
  120. package/dist/cli/commands/init.js +435 -0
  121. package/dist/cli/commands/listen.d.ts +16 -0
  122. package/dist/cli/commands/listen.js +39 -0
  123. package/dist/cli/commands/market.d.ts +52 -0
  124. package/dist/cli/commands/market.js +436 -0
  125. package/dist/cli/commands/migrate.d.ts +13 -0
  126. package/dist/cli/commands/migrate.js +89 -0
  127. package/dist/cli/commands/openapi.d.ts +37 -0
  128. package/dist/cli/commands/openapi.js +67 -0
  129. package/dist/cli/commands/pattern.d.ts +34 -0
  130. package/dist/cli/commands/pattern.js +185 -0
  131. package/dist/cli/commands/plugin.d.ts +16 -0
  132. package/dist/cli/commands/plugin.js +176 -0
  133. package/dist/cli/commands/run.d.ts +49 -0
  134. package/dist/cli/commands/run.js +191 -0
  135. package/dist/cli/commands/serve.d.ts +45 -0
  136. package/dist/cli/commands/serve.js +81 -0
  137. package/dist/cli/commands/templates.d.ts +8 -0
  138. package/dist/cli/commands/templates.js +54 -0
  139. package/dist/cli/commands/ui.d.ts +16 -0
  140. package/dist/cli/commands/ui.js +130 -0
  141. package/dist/cli/commands/validate.d.ts +12 -0
  142. package/dist/cli/commands/validate.js +247 -0
  143. package/dist/cli/commands/watch.d.ts +9 -0
  144. package/dist/cli/commands/watch.js +70 -0
  145. package/dist/cli/flow-weaver.mjs +92924 -0
  146. package/dist/cli/index.d.ts +9 -0
  147. package/dist/cli/index.js +742 -0
  148. package/dist/cli/templates/ai/mock-provider.d.ts +7 -0
  149. package/dist/cli/templates/ai/mock-provider.js +64 -0
  150. package/dist/cli/templates/ai/types.d.ts +47 -0
  151. package/dist/cli/templates/ai/types.js +5 -0
  152. package/dist/cli/templates/approvals/index.d.ts +15 -0
  153. package/dist/cli/templates/approvals/index.js +241 -0
  154. package/dist/cli/templates/index.d.ts +102 -0
  155. package/dist/cli/templates/index.js +101 -0
  156. package/dist/cli/templates/nodes/agent-router.d.ts +3 -0
  157. package/dist/cli/templates/nodes/agent-router.js +114 -0
  158. package/dist/cli/templates/nodes/aggregator.d.ts +7 -0
  159. package/dist/cli/templates/nodes/aggregator.js +63 -0
  160. package/dist/cli/templates/nodes/conversation-memory.d.ts +3 -0
  161. package/dist/cli/templates/nodes/conversation-memory.js +85 -0
  162. package/dist/cli/templates/nodes/http.d.ts +7 -0
  163. package/dist/cli/templates/nodes/http.js +80 -0
  164. package/dist/cli/templates/nodes/human-approval.d.ts +3 -0
  165. package/dist/cli/templates/nodes/human-approval.js +110 -0
  166. package/dist/cli/templates/nodes/json-extractor.d.ts +3 -0
  167. package/dist/cli/templates/nodes/json-extractor.js +119 -0
  168. package/dist/cli/templates/nodes/llm-call.d.ts +3 -0
  169. package/dist/cli/templates/nodes/llm-call.js +106 -0
  170. package/dist/cli/templates/nodes/prompt-template.d.ts +3 -0
  171. package/dist/cli/templates/nodes/prompt-template.js +52 -0
  172. package/dist/cli/templates/nodes/rag-retriever.d.ts +3 -0
  173. package/dist/cli/templates/nodes/rag-retriever.js +128 -0
  174. package/dist/cli/templates/nodes/tool-executor.d.ts +3 -0
  175. package/dist/cli/templates/nodes/tool-executor.js +108 -0
  176. package/dist/cli/templates/nodes/transformer.d.ts +7 -0
  177. package/dist/cli/templates/nodes/transformer.js +68 -0
  178. package/dist/cli/templates/nodes/validator.d.ts +7 -0
  179. package/dist/cli/templates/nodes/validator.js +62 -0
  180. package/dist/cli/templates/providers/index.d.ts +14 -0
  181. package/dist/cli/templates/providers/index.js +239 -0
  182. package/dist/cli/templates/shared/approval-types.d.ts +9 -0
  183. package/dist/cli/templates/shared/approval-types.js +31 -0
  184. package/dist/cli/templates/shared/llm-types.d.ts +15 -0
  185. package/dist/cli/templates/shared/llm-types.js +104 -0
  186. package/dist/cli/templates/workflows/aggregator.d.ts +7 -0
  187. package/dist/cli/templates/workflows/aggregator.js +104 -0
  188. package/dist/cli/templates/workflows/ai-agent-durable.d.ts +8 -0
  189. package/dist/cli/templates/workflows/ai-agent-durable.js +338 -0
  190. package/dist/cli/templates/workflows/ai-agent.d.ts +31 -0
  191. package/dist/cli/templates/workflows/ai-agent.js +326 -0
  192. package/dist/cli/templates/workflows/ai-chat.d.ts +7 -0
  193. package/dist/cli/templates/workflows/ai-chat.js +169 -0
  194. package/dist/cli/templates/workflows/ai-pipeline-durable.d.ts +8 -0
  195. package/dist/cli/templates/workflows/ai-pipeline-durable.js +330 -0
  196. package/dist/cli/templates/workflows/ai-rag.d.ts +7 -0
  197. package/dist/cli/templates/workflows/ai-rag.js +186 -0
  198. package/dist/cli/templates/workflows/ai-react.d.ts +7 -0
  199. package/dist/cli/templates/workflows/ai-react.js +294 -0
  200. package/dist/cli/templates/workflows/conditional.d.ts +12 -0
  201. package/dist/cli/templates/workflows/conditional.js +142 -0
  202. package/dist/cli/templates/workflows/error-handler.d.ts +7 -0
  203. package/dist/cli/templates/workflows/error-handler.js +147 -0
  204. package/dist/cli/templates/workflows/foreach.d.ts +7 -0
  205. package/dist/cli/templates/workflows/foreach.js +143 -0
  206. package/dist/cli/templates/workflows/sequential.d.ts +7 -0
  207. package/dist/cli/templates/workflows/sequential.js +198 -0
  208. package/dist/cli/templates/workflows/webhook.d.ts +7 -0
  209. package/dist/cli/templates/workflows/webhook.js +161 -0
  210. package/dist/cli/utils/logger.d.ts +15 -0
  211. package/dist/cli/utils/logger.js +46 -0
  212. package/dist/constants.d.ts +100 -0
  213. package/dist/constants.js +125 -0
  214. package/dist/defaults.d.ts +3 -0
  215. package/dist/defaults.js +3 -0
  216. package/dist/deployment/config/defaults.d.ts +29 -0
  217. package/dist/deployment/config/defaults.js +98 -0
  218. package/dist/deployment/config/loader.d.ts +24 -0
  219. package/dist/deployment/config/loader.js +236 -0
  220. package/dist/deployment/config/types.d.ts +117 -0
  221. package/dist/deployment/config/types.js +5 -0
  222. package/dist/deployment/core/adapters.d.ts +90 -0
  223. package/dist/deployment/core/adapters.js +251 -0
  224. package/dist/deployment/core/executor.d.ts +62 -0
  225. package/dist/deployment/core/executor.js +197 -0
  226. package/dist/deployment/core/formatters.d.ts +57 -0
  227. package/dist/deployment/core/formatters.js +170 -0
  228. package/dist/deployment/index.d.ts +31 -0
  229. package/dist/deployment/index.js +48 -0
  230. package/dist/deployment/openapi/generator.d.ts +146 -0
  231. package/dist/deployment/openapi/generator.js +347 -0
  232. package/dist/deployment/openapi/schema-converter.d.ts +49 -0
  233. package/dist/deployment/openapi/schema-converter.js +192 -0
  234. package/dist/deployment/targets/base.d.ts +316 -0
  235. package/dist/deployment/targets/base.js +823 -0
  236. package/dist/deployment/targets/cloudflare.d.ts +23 -0
  237. package/dist/deployment/targets/cloudflare.js +1125 -0
  238. package/dist/deployment/targets/inngest.d.ts +38 -0
  239. package/dist/deployment/targets/inngest.js +926 -0
  240. package/dist/deployment/targets/lambda.d.ts +23 -0
  241. package/dist/deployment/targets/lambda.js +1289 -0
  242. package/dist/deployment/targets/vercel.d.ts +23 -0
  243. package/dist/deployment/targets/vercel.js +886 -0
  244. package/dist/deployment/types.d.ts +183 -0
  245. package/dist/deployment/types.js +8 -0
  246. package/dist/diagram/geometry.d.ts +26 -0
  247. package/dist/diagram/geometry.js +850 -0
  248. package/dist/diagram/index.d.ts +16 -0
  249. package/dist/diagram/index.js +42 -0
  250. package/dist/diagram/layout.d.ts +11 -0
  251. package/dist/diagram/layout.js +143 -0
  252. package/dist/diagram/orthogonal-router.d.ts +79 -0
  253. package/dist/diagram/orthogonal-router.js +568 -0
  254. package/dist/diagram/renderer.d.ts +3 -0
  255. package/dist/diagram/renderer.js +207 -0
  256. package/dist/diagram/theme.d.ts +20 -0
  257. package/dist/diagram/theme.js +189 -0
  258. package/dist/diagram/types.d.ts +70 -0
  259. package/dist/diagram/types.js +2 -0
  260. package/dist/diff/WorkflowDiffer.d.ts +13 -0
  261. package/dist/diff/WorkflowDiffer.js +429 -0
  262. package/dist/diff/formatDiff.d.ts +10 -0
  263. package/dist/diff/formatDiff.js +220 -0
  264. package/dist/diff/impact.d.ts +29 -0
  265. package/dist/diff/impact.js +119 -0
  266. package/dist/diff/index.d.ts +10 -0
  267. package/dist/diff/index.js +9 -0
  268. package/dist/diff/types.d.ts +138 -0
  269. package/dist/diff/types.js +35 -0
  270. package/dist/doc-metadata/extractors/annotations.d.ts +56 -0
  271. package/dist/doc-metadata/extractors/annotations.js +337 -0
  272. package/dist/doc-metadata/extractors/cli-commands.d.ts +17 -0
  273. package/dist/doc-metadata/extractors/cli-commands.js +355 -0
  274. package/dist/doc-metadata/extractors/mcp-tools.d.ts +16 -0
  275. package/dist/doc-metadata/extractors/mcp-tools.js +689 -0
  276. package/dist/doc-metadata/extractors/plugin-api.d.ts +19 -0
  277. package/dist/doc-metadata/extractors/plugin-api.js +279 -0
  278. package/dist/doc-metadata/index.d.ts +5 -0
  279. package/dist/doc-metadata/index.js +4 -0
  280. package/dist/doc-metadata/types.d.ts +120 -0
  281. package/dist/doc-metadata/types.js +5 -0
  282. package/dist/editor-completions/annotationValues.d.ts +12 -0
  283. package/dist/editor-completions/annotationValues.js +138 -0
  284. package/dist/editor-completions/contextParser.d.ts +40 -0
  285. package/dist/editor-completions/contextParser.js +410 -0
  286. package/dist/editor-completions/dataTypes.d.ts +16 -0
  287. package/dist/editor-completions/dataTypes.js +95 -0
  288. package/dist/editor-completions/goToDefinition.d.ts +27 -0
  289. package/dist/editor-completions/goToDefinition.js +112 -0
  290. package/dist/editor-completions/index.d.ts +39 -0
  291. package/dist/editor-completions/index.js +181 -0
  292. package/dist/editor-completions/jsDocAnnotations.d.ts +29 -0
  293. package/dist/editor-completions/jsDocAnnotations.js +357 -0
  294. package/dist/editor-completions/modifierCompletions.d.ts +17 -0
  295. package/dist/editor-completions/modifierCompletions.js +197 -0
  296. package/dist/editor-completions/types.d.ts +119 -0
  297. package/dist/editor-completions/types.js +8 -0
  298. package/dist/export/index.d.ts +68 -0
  299. package/dist/export/index.js +1074 -0
  300. package/dist/export/templates.d.ts +24 -0
  301. package/dist/export/templates.js +186 -0
  302. package/dist/friendly-errors.d.ts +35 -0
  303. package/dist/friendly-errors.js +375 -0
  304. package/dist/function-like.d.ts +38 -0
  305. package/dist/function-like.js +83 -0
  306. package/dist/generated-branding.d.ts +16 -0
  307. package/dist/generated-branding.js +22 -0
  308. package/dist/generator/async-detection.d.ts +27 -0
  309. package/dist/generator/async-detection.js +56 -0
  310. package/dist/generator/code-utils.d.ts +76 -0
  311. package/dist/generator/code-utils.js +410 -0
  312. package/dist/generator/control-flow.d.ts +54 -0
  313. package/dist/generator/control-flow.js +284 -0
  314. package/dist/generator/inngest.d.ts +53 -0
  315. package/dist/generator/inngest.js +1126 -0
  316. package/dist/generator/scope-function-generator.d.ts +78 -0
  317. package/dist/generator/scope-function-generator.js +360 -0
  318. package/dist/generator/unified.d.ts +42 -0
  319. package/dist/generator/unified.js +1504 -0
  320. package/dist/generator.d.ts +54 -0
  321. package/dist/generator.js +100 -0
  322. package/dist/index.d.ts +85 -0
  323. package/dist/index.js +89 -0
  324. package/dist/jsdoc-parser.d.ts +308 -0
  325. package/dist/jsdoc-parser.js +923 -0
  326. package/dist/jsdoc-port-sync/constants.d.ts +41 -0
  327. package/dist/jsdoc-port-sync/constants.js +103 -0
  328. package/dist/jsdoc-port-sync/diff.d.ts +76 -0
  329. package/dist/jsdoc-port-sync/diff.js +319 -0
  330. package/dist/jsdoc-port-sync/index.d.ts +42 -0
  331. package/dist/jsdoc-port-sync/index.js +45 -0
  332. package/dist/jsdoc-port-sync/port-parser.d.ts +68 -0
  333. package/dist/jsdoc-port-sync/port-parser.js +579 -0
  334. package/dist/jsdoc-port-sync/rename.d.ts +21 -0
  335. package/dist/jsdoc-port-sync/rename.js +256 -0
  336. package/dist/jsdoc-port-sync/signature-parser.d.ts +104 -0
  337. package/dist/jsdoc-port-sync/signature-parser.js +559 -0
  338. package/dist/jsdoc-port-sync/sync.d.ts +36 -0
  339. package/dist/jsdoc-port-sync/sync.js +644 -0
  340. package/dist/jsdoc-port-sync.d.ts +10 -0
  341. package/dist/jsdoc-port-sync.js +10 -0
  342. package/dist/marketplace/index.d.ts +11 -0
  343. package/dist/marketplace/index.js +10 -0
  344. package/dist/marketplace/manifest.d.ts +32 -0
  345. package/dist/marketplace/manifest.js +176 -0
  346. package/dist/marketplace/registry.d.ts +30 -0
  347. package/dist/marketplace/registry.js +100 -0
  348. package/dist/marketplace/types.d.ts +154 -0
  349. package/dist/marketplace/types.js +9 -0
  350. package/dist/marketplace/validator.d.ts +13 -0
  351. package/dist/marketplace/validator.js +131 -0
  352. package/dist/mcp/auto-registration.d.ts +3 -0
  353. package/dist/mcp/auto-registration.js +62 -0
  354. package/dist/mcp/editor-connection.d.ts +50 -0
  355. package/dist/mcp/editor-connection.js +125 -0
  356. package/dist/mcp/event-buffer.d.ts +62 -0
  357. package/dist/mcp/event-buffer.js +150 -0
  358. package/dist/mcp/index.d.ts +12 -0
  359. package/dist/mcp/index.js +11 -0
  360. package/dist/mcp/resources.d.ts +14 -0
  361. package/dist/mcp/resources.js +55 -0
  362. package/dist/mcp/response-utils.d.ts +63 -0
  363. package/dist/mcp/response-utils.js +89 -0
  364. package/dist/mcp/server.d.ts +4 -0
  365. package/dist/mcp/server.js +99 -0
  366. package/dist/mcp/tools-diagram.d.ts +8 -0
  367. package/dist/mcp/tools-diagram.js +53 -0
  368. package/dist/mcp/tools-editor.d.ts +5 -0
  369. package/dist/mcp/tools-editor.js +190 -0
  370. package/dist/mcp/tools-export.d.ts +9 -0
  371. package/dist/mcp/tools-export.js +180 -0
  372. package/dist/mcp/tools-marketplace.d.ts +9 -0
  373. package/dist/mcp/tools-marketplace.js +132 -0
  374. package/dist/mcp/tools-pattern.d.ts +3 -0
  375. package/dist/mcp/tools-pattern.js +783 -0
  376. package/dist/mcp/tools-query.d.ts +3 -0
  377. package/dist/mcp/tools-query.js +364 -0
  378. package/dist/mcp/tools-template.d.ts +10 -0
  379. package/dist/mcp/tools-template.js +119 -0
  380. package/dist/mcp/types.d.ts +70 -0
  381. package/dist/mcp/types.js +8 -0
  382. package/dist/mcp/workflow-executor.d.ts +47 -0
  383. package/dist/mcp/workflow-executor.js +133 -0
  384. package/dist/migration/registry.d.ts +30 -0
  385. package/dist/migration/registry.js +29 -0
  386. package/dist/node-types-generator.d.ts +49 -0
  387. package/dist/node-types-generator.js +139 -0
  388. package/dist/npm-packages.d.ts +56 -0
  389. package/dist/npm-packages.js +255 -0
  390. package/dist/parser.d.ts +204 -0
  391. package/dist/parser.js +2100 -0
  392. package/dist/plugin/PluginPanel.d.ts +12 -0
  393. package/dist/plugin/PluginPanel.js +5 -0
  394. package/dist/plugin/index.d.ts +13 -0
  395. package/dist/plugin/index.js +14 -0
  396. package/dist/plugin/types.d.ts +75 -0
  397. package/dist/plugin/types.js +8 -0
  398. package/dist/resolve-package-types.d.ts +17 -0
  399. package/dist/resolve-package-types.js +123 -0
  400. package/dist/runtime/CancellationError.d.ts +11 -0
  401. package/dist/runtime/CancellationError.js +20 -0
  402. package/dist/runtime/ExecutionContext.d.ts +146 -0
  403. package/dist/runtime/ExecutionContext.js +235 -0
  404. package/dist/runtime/builtin-functions.d.ts +8 -0
  405. package/dist/runtime/builtin-functions.js +549 -0
  406. package/dist/runtime/events.d.ts +50 -0
  407. package/dist/runtime/events.js +2 -0
  408. package/dist/runtime/function-registry.d.ts +59 -0
  409. package/dist/runtime/function-registry.js +66 -0
  410. package/dist/runtime/index.d.ts +7 -0
  411. package/dist/runtime/index.js +7 -0
  412. package/dist/runtime/parameter-resolver.d.ts +62 -0
  413. package/dist/runtime/parameter-resolver.js +113 -0
  414. package/dist/server/index.d.ts +7 -0
  415. package/dist/server/index.js +6 -0
  416. package/dist/server/types.d.ts +93 -0
  417. package/dist/server/types.js +5 -0
  418. package/dist/server/webhook-server.d.ts +50 -0
  419. package/dist/server/webhook-server.js +269 -0
  420. package/dist/server/workflow-registry.d.ts +61 -0
  421. package/dist/server/workflow-registry.js +202 -0
  422. package/dist/shared-project.d.ts +9 -0
  423. package/dist/shared-project.js +28 -0
  424. package/dist/sugar-optimizer.d.ts +40 -0
  425. package/dist/sugar-optimizer.js +387 -0
  426. package/dist/testing/assertions.d.ts +51 -0
  427. package/dist/testing/assertions.js +127 -0
  428. package/dist/testing/index.d.ts +30 -0
  429. package/dist/testing/index.js +24 -0
  430. package/dist/testing/mock-approval.d.ts +81 -0
  431. package/dist/testing/mock-approval.js +98 -0
  432. package/dist/testing/mock-llm.d.ts +124 -0
  433. package/dist/testing/mock-llm.js +119 -0
  434. package/dist/testing/recorder.d.ts +72 -0
  435. package/dist/testing/recorder.js +70 -0
  436. package/dist/testing/replayer.d.ts +56 -0
  437. package/dist/testing/replayer.js +143 -0
  438. package/dist/testing/token-tracker.d.ts +71 -0
  439. package/dist/testing/token-tracker.js +94 -0
  440. package/dist/type-checker.d.ts +42 -0
  441. package/dist/type-checker.js +190 -0
  442. package/dist/type-mappings.d.ts +29 -0
  443. package/dist/type-mappings.js +125 -0
  444. package/dist/types/branded-ports.d.ts +151 -0
  445. package/dist/types/branded-ports.js +121 -0
  446. package/dist/types/index.d.ts +5 -0
  447. package/dist/types/index.js +5 -0
  448. package/dist/types.d.ts +139 -0
  449. package/dist/types.js +15 -0
  450. package/dist/utils/error-utils.d.ts +15 -0
  451. package/dist/utils/error-utils.js +27 -0
  452. package/dist/utils/lru-cache.d.ts +15 -0
  453. package/dist/utils/lru-cache.js +40 -0
  454. package/dist/utils/port-ordering.d.ts +26 -0
  455. package/dist/utils/port-ordering.js +88 -0
  456. package/dist/utils/port-tag-utils.d.ts +23 -0
  457. package/dist/utils/port-tag-utils.js +41 -0
  458. package/dist/utils/string-distance.d.ts +14 -0
  459. package/dist/utils/string-distance.js +56 -0
  460. package/dist/validation/agent-detection.d.ts +33 -0
  461. package/dist/validation/agent-detection.js +115 -0
  462. package/dist/validation/agent-rules.d.ts +48 -0
  463. package/dist/validation/agent-rules.js +262 -0
  464. package/dist/validator.d.ts +92 -0
  465. package/dist/validator.js +970 -0
  466. package/package.json +109 -0
@@ -0,0 +1,1504 @@
1
+ import { extractStartPorts } from '../ast/workflow-utils.js';
2
+ import { mapToTypeScript } from '../type-mappings.js';
3
+ import { buildNodeArgumentsWithContext, toValidIdentifier } from './code-utils.js';
4
+ import { buildControlFlowGraph, detectBranchingChains, findAllBranchingNodes, findNodesInBranch, performKahnsTopologicalSort, isPerPortScopedChild, } from './control-flow.js';
5
+ import { RESERVED_NODE_NAMES, RESERVED_PORT_NAMES, EXECUTION_STRATEGIES, isStartNode, isExitNode, isExecutePort, isSuccessPort, isFailurePort, } from '../constants.js';
6
+ /**
7
+ * Helper: Determine if an instance has pull execution enabled
8
+ * Checks instance config first, then falls back to node type default config
9
+ * Returns { enabled: boolean, triggerPort: string }
10
+ */
11
+ function getPullExecutionConfig(instance, nodeType) {
12
+ // Check instance config first
13
+ if (instance.config?.pullExecution) {
14
+ const pullConfig = instance.config.pullExecution;
15
+ if (typeof pullConfig === 'boolean') {
16
+ return { enabled: pullConfig, triggerPort: 'execute' };
17
+ }
18
+ return {
19
+ enabled: true,
20
+ triggerPort: pullConfig.triggerPort || 'execute',
21
+ };
22
+ }
23
+ // Fall back to node type default config
24
+ if (nodeType.defaultConfig?.pullExecution) {
25
+ const pullConfig = nodeType.defaultConfig.pullExecution;
26
+ if (typeof pullConfig === 'boolean') {
27
+ return { enabled: pullConfig, triggerPort: 'execute' };
28
+ }
29
+ return {
30
+ enabled: true,
31
+ triggerPort: pullConfig.triggerPort || 'execute',
32
+ };
33
+ }
34
+ // No pull execution configured
35
+ return { enabled: false, triggerPort: 'execute' };
36
+ }
37
+ /**
38
+ * Generates executable TypeScript code from a workflow AST using ExecutionContext for state management.
39
+ *
40
+ * This is the main code generation function that transforms a visual workflow into runnable code.
41
+ *
42
+ * ## Algorithm Overview:
43
+ * 1. **Setup Phase**: Initialize ExecutionContext, set recursion depth protection
44
+ * 2. **Start Node**: Store workflow parameters as Start node outputs
45
+ * 3. **Control Flow Graph**: Build CFG from connections, perform topological sort
46
+ * 4. **Branch Detection**: Identify branching nodes (with onSuccess/onFailure) and their regions
47
+ * 5. **Code Generation**: For each node in execution order:
48
+ * - Regular nodes: Generate direct execution with variable storage
49
+ * - Branching nodes: Generate if/else blocks for success/failure paths
50
+ * - Scoped children: Generate scope function closures (for forEach, etc.)
51
+ * - Pull nodes: Generate lazy executors registered with context
52
+ * 6. **Exit Node**: Collect outputs and return result object
53
+ *
54
+ * ## Key Concepts:
55
+ * - **Branching Nodes**: Nodes with both onSuccess and onFailure ports create conditional branches
56
+ * - **Per-Port Scoped Children**: Children of forEach-like nodes execute via closure functions
57
+ * - **Pull Execution**: Nodes marked for lazy evaluation only run when outputs are consumed
58
+ * - **Execution Index**: Each node execution gets a unique index for variable tracking
59
+ *
60
+ * ## Generated Code Structure:
61
+ * ```typescript
62
+ * const ctx = new GeneratedExecutionContext(isAsync, debugger?);
63
+ * const startIdx = ctx.addExecution('Start');
64
+ * ctx.setVariable({ id: 'Start', portName: 'param', ... }, value);
65
+ * // ... node executions in topological order ...
66
+ * const exitIdx = ctx.addExecution('Exit');
67
+ * return { onSuccess: ..., onFailure: ..., ...outputs };
68
+ * ```
69
+ *
70
+ * @param workflow - The workflow AST to generate code for
71
+ * @param nodeTypes - Available node type definitions (includes workflow's nodeTypes + imports)
72
+ * @param isAsync - Whether to generate async code (adds await, returns Promise)
73
+ * @param production - If true, omits debug instrumentation for smaller output
74
+ * @returns Generated TypeScript function body (without function signature)
75
+ */
76
+ export function generateControlFlowWithExecutionContext(workflow, nodeTypes, isAsync, production = false, bundleMode = false) {
77
+ const lines = [];
78
+ // In development mode, determine effective debugger (from parameter or environment)
79
+ if (!production) {
80
+ lines.push(` // Use passed debugger or auto-detect from environment variable`);
81
+ lines.push(` const __effectiveDebugger__ = (`);
82
+ lines.push(` typeof __flowWeaverDebugger__ !== 'undefined' ? __flowWeaverDebugger__ :`);
83
+ lines.push(` typeof process !== 'undefined' && process.env.FLOW_WEAVER_DEBUG`);
84
+ lines.push(` ? createFlowWeaverDebugClient(process.env.FLOW_WEAVER_DEBUG, '${workflow.functionName}')`);
85
+ lines.push(` : undefined`);
86
+ lines.push(` );`);
87
+ lines.push('');
88
+ }
89
+ // Recursion depth protection: prevent infinite recursion in workflows
90
+ lines.push(` // Recursion depth protection`);
91
+ lines.push(` const __rd__ = (params as { __rd__?: number }).__rd__ ?? 0;`);
92
+ lines.push(` if (__rd__ >= 1000) {`);
93
+ lines.push(` throw new Error('Max recursion depth exceeded (1000) in workflow "${workflow.functionName}"');`);
94
+ lines.push(` }`);
95
+ lines.push('');
96
+ // In development mode, pass the effective debugger (from parameter or environment)
97
+ // In production mode, omit the debugger parameter
98
+ // Always pass abort signal for cancellation support
99
+ const asyncArg = isAsync ? 'true' : 'false';
100
+ if (production) {
101
+ lines.push(` const ctx = new GeneratedExecutionContext(${asyncArg}, __abortSignal__);`);
102
+ }
103
+ else {
104
+ lines.push(` const ctx = new GeneratedExecutionContext(${asyncArg}, __effectiveDebugger__, __abortSignal__);`);
105
+ }
106
+ lines.push('');
107
+ lines.push(` const startIdx = ctx.addExecution('${RESERVED_NODE_NAMES.START}');`);
108
+ Object.keys(extractStartPorts(workflow)).forEach((portName) => {
109
+ const setCall = isAsync ? `await ctx.setVariable` : `ctx.setVariable`;
110
+ // STEP Port Architecture: execute comes from workflow parameter, data from params object
111
+ const valueSource = isExecutePort(portName) ? 'execute' : `params.${portName}`;
112
+ lines.push(` ${setCall}({ id: '${RESERVED_NODE_NAMES.START}', portName: '${portName}', executionIndex: startIdx, nodeTypeName: '${RESERVED_NODE_NAMES.START}' }, ${valueSource});`);
113
+ });
114
+ lines.push(` ctx.sendStatusChangedEvent({`);
115
+ lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.START}',`);
116
+ lines.push(` id: '${RESERVED_NODE_NAMES.START}',`);
117
+ lines.push(` executionIndex: startIdx,`);
118
+ lines.push(` status: 'SUCCEEDED',`);
119
+ lines.push(` });`);
120
+ lines.push('');
121
+ const cfg = buildControlFlowGraph(workflow, nodeTypes);
122
+ const executionOrder = performKahnsTopologicalSort(cfg); // Now returns instance IDs
123
+ const branchingNodes = findAllBranchingNodes(workflow, nodeTypes);
124
+ const allInstanceIds = new Set(workflow.instances.map((i) => i.id));
125
+ const branchRegions = new Map();
126
+ branchingNodes.forEach((branchInstanceId) => {
127
+ const successNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_SUCCESS, workflow, allInstanceIds, branchingNodes, nodeTypes);
128
+ const failureNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_FAILURE, workflow, allInstanceIds, branchingNodes, nodeTypes);
129
+ branchRegions.set(branchInstanceId, { successNodes, failureNodes });
130
+ });
131
+ // Determine which nodes are in conditional branches (need let declaration)
132
+ // Nodes in branches may not execute, so we need undefined checks for them
133
+ const nodesInBranches = new Set();
134
+ branchRegions.forEach((region) => {
135
+ region.successNodes.forEach((n) => nodesInBranches.add(n));
136
+ region.failureNodes.forEach((n) => nodesInBranches.add(n));
137
+ });
138
+ // Identify pull execution nodes (they also need let due to undefined check)
139
+ const pullExecutionNodes = new Set();
140
+ workflow.instances.forEach((instance) => {
141
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
142
+ const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
143
+ if (nodeType) {
144
+ const pullConfig = getPullExecutionConfig(instance, nodeType);
145
+ if (pullConfig.enabled) {
146
+ pullExecutionNodes.add(instance.id);
147
+ }
148
+ }
149
+ });
150
+ // Identify node-level scoped children (they need let because referenced outside scope block)
151
+ const nodeLevelScopedChildren = new Set();
152
+ workflow.instances.forEach((instance) => {
153
+ if (instance.parent && !isPerPortScopedChild(instance, workflow, nodeTypes)) {
154
+ nodeLevelScopedChildren.add(instance.id);
155
+ }
156
+ });
157
+ // Create execution index variables only for nodes that need undefined checking
158
+ // Skip per-port scoped children (they're in scope functions)
159
+ let hasLetDeclarations = false;
160
+ workflow.instances.forEach((instance) => {
161
+ if (!isPerPortScopedChild(instance, workflow, nodeTypes)) {
162
+ // Nodes in branches, branching nodes, pull nodes, or scoped children need let
163
+ if (nodesInBranches.has(instance.id) ||
164
+ branchingNodes.has(instance.id) ||
165
+ pullExecutionNodes.has(instance.id) ||
166
+ nodeLevelScopedChildren.has(instance.id)) {
167
+ lines.push(` let ${toValidIdentifier(instance.id)}Idx: number | undefined;`);
168
+ hasLetDeclarations = true;
169
+ }
170
+ }
171
+ });
172
+ if (hasLetDeclarations) {
173
+ lines.push('');
174
+ }
175
+ const instancesInMultipleBranches = new Set();
176
+ allInstanceIds.forEach((instanceId) => {
177
+ let branchCount = 0;
178
+ branchRegions.forEach((region) => {
179
+ if (region.successNodes.has(instanceId) || region.failureNodes.has(instanceId)) {
180
+ branchCount++;
181
+ }
182
+ });
183
+ if (branchCount > 1) {
184
+ instancesInMultipleBranches.add(instanceId);
185
+ }
186
+ });
187
+ branchRegions.forEach((region, _branchNode) => {
188
+ instancesInMultipleBranches.forEach((instanceId) => {
189
+ region.successNodes.delete(instanceId);
190
+ region.failureNodes.delete(instanceId);
191
+ });
192
+ });
193
+ // Promote nodes that have DATA dependencies on nodes outside their branch.
194
+ // Without this, STEP-nesting places the node before its data providers are generated.
195
+ const nodesPromotedFromBranches = new Set();
196
+ branchRegions.forEach((region, branchNodeId) => {
197
+ const allBranchNodes = new Set([...region.successNodes, ...region.failureNodes]);
198
+ allBranchNodes.forEach((nodeId) => {
199
+ const hasExternalDataDep = workflow.connections.some((conn) => {
200
+ if (conn.to.node !== nodeId)
201
+ return false;
202
+ if (conn.from.scope || conn.to.scope)
203
+ return false;
204
+ const fromNode = conn.from.node;
205
+ // Dependencies on branch parent or Start are fine (already generated)
206
+ if (fromNode === branchNodeId || isStartNode(fromNode))
207
+ return false;
208
+ // STEP connections (execute port) are handled by the guard, not data flow
209
+ if (isExecutePort(conn.to.port))
210
+ return false;
211
+ // External dep: source is NOT in the same branch
212
+ return !allBranchNodes.has(fromNode);
213
+ });
214
+ if (hasExternalDataDep) {
215
+ nodesPromotedFromBranches.add(nodeId);
216
+ }
217
+ });
218
+ });
219
+ // Remove promoted nodes from branch regions (they'll generate at top level)
220
+ branchRegions.forEach((region) => {
221
+ nodesPromotedFromBranches.forEach((nodeId) => {
222
+ region.successNodes.delete(nodeId);
223
+ region.failureNodes.delete(nodeId);
224
+ });
225
+ });
226
+ // Identify branching nodes whose _success flag must be tracked because
227
+ // promoted nodes depend on their onSuccess/onFailure ports for STEP guards.
228
+ const branchingNodesNeedingSuccessFlag = new Set();
229
+ nodesPromotedFromBranches.forEach((promotedNodeId) => {
230
+ workflow.connections.forEach((conn) => {
231
+ if (conn.to.node === promotedNodeId && isExecutePort(conn.to.port)) {
232
+ const sourceNode = conn.from.node;
233
+ const sourcePort = conn.from.port;
234
+ if (branchingNodes.has(sourceNode) && (isSuccessPort(sourcePort) || isFailurePort(sourcePort))) {
235
+ branchingNodesNeedingSuccessFlag.add(sourceNode);
236
+ }
237
+ }
238
+ });
239
+ });
240
+ // Detect sequential branching chains for flattening
241
+ const branchingChains = detectBranchingChains(branchingNodes, branchRegions);
242
+ const chainMembers = new Set();
243
+ branchingChains.forEach((chain) => {
244
+ // All non-head nodes are chain members (skip in main loop)
245
+ for (let i = 1; i < chain.length; i++) {
246
+ chainMembers.add(chain[i]);
247
+ }
248
+ });
249
+ const generatedNodes = new Set();
250
+ const availableVars = new Map();
251
+ Object.keys(extractStartPorts(workflow)).forEach((portName) => {
252
+ availableVars.set(`${RESERVED_NODE_NAMES.START}.${portName}`, `params.${portName}`);
253
+ });
254
+ executionOrder.forEach((instanceId) => {
255
+ if (isStartNode(instanceId) || isExitNode(instanceId) || generatedNodes.has(instanceId)) {
256
+ return;
257
+ }
258
+ // Find the instance and its node type
259
+ const instance = workflow.instances.find((i) => i.id === instanceId);
260
+ if (!instance) {
261
+ lines.push(` // Node '${instanceId}' skipped: instance not found in workflow`);
262
+ return;
263
+ }
264
+ // Skip per-port scoped children (they're in scope functions)
265
+ // Include node-level scoped children (they're in scope blocks)
266
+ if (isPerPortScopedChild(instance, workflow, nodeTypes)) {
267
+ return;
268
+ }
269
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
270
+ const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
271
+ if (!nodeType) {
272
+ lines.push(` // Node '${instance.id}' skipped: type '${instance.nodeType}' not found`);
273
+ return;
274
+ }
275
+ if (branchingNodes.has(instanceId)) {
276
+ // Chain members are generated by their chain head — skip
277
+ if (chainMembers.has(instanceId)) {
278
+ return;
279
+ }
280
+ // Chain heads: use flat chain generation
281
+ if (branchingChains.has(instanceId)) {
282
+ // For promoted chain heads, wrap in STEP guard
283
+ let chainIndent = ' ';
284
+ let chainNeedsClose = false;
285
+ if (nodesPromotedFromBranches.has(instanceId)) {
286
+ const stepSourceConditions = [];
287
+ workflow.connections.forEach((conn) => {
288
+ if (conn.to.node === instanceId && isExecutePort(conn.to.port)) {
289
+ const src = conn.from.node;
290
+ if (!isStartNode(src)) {
291
+ stepSourceConditions.push(buildStepSourceCondition(src, conn.from.port, branchingNodes));
292
+ }
293
+ }
294
+ });
295
+ if (stepSourceConditions.length > 0) {
296
+ const condition = stepSourceConditions.join(' && ');
297
+ lines.push(` if (${condition}) {`);
298
+ chainIndent = ' ';
299
+ chainNeedsClose = true;
300
+ }
301
+ }
302
+ generateBranchingChainCode(branchingChains.get(instanceId), workflow, nodeTypes, branchingNodes, branchRegions, availableVars, generatedNodes, lines, chainIndent, isAsync, 'ctx', bundleMode, branchingNodesNeedingSuccessFlag);
303
+ if (chainNeedsClose) {
304
+ lines.push(` }`);
305
+ }
306
+ return;
307
+ }
308
+ // Non-chain branching nodes: existing path
309
+ // For promoted branching nodes, wrap in STEP guard from execute port source
310
+ let branchIndent = ' ';
311
+ let branchNeedsClose = false;
312
+ if (nodesPromotedFromBranches.has(instanceId)) {
313
+ const stepSourceConditions = [];
314
+ workflow.connections.forEach((conn) => {
315
+ if (conn.to.node === instanceId && isExecutePort(conn.to.port)) {
316
+ const src = conn.from.node;
317
+ if (!isStartNode(src)) {
318
+ stepSourceConditions.push(buildStepSourceCondition(src, conn.from.port, branchingNodes));
319
+ }
320
+ }
321
+ });
322
+ if (stepSourceConditions.length > 0) {
323
+ const condition = stepSourceConditions.join(' && ');
324
+ lines.push(` if (${condition}) {`);
325
+ branchIndent = ' ';
326
+ branchNeedsClose = true;
327
+ }
328
+ }
329
+ generateBranchingNodeCode(instance, nodeType, workflow, nodeTypes, branchRegions.get(instanceId), availableVars, generatedNodes, lines, branchIndent, false, branchingNodes, branchRegions, isAsync, 'ctx', bundleMode, new Set(), branchingNodesNeedingSuccessFlag.has(instanceId));
330
+ if (branchNeedsClose) {
331
+ lines.push(` }`);
332
+ }
333
+ const region = branchRegions.get(instanceId);
334
+ region.successNodes.forEach((n) => generatedNodes.add(n));
335
+ region.failureNodes.forEach((n) => generatedNodes.add(n));
336
+ // Check if this node creates a scope and generate scoped children
337
+ generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, ' ', branchingNodes, branchRegions, isAsync, bundleMode);
338
+ }
339
+ else {
340
+ const belongsToBranch = Array.from(branchRegions.values()).some((region) => region.successNodes.has(instanceId) || region.failureNodes.has(instanceId));
341
+ if (!belongsToBranch) {
342
+ const nodeUseConst = !nodesInBranches.has(instanceId) &&
343
+ !branchingNodes.has(instanceId) &&
344
+ !pullExecutionNodes.has(instanceId) &&
345
+ !nodeLevelScopedChildren.has(instanceId);
346
+ generateNodeCallWithContext(instance, nodeType, workflow, availableVars, lines, nodeTypes, ' ', isAsync, nodeUseConst, undefined, // instanceParent
347
+ 'ctx', // ctxVar
348
+ bundleMode, false, // skipExecuteGuard
349
+ branchingNodes // for port-aware STEP guards
350
+ );
351
+ generatedNodes.add(instanceId);
352
+ // Check if this node creates a scope and generate scoped children
353
+ generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, ' ', branchingNodes, branchRegions, isAsync, bundleMode);
354
+ }
355
+ }
356
+ });
357
+ lines.push(` const exitIdx = ctx.addExecution('${RESERVED_NODE_NAMES.EXIT}');`);
358
+ const exitConnections = workflow.connections.filter((conn) => isExitNode(conn.to.node));
359
+ // Group exit connections by port (multiple connections to the same port are coalesced)
360
+ const exitConnectionsByPort = new Map();
361
+ exitConnections.forEach((conn) => {
362
+ const existing = exitConnectionsByPort.get(conn.to.port) || [];
363
+ existing.push(conn);
364
+ exitConnectionsByPort.set(conn.to.port, existing);
365
+ });
366
+ const returnProps = [];
367
+ const awaitKeyword = isAsync ? 'await ' : '';
368
+ const setCall = isAsync ? 'await ctx.setVariable' : 'ctx.setVariable';
369
+ exitConnectionsByPort.forEach((conns, exitPort) => {
370
+ // Get exit port type for type casting - check if exit port is declared
371
+ const exitPortDef = workflow.exitPorts[exitPort];
372
+ // Skip connections to undeclared exit ports (typos like Exit.resultx when only @returns result exists)
373
+ if (!exitPortDef) {
374
+ lines.push(` // Exit connection skipped: '${exitPort}' is not a declared @returns port`);
375
+ return;
376
+ }
377
+ const varName = `exit_${exitPort}`;
378
+ const exitPortType = exitPortDef?.tsType || (exitPortDef ? mapToTypeScript(exitPortDef.dataType) : 'unknown');
379
+ // Filter to valid connections (skip undeclared nodes, missing types)
380
+ const validConns = conns.filter((conn) => {
381
+ const sourceNode = conn.from.node;
382
+ const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
383
+ const sourceNodeType = nodeTypes.find((n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType);
384
+ if (!isStartNode(sourceNode) && !sourceInstance) {
385
+ lines.push(` // Exit connection skipped: source node '${sourceNode}' is not declared`);
386
+ return false;
387
+ }
388
+ if (!isStartNode(sourceNode) && sourceInstance && !sourceNodeType) {
389
+ lines.push(` // Exit connection skipped: source node '${sourceNode}' has missing type '${sourceInstance.nodeType}'`);
390
+ return false;
391
+ }
392
+ return true;
393
+ });
394
+ if (validConns.length === 0) {
395
+ lines.push(` const ${varName} = undefined as unknown;`);
396
+ returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
397
+ return;
398
+ }
399
+ // Helper to build a value expression for a single connection
400
+ const buildSourceExpr = (conn, defaultValue) => {
401
+ const sourceNode = conn.from.node;
402
+ const sourcePort = conn.from.port;
403
+ const sourceIdx = isStartNode(sourceNode) ? 'startIdx' : `${toValidIdentifier(sourceNode)}Idx`;
404
+ const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
405
+ const sourceNodeType = nodeTypes.find((n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType);
406
+ const pullConfig = sourceInstance && sourceNodeType
407
+ ? getPullExecutionConfig(sourceInstance, sourceNodeType)
408
+ : { enabled: false, triggerPort: 'execute' };
409
+ const isPullNode = pullConfig.enabled;
410
+ const needsUndefinedCheck = !isStartNode(sourceNode) &&
411
+ (nodesInBranches.has(sourceNode) ||
412
+ branchingNodes.has(sourceNode) ||
413
+ pullExecutionNodes.has(sourceNode) ||
414
+ nodeLevelScopedChildren.has(sourceNode));
415
+ if (isPullNode) {
416
+ return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx}!, nodeTypeName: '${sourceNode}' })`;
417
+ }
418
+ else if (needsUndefinedCheck) {
419
+ return `${sourceIdx} !== undefined ? ${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx} }) : ${defaultValue}`;
420
+ }
421
+ else {
422
+ return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx} })`;
423
+ }
424
+ };
425
+ const isControlFlowPort = exitPort === 'onSuccess' || exitPort === 'onFailure';
426
+ const defaultValue = isControlFlowPort ? 'false' : 'undefined';
427
+ if (validConns.length === 1) {
428
+ // Single connection - straightforward assignment
429
+ lines.push(` const ${varName} = ${buildSourceExpr(validConns[0], defaultValue)};`);
430
+ }
431
+ else {
432
+ // Multiple connections - coalesce with || (STEP ports) or ?? (data ports)
433
+ const operator = isControlFlowPort ? ' || ' : ' ?? ';
434
+ const parts = validConns.map((conn) => `(${buildSourceExpr(conn, defaultValue)})`);
435
+ lines.push(` const ${varName} = ${parts.join(operator)};`);
436
+ }
437
+ // Emit VARIABLE_SET for Exit node INPUT ports
438
+ if (!production) {
439
+ lines.push(` ${setCall}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: '${exitPort}', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, ${varName});`);
440
+ }
441
+ // Cast to the exit port's declared type for type safety
442
+ returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
443
+ });
444
+ // Add default undefined for declared exit ports that weren't connected
445
+ // (e.g., when connection had typo like Exit.resultx instead of Exit.result)
446
+ const connectedExitPorts = new Set(returnProps.map((prop) => prop.split(':')[0]));
447
+ Object.entries(workflow.exitPorts).forEach(([portName, portDef]) => {
448
+ if (!connectedExitPorts.has(portName) && portName !== 'onSuccess' && portName !== 'onFailure') {
449
+ const portType = portDef?.tsType || (portDef ? mapToTypeScript(portDef.dataType) : 'unknown');
450
+ lines.push(` // Exit port '${portName}' has no valid connection - using undefined`);
451
+ returnProps.push(`${portName}: undefined as unknown as ${portType}`);
452
+ }
453
+ });
454
+ lines.push('');
455
+ // Check if onSuccess/onFailure are explicitly connected
456
+ const hasOnSuccess = returnProps.some((prop) => prop.startsWith('onSuccess:'));
457
+ const hasOnFailure = returnProps.some((prop) => prop.startsWith('onFailure:'));
458
+ // Only add defaults if not explicitly connected
459
+ const defaults = [];
460
+ const setCallForDefaults = isAsync ? 'await ctx.setVariable' : 'ctx.setVariable';
461
+ if (!hasOnSuccess) {
462
+ defaults.push('onSuccess: true');
463
+ // Emit VARIABLE_SET for default onSuccess
464
+ if (!production) {
465
+ lines.push(` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onSuccess', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, true);`);
466
+ }
467
+ }
468
+ if (!hasOnFailure) {
469
+ defaults.push('onFailure: false');
470
+ // Emit VARIABLE_SET for default onFailure
471
+ if (!production) {
472
+ lines.push(` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onFailure', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, false);`);
473
+ }
474
+ }
475
+ const allProps = defaults.length > 0
476
+ ? `${defaults.join(', ')}${returnProps.length > 0 ? ', ' : ''}${returnProps.join(', ')}`
477
+ : returnProps.join(', ');
478
+ lines.push(` const finalResult = { ${allProps} };`);
479
+ lines.push('');
480
+ lines.push(` ctx.sendStatusChangedEvent({`);
481
+ lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}',`);
482
+ lines.push(` id: '${RESERVED_NODE_NAMES.EXIT}',`);
483
+ lines.push(` executionIndex: exitIdx,`);
484
+ lines.push(` status: 'SUCCEEDED',`);
485
+ lines.push(` });`);
486
+ lines.push(` ctx.sendWorkflowCompletedEvent({`);
487
+ lines.push(` executionIndex: exitIdx,`);
488
+ lines.push(` status: 'SUCCEEDED',`);
489
+ lines.push(` result: finalResult,`);
490
+ lines.push(` });`);
491
+ lines.push('');
492
+ lines.push(` return finalResult;`);
493
+ return lines.join('\n');
494
+ }
495
+ /**
496
+ * Helper function to generate scoped children execution for nodes that create scopes
497
+ */
498
+ function generateScopedChildrenExecution(parentInstance, parentNodeType, workflow, allNodeTypes, generatedNodes, availableVars, lines, indent, branchingNodes, branchRegions, isAsync, bundleMode = false) {
499
+ // Check if this node creates a scope
500
+ if (!parentNodeType.scope)
501
+ return;
502
+ // Check if this is a per-port scope (has scoped OUTPUT ports)
503
+ // Per-port scopes have children handled inside the scope function closure generated by
504
+ // buildNodeArgumentsWithContext -> generateScopeFunctionClosure, so we skip here
505
+ const rawScopeName = parentNodeType.scope;
506
+ const hasPerPortScope = Object.values(parentNodeType.outputs).some((portDef) => portDef.scope === rawScopeName);
507
+ if (hasPerPortScope) {
508
+ // Children are already generated inside the scope function closure
509
+ return;
510
+ }
511
+ const scopeName = `${parentInstance.id}.${parentNodeType.scope}`;
512
+ // Find children in this scope from workflow.scopes or instance.parentScope
513
+ const childrenInScope = [];
514
+ // Check workflow.scopes mapping
515
+ if (workflow.scopes && workflow.scopes[scopeName]) {
516
+ childrenInScope.push(...workflow.scopes[scopeName]);
517
+ }
518
+ // Also check instances with parent field
519
+ workflow.instances.forEach((instance) => {
520
+ const parentScopeName = instance.parent
521
+ ? `${instance.parent.id}.${instance.parent.scope}`
522
+ : null;
523
+ if (parentScopeName === scopeName && !childrenInScope.includes(instance.id)) {
524
+ childrenInScope.push(instance.id);
525
+ }
526
+ });
527
+ if (childrenInScope.length === 0)
528
+ return;
529
+ // Generate scope creation
530
+ const safeParentId = toValidIdentifier(parentInstance.id);
531
+ lines.push(``);
532
+ lines.push(`${indent}// Create scope for children of ${parentInstance.id}`);
533
+ lines.push(`${indent}const ${safeParentId}_scopedCtx = ctx.createScope('${parentInstance.id}', ${safeParentId}Idx, '${parentNodeType.scope}');`);
534
+ lines.push(``);
535
+ // Scoped context variable name for child generators
536
+ const scopedCtxVar = `${safeParentId}_scopedCtx`;
537
+ // Generate child nodes execution using scoped context
538
+ childrenInScope.forEach((childInstanceId) => {
539
+ if (generatedNodes.has(childInstanceId))
540
+ return;
541
+ const childInstance = workflow.instances.find((i) => i.id === childInstanceId);
542
+ if (!childInstance)
543
+ return;
544
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
545
+ const childNodeType = allNodeTypes.find((nt) => nt.name === childInstance.nodeType || nt.functionName === childInstance.nodeType);
546
+ if (!childNodeType)
547
+ return;
548
+ // Check if this child is a branching node
549
+ if (branchingNodes.has(childInstanceId)) {
550
+ generateBranchingNodeCode(childInstance, childNodeType, workflow, allNodeTypes, branchRegions.get(childInstanceId), availableVars, generatedNodes, lines, indent, false, branchingNodes, branchRegions, isAsync, scopedCtxVar, // Pass scoped context name
551
+ bundleMode);
552
+ const region = branchRegions.get(childInstanceId);
553
+ region.successNodes.forEach((n) => generatedNodes.add(n));
554
+ region.failureNodes.forEach((n) => generatedNodes.add(n));
555
+ }
556
+ else {
557
+ generateNodeCallWithContext(childInstance, childNodeType, workflow, availableVars, lines, allNodeTypes, indent, isAsync, false, // useConst = false - scoped children need let (referenced outside scope block)
558
+ parentInstance.id, // instanceParent - parent node is const, no ! needed when referencing it
559
+ scopedCtxVar, // Pass scoped context name
560
+ bundleMode);
561
+ }
562
+ generatedNodes.add(childInstanceId);
563
+ });
564
+ lines.push(``);
565
+ lines.push(`${indent}// Merge scope back into parent context`);
566
+ lines.push(`${indent}ctx.mergeScope(${parentInstance.id}_scopedCtx);`);
567
+ lines.push(``);
568
+ }
569
+ /**
570
+ * Sort branch nodes topologically based on their dependencies
571
+ *
572
+ * This ensures nodes within a branch execute in the correct order:
573
+ * dependencies before dependents.
574
+ *
575
+ * @param nodeIds - Set of node IDs within the branch
576
+ * @param workflow - The workflow AST
577
+ * @returns Array of node IDs in topologically sorted order
578
+ */
579
+ function sortBranchNodesTopologically(nodeIds, workflow) {
580
+ if (nodeIds.size === 0) {
581
+ return [];
582
+ }
583
+ // Build a mini control flow graph for just these nodes
584
+ const graph = new Map();
585
+ const inDegree = new Map();
586
+ // Initialize
587
+ nodeIds.forEach((id) => {
588
+ graph.set(id, []);
589
+ inDegree.set(id, 0);
590
+ });
591
+ // Add edges based on data flow connections within the branch
592
+ workflow.connections.forEach((conn) => {
593
+ const fromNode = conn.from.node;
594
+ const toNode = conn.to.node;
595
+ // Only consider connections within this branch
596
+ if (nodeIds.has(fromNode) && nodeIds.has(toNode)) {
597
+ const successors = graph.get(fromNode) || [];
598
+ if (!successors.includes(toNode)) {
599
+ successors.push(toNode);
600
+ graph.set(fromNode, successors);
601
+ inDegree.set(toNode, (inDegree.get(toNode) || 0) + 1);
602
+ }
603
+ }
604
+ });
605
+ // Perform Kahn's topological sort
606
+ const result = [];
607
+ const queue = [];
608
+ // Start with nodes that have no dependencies within the branch
609
+ inDegree.forEach((degree, node) => {
610
+ if (degree === 0) {
611
+ queue.push(node);
612
+ }
613
+ });
614
+ while (queue.length > 0) {
615
+ const node = queue.shift();
616
+ result.push(node);
617
+ const successors = graph.get(node) || [];
618
+ successors.forEach((successor) => {
619
+ const newDegree = (inDegree.get(successor) || 0) - 1;
620
+ inDegree.set(successor, newDegree);
621
+ if (newDegree === 0) {
622
+ queue.push(successor);
623
+ }
624
+ });
625
+ }
626
+ // If we haven't processed all nodes, there's a cycle (shouldn't happen after validation)
627
+ if (result.length !== nodeIds.size) {
628
+ // Fallback: add remaining nodes in arbitrary order
629
+ nodeIds.forEach((id) => {
630
+ if (!result.includes(id)) {
631
+ result.push(id);
632
+ }
633
+ });
634
+ }
635
+ return result;
636
+ }
637
+ /**
638
+ * Generates CANCELLED status events for all nodes in a branch that won't be executed.
639
+ * This is called when a branching node decides to take one path, marking nodes in
640
+ * the non-taken path as CANCELLED. We must add an execution index first to have a
641
+ * valid reference for the event.
642
+ */
643
+ function generateCancelledEventsForBranch(nodeIds, workflow, allNodeTypes, lines, indent, ctxVar = 'ctx' // Context variable name (for scoped contexts)
644
+ ) {
645
+ nodeIds.forEach((instanceId) => {
646
+ const instance = workflow.instances.find((i) => i.id === instanceId);
647
+ if (!instance)
648
+ return;
649
+ const safeId = toValidIdentifier(instanceId);
650
+ // Add execution index for this skipped node so the event has a valid reference
651
+ lines.push(`${indent}const ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
652
+ lines.push(`${indent}${ctxVar}.sendStatusChangedEvent({`);
653
+ lines.push(`${indent} nodeTypeName: '${instance.nodeType}',`);
654
+ lines.push(`${indent} id: '${instanceId}',`);
655
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
656
+ lines.push(`${indent} status: 'CANCELLED',`);
657
+ lines.push(`${indent}});`);
658
+ });
659
+ }
660
+ /**
661
+ * Build a guard condition for a STEP connection source.
662
+ * For branching nodes whose onSuccess/onFailure port is the source,
663
+ * use the _success flag to guard the correct branch path.
664
+ * For other sources, use Idx !== undefined.
665
+ */
666
+ function buildStepSourceCondition(sourceNode, sourcePort, branchingNodes) {
667
+ const safeNode = toValidIdentifier(sourceNode);
668
+ if (branchingNodes.has(sourceNode)) {
669
+ if (isSuccessPort(sourcePort)) {
670
+ return `${safeNode}_success`;
671
+ }
672
+ if (isFailurePort(sourcePort)) {
673
+ return `${safeNode}_success === false`;
674
+ }
675
+ }
676
+ return `${safeNode}Idx !== undefined`;
677
+ }
678
+ /**
679
+ * Generate flat code for a sequential chain of branching nodes.
680
+ *
681
+ * Instead of nesting each subsequent branching node inside the previous one's
682
+ * success/failure branch (O(N) depth), this generates them sequentially with
683
+ * accumulated guard conditions (O(1) depth).
684
+ *
685
+ * For chain [A, B, C]:
686
+ * A code (no guard)
687
+ * if (A_success) { B code } else { CANCELLED for B,C and regions }
688
+ * if (A_success && B_success) { C code } else { CANCELLED for C and regions }
689
+ */
690
+ function generateBranchingChainCode(chain, workflow, nodeTypes, branchingNodes, branchRegions, availableVars, generatedNodes, lines, indent, isAsync, ctxVar, bundleMode, forceTrackSuccessNodes = new Set()) {
691
+ // Pre-declare success flags for all non-last chain nodes so they're
692
+ // accessible across guard blocks (avoiding let-in-block scoping issues).
693
+ // Also pre-declare for the last node if promoted nodes depend on its _success flag.
694
+ const preDeclaredFlags = new Set();
695
+ for (let i = 0; i < chain.length; i++) {
696
+ const isLast = i === chain.length - 1;
697
+ if (!isLast || forceTrackSuccessNodes.has(chain[i])) {
698
+ const safeId = toValidIdentifier(chain[i]);
699
+ lines.push(`${indent}let ${safeId}_success = false;`);
700
+ preDeclaredFlags.add(safeId);
701
+ }
702
+ }
703
+ if (chain.length > 1) {
704
+ lines.push('');
705
+ }
706
+ const guardParts = [];
707
+ for (let i = 0; i < chain.length; i++) {
708
+ const nodeId = chain[i];
709
+ const isLast = i === chain.length - 1;
710
+ const instance = workflow.instances.find((inst) => inst.id === nodeId);
711
+ if (!instance)
712
+ continue;
713
+ const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
714
+ if (!nodeType)
715
+ continue;
716
+ const safeId = toValidIdentifier(nodeId);
717
+ const originalRegion = branchRegions.get(nodeId);
718
+ // For non-last nodes, create modified region with chain successor removed
719
+ // so generateBranchingNodeCode won't recurse into the next chain node
720
+ let effectiveRegion = originalRegion;
721
+ if (!isLast) {
722
+ const nextNodeId = chain[i + 1];
723
+ effectiveRegion = {
724
+ successNodes: new Set([...originalRegion.successNodes].filter((n) => n !== nextNodeId)),
725
+ failureNodes: new Set([...originalRegion.failureNodes].filter((n) => n !== nextNodeId)),
726
+ };
727
+ }
728
+ const hasGuard = guardParts.length > 0;
729
+ const guardCondition = guardParts.join(' && ');
730
+ if (hasGuard) {
731
+ lines.push(`${indent}if (${guardCondition}) {`);
732
+ }
733
+ const nodeIndent = hasGuard ? indent + ' ' : indent;
734
+ generateBranchingNodeCode(instance, nodeType, workflow, nodeTypes, effectiveRegion, availableVars, generatedNodes, lines, nodeIndent, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode, preDeclaredFlags, !isLast || forceTrackSuccessNodes.has(chain[i]) // forceTrackSuccess for non-last chain nodes or nodes with promoted dependents
735
+ );
736
+ // Generate scoped children for this chain node
737
+ generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, nodeIndent, branchingNodes, branchRegions, isAsync, bundleMode);
738
+ if (hasGuard) {
739
+ lines.push(`${indent}} else {`);
740
+ // Emit CANCELLED for this node and all remaining chain nodes + their regions
741
+ const remainingNodes = new Set();
742
+ for (let j = i; j < chain.length; j++) {
743
+ const chainNodeId = chain[j];
744
+ remainingNodes.add(chainNodeId);
745
+ const region = branchRegions.get(chainNodeId);
746
+ region.successNodes.forEach((n) => remainingNodes.add(n));
747
+ region.failureNodes.forEach((n) => remainingNodes.add(n));
748
+ }
749
+ generateCancelledEventsForBranch(remainingNodes, workflow, nodeTypes, lines, indent + ' ', ctxVar);
750
+ lines.push(`${indent}}`);
751
+ }
752
+ // Add success condition for next iteration's guard
753
+ if (!isLast) {
754
+ guardParts.push(`${safeId}_success`);
755
+ }
756
+ // Mark node and its effective region as generated
757
+ generatedNodes.add(nodeId);
758
+ effectiveRegion.successNodes.forEach((n) => generatedNodes.add(n));
759
+ effectiveRegion.failureNodes.forEach((n) => generatedNodes.add(n));
760
+ }
761
+ }
762
+ function generateBranchingNodeCode(instance, branchNode, workflow, allNodeTypes, region, availableVars, generatedNodes, lines, indent, _generateReturns = true, // DEPRECATED: always false, kept for signature compat
763
+ branchingNodes, branchRegions, isAsync, ctxVar = 'ctx', // Context variable name (for scoped contexts)
764
+ bundleMode = false, preDeclaredSuccessFlags = new Set(), forceTrackSuccess = false) {
765
+ const instanceId = instance.id;
766
+ const safeId = toValidIdentifier(instanceId);
767
+ const functionName = branchNode.functionName;
768
+ lines.push(`${indent}${ctxVar}.checkAborted('${instanceId}');`);
769
+ lines.push(`${indent}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
770
+ lines.push(`${indent}${ctxVar}.sendStatusChangedEvent({`);
771
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
772
+ lines.push(`${indent} id: '${instanceId}',`);
773
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
774
+ lines.push(`${indent} status: 'RUNNING',`);
775
+ lines.push(`${indent}});`);
776
+ lines.push('');
777
+ // Only declare success flag if there are downstream nodes
778
+ const hasSuccessDownstream = region.successNodes.size > 0;
779
+ const hasFailureDownstream = region.failureNodes.size > 0;
780
+ const hasDownstream = hasSuccessDownstream || hasFailureDownstream;
781
+ // Track success flag when there are downstream nodes OR when chain code needs it
782
+ const trackSuccess = hasDownstream || forceTrackSuccess;
783
+ if (trackSuccess) {
784
+ if (preDeclaredSuccessFlags.has(safeId)) {
785
+ // Flag was pre-declared by chain code — use assignment, not declaration
786
+ lines.push(`${indent}${safeId}_success = false;`);
787
+ }
788
+ else {
789
+ lines.push(`${indent}let ${safeId}_success = false;`);
790
+ }
791
+ lines.push('');
792
+ }
793
+ lines.push(`${indent}try {`);
794
+ const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
795
+ const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
796
+ const argNames = buildNodeArgumentsWithContext({
797
+ node: branchNode,
798
+ workflow,
799
+ id: instanceId,
800
+ lines,
801
+ indent: `${indent} `,
802
+ getCall,
803
+ isAsync,
804
+ emitInputEvents: true,
805
+ setCall,
806
+ nodeTypeName: functionName,
807
+ bundleMode,
808
+ });
809
+ const awaitKeyword = branchNode.isAsync ? 'await ' : '';
810
+ if (branchNode.expression) {
811
+ // Expression branching node: call without execute, auto-set onSuccess/onFailure
812
+ lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
813
+ // Determine data output ports (exclude control flow and scoped ports)
814
+ const dataOutputPorts = Object.keys(branchNode.outputs).filter((portName) => {
815
+ const portConfig = branchNode.outputs[portName];
816
+ if (portConfig.scope)
817
+ return false;
818
+ if (isSuccessPort(portName) || isFailurePort(portName))
819
+ return false;
820
+ if (portConfig.isControlFlow || portConfig.failure)
821
+ return false;
822
+ return true;
823
+ });
824
+ if (dataOutputPorts.length === 1) {
825
+ // Single data output: destructure if result is an object with the port key, else use raw value
826
+ // Extract to unknown-typed variable to prevent TypeScript from narrowing
827
+ // specific return types (e.g. boolean) to `never` in the typeof check
828
+ const portName = dataOutputPorts[0];
829
+ const rawVar = `${safeId}Result_raw`;
830
+ lines.push(`${indent} const ${rawVar}: unknown = ${safeId}Result;`);
831
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, typeof ${rawVar} === 'object' && ${rawVar} !== null && '${portName}' in ${rawVar} ? ${rawVar}.${portName} : ${rawVar});`);
832
+ }
833
+ else {
834
+ // Multiple data outputs: destructure from object return
835
+ dataOutputPorts.forEach((portName) => {
836
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
837
+ });
838
+ }
839
+ // Auto-set onSuccess/onFailure
840
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
841
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
842
+ }
843
+ else if (branchNode.variant === 'MAP_ITERATOR') {
844
+ // MAP_ITERATOR: inline iteration — no user function to call
845
+ // argNames: [execute, items, scopeFn]
846
+ const executeArg = argNames[0];
847
+ const itemsArg = argNames[1];
848
+ const scopeFnArg = argNames[2];
849
+ lines.push(`${indent} let ${safeId}Result: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
850
+ lines.push(`${indent} if (!${executeArg}) {`);
851
+ lines.push(`${indent} ${safeId}Result = { onSuccess: false, onFailure: false, results: [] };`);
852
+ lines.push(`${indent} } else {`);
853
+ lines.push(`${indent} const __results: unknown[] = [];`);
854
+ lines.push(`${indent} for (const __item of ${itemsArg}) {`);
855
+ lines.push(`${indent} __results.push((${isAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
856
+ lines.push(`${indent} }`);
857
+ lines.push(`${indent} ${safeId}Result = { onSuccess: true, onFailure: false, results: __results };`);
858
+ lines.push(`${indent} }`);
859
+ // Set output ports from result
860
+ Object.keys(branchNode.outputs).forEach((portName) => {
861
+ const portConfig = branchNode.outputs[portName];
862
+ if (portConfig.scope)
863
+ return;
864
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
865
+ });
866
+ }
867
+ else if (branchNode.variant === 'IMPORTED_WORKFLOW' || branchNode.variant === 'WORKFLOW') {
868
+ // Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
869
+ // Workflows use (execute, params) signature where params is an object
870
+ // Regular nodes use (execute, arg1, arg2, ...) positional signature
871
+ // For workflow calls, wrap data args in an object and include recursion depth
872
+ const executeArg = argNames[0]; // First arg is always execute
873
+ const dataArgs = argNames.slice(1); // Rest are data inputs
874
+ const inputPortNames = Object.keys(branchNode.inputs).filter((p) => !isExecutePort(p));
875
+ // Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
876
+ // Assign to variable first to avoid TypeScript excess property checking on object literals
877
+ const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
878
+ paramsEntries.push('__rd__: __rd__ + 1');
879
+ const paramsObj = `{ ${paramsEntries.join(', ')} }`;
880
+ const paramsVar = `__${safeId}Params__`;
881
+ lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
882
+ lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
883
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
884
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
885
+ Object.keys(branchNode.outputs).forEach((portName) => {
886
+ const portConfig = branchNode.outputs[portName];
887
+ if (portConfig.scope)
888
+ return;
889
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
890
+ });
891
+ }
892
+ else if (branchNode.scope || (branchNode.scopes && branchNode.scopes.length > 0)) {
893
+ // Scoped node call with positional arguments (uses _impl signature)
894
+ // Scoped nodes have callback parameters that can't be passed via params object
895
+ lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
896
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
897
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
898
+ Object.keys(branchNode.outputs).forEach((portName) => {
899
+ const portConfig = branchNode.outputs[portName];
900
+ if (portConfig.scope)
901
+ return;
902
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
903
+ });
904
+ }
905
+ else {
906
+ // Regular node call - always use positional args
907
+ // In bundle mode we import _impl which takes (execute, ...positional_args)
908
+ lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
909
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
910
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
911
+ Object.keys(branchNode.outputs).forEach((portName) => {
912
+ const portConfig = branchNode.outputs[portName];
913
+ if (portConfig.scope)
914
+ return;
915
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
916
+ });
917
+ }
918
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
919
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
920
+ lines.push(`${indent} id: '${instanceId}',`);
921
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
922
+ lines.push(`${indent} status: 'SUCCEEDED',`);
923
+ lines.push(`${indent} });`);
924
+ // Use onSuccess from result to determine control flow
925
+ // For expression nodes, onSuccess is always true here (catch handles failure)
926
+ if (trackSuccess) {
927
+ lines.push(`${indent} ${safeId}_success = ${branchNode.expression ? 'true' : `${safeId}Result.onSuccess`};`);
928
+ }
929
+ lines.push(`${indent}} catch (error: unknown) {`);
930
+ lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
931
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
932
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
933
+ lines.push(`${indent} id: '${instanceId}',`);
934
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
935
+ lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
936
+ lines.push(`${indent} });`);
937
+ lines.push(`${indent} if (!isCancellation) {`);
938
+ lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
939
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
940
+ lines.push(`${indent} id: '${instanceId}',`);
941
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
942
+ lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
943
+ lines.push(`${indent} });`);
944
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
945
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
946
+ if (trackSuccess) {
947
+ lines.push(`${indent} ${safeId}_success = false;`);
948
+ }
949
+ lines.push(`${indent} }`);
950
+ // Emit CANCELLED for all downstream nodes since branching node threw
951
+ if (hasSuccessDownstream) {
952
+ generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar);
953
+ }
954
+ if (hasFailureDownstream) {
955
+ generateCancelledEventsForBranch(region.failureNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar);
956
+ }
957
+ // Re-throw the error to propagate it up (important for recursive workflows)
958
+ lines.push(`${indent} throw error;`);
959
+ lines.push(`${indent}}`);
960
+ lines.push('');
961
+ // Only generate if/else if there are downstream nodes
962
+ if (hasDownstream) {
963
+ lines.push(`${indent}if (${safeId}_success) {`);
964
+ // Emit CANCELLED for failure branch nodes since success path was taken
965
+ if (hasFailureDownstream) {
966
+ generateCancelledEventsForBranch(region.failureNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar);
967
+ }
968
+ const successVars = new Map(availableVars);
969
+ Object.keys(branchNode.outputs).forEach((portName) => {
970
+ successVars.set(`${instanceId}.${portName}`, `${safeId}Result.${portName}`);
971
+ });
972
+ // Sort success branch nodes topologically to ensure correct execution order
973
+ const successInstanceIds = sortBranchNodesTopologically(region.successNodes, workflow);
974
+ const successExecutedNodes = [instance.id];
975
+ successInstanceIds.forEach((instanceId) => {
976
+ const inst = workflow.instances.find((i) => i.id === instanceId);
977
+ if (!inst)
978
+ return;
979
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
980
+ const nodeType = allNodeTypes.find((nt) => nt.name === inst.nodeType || nt.functionName === inst.nodeType);
981
+ if (!nodeType)
982
+ return;
983
+ if (branchingNodes.has(instanceId)) {
984
+ const nestedRegion = branchRegions.get(instanceId);
985
+ generateBranchingNodeCode(inst, nodeType, workflow, allNodeTypes, nestedRegion, successVars, generatedNodes, lines, `${indent} `, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode);
986
+ successExecutedNodes.push(instanceId);
987
+ nestedRegion.successNodes.forEach((n) => successExecutedNodes.push(n));
988
+ nestedRegion.failureNodes.forEach((n) => successExecutedNodes.push(n));
989
+ }
990
+ else {
991
+ generateNodeCallWithContext(inst, nodeType, workflow, successVars, lines, allNodeTypes, `${indent} `, isAsync, false, // useConst
992
+ undefined, // instanceParent
993
+ ctxVar, bundleMode, true // skipExecuteGuard — inside branch, execute is guaranteed by if/else
994
+ );
995
+ Object.keys(nodeType.outputs).forEach((portName) => {
996
+ successVars.set(`${instanceId}.${portName}`, `${toValidIdentifier(instanceId)}Result.${portName}`);
997
+ });
998
+ successExecutedNodes.push(instanceId);
999
+ generatedNodes.add(instanceId);
1000
+ }
1001
+ });
1002
+ // Only generate else block if there are failure nodes to execute
1003
+ if (hasFailureDownstream) {
1004
+ lines.push(`${indent}} else {`);
1005
+ // Emit CANCELLED for success branch nodes since failure path was taken
1006
+ if (hasSuccessDownstream) {
1007
+ generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar);
1008
+ }
1009
+ const failureVars = new Map(availableVars);
1010
+ Object.keys(branchNode.outputs).forEach((portName) => {
1011
+ failureVars.set(`${instanceId}.${portName}`, `${safeId}Result.${portName}`);
1012
+ });
1013
+ // Sort failure branch nodes topologically to ensure correct execution order
1014
+ const failureInstanceIds = sortBranchNodesTopologically(region.failureNodes, workflow);
1015
+ const failureExecutedNodes = [instance.id];
1016
+ failureInstanceIds.forEach((instanceId) => {
1017
+ const inst = workflow.instances.find((i) => i.id === instanceId);
1018
+ if (!inst)
1019
+ return;
1020
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
1021
+ const nodeType = allNodeTypes.find((nt) => nt.name === inst.nodeType || nt.functionName === inst.nodeType);
1022
+ if (!nodeType)
1023
+ return;
1024
+ if (branchingNodes.has(instanceId)) {
1025
+ const nestedRegion = branchRegions.get(instanceId);
1026
+ generateBranchingNodeCode(inst, nodeType, workflow, allNodeTypes, nestedRegion, failureVars, generatedNodes, lines, `${indent} `, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode);
1027
+ failureExecutedNodes.push(instanceId);
1028
+ nestedRegion.successNodes.forEach((n) => failureExecutedNodes.push(n));
1029
+ nestedRegion.failureNodes.forEach((n) => failureExecutedNodes.push(n));
1030
+ }
1031
+ else {
1032
+ generateNodeCallWithContext(inst, nodeType, workflow, failureVars, lines, allNodeTypes, `${indent} `, isAsync, false, // useConst
1033
+ undefined, // instanceParent
1034
+ ctxVar, bundleMode, true // skipExecuteGuard — inside branch, execute is guaranteed by if/else
1035
+ );
1036
+ Object.keys(nodeType.outputs).forEach((portName) => {
1037
+ failureVars.set(`${instanceId}.${portName}`, `${toValidIdentifier(instanceId)}Result.${portName}`);
1038
+ });
1039
+ failureExecutedNodes.push(instanceId);
1040
+ generatedNodes.add(instanceId);
1041
+ }
1042
+ });
1043
+ lines.push(`${indent}}`);
1044
+ }
1045
+ else {
1046
+ // No failure branch - emit CANCELLED for success nodes and close
1047
+ if (hasSuccessDownstream) {
1048
+ lines.push(`${indent}} else {`);
1049
+ generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar);
1050
+ lines.push(`${indent}}`);
1051
+ }
1052
+ else {
1053
+ lines.push(`${indent}}`);
1054
+ }
1055
+ }
1056
+ }
1057
+ generatedNodes.add(instanceId);
1058
+ }
1059
+ function generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar = 'ctx', // Context variable name (for scoped contexts)
1060
+ bundleMode = false) {
1061
+ const instanceId = instance.id;
1062
+ const safeId = toValidIdentifier(instanceId);
1063
+ const functionName = nodeType.functionName;
1064
+ // Executor must be async if:
1065
+ // 1. The workflow is async (context returns Promises)
1066
+ // 2. OR the node function is async (function returns Promise)
1067
+ // Sync executors only work when BOTH workflow and node are sync
1068
+ const executorIsAsync = isAsync || nodeType.isAsync;
1069
+ const asyncKeyword = executorIsAsync ? 'async ' : '';
1070
+ const awaitKeyword = nodeType.isAsync ? 'await ' : '';
1071
+ // Create a lazy execution function for this pull node
1072
+ // The function will only execute when its outputs are accessed
1073
+ lines.push(`${indent}// Pull execution node: ${instanceId}`);
1074
+ lines.push(`${indent}const ${safeId}_executor = ${asyncKeyword}() => {`);
1075
+ lines.push(`${indent} if (${safeId}Idx !== undefined) {`);
1076
+ lines.push(`${indent} return; // Already executed`);
1077
+ lines.push(`${indent} }`);
1078
+ lines.push(`${indent} ${ctxVar}.checkAborted('${instanceId}');`);
1079
+ lines.push(`${indent} ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
1080
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
1081
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1082
+ lines.push(`${indent} id: '${instanceId}',`);
1083
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1084
+ lines.push(`${indent} status: 'RUNNING',`);
1085
+ lines.push(`${indent} });`);
1086
+ lines.push(`${indent} try {`);
1087
+ // Use executor's async status for get/set calls within the executor
1088
+ const getCall = executorIsAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
1089
+ const setCall = executorIsAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
1090
+ const args = buildNodeArgumentsWithContext({
1091
+ node: nodeType,
1092
+ workflow,
1093
+ id: instanceId,
1094
+ lines,
1095
+ indent: `${indent} `,
1096
+ getCall,
1097
+ isAsync: executorIsAsync,
1098
+ emitInputEvents: true,
1099
+ setCall,
1100
+ nodeTypeName: functionName,
1101
+ bundleMode,
1102
+ });
1103
+ const resultVar = `${safeId}Result`;
1104
+ // Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
1105
+ // Workflows use (execute, params) signature where params is an object
1106
+ // Regular nodes use (execute, arg1, arg2, ...) positional signature
1107
+ if (nodeType.variant === 'MAP_ITERATOR') {
1108
+ // MAP_ITERATOR: inline iteration in pull executor
1109
+ const executeArg = args[0];
1110
+ const itemsArg = args[1];
1111
+ const scopeFnArg = args[2];
1112
+ lines.push(`${indent} let ${resultVar}: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
1113
+ lines.push(`${indent} if (!${executeArg}) {`);
1114
+ lines.push(`${indent} ${resultVar} = { onSuccess: false, onFailure: false, results: [] };`);
1115
+ lines.push(`${indent} } else {`);
1116
+ lines.push(`${indent} const __results: unknown[] = [];`);
1117
+ lines.push(`${indent} for (const __item of ${itemsArg}) {`);
1118
+ lines.push(`${indent} __results.push((${executorIsAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
1119
+ lines.push(`${indent} }`);
1120
+ lines.push(`${indent} ${resultVar} = { onSuccess: true, onFailure: false, results: __results };`);
1121
+ lines.push(`${indent} }`);
1122
+ }
1123
+ else if (nodeType.variant === 'IMPORTED_WORKFLOW' || nodeType.variant === 'WORKFLOW') {
1124
+ // For workflow calls, wrap data args in an object and include recursion depth
1125
+ const executeArg = args[0]; // First arg is always execute
1126
+ const dataArgs = args.slice(1); // Rest are data inputs
1127
+ const inputPortNames = Object.keys(nodeType.inputs).filter((p) => !isExecutePort(p));
1128
+ // Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
1129
+ // Assign to variable first to avoid TypeScript excess property checking on object literals
1130
+ const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
1131
+ paramsEntries.push('__rd__: __rd__ + 1');
1132
+ const paramsObj = `{ ${paramsEntries.join(', ')} }`;
1133
+ const paramsVar = `__${safeId}Params__`;
1134
+ lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
1135
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
1136
+ }
1137
+ else if (nodeType.scope || (nodeType.scopes && nodeType.scopes.length > 0)) {
1138
+ // Scoped node call with positional arguments (uses _impl signature)
1139
+ // Scoped nodes have callback parameters that can't be passed via params object
1140
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
1141
+ }
1142
+ else {
1143
+ // Regular node call - always use positional args
1144
+ // In bundle mode we import _impl which takes (execute, ...positional_args)
1145
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
1146
+ }
1147
+ Object.keys(nodeType.outputs).forEach((portName) => {
1148
+ if (isSuccessPort(portName) || isFailurePort(portName))
1149
+ return;
1150
+ // Skip scoped OUTPUT ports - they don't exist in the function return value
1151
+ const portConfig = nodeType.outputs[portName];
1152
+ if (portConfig.scope)
1153
+ return;
1154
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1155
+ });
1156
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
1157
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1158
+ lines.push(`${indent} id: '${instanceId}',`);
1159
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1160
+ lines.push(`${indent} status: 'SUCCEEDED',`);
1161
+ lines.push(`${indent} });`);
1162
+ lines.push(`${indent} } catch (error: unknown) {`);
1163
+ lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
1164
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
1165
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1166
+ lines.push(`${indent} id: '${instanceId}',`);
1167
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1168
+ lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
1169
+ lines.push(`${indent} });`);
1170
+ lines.push(`${indent} if (!isCancellation) {`);
1171
+ lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
1172
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1173
+ lines.push(`${indent} id: '${instanceId}',`);
1174
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1175
+ lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
1176
+ lines.push(`${indent} });`);
1177
+ lines.push(`${indent} }`);
1178
+ lines.push(`${indent} throw error;`);
1179
+ lines.push(`${indent} }`);
1180
+ lines.push(`${indent}};`);
1181
+ lines.push(`${indent}// Register lazy executor for pull node`);
1182
+ lines.push(`${indent}${ctxVar}.registerPullExecutor('${instanceId}', ${safeId}_executor);`);
1183
+ lines.push(``);
1184
+ }
1185
+ function generateNodeCallWithContext(instance, nodeType, workflow, _availableVars, lines, _allNodeTypes, indent, isAsync, useConst = false, // Use const for nodes that always execute (not in branches)
1186
+ instanceParent, // Parent node ID for scope children (parent is const, no ! needed)
1187
+ ctxVar = 'ctx', // Context variable name (for scoped contexts)
1188
+ bundleMode = false, // Bundle mode uses params object pattern for wrapper functions
1189
+ skipExecuteGuard = false, // Skip execute port STEP guard (for nodes inside branch blocks)
1190
+ branchingNodes = new Set() // Branching nodes set for port-aware STEP guards
1191
+ ) {
1192
+ const instanceId = instance.id;
1193
+ const safeId = toValidIdentifier(instanceId);
1194
+ const functionName = nodeType.functionName;
1195
+ // Check if this instance has pull execution enabled
1196
+ const fullInstance = workflow.instances.find((i) => i.id === instanceId);
1197
+ const pullConfig = fullInstance
1198
+ ? getPullExecutionConfig(fullInstance, nodeType)
1199
+ : { enabled: false, triggerPort: 'execute' };
1200
+ // If this is a pull execution node, wrap it in a lazy function
1201
+ if (pullConfig.enabled) {
1202
+ generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar, bundleMode);
1203
+ return;
1204
+ }
1205
+ const stepInputs = Object.entries(nodeType.inputs).filter(([portName, portConfig]) => {
1206
+ // Include the primary execute port for promoted nodes (useConst=false at top level)
1207
+ // so they get a STEP guard from their conditional source.
1208
+ // Skip execute guard for nodes inside branch blocks (execute is guaranteed by if/else).
1209
+ if (isExecutePort(portName) && !useConst && !skipExecuteGuard) {
1210
+ return portConfig.dataType === 'STEP';
1211
+ }
1212
+ return portConfig.dataType === 'STEP' && !isExecutePort(portName);
1213
+ });
1214
+ // Expression nodes don't declare 'execute' in their inputs, but the workflow
1215
+ // connects STEP signals to their execute port. Include it for promoted expression
1216
+ // nodes so they get a proper branch-aware STEP guard.
1217
+ if (nodeType.expression && !useConst && !skipExecuteGuard && !stepInputs.some(([p]) => isExecutePort(p))) {
1218
+ const hasExecuteConnection = workflow.connections.some((conn) => conn.to.node === instanceId && isExecutePort(conn.to.port));
1219
+ if (hasExecuteConnection) {
1220
+ stepInputs.push(['execute', { dataType: 'STEP' }]);
1221
+ }
1222
+ }
1223
+ const stepSignalSources = [];
1224
+ if (stepInputs.length > 0) {
1225
+ workflow.connections.forEach((conn) => {
1226
+ const toNode = conn.to.node;
1227
+ const toPort = conn.to.port;
1228
+ if (toNode === instanceId && stepInputs.some(([port]) => port === toPort)) {
1229
+ const sourceNode = conn.from.node;
1230
+ // Skip per-port scoped children - they don't have Idx in main scope
1231
+ const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
1232
+ if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
1233
+ return;
1234
+ }
1235
+ if (!isStartNode(sourceNode) && !stepSignalSources.includes(sourceNode)) {
1236
+ stepSignalSources.push(sourceNode);
1237
+ }
1238
+ }
1239
+ });
1240
+ }
1241
+ let shouldIndent = false;
1242
+ if (stepSignalSources.length > 0) {
1243
+ const conditions = [];
1244
+ const portToSources = new Map();
1245
+ stepInputs.forEach(([portName]) => {
1246
+ const sources = [];
1247
+ workflow.connections.forEach((conn) => {
1248
+ const toNode = conn.to.node;
1249
+ const toPort = conn.to.port;
1250
+ if (toNode === instanceId && toPort === portName) {
1251
+ const sourceNode = conn.from.node;
1252
+ if (!isStartNode(sourceNode)) {
1253
+ // Skip per-port scoped children - they don't have Idx in main scope
1254
+ const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
1255
+ if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
1256
+ return;
1257
+ }
1258
+ sources.push({ node: sourceNode, port: conn.from.port });
1259
+ }
1260
+ }
1261
+ });
1262
+ if (sources.length > 0) {
1263
+ portToSources.set(portName, sources);
1264
+ }
1265
+ });
1266
+ if (nodeType.executeWhen === EXECUTION_STRATEGIES.CONJUNCTION) {
1267
+ // CONJUNCTION: Execute when ALL input ports have data (AND logic)
1268
+ portToSources.forEach((sources, _portName) => {
1269
+ if (sources.length === 1) {
1270
+ conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
1271
+ }
1272
+ else {
1273
+ const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
1274
+ conditions.push(`(${orCondition})`);
1275
+ }
1276
+ });
1277
+ if (conditions.length > 0) {
1278
+ const fullCondition = conditions.join(' && ');
1279
+ lines.push(`${indent}if (${fullCondition}) {`);
1280
+ indent = `${indent} `;
1281
+ shouldIndent = true;
1282
+ }
1283
+ }
1284
+ else if (nodeType.executeWhen === EXECUTION_STRATEGIES.DISJUNCTION) {
1285
+ // DISJUNCTION: Execute when ANY input port has data (OR logic)
1286
+ const allSources = [];
1287
+ portToSources.forEach((sources) => {
1288
+ sources.forEach((source) => {
1289
+ if (!allSources.some((s) => s.node === source.node && s.port === source.port)) {
1290
+ allSources.push(source);
1291
+ }
1292
+ });
1293
+ });
1294
+ if (allSources.length > 0) {
1295
+ const fullCondition = allSources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
1296
+ lines.push(`${indent}if (${fullCondition}) {`);
1297
+ indent = `${indent} `;
1298
+ shouldIndent = true;
1299
+ }
1300
+ }
1301
+ else if (nodeType.executeWhen === EXECUTION_STRATEGIES.CUSTOM) {
1302
+ // CUSTOM: User-provided execution condition
1303
+ // Custom condition should be in nodeType.metadata.customExecuteCondition
1304
+ const customCondition = nodeType.metadata?.customExecuteCondition;
1305
+ if (customCondition && typeof customCondition === 'string') {
1306
+ lines.push(`${indent}if (${customCondition}) {`);
1307
+ indent = `${indent} `;
1308
+ shouldIndent = true;
1309
+ }
1310
+ else {
1311
+ // Fallback to CONJUNCTION if no custom condition provided
1312
+ portToSources.forEach((sources, _portName) => {
1313
+ if (sources.length === 1) {
1314
+ conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
1315
+ }
1316
+ else {
1317
+ const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
1318
+ conditions.push(`(${orCondition})`);
1319
+ }
1320
+ });
1321
+ if (conditions.length > 0) {
1322
+ const fullCondition = conditions.join(' && ');
1323
+ lines.push(`${indent}if (${fullCondition}) {`);
1324
+ indent = `${indent} `;
1325
+ shouldIndent = true;
1326
+ }
1327
+ }
1328
+ }
1329
+ }
1330
+ const varDecl = useConst ? 'const ' : '';
1331
+ lines.push(`${indent}${ctxVar}.checkAborted('${instanceId}');`);
1332
+ lines.push(`${indent}${varDecl}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
1333
+ lines.push(`${indent}${ctxVar}.sendStatusChangedEvent({`);
1334
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1335
+ lines.push(`${indent} id: '${instanceId}',`);
1336
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1337
+ lines.push(`${indent} status: 'RUNNING',`);
1338
+ lines.push(`${indent}});`);
1339
+ lines.push(`${indent}try {`);
1340
+ const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
1341
+ const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
1342
+ const args = buildNodeArgumentsWithContext({
1343
+ node: nodeType,
1344
+ workflow,
1345
+ id: instanceId,
1346
+ lines,
1347
+ indent: `${indent} `,
1348
+ getCall,
1349
+ isAsync,
1350
+ instanceParent,
1351
+ emitInputEvents: true,
1352
+ setCall,
1353
+ nodeTypeName: functionName,
1354
+ bundleMode,
1355
+ });
1356
+ const resultVar = `${safeId}Result`;
1357
+ const awaitKeyword = nodeType.isAsync ? 'await ' : '';
1358
+ if (nodeType.expression) {
1359
+ // Expression node: call without execute, map raw return to output ports
1360
+ // _impl returns data only; onSuccess/onFailure are auto-set in the workflow body
1361
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
1362
+ // Determine data output ports (exclude control flow and scoped ports)
1363
+ const dataOutputPorts = Object.keys(nodeType.outputs).filter((portName) => {
1364
+ const portConfig = nodeType.outputs[portName];
1365
+ if (portConfig.scope)
1366
+ return false;
1367
+ if (isSuccessPort(portName) || isFailurePort(portName))
1368
+ return false;
1369
+ if (portConfig.isControlFlow || portConfig.failure)
1370
+ return false;
1371
+ return true;
1372
+ });
1373
+ if (dataOutputPorts.length === 1) {
1374
+ // Single data output: destructure if result is an object with the port key, else use raw value
1375
+ // Extract to unknown-typed variable to prevent TypeScript from narrowing
1376
+ // specific return types (e.g. boolean) to `never` in the typeof check
1377
+ const portName = dataOutputPorts[0];
1378
+ const rawVar = `${resultVar}_raw`;
1379
+ lines.push(`${indent} const ${rawVar}: unknown = ${resultVar};`);
1380
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, typeof ${rawVar} === 'object' && ${rawVar} !== null && '${portName}' in ${rawVar} ? ${rawVar}.${portName} : ${rawVar});`);
1381
+ }
1382
+ else {
1383
+ // Multiple data outputs: destructure from object return
1384
+ dataOutputPorts.forEach((portName) => {
1385
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1386
+ });
1387
+ }
1388
+ // Auto-set onSuccess/onFailure
1389
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
1390
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
1391
+ }
1392
+ else if (nodeType.variant === 'MAP_ITERATOR') {
1393
+ // MAP_ITERATOR: inline iteration — no user function to call
1394
+ // args: [execute, items, scopeFn]
1395
+ const executeArg = args[0];
1396
+ const itemsArg = args[1];
1397
+ const scopeFnArg = args[2];
1398
+ lines.push(`${indent} let ${resultVar}: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
1399
+ lines.push(`${indent} if (!${executeArg}) {`);
1400
+ lines.push(`${indent} ${resultVar} = { onSuccess: false, onFailure: false, results: [] };`);
1401
+ lines.push(`${indent} } else {`);
1402
+ lines.push(`${indent} const __results: unknown[] = [];`);
1403
+ lines.push(`${indent} for (const __item of ${itemsArg}) {`);
1404
+ lines.push(`${indent} __results.push((${isAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
1405
+ lines.push(`${indent} }`);
1406
+ lines.push(`${indent} ${resultVar} = { onSuccess: true, onFailure: false, results: __results };`);
1407
+ lines.push(`${indent} }`);
1408
+ // Set output ports from result
1409
+ Object.keys(nodeType.outputs).forEach((portName) => {
1410
+ const portConfig = nodeType.outputs[portName];
1411
+ if (portConfig.scope)
1412
+ return;
1413
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1414
+ });
1415
+ }
1416
+ else if (nodeType.variant === 'IMPORTED_WORKFLOW' || nodeType.variant === 'WORKFLOW') {
1417
+ // Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
1418
+ // Workflows use (execute, params) signature where params is an object
1419
+ // Regular nodes use (execute, arg1, arg2, ...) positional signature
1420
+ // For workflow calls, wrap data args in an object and include recursion depth
1421
+ const executeArg = args[0]; // First arg is always execute
1422
+ const dataArgs = args.slice(1); // Rest are data inputs
1423
+ const inputPortNames = Object.keys(nodeType.inputs).filter((p) => !isExecutePort(p));
1424
+ // Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
1425
+ // Assign to variable first to avoid TypeScript excess property checking on object literals
1426
+ const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
1427
+ paramsEntries.push('__rd__: __rd__ + 1');
1428
+ const paramsObj = `{ ${paramsEntries.join(', ')} }`;
1429
+ const paramsVar = `__${safeId}Params__`;
1430
+ lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
1431
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
1432
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
1433
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
1434
+ Object.keys(nodeType.outputs).forEach((portName) => {
1435
+ const portConfig = nodeType.outputs[portName];
1436
+ // Skip scoped OUTPUT ports - they don't exist in the function return value
1437
+ if (portConfig.scope)
1438
+ return;
1439
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1440
+ });
1441
+ }
1442
+ else if (nodeType.scope || (nodeType.scopes && nodeType.scopes.length > 0)) {
1443
+ // Scoped node call with positional arguments (uses _impl signature)
1444
+ // Scoped nodes have callback parameters that can't be passed via params object
1445
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
1446
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
1447
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
1448
+ Object.keys(nodeType.outputs).forEach((portName) => {
1449
+ const portConfig = nodeType.outputs[portName];
1450
+ // Skip scoped OUTPUT ports - they don't exist in the function return value
1451
+ if (portConfig.scope)
1452
+ return;
1453
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1454
+ });
1455
+ }
1456
+ else {
1457
+ // Regular node call - always use positional args
1458
+ // In bundle mode we import _impl which takes (execute, ...positional_args)
1459
+ lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
1460
+ // STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
1461
+ // Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
1462
+ Object.keys(nodeType.outputs).forEach((portName) => {
1463
+ const portConfig = nodeType.outputs[portName];
1464
+ // Skip scoped OUTPUT ports - they don't exist in the function return value
1465
+ if (portConfig.scope)
1466
+ return;
1467
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
1468
+ });
1469
+ }
1470
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
1471
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1472
+ lines.push(`${indent} id: '${instanceId}',`);
1473
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1474
+ lines.push(`${indent} status: 'SUCCEEDED',`);
1475
+ lines.push(`${indent} });`);
1476
+ lines.push(`${indent}} catch (error: unknown) {`);
1477
+ lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
1478
+ lines.push(`${indent} ${ctxVar}.sendStatusChangedEvent({`);
1479
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1480
+ lines.push(`${indent} id: '${instanceId}',`);
1481
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1482
+ lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
1483
+ lines.push(`${indent} });`);
1484
+ lines.push(`${indent} if (!isCancellation) {`);
1485
+ lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
1486
+ lines.push(`${indent} nodeTypeName: '${functionName}',`);
1487
+ lines.push(`${indent} id: '${instanceId}',`);
1488
+ lines.push(`${indent} executionIndex: ${safeId}Idx,`);
1489
+ lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
1490
+ lines.push(`${indent} });`);
1491
+ if (nodeType.expression) {
1492
+ // Expression node: auto-set failure flags in catch block
1493
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
1494
+ lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
1495
+ }
1496
+ lines.push(`${indent} }`);
1497
+ lines.push(`${indent} throw error;`);
1498
+ lines.push(`${indent}}`);
1499
+ if (shouldIndent) {
1500
+ const originalIndent = indent.slice(0, -2);
1501
+ lines.push(`${originalIndent}}`);
1502
+ }
1503
+ }
1504
+ //# sourceMappingURL=unified.js.map