@synergenius/flow-weaver 0.33.2 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -226
- package/NOTICE +13 -0
- package/README.md +152 -245
- package/dist/agent/agent-loop.d.ts +4 -2
- package/dist/agent/cli-session.d.ts +3 -3
- package/dist/agent/cli-session.js +511 -438
- package/dist/agent/cli-spawn-config.d.ts +4 -4
- package/dist/agent/gate.d.ts +164 -0
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +174 -28
- package/dist/agent/mcp-bridge.d.ts +1 -1
- package/dist/agent/mcp-bridge.js +114 -128
- package/dist/agent/mcp-tool-server.d.ts +2 -2
- package/dist/agent/mcp-tool-server.js +147 -189
- package/dist/agent/profiles.d.ts +69 -0
- package/dist/agent/providers/anthropic.d.ts +2 -4
- package/dist/agent/providers/claude-cli.d.ts +4 -5
- package/dist/agent/providers/openai-compat.d.ts +8 -1
- package/dist/agent/streaming.d.ts +2 -2
- package/dist/agent/types.d.ts +16 -10
- package/dist/api/builder.d.ts +3 -3
- package/dist/api/compile.d.ts +9 -4
- package/dist/api/durable-validation.d.ts +15 -0
- package/dist/api/generate-in-place.d.ts +21 -19
- package/dist/api/generate.d.ts +21 -16
- package/dist/api/generated-module/local-node-functions.d.ts +24 -0
- package/dist/api/generated-module/module-format.d.ts +23 -0
- package/dist/api/generated-module/module-writer.d.ts +34 -0
- package/dist/api/generated-module/node-type-imports.d.ts +39 -0
- package/dist/api/generated-module/preamble.d.ts +16 -0
- package/dist/api/generated-module/workflow-functions.d.ts +29 -0
- package/dist/api/graph-identity.d.ts +39 -0
- package/dist/api/helpers.d.ts +4 -60
- package/dist/api/in-place/built-in-nodes.d.ts +18 -0
- package/dist/api/in-place/function-body.d.ts +21 -0
- package/dist/api/in-place/fw-imports.d.ts +30 -0
- package/dist/api/in-place/macro-coverage.d.ts +14 -0
- package/dist/api/in-place/node-type-functions.d.ts +18 -0
- package/dist/api/in-place/node-type-jsdoc.d.ts +20 -0
- package/dist/api/in-place/orphaned-node-types.d.ts +16 -0
- package/dist/api/in-place/runtime-section.d.ts +16 -0
- package/dist/api/in-place/signature.d.ts +47 -0
- package/dist/api/in-place/source-file.d.ts +19 -0
- package/dist/api/in-place/workflow-jsdoc.d.ts +19 -0
- package/dist/api/index.d.ts +18 -21
- package/dist/api/index.js +259 -53
- package/dist/api/inline-engine.generated.d.ts +3 -0
- package/dist/api/inline-runtime.d.ts +10 -2
- package/dist/api/manipulation/connections.d.ts +1 -1
- package/dist/api/manipulation/index.d.ts +8 -8
- package/dist/api/manipulation/node-types.d.ts +1 -1
- package/dist/api/manipulation/nodes.d.ts +3 -13
- package/dist/api/manipulation/ports.d.ts +1 -1
- package/dist/api/manipulation/scopes.d.ts +1 -1
- package/dist/api/manipulation/workflow.d.ts +1 -1
- package/dist/api/modify-operation.d.ts +0 -2
- package/dist/api/parse.d.ts +22 -1
- package/dist/api/port-references.d.ts +66 -0
- package/dist/api/query.d.ts +18 -2
- package/dist/api/templates.d.ts +1 -1
- package/dist/api/transform.d.ts +1 -1
- package/dist/api/validate.d.ts +1 -1
- package/dist/api/validation-registry.d.ts +2 -2
- package/dist/artifacts/brief.d.ts +43 -0
- package/dist/artifacts/index.d.ts +31 -0
- package/dist/artifacts/pdf.d.ts +19 -0
- package/dist/ast/builder.d.ts +3 -3
- package/dist/ast/index.d.ts +4 -4
- package/dist/ast/index.js +60 -5
- package/dist/ast/serialization-node.d.ts +1 -1
- package/dist/ast/serialization.d.ts +1 -1
- package/dist/ast/types.d.ts +82 -291
- package/dist/ast/workflow-utils.d.ts +4 -4
- package/dist/built-in-nodes/coercion-types.d.ts +1 -1
- package/dist/built-in-nodes/delay.d.ts +3 -1
- package/dist/built-in-nodes/generated-registry.d.ts +1 -1
- package/dist/built-in-nodes/index.d.ts +6 -5
- package/dist/built-in-nodes/index.js +79 -5
- package/dist/built-in-nodes/invoke-workflow.d.ts +2 -1
- package/dist/built-in-nodes/mock-types.d.ts +45 -14
- package/dist/built-in-nodes/sleep.d.ts +12 -0
- package/dist/built-in-nodes/wait-for-agent.d.ts +2 -1
- package/dist/built-in-nodes/wait-for-event.d.ts +2 -1
- package/dist/chevrotain-parser/coerce-parser.d.ts +1 -1
- package/dist/chevrotain-parser/connect-parser.d.ts +1 -1
- package/dist/chevrotain-parser/grammar-diagrams.d.ts +1 -1
- package/dist/chevrotain-parser/index.d.ts +20 -23
- package/dist/chevrotain-parser/node-parser.d.ts +7 -8
- package/dist/chevrotain-parser/parse-line.d.ts +54 -0
- package/dist/chevrotain-parser/tokens.d.ts +0 -17
- package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +6 -1
- package/dist/chunks/chunk-4EUSHAY6.js +298 -0
- package/dist/chunks/chunk-4KDUEOFD.js +197 -0
- package/dist/chunks/chunk-5Y3AIUWY.js +911 -0
- package/dist/chunks/chunk-6KMVP6AU.js +20 -0
- package/dist/chunks/chunk-6PNJORFL.js +128 -0
- package/dist/chunks/chunk-7AAQNGRH.js +2001 -0
- package/dist/chunks/chunk-7YYKTPJ5.js +22705 -0
- package/dist/chunks/chunk-A5MBOFVY.js +33 -0
- package/dist/chunks/chunk-AXNQRVJB.js +18 -0
- package/dist/chunks/chunk-B6HQ6YS5.js +572 -0
- package/dist/chunks/chunk-CY3RNVML.js +296 -0
- package/dist/chunks/chunk-CYHYDLV4.js +653 -0
- package/dist/chunks/chunk-CZBCCK4Y.js +82 -0
- package/dist/chunks/chunk-DCMGQP5V.js +505 -0
- package/dist/chunks/chunk-DIAA66GL.js +72 -0
- package/dist/chunks/chunk-DUO6H5PR.js +20 -0
- package/dist/chunks/chunk-E5WN7IGO.js +125 -0
- package/dist/chunks/chunk-EFGFXXQF.js +184 -0
- package/dist/chunks/chunk-F3RTZV2A.js +2811 -0
- package/dist/chunks/chunk-FMTLP5ZX.js +1397 -0
- package/dist/chunks/chunk-HKZLQJDV.js +79 -0
- package/dist/chunks/chunk-INDEVYEC.js +146 -0
- package/dist/chunks/chunk-JQTF4R5L.js +390 -0
- package/dist/chunks/chunk-KMCAYNUM.js +331 -0
- package/dist/chunks/chunk-KMWCCOUJ.js +1444 -0
- package/dist/chunks/chunk-KU3CLJI2.js +338 -0
- package/dist/chunks/chunk-NDSTGMKR.js +3300 -0
- package/dist/chunks/chunk-NQNLUAPO.js +505 -0
- package/dist/chunks/chunk-O2U2LO57.js +466 -0
- package/dist/chunks/chunk-OOQG7UPP.js +457 -0
- package/dist/chunks/chunk-OZQAW5ZG.js +112 -0
- package/dist/chunks/chunk-P2CVDGHR.js +487 -0
- package/dist/chunks/chunk-P44RIZ7C.js +592 -0
- package/dist/chunks/chunk-PEZCT37T.js +1475 -0
- package/dist/chunks/chunk-PKWSAW6O.js +152 -0
- package/dist/chunks/chunk-Q6MDDZMT.js +4373 -0
- package/dist/chunks/chunk-QDE4Z7KW.js +6 -0
- package/dist/chunks/chunk-QJDAVSNY.js +8 -0
- package/dist/chunks/chunk-QYPORSLO.js +7552 -0
- package/dist/chunks/chunk-R4QGNK34.js +183 -0
- package/dist/chunks/chunk-REEYC73I.js +3002 -0
- package/dist/chunks/chunk-SEWDMQ6Q.js +46 -0
- package/dist/chunks/chunk-TAIUWKZB.js +3057 -0
- package/dist/chunks/chunk-TN4YZ3O7.js +196 -0
- package/dist/chunks/chunk-TRMFB43C.js +143 -0
- package/dist/chunks/chunk-VO4QXG2X.js +1016 -0
- package/dist/chunks/chunk-VP2P3BPA.js +1002 -0
- package/dist/chunks/chunk-VQ7H33J4.js +2176 -0
- package/dist/chunks/chunk-VWA7MCV2.js +105 -0
- package/dist/chunks/chunk-XAV22WD7.js +253 -0
- package/dist/chunks/chunk-YSPBPZDV.js +148 -0
- package/dist/chunks/chunk-ZKEGXO73.js +282 -0
- package/dist/chunks/chunk-ZMGN6F3S.js +4256 -0
- package/dist/chunks/grammar-diagrams-AI7VDXOM.js +14 -0
- package/dist/chunks/index.min-ECJD6GZC.js +37 -0
- package/dist/chunks/manifest-EPTR5MYH.js +21 -0
- package/dist/chunks/openapi-document-6TCGVDUW.js +69 -0
- package/dist/chunks/registry-K2U5KUTY.js +23 -0
- package/dist/chunks/serialization-node-UGY6CFH6.js +38 -0
- package/dist/chunks/validate-GEKSZPIN.js +24 -0
- package/dist/chunks/validation-registry-IQZX3UE3.js +7 -0
- package/dist/chunks/validator-VXFHLPDW.js +26 -0
- package/dist/chunks/workflow-executor-KBGFKHNR.js +36 -0
- package/dist/cli/commands/agents.d.ts +16 -0
- package/dist/cli/commands/artifact.d.ts +10 -0
- package/dist/cli/commands/compile.d.ts +0 -30
- package/dist/cli/commands/console.d.ts +32 -0
- package/dist/cli/commands/context.d.ts +0 -3
- package/dist/cli/commands/describe.js +386 -352
- package/dist/cli/commands/dev.d.ts +9 -4
- package/dist/cli/commands/diagram.d.ts +3 -5
- package/dist/cli/commands/doctor.d.ts +27 -4
- package/dist/cli/commands/doctor.js +3307 -633
- package/dist/cli/commands/export.d.ts +6 -4
- package/dist/cli/commands/grammar.d.ts +0 -3
- package/dist/cli/commands/implement.d.ts +1 -1
- package/dist/cli/commands/init/agent-handoff.d.ts +28 -0
- package/dist/cli/commands/init/config.d.ts +3 -0
- package/dist/cli/commands/init/files.d.ts +12 -0
- package/dist/cli/commands/init/naming.d.ts +13 -0
- package/dist/cli/commands/init/output.d.ts +12 -0
- package/dist/cli/commands/init/setup.d.ts +21 -0
- package/dist/cli/commands/init/types.d.ts +53 -0
- package/dist/cli/commands/init-personas.d.ts +9 -1
- package/dist/cli/commands/init.d.ts +24 -88
- package/dist/cli/commands/market.d.ts +1 -1
- package/dist/cli/commands/mcp-setup.d.ts +1 -4
- package/dist/cli/commands/modify.d.ts +0 -5
- package/dist/cli/commands/openapi.d.ts +9 -11
- package/dist/cli/commands/run.d.ts +0 -4
- package/dist/cli/commands/serve.d.ts +24 -26
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/exports.d.ts +3 -4
- package/dist/cli/exports.js +124 -23
- package/dist/cli/flow-weaver-agents-H653446S.mjs +81 -0
- package/dist/cli/flow-weaver-artifact-WBFYTW76.mjs +58 -0
- package/dist/cli/flow-weaver-chunk-265CEK74.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-2C7PZCV5.mjs +186 -0
- package/dist/cli/flow-weaver-chunk-3BSYCWPL.mjs +3109 -0
- package/dist/cli/flow-weaver-chunk-3MKUQP3D.mjs +547 -0
- package/dist/cli/flow-weaver-chunk-3ZLMPQTH.mjs +27 -0
- package/dist/cli/flow-weaver-chunk-4DVY73ML.mjs +280 -0
- package/dist/cli/flow-weaver-chunk-54M4KNEL.mjs +200 -0
- package/dist/cli/flow-weaver-chunk-55QMUZIA.mjs +256 -0
- package/dist/cli/flow-weaver-chunk-6ZUJ7LMX.mjs +2003 -0
- package/dist/cli/flow-weaver-chunk-7ET6N32V.mjs +419 -0
- package/dist/cli/flow-weaver-chunk-7G63RR4K.mjs +8880 -0
- package/dist/cli/flow-weaver-chunk-A6M5K3W7.mjs +4418 -0
- package/dist/cli/flow-weaver-chunk-AA3NATMX.mjs +154 -0
- package/dist/cli/flow-weaver-chunk-APX6MQ5A.mjs +5652 -0
- package/dist/cli/flow-weaver-chunk-BHCC5FE5.mjs +17 -0
- package/dist/cli/flow-weaver-chunk-DM4BBRRU.mjs +35 -0
- package/dist/cli/flow-weaver-chunk-DRN7KPWY.mjs +168 -0
- package/dist/cli/flow-weaver-chunk-EKWF27G6.mjs +23 -0
- package/dist/cli/flow-weaver-chunk-EM5HWQ4R.mjs +338 -0
- package/dist/cli/flow-weaver-chunk-FPPPU2DG.mjs +226 -0
- package/dist/cli/flow-weaver-chunk-GAQXZHNO.mjs +3005 -0
- package/dist/cli/flow-weaver-chunk-GBUNNP33.mjs +1329 -0
- package/dist/cli/flow-weaver-chunk-HMAKDE55.mjs +636 -0
- package/dist/cli/flow-weaver-chunk-HQ3ZAN44.mjs +367 -0
- package/dist/cli/flow-weaver-chunk-I4APCXYN.mjs +21442 -0
- package/dist/cli/flow-weaver-chunk-IJJ4JXTL.mjs +2297 -0
- package/dist/cli/flow-weaver-chunk-J7EPZW2I.mjs +33 -0
- package/dist/cli/flow-weaver-chunk-KOEMJHLG.mjs +83 -0
- package/dist/cli/flow-weaver-chunk-M6CBWQKJ.mjs +271 -0
- package/dist/cli/flow-weaver-chunk-MHJ4N652.mjs +1580 -0
- package/dist/cli/flow-weaver-chunk-MKAFQWLP.mjs +202 -0
- package/dist/cli/flow-weaver-chunk-MMV2XGD3.mjs +3291 -0
- package/dist/cli/flow-weaver-chunk-MSI2UJES.mjs +125 -0
- package/dist/cli/flow-weaver-chunk-N767YRAI.mjs +48 -0
- package/dist/cli/flow-weaver-chunk-NQKKWDGF.mjs +56 -0
- package/dist/cli/flow-weaver-chunk-NYZDQYFU.mjs +45 -0
- package/dist/cli/flow-weaver-chunk-OMVWSNHJ.mjs +14 -0
- package/dist/cli/flow-weaver-chunk-PUWZ47H5.mjs +2188 -0
- package/dist/cli/flow-weaver-chunk-QOPRVDES.mjs +46 -0
- package/dist/cli/flow-weaver-chunk-RIWE7YTR.mjs +216 -0
- package/dist/cli/flow-weaver-chunk-S63Q3JIU.mjs +789 -0
- package/dist/cli/flow-weaver-chunk-T7X5A23H.mjs +558 -0
- package/dist/cli/flow-weaver-chunk-TLV3VZUN.mjs +2 -0
- package/dist/cli/flow-weaver-chunk-TS5P3PIN.mjs +194 -0
- package/dist/cli/flow-weaver-chunk-U3EO3X4B.mjs +649 -0
- package/dist/cli/flow-weaver-chunk-UNFS3NQP.mjs +34 -0
- package/dist/cli/flow-weaver-chunk-V6DHWFQG.mjs +2811 -0
- package/dist/cli/flow-weaver-chunk-VXWNLYQU.mjs +9 -0
- package/dist/cli/flow-weaver-chunk-W3YNK26P.mjs +149 -0
- package/dist/cli/flow-weaver-chunk-XG2WKXYZ.mjs +324 -0
- package/dist/cli/flow-weaver-chunk-YLSRZWFK.mjs +2219 -0
- package/dist/cli/flow-weaver-chunk-YVPURWS6.mjs +40 -0
- package/dist/cli/flow-weaver-chunk-Z44JK332.mjs +582 -0
- package/dist/cli/flow-weaver-chunk-Z5ZJPNSY.mjs +517 -0
- package/dist/cli/flow-weaver-chunk-ZQALO25T.mjs +594 -0
- package/dist/cli/flow-weaver-compile-A2CABDSB.mjs +37 -0
- package/dist/cli/flow-weaver-console-6EBHDBTN.mjs +71 -0
- package/dist/cli/flow-weaver-context-ECLYANFF.mjs +17 -0
- package/dist/cli/flow-weaver-context-ZRUMW2KE.mjs +69 -0
- package/dist/cli/flow-weaver-create-WIP4EFNP.mjs +124 -0
- package/dist/cli/flow-weaver-deployment-WJJRK7NO.mjs +14 -0
- package/dist/cli/flow-weaver-describe-XDIFF5LI.mjs +41 -0
- package/dist/cli/flow-weaver-dev-LG2NSQAU.mjs +196 -0
- package/dist/cli/flow-weaver-diagram-3EVQARQE.mjs +52 -0
- package/dist/cli/flow-weaver-diagram-7LR7O4M2.mjs +44 -0
- package/dist/cli/flow-weaver-diff-USF7DJLU.mjs +85 -0
- package/dist/cli/flow-weaver-docs-QU2ZOXKH.mjs +96 -0
- package/dist/cli/flow-weaver-doctor-K447YWHR.mjs +43 -0
- package/dist/cli/flow-weaver-export-LAS6N56Y.mjs +140 -0
- package/dist/cli/flow-weaver-grammar-RIRUS3Q6.mjs +49 -0
- package/dist/cli/flow-weaver-grammar-diagrams-WXRTW5SK.mjs +15 -0
- package/dist/cli/flow-weaver-implement-5JG6BCFZ.mjs +114 -0
- package/dist/cli/flow-weaver-index.min-OR5SIHXD.mjs +39 -0
- package/dist/cli/flow-weaver-init-34YZNPQX.mjs +972 -0
- package/dist/cli/flow-weaver-init-personas-NOGZTQQ2.mjs +48 -0
- package/dist/cli/flow-weaver-manifest-4JNUSEB3.mjs +18 -0
- package/dist/cli/flow-weaver-market-WA4CKHKV.mjs +444 -0
- package/dist/cli/flow-weaver-mcp-setup-O26MGXSD.mjs +22 -0
- package/dist/cli/flow-weaver-migrate-VFVEUTPH.mjs +119 -0
- package/dist/cli/flow-weaver-modify-VM3JP5K6.mjs +97 -0
- package/dist/cli/flow-weaver-openapi-IRBWI3HM.mjs +71 -0
- package/dist/cli/flow-weaver-openapi-document-7N6YMPG3.mjs +30 -0
- package/dist/cli/flow-weaver-pack-commands-OS2QRXAK.mjs +146 -0
- package/dist/cli/flow-weaver-registry-5MLFRPPT.mjs +25 -0
- package/dist/cli/flow-weaver-run-DSTLSTHR.mjs +555 -0
- package/dist/cli/flow-weaver-serialization-node-LE6PCG4M.mjs +50 -0
- package/dist/cli/flow-weaver-serve-NVLCLSOW.mjs +192 -0
- package/dist/cli/flow-weaver-server-7IERLVXE.mjs +2925 -0
- package/dist/cli/flow-weaver-server-KWF4PXYC.mjs +19112 -0
- package/dist/cli/flow-weaver-status-75W2S3YB.mjs +138 -0
- package/dist/cli/flow-weaver-strip-AZPXUYVA.mjs +79 -0
- package/dist/cli/flow-weaver-templates-DSNMB6K7.mjs +72 -0
- package/dist/cli/flow-weaver-validate-A4UWDQN6.mjs +19 -0
- package/dist/cli/flow-weaver-validate-GRBZ5W4C.mjs +296 -0
- package/dist/cli/flow-weaver-validation-registry-BINGUNXX.mjs +9 -0
- package/dist/cli/flow-weaver-validator-TY56CI42.mjs +21 -0
- package/dist/cli/flow-weaver-watch-OPPERXL6.mjs +114 -0
- package/dist/cli/flow-weaver-workflow-executor-6AHYPXAN.mjs +26 -0
- package/dist/cli/flow-weaver.mjs +3557 -89384
- package/dist/cli/index.d.ts +5 -2
- package/dist/cli/pack-commands-needed.d.ts +12 -0
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/templates/ai/mock-provider.d.ts +1 -1
- package/dist/cli/templates/index.d.ts +1 -8
- package/dist/cli/templates/nodes/agent-router.d.ts +1 -1
- package/dist/cli/templates/nodes/aggregator.d.ts +1 -1
- package/dist/cli/templates/nodes/conversation-memory.d.ts +1 -1
- package/dist/cli/templates/nodes/http.d.ts +1 -1
- package/dist/cli/templates/nodes/human-approval.d.ts +1 -1
- package/dist/cli/templates/nodes/json-extractor.d.ts +1 -1
- package/dist/cli/templates/nodes/llm-call.d.ts +1 -1
- package/dist/cli/templates/nodes/prompt-template.d.ts +1 -1
- package/dist/cli/templates/nodes/rag-retriever.d.ts +1 -1
- package/dist/cli/templates/nodes/tool-executor.d.ts +1 -1
- package/dist/cli/templates/nodes/transformer.d.ts +1 -1
- package/dist/cli/templates/nodes/validator.d.ts +1 -1
- package/dist/cli/templates/shared/approval-types.d.ts +1 -1
- package/dist/cli/templates/shared/case.d.ts +14 -0
- package/dist/cli/templates/shared/llm-types.d.ts +4 -4
- package/dist/cli/templates/workflows/aggregator.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-agent.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-chat.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-rag.d.ts +1 -1
- package/dist/cli/templates/workflows/ai-react.d.ts +1 -1
- package/dist/cli/templates/workflows/approval.d.ts +10 -0
- package/dist/cli/templates/workflows/conditional.d.ts +2 -2
- package/dist/cli/templates/workflows/error-handler.d.ts +2 -2
- package/dist/cli/templates/workflows/foreach.d.ts +1 -1
- package/dist/cli/templates/workflows/sequential.d.ts +1 -1
- package/dist/cli/templates/workflows/webhook.d.ts +1 -1
- package/dist/cli/utils/interactive.d.ts +6 -0
- package/dist/cli/utils/json-option.d.ts +8 -0
- package/dist/cli/utils/logger.d.ts +2 -0
- package/dist/compiler/durable-source-proof.d.ts +23 -0
- package/dist/compiler/executable-artifact.d.ts +29 -0
- package/dist/compiler/index.d.ts +4 -0
- package/dist/compiler/index.js +36 -0
- package/dist/console/app.js +106 -0
- package/dist/console/assets/synergenius.svg +18 -0
- package/dist/console/author.d.ts +32 -0
- package/dist/console/cli-catalog.d.ts +21 -0
- package/dist/console/cli-run.d.ts +35 -0
- package/dist/console/cli-run.js +46 -0
- package/dist/console/debug.d.ts +67 -0
- package/dist/console/diff-view.d.ts +96 -0
- package/dist/console/export.d.ts +36 -0
- package/dist/console/git.d.ts +27 -0
- package/dist/console/index.d.ts +4 -0
- package/dist/console/index.html +16 -0
- package/dist/console/index.js +203 -0
- package/dist/console/packs.d.ts +100 -0
- package/dist/console/request-guard.d.ts +10 -0
- package/dist/console/respond.d.ts +16 -0
- package/dist/console/router.d.ts +64 -0
- package/dist/console/routes-agents.d.ts +14 -0
- package/dist/console/routes-project.d.ts +3 -0
- package/dist/console/routes-runs.d.ts +3 -0
- package/dist/console/routes-services.d.ts +7 -0
- package/dist/console/routes-tools.d.ts +3 -0
- package/dist/console/runs.d.ts +69 -0
- package/dist/console/scan.d.ts +49 -0
- package/dist/console/schema.d.ts +74 -0
- package/dist/console/server.d.ts +34 -0
- package/dist/console/server.js +43264 -0
- package/dist/console/services.d.ts +119 -0
- package/dist/console/source.d.ts +23 -0
- package/dist/console/status.d.ts +68 -0
- package/dist/console/styles.css +1023 -0
- package/dist/console/terminals.d.ts +22 -0
- package/dist/console/workflow-view.d.ts +153 -0
- package/dist/constants.d.ts +5 -1
- package/dist/constants.js +56 -167
- package/dist/context/index.d.ts +10 -0
- package/dist/context/index.js +14 -191
- package/dist/coordinator/agent-gate.d.ts +107 -0
- package/dist/coordinator/bundle-digest.d.ts +20 -0
- package/dist/coordinator/clock-tick.d.ts +4 -0
- package/dist/coordinator/effect-receipts.d.ts +10 -0
- package/dist/coordinator/errors.d.ts +31 -0
- package/dist/coordinator/file-store.d.ts +3 -0
- package/dist/coordinator/gate-labeling.d.ts +33 -0
- package/dist/coordinator/gate-resolution.d.ts +28 -0
- package/dist/coordinator/index.d.ts +12 -0
- package/dist/coordinator/index.js +107 -0
- package/dist/coordinator/memory-store.d.ts +3 -0
- package/dist/coordinator/params.d.ts +18 -0
- package/dist/coordinator/run-annotations.d.ts +6 -0
- package/dist/coordinator/run-context.d.ts +11 -0
- package/dist/coordinator/run-ending.d.ts +5 -0
- package/dist/coordinator/run-outcome.d.ts +31 -0
- package/dist/coordinator/run-resume.d.ts +6 -0
- package/dist/coordinator/run-start.d.ts +4 -0
- package/dist/coordinator/run-store.d.ts +275 -0
- package/dist/coordinator/run-trace.d.ts +41 -0
- package/dist/coordinator/run-views.d.ts +12 -0
- package/dist/coordinator/runs-dir.d.ts +26 -0
- package/dist/coordinator/store.d.ts +67 -0
- package/dist/coordinator/time.d.ts +39 -0
- package/dist/coordinator/workflow-selection.d.ts +6 -0
- package/dist/deployment/index.d.ts +11 -16
- package/dist/deployment/index.js +541 -50
- package/dist/deployment/targets/base.d.ts +11 -13
- package/dist/deployment/targets/openapi-parts.d.ts +92 -0
- package/dist/diagram/arms.d.ts +70 -0
- package/dist/diagram/ascii-renderer.d.ts +1 -1
- package/dist/diagram/geometry.d.ts +20 -32
- package/dist/diagram/graph/bounds.d.ts +30 -0
- package/dist/diagram/graph/metrics.d.ts +21 -0
- package/dist/diagram/graph/nodes.d.ts +33 -0
- package/dist/diagram/graph/placement.d.ts +22 -0
- package/dist/diagram/graph/ports.d.ts +23 -0
- package/dist/diagram/graph/routing.d.ts +40 -0
- package/dist/diagram/graph/scopes.d.ts +30 -0
- package/dist/diagram/index.d.ts +15 -23
- package/dist/diagram/index.js +632 -111
- package/dist/diagram/labels.d.ts +12 -0
- package/dist/diagram/lanes.d.ts +93 -0
- package/dist/diagram/layout.d.ts +1 -1
- package/dist/diagram/material-symbols.d.ts +7 -0
- package/dist/diagram/orthogonal-router.d.ts +2 -3
- package/dist/diagram/process-view.d.ts +79 -0
- package/dist/diagram/spine.d.ts +25 -0
- package/dist/diagram/theme.d.ts +1 -5
- package/dist/diagram/types.d.ts +6 -20
- package/dist/diff/index.js +53 -9
- package/dist/diff/types.d.ts +1 -5
- package/dist/doc-metadata/extractors/annotations.d.ts +0 -4
- package/dist/doc-metadata/extractors/cli-commands.d.ts +2 -1
- package/dist/doc-metadata/extractors/core-metadata.d.ts +19 -4
- package/dist/doc-metadata/extractors/error-codes.d.ts +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.d.ts +7 -3
- package/dist/doc-metadata/index.d.ts +1 -2
- package/dist/doc-metadata/index.js +2368 -9
- package/dist/doc-metadata/types.d.ts +7 -24
- package/dist/docs/guide.d.ts +33 -0
- package/dist/docs/index.d.ts +7 -4
- package/dist/docs/index.js +298 -298
- package/dist/docs/pack-topics.d.ts +18 -0
- package/dist/editor-completions/annotationValues.d.ts +1 -1
- package/dist/editor-completions/contextParser.d.ts +3 -2
- package/dist/editor-completions/dataTypes.d.ts +1 -1
- package/dist/editor-completions/goToDefinition.d.ts +1 -1
- package/dist/editor-completions/index.d.ts +8 -8
- package/dist/editor-completions/index.js +1244 -175
- package/dist/editor-completions/jsDocAnnotations.d.ts +1 -1
- package/dist/editor-completions/modifierCompletions.d.ts +1 -1
- package/dist/export/index.d.ts +7 -1
- package/dist/generated-branding.js +8 -21
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +7 -3
- package/dist/generator/annotation-generator.d.ts +84 -0
- package/dist/generator/async-detection.d.ts +1 -1
- package/dist/{body-generator.d.ts → generator/body-generator.d.ts} +7 -3
- package/dist/generator/code-utils.d.ts +15 -60
- package/dist/generator/connection-reads.d.ts +94 -0
- package/dist/generator/control-flow-plan.d.ts +74 -0
- package/dist/generator/control-flow.d.ts +1 -1
- package/dist/generator/exit-assembly.d.ts +11 -0
- package/dist/generator/index.d.ts +4 -5
- package/dist/generator/index.js +34 -11
- package/dist/generator/input-port-values.d.ts +22 -0
- package/dist/generator/{scope-function-generator.d.ts → node-arguments.d.ts} +76 -1
- package/dist/generator/node-invocation.d.ts +74 -0
- package/dist/{node-types-generator.d.ts → generator/node-types-generator.d.ts} +3 -2
- package/dist/generator/scope-closure.d.ts +23 -0
- package/dist/generator/scoped-child.d.ts +49 -0
- package/dist/{sugar-optimizer.d.ts → generator/sugar-optimizer.d.ts} +1 -1
- package/dist/generator/unified.d.ts +14 -2
- package/dist/generator/workflow-generator.d.ts +37 -0
- package/dist/index.d.ts +29 -26
- package/dist/index.js +1322 -92
- package/dist/jsdoc-port-sync/apply-diff.d.ts +15 -0
- package/dist/jsdoc-port-sync/callback-signature.d.ts +17 -0
- package/dist/jsdoc-port-sync/diff.d.ts +5 -7
- package/dist/jsdoc-port-sync/incomplete-lines.d.ts +41 -0
- package/dist/jsdoc-port-sync/index.d.ts +11 -11
- package/dist/jsdoc-port-sync/index.js +88 -45
- package/dist/jsdoc-port-sync/jsdoc-to-signature.d.ts +66 -0
- package/dist/jsdoc-port-sync/port-parser.d.ts +6 -24
- package/dist/jsdoc-port-sync/port-tag-placement.d.ts +55 -0
- package/dist/jsdoc-port-sync/scoped-ports.d.ts +21 -0
- package/dist/jsdoc-port-sync/signature-parser.d.ts +8 -1
- package/dist/jsdoc-port-sync/signature-to-jsdoc.d.ts +77 -0
- package/dist/jsdoc-port-sync/sync.d.ts +4 -1
- package/dist/marketplace/index.d.ts +5 -3
- package/dist/marketplace/index.js +62 -10
- package/dist/marketplace/install.d.ts +24 -0
- package/dist/marketplace/manifest.d.ts +1 -1
- package/dist/marketplace/npmrc.d.ts +24 -0
- package/dist/marketplace/registry.d.ts +46 -11
- package/dist/marketplace/types.d.ts +41 -24
- package/dist/marketplace/validator.d.ts +1 -1
- package/dist/mcp/debug-session.d.ts +2 -4
- package/dist/mcp/dist-imports.d.ts +12 -0
- package/dist/mcp/response-utils.d.ts +11 -9
- package/dist/mcp/server.d.ts +0 -1
- package/dist/mcp/tools-context.d.ts +11 -0
- package/dist/mcp/tools-diagram.d.ts +1 -2
- package/dist/mcp/tools-export.d.ts +2 -2
- package/dist/mcp/tools-marketplace.d.ts +1 -1
- package/dist/mcp/tools-run.d.ts +17 -0
- package/dist/mcp/tools-workflow.d.ts +3 -0
- package/dist/mcp/types.d.ts +4 -14
- package/dist/mcp/workflow-executor.d.ts +57 -19
- package/dist/npm-packages.d.ts +1 -1
- package/dist/npm-packages.js +10 -473
- package/dist/parser/annotation-parser.d.ts +88 -0
- package/dist/parser/annotation-suggestion.d.ts +23 -0
- package/dist/parser/durable-effect-contract.d.ts +10 -0
- package/dist/parser/expression-references.d.ts +101 -0
- package/dist/parser/external-node-types.d.ts +69 -0
- package/dist/{function-like.d.ts → parser/function-like.d.ts} +10 -3
- package/dist/parser/generated-sections.d.ts +25 -0
- package/dist/parser/import-resolution.d.ts +67 -0
- package/dist/parser/import-stub.d.ts +29 -0
- package/dist/parser/jsdoc/config-types.d.ts +209 -0
- package/dist/parser/jsdoc/graph-tags.d.ts +54 -0
- package/dist/parser/jsdoc/node-type-block.d.ts +23 -0
- package/dist/parser/jsdoc/parse-line-once.d.ts +11 -0
- package/dist/parser/jsdoc/port-tags.d.ts +42 -0
- package/dist/parser/jsdoc/runtime-tags.d.ts +65 -0
- package/dist/parser/jsdoc/signature-types.d.ts +52 -0
- package/dist/parser/jsdoc/workflow-block.d.ts +22 -0
- package/dist/parser/jsdoc-parser.d.ts +23 -0
- package/dist/parser/macro-expansion.d.ts +80 -0
- package/dist/parser/module-resolution.d.ts +38 -0
- package/dist/parser/node-inference.d.ts +39 -0
- package/dist/parser/npm-node-types.d.ts +24 -0
- package/dist/parser/pack-handlers.d.ts +12 -0
- package/dist/parser/path-data-resolution.d.ts +75 -0
- package/dist/parser/port-inference.d.ts +15 -0
- package/dist/{resolve-package-types.d.ts → parser/resolve-package-types.d.ts} +4 -0
- package/dist/parser/serialize-deploy-annotations.d.ts +19 -0
- package/dist/parser/tag-registry.d.ts +22 -0
- package/dist/parser/workflow-extraction.d.ts +39 -0
- package/dist/runtime/ExecutionContext.d.ts +87 -99
- package/dist/runtime/continuation-core.d.ts +147 -0
- package/dist/runtime/continuation.d.ts +48 -0
- package/dist/runtime/debug-controller.d.ts +7 -22
- package/dist/runtime/durable-execution.d.ts +176 -0
- package/dist/runtime/events.d.ts +16 -1
- package/dist/runtime/executable-module-contract.d.ts +16 -0
- package/dist/runtime/index.d.ts +13 -10
- package/dist/runtime/index.js +947 -9
- package/dist/runtime/strict-json.d.ts +15 -0
- package/dist/server/access.d.ts +12 -0
- package/dist/server/api.d.ts +126 -0
- package/dist/server/callback-delivery.d.ts +62 -0
- package/dist/server/callback-url.d.ts +44 -0
- package/dist/server/context.d.ts +35 -0
- package/dist/server/http-error.d.ts +10 -0
- package/dist/server/index.d.ts +7 -1
- package/dist/server/index.js +125 -6
- package/dist/server/openapi-document.d.ts +33 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/respond.d.ts +35 -0
- package/dist/server/route-params.d.ts +17 -0
- package/dist/server/route-plan.d.ts +41 -0
- package/dist/server/routes-catalog.d.ts +5 -0
- package/dist/server/routes-declared.d.ts +12 -0
- package/dist/server/routes-runs.d.ts +8 -0
- package/dist/server/run-driver.d.ts +58 -0
- package/dist/server/run-view.d.ts +26 -0
- package/dist/server/transport.d.ts +35 -0
- package/dist/server/types.d.ts +143 -52
- package/dist/server/webhook-server.d.ts +12 -38
- package/dist/server/workflow-registry.d.ts +7 -4
- package/dist/service-registry.d.ts +49 -0
- package/dist/service-registry.js +123 -0
- package/dist/testing/assertions.d.ts +2 -2
- package/dist/testing/index.d.ts +14 -13
- package/dist/testing/index.js +582 -24
- package/dist/testing/mock-approval.d.ts +3 -5
- package/dist/testing/mock-llm.d.ts +2 -3
- package/dist/testing/recorder.d.ts +5 -6
- package/dist/testing/replayer.d.ts +6 -7
- package/dist/testing/run-store-check.d.ts +3 -0
- package/dist/testing/token-tracker.d.ts +2 -2
- package/dist/{type-mappings.d.ts → types/type-mappings.d.ts} +13 -1
- package/dist/utils/error-utils.d.ts +4 -2
- package/dist/utils/port-ordering.d.ts +2 -2
- package/dist/utils/port-tag-utils.d.ts +1 -1
- package/dist/validation/agent-detection.d.ts +17 -5
- package/dist/validation/agent-rules.d.ts +6 -7
- package/dist/validation/core-rules.d.ts +49 -0
- package/dist/validation/design-rules.d.ts +9 -3
- package/dist/validation/rule-registry.d.ts +3 -3
- package/dist/validation/rules/connections.d.ts +27 -0
- package/dist/validation/rules/context.d.ts +11 -0
- package/dist/validation/rules/cycles.d.ts +17 -0
- package/dist/validation/rules/data-flow.d.ts +19 -0
- package/dist/validation/rules/scope-topology.d.ts +35 -0
- package/dist/validation/rules/type-compatibility.d.ts +30 -0
- package/dist/validation/type-checker.d.ts +39 -0
- package/dist/validation/validator-helpers.d.ts +31 -0
- package/dist/validation/validator.d.ts +30 -0
- package/docs/reference/advanced-annotations.md +277 -60
- package/docs/reference/built-in-nodes.md +164 -94
- package/docs/reference/cancellation.md +44 -0
- package/docs/reference/cli-reference.md +252 -234
- package/docs/reference/compilation.md +36 -109
- package/docs/reference/concepts.md +80 -63
- package/docs/reference/console.md +169 -0
- package/docs/reference/debugging.md +26 -57
- package/docs/reference/deployment.md +217 -143
- package/docs/reference/durable-gates.md +445 -0
- package/docs/reference/error-codes.md +90 -43
- package/docs/reference/export-interface.md +91 -26
- package/docs/reference/iterative-development.md +50 -28
- package/docs/reference/jsdoc-grammar.md +66 -311
- package/docs/reference/library.md +250 -0
- package/docs/reference/marketplace.md +301 -152
- package/docs/reference/mcp-tools.md +102 -0
- package/docs/reference/node-conversion.md +84 -59
- package/docs/reference/orientation.md +68 -0
- package/docs/reference/scaffold.md +5 -4
- package/docs/reference/tutorial.md +106 -166
- package/docs/reference/visual-reference.md +1 -1
- package/package.json +85 -50
- package/dist/agent/agent-loop.js +0 -156
- package/dist/agent/cli-spawn-config.js +0 -65
- package/dist/agent/device-connection.d.ts +0 -79
- package/dist/agent/device-connection.js +0 -184
- package/dist/agent/env-allowlist.js +0 -60
- package/dist/agent/providers/anthropic.js +0 -198
- package/dist/agent/providers/claude-cli.js +0 -161
- package/dist/agent/providers/openai-compat.js +0 -205
- package/dist/agent/providers/platform.d.ts +0 -23
- package/dist/agent/providers/platform.js +0 -132
- package/dist/agent/streaming.js +0 -186
- package/dist/agent/types.js +0 -20
- package/dist/annotation-generator.d.ts +0 -50
- package/dist/annotation-generator.js +0 -743
- package/dist/api/builder.js +0 -345
- package/dist/api/command-runner.js +0 -787
- package/dist/api/compile.js +0 -148
- package/dist/api/extract-types.js +0 -57
- package/dist/api/generate-in-place.js +0 -1477
- package/dist/api/generate.js +0 -514
- package/dist/api/helpers.js +0 -285
- package/dist/api/inline-runtime.js +0 -526
- package/dist/api/manipulation/connections.js +0 -151
- package/dist/api/manipulation/index.js +0 -41
- package/dist/api/manipulation/node-types.js +0 -200
- package/dist/api/manipulation/nodes.js +0 -355
- package/dist/api/manipulation/ports.js +0 -228
- package/dist/api/manipulation/scopes.js +0 -156
- package/dist/api/manipulation/workflow.js +0 -116
- package/dist/api/modify-operation.js +0 -177
- package/dist/api/parse.js +0 -115
- package/dist/api/patterns.d.ts +0 -112
- package/dist/api/patterns.js +0 -303
- package/dist/api/query.js +0 -840
- package/dist/api/templates.js +0 -117
- package/dist/api/transform.js +0 -40
- package/dist/api/validate.js +0 -64
- package/dist/api/validation-registry.js +0 -10
- package/dist/api/workflow-file-operations.js +0 -180
- package/dist/ast/builder.js +0 -395
- package/dist/ast/serialization-node.js +0 -30
- package/dist/ast/serialization.js +0 -134
- package/dist/ast/types.js +0 -2
- package/dist/ast/workflow-utils.js +0 -114
- package/dist/body-generator.js +0 -35
- package/dist/built-in-nodes/coercion-types.js +0 -61
- package/dist/built-in-nodes/delay.js +0 -29
- package/dist/built-in-nodes/generated-registry.js +0 -299
- package/dist/built-in-nodes/invoke-workflow.js +0 -36
- package/dist/built-in-nodes/mock-types.js +0 -42
- package/dist/built-in-nodes/wait-for-agent.js +0 -31
- package/dist/built-in-nodes/wait-for-event.js +0 -25
- package/dist/chevrotain-parser/coerce-parser.js +0 -103
- package/dist/chevrotain-parser/connect-parser.js +0 -136
- package/dist/chevrotain-parser/fan-parser.js +0 -149
- package/dist/chevrotain-parser/grammar-diagrams.js +0 -273
- package/dist/chevrotain-parser/index.js +0 -31
- package/dist/chevrotain-parser/map-parser.js +0 -130
- package/dist/chevrotain-parser/node-parser.js +0 -558
- package/dist/chevrotain-parser/path-parser.js +0 -140
- package/dist/chevrotain-parser/port-parser.js +0 -519
- package/dist/chevrotain-parser/position-parser.d.ts +0 -20
- package/dist/chevrotain-parser/position-parser.js +0 -83
- package/dist/chevrotain-parser/scope-parser.js +0 -104
- package/dist/chevrotain-parser/tokens.js +0 -429
- package/dist/chevrotain-parser/trigger-cancel-parser.js +0 -291
- package/dist/cli/commands/account.d.ts +0 -4
- package/dist/cli/commands/account.js +0 -50
- package/dist/cli/commands/ai-credentials.d.ts +0 -12
- package/dist/cli/commands/ai-credentials.js +0 -95
- package/dist/cli/commands/apikey.d.ts +0 -4
- package/dist/cli/commands/apikey.js +0 -64
- package/dist/cli/commands/auth.d.ts +0 -9
- package/dist/cli/commands/auth.js +0 -255
- package/dist/cli/commands/compile.js +0 -331
- package/dist/cli/commands/connect.d.ts +0 -4
- package/dist/cli/commands/connect.js +0 -163
- package/dist/cli/commands/context.js +0 -51
- package/dist/cli/commands/create.js +0 -139
- package/dist/cli/commands/deploy.d.ts +0 -6
- package/dist/cli/commands/deploy.js +0 -74
- package/dist/cli/commands/dev.js +0 -227
- package/dist/cli/commands/diagram.js +0 -39
- package/dist/cli/commands/diff.js +0 -57
- package/dist/cli/commands/docs.js +0 -74
- package/dist/cli/commands/export.js +0 -153
- package/dist/cli/commands/grammar.js +0 -40
- package/dist/cli/commands/implement.js +0 -91
- package/dist/cli/commands/init-personas.js +0 -541
- package/dist/cli/commands/init.js +0 -797
- package/dist/cli/commands/market.js +0 -440
- package/dist/cli/commands/mcp-setup.js +0 -433
- package/dist/cli/commands/migrate.js +0 -91
- package/dist/cli/commands/modify.js +0 -58
- package/dist/cli/commands/openapi.js +0 -71
- package/dist/cli/commands/org.d.ts +0 -8
- package/dist/cli/commands/org.js +0 -136
- package/dist/cli/commands/pattern.d.ts +0 -34
- package/dist/cli/commands/pattern.js +0 -186
- package/dist/cli/commands/plugin.d.ts +0 -16
- package/dist/cli/commands/plugin.js +0 -176
- package/dist/cli/commands/run.js +0 -742
- package/dist/cli/commands/serve.js +0 -93
- package/dist/cli/commands/status.js +0 -123
- package/dist/cli/commands/strip.js +0 -61
- package/dist/cli/commands/templates.js +0 -58
- package/dist/cli/commands/validate.js +0 -267
- package/dist/cli/commands/watch.js +0 -71
- package/dist/cli/config/credentials.d.ts +0 -15
- package/dist/cli/config/credentials.js +0 -43
- package/dist/cli/config/platform-client.d.ts +0 -123
- package/dist/cli/config/platform-client.js +0 -248
- package/dist/cli/env-setup.js +0 -12
- package/dist/cli/index.js +0 -850
- package/dist/cli/pack-commands.js +0 -122
- package/dist/cli/postinstall.d.ts +0 -16
- package/dist/cli/postinstall.js +0 -119
- package/dist/cli/templates/ai/mock-provider.js +0 -64
- package/dist/cli/templates/ai/types.js +0 -5
- package/dist/cli/templates/approvals/index.d.ts +0 -15
- package/dist/cli/templates/approvals/index.js +0 -241
- package/dist/cli/templates/index.js +0 -123
- package/dist/cli/templates/nodes/agent-router.js +0 -114
- package/dist/cli/templates/nodes/aggregator.js +0 -63
- package/dist/cli/templates/nodes/conversation-memory.js +0 -85
- package/dist/cli/templates/nodes/http.js +0 -80
- package/dist/cli/templates/nodes/human-approval.js +0 -109
- package/dist/cli/templates/nodes/json-extractor.js +0 -119
- package/dist/cli/templates/nodes/llm-call.js +0 -106
- package/dist/cli/templates/nodes/prompt-template.js +0 -52
- package/dist/cli/templates/nodes/rag-retriever.js +0 -128
- package/dist/cli/templates/nodes/tool-executor.js +0 -108
- package/dist/cli/templates/nodes/transformer.js +0 -68
- package/dist/cli/templates/nodes/validator.js +0 -62
- package/dist/cli/templates/pack-loader.js +0 -58
- package/dist/cli/templates/providers/index.js +0 -239
- package/dist/cli/templates/shared/approval-types.js +0 -31
- package/dist/cli/templates/shared/llm-types.js +0 -109
- package/dist/cli/templates/workflows/aggregator.js +0 -101
- package/dist/cli/templates/workflows/ai-agent.js +0 -326
- package/dist/cli/templates/workflows/ai-chat.js +0 -167
- package/dist/cli/templates/workflows/ai-rag.js +0 -184
- package/dist/cli/templates/workflows/ai-react.js +0 -293
- package/dist/cli/templates/workflows/conditional.js +0 -139
- package/dist/cli/templates/workflows/error-handler.js +0 -145
- package/dist/cli/templates/workflows/foreach.js +0 -140
- package/dist/cli/templates/workflows/sequential.js +0 -200
- package/dist/cli/templates/workflows/webhook.js +0 -158
- package/dist/cli/utils/cli-helpers.d.ts +0 -44
- package/dist/cli/utils/cli-helpers.js +0 -99
- package/dist/cli/utils/logger.js +0 -126
- package/dist/cli/utils/parse-int-strict.js +0 -17
- package/dist/cli/utils/safe-write.js +0 -54
- package/dist/deployment/config/defaults.d.ts +0 -29
- package/dist/deployment/config/defaults.js +0 -98
- package/dist/deployment/config/loader.d.ts +0 -24
- package/dist/deployment/config/loader.js +0 -237
- package/dist/deployment/config/types.d.ts +0 -117
- package/dist/deployment/config/types.js +0 -5
- package/dist/deployment/core/adapters.d.ts +0 -66
- package/dist/deployment/core/adapters.js +0 -156
- package/dist/deployment/core/executor.d.ts +0 -62
- package/dist/deployment/core/executor.js +0 -197
- package/dist/deployment/core/formatters.d.ts +0 -42
- package/dist/deployment/core/formatters.js +0 -146
- package/dist/deployment/openapi/generator.d.ts +0 -146
- package/dist/deployment/openapi/generator.js +0 -347
- package/dist/deployment/openapi/schema-converter.d.ts +0 -49
- package/dist/deployment/openapi/schema-converter.js +0 -192
- package/dist/deployment/targets/base.js +0 -855
- package/dist/deployment/types.d.ts +0 -140
- package/dist/deployment/types.js +0 -8
- package/dist/diagram/ascii-renderer.js +0 -528
- package/dist/diagram/geometry.js +0 -1226
- package/dist/diagram/html-viewer.d.ts +0 -21
- package/dist/diagram/html-viewer.js +0 -1745
- package/dist/diagram/layout.js +0 -143
- package/dist/diagram/orthogonal-router.js +0 -458
- package/dist/diagram/renderer.d.ts +0 -3
- package/dist/diagram/renderer.js +0 -280
- package/dist/diagram/theme.js +0 -282
- package/dist/diagram/types.js +0 -2
- package/dist/diff/WorkflowDiffer.js +0 -429
- package/dist/diff/formatDiff.js +0 -239
- package/dist/diff/impact.js +0 -119
- package/dist/diff/types.js +0 -35
- package/dist/doc-metadata/extractors/annotations.js +0 -638
- package/dist/doc-metadata/extractors/cli-commands.js +0 -326
- package/dist/doc-metadata/extractors/core-metadata.js +0 -61
- package/dist/doc-metadata/extractors/error-codes.js +0 -372
- package/dist/doc-metadata/extractors/grammar-rules.js +0 -51
- package/dist/doc-metadata/extractors/mcp-tools.js +0 -705
- package/dist/doc-metadata/extractors/plugin-api.d.ts +0 -19
- package/dist/doc-metadata/extractors/plugin-api.js +0 -279
- package/dist/doc-metadata/types.js +0 -5
- package/dist/editor-completions/annotationValues.js +0 -146
- package/dist/editor-completions/contextParser.js +0 -410
- package/dist/editor-completions/dataTypes.js +0 -95
- package/dist/editor-completions/goToDefinition.js +0 -112
- package/dist/editor-completions/jsDocAnnotations.js +0 -359
- package/dist/editor-completions/modifierCompletions.js +0 -197
- package/dist/editor-completions/types.js +0 -8
- package/dist/export/index.js +0 -212
- package/dist/extensions/index.d.ts +0 -16
- package/dist/extensions/index.js +0 -17
- package/dist/friendly-errors.js +0 -761
- package/dist/function-like.js +0 -84
- package/dist/generator/async-detection.js +0 -56
- package/dist/generator/code-utils.js +0 -551
- package/dist/generator/compile-target-registry.d.ts +0 -20
- package/dist/generator/compile-target-registry.js +0 -20
- package/dist/generator/control-flow.js +0 -358
- package/dist/generator/dev-mode-registry.d.ts +0 -27
- package/dist/generator/dev-mode-registry.js +0 -20
- package/dist/generator/scope-function-generator.js +0 -397
- package/dist/generator/unified.js +0 -1831
- package/dist/generator.d.ts +0 -54
- package/dist/generator.js +0 -101
- package/dist/jsdoc-parser.d.ts +0 -377
- package/dist/jsdoc-parser.js +0 -1189
- package/dist/jsdoc-port-sync/constants.js +0 -103
- package/dist/jsdoc-port-sync/diff.js +0 -319
- package/dist/jsdoc-port-sync/port-parser.js +0 -579
- package/dist/jsdoc-port-sync/rename.js +0 -256
- package/dist/jsdoc-port-sync/signature-parser.js +0 -559
- package/dist/jsdoc-port-sync/sync.js +0 -653
- package/dist/marketplace/manifest.js +0 -194
- package/dist/marketplace/registry.js +0 -198
- package/dist/marketplace/types.js +0 -9
- package/dist/marketplace/validator.js +0 -150
- package/dist/mcp/agent-channel.d.ts +0 -35
- package/dist/mcp/agent-channel.js +0 -61
- package/dist/mcp/auto-registration.d.ts +0 -3
- package/dist/mcp/auto-registration.js +0 -62
- package/dist/mcp/debug-session.js +0 -25
- package/dist/mcp/index.d.ts +0 -11
- package/dist/mcp/index.js +0 -10
- package/dist/mcp/pack-tools.js +0 -59
- package/dist/mcp/prompts.js +0 -68
- package/dist/mcp/response-utils.js +0 -97
- package/dist/mcp/run-registry.d.ts +0 -29
- package/dist/mcp/run-registry.js +0 -24
- package/dist/mcp/server.js +0 -63
- package/dist/mcp/tools-context.js +0 -51
- package/dist/mcp/tools-debug.js +0 -451
- package/dist/mcp/tools-diagram.js +0 -97
- package/dist/mcp/tools-docs.js +0 -62
- package/dist/mcp/tools-export.js +0 -222
- package/dist/mcp/tools-marketplace.js +0 -132
- package/dist/mcp/tools-model.d.ts +0 -3
- package/dist/mcp/tools-model.js +0 -279
- package/dist/mcp/tools-pattern.d.ts +0 -3
- package/dist/mcp/tools-pattern.js +0 -604
- package/dist/mcp/tools-query.js +0 -377
- package/dist/mcp/tools-resources.js +0 -41
- package/dist/mcp/tools-template.js +0 -119
- package/dist/mcp/tools-workflow-run.d.ts +0 -40
- package/dist/mcp/tools-workflow-run.js +0 -150
- package/dist/mcp/types.js +0 -2
- package/dist/mcp/workflow-executor.js +0 -242
- package/dist/migration/registry.js +0 -29
- package/dist/node-types-generator.js +0 -139
- package/dist/parser/tag-registry.js +0 -57
- package/dist/parser.d.ts +0 -232
- package/dist/parser.js +0 -2500
- package/dist/plugin/PluginPanel.d.ts +0 -12
- package/dist/plugin/PluginPanel.js +0 -5
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +0 -14
- package/dist/plugin/types.d.ts +0 -75
- package/dist/plugin/types.js +0 -8
- package/dist/resolve-package-types.js +0 -123
- package/dist/runtime/CancellationError.js +0 -20
- package/dist/runtime/ExecutionContext.js +0 -280
- package/dist/runtime/builtin-functions.js +0 -549
- package/dist/runtime/checkpoint.d.ts +0 -84
- package/dist/runtime/checkpoint.js +0 -225
- package/dist/runtime/debug-controller.js +0 -247
- package/dist/runtime/events.js +0 -2
- package/dist/runtime/function-registry.js +0 -66
- package/dist/runtime/parameter-resolver.js +0 -113
- package/dist/server/types.js +0 -5
- package/dist/server/webhook-server.js +0 -269
- package/dist/server/workflow-registry.js +0 -202
- package/dist/shared-project.js +0 -28
- package/dist/sugar-optimizer.js +0 -498
- package/dist/testing/assertions.js +0 -127
- package/dist/testing/mock-approval.js +0 -98
- package/dist/testing/mock-llm.js +0 -119
- package/dist/testing/recorder.js +0 -70
- package/dist/testing/replayer.js +0 -143
- package/dist/testing/token-tracker.js +0 -94
- package/dist/type-checker.d.ts +0 -42
- package/dist/type-checker.js +0 -190
- package/dist/type-mappings.js +0 -131
- package/dist/types/branded-ports.d.ts +0 -151
- package/dist/types/branded-ports.js +0 -121
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -5
- package/dist/types.d.ts +0 -139
- package/dist/types.js +0 -15
- package/dist/utils/error-utils.js +0 -27
- package/dist/utils/lru-cache.js +0 -40
- package/dist/utils/port-ordering.js +0 -81
- package/dist/utils/port-tag-utils.js +0 -41
- package/dist/utils/string-distance.js +0 -56
- package/dist/validation/agent-detection.js +0 -115
- package/dist/validation/agent-rules.js +0 -262
- package/dist/validation/design-rules.js +0 -377
- package/dist/validation/rule-registry.js +0 -37
- package/dist/validator.d.ts +0 -113
- package/dist/validator.js +0 -1631
- package/docs/reference/patterns.md +0 -159
- package/scripts/postinstall.cjs +0 -86
- /package/dist/{shared-project.d.ts → parser/shared-project.d.ts} +0 -0
- /package/dist/{friendly-errors.d.ts → validation/friendly-errors.d.ts} +0 -0
|
@@ -1,1831 +0,0 @@
|
|
|
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, computeParallelLevels, 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. **Parallel Detection**: For async workflows, compute parallel levels from the CFG
|
|
48
|
-
* and group independent nodes at the same topological depth into Promise.all() blocks
|
|
49
|
-
* 6. **Code Generation**: For each node in execution order:
|
|
50
|
-
* - Regular nodes: Generate direct execution with variable storage
|
|
51
|
-
* - Parallel groups: Wrap 2+ independent nodes in `await Promise.all([...])`
|
|
52
|
-
* - Branching nodes: Generate if/else blocks for success/failure paths
|
|
53
|
-
* - Scoped children: Generate scope function closures (for forEach, etc.)
|
|
54
|
-
* - Pull nodes: Generate lazy executors registered with context
|
|
55
|
-
* 7. **Exit Node**: Collect outputs and return result object
|
|
56
|
-
*
|
|
57
|
-
* ## Key Concepts:
|
|
58
|
-
* - **Parallel Execution**: Independent async nodes at the same topological level run concurrently
|
|
59
|
-
* via Promise.all(). Sync workflows skip this since there's no event loop concurrency.
|
|
60
|
-
* - **Branching Nodes**: Nodes with both onSuccess and onFailure ports create conditional branches
|
|
61
|
-
* - **Per-Port Scoped Children**: Children of forEach-like nodes execute via closure functions
|
|
62
|
-
* - **Pull Execution**: Nodes marked for lazy evaluation only run when outputs are consumed
|
|
63
|
-
* - **Execution Index**: Each node execution gets a unique index for variable tracking
|
|
64
|
-
*
|
|
65
|
-
* ## Generated Code Structure:
|
|
66
|
-
* ```typescript
|
|
67
|
-
* const ctx = new GeneratedExecutionContext(isAsync, debugger?);
|
|
68
|
-
* const startIdx = ctx.addExecution('Start');
|
|
69
|
-
* ctx.setVariable({ id: 'Start', portName: 'param', ... }, value);
|
|
70
|
-
* // ... node executions in topological order ...
|
|
71
|
-
* const exitIdx = ctx.addExecution('Exit');
|
|
72
|
-
* return { onSuccess: ..., onFailure: ..., ...outputs };
|
|
73
|
-
* ```
|
|
74
|
-
*
|
|
75
|
-
* @param workflow - The workflow AST to generate code for
|
|
76
|
-
* @param nodeTypes - Available node type definitions (includes workflow's nodeTypes + imports)
|
|
77
|
-
* @param isAsync - Whether to generate async code (adds await, returns Promise)
|
|
78
|
-
* @param production - If true, omits debug instrumentation for smaller output
|
|
79
|
-
* @returns Generated TypeScript function body (without function signature)
|
|
80
|
-
*/
|
|
81
|
-
export function generateControlFlowWithExecutionContext(workflow, nodeTypes, isAsync, production = false, bundleMode = false) {
|
|
82
|
-
const lines = [];
|
|
83
|
-
// In development mode, use the injected debugger if available
|
|
84
|
-
if (!production) {
|
|
85
|
-
lines.push(` const __effectiveDebugger__ = typeof __flowWeaverDebugger__ !== 'undefined' ? __flowWeaverDebugger__ : undefined;`);
|
|
86
|
-
lines.push('');
|
|
87
|
-
}
|
|
88
|
-
// Recursion depth protection: prevent infinite recursion in workflows
|
|
89
|
-
lines.push(` // Recursion depth protection`);
|
|
90
|
-
lines.push(` const __rd__ = (params as { __rd__?: number }).__rd__ ?? 0;`);
|
|
91
|
-
lines.push(` if (__rd__ >= 1000) {`);
|
|
92
|
-
lines.push(` throw new Error('Max recursion depth exceeded (1000) in workflow "${workflow.functionName}"');`);
|
|
93
|
-
lines.push(` }`);
|
|
94
|
-
lines.push('');
|
|
95
|
-
// In development mode, pass the effective debugger (from parameter or environment)
|
|
96
|
-
// In production mode, omit the debugger parameter
|
|
97
|
-
// Always pass abort signal for cancellation support
|
|
98
|
-
// In dev mode, always treat as async so the debugger can pause execution
|
|
99
|
-
// at breakpoints (sendStatusChangedEvent must be awaited for this to work).
|
|
100
|
-
// Production mode respects the original isAsync to avoid overhead.
|
|
101
|
-
if (!production) {
|
|
102
|
-
isAsync = true; // eslint-disable-line no-param-reassign
|
|
103
|
-
}
|
|
104
|
-
const asyncArg = isAsync ? 'true' : 'false';
|
|
105
|
-
if (production) {
|
|
106
|
-
lines.push(` const ctx = new GeneratedExecutionContext(${asyncArg}, __abortSignal__);`);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
lines.push(` const ctx = new GeneratedExecutionContext(${asyncArg}, __effectiveDebugger__, __abortSignal__);`);
|
|
110
|
-
}
|
|
111
|
-
lines.push('');
|
|
112
|
-
// Debug controller: step-through debugging and checkpoint/resume
|
|
113
|
-
// In production mode, no controller is emitted. In dev mode, the controller
|
|
114
|
-
// resolves from globalThis (injected by executor) or falls back to a no-op.
|
|
115
|
-
if (!production) {
|
|
116
|
-
lines.push(` // Debug controller for step-through debugging and checkpoint/resume`);
|
|
117
|
-
lines.push(` const __ctrl__: TDebugController = (`);
|
|
118
|
-
lines.push(` typeof globalThis !== 'undefined' && (globalThis as unknown as { __fw_debug_controller__?: TDebugController }).__fw_debug_controller__`);
|
|
119
|
-
lines.push(` ? (globalThis as unknown as { __fw_debug_controller__?: TDebugController }).__fw_debug_controller__`);
|
|
120
|
-
lines.push(` : { beforeNode: () => true, afterNode: () => {} }`);
|
|
121
|
-
lines.push(` )!;`);
|
|
122
|
-
lines.push('');
|
|
123
|
-
}
|
|
124
|
-
lines.push(` const startIdx = ctx.addExecution('${RESERVED_NODE_NAMES.START}');`);
|
|
125
|
-
const awaitPrefixTop = isAsync ? 'await ' : '';
|
|
126
|
-
Object.keys(extractStartPorts(workflow)).forEach((portName) => {
|
|
127
|
-
const setCall = isAsync ? `await ctx.setVariable` : `ctx.setVariable`;
|
|
128
|
-
// STEP Port Architecture: execute comes from workflow parameter, data from params object
|
|
129
|
-
const valueSource = isExecutePort(portName) ? 'execute' : `params.${portName}`;
|
|
130
|
-
lines.push(` ${setCall}({ id: '${RESERVED_NODE_NAMES.START}', portName: '${portName}', executionIndex: startIdx, nodeTypeName: '${RESERVED_NODE_NAMES.START}' }, ${valueSource});`);
|
|
131
|
-
});
|
|
132
|
-
lines.push(` ${awaitPrefixTop}ctx.sendStatusChangedEvent({`);
|
|
133
|
-
lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.START}',`);
|
|
134
|
-
lines.push(` id: '${RESERVED_NODE_NAMES.START}',`);
|
|
135
|
-
lines.push(` executionIndex: startIdx,`);
|
|
136
|
-
lines.push(` status: 'SUCCEEDED',`);
|
|
137
|
-
lines.push(` });`);
|
|
138
|
-
lines.push('');
|
|
139
|
-
const cfg = buildControlFlowGraph(workflow, nodeTypes);
|
|
140
|
-
const executionOrder = performKahnsTopologicalSort(cfg); // Now returns instance IDs
|
|
141
|
-
const branchingNodes = findAllBranchingNodes(workflow, nodeTypes);
|
|
142
|
-
const allInstanceIds = new Set(workflow.instances.map((i) => i.id));
|
|
143
|
-
const branchRegions = new Map();
|
|
144
|
-
branchingNodes.forEach((branchInstanceId) => {
|
|
145
|
-
const successNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_SUCCESS, workflow, allInstanceIds, branchingNodes, nodeTypes);
|
|
146
|
-
const failureNodes = findNodesInBranch(branchInstanceId, RESERVED_PORT_NAMES.ON_FAILURE, workflow, allInstanceIds, branchingNodes, nodeTypes);
|
|
147
|
-
branchRegions.set(branchInstanceId, { successNodes, failureNodes });
|
|
148
|
-
});
|
|
149
|
-
// Determine which nodes are in conditional branches (need let declaration)
|
|
150
|
-
// Nodes in branches may not execute, so we need undefined checks for them
|
|
151
|
-
const nodesInBranches = new Set();
|
|
152
|
-
branchRegions.forEach((region) => {
|
|
153
|
-
region.successNodes.forEach((n) => nodesInBranches.add(n));
|
|
154
|
-
region.failureNodes.forEach((n) => nodesInBranches.add(n));
|
|
155
|
-
});
|
|
156
|
-
// Identify pull execution nodes (they also need let due to undefined check)
|
|
157
|
-
const pullExecutionNodes = new Set();
|
|
158
|
-
workflow.instances.forEach((instance) => {
|
|
159
|
-
// Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
|
|
160
|
-
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
161
|
-
if (nodeType) {
|
|
162
|
-
const pullConfig = getPullExecutionConfig(instance, nodeType);
|
|
163
|
-
if (pullConfig.enabled) {
|
|
164
|
-
pullExecutionNodes.add(instance.id);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
// Identify node-level scoped children (they need let because referenced outside scope block)
|
|
169
|
-
const nodeLevelScopedChildren = new Set();
|
|
170
|
-
workflow.instances.forEach((instance) => {
|
|
171
|
-
if (instance.parent && !isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
172
|
-
nodeLevelScopedChildren.add(instance.id);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
// Create execution index variables only for nodes that need undefined checking
|
|
176
|
-
// Skip per-port scoped children (they're in scope functions)
|
|
177
|
-
let hasLetDeclarations = false;
|
|
178
|
-
workflow.instances.forEach((instance) => {
|
|
179
|
-
if (!isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
180
|
-
// Nodes in branches, branching nodes, pull nodes, or scoped children need let
|
|
181
|
-
if (nodesInBranches.has(instance.id) ||
|
|
182
|
-
branchingNodes.has(instance.id) ||
|
|
183
|
-
pullExecutionNodes.has(instance.id) ||
|
|
184
|
-
nodeLevelScopedChildren.has(instance.id)) {
|
|
185
|
-
lines.push(` let ${toValidIdentifier(instance.id)}Idx: number | undefined;`);
|
|
186
|
-
hasLetDeclarations = true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
// Pre-declare _success flags for branching nodes that have downstream nodes.
|
|
191
|
-
// These flags must be at the function's top level because downstream guards
|
|
192
|
-
// (promoted nodes, chain guards) may reference them outside the branch block
|
|
193
|
-
// where the branching node is generated.
|
|
194
|
-
const topLevelSuccessFlags = new Set();
|
|
195
|
-
branchRegions.forEach((region, nodeId) => {
|
|
196
|
-
if (region.successNodes.size > 0 || region.failureNodes.size > 0) {
|
|
197
|
-
const safeId = toValidIdentifier(nodeId);
|
|
198
|
-
lines.push(` let ${safeId}_success = false;`);
|
|
199
|
-
topLevelSuccessFlags.add(safeId);
|
|
200
|
-
hasLetDeclarations = true;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
if (hasLetDeclarations) {
|
|
204
|
-
lines.push('');
|
|
205
|
-
}
|
|
206
|
-
const instancesInMultipleBranches = new Set();
|
|
207
|
-
allInstanceIds.forEach((instanceId) => {
|
|
208
|
-
let branchCount = 0;
|
|
209
|
-
branchRegions.forEach((region) => {
|
|
210
|
-
if (region.successNodes.has(instanceId) || region.failureNodes.has(instanceId)) {
|
|
211
|
-
branchCount++;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
if (branchCount > 1) {
|
|
215
|
-
instancesInMultipleBranches.add(instanceId);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
branchRegions.forEach((region, _branchNode) => {
|
|
219
|
-
instancesInMultipleBranches.forEach((instanceId) => {
|
|
220
|
-
region.successNodes.delete(instanceId);
|
|
221
|
-
region.failureNodes.delete(instanceId);
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
// Promote nodes that appear in BOTH success and failure regions of the same
|
|
225
|
-
// branching node. These nodes execute regardless of which branch is taken,
|
|
226
|
-
// so they must not be nested inside either branch (which would cause duplicate
|
|
227
|
-
// variable declarations and cancelled events for nodes that actually run).
|
|
228
|
-
const nodesInBothBranches = new Set();
|
|
229
|
-
branchRegions.forEach((region) => {
|
|
230
|
-
region.successNodes.forEach((nodeId) => {
|
|
231
|
-
if (region.failureNodes.has(nodeId)) {
|
|
232
|
-
nodesInBothBranches.add(nodeId);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
branchRegions.forEach((region) => {
|
|
237
|
-
nodesInBothBranches.forEach((nodeId) => {
|
|
238
|
-
region.successNodes.delete(nodeId);
|
|
239
|
-
region.failureNodes.delete(nodeId);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
// Promote nodes that have DATA dependencies on nodes outside their branch.
|
|
243
|
-
// Without this, STEP-nesting places the node before its data providers are generated.
|
|
244
|
-
const nodesPromotedFromBranches = new Set(nodesInBothBranches);
|
|
245
|
-
branchRegions.forEach((region, branchNodeId) => {
|
|
246
|
-
const allBranchNodes = new Set([...region.successNodes, ...region.failureNodes]);
|
|
247
|
-
allBranchNodes.forEach((nodeId) => {
|
|
248
|
-
const hasExternalDataDep = workflow.connections.some((conn) => {
|
|
249
|
-
if (conn.to.node !== nodeId)
|
|
250
|
-
return false;
|
|
251
|
-
if (conn.from.scope || conn.to.scope)
|
|
252
|
-
return false;
|
|
253
|
-
const fromNode = conn.from.node;
|
|
254
|
-
// Dependencies on branch parent or Start are fine (already generated)
|
|
255
|
-
if (fromNode === branchNodeId || isStartNode(fromNode))
|
|
256
|
-
return false;
|
|
257
|
-
// STEP connections (execute port) are handled by the guard, not data flow
|
|
258
|
-
if (isExecutePort(conn.to.port))
|
|
259
|
-
return false;
|
|
260
|
-
// External dep: source is NOT in the same branch
|
|
261
|
-
return !allBranchNodes.has(fromNode);
|
|
262
|
-
});
|
|
263
|
-
if (hasExternalDataDep) {
|
|
264
|
-
nodesPromotedFromBranches.add(nodeId);
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
// Remove promoted nodes from branch regions (they'll generate at top level)
|
|
269
|
-
branchRegions.forEach((region) => {
|
|
270
|
-
nodesPromotedFromBranches.forEach((nodeId) => {
|
|
271
|
-
region.successNodes.delete(nodeId);
|
|
272
|
-
region.failureNodes.delete(nodeId);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
// Identify branching nodes whose _success flag must be tracked because
|
|
276
|
-
// promoted nodes depend on their onSuccess/onFailure ports for STEP guards.
|
|
277
|
-
const branchingNodesNeedingSuccessFlag = new Set();
|
|
278
|
-
nodesPromotedFromBranches.forEach((promotedNodeId) => {
|
|
279
|
-
workflow.connections.forEach((conn) => {
|
|
280
|
-
if (conn.to.node === promotedNodeId && isExecutePort(conn.to.port)) {
|
|
281
|
-
const sourceNode = conn.from.node;
|
|
282
|
-
const sourcePort = conn.from.port;
|
|
283
|
-
if (branchingNodes.has(sourceNode) && (isSuccessPort(sourcePort) || isFailurePort(sourcePort))) {
|
|
284
|
-
branchingNodesNeedingSuccessFlag.add(sourceNode);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
// Detect sequential branching chains for flattening
|
|
290
|
-
const branchingChains = detectBranchingChains(branchingNodes, branchRegions);
|
|
291
|
-
const chainMembers = new Set();
|
|
292
|
-
branchingChains.forEach((chain) => {
|
|
293
|
-
// All non-head nodes are chain members (skip in main loop)
|
|
294
|
-
for (let i = 1; i < chain.length; i++) {
|
|
295
|
-
chainMembers.add(chain[i]);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
// Compute parallel levels for async workflows
|
|
299
|
-
const perPortScopedChildrenSet = new Set();
|
|
300
|
-
workflow.instances.forEach((instance) => {
|
|
301
|
-
if (isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
302
|
-
perPortScopedChildrenSet.add(instance.id);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
const parallelGroupOf = new Map();
|
|
306
|
-
if (isAsync) {
|
|
307
|
-
const parallelLevels = computeParallelLevels(cfg, branchingNodes, perPortScopedChildrenSet);
|
|
308
|
-
for (const group of parallelLevels) {
|
|
309
|
-
if (group.length < 2)
|
|
310
|
-
continue;
|
|
311
|
-
// Filter out nodes that can't be parallelized
|
|
312
|
-
const eligible = group.filter((id) => {
|
|
313
|
-
if (nodesInBranches.has(id))
|
|
314
|
-
return false;
|
|
315
|
-
if (pullExecutionNodes.has(id))
|
|
316
|
-
return false;
|
|
317
|
-
if (nodeLevelScopedChildren.has(id))
|
|
318
|
-
return false;
|
|
319
|
-
if (nodesPromotedFromBranches.has(id))
|
|
320
|
-
return false;
|
|
321
|
-
if (chainMembers.has(id))
|
|
322
|
-
return false;
|
|
323
|
-
if (branchingNodes.has(id))
|
|
324
|
-
return false;
|
|
325
|
-
return true;
|
|
326
|
-
});
|
|
327
|
-
if (eligible.length >= 2) {
|
|
328
|
-
for (const nodeId of eligible) {
|
|
329
|
-
parallelGroupOf.set(nodeId, eligible);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
// Pre-declare execution indices for parallel group nodes
|
|
335
|
-
if (parallelGroupOf.size > 0) {
|
|
336
|
-
const declared = new Set();
|
|
337
|
-
parallelGroupOf.forEach((_, instanceId) => {
|
|
338
|
-
if (declared.has(instanceId))
|
|
339
|
-
return;
|
|
340
|
-
declared.add(instanceId);
|
|
341
|
-
// Only declare if not already declared by earlier let declarations
|
|
342
|
-
if (!nodesInBranches.has(instanceId) &&
|
|
343
|
-
!branchingNodes.has(instanceId) &&
|
|
344
|
-
!pullExecutionNodes.has(instanceId) &&
|
|
345
|
-
!nodeLevelScopedChildren.has(instanceId)) {
|
|
346
|
-
lines.push(` let ${toValidIdentifier(instanceId)}Idx: number | undefined;`);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
lines.push('');
|
|
350
|
-
}
|
|
351
|
-
const generatedNodes = new Set();
|
|
352
|
-
const availableVars = new Map();
|
|
353
|
-
Object.keys(extractStartPorts(workflow)).forEach((portName) => {
|
|
354
|
-
availableVars.set(`${RESERVED_NODE_NAMES.START}.${portName}`, `params.${portName}`);
|
|
355
|
-
});
|
|
356
|
-
executionOrder.forEach((instanceId) => {
|
|
357
|
-
if (isStartNode(instanceId) || isExitNode(instanceId) || generatedNodes.has(instanceId)) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
// Find the instance and its node type
|
|
361
|
-
const instance = workflow.instances.find((i) => i.id === instanceId);
|
|
362
|
-
if (!instance) {
|
|
363
|
-
lines.push(` // Node '${instanceId}' skipped: instance not found in workflow`);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
// Skip per-port scoped children (they're in scope functions)
|
|
367
|
-
// Include node-level scoped children (they're in scope blocks)
|
|
368
|
-
if (isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
// Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
|
|
372
|
-
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
373
|
-
if (!nodeType) {
|
|
374
|
-
lines.push(` // Node '${instance.id}' skipped: type '${instance.nodeType}' not found`);
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
// Handle parallel groups: emit Promise.all when hitting first node of a group
|
|
378
|
-
if (parallelGroupOf.has(instanceId)) {
|
|
379
|
-
const group = parallelGroupOf.get(instanceId);
|
|
380
|
-
const ungeneratedGroup = group.filter((id) => !generatedNodes.has(id));
|
|
381
|
-
if (ungeneratedGroup.length >= 2) {
|
|
382
|
-
generateParallelGroupWithContext(ungeneratedGroup, workflow, nodeTypes, availableVars, lines, generatedNodes, ' ', isAsync, 'ctx', bundleMode, branchingNodes, production);
|
|
383
|
-
// Generate scoped children for each parallel node
|
|
384
|
-
for (const parallelNodeId of ungeneratedGroup) {
|
|
385
|
-
const inst = workflow.instances.find((i) => i.id === parallelNodeId);
|
|
386
|
-
if (!inst)
|
|
387
|
-
continue;
|
|
388
|
-
const nt = nodeTypes.find((n) => n.name === inst.nodeType || n.functionName === inst.nodeType);
|
|
389
|
-
if (!nt)
|
|
390
|
-
continue;
|
|
391
|
-
generateScopedChildrenExecution(inst, nt, workflow, nodeTypes, generatedNodes, availableVars, lines, ' ', branchingNodes, branchRegions, isAsync, bundleMode, production);
|
|
392
|
-
}
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
// else: degenerated to 1 or 0, fall through to sequential handling
|
|
396
|
-
}
|
|
397
|
-
if (branchingNodes.has(instanceId)) {
|
|
398
|
-
// Chain members are generated by their chain head — skip
|
|
399
|
-
if (chainMembers.has(instanceId)) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
// Chain heads: use flat chain generation
|
|
403
|
-
if (branchingChains.has(instanceId)) {
|
|
404
|
-
// For promoted chain heads, wrap in STEP guard
|
|
405
|
-
let chainIndent = ' ';
|
|
406
|
-
let chainNeedsClose = false;
|
|
407
|
-
if (nodesPromotedFromBranches.has(instanceId)) {
|
|
408
|
-
const stepSourceConditions = [];
|
|
409
|
-
workflow.connections.forEach((conn) => {
|
|
410
|
-
if (conn.to.node === instanceId && isExecutePort(conn.to.port)) {
|
|
411
|
-
const src = conn.from.node;
|
|
412
|
-
if (!isStartNode(src)) {
|
|
413
|
-
stepSourceConditions.push(buildStepSourceCondition(src, conn.from.port, branchingNodes));
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
if (stepSourceConditions.length > 0) {
|
|
418
|
-
const condition = stepSourceConditions.length === 1
|
|
419
|
-
? stepSourceConditions[0]
|
|
420
|
-
: stepSourceConditions.join(' || ');
|
|
421
|
-
lines.push(` if (${condition}) {`);
|
|
422
|
-
chainIndent = ' ';
|
|
423
|
-
chainNeedsClose = true;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
generateBranchingChainCode(branchingChains.get(instanceId), workflow, nodeTypes, branchingNodes, branchRegions, availableVars, generatedNodes, lines, chainIndent, isAsync, 'ctx', bundleMode, branchingNodesNeedingSuccessFlag, production, topLevelSuccessFlags);
|
|
427
|
-
if (chainNeedsClose) {
|
|
428
|
-
lines.push(` }`);
|
|
429
|
-
}
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
// Non-chain branching nodes: existing path
|
|
433
|
-
// For promoted branching nodes, wrap in STEP guard from execute port source
|
|
434
|
-
let branchIndent = ' ';
|
|
435
|
-
let branchNeedsClose = false;
|
|
436
|
-
// Pre-declare _success flag at the outer scope so downstream guards
|
|
437
|
-
// (which may run after the promoted guard block) can access it.
|
|
438
|
-
const nodeRegion = branchRegions.get(instanceId);
|
|
439
|
-
const promotedPreDeclared = new Set(topLevelSuccessFlags);
|
|
440
|
-
if (nodesPromotedFromBranches.has(instanceId)) {
|
|
441
|
-
const stepSourceConditions = [];
|
|
442
|
-
workflow.connections.forEach((conn) => {
|
|
443
|
-
if (conn.to.node === instanceId && isExecutePort(conn.to.port)) {
|
|
444
|
-
const src = conn.from.node;
|
|
445
|
-
if (!isStartNode(src)) {
|
|
446
|
-
stepSourceConditions.push(buildStepSourceCondition(src, conn.from.port, branchingNodes));
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
if (stepSourceConditions.length > 0) {
|
|
451
|
-
const condition = stepSourceConditions.length === 1
|
|
452
|
-
? stepSourceConditions[0]
|
|
453
|
-
: stepSourceConditions.join(' || ');
|
|
454
|
-
lines.push(` if (${condition}) {`);
|
|
455
|
-
branchIndent = ' ';
|
|
456
|
-
branchNeedsClose = true;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
generateBranchingNodeCode(instance, nodeType, workflow, nodeTypes, nodeRegion, availableVars, generatedNodes, lines, branchIndent, false, branchingNodes, branchRegions, isAsync, 'ctx', bundleMode, promotedPreDeclared, branchingNodesNeedingSuccessFlag.has(instanceId) || topLevelSuccessFlags.has(toValidIdentifier(instanceId)), production);
|
|
460
|
-
if (branchNeedsClose) {
|
|
461
|
-
lines.push(` }`);
|
|
462
|
-
}
|
|
463
|
-
const region = branchRegions.get(instanceId);
|
|
464
|
-
region.successNodes.forEach((n) => generatedNodes.add(n));
|
|
465
|
-
region.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
466
|
-
// Check if this node creates a scope and generate scoped children
|
|
467
|
-
generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, ' ', branchingNodes, branchRegions, isAsync, bundleMode, production);
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
const belongsToBranch = Array.from(branchRegions.values()).some((region) => region.successNodes.has(instanceId) || region.failureNodes.has(instanceId));
|
|
471
|
-
if (!belongsToBranch) {
|
|
472
|
-
const nodeUseConst = !nodesInBranches.has(instanceId) &&
|
|
473
|
-
!branchingNodes.has(instanceId) &&
|
|
474
|
-
!pullExecutionNodes.has(instanceId) &&
|
|
475
|
-
!nodeLevelScopedChildren.has(instanceId) &&
|
|
476
|
-
!parallelGroupOf.has(instanceId);
|
|
477
|
-
generateNodeCallWithContext(instance, nodeType, workflow, availableVars, lines, nodeTypes, ' ', isAsync, nodeUseConst, undefined, // instanceParent
|
|
478
|
-
'ctx', // ctxVar
|
|
479
|
-
bundleMode, false, // skipExecuteGuard
|
|
480
|
-
branchingNodes, // for port-aware STEP guards
|
|
481
|
-
production);
|
|
482
|
-
generatedNodes.add(instanceId);
|
|
483
|
-
// Check if this node creates a scope and generate scoped children
|
|
484
|
-
generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, ' ', branchingNodes, branchRegions, isAsync, bundleMode, production);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
lines.push(` const exitIdx = ctx.addExecution('${RESERVED_NODE_NAMES.EXIT}');`);
|
|
489
|
-
const exitConnections = workflow.connections.filter((conn) => isExitNode(conn.to.node));
|
|
490
|
-
// Group exit connections by port (multiple connections to the same port are coalesced)
|
|
491
|
-
const exitConnectionsByPort = new Map();
|
|
492
|
-
exitConnections.forEach((conn) => {
|
|
493
|
-
const existing = exitConnectionsByPort.get(conn.to.port) || [];
|
|
494
|
-
existing.push(conn);
|
|
495
|
-
exitConnectionsByPort.set(conn.to.port, existing);
|
|
496
|
-
});
|
|
497
|
-
const returnProps = [];
|
|
498
|
-
const awaitKeyword = isAsync ? 'await ' : '';
|
|
499
|
-
const setCall = isAsync ? 'await ctx.setVariable' : 'ctx.setVariable';
|
|
500
|
-
exitConnectionsByPort.forEach((conns, exitPort) => {
|
|
501
|
-
// Get exit port type for type casting - check if exit port is declared
|
|
502
|
-
const exitPortDef = workflow.exitPorts[exitPort];
|
|
503
|
-
// Skip connections to undeclared exit ports (typos like Exit.resultx when only @returns result exists)
|
|
504
|
-
if (!exitPortDef) {
|
|
505
|
-
lines.push(` // Exit connection skipped: '${exitPort}' is not a declared @returns port`);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
const varName = `exit_${exitPort}`;
|
|
509
|
-
const exitPortType = exitPortDef?.tsType || (exitPortDef ? mapToTypeScript(exitPortDef.dataType) : 'unknown');
|
|
510
|
-
// Filter to valid connections (skip undeclared nodes, missing types)
|
|
511
|
-
const validConns = conns.filter((conn) => {
|
|
512
|
-
const sourceNode = conn.from.node;
|
|
513
|
-
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
514
|
-
const sourceNodeType = nodeTypes.find((n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType);
|
|
515
|
-
if (!isStartNode(sourceNode) && !sourceInstance) {
|
|
516
|
-
lines.push(` // Exit connection skipped: source node '${sourceNode}' is not declared`);
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
|
-
if (!isStartNode(sourceNode) && sourceInstance && !sourceNodeType) {
|
|
520
|
-
lines.push(` // Exit connection skipped: source node '${sourceNode}' has missing type '${sourceInstance.nodeType}'`);
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
return true;
|
|
524
|
-
});
|
|
525
|
-
if (validConns.length === 0) {
|
|
526
|
-
lines.push(` const ${varName} = undefined as unknown;`);
|
|
527
|
-
returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
// Helper to build a value expression for a single connection
|
|
531
|
-
const buildSourceExpr = (conn, defaultValue) => {
|
|
532
|
-
const sourceNode = conn.from.node;
|
|
533
|
-
const sourcePort = conn.from.port;
|
|
534
|
-
const sourceIdx = isStartNode(sourceNode) ? 'startIdx' : `${toValidIdentifier(sourceNode)}Idx`;
|
|
535
|
-
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
536
|
-
const sourceNodeType = nodeTypes.find((n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType);
|
|
537
|
-
const pullConfig = sourceInstance && sourceNodeType
|
|
538
|
-
? getPullExecutionConfig(sourceInstance, sourceNodeType)
|
|
539
|
-
: { enabled: false, triggerPort: 'execute' };
|
|
540
|
-
const isPullNode = pullConfig.enabled;
|
|
541
|
-
// Parallel group nodes are NOT included here because they always execute —
|
|
542
|
-
// they sit outside branches, aren't lazy (pull), and aren't scoped children.
|
|
543
|
-
// The await Promise.all(...) guarantees their Idx variables are assigned
|
|
544
|
-
// before any downstream node reads them, so no undefined check is needed.
|
|
545
|
-
const needsUndefinedCheck = !isStartNode(sourceNode) &&
|
|
546
|
-
(nodesInBranches.has(sourceNode) ||
|
|
547
|
-
branchingNodes.has(sourceNode) ||
|
|
548
|
-
pullExecutionNodes.has(sourceNode) ||
|
|
549
|
-
nodeLevelScopedChildren.has(sourceNode));
|
|
550
|
-
if (isPullNode) {
|
|
551
|
-
return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx}!, nodeTypeName: '${sourceNode}' })`;
|
|
552
|
-
}
|
|
553
|
-
else if (needsUndefinedCheck) {
|
|
554
|
-
return `${sourceIdx} !== undefined ? ${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx} }) : ${defaultValue}`;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx} })`;
|
|
558
|
-
}
|
|
559
|
-
};
|
|
560
|
-
const isControlFlowPort = exitPort === 'onSuccess' || exitPort === 'onFailure';
|
|
561
|
-
const defaultValue = isControlFlowPort ? 'false' : 'undefined';
|
|
562
|
-
if (validConns.length === 1) {
|
|
563
|
-
// Single connection - straightforward assignment
|
|
564
|
-
lines.push(` const ${varName} = ${buildSourceExpr(validConns[0], defaultValue)};`);
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
// Multiple connections - coalesce with || (STEP ports) or ?? (data ports)
|
|
568
|
-
const operator = isControlFlowPort ? ' || ' : ' ?? ';
|
|
569
|
-
const parts = validConns.map((conn) => `(${buildSourceExpr(conn, defaultValue)})`);
|
|
570
|
-
lines.push(` const ${varName} = ${parts.join(operator)};`);
|
|
571
|
-
}
|
|
572
|
-
// Emit VARIABLE_SET for Exit node INPUT ports
|
|
573
|
-
if (!production) {
|
|
574
|
-
lines.push(` ${setCall}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: '${exitPort}', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, ${varName});`);
|
|
575
|
-
}
|
|
576
|
-
// Cast to the exit port's declared type for type safety
|
|
577
|
-
returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
|
|
578
|
-
});
|
|
579
|
-
// Add default undefined for declared exit ports that weren't connected
|
|
580
|
-
// (e.g., when connection had typo like Exit.resultx instead of Exit.result)
|
|
581
|
-
const connectedExitPorts = new Set(returnProps.map((prop) => prop.split(':')[0]));
|
|
582
|
-
Object.entries(workflow.exitPorts).forEach(([portName, portDef]) => {
|
|
583
|
-
if (!connectedExitPorts.has(portName) && portName !== 'onSuccess' && portName !== 'onFailure') {
|
|
584
|
-
const portType = portDef?.tsType || (portDef ? mapToTypeScript(portDef.dataType) : 'unknown');
|
|
585
|
-
lines.push(` // Exit port '${portName}' has no valid connection - using undefined`);
|
|
586
|
-
returnProps.push(`${portName}: undefined as unknown as ${portType}`);
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
lines.push('');
|
|
590
|
-
// Check if onSuccess/onFailure are explicitly connected
|
|
591
|
-
const hasOnSuccess = returnProps.some((prop) => prop.startsWith('onSuccess:'));
|
|
592
|
-
const hasOnFailure = returnProps.some((prop) => prop.startsWith('onFailure:'));
|
|
593
|
-
// Only add defaults if not explicitly connected
|
|
594
|
-
const defaults = [];
|
|
595
|
-
const setCallForDefaults = isAsync ? 'await ctx.setVariable' : 'ctx.setVariable';
|
|
596
|
-
if (!hasOnSuccess) {
|
|
597
|
-
defaults.push('onSuccess: true');
|
|
598
|
-
// Emit VARIABLE_SET for default onSuccess
|
|
599
|
-
if (!production) {
|
|
600
|
-
lines.push(` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onSuccess', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, true);`);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (!hasOnFailure) {
|
|
604
|
-
defaults.push('onFailure: false');
|
|
605
|
-
// Emit VARIABLE_SET for default onFailure
|
|
606
|
-
if (!production) {
|
|
607
|
-
lines.push(` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onFailure', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, false);`);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// Assemble final result with onSuccess first, onFailure second, then data ports.
|
|
611
|
-
// This ensures JSON.stringify output is readable and predictable.
|
|
612
|
-
const allPropsUnsorted = [...defaults, ...returnProps];
|
|
613
|
-
const onSuccessProp = allPropsUnsorted.find(p => p.trimStart().startsWith('onSuccess'));
|
|
614
|
-
const onFailureProp = allPropsUnsorted.find(p => p.trimStart().startsWith('onFailure'));
|
|
615
|
-
const dataProps = allPropsUnsorted.filter(p => {
|
|
616
|
-
const key = p.trimStart().split(':')[0].trim();
|
|
617
|
-
return key !== 'onSuccess' && key !== 'onFailure';
|
|
618
|
-
});
|
|
619
|
-
const orderedProps = [];
|
|
620
|
-
if (onSuccessProp)
|
|
621
|
-
orderedProps.push(onSuccessProp);
|
|
622
|
-
if (onFailureProp)
|
|
623
|
-
orderedProps.push(onFailureProp);
|
|
624
|
-
orderedProps.push(...dataProps);
|
|
625
|
-
const allProps = orderedProps.join(', ');
|
|
626
|
-
lines.push(` const finalResult = { ${allProps} };`);
|
|
627
|
-
lines.push('');
|
|
628
|
-
lines.push(` ${awaitPrefixTop}ctx.sendStatusChangedEvent({`);
|
|
629
|
-
lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}',`);
|
|
630
|
-
lines.push(` id: '${RESERVED_NODE_NAMES.EXIT}',`);
|
|
631
|
-
lines.push(` executionIndex: exitIdx,`);
|
|
632
|
-
lines.push(` status: 'SUCCEEDED',`);
|
|
633
|
-
lines.push(` });`);
|
|
634
|
-
lines.push(` ctx.sendWorkflowCompletedEvent({`);
|
|
635
|
-
lines.push(` executionIndex: exitIdx,`);
|
|
636
|
-
lines.push(` status: 'SUCCEEDED',`);
|
|
637
|
-
lines.push(` result: finalResult,`);
|
|
638
|
-
lines.push(` });`);
|
|
639
|
-
lines.push('');
|
|
640
|
-
lines.push(` return finalResult;`);
|
|
641
|
-
return lines.join('\n');
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Helper function to generate scoped children execution for nodes that create scopes
|
|
645
|
-
*/
|
|
646
|
-
function generateScopedChildrenExecution(parentInstance, parentNodeType, workflow, allNodeTypes, generatedNodes, availableVars, lines, indent, branchingNodes, branchRegions, isAsync, bundleMode = false, production = false) {
|
|
647
|
-
// Check if this node creates a scope
|
|
648
|
-
if (!parentNodeType.scope)
|
|
649
|
-
return;
|
|
650
|
-
// Check if this is a per-port scope (has scoped OUTPUT ports)
|
|
651
|
-
// Per-port scopes have children handled inside the scope function closure generated by
|
|
652
|
-
// buildNodeArgumentsWithContext -> generateScopeFunctionClosure, so we skip here
|
|
653
|
-
const rawScopeName = parentNodeType.scope;
|
|
654
|
-
const hasPerPortScope = Object.values(parentNodeType.outputs).some((portDef) => portDef.scope === rawScopeName);
|
|
655
|
-
if (hasPerPortScope) {
|
|
656
|
-
// Children are already generated inside the scope function closure
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const scopeName = `${parentInstance.id}.${parentNodeType.scope}`;
|
|
660
|
-
// Find children in this scope from workflow.scopes or instance.parentScope
|
|
661
|
-
const childrenInScope = [];
|
|
662
|
-
// Check workflow.scopes mapping
|
|
663
|
-
if (workflow.scopes && workflow.scopes[scopeName]) {
|
|
664
|
-
childrenInScope.push(...workflow.scopes[scopeName]);
|
|
665
|
-
}
|
|
666
|
-
// Also check instances with parent field
|
|
667
|
-
workflow.instances.forEach((instance) => {
|
|
668
|
-
const parentScopeName = instance.parent
|
|
669
|
-
? `${instance.parent.id}.${instance.parent.scope}`
|
|
670
|
-
: null;
|
|
671
|
-
if (parentScopeName === scopeName && !childrenInScope.includes(instance.id)) {
|
|
672
|
-
childrenInScope.push(instance.id);
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
if (childrenInScope.length === 0)
|
|
676
|
-
return;
|
|
677
|
-
// Generate scope creation
|
|
678
|
-
const safeParentId = toValidIdentifier(parentInstance.id);
|
|
679
|
-
lines.push(``);
|
|
680
|
-
lines.push(`${indent}// Create scope for children of ${parentInstance.id}`);
|
|
681
|
-
lines.push(`${indent}const ${safeParentId}_scopedCtx = ctx.createScope('${parentInstance.id}', ${safeParentId}Idx, '${parentNodeType.scope}');`);
|
|
682
|
-
lines.push(``);
|
|
683
|
-
// Scoped context variable name for child generators
|
|
684
|
-
const scopedCtxVar = `${safeParentId}_scopedCtx`;
|
|
685
|
-
// Generate child nodes execution using scoped context
|
|
686
|
-
childrenInScope.forEach((childInstanceId) => {
|
|
687
|
-
if (generatedNodes.has(childInstanceId))
|
|
688
|
-
return;
|
|
689
|
-
const childInstance = workflow.instances.find((i) => i.id === childInstanceId);
|
|
690
|
-
if (!childInstance)
|
|
691
|
-
return;
|
|
692
|
-
// Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
|
|
693
|
-
const childNodeType = allNodeTypes.find((nt) => nt.name === childInstance.nodeType || nt.functionName === childInstance.nodeType);
|
|
694
|
-
if (!childNodeType)
|
|
695
|
-
return;
|
|
696
|
-
// Check if this child is a branching node
|
|
697
|
-
if (branchingNodes.has(childInstanceId)) {
|
|
698
|
-
generateBranchingNodeCode(childInstance, childNodeType, workflow, allNodeTypes, branchRegions.get(childInstanceId), availableVars, generatedNodes, lines, indent, false, branchingNodes, branchRegions, isAsync, scopedCtxVar, // Pass scoped context name
|
|
699
|
-
bundleMode, new Set(), false, production);
|
|
700
|
-
const region = branchRegions.get(childInstanceId);
|
|
701
|
-
region.successNodes.forEach((n) => generatedNodes.add(n));
|
|
702
|
-
region.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
generateNodeCallWithContext(childInstance, childNodeType, workflow, availableVars, lines, allNodeTypes, indent, isAsync, false, // useConst = false - scoped children need let (referenced outside scope block)
|
|
706
|
-
parentInstance.id, // instanceParent - parent node is const, no ! needed when referencing it
|
|
707
|
-
scopedCtxVar, // Pass scoped context name
|
|
708
|
-
bundleMode, false, // skipExecuteGuard
|
|
709
|
-
new Set(), // branchingNodes
|
|
710
|
-
production);
|
|
711
|
-
}
|
|
712
|
-
generatedNodes.add(childInstanceId);
|
|
713
|
-
});
|
|
714
|
-
lines.push(``);
|
|
715
|
-
lines.push(`${indent}// Merge scope back into parent context`);
|
|
716
|
-
lines.push(`${indent}ctx.mergeScope(${parentInstance.id}_scopedCtx);`);
|
|
717
|
-
lines.push(``);
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* Generate a Promise.all block for 2+ parallel nodes in the unified generator.
|
|
721
|
-
*
|
|
722
|
-
* Each node's execution code is wrapped in an async IIFE inside Promise.all.
|
|
723
|
-
* The outer `let` variables for execution indices are assigned inside the IIFEs.
|
|
724
|
-
*/
|
|
725
|
-
function generateParallelGroupWithContext(nodeIds, workflow, nodeTypes, availableVars, lines, generatedNodes, indent, isAsync, ctxVar, bundleMode, branchingNodes, production = false) {
|
|
726
|
-
// Collect code buffers for each node
|
|
727
|
-
const nodeBuffers = [];
|
|
728
|
-
for (const nodeId of nodeIds) {
|
|
729
|
-
const instance = workflow.instances.find((i) => i.id === nodeId);
|
|
730
|
-
if (!instance)
|
|
731
|
-
continue;
|
|
732
|
-
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
733
|
-
if (!nodeType)
|
|
734
|
-
continue;
|
|
735
|
-
const nodeLines = [];
|
|
736
|
-
generateNodeCallWithContext(instance, nodeType, workflow, availableVars, nodeLines, nodeTypes, `${indent} `, // indent for inside the async IIFE
|
|
737
|
-
isAsync, false, // useConst = false — outer let declarations
|
|
738
|
-
undefined, ctxVar, bundleMode, false, branchingNodes, production);
|
|
739
|
-
nodeBuffers.push({ id: nodeId, lines: nodeLines });
|
|
740
|
-
}
|
|
741
|
-
// Fallback: if only 0-1 nodes remain, emit directly without Promise.all
|
|
742
|
-
if (nodeBuffers.length < 2) {
|
|
743
|
-
for (const buf of nodeBuffers) {
|
|
744
|
-
for (const line of buf.lines) {
|
|
745
|
-
lines.push(line);
|
|
746
|
-
}
|
|
747
|
-
generatedNodes.add(buf.id);
|
|
748
|
-
}
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
lines.push(`${indent}await Promise.all([`);
|
|
752
|
-
for (let i = 0; i < nodeBuffers.length; i++) {
|
|
753
|
-
const buf = nodeBuffers[i];
|
|
754
|
-
const comma = i < nodeBuffers.length - 1 ? ',' : '';
|
|
755
|
-
lines.push(`${indent} (async () => {`);
|
|
756
|
-
for (const line of buf.lines) {
|
|
757
|
-
lines.push(line);
|
|
758
|
-
}
|
|
759
|
-
lines.push(`${indent} })()${comma}`);
|
|
760
|
-
generatedNodes.add(buf.id);
|
|
761
|
-
}
|
|
762
|
-
lines.push(`${indent}]);`);
|
|
763
|
-
lines.push('');
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Sort branch nodes topologically based on their dependencies
|
|
767
|
-
*
|
|
768
|
-
* This ensures nodes within a branch execute in the correct order:
|
|
769
|
-
* dependencies before dependents.
|
|
770
|
-
*
|
|
771
|
-
* @param nodeIds - Set of node IDs within the branch
|
|
772
|
-
* @param workflow - The workflow AST
|
|
773
|
-
* @returns Array of node IDs in topologically sorted order
|
|
774
|
-
*/
|
|
775
|
-
function sortBranchNodesTopologically(nodeIds, workflow) {
|
|
776
|
-
if (nodeIds.size === 0) {
|
|
777
|
-
return [];
|
|
778
|
-
}
|
|
779
|
-
// Build a mini control flow graph for just these nodes
|
|
780
|
-
const graph = new Map();
|
|
781
|
-
const inDegree = new Map();
|
|
782
|
-
// Initialize
|
|
783
|
-
nodeIds.forEach((id) => {
|
|
784
|
-
graph.set(id, []);
|
|
785
|
-
inDegree.set(id, 0);
|
|
786
|
-
});
|
|
787
|
-
// Add edges based on data flow connections within the branch
|
|
788
|
-
workflow.connections.forEach((conn) => {
|
|
789
|
-
const fromNode = conn.from.node;
|
|
790
|
-
const toNode = conn.to.node;
|
|
791
|
-
// Only consider connections within this branch
|
|
792
|
-
if (nodeIds.has(fromNode) && nodeIds.has(toNode)) {
|
|
793
|
-
const successors = graph.get(fromNode) || [];
|
|
794
|
-
if (!successors.includes(toNode)) {
|
|
795
|
-
successors.push(toNode);
|
|
796
|
-
graph.set(fromNode, successors);
|
|
797
|
-
inDegree.set(toNode, (inDegree.get(toNode) || 0) + 1);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
// Perform Kahn's topological sort
|
|
802
|
-
const result = [];
|
|
803
|
-
const queue = [];
|
|
804
|
-
// Start with nodes that have no dependencies within the branch
|
|
805
|
-
inDegree.forEach((degree, node) => {
|
|
806
|
-
if (degree === 0) {
|
|
807
|
-
queue.push(node);
|
|
808
|
-
}
|
|
809
|
-
});
|
|
810
|
-
while (queue.length > 0) {
|
|
811
|
-
const node = queue.shift();
|
|
812
|
-
result.push(node);
|
|
813
|
-
const successors = graph.get(node) || [];
|
|
814
|
-
successors.forEach((successor) => {
|
|
815
|
-
const newDegree = (inDegree.get(successor) || 0) - 1;
|
|
816
|
-
inDegree.set(successor, newDegree);
|
|
817
|
-
if (newDegree === 0) {
|
|
818
|
-
queue.push(successor);
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
// If we haven't processed all nodes, there's a cycle (shouldn't happen after validation)
|
|
823
|
-
if (result.length !== nodeIds.size) {
|
|
824
|
-
// Fallback: add remaining nodes in arbitrary order
|
|
825
|
-
nodeIds.forEach((id) => {
|
|
826
|
-
if (!result.includes(id)) {
|
|
827
|
-
result.push(id);
|
|
828
|
-
}
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
return result;
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Generates CANCELLED status events for all nodes in a branch that won't be executed.
|
|
835
|
-
* This is called when a branching node decides to take one path, marking nodes in
|
|
836
|
-
* the non-taken path as CANCELLED. We must add an execution index first to have a
|
|
837
|
-
* valid reference for the event.
|
|
838
|
-
*/
|
|
839
|
-
function generateCancelledEventsForBranch(nodeIds, workflow, allNodeTypes, lines, indent, ctxVar = 'ctx', // Context variable name (for scoped contexts)
|
|
840
|
-
isAsync = false) {
|
|
841
|
-
const awaitPrefix = isAsync ? 'await ' : '';
|
|
842
|
-
nodeIds.forEach((instanceId) => {
|
|
843
|
-
const instance = workflow.instances.find((i) => i.id === instanceId);
|
|
844
|
-
if (!instance)
|
|
845
|
-
return;
|
|
846
|
-
const safeId = toValidIdentifier(instanceId);
|
|
847
|
-
// Use const (block-scoped) intentionally — the outer `let ${safeId}Idx`
|
|
848
|
-
// stays undefined, which signals to downstream guards that this node was
|
|
849
|
-
// CANCELLED and its data ports should not be read.
|
|
850
|
-
lines.push(`${indent}const ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
851
|
-
// Set STEP port variables so downstream nodes reading onSuccess/onFailure
|
|
852
|
-
// from this cancelled node don't crash with "Variable not found".
|
|
853
|
-
lines.push(`${indent}${awaitPrefix}${ctxVar}.setVariable({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${instance.nodeType}' }, false);`);
|
|
854
|
-
lines.push(`${indent}${awaitPrefix}${ctxVar}.setVariable({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${instance.nodeType}' }, false);`);
|
|
855
|
-
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
856
|
-
lines.push(`${indent} nodeTypeName: '${instance.nodeType}',`);
|
|
857
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
858
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
859
|
-
lines.push(`${indent} status: 'CANCELLED',`);
|
|
860
|
-
lines.push(`${indent}});`);
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
/**
|
|
864
|
-
* Build a guard condition for a STEP connection source.
|
|
865
|
-
* For branching nodes whose onSuccess/onFailure port is the source,
|
|
866
|
-
* use the _success flag to guard the correct branch path.
|
|
867
|
-
* For other sources, use Idx !== undefined.
|
|
868
|
-
*/
|
|
869
|
-
function buildStepSourceCondition(sourceNode, sourcePort, branchingNodes) {
|
|
870
|
-
const safeNode = toValidIdentifier(sourceNode);
|
|
871
|
-
if (branchingNodes.has(sourceNode)) {
|
|
872
|
-
if (isSuccessPort(sourcePort)) {
|
|
873
|
-
return `${safeNode}_success`;
|
|
874
|
-
}
|
|
875
|
-
if (isFailurePort(sourcePort)) {
|
|
876
|
-
return `${safeNode}_success === false`;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
return `${safeNode}Idx !== undefined`;
|
|
880
|
-
}
|
|
881
|
-
/**
|
|
882
|
-
* Generate flat code for a sequential chain of branching nodes.
|
|
883
|
-
*
|
|
884
|
-
* Instead of nesting each subsequent branching node inside the previous one's
|
|
885
|
-
* success/failure branch (O(N) depth), this generates them sequentially with
|
|
886
|
-
* accumulated guard conditions (O(1) depth).
|
|
887
|
-
*
|
|
888
|
-
* For chain [A, B, C]:
|
|
889
|
-
* A code (no guard)
|
|
890
|
-
* if (A_success) { B code } else { CANCELLED for B,C and regions }
|
|
891
|
-
* if (A_success && B_success) { C code } else { CANCELLED for C and regions }
|
|
892
|
-
*/
|
|
893
|
-
function generateBranchingChainCode(chain, workflow, nodeTypes, branchingNodes, branchRegions, availableVars, generatedNodes, lines, indent, isAsync, ctxVar, bundleMode, forceTrackSuccessNodes = new Set(), production = false, alreadyDeclaredFlags = new Set()) {
|
|
894
|
-
// Pre-declare success flags for all non-last chain nodes so they're
|
|
895
|
-
// accessible across guard blocks (avoiding let-in-block scoping issues).
|
|
896
|
-
// Also pre-declare for the last node if promoted nodes depend on its _success flag.
|
|
897
|
-
// Skip flags already declared at a higher scope (e.g. function top-level).
|
|
898
|
-
const preDeclaredFlags = new Set(alreadyDeclaredFlags);
|
|
899
|
-
for (let i = 0; i < chain.length; i++) {
|
|
900
|
-
const isLast = i === chain.length - 1;
|
|
901
|
-
const safeId = toValidIdentifier(chain[i]);
|
|
902
|
-
if (!isLast || forceTrackSuccessNodes.has(chain[i]) || alreadyDeclaredFlags.has(safeId)) {
|
|
903
|
-
if (!alreadyDeclaredFlags.has(safeId)) {
|
|
904
|
-
lines.push(`${indent}let ${safeId}_success = false;`);
|
|
905
|
-
}
|
|
906
|
-
preDeclaredFlags.add(safeId);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (chain.length > 1) {
|
|
910
|
-
lines.push('');
|
|
911
|
-
}
|
|
912
|
-
const guardParts = [];
|
|
913
|
-
for (let i = 0; i < chain.length; i++) {
|
|
914
|
-
const nodeId = chain[i];
|
|
915
|
-
const isLast = i === chain.length - 1;
|
|
916
|
-
const instance = workflow.instances.find((inst) => inst.id === nodeId);
|
|
917
|
-
if (!instance)
|
|
918
|
-
continue;
|
|
919
|
-
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
920
|
-
if (!nodeType)
|
|
921
|
-
continue;
|
|
922
|
-
const safeId = toValidIdentifier(nodeId);
|
|
923
|
-
const originalRegion = branchRegions.get(nodeId);
|
|
924
|
-
// For non-last nodes, create modified region with chain successor removed
|
|
925
|
-
// so generateBranchingNodeCode won't recurse into the next chain node
|
|
926
|
-
let effectiveRegion = originalRegion;
|
|
927
|
-
if (!isLast) {
|
|
928
|
-
const nextNodeId = chain[i + 1];
|
|
929
|
-
effectiveRegion = {
|
|
930
|
-
successNodes: new Set([...originalRegion.successNodes].filter((n) => n !== nextNodeId)),
|
|
931
|
-
failureNodes: new Set([...originalRegion.failureNodes].filter((n) => n !== nextNodeId)),
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
const hasGuard = guardParts.length > 0;
|
|
935
|
-
const guardCondition = guardParts.join(' && ');
|
|
936
|
-
if (hasGuard) {
|
|
937
|
-
lines.push(`${indent}if (${guardCondition}) {`);
|
|
938
|
-
}
|
|
939
|
-
const nodeIndent = hasGuard ? indent + ' ' : indent;
|
|
940
|
-
generateBranchingNodeCode(instance, nodeType, workflow, nodeTypes, effectiveRegion, availableVars, generatedNodes, lines, nodeIndent, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode, preDeclaredFlags, !isLast || forceTrackSuccessNodes.has(chain[i]) || alreadyDeclaredFlags.has(safeId), // forceTrackSuccess for non-last chain nodes, nodes with promoted dependents, or nodes with pre-declared flags
|
|
941
|
-
production);
|
|
942
|
-
// Generate scoped children for this chain node
|
|
943
|
-
generateScopedChildrenExecution(instance, nodeType, workflow, nodeTypes, generatedNodes, availableVars, lines, nodeIndent, branchingNodes, branchRegions, isAsync, bundleMode, production);
|
|
944
|
-
if (hasGuard) {
|
|
945
|
-
lines.push(`${indent}} else {`);
|
|
946
|
-
// Emit CANCELLED for this node and all remaining chain nodes + their regions
|
|
947
|
-
const remainingNodes = new Set();
|
|
948
|
-
for (let j = i; j < chain.length; j++) {
|
|
949
|
-
const chainNodeId = chain[j];
|
|
950
|
-
remainingNodes.add(chainNodeId);
|
|
951
|
-
const region = branchRegions.get(chainNodeId);
|
|
952
|
-
region.successNodes.forEach((n) => remainingNodes.add(n));
|
|
953
|
-
region.failureNodes.forEach((n) => remainingNodes.add(n));
|
|
954
|
-
}
|
|
955
|
-
generateCancelledEventsForBranch(remainingNodes, workflow, nodeTypes, lines, indent + ' ', ctxVar, isAsync);
|
|
956
|
-
lines.push(`${indent}}`);
|
|
957
|
-
}
|
|
958
|
-
// Add success condition for next iteration's guard
|
|
959
|
-
if (!isLast) {
|
|
960
|
-
guardParts.push(`${safeId}_success`);
|
|
961
|
-
}
|
|
962
|
-
// Mark node and its effective region as generated
|
|
963
|
-
generatedNodes.add(nodeId);
|
|
964
|
-
effectiveRegion.successNodes.forEach((n) => generatedNodes.add(n));
|
|
965
|
-
effectiveRegion.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
function generateBranchingNodeCode(instance, branchNode, workflow, allNodeTypes, region, availableVars, generatedNodes, lines, indent, _generateReturns = true, // DEPRECATED: always false, kept for signature compat
|
|
969
|
-
branchingNodes, branchRegions, isAsync, ctxVar = 'ctx', // Context variable name (for scoped contexts)
|
|
970
|
-
bundleMode = false, preDeclaredSuccessFlags = new Set(), forceTrackSuccess = false, production = false) {
|
|
971
|
-
const instanceId = instance.id;
|
|
972
|
-
const safeId = toValidIdentifier(instanceId);
|
|
973
|
-
const functionName = branchNode.functionName;
|
|
974
|
-
// Debug controller: beforeNode hook for branching nodes
|
|
975
|
-
const emitDebugHooks = !production;
|
|
976
|
-
const outerIndent = indent;
|
|
977
|
-
// Only declare success flag if there are downstream nodes
|
|
978
|
-
const hasSuccessDownstream = region.successNodes.size > 0;
|
|
979
|
-
const hasFailureDownstream = region.failureNodes.size > 0;
|
|
980
|
-
const hasDownstream = hasSuccessDownstream || hasFailureDownstream;
|
|
981
|
-
// Track success flag when there are downstream nodes OR when chain code needs it
|
|
982
|
-
const trackSuccess = hasDownstream || forceTrackSuccess;
|
|
983
|
-
if (emitDebugHooks) {
|
|
984
|
-
// Hoist success flag declaration before the if block so it remains in scope
|
|
985
|
-
// for downstream branching code that runs after the if/else.
|
|
986
|
-
if (trackSuccess && !preDeclaredSuccessFlags.has(safeId)) {
|
|
987
|
-
lines.push(`${indent}let ${safeId}_success = false;`);
|
|
988
|
-
}
|
|
989
|
-
const awaitHook = isAsync ? 'await ' : '';
|
|
990
|
-
lines.push(`${indent}if (${awaitHook}__ctrl__.beforeNode('${instanceId}', ${ctxVar})) {`);
|
|
991
|
-
indent = `${indent} `;
|
|
992
|
-
}
|
|
993
|
-
const awaitPrefix = isAsync ? 'await ' : '';
|
|
994
|
-
if (!production) {
|
|
995
|
-
lines.push('');
|
|
996
|
-
lines.push(`${indent}// ── ${instanceId} (${functionName}) ──`);
|
|
997
|
-
}
|
|
998
|
-
lines.push(`${indent}${ctxVar}.checkAborted('${instanceId}');`);
|
|
999
|
-
lines.push(`${indent}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1000
|
-
lines.push(`${indent}if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${instanceId}';`);
|
|
1001
|
-
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1002
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1003
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1004
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1005
|
-
lines.push(`${indent} status: 'RUNNING',`);
|
|
1006
|
-
lines.push(`${indent}});`);
|
|
1007
|
-
lines.push('');
|
|
1008
|
-
if (trackSuccess) {
|
|
1009
|
-
if (preDeclaredSuccessFlags.has(safeId) || emitDebugHooks) {
|
|
1010
|
-
// Flag was pre-declared (by chain code or hoisted for debug hooks) — assignment only
|
|
1011
|
-
lines.push(`${indent}${safeId}_success = false;`);
|
|
1012
|
-
}
|
|
1013
|
-
else {
|
|
1014
|
-
lines.push(`${indent}let ${safeId}_success = false;`);
|
|
1015
|
-
}
|
|
1016
|
-
lines.push('');
|
|
1017
|
-
}
|
|
1018
|
-
lines.push(`${indent}try {`);
|
|
1019
|
-
const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1020
|
-
const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1021
|
-
const argNames = buildNodeArgumentsWithContext({
|
|
1022
|
-
node: branchNode,
|
|
1023
|
-
workflow,
|
|
1024
|
-
id: instanceId,
|
|
1025
|
-
lines,
|
|
1026
|
-
indent: `${indent} `,
|
|
1027
|
-
getCall,
|
|
1028
|
-
isAsync,
|
|
1029
|
-
emitInputEvents: true,
|
|
1030
|
-
setCall,
|
|
1031
|
-
nodeTypeName: functionName,
|
|
1032
|
-
bundleMode,
|
|
1033
|
-
production,
|
|
1034
|
-
});
|
|
1035
|
-
const awaitKeyword = branchNode.isAsync ? 'await ' : '';
|
|
1036
|
-
if (branchNode.expression) {
|
|
1037
|
-
// Expression branching node: call without execute, auto-set onSuccess/onFailure
|
|
1038
|
-
lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
|
|
1039
|
-
// Determine data output ports (exclude control flow and scoped ports)
|
|
1040
|
-
const dataOutputPorts = Object.keys(branchNode.outputs).filter((portName) => {
|
|
1041
|
-
const portConfig = branchNode.outputs[portName];
|
|
1042
|
-
if (portConfig.scope)
|
|
1043
|
-
return false;
|
|
1044
|
-
if (isSuccessPort(portName) || isFailurePort(portName))
|
|
1045
|
-
return false;
|
|
1046
|
-
if (portConfig.isControlFlow || portConfig.failure)
|
|
1047
|
-
return false;
|
|
1048
|
-
return true;
|
|
1049
|
-
});
|
|
1050
|
-
if (dataOutputPorts.length === 1) {
|
|
1051
|
-
// Single data output: destructure if result is an object with the port key, else use raw value
|
|
1052
|
-
// Extract to unknown-typed variable to prevent TypeScript from narrowing
|
|
1053
|
-
// specific return types (e.g. boolean) to `never` in the typeof check
|
|
1054
|
-
const portName = dataOutputPorts[0];
|
|
1055
|
-
const rawVar = `${safeId}Result_raw`;
|
|
1056
|
-
lines.push(`${indent} const ${rawVar}: unknown = ${safeId}Result;`);
|
|
1057
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, typeof ${rawVar} === 'object' && ${rawVar} !== null && '${portName}' in ${rawVar} ? ${rawVar}.${portName} : ${rawVar});`);
|
|
1058
|
-
}
|
|
1059
|
-
else {
|
|
1060
|
-
// Multiple data outputs: destructure from object return
|
|
1061
|
-
dataOutputPorts.forEach((portName) => {
|
|
1062
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
// Auto-set onSuccess/onFailure
|
|
1066
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
|
|
1067
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
|
|
1068
|
-
}
|
|
1069
|
-
else if (branchNode.variant === 'MAP_ITERATOR') {
|
|
1070
|
-
// MAP_ITERATOR: inline iteration — no user function to call
|
|
1071
|
-
// argNames: [execute, items, scopeFn]
|
|
1072
|
-
const executeArg = argNames[0];
|
|
1073
|
-
const itemsArg = argNames[1];
|
|
1074
|
-
const scopeFnArg = argNames[2];
|
|
1075
|
-
lines.push(`${indent} let ${safeId}Result: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
|
|
1076
|
-
lines.push(`${indent} if (!${executeArg}) {`);
|
|
1077
|
-
lines.push(`${indent} ${safeId}Result = { onSuccess: false, onFailure: false, results: [] };`);
|
|
1078
|
-
lines.push(`${indent} } else {`);
|
|
1079
|
-
lines.push(`${indent} const __results: unknown[] = [];`);
|
|
1080
|
-
lines.push(`${indent} for (const __item of ${itemsArg}) {`);
|
|
1081
|
-
lines.push(`${indent} __results.push((${isAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
|
|
1082
|
-
lines.push(`${indent} }`);
|
|
1083
|
-
lines.push(`${indent} ${safeId}Result = { onSuccess: true, onFailure: false, results: __results };`);
|
|
1084
|
-
lines.push(`${indent} }`);
|
|
1085
|
-
// Set output ports from result
|
|
1086
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1087
|
-
const portConfig = branchNode.outputs[portName];
|
|
1088
|
-
if (portConfig.scope)
|
|
1089
|
-
return;
|
|
1090
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
else if (branchNode.variant === 'IMPORTED_WORKFLOW' || branchNode.variant === 'WORKFLOW') {
|
|
1094
|
-
// Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
|
|
1095
|
-
// Workflows use (execute, params) signature where params is an object
|
|
1096
|
-
// Regular nodes use (execute, arg1, arg2, ...) positional signature
|
|
1097
|
-
// For workflow calls, wrap data args in an object and include recursion depth
|
|
1098
|
-
const executeArg = argNames[0]; // First arg is always execute
|
|
1099
|
-
const dataArgs = argNames.slice(1); // Rest are data inputs
|
|
1100
|
-
const inputPortNames = Object.keys(branchNode.inputs).filter((p) => !isExecutePort(p));
|
|
1101
|
-
// Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
|
|
1102
|
-
// Assign to variable first to avoid TypeScript excess property checking on object literals
|
|
1103
|
-
const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
|
|
1104
|
-
paramsEntries.push('__rd__: __rd__ + 1');
|
|
1105
|
-
const paramsObj = `{ ${paramsEntries.join(', ')} }`;
|
|
1106
|
-
const paramsVar = `__${safeId}Params__`;
|
|
1107
|
-
lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
|
|
1108
|
-
lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
|
|
1109
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1110
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1111
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1112
|
-
const portConfig = branchNode.outputs[portName];
|
|
1113
|
-
if (portConfig.scope)
|
|
1114
|
-
return;
|
|
1115
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
else if (branchNode.scope || (branchNode.scopes && branchNode.scopes.length > 0)) {
|
|
1119
|
-
// Scoped node call with positional arguments (uses _impl signature)
|
|
1120
|
-
// Scoped nodes have callback parameters that can't be passed via params object
|
|
1121
|
-
lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
|
|
1122
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1123
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1124
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1125
|
-
const portConfig = branchNode.outputs[portName];
|
|
1126
|
-
if (portConfig.scope)
|
|
1127
|
-
return;
|
|
1128
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
// Regular node call - always use positional args
|
|
1133
|
-
// In bundle mode we import _impl which takes (execute, ...positional_args)
|
|
1134
|
-
lines.push(`${indent} const ${safeId}Result = ${awaitKeyword}${functionName}(${argNames.join(', ')});`);
|
|
1135
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1136
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1137
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1138
|
-
const portConfig = branchNode.outputs[portName];
|
|
1139
|
-
if (portConfig.scope)
|
|
1140
|
-
return;
|
|
1141
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${safeId}Result.${portName});`);
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1145
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1146
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1147
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1148
|
-
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1149
|
-
lines.push(`${indent} });`);
|
|
1150
|
-
// Debug controller: afterNode hook for branching nodes
|
|
1151
|
-
if (emitDebugHooks) {
|
|
1152
|
-
const awaitHook = isAsync ? 'await ' : '';
|
|
1153
|
-
lines.push(`${indent} ${awaitHook}__ctrl__.afterNode('${instanceId}', ${ctxVar});`);
|
|
1154
|
-
}
|
|
1155
|
-
// Use onSuccess from result to determine control flow
|
|
1156
|
-
// For expression nodes, onSuccess is always true here (catch handles failure)
|
|
1157
|
-
if (trackSuccess) {
|
|
1158
|
-
lines.push(`${indent} ${safeId}_success = ${branchNode.expression ? 'true' : `${safeId}Result.onSuccess`};`);
|
|
1159
|
-
}
|
|
1160
|
-
lines.push(`${indent}} catch (error: unknown) {`);
|
|
1161
|
-
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1162
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1163
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1164
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1165
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1166
|
-
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1167
|
-
lines.push(`${indent} });`);
|
|
1168
|
-
lines.push(`${indent} if (!isCancellation) {`);
|
|
1169
|
-
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1170
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1171
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1172
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1173
|
-
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1174
|
-
lines.push(`${indent} });`);
|
|
1175
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
|
|
1176
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
|
|
1177
|
-
if (trackSuccess) {
|
|
1178
|
-
lines.push(`${indent} ${safeId}_success = false;`);
|
|
1179
|
-
}
|
|
1180
|
-
lines.push(`${indent} }`);
|
|
1181
|
-
// Emit CANCELLED for all downstream nodes since branching node threw
|
|
1182
|
-
if (hasSuccessDownstream) {
|
|
1183
|
-
generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1184
|
-
}
|
|
1185
|
-
if (hasFailureDownstream) {
|
|
1186
|
-
generateCancelledEventsForBranch(region.failureNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1187
|
-
}
|
|
1188
|
-
// Re-throw the error to propagate it up (important for recursive workflows)
|
|
1189
|
-
lines.push(`${indent} throw error;`);
|
|
1190
|
-
lines.push(`${indent}}`);
|
|
1191
|
-
// Close the debug controller beforeNode if block for the node's own execution.
|
|
1192
|
-
// The downstream branching must be outside the if block so it runs even when
|
|
1193
|
-
// the node is skipped (checkpoint resume).
|
|
1194
|
-
if (emitDebugHooks) {
|
|
1195
|
-
lines.push(`${outerIndent}} else {`);
|
|
1196
|
-
// Node was skipped (checkpoint resume). Controller already restored outputs
|
|
1197
|
-
// into ctx, but we need local vars for downstream branching.
|
|
1198
|
-
lines.push(`${outerIndent} ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1199
|
-
if (trackSuccess) {
|
|
1200
|
-
lines.push(`${outerIndent} ${safeId}_success = true;`);
|
|
1201
|
-
}
|
|
1202
|
-
lines.push(`${outerIndent}}`);
|
|
1203
|
-
indent = outerIndent; // Restore indent level for downstream code
|
|
1204
|
-
}
|
|
1205
|
-
lines.push('');
|
|
1206
|
-
// Only generate if/else if there are downstream nodes
|
|
1207
|
-
if (hasDownstream) {
|
|
1208
|
-
lines.push(`${indent}if (${safeId}_success) {`);
|
|
1209
|
-
// Emit CANCELLED for failure branch nodes since success path was taken
|
|
1210
|
-
if (hasFailureDownstream) {
|
|
1211
|
-
generateCancelledEventsForBranch(region.failureNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1212
|
-
}
|
|
1213
|
-
const successVars = new Map(availableVars);
|
|
1214
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1215
|
-
successVars.set(`${instanceId}.${portName}`, `${safeId}Result.${portName}`);
|
|
1216
|
-
});
|
|
1217
|
-
// Sort success branch nodes topologically to ensure correct execution order
|
|
1218
|
-
const successInstanceIds = sortBranchNodesTopologically(region.successNodes, workflow);
|
|
1219
|
-
const successExecutedNodes = [instance.id];
|
|
1220
|
-
successInstanceIds.forEach((instanceId) => {
|
|
1221
|
-
const inst = workflow.instances.find((i) => i.id === instanceId);
|
|
1222
|
-
if (!inst)
|
|
1223
|
-
return;
|
|
1224
|
-
// Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
|
|
1225
|
-
const nodeType = allNodeTypes.find((nt) => nt.name === inst.nodeType || nt.functionName === inst.nodeType);
|
|
1226
|
-
if (!nodeType)
|
|
1227
|
-
return;
|
|
1228
|
-
if (branchingNodes.has(instanceId)) {
|
|
1229
|
-
const nestedRegion = branchRegions.get(instanceId);
|
|
1230
|
-
// Pre-declare nested branching node's _success flag at the current
|
|
1231
|
-
// scope so it remains accessible to downstream guards that may run
|
|
1232
|
-
// outside this branch block. (Fixes scoping bug where the flag was
|
|
1233
|
-
// declared inside a nested conditional but referenced at a higher scope.)
|
|
1234
|
-
const nestedSafeId = toValidIdentifier(instanceId);
|
|
1235
|
-
const nestedHasDownstream = nestedRegion.successNodes.size > 0 || nestedRegion.failureNodes.size > 0;
|
|
1236
|
-
const nestedPreDeclared = new Set(preDeclaredSuccessFlags);
|
|
1237
|
-
if (nestedHasDownstream && !nestedPreDeclared.has(nestedSafeId)) {
|
|
1238
|
-
lines.push(`${indent} let ${nestedSafeId}_success = false;`);
|
|
1239
|
-
nestedPreDeclared.add(nestedSafeId);
|
|
1240
|
-
}
|
|
1241
|
-
generateBranchingNodeCode(inst, nodeType, workflow, allNodeTypes, nestedRegion, successVars, generatedNodes, lines, `${indent} `, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode, nestedPreDeclared, nestedPreDeclared.has(nestedSafeId), // force tracking if flag was pre-declared at higher scope
|
|
1242
|
-
production);
|
|
1243
|
-
successExecutedNodes.push(instanceId);
|
|
1244
|
-
nestedRegion.successNodes.forEach((n) => successExecutedNodes.push(n));
|
|
1245
|
-
nestedRegion.failureNodes.forEach((n) => successExecutedNodes.push(n));
|
|
1246
|
-
}
|
|
1247
|
-
else {
|
|
1248
|
-
generateNodeCallWithContext(inst, nodeType, workflow, successVars, lines, allNodeTypes, `${indent} `, isAsync, false, // useConst
|
|
1249
|
-
undefined, // instanceParent
|
|
1250
|
-
ctxVar, bundleMode, true, // skipExecuteGuard — inside branch, execute is guaranteed by if/else
|
|
1251
|
-
branchingNodes, production);
|
|
1252
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1253
|
-
successVars.set(`${instanceId}.${portName}`, `${toValidIdentifier(instanceId)}Result.${portName}`);
|
|
1254
|
-
});
|
|
1255
|
-
successExecutedNodes.push(instanceId);
|
|
1256
|
-
generatedNodes.add(instanceId);
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
// Only generate else block if there are failure nodes to execute
|
|
1260
|
-
if (hasFailureDownstream) {
|
|
1261
|
-
lines.push(`${indent}} else {`);
|
|
1262
|
-
// Emit CANCELLED for success branch nodes since failure path was taken
|
|
1263
|
-
if (hasSuccessDownstream) {
|
|
1264
|
-
generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1265
|
-
}
|
|
1266
|
-
const failureVars = new Map(availableVars);
|
|
1267
|
-
Object.keys(branchNode.outputs).forEach((portName) => {
|
|
1268
|
-
failureVars.set(`${instanceId}.${portName}`, `${safeId}Result.${portName}`);
|
|
1269
|
-
});
|
|
1270
|
-
// Sort failure branch nodes topologically to ensure correct execution order
|
|
1271
|
-
const failureInstanceIds = sortBranchNodesTopologically(region.failureNodes, workflow);
|
|
1272
|
-
const failureExecutedNodes = [instance.id];
|
|
1273
|
-
failureInstanceIds.forEach((instanceId) => {
|
|
1274
|
-
const inst = workflow.instances.find((i) => i.id === instanceId);
|
|
1275
|
-
if (!inst)
|
|
1276
|
-
return;
|
|
1277
|
-
// Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
|
|
1278
|
-
const nodeType = allNodeTypes.find((nt) => nt.name === inst.nodeType || nt.functionName === inst.nodeType);
|
|
1279
|
-
if (!nodeType)
|
|
1280
|
-
return;
|
|
1281
|
-
if (branchingNodes.has(instanceId)) {
|
|
1282
|
-
const nestedRegion = branchRegions.get(instanceId);
|
|
1283
|
-
// Pre-declare nested branching node's _success flag at the current
|
|
1284
|
-
// scope (same fix as success branch above).
|
|
1285
|
-
const nestedSafeId = toValidIdentifier(instanceId);
|
|
1286
|
-
const nestedHasDownstream = nestedRegion.successNodes.size > 0 || nestedRegion.failureNodes.size > 0;
|
|
1287
|
-
const nestedPreDeclared = new Set(preDeclaredSuccessFlags);
|
|
1288
|
-
if (nestedHasDownstream && !nestedPreDeclared.has(nestedSafeId)) {
|
|
1289
|
-
lines.push(`${indent} let ${nestedSafeId}_success = false;`);
|
|
1290
|
-
nestedPreDeclared.add(nestedSafeId);
|
|
1291
|
-
}
|
|
1292
|
-
generateBranchingNodeCode(inst, nodeType, workflow, allNodeTypes, nestedRegion, failureVars, generatedNodes, lines, `${indent} `, false, branchingNodes, branchRegions, isAsync, ctxVar, bundleMode, nestedPreDeclared, nestedPreDeclared.has(nestedSafeId), // force tracking if flag was pre-declared at higher scope
|
|
1293
|
-
production);
|
|
1294
|
-
failureExecutedNodes.push(instanceId);
|
|
1295
|
-
nestedRegion.successNodes.forEach((n) => failureExecutedNodes.push(n));
|
|
1296
|
-
nestedRegion.failureNodes.forEach((n) => failureExecutedNodes.push(n));
|
|
1297
|
-
}
|
|
1298
|
-
else {
|
|
1299
|
-
generateNodeCallWithContext(inst, nodeType, workflow, failureVars, lines, allNodeTypes, `${indent} `, isAsync, false, // useConst
|
|
1300
|
-
undefined, // instanceParent
|
|
1301
|
-
ctxVar, bundleMode, true, // skipExecuteGuard — inside branch, execute is guaranteed by if/else
|
|
1302
|
-
branchingNodes, production);
|
|
1303
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1304
|
-
failureVars.set(`${instanceId}.${portName}`, `${toValidIdentifier(instanceId)}Result.${portName}`);
|
|
1305
|
-
});
|
|
1306
|
-
failureExecutedNodes.push(instanceId);
|
|
1307
|
-
generatedNodes.add(instanceId);
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1310
|
-
lines.push(`${indent}}`);
|
|
1311
|
-
}
|
|
1312
|
-
else {
|
|
1313
|
-
// No failure branch - emit CANCELLED for success nodes and close
|
|
1314
|
-
if (hasSuccessDownstream) {
|
|
1315
|
-
lines.push(`${indent}} else {`);
|
|
1316
|
-
generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1317
|
-
lines.push(`${indent}}`);
|
|
1318
|
-
}
|
|
1319
|
-
else {
|
|
1320
|
-
lines.push(`${indent}}`);
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
generatedNodes.add(instanceId);
|
|
1325
|
-
}
|
|
1326
|
-
function generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar = 'ctx', // Context variable name (for scoped contexts)
|
|
1327
|
-
bundleMode = false, production = false) {
|
|
1328
|
-
const instanceId = instance.id;
|
|
1329
|
-
const safeId = toValidIdentifier(instanceId);
|
|
1330
|
-
const functionName = nodeType.functionName;
|
|
1331
|
-
// Executor must be async if:
|
|
1332
|
-
// 1. The workflow is async (context returns Promises)
|
|
1333
|
-
// 2. OR the node function is async (function returns Promise)
|
|
1334
|
-
// Sync executors only work when BOTH workflow and node are sync
|
|
1335
|
-
const executorIsAsync = isAsync || nodeType.isAsync;
|
|
1336
|
-
const asyncKeyword = executorIsAsync ? 'async ' : '';
|
|
1337
|
-
const awaitKeyword = nodeType.isAsync ? 'await ' : '';
|
|
1338
|
-
const awaitPrefix = executorIsAsync ? 'await ' : '';
|
|
1339
|
-
// Create a lazy execution function for this pull node
|
|
1340
|
-
// The function will only execute when its outputs are accessed
|
|
1341
|
-
lines.push(`${indent}// Pull execution node: ${instanceId}`);
|
|
1342
|
-
lines.push(`${indent}const ${safeId}_executor = ${asyncKeyword}() => {`);
|
|
1343
|
-
lines.push(`${indent} if (${safeId}Idx !== undefined) {`);
|
|
1344
|
-
lines.push(`${indent} return; // Already executed`);
|
|
1345
|
-
lines.push(`${indent} }`);
|
|
1346
|
-
lines.push(`${indent} ${ctxVar}.checkAborted('${instanceId}');`);
|
|
1347
|
-
lines.push(`${indent} ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1348
|
-
lines.push(`${indent} if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${instanceId}';`);
|
|
1349
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1350
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1351
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1352
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1353
|
-
lines.push(`${indent} status: 'RUNNING',`);
|
|
1354
|
-
lines.push(`${indent} });`);
|
|
1355
|
-
lines.push(`${indent} try {`);
|
|
1356
|
-
// Use executor's async status for get/set calls within the executor
|
|
1357
|
-
const getCall = executorIsAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1358
|
-
const setCall = executorIsAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1359
|
-
const args = buildNodeArgumentsWithContext({
|
|
1360
|
-
node: nodeType,
|
|
1361
|
-
workflow,
|
|
1362
|
-
id: instanceId,
|
|
1363
|
-
lines,
|
|
1364
|
-
indent: `${indent} `,
|
|
1365
|
-
getCall,
|
|
1366
|
-
isAsync: executorIsAsync,
|
|
1367
|
-
emitInputEvents: true,
|
|
1368
|
-
setCall,
|
|
1369
|
-
nodeTypeName: functionName,
|
|
1370
|
-
bundleMode,
|
|
1371
|
-
production,
|
|
1372
|
-
});
|
|
1373
|
-
const resultVar = `${safeId}Result`;
|
|
1374
|
-
// Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
|
|
1375
|
-
// Workflows use (execute, params) signature where params is an object
|
|
1376
|
-
// Regular nodes use (execute, arg1, arg2, ...) positional signature
|
|
1377
|
-
if (nodeType.variant === 'MAP_ITERATOR') {
|
|
1378
|
-
// MAP_ITERATOR: inline iteration in pull executor
|
|
1379
|
-
const executeArg = args[0];
|
|
1380
|
-
const itemsArg = args[1];
|
|
1381
|
-
const scopeFnArg = args[2];
|
|
1382
|
-
lines.push(`${indent} let ${resultVar}: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
|
|
1383
|
-
lines.push(`${indent} if (!${executeArg}) {`);
|
|
1384
|
-
lines.push(`${indent} ${resultVar} = { onSuccess: false, onFailure: false, results: [] };`);
|
|
1385
|
-
lines.push(`${indent} } else {`);
|
|
1386
|
-
lines.push(`${indent} const __results: unknown[] = [];`);
|
|
1387
|
-
lines.push(`${indent} for (const __item of ${itemsArg}) {`);
|
|
1388
|
-
lines.push(`${indent} __results.push((${executorIsAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
|
|
1389
|
-
lines.push(`${indent} }`);
|
|
1390
|
-
lines.push(`${indent} ${resultVar} = { onSuccess: true, onFailure: false, results: __results };`);
|
|
1391
|
-
lines.push(`${indent} }`);
|
|
1392
|
-
}
|
|
1393
|
-
else if (nodeType.variant === 'IMPORTED_WORKFLOW' || nodeType.variant === 'WORKFLOW') {
|
|
1394
|
-
// For workflow calls, wrap data args in an object and include recursion depth
|
|
1395
|
-
const executeArg = args[0]; // First arg is always execute
|
|
1396
|
-
const dataArgs = args.slice(1); // Rest are data inputs
|
|
1397
|
-
const inputPortNames = Object.keys(nodeType.inputs).filter((p) => !isExecutePort(p));
|
|
1398
|
-
// Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
|
|
1399
|
-
// Assign to variable first to avoid TypeScript excess property checking on object literals
|
|
1400
|
-
const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
|
|
1401
|
-
paramsEntries.push('__rd__: __rd__ + 1');
|
|
1402
|
-
const paramsObj = `{ ${paramsEntries.join(', ')} }`;
|
|
1403
|
-
const paramsVar = `__${safeId}Params__`;
|
|
1404
|
-
lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
|
|
1405
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
|
|
1406
|
-
}
|
|
1407
|
-
else if (nodeType.scope || (nodeType.scopes && nodeType.scopes.length > 0)) {
|
|
1408
|
-
// Scoped node call with positional arguments (uses _impl signature)
|
|
1409
|
-
// Scoped nodes have callback parameters that can't be passed via params object
|
|
1410
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
|
|
1411
|
-
}
|
|
1412
|
-
else {
|
|
1413
|
-
// Regular node call - always use positional args
|
|
1414
|
-
// In bundle mode we import _impl which takes (execute, ...positional_args)
|
|
1415
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
|
|
1416
|
-
}
|
|
1417
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1418
|
-
if (isSuccessPort(portName) || isFailurePort(portName))
|
|
1419
|
-
return;
|
|
1420
|
-
// Skip scoped OUTPUT ports - they don't exist in the function return value
|
|
1421
|
-
const portConfig = nodeType.outputs[portName];
|
|
1422
|
-
if (portConfig.scope)
|
|
1423
|
-
return;
|
|
1424
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1425
|
-
});
|
|
1426
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1427
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1428
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1429
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1430
|
-
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1431
|
-
lines.push(`${indent} });`);
|
|
1432
|
-
lines.push(`${indent} } catch (error: unknown) {`);
|
|
1433
|
-
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1434
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1435
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1436
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1437
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1438
|
-
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1439
|
-
lines.push(`${indent} });`);
|
|
1440
|
-
lines.push(`${indent} if (!isCancellation) {`);
|
|
1441
|
-
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1442
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1443
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1444
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1445
|
-
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1446
|
-
lines.push(`${indent} });`);
|
|
1447
|
-
lines.push(`${indent} }`);
|
|
1448
|
-
lines.push(`${indent} throw error;`);
|
|
1449
|
-
lines.push(`${indent} }`);
|
|
1450
|
-
lines.push(`${indent}};`);
|
|
1451
|
-
lines.push(`${indent}// Register lazy executor for pull node`);
|
|
1452
|
-
lines.push(`${indent}${ctxVar}.registerPullExecutor('${instanceId}', ${safeId}_executor);`);
|
|
1453
|
-
lines.push(``);
|
|
1454
|
-
}
|
|
1455
|
-
function generateNodeCallWithContext(instance, nodeType, workflow, _availableVars, lines, _allNodeTypes, indent, isAsync, useConst = false, // Use const for nodes that always execute (not in branches)
|
|
1456
|
-
instanceParent, // Parent node ID for scope children (parent is const, no ! needed)
|
|
1457
|
-
ctxVar = 'ctx', // Context variable name (for scoped contexts)
|
|
1458
|
-
bundleMode = false, // Bundle mode uses params object pattern for wrapper functions
|
|
1459
|
-
skipExecuteGuard = false, // Skip execute port STEP guard (for nodes inside branch blocks)
|
|
1460
|
-
branchingNodes = new Set(), // Branching nodes set for port-aware STEP guards
|
|
1461
|
-
production = false // When false, emit debug controller hooks (beforeNode/afterNode)
|
|
1462
|
-
) {
|
|
1463
|
-
const instanceId = instance.id;
|
|
1464
|
-
const safeId = toValidIdentifier(instanceId);
|
|
1465
|
-
const functionName = nodeType.functionName;
|
|
1466
|
-
// Check if this instance has pull execution enabled
|
|
1467
|
-
const fullInstance = workflow.instances.find((i) => i.id === instanceId);
|
|
1468
|
-
const pullConfig = fullInstance
|
|
1469
|
-
? getPullExecutionConfig(fullInstance, nodeType)
|
|
1470
|
-
: { enabled: false, triggerPort: 'execute' };
|
|
1471
|
-
// If this is a pull execution node, wrap it in a lazy function
|
|
1472
|
-
if (pullConfig.enabled) {
|
|
1473
|
-
generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar, bundleMode, production);
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
const stepInputs = Object.entries(nodeType.inputs).filter(([portName, portConfig]) => {
|
|
1477
|
-
// Include the primary execute port for promoted nodes (useConst=false at top level)
|
|
1478
|
-
// so they get a STEP guard from their conditional source.
|
|
1479
|
-
// Skip execute guard for nodes inside branch blocks (execute is guaranteed by if/else).
|
|
1480
|
-
if (isExecutePort(portName) && !useConst && !skipExecuteGuard) {
|
|
1481
|
-
return portConfig.dataType === 'STEP';
|
|
1482
|
-
}
|
|
1483
|
-
return portConfig.dataType === 'STEP' && !isExecutePort(portName);
|
|
1484
|
-
});
|
|
1485
|
-
// Expression nodes don't declare 'execute' in their inputs, but the workflow
|
|
1486
|
-
// connects STEP signals to their execute port. Include it for promoted expression
|
|
1487
|
-
// nodes so they get a proper branch-aware STEP guard.
|
|
1488
|
-
if (nodeType.expression && !useConst && !skipExecuteGuard && !stepInputs.some(([p]) => isExecutePort(p))) {
|
|
1489
|
-
const hasExecuteConnection = workflow.connections.some((conn) => conn.to.node === instanceId && isExecutePort(conn.to.port));
|
|
1490
|
-
if (hasExecuteConnection) {
|
|
1491
|
-
stepInputs.push(['execute', { dataType: 'STEP' }]);
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
const stepSignalSources = [];
|
|
1495
|
-
if (stepInputs.length > 0) {
|
|
1496
|
-
workflow.connections.forEach((conn) => {
|
|
1497
|
-
const toNode = conn.to.node;
|
|
1498
|
-
const toPort = conn.to.port;
|
|
1499
|
-
if (toNode === instanceId && stepInputs.some(([port]) => port === toPort)) {
|
|
1500
|
-
const sourceNode = conn.from.node;
|
|
1501
|
-
// Skip per-port scoped children - they don't have Idx in main scope
|
|
1502
|
-
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
1503
|
-
if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
|
|
1504
|
-
return;
|
|
1505
|
-
}
|
|
1506
|
-
if (!isStartNode(sourceNode) && !stepSignalSources.includes(sourceNode)) {
|
|
1507
|
-
stepSignalSources.push(sourceNode);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
let shouldIndent = false;
|
|
1513
|
-
if (stepSignalSources.length > 0) {
|
|
1514
|
-
const conditions = [];
|
|
1515
|
-
const portToSources = new Map();
|
|
1516
|
-
stepInputs.forEach(([portName]) => {
|
|
1517
|
-
const sources = [];
|
|
1518
|
-
workflow.connections.forEach((conn) => {
|
|
1519
|
-
const toNode = conn.to.node;
|
|
1520
|
-
const toPort = conn.to.port;
|
|
1521
|
-
if (toNode === instanceId && toPort === portName) {
|
|
1522
|
-
const sourceNode = conn.from.node;
|
|
1523
|
-
if (!isStartNode(sourceNode)) {
|
|
1524
|
-
// Skip per-port scoped children - they don't have Idx in main scope
|
|
1525
|
-
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
1526
|
-
if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
|
|
1527
|
-
return;
|
|
1528
|
-
}
|
|
1529
|
-
sources.push({ node: sourceNode, port: conn.from.port });
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
});
|
|
1533
|
-
if (sources.length > 0) {
|
|
1534
|
-
portToSources.set(portName, sources);
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
if (nodeType.executeWhen === EXECUTION_STRATEGIES.CONJUNCTION) {
|
|
1538
|
-
// CONJUNCTION: Execute when ALL input ports have data (AND logic)
|
|
1539
|
-
portToSources.forEach((sources, _portName) => {
|
|
1540
|
-
if (sources.length === 1) {
|
|
1541
|
-
conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
|
|
1542
|
-
}
|
|
1543
|
-
else {
|
|
1544
|
-
const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
|
|
1545
|
-
conditions.push(`(${orCondition})`);
|
|
1546
|
-
}
|
|
1547
|
-
});
|
|
1548
|
-
if (conditions.length > 0) {
|
|
1549
|
-
const fullCondition = conditions.join(' && ');
|
|
1550
|
-
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1551
|
-
indent = `${indent} `;
|
|
1552
|
-
shouldIndent = true;
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
else if (nodeType.executeWhen === EXECUTION_STRATEGIES.DISJUNCTION) {
|
|
1556
|
-
// DISJUNCTION: Execute when ANY input port has data (OR logic)
|
|
1557
|
-
const allSources = [];
|
|
1558
|
-
portToSources.forEach((sources) => {
|
|
1559
|
-
sources.forEach((source) => {
|
|
1560
|
-
if (!allSources.some((s) => s.node === source.node && s.port === source.port)) {
|
|
1561
|
-
allSources.push(source);
|
|
1562
|
-
}
|
|
1563
|
-
});
|
|
1564
|
-
});
|
|
1565
|
-
if (allSources.length > 0) {
|
|
1566
|
-
const fullCondition = allSources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
|
|
1567
|
-
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1568
|
-
indent = `${indent} `;
|
|
1569
|
-
shouldIndent = true;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
else if (nodeType.executeWhen === EXECUTION_STRATEGIES.CUSTOM) {
|
|
1573
|
-
// CUSTOM: User-provided execution condition
|
|
1574
|
-
// Custom condition should be in nodeType.metadata.customExecuteCondition
|
|
1575
|
-
const customCondition = nodeType.metadata?.customExecuteCondition;
|
|
1576
|
-
if (customCondition && typeof customCondition === 'string') {
|
|
1577
|
-
lines.push(`${indent}if (${customCondition}) {`);
|
|
1578
|
-
indent = `${indent} `;
|
|
1579
|
-
shouldIndent = true;
|
|
1580
|
-
}
|
|
1581
|
-
else {
|
|
1582
|
-
// Fallback to CONJUNCTION if no custom condition provided
|
|
1583
|
-
portToSources.forEach((sources, _portName) => {
|
|
1584
|
-
if (sources.length === 1) {
|
|
1585
|
-
conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
|
|
1586
|
-
}
|
|
1587
|
-
else {
|
|
1588
|
-
const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(' || ');
|
|
1589
|
-
conditions.push(`(${orCondition})`);
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
if (conditions.length > 0) {
|
|
1593
|
-
const fullCondition = conditions.join(' && ');
|
|
1594
|
-
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1595
|
-
indent = `${indent} `;
|
|
1596
|
-
shouldIndent = true;
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
// Debug controller: beforeNode hook (step-through / checkpoint resume)
|
|
1602
|
-
// When enabled, wraps the entire node execution in if(await __ctrl__.beforeNode(...))
|
|
1603
|
-
// so checkpoint resume can skip already-completed nodes.
|
|
1604
|
-
const emitDebugHooks = !production;
|
|
1605
|
-
const outerIndent = indent; // preserve for closing brace
|
|
1606
|
-
if (emitDebugHooks) {
|
|
1607
|
-
// When useConst=true, the Idx variable would normally be declared with const
|
|
1608
|
-
// inside the node execution block. Since debug hooks wrap that in if/else,
|
|
1609
|
-
// we must hoist the declaration before the if block to keep it in scope.
|
|
1610
|
-
if (useConst) {
|
|
1611
|
-
lines.push(`${indent}let ${safeId}Idx: number;`);
|
|
1612
|
-
}
|
|
1613
|
-
const awaitHook = isAsync ? 'await ' : '';
|
|
1614
|
-
lines.push(`${indent}if (${awaitHook}__ctrl__.beforeNode('${instanceId}', ${ctxVar})) {`);
|
|
1615
|
-
indent = `${indent} `;
|
|
1616
|
-
}
|
|
1617
|
-
// When debug hooks hoist the declaration, we use assignment only inside the block.
|
|
1618
|
-
const varDecl = (useConst && !emitDebugHooks) ? 'const ' : '';
|
|
1619
|
-
const awaitPrefix = isAsync ? 'await ' : '';
|
|
1620
|
-
lines.push(`${indent}${ctxVar}.checkAborted('${instanceId}');`);
|
|
1621
|
-
lines.push(`${indent}${varDecl}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1622
|
-
lines.push(`${indent}if (typeof globalThis !== 'undefined') (globalThis as unknown as { __fw_current_node_id__?: string }).__fw_current_node_id__ = '${instanceId}';`);
|
|
1623
|
-
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1624
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1625
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1626
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1627
|
-
lines.push(`${indent} status: 'RUNNING',`);
|
|
1628
|
-
lines.push(`${indent}});`);
|
|
1629
|
-
lines.push(`${indent}try {`);
|
|
1630
|
-
const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1631
|
-
const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1632
|
-
const args = buildNodeArgumentsWithContext({
|
|
1633
|
-
node: nodeType,
|
|
1634
|
-
workflow,
|
|
1635
|
-
id: instanceId,
|
|
1636
|
-
lines,
|
|
1637
|
-
indent: `${indent} `,
|
|
1638
|
-
getCall,
|
|
1639
|
-
isAsync,
|
|
1640
|
-
instanceParent,
|
|
1641
|
-
emitInputEvents: true,
|
|
1642
|
-
setCall,
|
|
1643
|
-
nodeTypeName: functionName,
|
|
1644
|
-
bundleMode,
|
|
1645
|
-
production,
|
|
1646
|
-
});
|
|
1647
|
-
const resultVar = `${safeId}Result`;
|
|
1648
|
-
const awaitKeyword = nodeType.isAsync ? 'await ' : '';
|
|
1649
|
-
if (nodeType.variant === 'STUB') {
|
|
1650
|
-
// Stub node: emit a runtime throw. The workflow was generated with generateStubs: true.
|
|
1651
|
-
lines.push(`${indent} throw new Error('Node "${instanceId}" uses stub type "${functionName}" which has no implementation.');`);
|
|
1652
|
-
}
|
|
1653
|
-
else if (nodeType.variant === 'COERCION') {
|
|
1654
|
-
// Coercion node: inline JS expression instead of function call
|
|
1655
|
-
const coerceExprMap = {
|
|
1656
|
-
__fw_toString: 'String',
|
|
1657
|
-
__fw_toNumber: 'Number',
|
|
1658
|
-
__fw_toBoolean: 'Boolean',
|
|
1659
|
-
__fw_toJSON: 'JSON.stringify',
|
|
1660
|
-
__fw_parseJSON: 'JSON.parse',
|
|
1661
|
-
};
|
|
1662
|
-
const coerceExpr = coerceExprMap[functionName] || 'String';
|
|
1663
|
-
// args[0] is the value input (execute is skipped for expression nodes)
|
|
1664
|
-
const valueArg = args[0] || 'undefined';
|
|
1665
|
-
lines.push(`${indent} const ${resultVar} = ${coerceExpr}(${valueArg});`);
|
|
1666
|
-
// Set the single result output port
|
|
1667
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'result', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar});`);
|
|
1668
|
-
// Auto-set onSuccess/onFailure
|
|
1669
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
|
|
1670
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
|
|
1671
|
-
}
|
|
1672
|
-
else if (nodeType.expression) {
|
|
1673
|
-
// Expression node: call without execute, map raw return to output ports
|
|
1674
|
-
// _impl returns data only; onSuccess/onFailure are auto-set in the workflow body
|
|
1675
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
|
|
1676
|
-
// Determine data output ports (exclude control flow and scoped ports)
|
|
1677
|
-
const dataOutputPorts = Object.keys(nodeType.outputs).filter((portName) => {
|
|
1678
|
-
const portConfig = nodeType.outputs[portName];
|
|
1679
|
-
if (portConfig.scope)
|
|
1680
|
-
return false;
|
|
1681
|
-
if (isSuccessPort(portName) || isFailurePort(portName))
|
|
1682
|
-
return false;
|
|
1683
|
-
if (portConfig.isControlFlow || portConfig.failure)
|
|
1684
|
-
return false;
|
|
1685
|
-
return true;
|
|
1686
|
-
});
|
|
1687
|
-
if (dataOutputPorts.length === 1) {
|
|
1688
|
-
// Single data output: destructure if result is an object with the port key, else use raw value
|
|
1689
|
-
// Extract to unknown-typed variable to prevent TypeScript from narrowing
|
|
1690
|
-
// specific return types (e.g. boolean) to `never` in the typeof check
|
|
1691
|
-
const portName = dataOutputPorts[0];
|
|
1692
|
-
const rawVar = `${resultVar}_raw`;
|
|
1693
|
-
lines.push(`${indent} const ${rawVar}: unknown = ${resultVar};`);
|
|
1694
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, typeof ${rawVar} === 'object' && ${rawVar} !== null && '${portName}' in ${rawVar} ? ${rawVar}.${portName} : ${rawVar});`);
|
|
1695
|
-
}
|
|
1696
|
-
else {
|
|
1697
|
-
// Multiple data outputs: destructure from object return
|
|
1698
|
-
dataOutputPorts.forEach((portName) => {
|
|
1699
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
// Auto-set onSuccess/onFailure
|
|
1703
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
|
|
1704
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
|
|
1705
|
-
}
|
|
1706
|
-
else if (nodeType.variant === 'MAP_ITERATOR') {
|
|
1707
|
-
// MAP_ITERATOR: inline iteration — no user function to call
|
|
1708
|
-
// args: [execute, items, scopeFn]
|
|
1709
|
-
const executeArg = args[0];
|
|
1710
|
-
const itemsArg = args[1];
|
|
1711
|
-
const scopeFnArg = args[2];
|
|
1712
|
-
lines.push(`${indent} let ${resultVar}: { onSuccess: boolean; onFailure: boolean; results: unknown[] };`);
|
|
1713
|
-
lines.push(`${indent} if (!${executeArg}) {`);
|
|
1714
|
-
lines.push(`${indent} ${resultVar} = { onSuccess: false, onFailure: false, results: [] };`);
|
|
1715
|
-
lines.push(`${indent} } else {`);
|
|
1716
|
-
lines.push(`${indent} const __results: unknown[] = [];`);
|
|
1717
|
-
lines.push(`${indent} for (const __item of ${itemsArg}) {`);
|
|
1718
|
-
lines.push(`${indent} __results.push((${isAsync ? 'await ' : ''}${scopeFnArg}(true, __item)).processed);`);
|
|
1719
|
-
lines.push(`${indent} }`);
|
|
1720
|
-
lines.push(`${indent} ${resultVar} = { onSuccess: true, onFailure: false, results: __results };`);
|
|
1721
|
-
lines.push(`${indent} }`);
|
|
1722
|
-
// Set output ports from result
|
|
1723
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1724
|
-
const portConfig = nodeType.outputs[portName];
|
|
1725
|
-
if (portConfig.scope)
|
|
1726
|
-
return;
|
|
1727
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
else if (nodeType.variant === 'IMPORTED_WORKFLOW' || nodeType.variant === 'WORKFLOW') {
|
|
1731
|
-
// Check if this is a workflow call (IMPORTED_WORKFLOW or WORKFLOW variant)
|
|
1732
|
-
// Workflows use (execute, params) signature where params is an object
|
|
1733
|
-
// Regular nodes use (execute, arg1, arg2, ...) positional signature
|
|
1734
|
-
// For workflow calls, wrap data args in an object and include recursion depth
|
|
1735
|
-
const executeArg = args[0]; // First arg is always execute
|
|
1736
|
-
const dataArgs = args.slice(1); // Rest are data inputs
|
|
1737
|
-
const inputPortNames = Object.keys(nodeType.inputs).filter((p) => !isExecutePort(p));
|
|
1738
|
-
// Build params object: { portName1: value1, portName2: value2, ..., __rd__: __rd__ + 1 }
|
|
1739
|
-
// Assign to variable first to avoid TypeScript excess property checking on object literals
|
|
1740
|
-
const paramsEntries = inputPortNames.map((portName, i) => `${portName}: ${dataArgs[i]}`);
|
|
1741
|
-
paramsEntries.push('__rd__: __rd__ + 1');
|
|
1742
|
-
const paramsObj = `{ ${paramsEntries.join(', ')} }`;
|
|
1743
|
-
const paramsVar = `__${safeId}Params__`;
|
|
1744
|
-
lines.push(`${indent} const ${paramsVar} = ${paramsObj};`);
|
|
1745
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${executeArg}, ${paramsVar});`);
|
|
1746
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1747
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1748
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1749
|
-
const portConfig = nodeType.outputs[portName];
|
|
1750
|
-
// Skip scoped OUTPUT ports - they don't exist in the function return value
|
|
1751
|
-
if (portConfig.scope)
|
|
1752
|
-
return;
|
|
1753
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
else if (nodeType.scope || (nodeType.scopes && nodeType.scopes.length > 0)) {
|
|
1757
|
-
// Scoped node call with positional arguments (uses _impl signature)
|
|
1758
|
-
// Scoped nodes have callback parameters that can't be passed via params object
|
|
1759
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
|
|
1760
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1761
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1762
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1763
|
-
const portConfig = nodeType.outputs[portName];
|
|
1764
|
-
// Skip scoped OUTPUT ports - they don't exist in the function return value
|
|
1765
|
-
if (portConfig.scope)
|
|
1766
|
-
return;
|
|
1767
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
else {
|
|
1771
|
-
// Regular node call - always use positional args
|
|
1772
|
-
// In bundle mode we import _impl which takes (execute, ...positional_args)
|
|
1773
|
-
lines.push(`${indent} const ${resultVar} = ${awaitKeyword}${functionName}(${args.join(', ')});`);
|
|
1774
|
-
// STEP Port Architecture: Extract ALL outputs from result, including onSuccess/onFailure
|
|
1775
|
-
// Skip scoped OUTPUT ports - they're parameters to scope functions, not return values
|
|
1776
|
-
Object.keys(nodeType.outputs).forEach((portName) => {
|
|
1777
|
-
const portConfig = nodeType.outputs[portName];
|
|
1778
|
-
// Skip scoped OUTPUT ports - they don't exist in the function return value
|
|
1779
|
-
if (portConfig.scope)
|
|
1780
|
-
return;
|
|
1781
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: '${portName}', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, ${resultVar}.${portName});`);
|
|
1782
|
-
});
|
|
1783
|
-
}
|
|
1784
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1785
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1786
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1787
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1788
|
-
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1789
|
-
lines.push(`${indent} });`);
|
|
1790
|
-
// Debug controller: afterNode hook (checkpoint write, step pause)
|
|
1791
|
-
if (emitDebugHooks) {
|
|
1792
|
-
const awaitHook = isAsync ? 'await ' : '';
|
|
1793
|
-
lines.push(`${indent} ${awaitHook}__ctrl__.afterNode('${instanceId}', ${ctxVar});`);
|
|
1794
|
-
}
|
|
1795
|
-
lines.push(`${indent}} catch (error: unknown) {`);
|
|
1796
|
-
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1797
|
-
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1798
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1799
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1800
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1801
|
-
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1802
|
-
lines.push(`${indent} });`);
|
|
1803
|
-
lines.push(`${indent} if (!isCancellation) {`);
|
|
1804
|
-
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1805
|
-
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1806
|
-
lines.push(`${indent} id: '${instanceId}',`);
|
|
1807
|
-
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1808
|
-
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1809
|
-
lines.push(`${indent} });`);
|
|
1810
|
-
if (nodeType.expression) {
|
|
1811
|
-
// Expression node: auto-set failure flags in catch block
|
|
1812
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`);
|
|
1813
|
-
lines.push(`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`);
|
|
1814
|
-
}
|
|
1815
|
-
lines.push(`${indent} }`);
|
|
1816
|
-
lines.push(`${indent} throw error;`);
|
|
1817
|
-
lines.push(`${indent}}`);
|
|
1818
|
-
// Close the debug controller beforeNode if block
|
|
1819
|
-
if (emitDebugHooks) {
|
|
1820
|
-
// Else block: node was skipped (checkpoint resume).
|
|
1821
|
-
// Register execution so downstream nodes can reference {safeId}Idx.
|
|
1822
|
-
lines.push(`${outerIndent}} else {`);
|
|
1823
|
-
lines.push(`${outerIndent} ${varDecl}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1824
|
-
lines.push(`${outerIndent}}`);
|
|
1825
|
-
}
|
|
1826
|
-
if (shouldIndent) {
|
|
1827
|
-
const originalIndent = outerIndent.slice(0, -2);
|
|
1828
|
-
lines.push(`${originalIndent}}`);
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
//# sourceMappingURL=unified.js.map
|