@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,1126 @@
1
+ /**
2
+ * Inngest Deep Code Generator
3
+ *
4
+ * Generates Inngest function code with per-node `step.run()` wrapping,
5
+ * giving each node individual durability, retries, and checkpointing.
6
+ *
7
+ * This is a standalone generator alongside unified.ts — it shares the
8
+ * control-flow analysis layer but produces fundamentally different output:
9
+ * - Local `const` variables instead of `ctx.setVariable()/getVariable()`
10
+ * - `step.run()` per non-expression node for durability
11
+ * - `Promise.all()` for parallel independent nodes
12
+ * - Indexed `step.run()` for forEach/scoped iteration
13
+ * - Branching chain flattening for multi-way routing
14
+ *
15
+ * @module generator/inngest
16
+ */
17
+ import { toValidIdentifier } from './code-utils.js';
18
+ import { buildControlFlowGraph, detectBranchingChains, findAllBranchingNodes, findNodesInBranch, performKahnsTopologicalSort, isPerPortScopedChild, } from './control-flow.js';
19
+ import { RESERVED_PORT_NAMES, isStartNode, isExitNode, isExecutePort, isSuccessPort, isFailurePort, } from '../constants.js';
20
+ // ---------------------------------------------------------------------------
21
+ // Built-in Node Detection
22
+ // ---------------------------------------------------------------------------
23
+ const BUILTIN_IMPORT_PREFIX = '@synergenius/flow-weaver/built-in-nodes';
24
+ const BUILT_IN_HANDLERS = {
25
+ delay: 'delay',
26
+ waitForEvent: 'waitForEvent',
27
+ invokeWorkflow: 'invokeWorkflow',
28
+ };
29
+ /**
30
+ * Check if a node type is a built-in node.
31
+ * Returns the built-in name (e.g. 'delay') or false.
32
+ */
33
+ function isBuiltInNode(nodeType) {
34
+ // Primary: check import source
35
+ if (nodeType.importSource?.startsWith(BUILTIN_IMPORT_PREFIX)) {
36
+ return nodeType.functionName in BUILT_IN_HANDLERS ? nodeType.functionName : false;
37
+ }
38
+ // Fallback for test fixtures / same-file definitions:
39
+ // Check if function name matches AND the node has the exact built-in signature
40
+ if (nodeType.functionName in BUILT_IN_HANDLERS) {
41
+ return verifyBuiltInSignature(nodeType) ? nodeType.functionName : false;
42
+ }
43
+ return false;
44
+ }
45
+ /**
46
+ * Verify a node type has the exact built-in signature by checking its input port names.
47
+ */
48
+ function verifyBuiltInSignature(nodeType) {
49
+ const inputNames = Object.keys(nodeType.inputs).filter((n) => n !== 'execute');
50
+ switch (nodeType.functionName) {
51
+ case 'delay':
52
+ return inputNames.length === 1 && inputNames[0] === 'duration';
53
+ case 'waitForEvent':
54
+ return inputNames.includes('eventName');
55
+ case 'invokeWorkflow':
56
+ return inputNames.includes('functionId') && inputNames.includes('payload');
57
+ default:
58
+ return false;
59
+ }
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Typed Event Schema (Feature 1)
63
+ // ---------------------------------------------------------------------------
64
+ /**
65
+ * Generate Zod event schema from workflow start ports (populated by @param).
66
+ */
67
+ function generateEventSchema(workflow, eventName) {
68
+ const lines = [];
69
+ const schemaFields = [];
70
+ // workflow.startPorts maps @param annotations
71
+ for (const [name, port] of Object.entries(workflow.startPorts || {})) {
72
+ if (name === 'execute')
73
+ continue; // Skip execute port
74
+ const zodType = mapTypeToZod(port.tsType || port.dataType);
75
+ schemaFields.push(` ${name}: ${zodType},`);
76
+ }
77
+ if (schemaFields.length === 0)
78
+ return [];
79
+ const varName = toValidIdentifier(workflow.functionName) + 'Event';
80
+ lines.push(`const ${varName} = {`);
81
+ lines.push(` name: '${eventName}',`);
82
+ lines.push(` schema: z.object({`);
83
+ lines.push(` data: z.object({`);
84
+ lines.push(...schemaFields);
85
+ lines.push(` }),`);
86
+ lines.push(` }),`);
87
+ lines.push(`};`);
88
+ lines.push('');
89
+ return lines;
90
+ }
91
+ /**
92
+ * Map TypeScript/Flow Weaver types to Zod schema types.
93
+ */
94
+ function mapTypeToZod(type) {
95
+ if (!type)
96
+ return 'z.unknown()';
97
+ const t = type.trim().toLowerCase();
98
+ if (t === 'string')
99
+ return 'z.string()';
100
+ if (t === 'number')
101
+ return 'z.number()';
102
+ if (t === 'boolean')
103
+ return 'z.boolean()';
104
+ if (t === 'string[]')
105
+ return 'z.array(z.string())';
106
+ if (t === 'number[]')
107
+ return 'z.array(z.number())';
108
+ if (t === 'object' || t.startsWith('record<'))
109
+ return 'z.record(z.unknown())';
110
+ return `z.unknown() /* ${type} */`;
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Helpers
114
+ // ---------------------------------------------------------------------------
115
+ /** Convert camelCase/PascalCase to kebab-case for Inngest IDs */
116
+ function toKebabCase(name) {
117
+ return name
118
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
119
+ .replace(/[^a-zA-Z0-9-]/g, '-')
120
+ .replace(/-+/g, '-')
121
+ .toLowerCase();
122
+ }
123
+ /**
124
+ * Resolve a port's value source for a given node instance.
125
+ *
126
+ * Unlike `buildNodeArgumentsWithContext` in code-utils.ts which emits
127
+ * `ctx.getVariable()` calls, this returns a plain JS expression referencing
128
+ * local `const` variables produced by earlier `step.run()` calls.
129
+ */
130
+ function resolvePortValue(portName, instanceId, nodeType, workflow, _nodeTypes) {
131
+ const safeId = toValidIdentifier(instanceId);
132
+ const portDef = nodeType.inputs[portName];
133
+ const instance = workflow.instances.find((i) => i.id === instanceId);
134
+ // Check for instance-level expression override
135
+ const instancePortConfig = instance?.config?.portConfigs?.find((pc) => pc.portName === portName && (pc.direction == null || pc.direction === 'INPUT'));
136
+ if (instancePortConfig?.expression !== undefined) {
137
+ const expr = String(instancePortConfig.expression);
138
+ const isFunction = expr.includes('=>') || expr.trim().startsWith('function');
139
+ if (isFunction) {
140
+ return `await (${expr})()`;
141
+ }
142
+ return expr;
143
+ }
144
+ // Check for connections
145
+ const connections = workflow.connections.filter((conn) => conn.to.node === instanceId && conn.to.port === portName
146
+ && !conn.from.scope && !conn.to.scope);
147
+ if (connections.length > 0) {
148
+ if (connections.length === 1) {
149
+ const conn = connections[0];
150
+ const sourceNode = conn.from.node;
151
+ const sourcePort = conn.from.port;
152
+ if (isStartNode(sourceNode)) {
153
+ return `event.data.${sourcePort}`;
154
+ }
155
+ const safeSource = toValidIdentifier(sourceNode);
156
+ return `${safeSource}_result.${sourcePort}`;
157
+ }
158
+ // Multiple connections — use first non-undefined (fan-in)
159
+ const attempts = connections.map((conn) => {
160
+ const sourceNode = conn.from.node;
161
+ const sourcePort = conn.from.port;
162
+ if (isStartNode(sourceNode)) {
163
+ return `event.data.${sourcePort}`;
164
+ }
165
+ const safeSource = toValidIdentifier(sourceNode);
166
+ return `${safeSource}_result?.${sourcePort}`;
167
+ });
168
+ return attempts.join(' ?? ');
169
+ }
170
+ // Check for node type expression
171
+ if (portDef?.expression) {
172
+ const expr = portDef.expression;
173
+ const isFunction = expr.includes('=>') || expr.trim().startsWith('function');
174
+ if (isFunction) {
175
+ return `await (${expr})()`;
176
+ }
177
+ return expr;
178
+ }
179
+ // Default value
180
+ if (portDef?.default !== undefined) {
181
+ return JSON.stringify(portDef.default);
182
+ }
183
+ // Optional port
184
+ if (portDef?.optional) {
185
+ return 'undefined';
186
+ }
187
+ // No source — undefined with comment
188
+ return `undefined /* no source for ${safeId}.${portName} */`;
189
+ }
190
+ /**
191
+ * Build the argument list for calling a node function.
192
+ * Returns array of JS expression strings to pass as function arguments.
193
+ */
194
+ function buildNodeArgs(instanceId, nodeType, workflow, nodeTypes) {
195
+ const args = [];
196
+ // Handle execute port (first arg for non-expression nodes)
197
+ if (!nodeType.expression) {
198
+ const executeConns = workflow.connections.filter((conn) => conn.to.node === instanceId && conn.to.port === 'execute'
199
+ && !conn.from.scope && !conn.to.scope);
200
+ if (executeConns.length > 0) {
201
+ const conn = executeConns[0];
202
+ if (isStartNode(conn.from.node)) {
203
+ args.push('true');
204
+ }
205
+ else {
206
+ // Delay nodes (step.sleep) have no result variable — use literal values
207
+ const sourceNt = getNodeType(conn.from.node, workflow, nodeTypes);
208
+ if (sourceNt && isBuiltInNode(sourceNt) === 'delay') {
209
+ args.push(conn.from.port === 'onSuccess' ? 'true' : 'false');
210
+ }
211
+ else {
212
+ const safeSource = toValidIdentifier(conn.from.node);
213
+ args.push(`${safeSource}_result.${conn.from.port}`);
214
+ }
215
+ }
216
+ }
217
+ else {
218
+ args.push('true');
219
+ }
220
+ }
221
+ // Handle data ports
222
+ for (const portName of Object.keys(nodeType.inputs)) {
223
+ if (isExecutePort(portName))
224
+ continue;
225
+ if (nodeType.inputs[portName].scope)
226
+ continue; // Skip scoped ports
227
+ const value = resolvePortValue(portName, instanceId, nodeType, workflow, nodeTypes);
228
+ args.push(value);
229
+ }
230
+ return args;
231
+ }
232
+ /**
233
+ * Look up a node type for an instance.
234
+ */
235
+ function getNodeType(instanceId, workflow, nodeTypes) {
236
+ const instance = workflow.instances.find((i) => i.id === instanceId);
237
+ if (!instance)
238
+ return undefined;
239
+ return nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
240
+ }
241
+ // ---------------------------------------------------------------------------
242
+ // Parallel Detection
243
+ // ---------------------------------------------------------------------------
244
+ /**
245
+ * Detect parallelizable groups within a list of node IDs.
246
+ *
247
+ * For each node, compute its direct predecessors within the given set.
248
+ * Nodes with identical predecessor sets can execute in parallel.
249
+ *
250
+ * Returns an ordered list of groups preserving topological order.
251
+ */
252
+ function detectParallelInList(nodeIds, workflow, nodeTypes) {
253
+ if (nodeIds.length <= 1)
254
+ return nodeIds.map((n) => [n]);
255
+ // Build mini-CFG: for each node, which other nodes in the list feed into it?
256
+ const predecessors = new Map();
257
+ const nodeSet = new Set(nodeIds);
258
+ for (const nodeId of nodeIds) {
259
+ const preds = new Set();
260
+ for (const conn of workflow.connections) {
261
+ if (conn.to.node !== nodeId)
262
+ continue;
263
+ if (conn.from.scope || conn.to.scope)
264
+ continue;
265
+ const fromNode = conn.from.node;
266
+ if (nodeSet.has(fromNode)) {
267
+ preds.add(fromNode);
268
+ }
269
+ }
270
+ predecessors.set(nodeId, preds);
271
+ }
272
+ // Group by predecessor set (serialized for comparison)
273
+ const groups = [];
274
+ const processed = new Set();
275
+ for (const nodeId of nodeIds) {
276
+ if (processed.has(nodeId))
277
+ continue;
278
+ const preds = predecessors.get(nodeId);
279
+ const predsKey = Array.from(preds).sort().join(',');
280
+ const parallel = [nodeId];
281
+ for (const other of nodeIds) {
282
+ if (other === nodeId || processed.has(other))
283
+ continue;
284
+ const otherPreds = predecessors.get(other);
285
+ const otherKey = Array.from(otherPreds).sort().join(',');
286
+ if (predsKey === otherKey) {
287
+ parallel.push(other);
288
+ }
289
+ }
290
+ for (const n of parallel) {
291
+ processed.add(n);
292
+ }
293
+ groups.push(parallel);
294
+ }
295
+ return groups;
296
+ }
297
+ // ---------------------------------------------------------------------------
298
+ // Code Generation — Core Emitters
299
+ // ---------------------------------------------------------------------------
300
+ /**
301
+ * Generate a step.run() call for a durable node.
302
+ */
303
+ function generateStepRunCall(instanceId, nodeType, workflow, nodeTypes, indent) {
304
+ const args = buildNodeArgs(instanceId, nodeType, workflow, nodeTypes);
305
+ const safeId = toValidIdentifier(instanceId);
306
+ const fnCall = `${nodeType.functionName}(${args.join(', ')})`;
307
+ const awaitPrefix = nodeType.isAsync ? 'await ' : '';
308
+ return `${indent}${safeId}_result = await step.run('${instanceId}', async () => {\n` +
309
+ `${indent} return ${awaitPrefix}${fnCall};\n` +
310
+ `${indent}});`;
311
+ }
312
+ /**
313
+ * Generate an inline call for an expression node (no step.run wrapper).
314
+ */
315
+ function generateExpressionCall(instanceId, nodeType, workflow, nodeTypes, indent) {
316
+ const args = buildNodeArgs(instanceId, nodeType, workflow, nodeTypes);
317
+ const safeId = toValidIdentifier(instanceId);
318
+ const fnCall = `${nodeType.functionName}(${args.join(', ')})`;
319
+ const awaitPrefix = nodeType.isAsync ? 'await ' : '';
320
+ return `${indent}${safeId}_result = ${awaitPrefix}${fnCall};`;
321
+ }
322
+ /**
323
+ * Emit the step.run or expression call for a single node.
324
+ * Built-in nodes (delay, waitForEvent, invokeWorkflow) are emitted with their
325
+ * corresponding Inngest step primitives instead of step.run().
326
+ */
327
+ function emitNodeCall(nodeId, nodeType, workflow, nodeTypes, indent, lines) {
328
+ const builtIn = isBuiltInNode(nodeType);
329
+ if (builtIn === 'delay') {
330
+ const args = buildNodeArgs(nodeId, nodeType, workflow, nodeTypes);
331
+ const durationArg = args[1]; // args[0] is execute=true, args[1] is duration
332
+ lines.push(`${indent}await step.sleep('${nodeId}', ${durationArg});`);
333
+ lines.push('');
334
+ return;
335
+ }
336
+ if (builtIn === 'waitForEvent') {
337
+ const safeId = toValidIdentifier(nodeId);
338
+ const args = buildNodeArgs(nodeId, nodeType, workflow, nodeTypes);
339
+ const eventNameArg = args[1]; // execute=args[0], eventName=args[1]
340
+ const matchArg = args[2]; // optional
341
+ const timeoutArg = args[3]; // optional
342
+ lines.push(`${indent}const ${safeId}_raw = await step.waitForEvent('${nodeId}', {`);
343
+ lines.push(`${indent} event: ${eventNameArg},`);
344
+ if (matchArg && matchArg !== 'undefined') {
345
+ lines.push(`${indent} match: ${matchArg},`);
346
+ }
347
+ if (timeoutArg && timeoutArg !== 'undefined') {
348
+ lines.push(`${indent} timeout: ${timeoutArg},`);
349
+ }
350
+ lines.push(`${indent}});`);
351
+ lines.push(`${indent}${safeId}_result = ${safeId}_raw`);
352
+ lines.push(`${indent} ? { onSuccess: true, onFailure: false, eventData: ${safeId}_raw.data }`);
353
+ lines.push(`${indent} : { onSuccess: false, onFailure: true, eventData: {} };`);
354
+ lines.push('');
355
+ return;
356
+ }
357
+ if (builtIn === 'invokeWorkflow') {
358
+ const safeId = toValidIdentifier(nodeId);
359
+ const args = buildNodeArgs(nodeId, nodeType, workflow, nodeTypes);
360
+ const functionIdArg = args[1];
361
+ const payloadArg = args[2];
362
+ const timeoutArg = args[3];
363
+ lines.push(`${indent}try {`);
364
+ lines.push(`${indent} ${safeId}_result = await step.invoke('${nodeId}', {`);
365
+ lines.push(`${indent} function: ${functionIdArg},`);
366
+ lines.push(`${indent} data: ${payloadArg},`);
367
+ if (timeoutArg && timeoutArg !== 'undefined') {
368
+ lines.push(`${indent} timeout: ${timeoutArg},`);
369
+ }
370
+ lines.push(`${indent} });`);
371
+ lines.push(`${indent} ${safeId}_result = { onSuccess: true, onFailure: false, result: ${safeId}_result };`);
372
+ lines.push(`${indent}} catch (err) {`);
373
+ lines.push(`${indent} ${safeId}_result = { onSuccess: false, onFailure: true, result: {} };`);
374
+ lines.push(`${indent}}`);
375
+ lines.push('');
376
+ return;
377
+ }
378
+ if (nodeType.expression) {
379
+ lines.push(generateExpressionCall(nodeId, nodeType, workflow, nodeTypes, indent));
380
+ }
381
+ else {
382
+ lines.push(generateStepRunCall(nodeId, nodeType, workflow, nodeTypes, indent));
383
+ }
384
+ lines.push('');
385
+ }
386
+ /**
387
+ * Ensure all expression node dependencies for a given node are emitted before it.
388
+ * Expression nodes are pure functions that can be safely emitted inline wherever needed.
389
+ */
390
+ function ensureExpressionDependencies(nodeId, workflow, nodeTypes, indent, lines, generatedNodes) {
391
+ for (const conn of workflow.connections) {
392
+ if (conn.to.node !== nodeId)
393
+ continue;
394
+ if (conn.from.scope || conn.to.scope)
395
+ continue;
396
+ const fromNode = conn.from.node;
397
+ if (isStartNode(fromNode) || isExitNode(fromNode))
398
+ continue;
399
+ if (generatedNodes.has(fromNode))
400
+ continue;
401
+ const nt = getNodeType(fromNode, workflow, nodeTypes);
402
+ if (!nt || !nt.expression)
403
+ continue;
404
+ // Recursively ensure this expression's own dependencies first
405
+ ensureExpressionDependencies(fromNode, workflow, nodeTypes, indent, lines, generatedNodes);
406
+ if (generatedNodes.has(fromNode))
407
+ continue;
408
+ // Emit the expression node inline
409
+ generatedNodes.add(fromNode);
410
+ lines.push(generateExpressionCall(fromNode, nt, workflow, nodeTypes, indent));
411
+ lines.push('');
412
+ }
413
+ }
414
+ /**
415
+ * Emit the if/else branching body for a branching node (without the step.run call).
416
+ * Used after Promise.all to emit branching bodies separately from the step execution.
417
+ */
418
+ function emitBranchingBody(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes) {
419
+ const safeId = toValidIdentifier(nodeId);
420
+ const region = branchRegions.get(nodeId);
421
+ if (!region)
422
+ return;
423
+ const hasSuccessBranch = region.successNodes.size > 0;
424
+ const hasFailureBranch = region.failureNodes.size > 0;
425
+ if (!hasSuccessBranch && !hasFailureBranch)
426
+ return;
427
+ // Delay nodes (step.sleep) always succeed — emit success branch directly, no if/else
428
+ const branchNodeType = getNodeType(nodeId, workflow, nodeTypes);
429
+ if (branchNodeType && isBuiltInNode(branchNodeType) === 'delay') {
430
+ if (hasSuccessBranch) {
431
+ const successOrder = getOrderedNodes(Array.from(region.successNodes), workflow, nodeTypes);
432
+ generateNodeBlock(successOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
433
+ }
434
+ return;
435
+ }
436
+ lines.push(`${indent}if (${safeId}_result.onSuccess) {`);
437
+ if (hasSuccessBranch) {
438
+ const successOrder = getOrderedNodes(Array.from(region.successNodes), workflow, nodeTypes);
439
+ generateNodeBlock(successOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
440
+ }
441
+ if (hasFailureBranch) {
442
+ lines.push(`${indent}} else {`);
443
+ const failureOrder = getOrderedNodes(Array.from(region.failureNodes), workflow, nodeTypes);
444
+ generateNodeBlock(failureOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
445
+ lines.push(`${indent}}`);
446
+ }
447
+ else {
448
+ lines.push(`${indent}}`);
449
+ }
450
+ }
451
+ /**
452
+ * Emit a Promise.all block for 2+ parallel nodes.
453
+ * Delay nodes are excluded and emitted separately (step.sleep returns void).
454
+ * waitForEvent/invokeWorkflow get their Inngest step primitives instead of step.run.
455
+ */
456
+ function emitPromiseAll(nodeIds, workflow, nodeTypes, indent, lines, generatedNodes) {
457
+ // Separate delay nodes from the group (step.sleep returns void, breaks destructuring)
458
+ const delayNodes = [];
459
+ const nonDelayNodes = [];
460
+ for (const nodeId of nodeIds) {
461
+ const nt = getNodeType(nodeId, workflow, nodeTypes);
462
+ if (nt && isBuiltInNode(nt) === 'delay') {
463
+ delayNodes.push(nodeId);
464
+ }
465
+ else {
466
+ nonDelayNodes.push(nodeId);
467
+ }
468
+ }
469
+ // Emit delay nodes sequentially first
470
+ for (const delayId of delayNodes) {
471
+ generatedNodes.add(delayId);
472
+ const nt = getNodeType(delayId, workflow, nodeTypes);
473
+ emitNodeCall(delayId, nt, workflow, nodeTypes, indent, lines);
474
+ }
475
+ // If only 1 non-delay node remains, emit it directly
476
+ if (nonDelayNodes.length === 1) {
477
+ const nodeId = nonDelayNodes[0];
478
+ generatedNodes.add(nodeId);
479
+ const nt = getNodeType(nodeId, workflow, nodeTypes);
480
+ emitNodeCall(nodeId, nt, workflow, nodeTypes, indent, lines);
481
+ return;
482
+ }
483
+ if (nonDelayNodes.length === 0)
484
+ return;
485
+ const destructured = [];
486
+ const stepCalls = [];
487
+ for (const nodeId of nonDelayNodes) {
488
+ generatedNodes.add(nodeId);
489
+ const nt = getNodeType(nodeId, workflow, nodeTypes);
490
+ const safeId = toValidIdentifier(nodeId);
491
+ const builtIn = isBuiltInNode(nt);
492
+ if (builtIn === 'waitForEvent') {
493
+ const args = buildNodeArgs(nodeId, nt, workflow, nodeTypes);
494
+ const eventNameArg = args[1];
495
+ const matchArg = args[2];
496
+ const timeoutArg = args[3];
497
+ let waitCall = `${indent} step.waitForEvent('${nodeId}', { event: ${eventNameArg}`;
498
+ if (matchArg && matchArg !== 'undefined')
499
+ waitCall += `, match: ${matchArg}`;
500
+ if (timeoutArg && timeoutArg !== 'undefined')
501
+ waitCall += `, timeout: ${timeoutArg}`;
502
+ waitCall += ` })`;
503
+ stepCalls.push(waitCall);
504
+ }
505
+ else if (builtIn === 'invokeWorkflow') {
506
+ const args = buildNodeArgs(nodeId, nt, workflow, nodeTypes);
507
+ const functionIdArg = args[1];
508
+ const payloadArg = args[2];
509
+ const timeoutArg = args[3];
510
+ let invokeCall = `${indent} step.invoke('${nodeId}', { function: ${functionIdArg}, data: ${payloadArg}`;
511
+ if (timeoutArg && timeoutArg !== 'undefined')
512
+ invokeCall += `, timeout: ${timeoutArg}`;
513
+ invokeCall += ` })`;
514
+ stepCalls.push(invokeCall);
515
+ }
516
+ else if (nt.expression) {
517
+ const args = buildNodeArgs(nodeId, nt, workflow, nodeTypes);
518
+ const fnCall = `${nt.functionName}(${args.join(', ')})`;
519
+ stepCalls.push(`${indent} Promise.resolve(${fnCall})`);
520
+ }
521
+ else {
522
+ const args = buildNodeArgs(nodeId, nt, workflow, nodeTypes);
523
+ const fnCall = `${nt.functionName}(${args.join(', ')})`;
524
+ const awaitPrefix = nt.isAsync ? 'await ' : '';
525
+ stepCalls.push(`${indent} step.run('${nodeId}', async () => ${awaitPrefix}${fnCall})`);
526
+ }
527
+ destructured.push(`${safeId}_result`);
528
+ }
529
+ lines.push(`${indent}[${destructured.join(', ')}] = await Promise.all([`);
530
+ lines.push(stepCalls.join(',\n'));
531
+ lines.push(`${indent}]);`);
532
+ lines.push('');
533
+ }
534
+ // ---------------------------------------------------------------------------
535
+ // Code Generation — forEach / Scoped Iteration
536
+ // ---------------------------------------------------------------------------
537
+ /**
538
+ * Generate code for forEach/scoped iteration patterns.
539
+ *
540
+ * Per-port scoped children execute inside a loop with indexed step names
541
+ * for per-item durability: step.run(`processItem-${i}`, ...).
542
+ */
543
+ function generateForEachScope(parentId, parentNodeType, scopeName, workflow, nodeTypes, indent, lines) {
544
+ const safeParent = toValidIdentifier(parentId);
545
+ // Find child instances in this scope
546
+ const childInstances = workflow.instances.filter((inst) => {
547
+ if (!inst.parent)
548
+ return false;
549
+ return inst.parent.id === parentId && inst.parent.scope === scopeName;
550
+ });
551
+ if (childInstances.length === 0)
552
+ return;
553
+ // Find the output port that provides items to iterate
554
+ // Look for scoped output ports (these are parameters TO children)
555
+ const scopedOutputPorts = Object.entries(parentNodeType.outputs).filter(([_name, portDef]) => portDef.scope === scopeName);
556
+ // The 'item' port (or similar) carries the current iteration value
557
+ const itemPort = scopedOutputPorts.find(([name]) => name !== 'start' && name !== 'success' && name !== 'failure');
558
+ if (!itemPort) {
559
+ // No item port — just a simple callback scope, not forEach
560
+ lines.push(`${indent}// Scope '${scopeName}' for ${parentId} (callback pattern)`);
561
+ for (const child of childInstances) {
562
+ const childNt = getNodeType(child.id, workflow, nodeTypes);
563
+ if (!childNt)
564
+ continue;
565
+ lines.push(`${indent}const ${toValidIdentifier(child.id)}_result = await step.run('${child.id}', async () => {`);
566
+ const args = buildNodeArgs(child.id, childNt, workflow, nodeTypes);
567
+ const fnCall = `${childNt.functionName}(${args.join(', ')})`;
568
+ const awaitPrefix = childNt.isAsync ? 'await ' : '';
569
+ lines.push(`${indent} return ${awaitPrefix}${fnCall};`);
570
+ lines.push(`${indent}});`);
571
+ lines.push('');
572
+ }
573
+ return;
574
+ }
575
+ // Find the source that provides the array to iterate
576
+ // The parent's result should have the items
577
+ const [itemPortName] = itemPort;
578
+ const arraySource = `${safeParent}_result.${itemPortName}`;
579
+ // Generate the loop with indexed step names
580
+ lines.push(`${indent}const ${safeParent}_${scopeName}_results = [];`);
581
+ lines.push(`${indent}for (let __i__ = 0; __i__ < ${arraySource}.length; __i__++) {`);
582
+ lines.push(`${indent} const __item__ = ${arraySource}[__i__];`);
583
+ for (const child of childInstances) {
584
+ const childNt = getNodeType(child.id, workflow, nodeTypes);
585
+ if (!childNt)
586
+ continue;
587
+ const safeChild = toValidIdentifier(child.id);
588
+ // Build args, replacing the scoped connection with __item__
589
+ const args = [];
590
+ if (!childNt.expression) {
591
+ args.push('true'); // execute = true
592
+ }
593
+ for (const portName of Object.keys(childNt.inputs)) {
594
+ if (isExecutePort(portName))
595
+ continue;
596
+ if (childNt.inputs[portName].scope)
597
+ continue;
598
+ // Check if this port connects from the scope's item port
599
+ const scopedConn = workflow.connections.find((conn) => conn.to.node === child.id && conn.to.port === portName
600
+ && conn.from.node === parentId && conn.from.port === itemPortName);
601
+ if (scopedConn) {
602
+ args.push('__item__');
603
+ }
604
+ else {
605
+ args.push(resolvePortValue(portName, child.id, childNt, workflow, nodeTypes));
606
+ }
607
+ }
608
+ const fnCall = `${childNt.functionName}(${args.join(', ')})`;
609
+ const awaitPrefix = childNt.isAsync ? 'await ' : '';
610
+ if (childNt.expression) {
611
+ lines.push(`${indent} const ${safeChild}_result = ${awaitPrefix}${fnCall};`);
612
+ }
613
+ else {
614
+ lines.push(`${indent} const ${safeChild}_result = await step.run(\`${child.id}-\${__i__}\`, async () => {`);
615
+ lines.push(`${indent} return ${awaitPrefix}${fnCall};`);
616
+ lines.push(`${indent} });`);
617
+ }
618
+ lines.push(`${indent} ${safeParent}_${scopeName}_results.push(${safeChild}_result);`);
619
+ }
620
+ lines.push(`${indent}}`);
621
+ lines.push('');
622
+ }
623
+ // ---------------------------------------------------------------------------
624
+ // Code Generation — Block Generation with Parallelism & Branching
625
+ // ---------------------------------------------------------------------------
626
+ /**
627
+ * Generate code for a set of nodes, handling branching, parallelism, and chains.
628
+ *
629
+ * This is the recursive core called for top-level nodes and branch bodies.
630
+ */
631
+ function generateNodeBlock(nodeIds, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes) {
632
+ // Filter to ungenerated real nodes
633
+ const remaining = nodeIds.filter((n) => !generatedNodes.has(n) && !isStartNode(n) && !isExitNode(n));
634
+ if (remaining.length === 0)
635
+ return;
636
+ // Detect parallelism within this block
637
+ const groups = detectParallelInList(remaining, workflow, nodeTypes);
638
+ for (const group of groups) {
639
+ const eligible = group.filter((n) => !generatedNodes.has(n));
640
+ if (eligible.length === 0)
641
+ continue;
642
+ if (eligible.length >= 2) {
643
+ // Check if all are expression nodes (no need for Promise.all)
644
+ const allExpr = eligible.every((n) => {
645
+ const nt = getNodeType(n, workflow, nodeTypes);
646
+ return nt?.expression;
647
+ });
648
+ if (allExpr) {
649
+ for (const nodeId of eligible) {
650
+ emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
651
+ }
652
+ }
653
+ else {
654
+ // Separate expression nodes, chain heads, and parallelizable step.run nodes
655
+ const exprNodes = eligible.filter((n) => {
656
+ const nt = getNodeType(n, workflow, nodeTypes);
657
+ return nt?.expression;
658
+ });
659
+ const chainHeadNodes = eligible.filter((n) => {
660
+ const nt = getNodeType(n, workflow, nodeTypes);
661
+ return nt && !nt.expression && branchingChains.has(n);
662
+ });
663
+ const parallelStepNodes = eligible.filter((n) => {
664
+ const nt = getNodeType(n, workflow, nodeTypes);
665
+ return nt && !nt.expression && !branchingChains.has(n);
666
+ });
667
+ // Emit expression nodes individually first (may be data dependencies)
668
+ for (const nodeId of exprNodes) {
669
+ emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
670
+ }
671
+ // Ensure expression dependencies for all parallel step nodes
672
+ for (const nodeId of parallelStepNodes) {
673
+ ensureExpressionDependencies(nodeId, workflow, nodeTypes, indent, lines, generatedNodes);
674
+ }
675
+ // Emit step.run nodes via Promise.all (includes branching nodes)
676
+ if (parallelStepNodes.length >= 2) {
677
+ emitPromiseAll(parallelStepNodes, workflow, nodeTypes, indent, lines, generatedNodes);
678
+ // After Promise.all, emit branching bodies for any branching nodes
679
+ for (const nodeId of parallelStepNodes) {
680
+ if (branchingNodes.has(nodeId) && branchRegions.has(nodeId)) {
681
+ emitBranchingBody(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
682
+ }
683
+ }
684
+ }
685
+ else {
686
+ for (const nodeId of parallelStepNodes) {
687
+ emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
688
+ }
689
+ }
690
+ // Emit chain head nodes sequentially (they manage their own chain)
691
+ for (const nodeId of chainHeadNodes) {
692
+ emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
693
+ }
694
+ }
695
+ }
696
+ else {
697
+ // Single node — standard emission
698
+ for (const nodeId of eligible) {
699
+ emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
700
+ }
701
+ }
702
+ }
703
+ }
704
+ /**
705
+ * Emit code for a single node — dispatches to the right emitter.
706
+ */
707
+ function emitSingleNode(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes) {
708
+ if (generatedNodes.has(nodeId))
709
+ return;
710
+ const nodeType = getNodeType(nodeId, workflow, nodeTypes);
711
+ if (!nodeType)
712
+ return;
713
+ // Skip chain members — they'll be emitted by their chain head
714
+ if (chainMembers.has(nodeId))
715
+ return;
716
+ // Ensure expression node dependencies are emitted first
717
+ ensureExpressionDependencies(nodeId, workflow, nodeTypes, indent, lines, generatedNodes);
718
+ generatedNodes.add(nodeId);
719
+ // Check if this is the head of a branching chain
720
+ const chain = branchingChains.get(nodeId);
721
+ if (chain && branchingNodes.has(nodeId)) {
722
+ generateBranchingChain(chain, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
723
+ return;
724
+ }
725
+ // Check for forEach/scoped children
726
+ const hasPerPortScopedChildren = workflow.instances.some((inst) => inst.parent && inst.parent.id === nodeId
727
+ && isPerPortScopedChild(inst, workflow, nodeTypes));
728
+ if (branchingNodes.has(nodeId) && branchRegions.has(nodeId)) {
729
+ generateBranchingNode(nodeId, nodeType, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
730
+ }
731
+ else {
732
+ emitNodeCall(nodeId, nodeType, workflow, nodeTypes, indent, lines);
733
+ }
734
+ // Emit scoped children (forEach) after the parent node
735
+ if (hasPerPortScopedChildren) {
736
+ const scopeNames = new Set();
737
+ if (nodeType.scope)
738
+ scopeNames.add(nodeType.scope);
739
+ if (nodeType.scopes) {
740
+ for (const s of nodeType.scopes)
741
+ scopeNames.add(s);
742
+ }
743
+ // Also collect from port definitions
744
+ for (const portDef of Object.values(nodeType.outputs)) {
745
+ if (portDef.scope)
746
+ scopeNames.add(portDef.scope);
747
+ }
748
+ for (const portDef of Object.values(nodeType.inputs)) {
749
+ if (portDef.scope)
750
+ scopeNames.add(portDef.scope);
751
+ }
752
+ scopeNames.forEach((scopeName) => {
753
+ generateForEachScope(nodeId, nodeType, scopeName, workflow, nodeTypes, indent, lines);
754
+ });
755
+ }
756
+ }
757
+ /**
758
+ * Generate code for a branching node (has onSuccess/onFailure connections).
759
+ */
760
+ function generateBranchingNode(nodeId, nodeType, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes) {
761
+ // Generate the step.run / expression call for this node
762
+ emitNodeCall(nodeId, nodeType, workflow, nodeTypes, indent, lines);
763
+ // Emit the if/else branching body
764
+ emitBranchingBody(nodeId, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
765
+ }
766
+ /**
767
+ * Generate a chain of branching nodes as flat if/else if/else.
768
+ *
769
+ * Chains are sequential branching nodes where one direction has exactly one
770
+ * branching child and the other has zero. Flattening reduces nesting depth.
771
+ *
772
+ * Generated structure:
773
+ * step.run(A)
774
+ * if (!A.onSuccess) { ...A failure body... }
775
+ * step.run(B) // B is in A's success path
776
+ * else if (!B.onSuccess) { ...B failure body... }
777
+ * else { ...last node's success body... }
778
+ */
779
+ function generateBranchingChain(chain, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes) {
780
+ for (let i = 0; i < chain.length; i++) {
781
+ const nodeId = chain[i];
782
+ const safeId = toValidIdentifier(nodeId);
783
+ const nodeType = getNodeType(nodeId, workflow, nodeTypes);
784
+ // Ensure expression dependencies before emitting this chain node
785
+ ensureExpressionDependencies(nodeId, workflow, nodeTypes, indent, lines, generatedNodes);
786
+ generatedNodes.add(nodeId);
787
+ // Emit step.run / expression call for this chain node
788
+ if (nodeType) {
789
+ emitNodeCall(nodeId, nodeType, workflow, nodeTypes, indent, lines);
790
+ }
791
+ const region = branchRegions.get(nodeId);
792
+ if (!region)
793
+ continue;
794
+ const hasSuccessBranch = region.successNodes.size > 0;
795
+ const hasFailureBranch = region.failureNodes.size > 0;
796
+ if (!hasSuccessBranch && !hasFailureBranch)
797
+ continue;
798
+ const nextInChain = i + 1 < chain.length ? chain[i + 1] : null;
799
+ const isLast = !nextInChain;
800
+ const chainViaSuccess = nextInChain && region.successNodes.has(nextInChain);
801
+ const chainViaFailure = nextInChain && region.failureNodes.has(nextInChain);
802
+ if (isLast) {
803
+ // Delay nodes (step.sleep) always succeed — emit success branch directly, no if/else
804
+ if (nodeType && isBuiltInNode(nodeType) === 'delay') {
805
+ if (hasSuccessBranch) {
806
+ const successOrder = getOrderedNodes(Array.from(region.successNodes), workflow, nodeTypes);
807
+ generateNodeBlock(successOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
808
+ }
809
+ }
810
+ else if (hasSuccessBranch || hasFailureBranch) {
811
+ // Last node in chain — emit standard branching for both sides
812
+ lines.push(`${indent}if (${safeId}_result.onSuccess) {`);
813
+ if (hasSuccessBranch) {
814
+ const successOrder = getOrderedNodes(Array.from(region.successNodes), workflow, nodeTypes);
815
+ generateNodeBlock(successOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
816
+ }
817
+ if (hasFailureBranch) {
818
+ lines.push(`${indent}} else {`);
819
+ const failureOrder = getOrderedNodes(Array.from(region.failureNodes), workflow, nodeTypes);
820
+ generateNodeBlock(failureOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
821
+ }
822
+ lines.push(`${indent}}`);
823
+ }
824
+ }
825
+ else if (chainViaSuccess) {
826
+ // Chain continues through success path; emit failure branch if it exists
827
+ if (hasFailureBranch) {
828
+ lines.push(`${indent}if (!${safeId}_result.onSuccess) {`);
829
+ const failureOrder = getOrderedNodes(Array.from(region.failureNodes), workflow, nodeTypes);
830
+ generateNodeBlock(failureOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
831
+ lines.push(`${indent}}`);
832
+ }
833
+ // Built-in nodes (delay, waitForEvent, invokeWorkflow) bypass the execute flag,
834
+ // so the chain continuation must be explicitly guarded.
835
+ const nextNt = getNodeType(nextInChain, workflow, nodeTypes);
836
+ if (nextNt && isBuiltInNode(nextNt)) {
837
+ lines.push(`${indent}if (${safeId}_result.onSuccess) {`);
838
+ const remainingChain = chain.slice(i + 1);
839
+ generateBranchingChain(remainingChain, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
840
+ lines.push(`${indent}}`);
841
+ return; // Remaining chain already generated inside guard
842
+ }
843
+ // For normal nodes, execute flag handles it — continue flat
844
+ }
845
+ else if (chainViaFailure) {
846
+ // Chain continues through failure path; emit success branch if it exists
847
+ if (hasSuccessBranch) {
848
+ lines.push(`${indent}if (${safeId}_result.onSuccess) {`);
849
+ const successOrder = getOrderedNodes(Array.from(region.successNodes), workflow, nodeTypes);
850
+ generateNodeBlock(successOrder, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
851
+ lines.push(`${indent}}`);
852
+ }
853
+ // Built-in nodes bypass the execute flag — guard the chain continuation
854
+ const nextNtF = getNodeType(nextInChain, workflow, nodeTypes);
855
+ if (nextNtF && isBuiltInNode(nextNtF)) {
856
+ lines.push(`${indent}if (!${safeId}_result.onSuccess) {`);
857
+ const remainingChain = chain.slice(i + 1);
858
+ generateBranchingChain(remainingChain, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent + ' ', lines, generatedNodes);
859
+ lines.push(`${indent}}`);
860
+ return; // Remaining chain already generated inside guard
861
+ }
862
+ // For normal nodes, execute flag handles it — continue flat
863
+ }
864
+ }
865
+ }
866
+ /**
867
+ * Order nodes by topological sort within a subset.
868
+ */
869
+ function getOrderedNodes(nodeIds, workflow, nodeTypes) {
870
+ if (nodeIds.length <= 1)
871
+ return nodeIds;
872
+ const cfg = buildControlFlowGraph(workflow, nodeTypes);
873
+ const fullOrder = performKahnsTopologicalSort(cfg);
874
+ const nodeSet = new Set(nodeIds);
875
+ return fullOrder.filter((n) => nodeSet.has(n));
876
+ }
877
+ /**
878
+ * Collect exit port values and build the return statement.
879
+ */
880
+ function generateReturnStatement(workflow, indent) {
881
+ const exitConnections = workflow.connections.filter((conn) => isExitNode(conn.to.node));
882
+ if (exitConnections.length === 0) {
883
+ return `${indent}return {};`;
884
+ }
885
+ const props = [];
886
+ const seen = new Set();
887
+ for (const conn of exitConnections) {
888
+ const exitPort = conn.to.port;
889
+ if (seen.has(exitPort))
890
+ continue;
891
+ seen.add(exitPort);
892
+ const sourceNode = conn.from.node;
893
+ const sourcePort = conn.from.port;
894
+ if (isStartNode(sourceNode)) {
895
+ props.push(`${exitPort}: event.data.${sourcePort}`);
896
+ }
897
+ else {
898
+ const safeSource = toValidIdentifier(sourceNode);
899
+ props.push(`${exitPort}: ${safeSource}_result?.${sourcePort}`);
900
+ }
901
+ }
902
+ return `${indent}return { ${props.join(', ')} };`;
903
+ }
904
+ // ---------------------------------------------------------------------------
905
+ // Main Generator
906
+ // ---------------------------------------------------------------------------
907
+ /**
908
+ * Generate an Inngest function from a workflow AST.
909
+ *
910
+ * Produces a complete TypeScript module with:
911
+ * - Import statements (Inngest SDK + node type functions)
912
+ * - `inngest.createFunction()` with per-node `step.run()` calls
913
+ * - Parallel execution via `Promise.all` where safe
914
+ * - Branching via if/else for onSuccess/onFailure
915
+ * - Chain flattening for sequential branching (3-way routing)
916
+ * - Indexed `step.run()` for forEach iteration
917
+ *
918
+ * @param workflow - The workflow AST to generate from
919
+ * @param nodeTypes - All available node type definitions
920
+ * @param options - Generation options (service name, trigger, retries, etc.)
921
+ * @returns Complete TypeScript source code string
922
+ */
923
+ export function generateInngestFunction(workflow, nodeTypes, options) {
924
+ const serviceName = options?.serviceName ?? toKebabCase(workflow.functionName);
925
+ const functionId = toKebabCase(workflow.functionName);
926
+ const triggerEvent = options?.triggerEvent ?? `fw/${functionId}.execute`;
927
+ const retries = workflow.options?.retries ?? options?.retries ?? 3;
928
+ const lines = [];
929
+ // -- Imports --
930
+ lines.push(`import { Inngest } from 'inngest';`);
931
+ if (options?.typedEvents) {
932
+ lines.push(`import { z } from 'zod';`);
933
+ }
934
+ if (options?.serveHandler && options?.framework) {
935
+ const importMap = {
936
+ next: 'inngest/next',
937
+ express: 'inngest/express',
938
+ hono: 'inngest/hono',
939
+ fastify: 'inngest/fastify',
940
+ remix: 'inngest/remix',
941
+ };
942
+ lines.push(`import { serve } from '${importMap[options.framework]}';`);
943
+ }
944
+ lines.push('');
945
+ // Collect node type imports (deduplicate by function name)
946
+ const importedFunctions = new Set();
947
+ for (const instance of workflow.instances) {
948
+ if (isPerPortScopedChild(instance, workflow, nodeTypes))
949
+ continue;
950
+ const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
951
+ if (nodeType && !importedFunctions.has(nodeType.functionName)) {
952
+ if (isBuiltInNode(nodeType))
953
+ continue; // Skip built-in nodes — no user import
954
+ importedFunctions.add(nodeType.functionName);
955
+ lines.push(`import { ${nodeType.functionName} } from './node-types/${nodeType.functionName}.js';`);
956
+ }
957
+ }
958
+ lines.push('');
959
+ // -- Inngest client --
960
+ lines.push(`const inngest = new Inngest({ id: '${serviceName}' });`);
961
+ lines.push('');
962
+ // -- Typed event schema (Feature 1) --
963
+ if (options?.typedEvents) {
964
+ const schemaEventName = workflow.options?.trigger?.event ?? triggerEvent;
965
+ const schemaLines = generateEventSchema(workflow, schemaEventName);
966
+ if (schemaLines.length > 0) {
967
+ lines.push(...schemaLines);
968
+ }
969
+ }
970
+ // -- Function definition --
971
+ const fnVar = `${toValidIdentifier(workflow.functionName)}Fn`;
972
+ const configEntries = [
973
+ `id: '${functionId}'`,
974
+ `retries: ${retries}`,
975
+ ];
976
+ // Add timeout from workflow options
977
+ if (workflow.options?.timeout) {
978
+ configEntries.push(`timeouts: { finish: '${workflow.options.timeout}' }`);
979
+ }
980
+ // Add throttle from workflow options
981
+ if (workflow.options?.throttle) {
982
+ const t = workflow.options.throttle;
983
+ const throttleConfig = [`limit: ${t.limit}`];
984
+ if (t.period)
985
+ throttleConfig.push(`period: '${t.period}'`);
986
+ configEntries.push(`throttle: { ${throttleConfig.join(', ')} }`);
987
+ }
988
+ // Add cancelOn from workflow options
989
+ if (workflow.options?.cancelOn) {
990
+ const c = workflow.options.cancelOn;
991
+ const cancelConfig = [`event: '${c.event}'`];
992
+ if (c.match) {
993
+ cancelConfig.push(`match: '${c.match}'`);
994
+ }
995
+ if (c.timeout) {
996
+ cancelConfig.push(`timeout: '${c.timeout}'`);
997
+ }
998
+ configEntries.push(`cancelOn: [{ ${cancelConfig.join(', ')} }]`);
999
+ }
1000
+ if (options?.functionConfig) {
1001
+ for (const [key, value] of Object.entries(options.functionConfig)) {
1002
+ if (key !== 'id' && key !== 'retries') {
1003
+ configEntries.push(`${key}: ${JSON.stringify(value)}`);
1004
+ }
1005
+ }
1006
+ }
1007
+ lines.push(`export const ${fnVar} = inngest.createFunction(`);
1008
+ lines.push(` { ${configEntries.join(', ')} },`);
1009
+ // Trigger emission (Feature 2)
1010
+ const trigger = workflow.options?.trigger;
1011
+ if (trigger?.cron && trigger?.event) {
1012
+ lines.push(` { event: '${trigger.event}' },`);
1013
+ }
1014
+ else if (trigger?.cron) {
1015
+ lines.push(` { cron: '${trigger.cron}' },`);
1016
+ }
1017
+ else {
1018
+ const eventName = trigger?.event ?? triggerEvent;
1019
+ lines.push(` { event: '${eventName}' },`);
1020
+ }
1021
+ lines.push(` async ({ event, step }) => {`);
1022
+ // -- Build control flow --
1023
+ const cfg = buildControlFlowGraph(workflow, nodeTypes);
1024
+ const executionOrder = performKahnsTopologicalSort(cfg);
1025
+ const branchingNodes = findAllBranchingNodes(workflow, nodeTypes);
1026
+ // For Inngest: remove trivially branching nodes that only connect success/failure to Exit.
1027
+ // These don't need if/else blocks — they just route status to the workflow exit.
1028
+ // (The unified generator keeps them for catch block error handling.)
1029
+ for (const nodeId of [...branchingNodes]) {
1030
+ const hasNonExitBranch = workflow.connections.some((conn) => conn.from.node === nodeId &&
1031
+ (isSuccessPort(conn.from.port) || isFailurePort(conn.from.port)) &&
1032
+ !isExitNode(conn.to.node));
1033
+ if (!hasNonExitBranch) {
1034
+ branchingNodes.delete(nodeId);
1035
+ }
1036
+ }
1037
+ const allInstanceIds = new Set(workflow.instances.map((i) => i.id));
1038
+ const branchRegions = new Map();
1039
+ branchingNodes.forEach((branchInstanceId) => {
1040
+ const successNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_SUCCESS, workflow, allInstanceIds, branchingNodes, nodeTypes);
1041
+ const failureNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_FAILURE, workflow, allInstanceIds, branchingNodes, nodeTypes);
1042
+ branchRegions.set(branchInstanceId, { successNodes, failureNodes });
1043
+ });
1044
+ // Handle multi-branch membership: promote nodes in multiple branches
1045
+ const instancesInMultipleBranches = new Set();
1046
+ allInstanceIds.forEach((instanceId) => {
1047
+ let branchCount = 0;
1048
+ branchRegions.forEach((region) => {
1049
+ if (region.successNodes.has(instanceId) || region.failureNodes.has(instanceId)) {
1050
+ branchCount++;
1051
+ }
1052
+ });
1053
+ if (branchCount > 1) {
1054
+ instancesInMultipleBranches.add(instanceId);
1055
+ }
1056
+ });
1057
+ branchRegions.forEach((region) => {
1058
+ instancesInMultipleBranches.forEach((instanceId) => {
1059
+ region.successNodes.delete(instanceId);
1060
+ region.failureNodes.delete(instanceId);
1061
+ });
1062
+ });
1063
+ // Detect branching chains for if/else if flattening
1064
+ const branchingChains = detectBranchingChains(branchingNodes, branchRegions);
1065
+ // Identify chain members (non-heads) that are emitted by their chain head
1066
+ const chainMembers = new Set();
1067
+ branchingChains.forEach((chain) => {
1068
+ for (let i = 1; i < chain.length; i++) {
1069
+ chainMembers.add(chain[i]);
1070
+ }
1071
+ });
1072
+ // Compute nodes in any branch (not top-level)
1073
+ const nodesInAnyBranch = new Set();
1074
+ branchRegions.forEach((region) => {
1075
+ region.successNodes.forEach((n) => nodesInAnyBranch.add(n));
1076
+ region.failureNodes.forEach((n) => nodesInAnyBranch.add(n));
1077
+ });
1078
+ // Filter execution order to top-level nodes
1079
+ const topLevelNodes = executionOrder.filter((n) => !isStartNode(n) &&
1080
+ !isExitNode(n) &&
1081
+ !nodesInAnyBranch.has(n) &&
1082
+ !chainMembers.has(n));
1083
+ // -- Generate node execution code --
1084
+ const indent = ' ';
1085
+ const generatedNodes = new Set();
1086
+ // Pre-declare result variables so they're accessible from all scopes (branch bodies, return statement)
1087
+ // Skip delay nodes — step.sleep() returns void, no result variable needed
1088
+ const resultVarNames = [];
1089
+ for (const instance of workflow.instances) {
1090
+ if (isStartNode(instance.id) || isExitNode(instance.id))
1091
+ continue;
1092
+ if (isPerPortScopedChild(instance, workflow, nodeTypes))
1093
+ continue;
1094
+ const nt = getNodeType(instance.id, workflow, nodeTypes);
1095
+ if (nt && isBuiltInNode(nt) === 'delay')
1096
+ continue;
1097
+ resultVarNames.push(toValidIdentifier(instance.id) + '_result');
1098
+ }
1099
+ if (resultVarNames.length > 0) {
1100
+ lines.push(`${indent}let ${resultVarNames.map((v) => v + ': any').join(', ')};`);
1101
+ lines.push('');
1102
+ }
1103
+ generateNodeBlock(topLevelNodes, workflow, nodeTypes, branchingNodes, branchRegions, branchingChains, chainMembers, indent, lines, generatedNodes);
1104
+ // -- Return statement --
1105
+ lines.push(generateReturnStatement(workflow, indent));
1106
+ lines.push(' }');
1107
+ lines.push(');');
1108
+ lines.push('');
1109
+ // -- Serve handler (Feature 7) --
1110
+ if (options?.serveHandler && options?.framework) {
1111
+ const framework = options.framework;
1112
+ lines.push(`// --- Serve handler (${framework}) ---`);
1113
+ if (framework === 'next') {
1114
+ lines.push(`export const { GET, POST, PUT } = serve({`);
1115
+ }
1116
+ else {
1117
+ lines.push(`export const handler = serve({`);
1118
+ }
1119
+ lines.push(` client: inngest,`);
1120
+ lines.push(` functions: [${fnVar}],`);
1121
+ lines.push(`});`);
1122
+ lines.push('');
1123
+ }
1124
+ return lines.join('\n');
1125
+ }
1126
+ //# sourceMappingURL=inngest.js.map