@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
|
@@ -0,0 +1,3057 @@
|
|
|
1
|
+
import {
|
|
2
|
+
durableBranchPaths,
|
|
3
|
+
validateDurableClosure
|
|
4
|
+
} from "./chunk-KU3CLJI2.js";
|
|
5
|
+
import {
|
|
6
|
+
buildNodeArgumentsWithContext,
|
|
7
|
+
emitDurableNodeCall,
|
|
8
|
+
emitNodeInvocation,
|
|
9
|
+
emitPlainNodeCall,
|
|
10
|
+
emitResultOutputs,
|
|
11
|
+
nodeResultVar,
|
|
12
|
+
toValidIdentifier
|
|
13
|
+
} from "./chunk-5Y3AIUWY.js";
|
|
14
|
+
import {
|
|
15
|
+
extractStartPorts
|
|
16
|
+
} from "./chunk-CZBCCK4Y.js";
|
|
17
|
+
import {
|
|
18
|
+
canonicalWireValue,
|
|
19
|
+
sha256Hex
|
|
20
|
+
} from "./chunk-O2U2LO57.js";
|
|
21
|
+
import {
|
|
22
|
+
VERSION
|
|
23
|
+
} from "./chunk-QDE4Z7KW.js";
|
|
24
|
+
import {
|
|
25
|
+
getTopologicalOrder
|
|
26
|
+
} from "./chunk-P2CVDGHR.js";
|
|
27
|
+
import {
|
|
28
|
+
MARKERS
|
|
29
|
+
} from "./chunk-ZMGN6F3S.js";
|
|
30
|
+
import {
|
|
31
|
+
buildControlFlowGraph,
|
|
32
|
+
computeParallelLevels,
|
|
33
|
+
detectBranchingChains,
|
|
34
|
+
findAllBranchingNodes,
|
|
35
|
+
findNodesInBranch,
|
|
36
|
+
isPerPortScopedChild,
|
|
37
|
+
performKahnsTopologicalSort
|
|
38
|
+
} from "./chunk-4EUSHAY6.js";
|
|
39
|
+
import {
|
|
40
|
+
assignPortOrders,
|
|
41
|
+
detectSugarPatterns,
|
|
42
|
+
filterStaleMacros,
|
|
43
|
+
formatJSDocDescription,
|
|
44
|
+
generateJSDocPortTag,
|
|
45
|
+
generateNodeInstanceTag,
|
|
46
|
+
httpRouteText,
|
|
47
|
+
isPathImpliedDataEdge,
|
|
48
|
+
planPortTags,
|
|
49
|
+
serializePackDeployAnnotations
|
|
50
|
+
} from "./chunk-VP2P3BPA.js";
|
|
51
|
+
import {
|
|
52
|
+
mapToTypeScript
|
|
53
|
+
} from "./chunk-E5WN7IGO.js";
|
|
54
|
+
import {
|
|
55
|
+
EXECUTION_STRATEGIES,
|
|
56
|
+
RESERVED_NODE_NAMES,
|
|
57
|
+
RESERVED_PORT_NAMES,
|
|
58
|
+
isExecutePort,
|
|
59
|
+
isExitNode,
|
|
60
|
+
isFailurePort,
|
|
61
|
+
isStartNode,
|
|
62
|
+
isSuccessPort
|
|
63
|
+
} from "./chunk-R4QGNK34.js";
|
|
64
|
+
import {
|
|
65
|
+
__require
|
|
66
|
+
} from "./chunk-SEWDMQ6Q.js";
|
|
67
|
+
|
|
68
|
+
// src/api/graph-identity.ts
|
|
69
|
+
var connectionKey = (c) => `${c.from.node}.${c.from.port}\0${c.to.node}.${c.to.port}`;
|
|
70
|
+
function portShape(ports) {
|
|
71
|
+
const shape = {};
|
|
72
|
+
for (const name of Object.keys(ports).sort()) {
|
|
73
|
+
const port = ports[name];
|
|
74
|
+
shape[name] = {
|
|
75
|
+
...port.optional ? { optional: true } : {},
|
|
76
|
+
...port.isControlFlow || port.dataType === "STEP" ? { control: true } : {},
|
|
77
|
+
...port.failure ? { failure: true } : {}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return shape;
|
|
81
|
+
}
|
|
82
|
+
function graphIdentity(root, allWorkflows = []) {
|
|
83
|
+
const analysis = validateDurableClosure(root, allWorkflows, { enforce: false });
|
|
84
|
+
const reachable = [...analysis.reachable].sort(
|
|
85
|
+
(left, right) => left.functionName.localeCompare(right.functionName)
|
|
86
|
+
);
|
|
87
|
+
const workflowsByName = new Map(
|
|
88
|
+
[...allWorkflows, root].map((workflow) => [workflow.functionName, workflow])
|
|
89
|
+
);
|
|
90
|
+
const manifest = JSON.parse(
|
|
91
|
+
JSON.stringify(
|
|
92
|
+
reachable.map((workflow) => ({
|
|
93
|
+
functionName: workflow.functionName,
|
|
94
|
+
// Declaration order is how the file happens to be written; a
|
|
95
|
+
// regenerated JSDoc lists the same connections in another order.
|
|
96
|
+
instances: [...workflow.instances].sort((left, right) => left.id.localeCompare(right.id)).map(({ sourceLocation: _sourceLocation, ...instance }) => instance),
|
|
97
|
+
connections: [...workflow.connections].sort((left, right) => connectionKey(left).localeCompare(connectionKey(right))).map(({ sourceLocation: _sourceLocation, ...connection }) => connection),
|
|
98
|
+
scopes: workflow.scopes,
|
|
99
|
+
startPorts: portShape(workflow.startPorts),
|
|
100
|
+
exitPorts: portShape(workflow.exitPorts),
|
|
101
|
+
nodeTypes: [...workflow.nodeTypes].sort((left, right) => left.name.localeCompare(right.name)).map((nodeType) => ({
|
|
102
|
+
name: nodeType.name,
|
|
103
|
+
functionName: nodeType.functionName,
|
|
104
|
+
inputs: portShape(nodeType.inputs),
|
|
105
|
+
outputs: portShape(nodeType.outputs),
|
|
106
|
+
expression: nodeType.expression === true,
|
|
107
|
+
scope: nodeType.scope,
|
|
108
|
+
durableGate: nodeType.durableGate,
|
|
109
|
+
durableEffect: nodeType.durableEffect === true,
|
|
110
|
+
durablePure: nodeType.durablePure === true
|
|
111
|
+
}))
|
|
112
|
+
}))
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
const graphFingerprint = sha256Hex(canonicalWireValue(manifest));
|
|
116
|
+
const continuationGraph = {
|
|
117
|
+
nodes: reachable.flatMap((workflow) => {
|
|
118
|
+
const executionOrder = getTopologicalOrder(workflow, { includeScopedChildren: true });
|
|
119
|
+
const branchPaths = durableBranchPaths(workflow);
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
workflowId: workflow.functionName,
|
|
123
|
+
nodeId: "Start",
|
|
124
|
+
nodeType: "Start",
|
|
125
|
+
executionOrder: -1,
|
|
126
|
+
inputPorts: [],
|
|
127
|
+
outputPorts: Object.keys(workflow.startPorts),
|
|
128
|
+
scopeNames: [],
|
|
129
|
+
invokedWorkflows: [],
|
|
130
|
+
branchArms: [],
|
|
131
|
+
branchPath: [],
|
|
132
|
+
predecessors: []
|
|
133
|
+
},
|
|
134
|
+
...workflow.instances.map((instance) => {
|
|
135
|
+
const nodeType = workflow.nodeTypes.find(
|
|
136
|
+
(candidate) => candidate.name === instance.nodeType || candidate.functionName === instance.nodeType
|
|
137
|
+
);
|
|
138
|
+
const instanceOrder = executionOrder.indexOf(instance.id);
|
|
139
|
+
const instanceBranchPath = branchPaths.get(instance.id) ?? [];
|
|
140
|
+
const invokedWorkflows = instance.nodeType === "invokeWorkflow" ? reachable.map((candidate) => candidate.functionName) : workflowsByName.has(instance.nodeType) ? [instance.nodeType] : [];
|
|
141
|
+
return {
|
|
142
|
+
workflowId: workflow.functionName,
|
|
143
|
+
nodeId: instance.id,
|
|
144
|
+
nodeType: nodeType?.functionName ?? instance.nodeType,
|
|
145
|
+
executionOrder: instanceOrder,
|
|
146
|
+
inputPorts: Object.keys(nodeType?.inputs ?? {}),
|
|
147
|
+
outputPorts: Object.keys(nodeType?.outputs ?? {}),
|
|
148
|
+
scopeNames: [
|
|
149
|
+
...nodeType?.scope === void 0 ? [] : [nodeType.scope],
|
|
150
|
+
...nodeType?.scopes ?? [],
|
|
151
|
+
...Object.values(nodeType?.inputs ?? {}).map((port) => port.scope).filter((scope) => scope !== void 0),
|
|
152
|
+
...Object.values(nodeType?.outputs ?? {}).map((port) => port.scope).filter((scope) => scope !== void 0)
|
|
153
|
+
].filter((scope, index, scopes) => scopes.indexOf(scope) === index),
|
|
154
|
+
invokedWorkflows,
|
|
155
|
+
...instance.parent !== void 0 && instance.parent !== null && {
|
|
156
|
+
parentScope: { parentNodeId: instance.parent.id, scopeName: instance.parent.scope }
|
|
157
|
+
},
|
|
158
|
+
branchArms: [
|
|
159
|
+
...Object.hasOwn(nodeType?.outputs ?? {}, "onSuccess") ? ["success"] : [],
|
|
160
|
+
...Object.hasOwn(nodeType?.outputs ?? {}, "onFailure") ? ["failure"] : []
|
|
161
|
+
],
|
|
162
|
+
branchPath: instanceBranchPath,
|
|
163
|
+
predecessors: [
|
|
164
|
+
{ nodeId: "Start", branchPath: [] },
|
|
165
|
+
...executionOrder.slice(0, instanceOrder).map((nodeId) => ({ nodeId, branchPath: branchPaths.get(nodeId) ?? [] })).filter(
|
|
166
|
+
(predecessor) => predecessor.branchPath.every(
|
|
167
|
+
(requirement) => instanceBranchPath.some(
|
|
168
|
+
(active) => active.nodeId === requirement.nodeId && active.arm === requirement.arm
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
],
|
|
173
|
+
...nodeType?.durableGate !== void 0 && { durableGate: nodeType.durableGate },
|
|
174
|
+
...nodeType?.durableEffect === true && { durableEffect: true }
|
|
175
|
+
};
|
|
176
|
+
})
|
|
177
|
+
];
|
|
178
|
+
})
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
reachable,
|
|
182
|
+
capabilities: { gate: analysis.hasDurableGate, effect: analysis.hasDurableEffect },
|
|
183
|
+
graphFingerprint,
|
|
184
|
+
continuationGraph
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/generator/async-detection.ts
|
|
189
|
+
function shouldWorkflowBeAsync(workflow, nodeTypes) {
|
|
190
|
+
const nodeTypeMap = /* @__PURE__ */ new Map();
|
|
191
|
+
for (const nt of nodeTypes) {
|
|
192
|
+
nodeTypeMap.set(nt.name, nt.isAsync);
|
|
193
|
+
if (nt.functionName && nt.functionName !== nt.name) {
|
|
194
|
+
nodeTypeMap.set(nt.functionName, nt.isAsync);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const instance of workflow.instances) {
|
|
198
|
+
const isAsync = nodeTypeMap.get(instance.nodeType);
|
|
199
|
+
if (isAsync === true) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function validateWorkflowAsync(workflow, nodeTypes) {
|
|
206
|
+
const computedAsync = shouldWorkflowBeAsync(workflow, nodeTypes);
|
|
207
|
+
const userAsync = workflow.userSpecifiedAsync ?? true;
|
|
208
|
+
if (!userAsync && computedAsync) {
|
|
209
|
+
return {
|
|
210
|
+
shouldBeAsync: true,
|
|
211
|
+
// Force async for correctness
|
|
212
|
+
warning: `Workflow function '${workflow.functionName}' was declared as sync, but contains async nodes. Generating as async function.`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return { shouldBeAsync: computedAsync || userAsync };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/api/in-place/node-type-jsdoc.ts
|
|
219
|
+
function generateNodeTypeJSDoc(nodeType) {
|
|
220
|
+
const lines = [];
|
|
221
|
+
lines.push("/**");
|
|
222
|
+
if (nodeType.description) {
|
|
223
|
+
lines.push(...formatJSDocDescription(nodeType.description));
|
|
224
|
+
lines.push(` *`);
|
|
225
|
+
}
|
|
226
|
+
lines.push(" * @flowWeaver nodeType");
|
|
227
|
+
if (nodeType.expression) {
|
|
228
|
+
lines.push(" * @expression");
|
|
229
|
+
}
|
|
230
|
+
if (nodeType.name && nodeType.name !== nodeType.functionName) {
|
|
231
|
+
lines.push(` * @name ${nodeType.name}`);
|
|
232
|
+
}
|
|
233
|
+
if (nodeType.label) {
|
|
234
|
+
lines.push(` * @label ${nodeType.label}`);
|
|
235
|
+
}
|
|
236
|
+
if (nodeType.scope) {
|
|
237
|
+
lines.push(` * @scope ${nodeType.scope}`);
|
|
238
|
+
}
|
|
239
|
+
if (nodeType.defaultConfig?.pullExecution) {
|
|
240
|
+
lines.push(` * @pullExecution ${nodeType.defaultConfig.pullExecution.triggerPort}`);
|
|
241
|
+
}
|
|
242
|
+
if (nodeType.resilience) {
|
|
243
|
+
const retries = nodeType.resilience.retries ? ` retries=${nodeType.resilience.retries}` : "";
|
|
244
|
+
const fallback = nodeType.resilience.fallback ? ` fallback="${nodeType.resilience.fallback.replace(/"/g, '\\"')}"` : "";
|
|
245
|
+
lines.push(` * @resilience${retries}${fallback}`);
|
|
246
|
+
}
|
|
247
|
+
lines.push(...durableClassificationLines(nodeType));
|
|
248
|
+
if (nodeType.visuals) {
|
|
249
|
+
if (nodeType.visuals.color) {
|
|
250
|
+
lines.push(` * @color ${nodeType.visuals.color}`);
|
|
251
|
+
}
|
|
252
|
+
if (nodeType.visuals.icon) {
|
|
253
|
+
lines.push(` * @icon ${nodeType.visuals.icon}`);
|
|
254
|
+
}
|
|
255
|
+
if (nodeType.visuals.tags && nodeType.visuals.tags.length > 0) {
|
|
256
|
+
for (const tag of nodeType.visuals.tags) {
|
|
257
|
+
if (tag.tooltip) {
|
|
258
|
+
lines.push(` * @tag ${tag.label} "${tag.tooltip}"`);
|
|
259
|
+
} else {
|
|
260
|
+
lines.push(` * @tag ${tag.label}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
let inputEntries = [];
|
|
266
|
+
let outputEntries = [];
|
|
267
|
+
if (nodeType.inputs && Object.keys(nodeType.inputs).length > 0) {
|
|
268
|
+
inputEntries = assignPortOrders(Object.entries(nodeType.inputs), "input");
|
|
269
|
+
outputEntries = assignPortOrders(Object.entries(nodeType.outputs || {}), "output");
|
|
270
|
+
} else if (nodeType.ports && nodeType.ports.length > 0) {
|
|
271
|
+
const inputs = [];
|
|
272
|
+
const outputs = [];
|
|
273
|
+
for (const port of nodeType.ports) {
|
|
274
|
+
if (isExecutePort(port.name) || isSuccessPort(port.name) || isFailurePort(port.name)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const portDef = {
|
|
278
|
+
dataType: port.type || port.dataType || "ANY",
|
|
279
|
+
label: port.defaultLabel || port.name,
|
|
280
|
+
scope: port.scope,
|
|
281
|
+
metadata: { order: port.defaultOrder }
|
|
282
|
+
};
|
|
283
|
+
if (port.direction === "INPUT") {
|
|
284
|
+
inputs.push([port.name, portDef]);
|
|
285
|
+
} else {
|
|
286
|
+
outputs.push([port.name, portDef]);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
inputEntries = assignPortOrders(inputs, "input");
|
|
290
|
+
outputEntries = assignPortOrders(outputs, "output");
|
|
291
|
+
}
|
|
292
|
+
for (const { name, port, writeOrder } of planPortTags(inputEntries)) {
|
|
293
|
+
lines.push(` * ${generateJSDocPortTag(name, port, "input", void 0, { writeOrder })}`);
|
|
294
|
+
}
|
|
295
|
+
for (const { name, port, writeOrder } of planPortTags(outputEntries)) {
|
|
296
|
+
lines.push(` * ${generateJSDocPortTag(name, port, "output", void 0, { writeOrder })}`);
|
|
297
|
+
}
|
|
298
|
+
lines.push(" */");
|
|
299
|
+
return lines.join("\n");
|
|
300
|
+
}
|
|
301
|
+
function durableClassificationLines(nodeType) {
|
|
302
|
+
const lines = [];
|
|
303
|
+
if (nodeType.durableGate) {
|
|
304
|
+
lines.push(` * @durableGate ${nodeType.durableGate}`);
|
|
305
|
+
}
|
|
306
|
+
if (nodeType.durableEffect) {
|
|
307
|
+
lines.push(" * @durableEffect");
|
|
308
|
+
}
|
|
309
|
+
if (nodeType.durablePure) {
|
|
310
|
+
lines.push(" * @durablePure");
|
|
311
|
+
}
|
|
312
|
+
return lines;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/api/in-place/built-in-nodes.ts
|
|
316
|
+
function inlineBuiltInNodes(source, ast, production) {
|
|
317
|
+
const usedNodeTypes = new Set(ast.instances.map((i) => i.nodeType));
|
|
318
|
+
const builtInNodes = ast.nodeTypes.filter(
|
|
319
|
+
(nt) => !nt.sourceLocation && nt.functionText && nt.helperText != null && usedNodeTypes.has(nt.name)
|
|
320
|
+
);
|
|
321
|
+
if (builtInNodes.length === 0) {
|
|
322
|
+
return { code: source, changed: false };
|
|
323
|
+
}
|
|
324
|
+
const workflowFnPattern = new RegExp(
|
|
325
|
+
`(/\\*\\*(?:(?!\\*/)[\\s\\S])*?@flowWeaver\\s+workflow(?:(?!\\*/)[\\s\\S])*?\\*/)\\s*\\n\\s*(?:export\\s+)?(?:async\\s+)?function\\s+${ast.functionName}\\b`
|
|
326
|
+
);
|
|
327
|
+
const match = source.match(workflowFnPattern);
|
|
328
|
+
if (!match || match.index === void 0) {
|
|
329
|
+
return { code: source, changed: false };
|
|
330
|
+
}
|
|
331
|
+
const insertionLines = [];
|
|
332
|
+
const emittedHelpers = /* @__PURE__ */ new Set();
|
|
333
|
+
for (const node of builtInNodes) {
|
|
334
|
+
const helperText = production ? node.helperTextProduction ?? null : node.helperText ?? null;
|
|
335
|
+
if (helperText && !emittedHelpers.has(helperText) && !source.includes(helperText)) {
|
|
336
|
+
emittedHelpers.add(helperText);
|
|
337
|
+
insertionLines.push(helperText);
|
|
338
|
+
insertionLines.push("");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (const node of builtInNodes) {
|
|
342
|
+
const funcText = production && node.functionTextProduction != null ? node.functionTextProduction : node.functionText;
|
|
343
|
+
if (funcText) {
|
|
344
|
+
const portAnnotations = [];
|
|
345
|
+
portAnnotations.push("/**");
|
|
346
|
+
portAnnotations.push(` * @flowWeaver nodeType`);
|
|
347
|
+
portAnnotations.push(...durableClassificationLines(node));
|
|
348
|
+
for (const [name, port] of Object.entries(node.inputs)) {
|
|
349
|
+
if (name === "execute") continue;
|
|
350
|
+
const optPrefix = port.optional ? "[" : "";
|
|
351
|
+
const optSuffix = port.optional ? "]" : "";
|
|
352
|
+
portAnnotations.push(` * @input ${optPrefix}${name}${optSuffix} - ${port.label || name}`);
|
|
353
|
+
}
|
|
354
|
+
for (const [name, port] of Object.entries(node.outputs)) {
|
|
355
|
+
if (name === "onSuccess" || name === "onFailure") continue;
|
|
356
|
+
portAnnotations.push(` * @output ${name} - ${port.label || name}`);
|
|
357
|
+
}
|
|
358
|
+
portAnnotations.push(" */");
|
|
359
|
+
insertionLines.push(portAnnotations.join("\n"));
|
|
360
|
+
insertionLines.push(funcText);
|
|
361
|
+
insertionLines.push("");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const insertionCode = insertionLines.join("\n");
|
|
365
|
+
return {
|
|
366
|
+
code: source.slice(0, match.index) + insertionCode + "\n" + source.slice(match.index),
|
|
367
|
+
changed: true
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/generator/control-flow-plan.ts
|
|
372
|
+
function getPullExecutionConfig(instance, nodeType) {
|
|
373
|
+
if (instance.config?.pullExecution) {
|
|
374
|
+
const pullConfig = instance.config.pullExecution;
|
|
375
|
+
if (typeof pullConfig === "boolean") {
|
|
376
|
+
return { enabled: pullConfig, triggerPort: "execute" };
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
enabled: true,
|
|
380
|
+
triggerPort: pullConfig.triggerPort || "execute"
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (nodeType.defaultConfig?.pullExecution) {
|
|
384
|
+
const pullConfig = nodeType.defaultConfig.pullExecution;
|
|
385
|
+
if (typeof pullConfig === "boolean") {
|
|
386
|
+
return { enabled: pullConfig, triggerPort: "execute" };
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
enabled: true,
|
|
390
|
+
triggerPort: pullConfig.triggerPort || "execute"
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return { enabled: false, triggerPort: "execute" };
|
|
394
|
+
}
|
|
395
|
+
function analyzeControlFlow(workflow, nodeTypes, isAsync, durableSequential) {
|
|
396
|
+
const cfg = buildControlFlowGraph(workflow, nodeTypes);
|
|
397
|
+
const executionOrder = performKahnsTopologicalSort(cfg);
|
|
398
|
+
const branchingNodes = findAllBranchingNodes(workflow, nodeTypes);
|
|
399
|
+
const branchRegions = findBranchRegions(workflow, branchingNodes);
|
|
400
|
+
const nodesInBranches = /* @__PURE__ */ new Set();
|
|
401
|
+
branchRegions.forEach((region) => {
|
|
402
|
+
region.successNodes.forEach((n) => nodesInBranches.add(n));
|
|
403
|
+
region.failureNodes.forEach((n) => nodesInBranches.add(n));
|
|
404
|
+
});
|
|
405
|
+
const pullExecutionNodes = /* @__PURE__ */ new Set();
|
|
406
|
+
workflow.instances.forEach((instance) => {
|
|
407
|
+
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
408
|
+
if (nodeType) {
|
|
409
|
+
const pullConfig = getPullExecutionConfig(instance, nodeType);
|
|
410
|
+
if (pullConfig.enabled) {
|
|
411
|
+
pullExecutionNodes.add(instance.id);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
const nodeLevelScopedChildren = /* @__PURE__ */ new Set();
|
|
416
|
+
const perPortScopedChildren = /* @__PURE__ */ new Set();
|
|
417
|
+
workflow.instances.forEach((instance) => {
|
|
418
|
+
if (isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
419
|
+
perPortScopedChildren.add(instance.id);
|
|
420
|
+
} else if (instance.parent) {
|
|
421
|
+
nodeLevelScopedChildren.add(instance.id);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
const conditionalNodes = /* @__PURE__ */ new Set([
|
|
425
|
+
...nodesInBranches,
|
|
426
|
+
...branchingNodes,
|
|
427
|
+
...pullExecutionNodes,
|
|
428
|
+
...nodeLevelScopedChildren
|
|
429
|
+
]);
|
|
430
|
+
const letIndexNodes = workflow.instances.filter((instance) => !isPerPortScopedChild(instance, workflow, nodeTypes) && conditionalNodes.has(instance.id)).map((instance) => instance.id);
|
|
431
|
+
const topLevelSuccessFlags = /* @__PURE__ */ new Set();
|
|
432
|
+
branchRegions.forEach((region, nodeId) => {
|
|
433
|
+
if (region.successNodes.size > 0 || region.failureNodes.size > 0) {
|
|
434
|
+
topLevelSuccessFlags.add(toValidIdentifier(nodeId));
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
const promotedNodes = promoteNodesOutOfBranches(workflow, branchRegions);
|
|
438
|
+
const branchingNodesNeedingSuccessFlag = /* @__PURE__ */ new Set();
|
|
439
|
+
promotedNodes.forEach((promotedNodeId) => {
|
|
440
|
+
workflow.connections.forEach((conn) => {
|
|
441
|
+
if (conn.to.node === promotedNodeId && isExecutePort(conn.to.port)) {
|
|
442
|
+
const sourceNode = conn.from.node;
|
|
443
|
+
const sourcePort = conn.from.port;
|
|
444
|
+
if (branchingNodes.has(sourceNode) && (isSuccessPort(sourcePort) || isFailurePort(sourcePort))) {
|
|
445
|
+
branchingNodesNeedingSuccessFlag.add(sourceNode);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
const branchingChains = durableSequential ? /* @__PURE__ */ new Map() : detectBranchingChains(branchingNodes, branchRegions);
|
|
451
|
+
const chainMembers = /* @__PURE__ */ new Set();
|
|
452
|
+
branchingChains.forEach((chain) => {
|
|
453
|
+
for (let i = 1; i < chain.length; i++) {
|
|
454
|
+
chainMembers.add(chain[i]);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
const parallelGroupOf = /* @__PURE__ */ new Map();
|
|
458
|
+
if (isAsync && !durableSequential) {
|
|
459
|
+
const parallelLevels = computeParallelLevels(cfg, branchingNodes, perPortScopedChildren);
|
|
460
|
+
for (const group of parallelLevels) {
|
|
461
|
+
if (group.length < 2) continue;
|
|
462
|
+
const eligible = group.filter(
|
|
463
|
+
(id) => !conditionalNodes.has(id) && !promotedNodes.has(id) && !chainMembers.has(id)
|
|
464
|
+
);
|
|
465
|
+
if (eligible.length >= 2) {
|
|
466
|
+
for (const nodeId of eligible) {
|
|
467
|
+
parallelGroupOf.set(nodeId, eligible);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
executionOrder,
|
|
474
|
+
branchingNodes,
|
|
475
|
+
branchRegions,
|
|
476
|
+
conditionalNodes,
|
|
477
|
+
letIndexNodes,
|
|
478
|
+
perPortScopedChildren,
|
|
479
|
+
topLevelSuccessFlags,
|
|
480
|
+
promotedNodes,
|
|
481
|
+
branchingNodesNeedingSuccessFlag,
|
|
482
|
+
branchingChains,
|
|
483
|
+
chainMembers,
|
|
484
|
+
parallelGroupOf
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
function findBranchRegions(workflow, branchingNodes) {
|
|
488
|
+
const allInstanceIds = new Set(workflow.instances.map((i) => i.id));
|
|
489
|
+
const branchRegions = /* @__PURE__ */ new Map();
|
|
490
|
+
branchingNodes.forEach((branchInstanceId) => {
|
|
491
|
+
const successNodes = findNodesInBranch(
|
|
492
|
+
branchInstanceId,
|
|
493
|
+
RESERVED_PORT_NAMES.ON_SUCCESS,
|
|
494
|
+
workflow,
|
|
495
|
+
allInstanceIds,
|
|
496
|
+
branchingNodes
|
|
497
|
+
);
|
|
498
|
+
const failureNodes = findNodesInBranch(
|
|
499
|
+
branchInstanceId,
|
|
500
|
+
RESERVED_PORT_NAMES.ON_FAILURE,
|
|
501
|
+
workflow,
|
|
502
|
+
allInstanceIds,
|
|
503
|
+
branchingNodes
|
|
504
|
+
);
|
|
505
|
+
branchRegions.set(branchInstanceId, { successNodes, failureNodes });
|
|
506
|
+
});
|
|
507
|
+
return branchRegions;
|
|
508
|
+
}
|
|
509
|
+
function promoteNodesOutOfBranches(workflow, branchRegions) {
|
|
510
|
+
const instancesInMultipleBranches = /* @__PURE__ */ new Set();
|
|
511
|
+
workflow.instances.forEach(({ id: instanceId }) => {
|
|
512
|
+
let branchCount = 0;
|
|
513
|
+
branchRegions.forEach((region) => {
|
|
514
|
+
if (region.successNodes.has(instanceId) || region.failureNodes.has(instanceId)) {
|
|
515
|
+
branchCount++;
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
if (branchCount > 1) {
|
|
519
|
+
instancesInMultipleBranches.add(instanceId);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
removeFromRegions(branchRegions, instancesInMultipleBranches);
|
|
523
|
+
const nodesInBothBranches = /* @__PURE__ */ new Set();
|
|
524
|
+
branchRegions.forEach((region) => {
|
|
525
|
+
region.successNodes.forEach((nodeId) => {
|
|
526
|
+
if (region.failureNodes.has(nodeId)) {
|
|
527
|
+
nodesInBothBranches.add(nodeId);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
removeFromRegions(branchRegions, nodesInBothBranches);
|
|
532
|
+
const promotedNodes = new Set(nodesInBothBranches);
|
|
533
|
+
branchRegions.forEach((region, branchNodeId) => {
|
|
534
|
+
const allBranchNodes = /* @__PURE__ */ new Set([...region.successNodes, ...region.failureNodes]);
|
|
535
|
+
allBranchNodes.forEach((nodeId) => {
|
|
536
|
+
const hasExternalDataDep = workflow.connections.some((conn) => {
|
|
537
|
+
if (conn.to.node !== nodeId) return false;
|
|
538
|
+
if (conn.from.scope || conn.to.scope) return false;
|
|
539
|
+
const fromNode = conn.from.node;
|
|
540
|
+
if (fromNode === branchNodeId || isStartNode(fromNode)) return false;
|
|
541
|
+
if (isExecutePort(conn.to.port)) return false;
|
|
542
|
+
return !allBranchNodes.has(fromNode);
|
|
543
|
+
});
|
|
544
|
+
if (hasExternalDataDep) {
|
|
545
|
+
promotedNodes.add(nodeId);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
removeFromRegions(branchRegions, promotedNodes);
|
|
550
|
+
return promotedNodes;
|
|
551
|
+
}
|
|
552
|
+
function removeFromRegions(branchRegions, nodeIds) {
|
|
553
|
+
branchRegions.forEach((region) => {
|
|
554
|
+
nodeIds.forEach((nodeId) => {
|
|
555
|
+
region.successNodes.delete(nodeId);
|
|
556
|
+
region.failureNodes.delete(nodeId);
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/generator/exit-assembly.ts
|
|
562
|
+
function emitExitAssembly(lines, workflow, nodeTypes, plan, isAsync, production) {
|
|
563
|
+
lines.push(` ctx.checkAborted('${RESERVED_NODE_NAMES.EXIT}');`);
|
|
564
|
+
lines.push(` const exitIdx = ctx.addExecution('${RESERVED_NODE_NAMES.EXIT}');`);
|
|
565
|
+
const returnProps = emitConnectedExitPorts(lines, workflow, nodeTypes, plan, isAsync, production);
|
|
566
|
+
emitUnconnectedExitPortDefaults(lines, workflow, returnProps);
|
|
567
|
+
lines.push("");
|
|
568
|
+
emitFinalResult(lines, returnProps, isAsync, production);
|
|
569
|
+
}
|
|
570
|
+
function emitConnectedExitPorts(lines, workflow, nodeTypes, plan, isAsync, production) {
|
|
571
|
+
const exitConnections = workflow.connections.filter((conn) => isExitNode(conn.to.node));
|
|
572
|
+
const exitConnectionsByPort = /* @__PURE__ */ new Map();
|
|
573
|
+
exitConnections.forEach((conn) => {
|
|
574
|
+
const existing = exitConnectionsByPort.get(conn.to.port) || [];
|
|
575
|
+
existing.push(conn);
|
|
576
|
+
exitConnectionsByPort.set(conn.to.port, existing);
|
|
577
|
+
});
|
|
578
|
+
const returnProps = [];
|
|
579
|
+
const setCall = isAsync ? "await ctx.setVariable" : "ctx.setVariable";
|
|
580
|
+
exitConnectionsByPort.forEach((conns, exitPort) => {
|
|
581
|
+
const exitPortDef = workflow.exitPorts[exitPort];
|
|
582
|
+
if (!exitPortDef) {
|
|
583
|
+
lines.push(` // Exit connection skipped: '${exitPort}' is not a declared @returns port`);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const varName = `exit_${exitPort}`;
|
|
587
|
+
const exitPortType = exitPortDef?.tsType || (exitPortDef ? mapToTypeScript(exitPortDef.dataType) : "unknown");
|
|
588
|
+
const validConns = conns.filter((conn) => {
|
|
589
|
+
const sourceNode = conn.from.node;
|
|
590
|
+
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
591
|
+
const sourceNodeType = nodeTypes.find(
|
|
592
|
+
(n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType
|
|
593
|
+
);
|
|
594
|
+
if (!isStartNode(sourceNode) && !sourceInstance) {
|
|
595
|
+
lines.push(` // Exit connection skipped: source node '${sourceNode}' is not declared`);
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
if (!isStartNode(sourceNode) && sourceInstance && !sourceNodeType) {
|
|
599
|
+
lines.push(
|
|
600
|
+
` // Exit connection skipped: source node '${sourceNode}' has missing type '${sourceInstance.nodeType}'`
|
|
601
|
+
);
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
return true;
|
|
605
|
+
});
|
|
606
|
+
if (validConns.length === 0) {
|
|
607
|
+
lines.push(` const ${varName} = undefined as unknown;`);
|
|
608
|
+
returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const isControlFlowPort = exitPort === "onSuccess" || exitPort === "onFailure";
|
|
612
|
+
const defaultValue = isControlFlowPort ? "false" : "undefined";
|
|
613
|
+
if (validConns.length === 1) {
|
|
614
|
+
lines.push(
|
|
615
|
+
` const ${varName} = ${exitSourceExpression(validConns[0], defaultValue, workflow, nodeTypes, plan, isAsync)};`
|
|
616
|
+
);
|
|
617
|
+
} else {
|
|
618
|
+
const operator = isControlFlowPort ? " || " : " ?? ";
|
|
619
|
+
const parts = validConns.map(
|
|
620
|
+
(conn) => `(${exitSourceExpression(conn, defaultValue, workflow, nodeTypes, plan, isAsync)})`
|
|
621
|
+
);
|
|
622
|
+
lines.push(` const ${varName} = ${parts.join(operator)};`);
|
|
623
|
+
}
|
|
624
|
+
if (!production) {
|
|
625
|
+
lines.push(
|
|
626
|
+
` ${setCall}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: '${exitPort}', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, ${varName});`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
returnProps.push(`${exitPort}: ${varName} as ${exitPortType}`);
|
|
630
|
+
});
|
|
631
|
+
return returnProps;
|
|
632
|
+
}
|
|
633
|
+
function exitSourceExpression(conn, defaultValue, workflow, nodeTypes, plan, isAsync) {
|
|
634
|
+
const awaitKeyword = isAsync ? "await " : "";
|
|
635
|
+
const sourceNode = conn.from.node;
|
|
636
|
+
const sourcePort = conn.from.port;
|
|
637
|
+
const sourceIdx = isStartNode(sourceNode) ? "startIdx" : `${toValidIdentifier(sourceNode)}Idx`;
|
|
638
|
+
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
639
|
+
const sourceNodeType = nodeTypes.find(
|
|
640
|
+
(n) => n.name === sourceInstance?.nodeType || n.functionName === sourceInstance?.nodeType
|
|
641
|
+
);
|
|
642
|
+
const sourceNodeTypeName = isStartNode(sourceNode) ? RESERVED_NODE_NAMES.START : sourceNodeType?.functionName ?? sourceInstance?.nodeType ?? sourceNode;
|
|
643
|
+
const pullConfig = sourceInstance && sourceNodeType ? getPullExecutionConfig(sourceInstance, sourceNodeType) : { enabled: false, triggerPort: "execute" };
|
|
644
|
+
const isPullNode = pullConfig.enabled;
|
|
645
|
+
const needsUndefinedCheck = !isStartNode(sourceNode) && plan.conditionalNodes.has(sourceNode);
|
|
646
|
+
if (isPullNode) {
|
|
647
|
+
return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx} ?? 0, nodeTypeName: '${sourceNodeTypeName}' })`;
|
|
648
|
+
} else if (needsUndefinedCheck) {
|
|
649
|
+
return `${sourceIdx} !== undefined ? ${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx}, nodeTypeName: '${sourceNodeTypeName}' }) : ${defaultValue}`;
|
|
650
|
+
} else {
|
|
651
|
+
return `${awaitKeyword}ctx.getVariable({ id: '${sourceNode}', portName: '${sourcePort}', executionIndex: ${sourceIdx}, nodeTypeName: '${sourceNodeTypeName}' })`;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function emitUnconnectedExitPortDefaults(lines, workflow, returnProps) {
|
|
655
|
+
const connectedExitPorts = new Set(returnProps.map((prop) => prop.split(":")[0]));
|
|
656
|
+
Object.entries(workflow.exitPorts).forEach(([portName, portDef]) => {
|
|
657
|
+
if (!connectedExitPorts.has(portName) && portName !== "onSuccess" && portName !== "onFailure") {
|
|
658
|
+
const portType = portDef?.tsType || (portDef ? mapToTypeScript(portDef.dataType) : "unknown");
|
|
659
|
+
lines.push(` // Exit port '${portName}' has no valid connection - using undefined`);
|
|
660
|
+
returnProps.push(`${portName}: undefined as unknown as ${portType}`);
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
function emitFinalResult(lines, returnProps, isAsync, production) {
|
|
665
|
+
const hasOnSuccess = returnProps.some((prop) => prop.startsWith("onSuccess:"));
|
|
666
|
+
const hasOnFailure = returnProps.some((prop) => prop.startsWith("onFailure:"));
|
|
667
|
+
const defaults = [];
|
|
668
|
+
const setCallForDefaults = isAsync ? "await ctx.setVariable" : "ctx.setVariable";
|
|
669
|
+
if (!hasOnSuccess) {
|
|
670
|
+
defaults.push("onSuccess: true");
|
|
671
|
+
if (!production) {
|
|
672
|
+
lines.push(
|
|
673
|
+
` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onSuccess', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, true);`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (!hasOnFailure) {
|
|
678
|
+
defaults.push("onFailure: false");
|
|
679
|
+
if (!production) {
|
|
680
|
+
lines.push(
|
|
681
|
+
` ${setCallForDefaults}({ id: '${RESERVED_NODE_NAMES.EXIT}', portName: 'onFailure', executionIndex: exitIdx, nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}' }, false);`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const allPropsUnsorted = [...defaults, ...returnProps];
|
|
686
|
+
const onSuccessProp = allPropsUnsorted.find((p) => p.trimStart().startsWith("onSuccess"));
|
|
687
|
+
const onFailureProp = allPropsUnsorted.find((p) => p.trimStart().startsWith("onFailure"));
|
|
688
|
+
const dataProps = allPropsUnsorted.filter((p) => {
|
|
689
|
+
const key = p.trimStart().split(":")[0].trim();
|
|
690
|
+
return key !== "onSuccess" && key !== "onFailure";
|
|
691
|
+
});
|
|
692
|
+
const orderedProps = [];
|
|
693
|
+
if (onSuccessProp) orderedProps.push(onSuccessProp);
|
|
694
|
+
if (onFailureProp) orderedProps.push(onFailureProp);
|
|
695
|
+
orderedProps.push(...dataProps);
|
|
696
|
+
const allProps = orderedProps.join(", ");
|
|
697
|
+
const awaitPrefix = isAsync ? "await " : "";
|
|
698
|
+
lines.push(` const finalResult = { ${allProps} };`);
|
|
699
|
+
lines.push("");
|
|
700
|
+
lines.push(` ${awaitPrefix}ctx.sendStatusChangedEvent({`);
|
|
701
|
+
lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.EXIT}',`);
|
|
702
|
+
lines.push(` id: '${RESERVED_NODE_NAMES.EXIT}',`);
|
|
703
|
+
lines.push(` executionIndex: exitIdx,`);
|
|
704
|
+
lines.push(` status: 'SUCCEEDED',`);
|
|
705
|
+
lines.push(` });`);
|
|
706
|
+
lines.push(` ctx.sendWorkflowCompletedEvent({`);
|
|
707
|
+
lines.push(` executionIndex: exitIdx,`);
|
|
708
|
+
lines.push(` status: 'SUCCEEDED',`);
|
|
709
|
+
lines.push(` result: finalResult,`);
|
|
710
|
+
lines.push(` });`);
|
|
711
|
+
lines.push("");
|
|
712
|
+
lines.push(` return finalResult;`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/generator/unified.ts
|
|
716
|
+
function startPortValue(portName, port) {
|
|
717
|
+
const supplied = `params.${portName}`;
|
|
718
|
+
if (port.default === void 0) return supplied;
|
|
719
|
+
return `${supplied} === undefined ? ${JSON.stringify(port.default)} : ${supplied}`;
|
|
720
|
+
}
|
|
721
|
+
function generateControlFlowWithExecutionContext(workflow, nodeTypes, isAsync, production = false, bundleMode = false, durableSequential = false, identity) {
|
|
722
|
+
const bodyAsync = isAsync || !production;
|
|
723
|
+
const lines = [];
|
|
724
|
+
emitContextSetup(lines, workflow, bodyAsync, production, identity);
|
|
725
|
+
emitStartNode(lines, workflow, bodyAsync);
|
|
726
|
+
const plan = analyzeControlFlow(workflow, nodeTypes, bodyAsync, durableSequential);
|
|
727
|
+
emitExecutionIndexDeclarations(lines, plan);
|
|
728
|
+
emitNodes({
|
|
729
|
+
workflow,
|
|
730
|
+
nodeTypes,
|
|
731
|
+
isAsync: bodyAsync,
|
|
732
|
+
production,
|
|
733
|
+
bundleMode,
|
|
734
|
+
plan,
|
|
735
|
+
lines,
|
|
736
|
+
generatedNodes: /* @__PURE__ */ new Set()
|
|
737
|
+
});
|
|
738
|
+
emitExitAssembly(lines, workflow, nodeTypes, plan, bodyAsync, production);
|
|
739
|
+
return lines.join("\n");
|
|
740
|
+
}
|
|
741
|
+
function emitContextSetup(lines, workflow, isAsync, production, identity) {
|
|
742
|
+
lines.push(` // Recursion depth protection`);
|
|
743
|
+
lines.push(` const __rd__ = (params as { __rd__?: number }).__rd__ ?? 0;`);
|
|
744
|
+
lines.push(` if (__rd__ >= 1000) {`);
|
|
745
|
+
lines.push(` throw new Error('Max recursion depth exceeded (1000) in workflow "${workflow.functionName}"');`);
|
|
746
|
+
lines.push(` }`);
|
|
747
|
+
lines.push("");
|
|
748
|
+
const asyncArg = isAsync ? "true" : "false";
|
|
749
|
+
lines.push(` const ctx = new GeneratedExecutionContext(${asyncArg}, __runtime__);`);
|
|
750
|
+
if (identity !== void 0) {
|
|
751
|
+
lines.push(` ctx.bindWorkflow('${workflow.functionName}', '${identity.graphFingerprint}');`);
|
|
752
|
+
}
|
|
753
|
+
lines.push("");
|
|
754
|
+
if (!production) {
|
|
755
|
+
lines.push(
|
|
756
|
+
` const __ctrl__: TDebugController = (__runtime__.services.debugController ?? { beforeNode: () => {}, afterNode: () => {} }) as TDebugController;`
|
|
757
|
+
);
|
|
758
|
+
lines.push("");
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function emitStartNode(lines, workflow, isAsync) {
|
|
762
|
+
lines.push(` const startIdx = ctx.addExecution('${RESERVED_NODE_NAMES.START}');`);
|
|
763
|
+
lines.push(` if (ctx.shouldExecute('${RESERVED_NODE_NAMES.START}', '${RESERVED_NODE_NAMES.START}', startIdx)) {`);
|
|
764
|
+
const awaitPrefix = isAsync ? "await " : "";
|
|
765
|
+
Object.entries(extractStartPorts(workflow)).forEach(([portName, port]) => {
|
|
766
|
+
const setCall = isAsync ? `await ctx.setVariable` : `ctx.setVariable`;
|
|
767
|
+
const valueSource = isExecutePort(portName) ? "execute" : startPortValue(portName, port);
|
|
768
|
+
lines.push(
|
|
769
|
+
` ${setCall}({ id: '${RESERVED_NODE_NAMES.START}', portName: '${portName}', executionIndex: startIdx, nodeTypeName: '${RESERVED_NODE_NAMES.START}' }, ${valueSource});`
|
|
770
|
+
);
|
|
771
|
+
});
|
|
772
|
+
lines.push(` ${awaitPrefix}ctx.sendStatusChangedEvent({`);
|
|
773
|
+
lines.push(` nodeTypeName: '${RESERVED_NODE_NAMES.START}',`);
|
|
774
|
+
lines.push(` id: '${RESERVED_NODE_NAMES.START}',`);
|
|
775
|
+
lines.push(` executionIndex: startIdx,`);
|
|
776
|
+
lines.push(` status: 'SUCCEEDED',`);
|
|
777
|
+
lines.push(` });`);
|
|
778
|
+
lines.push(` ctx.commitNode('${RESERVED_NODE_NAMES.START}', '${RESERVED_NODE_NAMES.START}', startIdx);`);
|
|
779
|
+
lines.push(` }`);
|
|
780
|
+
lines.push("");
|
|
781
|
+
}
|
|
782
|
+
function emitExecutionIndexDeclarations(lines, plan) {
|
|
783
|
+
plan.letIndexNodes.forEach((instanceId) => {
|
|
784
|
+
lines.push(` let ${toValidIdentifier(instanceId)}Idx: number | undefined;`);
|
|
785
|
+
});
|
|
786
|
+
plan.topLevelSuccessFlags.forEach((safeId) => {
|
|
787
|
+
lines.push(` let ${safeId}_success = false;`);
|
|
788
|
+
});
|
|
789
|
+
if (plan.letIndexNodes.length > 0 || plan.topLevelSuccessFlags.size > 0) {
|
|
790
|
+
lines.push("");
|
|
791
|
+
}
|
|
792
|
+
if (plan.parallelGroupOf.size > 0) {
|
|
793
|
+
plan.parallelGroupOf.forEach((_, instanceId) => {
|
|
794
|
+
if (!plan.conditionalNodes.has(instanceId)) {
|
|
795
|
+
lines.push(` let ${toValidIdentifier(instanceId)}Idx: number | undefined;`);
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
lines.push("");
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function emitNodes(body) {
|
|
802
|
+
const { workflow, nodeTypes, plan, lines, generatedNodes } = body;
|
|
803
|
+
for (const instanceId of plan.executionOrder) {
|
|
804
|
+
if (isStartNode(instanceId) || isExitNode(instanceId) || generatedNodes.has(instanceId)) {
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
const instance = workflow.instances.find((i) => i.id === instanceId);
|
|
808
|
+
if (!instance) {
|
|
809
|
+
lines.push(` // Node '${instanceId}' skipped: instance not found in workflow`);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (isPerPortScopedChild(instance, workflow, nodeTypes)) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
816
|
+
if (!nodeType) {
|
|
817
|
+
lines.push(` // Node '${instance.id}' skipped: type '${instance.nodeType}' not found`);
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
const group = plan.parallelGroupOf.get(instanceId);
|
|
821
|
+
if (group && emitParallelGroup(body, group)) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
if (plan.branchingNodes.has(instanceId)) {
|
|
825
|
+
emitTopLevelBranchingNode(body, instance, nodeType);
|
|
826
|
+
} else {
|
|
827
|
+
emitTopLevelNode(body, instance, nodeType);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function emitParallelGroup(body, group) {
|
|
832
|
+
const { workflow, nodeTypes, isAsync, production, bundleMode, plan, lines, generatedNodes } = body;
|
|
833
|
+
const ungeneratedGroup = group.filter((id) => !generatedNodes.has(id));
|
|
834
|
+
if (ungeneratedGroup.length < 2) {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
generateParallelGroupWithContext(
|
|
838
|
+
ungeneratedGroup,
|
|
839
|
+
workflow,
|
|
840
|
+
nodeTypes,
|
|
841
|
+
lines,
|
|
842
|
+
generatedNodes,
|
|
843
|
+
" ",
|
|
844
|
+
isAsync,
|
|
845
|
+
"ctx",
|
|
846
|
+
bundleMode,
|
|
847
|
+
plan.branchingNodes,
|
|
848
|
+
production
|
|
849
|
+
);
|
|
850
|
+
for (const parallelNodeId of ungeneratedGroup) {
|
|
851
|
+
const inst = workflow.instances.find((i) => i.id === parallelNodeId);
|
|
852
|
+
if (!inst) continue;
|
|
853
|
+
const nt = nodeTypes.find((n) => n.name === inst.nodeType || n.functionName === inst.nodeType);
|
|
854
|
+
if (!nt) continue;
|
|
855
|
+
generateScopedChildrenExecution(
|
|
856
|
+
inst,
|
|
857
|
+
nt,
|
|
858
|
+
workflow,
|
|
859
|
+
nodeTypes,
|
|
860
|
+
generatedNodes,
|
|
861
|
+
lines,
|
|
862
|
+
" ",
|
|
863
|
+
plan.branchingNodes,
|
|
864
|
+
plan.branchRegions,
|
|
865
|
+
isAsync,
|
|
866
|
+
bundleMode,
|
|
867
|
+
production
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
function promotedStepGuard(body, instanceId) {
|
|
873
|
+
const { workflow, plan } = body;
|
|
874
|
+
if (!plan.promotedNodes.has(instanceId)) {
|
|
875
|
+
return void 0;
|
|
876
|
+
}
|
|
877
|
+
const stepSourceConditions = [];
|
|
878
|
+
workflow.connections.forEach((conn) => {
|
|
879
|
+
if (conn.to.node === instanceId && isExecutePort(conn.to.port)) {
|
|
880
|
+
const src = conn.from.node;
|
|
881
|
+
if (!isStartNode(src)) {
|
|
882
|
+
stepSourceConditions.push(buildStepSourceCondition(src, conn.from.port, plan.branchingNodes));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
return stepSourceConditions.length > 0 ? stepSourceConditions.join(" || ") : void 0;
|
|
887
|
+
}
|
|
888
|
+
function emitTopLevelBranchingNode(body, instance, nodeType) {
|
|
889
|
+
const { workflow, nodeTypes, isAsync, production, bundleMode, plan, lines, generatedNodes } = body;
|
|
890
|
+
const instanceId = instance.id;
|
|
891
|
+
if (plan.chainMembers.has(instanceId)) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const guard = promotedStepGuard(body, instanceId);
|
|
895
|
+
const indent = guard === void 0 ? " " : " ";
|
|
896
|
+
if (guard !== void 0) {
|
|
897
|
+
lines.push(` if (${guard}) {`);
|
|
898
|
+
}
|
|
899
|
+
const chain = plan.branchingChains.get(instanceId);
|
|
900
|
+
if (chain) {
|
|
901
|
+
generateBranchingChainCode(
|
|
902
|
+
chain,
|
|
903
|
+
workflow,
|
|
904
|
+
nodeTypes,
|
|
905
|
+
plan.branchingNodes,
|
|
906
|
+
plan.branchRegions,
|
|
907
|
+
generatedNodes,
|
|
908
|
+
lines,
|
|
909
|
+
indent,
|
|
910
|
+
isAsync,
|
|
911
|
+
"ctx",
|
|
912
|
+
bundleMode,
|
|
913
|
+
plan.branchingNodesNeedingSuccessFlag,
|
|
914
|
+
production,
|
|
915
|
+
plan.topLevelSuccessFlags
|
|
916
|
+
);
|
|
917
|
+
if (guard !== void 0) {
|
|
918
|
+
lines.push(` }`);
|
|
919
|
+
}
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const region = plan.branchRegions.get(instanceId);
|
|
923
|
+
generateBranchingNodeCode(
|
|
924
|
+
instance,
|
|
925
|
+
nodeType,
|
|
926
|
+
workflow,
|
|
927
|
+
nodeTypes,
|
|
928
|
+
region,
|
|
929
|
+
generatedNodes,
|
|
930
|
+
lines,
|
|
931
|
+
indent,
|
|
932
|
+
plan.branchingNodes,
|
|
933
|
+
plan.branchRegions,
|
|
934
|
+
isAsync,
|
|
935
|
+
"ctx",
|
|
936
|
+
bundleMode,
|
|
937
|
+
new Set(plan.topLevelSuccessFlags),
|
|
938
|
+
plan.branchingNodesNeedingSuccessFlag.has(instanceId) || plan.topLevelSuccessFlags.has(toValidIdentifier(instanceId)),
|
|
939
|
+
production
|
|
940
|
+
);
|
|
941
|
+
if (guard !== void 0) {
|
|
942
|
+
lines.push(` }`);
|
|
943
|
+
}
|
|
944
|
+
region.successNodes.forEach((n) => generatedNodes.add(n));
|
|
945
|
+
region.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
946
|
+
generateScopedChildrenExecution(
|
|
947
|
+
instance,
|
|
948
|
+
nodeType,
|
|
949
|
+
workflow,
|
|
950
|
+
nodeTypes,
|
|
951
|
+
generatedNodes,
|
|
952
|
+
lines,
|
|
953
|
+
" ",
|
|
954
|
+
plan.branchingNodes,
|
|
955
|
+
plan.branchRegions,
|
|
956
|
+
isAsync,
|
|
957
|
+
bundleMode,
|
|
958
|
+
production
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
function emitTopLevelNode(body, instance, nodeType) {
|
|
962
|
+
const { workflow, nodeTypes, isAsync, production, bundleMode, plan, lines, generatedNodes } = body;
|
|
963
|
+
const instanceId = instance.id;
|
|
964
|
+
const belongsToBranch = Array.from(plan.branchRegions.values()).some(
|
|
965
|
+
(region) => region.successNodes.has(instanceId) || region.failureNodes.has(instanceId)
|
|
966
|
+
);
|
|
967
|
+
if (belongsToBranch) {
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
const nodeUseConst = !plan.conditionalNodes.has(instanceId) && !plan.parallelGroupOf.has(instanceId);
|
|
971
|
+
generateNodeCallWithContext(
|
|
972
|
+
instance,
|
|
973
|
+
nodeType,
|
|
974
|
+
workflow,
|
|
975
|
+
lines,
|
|
976
|
+
nodeTypes,
|
|
977
|
+
" ",
|
|
978
|
+
isAsync,
|
|
979
|
+
nodeUseConst,
|
|
980
|
+
void 0,
|
|
981
|
+
// instanceParent
|
|
982
|
+
"ctx",
|
|
983
|
+
// ctxVar
|
|
984
|
+
bundleMode,
|
|
985
|
+
false,
|
|
986
|
+
// skipExecuteGuard
|
|
987
|
+
plan.branchingNodes,
|
|
988
|
+
// for port-aware STEP guards
|
|
989
|
+
production
|
|
990
|
+
);
|
|
991
|
+
generatedNodes.add(instanceId);
|
|
992
|
+
generateScopedChildrenExecution(
|
|
993
|
+
instance,
|
|
994
|
+
nodeType,
|
|
995
|
+
workflow,
|
|
996
|
+
nodeTypes,
|
|
997
|
+
generatedNodes,
|
|
998
|
+
lines,
|
|
999
|
+
" ",
|
|
1000
|
+
plan.branchingNodes,
|
|
1001
|
+
plan.branchRegions,
|
|
1002
|
+
isAsync,
|
|
1003
|
+
bundleMode,
|
|
1004
|
+
production
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
function generateScopedChildrenExecution(parentInstance, parentNodeType, workflow, allNodeTypes, generatedNodes, lines, indent, branchingNodes, branchRegions, isAsync, bundleMode = false, production = false) {
|
|
1008
|
+
if (!parentNodeType.scope) return;
|
|
1009
|
+
const rawScopeName = parentNodeType.scope;
|
|
1010
|
+
const hasPerPortScope = Object.values(parentNodeType.outputs).some((portDef) => portDef.scope === rawScopeName);
|
|
1011
|
+
if (hasPerPortScope) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const scopeName = `${parentInstance.id}.${parentNodeType.scope}`;
|
|
1015
|
+
const childrenInScope = [];
|
|
1016
|
+
if (workflow.scopes && workflow.scopes[scopeName]) {
|
|
1017
|
+
childrenInScope.push(...workflow.scopes[scopeName]);
|
|
1018
|
+
}
|
|
1019
|
+
workflow.instances.forEach((instance) => {
|
|
1020
|
+
const parentScopeName = instance.parent ? `${instance.parent.id}.${instance.parent.scope}` : null;
|
|
1021
|
+
if (parentScopeName === scopeName && !childrenInScope.includes(instance.id)) {
|
|
1022
|
+
childrenInScope.push(instance.id);
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
if (childrenInScope.length === 0) return;
|
|
1026
|
+
const safeParentId = toValidIdentifier(parentInstance.id);
|
|
1027
|
+
lines.push(``);
|
|
1028
|
+
lines.push(`${indent}// Create scope for children of ${parentInstance.id}`);
|
|
1029
|
+
lines.push(
|
|
1030
|
+
`${indent}const ${safeParentId}_scopedCtx = ctx.createScope('${parentInstance.id}', ${safeParentId}Idx, '${parentNodeType.scope}');`
|
|
1031
|
+
);
|
|
1032
|
+
lines.push(``);
|
|
1033
|
+
const scopedCtxVar = `${safeParentId}_scopedCtx`;
|
|
1034
|
+
childrenInScope.forEach((childInstanceId) => {
|
|
1035
|
+
if (generatedNodes.has(childInstanceId)) return;
|
|
1036
|
+
const childInstance = workflow.instances.find((i) => i.id === childInstanceId);
|
|
1037
|
+
if (!childInstance) return;
|
|
1038
|
+
const childNodeType = allNodeTypes.find(
|
|
1039
|
+
(nt) => nt.name === childInstance.nodeType || nt.functionName === childInstance.nodeType
|
|
1040
|
+
);
|
|
1041
|
+
if (!childNodeType) return;
|
|
1042
|
+
if (branchingNodes.has(childInstanceId)) {
|
|
1043
|
+
generateBranchingNodeCode(
|
|
1044
|
+
childInstance,
|
|
1045
|
+
childNodeType,
|
|
1046
|
+
workflow,
|
|
1047
|
+
allNodeTypes,
|
|
1048
|
+
branchRegions.get(childInstanceId),
|
|
1049
|
+
generatedNodes,
|
|
1050
|
+
lines,
|
|
1051
|
+
indent,
|
|
1052
|
+
branchingNodes,
|
|
1053
|
+
branchRegions,
|
|
1054
|
+
isAsync,
|
|
1055
|
+
scopedCtxVar,
|
|
1056
|
+
// Pass scoped context name
|
|
1057
|
+
bundleMode,
|
|
1058
|
+
/* @__PURE__ */ new Set(),
|
|
1059
|
+
false,
|
|
1060
|
+
production
|
|
1061
|
+
);
|
|
1062
|
+
const region = branchRegions.get(childInstanceId);
|
|
1063
|
+
region.successNodes.forEach((n) => generatedNodes.add(n));
|
|
1064
|
+
region.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
1065
|
+
} else {
|
|
1066
|
+
generateNodeCallWithContext(
|
|
1067
|
+
childInstance,
|
|
1068
|
+
childNodeType,
|
|
1069
|
+
workflow,
|
|
1070
|
+
lines,
|
|
1071
|
+
allNodeTypes,
|
|
1072
|
+
indent,
|
|
1073
|
+
isAsync,
|
|
1074
|
+
false,
|
|
1075
|
+
// useConst = false - scoped children need let (referenced outside scope block)
|
|
1076
|
+
parentInstance.id,
|
|
1077
|
+
// instanceParent - parent node is const, no ! needed when referencing it
|
|
1078
|
+
scopedCtxVar,
|
|
1079
|
+
// Pass scoped context name
|
|
1080
|
+
bundleMode,
|
|
1081
|
+
false,
|
|
1082
|
+
// skipExecuteGuard
|
|
1083
|
+
/* @__PURE__ */ new Set(),
|
|
1084
|
+
// branchingNodes
|
|
1085
|
+
production
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
generatedNodes.add(childInstanceId);
|
|
1089
|
+
});
|
|
1090
|
+
lines.push(``);
|
|
1091
|
+
lines.push(`${indent}// Merge scope back into parent context`);
|
|
1092
|
+
lines.push(`${indent}ctx.mergeScope(${parentInstance.id}_scopedCtx);`);
|
|
1093
|
+
lines.push(``);
|
|
1094
|
+
}
|
|
1095
|
+
function generateParallelGroupWithContext(nodeIds, workflow, nodeTypes, lines, generatedNodes, indent, isAsync, ctxVar, bundleMode, branchingNodes, production = false) {
|
|
1096
|
+
const parallelNodes = [];
|
|
1097
|
+
for (const nodeId of nodeIds) {
|
|
1098
|
+
const instance = workflow.instances.find((i) => i.id === nodeId);
|
|
1099
|
+
if (!instance) continue;
|
|
1100
|
+
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
1101
|
+
if (!nodeType) continue;
|
|
1102
|
+
parallelNodes.push({ id: nodeId, instance, nodeType });
|
|
1103
|
+
}
|
|
1104
|
+
if (parallelNodes.length < 2) {
|
|
1105
|
+
for (const node of parallelNodes) {
|
|
1106
|
+
generateNodeCallWithContext(
|
|
1107
|
+
node.instance,
|
|
1108
|
+
node.nodeType,
|
|
1109
|
+
workflow,
|
|
1110
|
+
lines,
|
|
1111
|
+
nodeTypes,
|
|
1112
|
+
indent,
|
|
1113
|
+
isAsync,
|
|
1114
|
+
false,
|
|
1115
|
+
void 0,
|
|
1116
|
+
ctxVar,
|
|
1117
|
+
bundleMode,
|
|
1118
|
+
false,
|
|
1119
|
+
branchingNodes,
|
|
1120
|
+
production
|
|
1121
|
+
);
|
|
1122
|
+
generatedNodes.add(node.id);
|
|
1123
|
+
}
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const nodeBuffers = parallelNodes.map((node, index) => {
|
|
1127
|
+
const parallelCtx = `${ctxVar}_parallel_${index}`;
|
|
1128
|
+
const nodeLines = [];
|
|
1129
|
+
generateNodeCallWithContext(
|
|
1130
|
+
node.instance,
|
|
1131
|
+
node.nodeType,
|
|
1132
|
+
workflow,
|
|
1133
|
+
nodeLines,
|
|
1134
|
+
nodeTypes,
|
|
1135
|
+
`${indent} `,
|
|
1136
|
+
isAsync,
|
|
1137
|
+
false,
|
|
1138
|
+
void 0,
|
|
1139
|
+
parallelCtx,
|
|
1140
|
+
bundleMode,
|
|
1141
|
+
false,
|
|
1142
|
+
branchingNodes,
|
|
1143
|
+
production
|
|
1144
|
+
);
|
|
1145
|
+
lines.push(`${indent}const ${parallelCtx} = ${ctxVar}.forkParallel();`);
|
|
1146
|
+
return { ...node, lines: nodeLines, parallelCtx };
|
|
1147
|
+
});
|
|
1148
|
+
lines.push(`${indent}await Promise.all([`);
|
|
1149
|
+
for (let i = 0; i < nodeBuffers.length; i++) {
|
|
1150
|
+
const buf = nodeBuffers[i];
|
|
1151
|
+
const comma = i < nodeBuffers.length - 1 ? "," : "";
|
|
1152
|
+
lines.push(`${indent} (async () => {`);
|
|
1153
|
+
for (const line of buf.lines) {
|
|
1154
|
+
lines.push(line);
|
|
1155
|
+
}
|
|
1156
|
+
lines.push(`${indent} })()${comma}`);
|
|
1157
|
+
generatedNodes.add(buf.id);
|
|
1158
|
+
}
|
|
1159
|
+
lines.push(`${indent}]);`);
|
|
1160
|
+
for (const buffer of nodeBuffers) {
|
|
1161
|
+
lines.push(`${indent}${ctxVar}.mergeParallel(${buffer.parallelCtx});`);
|
|
1162
|
+
}
|
|
1163
|
+
lines.push("");
|
|
1164
|
+
}
|
|
1165
|
+
function sortBranchNodesTopologically(nodeIds, workflow) {
|
|
1166
|
+
if (nodeIds.size === 0) {
|
|
1167
|
+
return [];
|
|
1168
|
+
}
|
|
1169
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1170
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
1171
|
+
nodeIds.forEach((id) => {
|
|
1172
|
+
graph.set(id, []);
|
|
1173
|
+
inDegree.set(id, 0);
|
|
1174
|
+
});
|
|
1175
|
+
workflow.connections.forEach((conn) => {
|
|
1176
|
+
const fromNode = conn.from.node;
|
|
1177
|
+
const toNode = conn.to.node;
|
|
1178
|
+
if (nodeIds.has(fromNode) && nodeIds.has(toNode)) {
|
|
1179
|
+
const successors = graph.get(fromNode) || [];
|
|
1180
|
+
if (!successors.includes(toNode)) {
|
|
1181
|
+
successors.push(toNode);
|
|
1182
|
+
graph.set(fromNode, successors);
|
|
1183
|
+
inDegree.set(toNode, (inDegree.get(toNode) || 0) + 1);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
const result = [];
|
|
1188
|
+
const queue = [];
|
|
1189
|
+
inDegree.forEach((degree, node) => {
|
|
1190
|
+
if (degree === 0) {
|
|
1191
|
+
queue.push(node);
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
while (queue.length > 0) {
|
|
1195
|
+
const node = queue.shift();
|
|
1196
|
+
result.push(node);
|
|
1197
|
+
const successors = graph.get(node) || [];
|
|
1198
|
+
successors.forEach((successor) => {
|
|
1199
|
+
const newDegree = (inDegree.get(successor) || 0) - 1;
|
|
1200
|
+
inDegree.set(successor, newDegree);
|
|
1201
|
+
if (newDegree === 0) {
|
|
1202
|
+
queue.push(successor);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
if (result.length !== nodeIds.size) {
|
|
1207
|
+
nodeIds.forEach((id) => {
|
|
1208
|
+
if (!result.includes(id)) {
|
|
1209
|
+
result.push(id);
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
return result;
|
|
1214
|
+
}
|
|
1215
|
+
function generateCancelledEventsForBranch(nodeIds, workflow, allNodeTypes, lines, indent, ctxVar = "ctx", isAsync = false) {
|
|
1216
|
+
const awaitPrefix = isAsync ? "await " : "";
|
|
1217
|
+
nodeIds.forEach((instanceId) => {
|
|
1218
|
+
const instance = workflow.instances.find((i) => i.id === instanceId);
|
|
1219
|
+
if (!instance) return;
|
|
1220
|
+
const safeId = toValidIdentifier(instanceId);
|
|
1221
|
+
lines.push(`${indent}const ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1222
|
+
lines.push(
|
|
1223
|
+
`${indent}${awaitPrefix}${ctxVar}.setVariable({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${instance.nodeType}' }, false);`
|
|
1224
|
+
);
|
|
1225
|
+
lines.push(
|
|
1226
|
+
`${indent}${awaitPrefix}${ctxVar}.setVariable({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${instance.nodeType}' }, false);`
|
|
1227
|
+
);
|
|
1228
|
+
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1229
|
+
lines.push(`${indent} nodeTypeName: '${instance.nodeType}',`);
|
|
1230
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1231
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1232
|
+
lines.push(`${indent} status: 'CANCELLED',`);
|
|
1233
|
+
lines.push(`${indent}});`);
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
function buildStepSourceCondition(sourceNode, sourcePort, branchingNodes) {
|
|
1237
|
+
const safeNode = toValidIdentifier(sourceNode);
|
|
1238
|
+
if (branchingNodes.has(sourceNode)) {
|
|
1239
|
+
if (isSuccessPort(sourcePort)) {
|
|
1240
|
+
return `${safeNode}_success`;
|
|
1241
|
+
}
|
|
1242
|
+
if (isFailurePort(sourcePort)) {
|
|
1243
|
+
return `${safeNode}_success === false`;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return `${safeNode}Idx !== undefined`;
|
|
1247
|
+
}
|
|
1248
|
+
function generateBranchingChainCode(chain, workflow, nodeTypes, branchingNodes, branchRegions, generatedNodes, lines, indent, isAsync, ctxVar, bundleMode, forceTrackSuccessNodes = /* @__PURE__ */ new Set(), production = false, alreadyDeclaredFlags = /* @__PURE__ */ new Set()) {
|
|
1249
|
+
const preDeclaredFlags = new Set(alreadyDeclaredFlags);
|
|
1250
|
+
for (let i = 0; i < chain.length; i++) {
|
|
1251
|
+
const isLast = i === chain.length - 1;
|
|
1252
|
+
const safeId = toValidIdentifier(chain[i]);
|
|
1253
|
+
if (!isLast || forceTrackSuccessNodes.has(chain[i]) || alreadyDeclaredFlags.has(safeId)) {
|
|
1254
|
+
if (!alreadyDeclaredFlags.has(safeId)) {
|
|
1255
|
+
lines.push(`${indent}let ${safeId}_success = false;`);
|
|
1256
|
+
}
|
|
1257
|
+
preDeclaredFlags.add(safeId);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (chain.length > 1) {
|
|
1261
|
+
lines.push("");
|
|
1262
|
+
}
|
|
1263
|
+
const guardParts = [];
|
|
1264
|
+
for (let i = 0; i < chain.length; i++) {
|
|
1265
|
+
const nodeId = chain[i];
|
|
1266
|
+
const isLast = i === chain.length - 1;
|
|
1267
|
+
const instance = workflow.instances.find((inst) => inst.id === nodeId);
|
|
1268
|
+
if (!instance) continue;
|
|
1269
|
+
const nodeType = nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
|
|
1270
|
+
if (!nodeType) continue;
|
|
1271
|
+
const safeId = toValidIdentifier(nodeId);
|
|
1272
|
+
const originalRegion = branchRegions.get(nodeId);
|
|
1273
|
+
let effectiveRegion = originalRegion;
|
|
1274
|
+
if (!isLast) {
|
|
1275
|
+
const nextNodeId = chain[i + 1];
|
|
1276
|
+
effectiveRegion = {
|
|
1277
|
+
successNodes: new Set([...originalRegion.successNodes].filter((n) => n !== nextNodeId)),
|
|
1278
|
+
failureNodes: new Set([...originalRegion.failureNodes].filter((n) => n !== nextNodeId))
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const hasGuard = guardParts.length > 0;
|
|
1282
|
+
const guardCondition = guardParts.join(" && ");
|
|
1283
|
+
if (hasGuard) {
|
|
1284
|
+
lines.push(`${indent}if (${guardCondition}) {`);
|
|
1285
|
+
}
|
|
1286
|
+
const nodeIndent = hasGuard ? indent + " " : indent;
|
|
1287
|
+
generateBranchingNodeCode(
|
|
1288
|
+
instance,
|
|
1289
|
+
nodeType,
|
|
1290
|
+
workflow,
|
|
1291
|
+
nodeTypes,
|
|
1292
|
+
effectiveRegion,
|
|
1293
|
+
generatedNodes,
|
|
1294
|
+
lines,
|
|
1295
|
+
nodeIndent,
|
|
1296
|
+
branchingNodes,
|
|
1297
|
+
branchRegions,
|
|
1298
|
+
isAsync,
|
|
1299
|
+
ctxVar,
|
|
1300
|
+
bundleMode,
|
|
1301
|
+
preDeclaredFlags,
|
|
1302
|
+
!isLast || forceTrackSuccessNodes.has(chain[i]) || alreadyDeclaredFlags.has(safeId),
|
|
1303
|
+
// forceTrackSuccess for non-last chain nodes, nodes with promoted dependents, or nodes with pre-declared flags
|
|
1304
|
+
production
|
|
1305
|
+
);
|
|
1306
|
+
generateScopedChildrenExecution(
|
|
1307
|
+
instance,
|
|
1308
|
+
nodeType,
|
|
1309
|
+
workflow,
|
|
1310
|
+
nodeTypes,
|
|
1311
|
+
generatedNodes,
|
|
1312
|
+
lines,
|
|
1313
|
+
nodeIndent,
|
|
1314
|
+
branchingNodes,
|
|
1315
|
+
branchRegions,
|
|
1316
|
+
isAsync,
|
|
1317
|
+
bundleMode,
|
|
1318
|
+
production
|
|
1319
|
+
);
|
|
1320
|
+
if (hasGuard) {
|
|
1321
|
+
lines.push(`${indent}} else {`);
|
|
1322
|
+
const remainingNodes = /* @__PURE__ */ new Set();
|
|
1323
|
+
for (let j = i; j < chain.length; j++) {
|
|
1324
|
+
const chainNodeId = chain[j];
|
|
1325
|
+
remainingNodes.add(chainNodeId);
|
|
1326
|
+
const region = branchRegions.get(chainNodeId);
|
|
1327
|
+
region.successNodes.forEach((n) => remainingNodes.add(n));
|
|
1328
|
+
region.failureNodes.forEach((n) => remainingNodes.add(n));
|
|
1329
|
+
}
|
|
1330
|
+
generateCancelledEventsForBranch(remainingNodes, workflow, nodeTypes, lines, indent + " ", ctxVar, isAsync);
|
|
1331
|
+
lines.push(`${indent}}`);
|
|
1332
|
+
}
|
|
1333
|
+
if (!isLast) {
|
|
1334
|
+
guardParts.push(`${safeId}_success`);
|
|
1335
|
+
}
|
|
1336
|
+
generatedNodes.add(nodeId);
|
|
1337
|
+
effectiveRegion.successNodes.forEach((n) => generatedNodes.add(n));
|
|
1338
|
+
effectiveRegion.failureNodes.forEach((n) => generatedNodes.add(n));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function generateBranchingNodeCode(instance, branchNode, workflow, allNodeTypes, region, generatedNodes, lines, indent, branchingNodes, branchRegions, isAsync, ctxVar = "ctx", bundleMode = false, preDeclaredSuccessFlags = /* @__PURE__ */ new Set(), forceTrackSuccess = false, production = false) {
|
|
1342
|
+
const hasDownstream = region.successNodes.size > 0 || region.failureNodes.size > 0;
|
|
1343
|
+
const branching = {
|
|
1344
|
+
instance,
|
|
1345
|
+
branchNode,
|
|
1346
|
+
workflow,
|
|
1347
|
+
allNodeTypes,
|
|
1348
|
+
region,
|
|
1349
|
+
generatedNodes,
|
|
1350
|
+
lines,
|
|
1351
|
+
indent,
|
|
1352
|
+
branchingNodes,
|
|
1353
|
+
branchRegions,
|
|
1354
|
+
isAsync,
|
|
1355
|
+
ctxVar,
|
|
1356
|
+
bundleMode,
|
|
1357
|
+
preDeclaredSuccessFlags,
|
|
1358
|
+
// Track success flag when there are downstream nodes OR when chain code needs it
|
|
1359
|
+
trackSuccess: hasDownstream || forceTrackSuccess,
|
|
1360
|
+
production
|
|
1361
|
+
};
|
|
1362
|
+
emitBranchingNodeExecution(branching);
|
|
1363
|
+
if (hasDownstream) {
|
|
1364
|
+
emitBranchArms(branching);
|
|
1365
|
+
}
|
|
1366
|
+
generatedNodes.add(instance.id);
|
|
1367
|
+
}
|
|
1368
|
+
function emitBranchingNodeExecution(branching) {
|
|
1369
|
+
const {
|
|
1370
|
+
instance,
|
|
1371
|
+
branchNode,
|
|
1372
|
+
workflow,
|
|
1373
|
+
allNodeTypes,
|
|
1374
|
+
region,
|
|
1375
|
+
lines,
|
|
1376
|
+
isAsync,
|
|
1377
|
+
ctxVar,
|
|
1378
|
+
bundleMode,
|
|
1379
|
+
preDeclaredSuccessFlags,
|
|
1380
|
+
trackSuccess,
|
|
1381
|
+
production
|
|
1382
|
+
} = branching;
|
|
1383
|
+
const instanceId = instance.id;
|
|
1384
|
+
const safeId = toValidIdentifier(instanceId);
|
|
1385
|
+
const functionName = branchNode.functionName;
|
|
1386
|
+
const resultVar = nodeResultVar(safeId, functionName);
|
|
1387
|
+
const emitDebugHooks = !production;
|
|
1388
|
+
const outerIndent = branching.indent;
|
|
1389
|
+
const indent = `${outerIndent} `;
|
|
1390
|
+
if (trackSuccess && !preDeclaredSuccessFlags.has(safeId)) {
|
|
1391
|
+
lines.push(`${outerIndent}let ${safeId}_success = false;`);
|
|
1392
|
+
}
|
|
1393
|
+
if (emitDebugHooks) {
|
|
1394
|
+
const awaitHook = isAsync ? "await " : "";
|
|
1395
|
+
lines.push(`${outerIndent}${awaitHook}__ctrl__.beforeNode('${instanceId}', ${ctxVar});`);
|
|
1396
|
+
}
|
|
1397
|
+
const awaitPrefix = isAsync ? "await " : "";
|
|
1398
|
+
if (!production) {
|
|
1399
|
+
lines.push("");
|
|
1400
|
+
lines.push(`${outerIndent}// \u2500\u2500 ${instanceId} (${functionName}) \u2500\u2500`);
|
|
1401
|
+
}
|
|
1402
|
+
lines.push(`${outerIndent}${ctxVar}.checkAborted('${instanceId}');`);
|
|
1403
|
+
lines.push(`${outerIndent}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1404
|
+
lines.push(`${outerIndent}if (${ctxVar}.shouldExecute('${instanceId}', '${functionName}', ${safeId}Idx)) {`);
|
|
1405
|
+
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1406
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1407
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1408
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1409
|
+
lines.push(`${indent} status: 'RUNNING',`);
|
|
1410
|
+
lines.push(`${indent}});`);
|
|
1411
|
+
lines.push("");
|
|
1412
|
+
if (trackSuccess) {
|
|
1413
|
+
lines.push(`${indent}${safeId}_success = false;`);
|
|
1414
|
+
lines.push("");
|
|
1415
|
+
}
|
|
1416
|
+
lines.push(`${indent}try {`);
|
|
1417
|
+
const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1418
|
+
const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1419
|
+
const argNames = buildNodeArgumentsWithContext({
|
|
1420
|
+
node: branchNode,
|
|
1421
|
+
workflow,
|
|
1422
|
+
id: instanceId,
|
|
1423
|
+
lines,
|
|
1424
|
+
indent: `${indent} `,
|
|
1425
|
+
getCall,
|
|
1426
|
+
isAsync,
|
|
1427
|
+
emitInputEvents: true,
|
|
1428
|
+
setCall,
|
|
1429
|
+
nodeTypeName: functionName,
|
|
1430
|
+
bundleMode,
|
|
1431
|
+
production,
|
|
1432
|
+
abortSignalExpression: `${ctxVar}.getAbortSignal()`,
|
|
1433
|
+
runtimeContextExpression: ctxVar
|
|
1434
|
+
});
|
|
1435
|
+
const awaitKeyword = branchNode.isAsync ? "await " : "";
|
|
1436
|
+
emitNodeInvocation(
|
|
1437
|
+
{ nodeType: branchNode, instanceId, safeId, functionName, resultVar, args: argNames, ctxVar, indent: `${indent} `, lines },
|
|
1438
|
+
{ setCall, isAsync, awaitKeyword, gateResultType: "any", inlineStubAndCoercion: false }
|
|
1439
|
+
);
|
|
1440
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1441
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1442
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1443
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1444
|
+
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1445
|
+
lines.push(`${indent} });`);
|
|
1446
|
+
lines.push(`${indent} ${ctxVar}.commitNode('${instanceId}', '${functionName}', ${safeId}Idx);`);
|
|
1447
|
+
if (emitDebugHooks) {
|
|
1448
|
+
const awaitHook = isAsync ? "await " : "";
|
|
1449
|
+
lines.push(`${indent} ${awaitHook}__ctrl__.afterNode('${instanceId}', ${ctxVar});`);
|
|
1450
|
+
}
|
|
1451
|
+
if (trackSuccess) {
|
|
1452
|
+
lines.push(`${indent} ${safeId}_success = ${branchNode.expression ? "true" : `${resultVar}.onSuccess`};`);
|
|
1453
|
+
}
|
|
1454
|
+
lines.push(`${indent}} catch (error: unknown) {`);
|
|
1455
|
+
lines.push(`${indent} if ((error as { code?: unknown })?.code === 'FLOW_WEAVER_DURABLE_GATE_YIELD') throw error;`);
|
|
1456
|
+
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1457
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1458
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1459
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1460
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1461
|
+
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1462
|
+
lines.push(`${indent} });`);
|
|
1463
|
+
lines.push(`${indent} if (!isCancellation) {`);
|
|
1464
|
+
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1465
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1466
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1467
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1468
|
+
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1469
|
+
lines.push(
|
|
1470
|
+
`${indent} code: typeof (error as { code?: unknown }).code === 'string' ? ((error as { code?: unknown }).code as string) : undefined,`
|
|
1471
|
+
);
|
|
1472
|
+
lines.push(`${indent} });`);
|
|
1473
|
+
lines.push(
|
|
1474
|
+
`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`
|
|
1475
|
+
);
|
|
1476
|
+
lines.push(
|
|
1477
|
+
`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`
|
|
1478
|
+
);
|
|
1479
|
+
if (trackSuccess) {
|
|
1480
|
+
lines.push(`${indent} ${safeId}_success = false;`);
|
|
1481
|
+
}
|
|
1482
|
+
lines.push(`${indent} }`);
|
|
1483
|
+
if (region.successNodes.size > 0) {
|
|
1484
|
+
generateCancelledEventsForBranch(
|
|
1485
|
+
region.successNodes,
|
|
1486
|
+
workflow,
|
|
1487
|
+
allNodeTypes,
|
|
1488
|
+
lines,
|
|
1489
|
+
`${indent} `,
|
|
1490
|
+
ctxVar,
|
|
1491
|
+
isAsync
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
if (region.failureNodes.size > 0) {
|
|
1495
|
+
generateCancelledEventsForBranch(
|
|
1496
|
+
region.failureNodes,
|
|
1497
|
+
workflow,
|
|
1498
|
+
allNodeTypes,
|
|
1499
|
+
lines,
|
|
1500
|
+
`${indent} `,
|
|
1501
|
+
ctxVar,
|
|
1502
|
+
isAsync
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
lines.push(`${indent} throw error;`);
|
|
1506
|
+
lines.push(`${indent}}`);
|
|
1507
|
+
lines.push(`${outerIndent}} else {`);
|
|
1508
|
+
if (trackSuccess) {
|
|
1509
|
+
const getPrefix = isAsync ? "await " : "";
|
|
1510
|
+
lines.push(
|
|
1511
|
+
`${outerIndent} ${safeId}_success = ${getPrefix}${ctxVar}.getVariable({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }) as boolean;`
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
lines.push(`${outerIndent}}`);
|
|
1515
|
+
lines.push("");
|
|
1516
|
+
}
|
|
1517
|
+
function emitBranchArms(branching) {
|
|
1518
|
+
const { instance, workflow, allNodeTypes, region, lines, indent, isAsync, ctxVar } = branching;
|
|
1519
|
+
const instanceId = instance.id;
|
|
1520
|
+
const safeId = toValidIdentifier(instanceId);
|
|
1521
|
+
const hasSuccessDownstream = region.successNodes.size > 0;
|
|
1522
|
+
const hasFailureDownstream = region.failureNodes.size > 0;
|
|
1523
|
+
lines.push(`${indent}if (${safeId}_success) {`);
|
|
1524
|
+
lines.push(`${indent} ${ctxVar}.enterBranch('${instanceId}', ${safeId}Idx, 'success');`);
|
|
1525
|
+
if (hasFailureDownstream) {
|
|
1526
|
+
generateCancelledEventsForBranch(region.failureNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1527
|
+
}
|
|
1528
|
+
emitBranchArmBody(branching, region.successNodes);
|
|
1529
|
+
if (hasFailureDownstream) {
|
|
1530
|
+
lines.push(`${indent} ${ctxVar}.exitBranch();`);
|
|
1531
|
+
lines.push(`${indent}} else {`);
|
|
1532
|
+
lines.push(`${indent} ${ctxVar}.enterBranch('${instanceId}', ${safeId}Idx, 'failure');`);
|
|
1533
|
+
if (hasSuccessDownstream) {
|
|
1534
|
+
generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1535
|
+
}
|
|
1536
|
+
emitBranchArmBody(branching, region.failureNodes);
|
|
1537
|
+
lines.push(`${indent} ${ctxVar}.exitBranch();`);
|
|
1538
|
+
lines.push(`${indent}}`);
|
|
1539
|
+
} else if (hasSuccessDownstream) {
|
|
1540
|
+
lines.push(`${indent} ${ctxVar}.exitBranch();`);
|
|
1541
|
+
lines.push(`${indent}} else {`);
|
|
1542
|
+
lines.push(`${indent} ${ctxVar}.enterBranch('${instanceId}', ${safeId}Idx, 'failure');`);
|
|
1543
|
+
generateCancelledEventsForBranch(region.successNodes, workflow, allNodeTypes, lines, `${indent} `, ctxVar, isAsync);
|
|
1544
|
+
lines.push(`${indent} ${ctxVar}.exitBranch();`);
|
|
1545
|
+
lines.push(`${indent}}`);
|
|
1546
|
+
} else {
|
|
1547
|
+
lines.push(`${indent} ${ctxVar}.exitBranch();`);
|
|
1548
|
+
lines.push(`${indent}}`);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function emitBranchArmBody(branching, armNodes) {
|
|
1552
|
+
const {
|
|
1553
|
+
workflow,
|
|
1554
|
+
allNodeTypes,
|
|
1555
|
+
generatedNodes,
|
|
1556
|
+
lines,
|
|
1557
|
+
indent,
|
|
1558
|
+
branchingNodes,
|
|
1559
|
+
branchRegions,
|
|
1560
|
+
isAsync,
|
|
1561
|
+
ctxVar,
|
|
1562
|
+
bundleMode,
|
|
1563
|
+
preDeclaredSuccessFlags,
|
|
1564
|
+
production
|
|
1565
|
+
} = branching;
|
|
1566
|
+
const armInstanceIds = sortBranchNodesTopologically(armNodes, workflow);
|
|
1567
|
+
armInstanceIds.forEach((instanceId) => {
|
|
1568
|
+
const inst = workflow.instances.find((i) => i.id === instanceId);
|
|
1569
|
+
if (!inst) return;
|
|
1570
|
+
const nodeType = allNodeTypes.find((nt) => nt.name === inst.nodeType || nt.functionName === inst.nodeType);
|
|
1571
|
+
if (!nodeType) return;
|
|
1572
|
+
if (branchingNodes.has(instanceId)) {
|
|
1573
|
+
const nestedRegion = branchRegions.get(instanceId);
|
|
1574
|
+
const nestedSafeId = toValidIdentifier(instanceId);
|
|
1575
|
+
const nestedHasDownstream = nestedRegion.successNodes.size > 0 || nestedRegion.failureNodes.size > 0;
|
|
1576
|
+
const nestedPreDeclared = new Set(preDeclaredSuccessFlags);
|
|
1577
|
+
if (nestedHasDownstream && !nestedPreDeclared.has(nestedSafeId)) {
|
|
1578
|
+
lines.push(`${indent} let ${nestedSafeId}_success = false;`);
|
|
1579
|
+
nestedPreDeclared.add(nestedSafeId);
|
|
1580
|
+
}
|
|
1581
|
+
generateBranchingNodeCode(
|
|
1582
|
+
inst,
|
|
1583
|
+
nodeType,
|
|
1584
|
+
workflow,
|
|
1585
|
+
allNodeTypes,
|
|
1586
|
+
nestedRegion,
|
|
1587
|
+
generatedNodes,
|
|
1588
|
+
lines,
|
|
1589
|
+
`${indent} `,
|
|
1590
|
+
branchingNodes,
|
|
1591
|
+
branchRegions,
|
|
1592
|
+
isAsync,
|
|
1593
|
+
ctxVar,
|
|
1594
|
+
bundleMode,
|
|
1595
|
+
nestedPreDeclared,
|
|
1596
|
+
nestedPreDeclared.has(nestedSafeId),
|
|
1597
|
+
// force tracking if flag was pre-declared at higher scope
|
|
1598
|
+
production
|
|
1599
|
+
);
|
|
1600
|
+
} else {
|
|
1601
|
+
generateNodeCallWithContext(
|
|
1602
|
+
inst,
|
|
1603
|
+
nodeType,
|
|
1604
|
+
workflow,
|
|
1605
|
+
lines,
|
|
1606
|
+
allNodeTypes,
|
|
1607
|
+
`${indent} `,
|
|
1608
|
+
isAsync,
|
|
1609
|
+
false,
|
|
1610
|
+
// useConst
|
|
1611
|
+
void 0,
|
|
1612
|
+
// instanceParent
|
|
1613
|
+
ctxVar,
|
|
1614
|
+
bundleMode,
|
|
1615
|
+
true,
|
|
1616
|
+
// skipExecuteGuard — inside branch, execute is guaranteed by if/else
|
|
1617
|
+
branchingNodes,
|
|
1618
|
+
production
|
|
1619
|
+
);
|
|
1620
|
+
generatedNodes.add(instanceId);
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
function generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar = "ctx", bundleMode = false, production = false) {
|
|
1625
|
+
const instanceId = instance.id;
|
|
1626
|
+
const safeId = toValidIdentifier(instanceId);
|
|
1627
|
+
const functionName = nodeType.functionName;
|
|
1628
|
+
const executorIsAsync = isAsync || nodeType.isAsync;
|
|
1629
|
+
const asyncKeyword = executorIsAsync ? "async " : "";
|
|
1630
|
+
const awaitKeyword = nodeType.isAsync ? "await " : "";
|
|
1631
|
+
const awaitPrefix = executorIsAsync ? "await " : "";
|
|
1632
|
+
lines.push(`${indent}// Pull execution node: ${instanceId}`);
|
|
1633
|
+
lines.push(`${indent}const ${safeId}_executor = ${asyncKeyword}() => {`);
|
|
1634
|
+
lines.push(`${indent} if (${safeId}Idx !== undefined) {`);
|
|
1635
|
+
lines.push(`${indent} return; // Already executed`);
|
|
1636
|
+
lines.push(`${indent} }`);
|
|
1637
|
+
lines.push(`${indent} ${ctxVar}.checkAborted('${instanceId}');`);
|
|
1638
|
+
lines.push(`${indent} ${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1639
|
+
lines.push(`${indent} if (!${ctxVar}.shouldExecute('${instanceId}', '${functionName}', ${safeId}Idx)) return;`);
|
|
1640
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1641
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1642
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1643
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1644
|
+
lines.push(`${indent} status: 'RUNNING',`);
|
|
1645
|
+
lines.push(`${indent} });`);
|
|
1646
|
+
lines.push(`${indent} try {`);
|
|
1647
|
+
const getCall = executorIsAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1648
|
+
const setCall = executorIsAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1649
|
+
const args = buildNodeArgumentsWithContext({
|
|
1650
|
+
node: nodeType,
|
|
1651
|
+
workflow,
|
|
1652
|
+
id: instanceId,
|
|
1653
|
+
lines,
|
|
1654
|
+
indent: `${indent} `,
|
|
1655
|
+
getCall,
|
|
1656
|
+
isAsync: executorIsAsync,
|
|
1657
|
+
emitInputEvents: true,
|
|
1658
|
+
setCall,
|
|
1659
|
+
nodeTypeName: functionName,
|
|
1660
|
+
bundleMode,
|
|
1661
|
+
production,
|
|
1662
|
+
abortSignalExpression: `${ctxVar}.getAbortSignal()`,
|
|
1663
|
+
runtimeContextExpression: ctxVar
|
|
1664
|
+
});
|
|
1665
|
+
const resultVar = nodeResultVar(safeId, functionName);
|
|
1666
|
+
const call = { nodeType, instanceId, safeId, functionName, resultVar, args, ctxVar, indent: `${indent} `, lines };
|
|
1667
|
+
if (!emitDurableNodeCall(call, "any")) {
|
|
1668
|
+
emitPlainNodeCall(call, executorIsAsync, awaitKeyword);
|
|
1669
|
+
}
|
|
1670
|
+
emitResultOutputs(call, setCall, (portName) => isSuccessPort(portName) || isFailurePort(portName));
|
|
1671
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1672
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1673
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1674
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1675
|
+
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1676
|
+
lines.push(`${indent} });`);
|
|
1677
|
+
lines.push(`${indent} ${ctxVar}.commitNode('${instanceId}', '${functionName}', ${safeId}Idx);`);
|
|
1678
|
+
lines.push(`${indent} } catch (error: unknown) {`);
|
|
1679
|
+
lines.push(`${indent} if ((error as { code?: unknown })?.code === 'FLOW_WEAVER_DURABLE_GATE_YIELD') throw error;`);
|
|
1680
|
+
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1681
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1682
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1683
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1684
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1685
|
+
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1686
|
+
lines.push(`${indent} });`);
|
|
1687
|
+
lines.push(`${indent} if (!isCancellation) {`);
|
|
1688
|
+
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1689
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1690
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1691
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1692
|
+
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1693
|
+
lines.push(
|
|
1694
|
+
`${indent} code: typeof (error as { code?: unknown }).code === 'string' ? ((error as { code?: unknown }).code as string) : undefined,`
|
|
1695
|
+
);
|
|
1696
|
+
lines.push(`${indent} });`);
|
|
1697
|
+
lines.push(`${indent} }`);
|
|
1698
|
+
lines.push(`${indent} throw error;`);
|
|
1699
|
+
lines.push(`${indent} }`);
|
|
1700
|
+
lines.push(`${indent}};`);
|
|
1701
|
+
lines.push(`${indent}// Register lazy executor for pull node`);
|
|
1702
|
+
lines.push(`${indent}${ctxVar}.registerPullExecutor('${instanceId}', ${safeId}_executor);`);
|
|
1703
|
+
lines.push(``);
|
|
1704
|
+
}
|
|
1705
|
+
function generateNodeCallWithContext(instance, nodeType, workflow, lines, _allNodeTypes, indent, isAsync, useConst = false, instanceParent, ctxVar = "ctx", bundleMode = false, skipExecuteGuard = false, branchingNodes = /* @__PURE__ */ new Set(), production = false) {
|
|
1706
|
+
const instanceId = instance.id;
|
|
1707
|
+
const safeId = toValidIdentifier(instanceId);
|
|
1708
|
+
const functionName = nodeType.functionName;
|
|
1709
|
+
const fullInstance = workflow.instances.find((i) => i.id === instanceId);
|
|
1710
|
+
const pullConfig = fullInstance ? getPullExecutionConfig(fullInstance, nodeType) : { enabled: false, triggerPort: "execute" };
|
|
1711
|
+
if (pullConfig.enabled) {
|
|
1712
|
+
generatePullNodeWithContext(instance, nodeType, workflow, lines, indent, isAsync, ctxVar, bundleMode, production);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
const stepInputs = Object.entries(nodeType.inputs).filter(
|
|
1716
|
+
([portName, portConfig]) => {
|
|
1717
|
+
if (isExecutePort(portName) && !useConst && !skipExecuteGuard) {
|
|
1718
|
+
return portConfig.dataType === "STEP";
|
|
1719
|
+
}
|
|
1720
|
+
return portConfig.dataType === "STEP" && !isExecutePort(portName);
|
|
1721
|
+
}
|
|
1722
|
+
);
|
|
1723
|
+
if (nodeType.expression && !useConst && !skipExecuteGuard && !stepInputs.some(([p]) => isExecutePort(p))) {
|
|
1724
|
+
const hasExecuteConnection = workflow.connections.some(
|
|
1725
|
+
(conn) => conn.to.node === instanceId && isExecutePort(conn.to.port)
|
|
1726
|
+
);
|
|
1727
|
+
if (hasExecuteConnection) {
|
|
1728
|
+
stepInputs.push(["execute", { dataType: "STEP" }]);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
const stepSignalSources = [];
|
|
1732
|
+
if (stepInputs.length > 0) {
|
|
1733
|
+
workflow.connections.forEach((conn) => {
|
|
1734
|
+
const toNode = conn.to.node;
|
|
1735
|
+
const toPort = conn.to.port;
|
|
1736
|
+
if (toNode === instanceId && stepInputs.some(([port]) => port === toPort)) {
|
|
1737
|
+
const sourceNode = conn.from.node;
|
|
1738
|
+
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
1739
|
+
if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
if (!isStartNode(sourceNode) && !stepSignalSources.includes(sourceNode)) {
|
|
1743
|
+
stepSignalSources.push(sourceNode);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
let shouldIndent = false;
|
|
1749
|
+
if (stepSignalSources.length > 0) {
|
|
1750
|
+
const conditions = [];
|
|
1751
|
+
const portToSources = /* @__PURE__ */ new Map();
|
|
1752
|
+
stepInputs.forEach(([portName]) => {
|
|
1753
|
+
const sources = [];
|
|
1754
|
+
workflow.connections.forEach((conn) => {
|
|
1755
|
+
const toNode = conn.to.node;
|
|
1756
|
+
const toPort = conn.to.port;
|
|
1757
|
+
if (toNode === instanceId && toPort === portName) {
|
|
1758
|
+
const sourceNode = conn.from.node;
|
|
1759
|
+
if (!isStartNode(sourceNode)) {
|
|
1760
|
+
const sourceInstance = workflow.instances.find((i) => i.id === sourceNode);
|
|
1761
|
+
if (sourceInstance && isPerPortScopedChild(sourceInstance, workflow, _allNodeTypes)) {
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
sources.push({ node: sourceNode, port: conn.from.port });
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
if (sources.length > 0) {
|
|
1769
|
+
portToSources.set(portName, sources);
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
if (nodeType.executeWhen === EXECUTION_STRATEGIES.CONJUNCTION) {
|
|
1773
|
+
portToSources.forEach((sources, _portName) => {
|
|
1774
|
+
if (sources.length === 1) {
|
|
1775
|
+
conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
|
|
1776
|
+
} else {
|
|
1777
|
+
const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(" || ");
|
|
1778
|
+
conditions.push(`(${orCondition})`);
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
if (conditions.length > 0) {
|
|
1782
|
+
const fullCondition = conditions.join(" && ");
|
|
1783
|
+
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1784
|
+
indent = `${indent} `;
|
|
1785
|
+
shouldIndent = true;
|
|
1786
|
+
}
|
|
1787
|
+
} else if (nodeType.executeWhen === EXECUTION_STRATEGIES.DISJUNCTION) {
|
|
1788
|
+
const allSources = [];
|
|
1789
|
+
portToSources.forEach((sources) => {
|
|
1790
|
+
sources.forEach((source) => {
|
|
1791
|
+
if (!allSources.some((s) => s.node === source.node && s.port === source.port)) {
|
|
1792
|
+
allSources.push(source);
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
1796
|
+
if (allSources.length > 0) {
|
|
1797
|
+
const fullCondition = allSources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(" || ");
|
|
1798
|
+
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1799
|
+
indent = `${indent} `;
|
|
1800
|
+
shouldIndent = true;
|
|
1801
|
+
}
|
|
1802
|
+
} else if (nodeType.executeWhen === EXECUTION_STRATEGIES.CUSTOM) {
|
|
1803
|
+
const customCondition = nodeType.metadata?.customExecuteCondition;
|
|
1804
|
+
if (customCondition && typeof customCondition === "string") {
|
|
1805
|
+
lines.push(`${indent}if (${customCondition}) {`);
|
|
1806
|
+
indent = `${indent} `;
|
|
1807
|
+
shouldIndent = true;
|
|
1808
|
+
} else {
|
|
1809
|
+
portToSources.forEach((sources, _portName) => {
|
|
1810
|
+
if (sources.length === 1) {
|
|
1811
|
+
conditions.push(buildStepSourceCondition(sources[0].node, sources[0].port, branchingNodes));
|
|
1812
|
+
} else {
|
|
1813
|
+
const orCondition = sources.map((s) => buildStepSourceCondition(s.node, s.port, branchingNodes)).join(" || ");
|
|
1814
|
+
conditions.push(`(${orCondition})`);
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
if (conditions.length > 0) {
|
|
1818
|
+
const fullCondition = conditions.join(" && ");
|
|
1819
|
+
lines.push(`${indent}if (${fullCondition}) {`);
|
|
1820
|
+
indent = `${indent} `;
|
|
1821
|
+
shouldIndent = true;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
const emitDebugHooks = !production;
|
|
1827
|
+
if (emitDebugHooks) {
|
|
1828
|
+
const awaitHook = isAsync ? "await " : "";
|
|
1829
|
+
lines.push(`${indent}${awaitHook}__ctrl__.beforeNode('${instanceId}', ${ctxVar});`);
|
|
1830
|
+
}
|
|
1831
|
+
const outerIndent = indent;
|
|
1832
|
+
const varDecl = useConst ? "const " : "";
|
|
1833
|
+
const awaitPrefix = isAsync ? "await " : "";
|
|
1834
|
+
lines.push(`${indent}${ctxVar}.checkAborted('${instanceId}');`);
|
|
1835
|
+
lines.push(`${indent}${varDecl}${safeId}Idx = ${ctxVar}.addExecution('${instanceId}');`);
|
|
1836
|
+
lines.push(`${indent}if (${ctxVar}.shouldExecute('${instanceId}', '${functionName}', ${safeId}Idx)) {`);
|
|
1837
|
+
indent = `${indent} `;
|
|
1838
|
+
lines.push(`${indent}${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1839
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1840
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1841
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1842
|
+
lines.push(`${indent} status: 'RUNNING',`);
|
|
1843
|
+
lines.push(`${indent}});`);
|
|
1844
|
+
lines.push(`${indent}try {`);
|
|
1845
|
+
const getCall = isAsync ? `await ${ctxVar}.getVariable` : `${ctxVar}.getVariable`;
|
|
1846
|
+
const setCall = isAsync ? `await ${ctxVar}.setVariable` : `${ctxVar}.setVariable`;
|
|
1847
|
+
const args = buildNodeArgumentsWithContext({
|
|
1848
|
+
node: nodeType,
|
|
1849
|
+
workflow,
|
|
1850
|
+
id: instanceId,
|
|
1851
|
+
lines,
|
|
1852
|
+
indent: `${indent} `,
|
|
1853
|
+
getCall,
|
|
1854
|
+
isAsync,
|
|
1855
|
+
instanceParent,
|
|
1856
|
+
emitInputEvents: true,
|
|
1857
|
+
setCall,
|
|
1858
|
+
nodeTypeName: functionName,
|
|
1859
|
+
bundleMode,
|
|
1860
|
+
production,
|
|
1861
|
+
abortSignalExpression: `${ctxVar}.getAbortSignal()`
|
|
1862
|
+
});
|
|
1863
|
+
const resultVar = nodeResultVar(safeId, functionName);
|
|
1864
|
+
const awaitKeyword = nodeType.isAsync ? "await " : "";
|
|
1865
|
+
emitNodeInvocation(
|
|
1866
|
+
{ nodeType, instanceId, safeId, functionName, resultVar, args, ctxVar, indent: `${indent} `, lines },
|
|
1867
|
+
{ setCall, isAsync, awaitKeyword, gateResultType: "Record<string, unknown>", inlineStubAndCoercion: true }
|
|
1868
|
+
);
|
|
1869
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1870
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1871
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1872
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1873
|
+
lines.push(`${indent} status: 'SUCCEEDED',`);
|
|
1874
|
+
lines.push(`${indent} });`);
|
|
1875
|
+
lines.push(`${indent} ${ctxVar}.commitNode('${instanceId}', '${functionName}', ${safeId}Idx);`);
|
|
1876
|
+
if (emitDebugHooks) {
|
|
1877
|
+
const awaitHook = isAsync ? "await " : "";
|
|
1878
|
+
lines.push(`${indent} ${awaitHook}__ctrl__.afterNode('${instanceId}', ${ctxVar});`);
|
|
1879
|
+
}
|
|
1880
|
+
lines.push(`${indent}} catch (error: unknown) {`);
|
|
1881
|
+
lines.push(`${indent} if ((error as { code?: unknown })?.code === 'FLOW_WEAVER_DURABLE_GATE_YIELD') throw error;`);
|
|
1882
|
+
lines.push(`${indent} const isCancellation = CancellationError.isCancellationError(error);`);
|
|
1883
|
+
lines.push(`${indent} ${awaitPrefix}${ctxVar}.sendStatusChangedEvent({`);
|
|
1884
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1885
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1886
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1887
|
+
lines.push(`${indent} status: isCancellation ? 'CANCELLED' : 'FAILED',`);
|
|
1888
|
+
lines.push(`${indent} });`);
|
|
1889
|
+
lines.push(`${indent} if (!isCancellation) {`);
|
|
1890
|
+
lines.push(`${indent} ${ctxVar}.sendLogErrorEvent({`);
|
|
1891
|
+
lines.push(`${indent} nodeTypeName: '${functionName}',`);
|
|
1892
|
+
lines.push(`${indent} id: '${instanceId}',`);
|
|
1893
|
+
lines.push(`${indent} executionIndex: ${safeId}Idx,`);
|
|
1894
|
+
lines.push(`${indent} error: error instanceof Error ? error.message : String(error),`);
|
|
1895
|
+
lines.push(
|
|
1896
|
+
`${indent} code: typeof (error as { code?: unknown }).code === 'string' ? ((error as { code?: unknown }).code as string) : undefined,`
|
|
1897
|
+
);
|
|
1898
|
+
lines.push(`${indent} });`);
|
|
1899
|
+
if (nodeType.expression) {
|
|
1900
|
+
lines.push(
|
|
1901
|
+
`${indent} ${setCall}({ id: '${instanceId}', portName: 'onSuccess', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, false);`
|
|
1902
|
+
);
|
|
1903
|
+
lines.push(
|
|
1904
|
+
`${indent} ${setCall}({ id: '${instanceId}', portName: 'onFailure', executionIndex: ${safeId}Idx, nodeTypeName: '${functionName}' }, true);`
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
lines.push(`${indent} }`);
|
|
1908
|
+
lines.push(`${indent} throw error;`);
|
|
1909
|
+
lines.push(`${indent}}`);
|
|
1910
|
+
lines.push(`${outerIndent}}`);
|
|
1911
|
+
if (shouldIndent) {
|
|
1912
|
+
const originalIndent = outerIndent.slice(0, -2);
|
|
1913
|
+
lines.push(`${originalIndent}}`);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// src/generator/body-generator.ts
|
|
1918
|
+
var BodyGenerator = class {
|
|
1919
|
+
/**
|
|
1920
|
+
* Generates a workflow function body that uses an execution context for
|
|
1921
|
+
* tracing, error handling, and runtime state management.
|
|
1922
|
+
*
|
|
1923
|
+
* @param workflow - The workflow AST defining the graph structure.
|
|
1924
|
+
* @param nodeTypes - The node type definitions referenced by the workflow.
|
|
1925
|
+
* @param isAsync - Whether to generate async code. Defaults to true.
|
|
1926
|
+
* @param production - If true, omits debug event emissions. Defaults to false.
|
|
1927
|
+
* @param bundleMode - If true, generates code suitable for bundled deployment. Defaults to false.
|
|
1928
|
+
* @param durableSequential - If true, the graph has a durable gate and runs strictly in order.
|
|
1929
|
+
* @param identity - The graph identity a gated body declares to the engine before it runs.
|
|
1930
|
+
* @returns The generated function body as a string.
|
|
1931
|
+
*/
|
|
1932
|
+
generateWithExecutionContext(workflow, nodeTypes, isAsync = true, production = false, bundleMode = false, durableSequential = false, identity) {
|
|
1933
|
+
return generateControlFlowWithExecutionContext(
|
|
1934
|
+
workflow,
|
|
1935
|
+
nodeTypes,
|
|
1936
|
+
isAsync,
|
|
1937
|
+
production,
|
|
1938
|
+
bundleMode,
|
|
1939
|
+
durableSequential,
|
|
1940
|
+
identity
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Generates a workflow function body using default settings (async, non-production, non-bundled).
|
|
1945
|
+
* Convenience method that wraps {@link generateWithExecutionContext}.
|
|
1946
|
+
*
|
|
1947
|
+
* @param workflow - The workflow AST defining the graph structure.
|
|
1948
|
+
* @param nodeTypes - The node type definitions referenced by the workflow.
|
|
1949
|
+
* @returns The generated function body as a string.
|
|
1950
|
+
*/
|
|
1951
|
+
generateDirectCode(workflow, nodeTypes) {
|
|
1952
|
+
return generateControlFlowWithExecutionContext(workflow, nodeTypes, true, false, false, false);
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1955
|
+
var bodyGenerator = new BodyGenerator();
|
|
1956
|
+
|
|
1957
|
+
// src/api/in-place/source-file.ts
|
|
1958
|
+
import * as ts from "typescript";
|
|
1959
|
+
function parseSource(source) {
|
|
1960
|
+
return ts.createSourceFile("temp.ts", source, ts.ScriptTarget.Latest, true);
|
|
1961
|
+
}
|
|
1962
|
+
function findFunctionDeclaration(sourceFile, functionName) {
|
|
1963
|
+
let functionNode;
|
|
1964
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
1965
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
|
|
1966
|
+
functionNode = node;
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
return functionNode;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// src/api/in-place/function-body.ts
|
|
1973
|
+
function generateFunctionBody(ast, production, isAsync, durableSequential, identity) {
|
|
1974
|
+
const lines = [];
|
|
1975
|
+
lines.push(" // ============================================================================");
|
|
1976
|
+
lines.push(" // DO NOT EDIT - This section is auto-generated by Flow Weaver");
|
|
1977
|
+
lines.push(" // Edit the @flowWeaver annotations above to modify workflow behavior");
|
|
1978
|
+
lines.push(" // ============================================================================");
|
|
1979
|
+
lines.push("");
|
|
1980
|
+
const body = bodyGenerator.generateWithExecutionContext(
|
|
1981
|
+
ast,
|
|
1982
|
+
ast.nodeTypes,
|
|
1983
|
+
isAsync,
|
|
1984
|
+
// Respect original function's async/sync nature
|
|
1985
|
+
production,
|
|
1986
|
+
false,
|
|
1987
|
+
durableSequential,
|
|
1988
|
+
identity
|
|
1989
|
+
);
|
|
1990
|
+
const indentedBody = body.split("\n").map((line) => line.trim() ? " " + line : line).join("\n");
|
|
1991
|
+
lines.push(indentedBody);
|
|
1992
|
+
return lines.join("\n");
|
|
1993
|
+
}
|
|
1994
|
+
function replaceWorkflowFunctionBody(source, functionName, newBody) {
|
|
1995
|
+
const functionNode = findFunctionDeclaration(parseSource(source), functionName);
|
|
1996
|
+
if (!functionNode || !functionNode.body) {
|
|
1997
|
+
return { code: source, changed: false };
|
|
1998
|
+
}
|
|
1999
|
+
const bodyText = source.slice(functionNode.body.pos, functionNode.body.end);
|
|
2000
|
+
const bodyStartMarkerIdx = bodyText.indexOf(MARKERS.BODY_START);
|
|
2001
|
+
const bodyEndMarkerIdx = bodyText.indexOf(MARKERS.BODY_END);
|
|
2002
|
+
if (bodyStartMarkerIdx !== -1 && bodyEndMarkerIdx !== -1) {
|
|
2003
|
+
const absoluteBodyStart = functionNode.body.pos + bodyStartMarkerIdx + MARKERS.BODY_START.length;
|
|
2004
|
+
const absoluteBodyEnd = functionNode.body.pos + bodyEndMarkerIdx;
|
|
2005
|
+
const before2 = source.slice(0, absoluteBodyStart);
|
|
2006
|
+
const after2 = source.slice(absoluteBodyEnd);
|
|
2007
|
+
const newCode = before2 + "\n" + newBody + "\n " + after2;
|
|
2008
|
+
const originalBody = source.slice(absoluteBodyStart, absoluteBodyEnd);
|
|
2009
|
+
const changed = originalBody.trim() !== newBody.trim();
|
|
2010
|
+
return { code: newCode, changed };
|
|
2011
|
+
}
|
|
2012
|
+
const openBraceIdx = source.indexOf("{", functionNode.body.pos);
|
|
2013
|
+
if (openBraceIdx === -1) {
|
|
2014
|
+
return { code: source, changed: false };
|
|
2015
|
+
}
|
|
2016
|
+
const closeBraceIdx = functionNode.body.end - 1;
|
|
2017
|
+
if (closeBraceIdx <= openBraceIdx) {
|
|
2018
|
+
return { code: source, changed: false };
|
|
2019
|
+
}
|
|
2020
|
+
const before = source.slice(0, openBraceIdx + 1);
|
|
2021
|
+
const after = source.slice(closeBraceIdx);
|
|
2022
|
+
const newBodyWithMarkers = [
|
|
2023
|
+
"",
|
|
2024
|
+
" " + MARKERS.BODY_START,
|
|
2025
|
+
newBody,
|
|
2026
|
+
" " + MARKERS.BODY_END,
|
|
2027
|
+
""
|
|
2028
|
+
].join("\n");
|
|
2029
|
+
return {
|
|
2030
|
+
code: before + newBodyWithMarkers + after,
|
|
2031
|
+
changed: true
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// src/api/in-place/fw-imports.ts
|
|
2036
|
+
function fwImportFunctionName(nodeType) {
|
|
2037
|
+
if (nodeType.functionName === nodeType.name && nodeType.name.startsWith("npm/")) {
|
|
2038
|
+
const parts = nodeType.name.split("/");
|
|
2039
|
+
return parts[parts.length - 1];
|
|
2040
|
+
}
|
|
2041
|
+
return nodeType.functionName;
|
|
2042
|
+
}
|
|
2043
|
+
function ensureFwImportStatements(source, ast) {
|
|
2044
|
+
const importNodeTypes = ast.nodeTypes.filter((nt) => nt.importSource);
|
|
2045
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2046
|
+
const importLines = [];
|
|
2047
|
+
for (const nt of importNodeTypes) {
|
|
2048
|
+
const fnName = fwImportFunctionName(nt);
|
|
2049
|
+
const key = JSON.stringify([fnName, nt.importSource]);
|
|
2050
|
+
if (seen.has(key)) continue;
|
|
2051
|
+
seen.add(key);
|
|
2052
|
+
importLines.push(`import { ${fnName} } from '${nt.importSource}';`);
|
|
2053
|
+
}
|
|
2054
|
+
const block = [MARKERS.IMPORTS_START, ...importLines, MARKERS.IMPORTS_END].join("\n");
|
|
2055
|
+
const startIdx = source.indexOf(MARKERS.IMPORTS_START);
|
|
2056
|
+
const endIdx = source.indexOf(MARKERS.IMPORTS_END);
|
|
2057
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
2058
|
+
const before = source.slice(0, startIdx);
|
|
2059
|
+
const after = source.slice(endIdx + MARKERS.IMPORTS_END.length);
|
|
2060
|
+
const next2 = `${before}${block}${after}`;
|
|
2061
|
+
return { code: next2, changed: next2 !== source };
|
|
2062
|
+
}
|
|
2063
|
+
if (importLines.length === 0) {
|
|
2064
|
+
return { code: source, changed: false };
|
|
2065
|
+
}
|
|
2066
|
+
const next = `${block}
|
|
2067
|
+
${source}`;
|
|
2068
|
+
return { code: next, changed: true };
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
// src/api/in-place/node-type-functions.ts
|
|
2072
|
+
import * as ts2 from "typescript";
|
|
2073
|
+
import * as path from "path";
|
|
2074
|
+
function syncNodeTypeFunctions(source, ast) {
|
|
2075
|
+
let code = source;
|
|
2076
|
+
let changed = false;
|
|
2077
|
+
for (const nodeType of ast.nodeTypes) {
|
|
2078
|
+
if (!isOwnedByFile(nodeType, ast.sourceFile)) {
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
const nodeTypeResult = replaceNodeTypeJSDoc(code, nodeType);
|
|
2082
|
+
if (nodeTypeResult.changed) {
|
|
2083
|
+
code = nodeTypeResult.code;
|
|
2084
|
+
changed = true;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
return { code, changed };
|
|
2088
|
+
}
|
|
2089
|
+
function isOwnedByFile(nodeType, workflowFile) {
|
|
2090
|
+
if (nodeType.variant === "IMPORTED_WORKFLOW" || nodeType.variant === "WORKFLOW" || nodeType.variant === "MAP_ITERATOR") {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
if (nodeType.sourceLocation?.file) {
|
|
2094
|
+
const ntFile = nodeType.sourceLocation.file.replace(/\\/g, "/");
|
|
2095
|
+
const astFile = workflowFile.replace(/\\/g, "/");
|
|
2096
|
+
const ntBase = ntFile.split("/").filter(Boolean).pop() ?? "";
|
|
2097
|
+
const astBase = astFile.split("/").filter(Boolean).pop() ?? "";
|
|
2098
|
+
if (path.resolve(ntFile) !== path.resolve(astFile) && ntBase !== astBase) {
|
|
2099
|
+
return false;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
if (!nodeType.sourceLocation && nodeType.helperText != null) {
|
|
2103
|
+
return false;
|
|
2104
|
+
}
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
2107
|
+
function nodeTypeFunctionText(nodeType) {
|
|
2108
|
+
const nodeTypeWithCode = nodeType;
|
|
2109
|
+
return nodeTypeWithCode.code || nodeType.functionText;
|
|
2110
|
+
}
|
|
2111
|
+
function renameFunctionInCode(code, newName) {
|
|
2112
|
+
const codeSourceFile = parseSource(code);
|
|
2113
|
+
let functionName;
|
|
2114
|
+
let functionNameStart;
|
|
2115
|
+
let functionNameEnd;
|
|
2116
|
+
ts2.forEachChild(codeSourceFile, (node) => {
|
|
2117
|
+
if (ts2.isFunctionDeclaration(node) && node.name) {
|
|
2118
|
+
functionName = node.name.text;
|
|
2119
|
+
functionNameStart = node.name.getStart();
|
|
2120
|
+
functionNameEnd = node.name.getEnd();
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
if (!functionName || functionNameStart === void 0 || functionNameEnd === void 0) {
|
|
2124
|
+
return code;
|
|
2125
|
+
}
|
|
2126
|
+
if (functionName === newName) {
|
|
2127
|
+
return code;
|
|
2128
|
+
}
|
|
2129
|
+
const before = code.slice(0, functionNameStart);
|
|
2130
|
+
const after = code.slice(functionNameEnd);
|
|
2131
|
+
return before + newName + after;
|
|
2132
|
+
}
|
|
2133
|
+
function insertNodeTypeFunction(source, nodeType, functionCode) {
|
|
2134
|
+
const sourceFile = parseSource(source);
|
|
2135
|
+
const runtimeEndMarker = MARKERS.RUNTIME_END;
|
|
2136
|
+
const runtimeEndPos = source.indexOf(runtimeEndMarker);
|
|
2137
|
+
let insertPosition = -1;
|
|
2138
|
+
if (runtimeEndPos !== -1) {
|
|
2139
|
+
const afterMarker = source.indexOf("\n", runtimeEndPos);
|
|
2140
|
+
insertPosition = afterMarker !== -1 ? afterMarker + 1 : runtimeEndPos + runtimeEndMarker.length;
|
|
2141
|
+
} else {
|
|
2142
|
+
let foundExportedFunction = false;
|
|
2143
|
+
ts2.forEachChild(sourceFile, (node) => {
|
|
2144
|
+
if (!foundExportedFunction && ts2.isFunctionDeclaration(node) && node.modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword)) {
|
|
2145
|
+
insertPosition = node.getFullStart();
|
|
2146
|
+
foundExportedFunction = true;
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
if (insertPosition === -1) {
|
|
2150
|
+
insertPosition = source.length;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
const renamedCode = renameFunctionInCode(functionCode, nodeType.functionName);
|
|
2154
|
+
const hasJSDoc = renamedCode.trim().startsWith("/**");
|
|
2155
|
+
let finalCode = renamedCode;
|
|
2156
|
+
if (!hasJSDoc) {
|
|
2157
|
+
const jsdoc = generateNodeTypeJSDoc(nodeType);
|
|
2158
|
+
finalCode = jsdoc + "\n" + renamedCode;
|
|
2159
|
+
}
|
|
2160
|
+
const before = source.slice(0, insertPosition);
|
|
2161
|
+
const after = source.slice(insertPosition);
|
|
2162
|
+
const needsLeadingNewline = before.length > 0 && !before.endsWith("\n\n");
|
|
2163
|
+
const needsTrailingNewline = after.length > 0 && !after.startsWith("\n");
|
|
2164
|
+
const newCode = before + (needsLeadingNewline ? "\n\n" : "") + finalCode + (needsTrailingNewline ? "\n\n" : "") + after;
|
|
2165
|
+
return { code: newCode, changed: true };
|
|
2166
|
+
}
|
|
2167
|
+
function findFunctionByNameTag(source, sourceFile, targetName) {
|
|
2168
|
+
let result;
|
|
2169
|
+
ts2.forEachChild(sourceFile, (node) => {
|
|
2170
|
+
if (ts2.isFunctionDeclaration(node) && node.name) {
|
|
2171
|
+
const functionStart = node.getFullStart();
|
|
2172
|
+
const leadingComments = ts2.getLeadingCommentRanges(source, functionStart);
|
|
2173
|
+
if (leadingComments) {
|
|
2174
|
+
for (const comment of leadingComments) {
|
|
2175
|
+
if (comment.kind === ts2.SyntaxKind.MultiLineCommentTrivia) {
|
|
2176
|
+
const commentText = source.slice(comment.pos, comment.end);
|
|
2177
|
+
const nameMatch = commentText.match(/@name\s+(\S+)/);
|
|
2178
|
+
if (nameMatch && nameMatch[1] === targetName) {
|
|
2179
|
+
result = node;
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
return result;
|
|
2188
|
+
}
|
|
2189
|
+
function replaceNodeTypeJSDoc(source, nodeType) {
|
|
2190
|
+
const sourceFile = parseSource(source);
|
|
2191
|
+
let needsRename = false;
|
|
2192
|
+
let functionNode = findFunctionDeclaration(sourceFile, nodeType.functionName);
|
|
2193
|
+
if (!functionNode && nodeType.name) {
|
|
2194
|
+
functionNode = findFunctionByNameTag(source, sourceFile, nodeType.name);
|
|
2195
|
+
if (functionNode && functionNode.name?.text !== nodeType.functionName) {
|
|
2196
|
+
needsRename = true;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
if (!functionNode && nodeType.name && nodeType.name !== nodeType.functionName) {
|
|
2200
|
+
functionNode = findFunctionDeclaration(sourceFile, nodeType.name);
|
|
2201
|
+
if (functionNode) {
|
|
2202
|
+
needsRename = true;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (!functionNode) {
|
|
2206
|
+
const functionCode = nodeTypeFunctionText(nodeType);
|
|
2207
|
+
if (functionCode) {
|
|
2208
|
+
return insertNodeTypeFunction(source, nodeType, functionCode);
|
|
2209
|
+
}
|
|
2210
|
+
return { code: source, changed: false };
|
|
2211
|
+
}
|
|
2212
|
+
let result = source;
|
|
2213
|
+
let hasChanges = false;
|
|
2214
|
+
if (needsRename && functionNode.name) {
|
|
2215
|
+
const nameStart = functionNode.name.getStart();
|
|
2216
|
+
const nameEnd = functionNode.name.getEnd();
|
|
2217
|
+
result = result.slice(0, nameStart) + nodeType.functionName + result.slice(nameEnd);
|
|
2218
|
+
hasChanges = true;
|
|
2219
|
+
const renamedNode = findFunctionDeclaration(parseSource(result), nodeType.functionName);
|
|
2220
|
+
if (renamedNode) {
|
|
2221
|
+
functionNode = renamedNode;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
const functionStart = functionNode.getFullStart();
|
|
2225
|
+
const leadingComments = ts2.getLeadingCommentRanges(result, functionStart);
|
|
2226
|
+
if (!leadingComments || leadingComments.length === 0) {
|
|
2227
|
+
return { code: result, changed: hasChanges };
|
|
2228
|
+
}
|
|
2229
|
+
const jsdocComments = [];
|
|
2230
|
+
for (const c of leadingComments) {
|
|
2231
|
+
if (c.kind === ts2.SyntaxKind.MultiLineCommentTrivia && result.slice(c.pos, c.pos + 3) === "/**") {
|
|
2232
|
+
jsdocComments.push(c);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
if (jsdocComments.length === 0) {
|
|
2236
|
+
return { code: result, changed: hasChanges };
|
|
2237
|
+
}
|
|
2238
|
+
const jsdocComment = jsdocComments[jsdocComments.length - 1];
|
|
2239
|
+
const staleDuplicates = [];
|
|
2240
|
+
for (let i = 0; i < jsdocComments.length - 1; i++) {
|
|
2241
|
+
const text = result.slice(jsdocComments[i].pos, jsdocComments[i].end);
|
|
2242
|
+
if (text.includes("@flowWeaver")) {
|
|
2243
|
+
staleDuplicates.push(jsdocComments[i]);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
const newJSDoc = generateNodeTypeJSDoc(nodeType);
|
|
2247
|
+
if (staleDuplicates.length > 0) {
|
|
2248
|
+
for (let i = staleDuplicates.length - 1; i >= 0; i--) {
|
|
2249
|
+
const dupe = staleDuplicates[i];
|
|
2250
|
+
let removeEnd = dupe.end;
|
|
2251
|
+
while (removeEnd < result.length && (result[removeEnd] === "\n" || result[removeEnd] === "\r")) {
|
|
2252
|
+
removeEnd++;
|
|
2253
|
+
}
|
|
2254
|
+
result = result.slice(0, dupe.pos) + result.slice(removeEnd);
|
|
2255
|
+
hasChanges = true;
|
|
2256
|
+
}
|
|
2257
|
+
const updatedFunctionNode = findFunctionDeclaration(parseSource(result), nodeType.functionName);
|
|
2258
|
+
if (!updatedFunctionNode) {
|
|
2259
|
+
return { code: result, changed: hasChanges };
|
|
2260
|
+
}
|
|
2261
|
+
const updatedStart = updatedFunctionNode.getFullStart();
|
|
2262
|
+
const updatedComments = ts2.getLeadingCommentRanges(result, updatedStart);
|
|
2263
|
+
if (!updatedComments) {
|
|
2264
|
+
return { code: result, changed: hasChanges };
|
|
2265
|
+
}
|
|
2266
|
+
let updatedJsdoc;
|
|
2267
|
+
for (const c of updatedComments) {
|
|
2268
|
+
if (c.kind === ts2.SyntaxKind.MultiLineCommentTrivia && result.slice(c.pos, c.pos + 3) === "/**") {
|
|
2269
|
+
updatedJsdoc = c;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
if (!updatedJsdoc) {
|
|
2273
|
+
return { code: result, changed: hasChanges };
|
|
2274
|
+
}
|
|
2275
|
+
return replaceJSDocContent(
|
|
2276
|
+
result,
|
|
2277
|
+
updatedJsdoc,
|
|
2278
|
+
updatedFunctionNode,
|
|
2279
|
+
nodeType,
|
|
2280
|
+
newJSDoc,
|
|
2281
|
+
hasChanges
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
return replaceJSDocContent(result, jsdocComment, functionNode, nodeType, newJSDoc, hasChanges);
|
|
2285
|
+
}
|
|
2286
|
+
function replaceJSDocContent(source, jsdocComment, functionNode, nodeType, newJSDoc, hasChanges) {
|
|
2287
|
+
const originalJSDoc = source.slice(jsdocComment.pos, jsdocComment.end);
|
|
2288
|
+
const jsdocChanged = originalJSDoc.trim() !== newJSDoc.trim();
|
|
2289
|
+
const newFunctionText = nodeTypeFunctionText(nodeType);
|
|
2290
|
+
let functionBodyChanged = false;
|
|
2291
|
+
let newFunctionDeclaration = "";
|
|
2292
|
+
if (newFunctionText) {
|
|
2293
|
+
let strippedFunctionText = newFunctionText.trim();
|
|
2294
|
+
while (strippedFunctionText.startsWith("/**")) {
|
|
2295
|
+
strippedFunctionText = strippedFunctionText.replace(/^\/\*\*[\s\S]*?\*\/\s*/, "").trim();
|
|
2296
|
+
}
|
|
2297
|
+
newFunctionDeclaration = strippedFunctionText;
|
|
2298
|
+
const currentFunctionDeclaration = source.slice(functionNode.getStart(), functionNode.getEnd()).trim();
|
|
2299
|
+
const normalizedNew = newFunctionDeclaration.replace(/\s+/g, " ");
|
|
2300
|
+
const normalizedCurrent = currentFunctionDeclaration.replace(/\s+/g, " ");
|
|
2301
|
+
functionBodyChanged = normalizedNew !== normalizedCurrent;
|
|
2302
|
+
}
|
|
2303
|
+
if (!jsdocChanged && !functionBodyChanged) {
|
|
2304
|
+
return { code: source, changed: hasChanges };
|
|
2305
|
+
}
|
|
2306
|
+
if (functionBodyChanged && newFunctionDeclaration) {
|
|
2307
|
+
const before = source.slice(0, jsdocComment.pos);
|
|
2308
|
+
const after = source.slice(functionNode.getEnd());
|
|
2309
|
+
return {
|
|
2310
|
+
code: before + newJSDoc + "\n" + newFunctionDeclaration + after,
|
|
2311
|
+
changed: true
|
|
2312
|
+
};
|
|
2313
|
+
} else if (jsdocChanged) {
|
|
2314
|
+
const before = source.slice(0, jsdocComment.pos);
|
|
2315
|
+
const after = source.slice(jsdocComment.end);
|
|
2316
|
+
return {
|
|
2317
|
+
code: before + newJSDoc + after,
|
|
2318
|
+
changed: true
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
return { code: source, changed: hasChanges };
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// src/api/in-place/orphaned-node-types.ts
|
|
2325
|
+
import * as ts3 from "typescript";
|
|
2326
|
+
function removeOrphanedNodeTypeFunctions(source, ast, allWorkflows) {
|
|
2327
|
+
const sourceFile = parseSource(source);
|
|
2328
|
+
const validFunctionNames = new Set(ast.nodeTypes.map((nt) => nt.functionName));
|
|
2329
|
+
if (allWorkflows) {
|
|
2330
|
+
for (const workflow of allWorkflows) {
|
|
2331
|
+
for (const nt of workflow.nodeTypes) {
|
|
2332
|
+
validFunctionNames.add(nt.functionName);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
const functionsToRemove = [];
|
|
2337
|
+
ts3.forEachChild(sourceFile, (node) => {
|
|
2338
|
+
if (ts3.isFunctionDeclaration(node) && node.name) {
|
|
2339
|
+
const functionName = node.name.text;
|
|
2340
|
+
const functionStart = node.getFullStart();
|
|
2341
|
+
const leadingComments = ts3.getLeadingCommentRanges(source, functionStart);
|
|
2342
|
+
if (!leadingComments) return;
|
|
2343
|
+
let isNodeTypeFunction = false;
|
|
2344
|
+
let jsdocStart = functionStart;
|
|
2345
|
+
for (const comment of leadingComments) {
|
|
2346
|
+
if (comment.kind === ts3.SyntaxKind.MultiLineCommentTrivia) {
|
|
2347
|
+
const commentText = source.slice(comment.pos, comment.end);
|
|
2348
|
+
if (commentText.includes("@flowWeaver nodeType")) {
|
|
2349
|
+
isNodeTypeFunction = true;
|
|
2350
|
+
jsdocStart = comment.pos;
|
|
2351
|
+
break;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
if (!isNodeTypeFunction) return;
|
|
2356
|
+
if (!validFunctionNames.has(functionName)) {
|
|
2357
|
+
const fullStart = jsdocStart;
|
|
2358
|
+
const fullEnd = node.end;
|
|
2359
|
+
let endPos = fullEnd;
|
|
2360
|
+
while (endPos < source.length && (source[endPos] === "\n" || source[endPos] === "\r")) {
|
|
2361
|
+
endPos++;
|
|
2362
|
+
}
|
|
2363
|
+
functionsToRemove.push({ start: fullStart, end: endPos });
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
if (functionsToRemove.length === 0) {
|
|
2368
|
+
return { code: source, changed: false };
|
|
2369
|
+
}
|
|
2370
|
+
let result = source;
|
|
2371
|
+
for (const { start, end } of functionsToRemove.sort((a, b) => b.start - a.start)) {
|
|
2372
|
+
result = result.slice(0, start) + result.slice(end);
|
|
2373
|
+
}
|
|
2374
|
+
return { code: result, changed: true };
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// src/api/in-place/runtime-section.ts
|
|
2378
|
+
import * as ts4 from "typescript";
|
|
2379
|
+
|
|
2380
|
+
// src/api/inline-engine.generated.ts
|
|
2381
|
+
var INLINE_ENGINE_SOURCE = '// ---- src/runtime/continuation-core.ts ------------------------------------------\n\n/* The version of the engine that wrote a continuation: the package\'s own. */\nconst ENGINE_VERSION: string = VERSION;\nconst CONTINUATION_FORMAT_VERSION = 1 as const;\nconst GENERATOR_ABI = "flow-weaver-generated-v2" as const;\nconst MAX_CONTINUATION_BYTES = 1024 * 1024;\nconst MAX_CONTINUATION_DEPTH = 32;\nconst MAX_CONTINUATION_ENTRIES = 10_000;\nconst MAX_CONTINUATION_STRING_BYTES = 256 * 1024;\n\n/*\n * Who answers a gate: a person deciding, a system supplying data, an agent\n * doing a task, or the clock (`timer`: a `sleep` node, woken by whoever\n * keeps the run when its time has come).\n */\ntype DurableGateKind = "approval" | "input" | "agent" | "timer";\n\ntype WireValue =\n | null\n | boolean\n | number\n | string\n | readonly WireValue[]\n | { readonly [key: string]: WireValue };\n\ninterface WorkflowFrameAddress {\n readonly workflowId: string;\n readonly invocation: number;\n readonly callerNodeId?: string;\n readonly callerExecutionIndex?: number;\n}\n\ninterface ScopeAddress {\n readonly parentNodeId: string;\n readonly parentExecutionIndex: number;\n readonly scopeName: string;\n readonly invocation: number;\n readonly loopIteration?: number;\n readonly branchArm?: string;\n}\n\ninterface BranchAddress {\n readonly workflowId: string;\n readonly frameDepth: number;\n readonly nodeId: string;\n readonly executionIndex: number;\n readonly arm: string;\n}\n\ninterface ExecutionAddress {\n readonly frames: readonly WorkflowFrameAddress[];\n readonly scopes: readonly ScopeAddress[];\n readonly branches: readonly BranchAddress[];\n readonly nodeId: string;\n readonly nodeType: string;\n readonly executionIndex: number;\n}\n\ninterface ContinuationVariable {\n readonly address: ExecutionAddress;\n readonly portName: string;\n readonly value: WireValue;\n}\n\ninterface EffectReceipt {\n readonly address: ExecutionAddress;\n readonly operationKey: string;\n readonly receipt: WireValue;\n}\n\ninterface ContinuationState {\n readonly completed: readonly ExecutionAddress[];\n readonly variables: readonly ContinuationVariable[];\n readonly nextBoundary: ExecutionAddress;\n}\n\ninterface DurableGate {\n readonly id: string;\n readonly kind: DurableGateKind;\n readonly address: ExecutionAddress;\n readonly payload: WireValue;\n}\n\n/* The branch arms a node sits under, as the compiled graph describes them. */\ntype ContinuationGraphNodeBranchPath = readonly {\n readonly nodeId: string;\n readonly arm: string;\n}[];\n\ninterface ContinuationEnvelope {\n readonly formatVersion: typeof CONTINUATION_FORMAT_VERSION;\n readonly runId: string;\n readonly gateId: string;\n readonly gateKind: DurableGateKind;\n readonly workflowId: string;\n readonly bundleDigest: string;\n readonly graphFingerprint: string;\n readonly engineVersion: string;\n readonly generatorAbi: string;\n readonly location: ExecutionAddress;\n readonly state: ContinuationState;\n readonly receipts: readonly EffectReceipt[];\n readonly createdAt: string;\n readonly checksum: string;\n}\n\ndeclare const acceptedContinuationBrand: unique symbol;\ntype AcceptedContinuationEnvelope = ContinuationEnvelope & {\n readonly [acceptedContinuationBrand]: true;\n};\n\ntype ContinuationRefusalReason =\n | "malformed"\n | "oversized"\n | "unsupported-format"\n | "incompatible-engine"\n | "incompatible-generator"\n | "wrong-workflow"\n | "wrong-bundle"\n | "wrong-graph"\n | "checksum-mismatch"\n | "stale-gate"\n | "wrong-run"\n | "ambiguous-effect";\n\ninterface ContinuationRefusal {\n readonly accepted: false;\n readonly reason: ContinuationRefusalReason;\n readonly message: string;\n}\n\ninterface AcceptedContinuation {\n readonly accepted: true;\n readonly envelope: AcceptedContinuationEnvelope;\n}\n\ntype DecodedContinuation = AcceptedContinuation | ContinuationRefusal;\n\n/* What a resume must match before an envelope is trusted. */\ninterface ContinuationIdentity {\n readonly runId: string;\n readonly workflowId: string;\n /* When given, the envelope must be the one this gate answer belongs to. */\n readonly gateId?: string;\n}\n\nconst ENVELOPE_KEYS = [\n "formatVersion",\n "runId",\n "gateId",\n "gateKind",\n "workflowId",\n "bundleDigest",\n "graphFingerprint",\n "engineVersion",\n "generatorAbi",\n "location",\n "state",\n "receipts",\n "createdAt",\n "checksum",\n] as const;\n\nconst acceptedContinuations = new WeakSet<object>();\n\n// ---------------------------------------------------------------------------\n// Hashing. A compiled file has no `node:crypto`, and Web Crypto only hashes\n// asynchronously, while gate ids are needed synchronously in the middle of a\n// run. This is SHA-256 from the specification, over UTF-8 bytes.\n// ---------------------------------------------------------------------------\n\nfunction utf8BytesOf(text: string): number[] {\n const bytes: number[] = [];\n for (let index = 0; index < text.length; index++) {\n let code = text.charCodeAt(index);\n if (code >= 0xd800 && code <= 0xdbff && index + 1 < text.length) {\n const low = text.charCodeAt(index + 1);\n if (low >= 0xdc00 && low <= 0xdfff) {\n code = 0x10000 + ((code - 0xd800) << 10) + (low - 0xdc00);\n index++;\n }\n }\n // A lone surrogate is not encodable. Every UTF-8 encoder, Node\'s\n // included, writes the replacement character for it.\n if (code >= 0xd800 && code <= 0xdfff) code = 0xfffd;\n if (code < 0x80) bytes.push(code);\n else if (code < 0x800) bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));\n else if (code < 0x10000) {\n bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));\n } else {\n bytes.push(\n 0xf0 | (code >> 18),\n 0x80 | ((code >> 12) & 0x3f),\n 0x80 | ((code >> 6) & 0x3f),\n 0x80 | (code & 0x3f),\n );\n }\n }\n return bytes;\n}\n\nfunction utf8ByteLength(text: string): number {\n return utf8BytesOf(text).length;\n}\n\nconst SHA256_K = [\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n];\n\n/* The SHA-256 of a string\'s UTF-8 bytes, as lowercase hex. */\nfunction sha256Hex(text: string): string {\n const bytes = utf8BytesOf(text);\n const bitLength = bytes.length * 8;\n bytes.push(0x80);\n while (bytes.length % 64 !== 56) bytes.push(0);\n // The length is a 64-bit big-endian integer. Strings this long never\n // occur, so the high word is the floor of the division.\n const high = Math.floor(bitLength / 0x100000000);\n const low = bitLength >>> 0;\n bytes.push(\n (high >>> 24) & 0xff, (high >>> 16) & 0xff, (high >>> 8) & 0xff, high & 0xff,\n (low >>> 24) & 0xff, (low >>> 16) & 0xff, (low >>> 8) & 0xff, low & 0xff,\n );\n\n const h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];\n const w = new Array<number>(64);\n const rotr = (value: number, bits: number): number => (value >>> bits) | (value << (32 - bits));\n\n for (let offset = 0; offset < bytes.length; offset += 64) {\n for (let i = 0; i < 16; i++) {\n const j = offset + i * 4;\n w[i] = ((bytes[j] << 24) | (bytes[j + 1] << 16) | (bytes[j + 2] << 8) | bytes[j + 3]) >>> 0;\n }\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);\n const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);\n w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;\n }\n let [a, b, c, d, e, f, g, hh] = h;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const temp1 = (hh + S1 + ch + SHA256_K[i] + w[i]) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const temp2 = (S0 + maj) >>> 0;\n hh = g;\n g = f;\n f = e;\n e = (d + temp1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (temp1 + temp2) >>> 0;\n }\n h[0] = (h[0] + a) >>> 0;\n h[1] = (h[1] + b) >>> 0;\n h[2] = (h[2] + c) >>> 0;\n h[3] = (h[3] + d) >>> 0;\n h[4] = (h[4] + e) >>> 0;\n h[5] = (h[5] + f) >>> 0;\n h[6] = (h[6] + g) >>> 0;\n h[7] = (h[7] + hh) >>> 0;\n }\n return h.map((word) => word.toString(16).padStart(8, "0")).join("");\n}\n\n// ---------------------------------------------------------------------------\n// Wire values: what may cross a gate or live in a continuation.\n// ---------------------------------------------------------------------------\n\nclass WireValidationError extends Error {\n constructor(\n readonly reason: "malformed" | "oversized",\n message: string,\n ) {\n super(message);\n this.name = "WireValidationError";\n }\n}\n\ninterface ValidationBudget {\n entries: number;\n bytes: number;\n readonly seen: Set<object>;\n}\n\nconst ownKeyOf = (value: object, key: string): boolean => Object.prototype.hasOwnProperty.call(value, key);\n\nfunction assertWireValue(\n value: unknown,\n path: string,\n depth: number,\n budget: ValidationBudget,\n): asserts value is WireValue {\n if (depth > MAX_CONTINUATION_DEPTH) {\n throw new WireValidationError("oversized", `${path} exceeds maximum depth`);\n }\n\n budget.entries++;\n if (budget.entries > MAX_CONTINUATION_ENTRIES) {\n throw new WireValidationError("oversized", "continuation exceeds aggregate entry limit");\n }\n\n if (value === null || typeof value === "boolean") return;\n if (typeof value === "number") {\n if (!Number.isFinite(value)) {\n throw new WireValidationError("malformed", `${path} contains a non-finite number`);\n }\n return;\n }\n if (typeof value === "string") {\n const bytes = utf8ByteLength(value);\n if (bytes > MAX_CONTINUATION_STRING_BYTES) {\n throw new WireValidationError("oversized", `${path} exceeds maximum string size`);\n }\n budget.bytes += bytes;\n if (budget.bytes > MAX_CONTINUATION_BYTES) {\n throw new WireValidationError("oversized", "continuation exceeds maximum wire size");\n }\n return;\n }\n if (\n value === undefined ||\n typeof value === "bigint" ||\n typeof value === "symbol" ||\n typeof value === "function"\n ) {\n throw new WireValidationError("malformed", `${path} is not a plain wire value`);\n }\n // Everything that is not an object was returned or refused above. The cast\n // is for a compiled file checked without strictNullChecks, where `unknown`\n // does not narrow to `object` by exclusion.\n const seen = value as object;\n if (budget.seen.has(seen)) {\n throw new WireValidationError("malformed", `${path} contains a cycle or repeated object`);\n }\n budget.seen.add(seen);\n\n if (Array.isArray(value)) {\n if (Object.getOwnPropertySymbols(value).length > 0) {\n throw new WireValidationError("malformed", `${path} contains a symbol property`);\n }\n const descriptors = Object.getOwnPropertyDescriptors(value);\n for (const [key, descriptor] of Object.entries(descriptors)) {\n if (key === "length") continue;\n if (!/^(0|[1-9]\\d*)$/.test(key) || descriptor.get || descriptor.set || !descriptor.enumerable) {\n throw new WireValidationError("malformed", `${path} contains an accessor or property`);\n }\n }\n for (let index = 0; index < value.length; index++) {\n if (!ownKeyOf(descriptors, String(index))) {\n throw new WireValidationError("malformed", `${path} contains a sparse array`);\n }\n assertWireValue(descriptors[String(index)].value, `${path}[${index}]`, depth + 1, budget);\n }\n budget.seen.delete(seen);\n return;\n }\n\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new WireValidationError("malformed", `${path} contains a class or host object`);\n }\n if (Object.getOwnPropertySymbols(value).length > 0) {\n throw new WireValidationError("malformed", `${path} contains a symbol property`);\n }\n const descriptors = Object.getOwnPropertyDescriptors(value);\n for (const [key, descriptor] of Object.entries(descriptors)) {\n if (descriptor.get || descriptor.set || !descriptor.enumerable) {\n throw new WireValidationError("malformed", `${path}.${key} is an accessor or non-enumerable property`);\n }\n const keyBytes = utf8ByteLength(key);\n if (keyBytes > MAX_CONTINUATION_STRING_BYTES) {\n throw new WireValidationError("oversized", `${path} contains an oversized key`);\n }\n budget.bytes += keyBytes;\n assertWireValue(descriptor.value, `${path}.${key}`, depth + 1, budget);\n }\n budget.seen.delete(seen);\n}\n\nfunction validateWireValue(value: unknown): asserts value is WireValue {\n assertWireValue(value, "$", 0, { entries: 0, bytes: 0, seen: new Set() });\n}\n\nfunction canonicalJson(value: WireValue): string {\n if (value === null || typeof value === "boolean" || typeof value === "number") {\n return JSON.stringify(value);\n }\n if (typeof value === "string") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;\n const record = value as { readonly [key: string]: WireValue };\n return `{${Object.keys(record)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)\n .join(",")}}`;\n}\n\nfunction canonicalWireValue(value: unknown): string {\n validateWireValue(value);\n return canonicalJson(value);\n}\n\nfunction cloneAndFreezeWireValue<T extends WireValue>(value: T): T {\n const cloned = JSON.parse(canonicalJson(value)) as T;\n const freeze = (entry: WireValue): void => {\n if (typeof entry !== "object" || entry === null) return;\n for (const child of Array.isArray(entry) ? entry : Object.values(entry)) {\n freeze(child);\n }\n Object.freeze(entry);\n };\n freeze(cloned);\n return cloned;\n}\n\n// ---------------------------------------------------------------------------\n// Identity: addresses, gate ids, operation keys, checksums.\n// ---------------------------------------------------------------------------\n\nfunction executionAddressKey(address: ExecutionAddress): string {\n return canonicalWireValue(address);\n}\n\nfunction durableGateId(runId: string, kind: DurableGateKind, address: ExecutionAddress): string {\n return sha256Hex(`${runId}\\0${kind}\\0${executionAddressKey(address)}`);\n}\n\nfunction operationKey(runId: string, address: ExecutionAddress): string {\n validateWireValue(address);\n return sha256Hex(`${runId}\\0${canonicalJson(address as unknown as WireValue)}`);\n}\n\nfunction withoutChecksum(envelope: ContinuationEnvelope): WireValue {\n const { checksum: _checksum, ...unsigned } = envelope;\n return unsigned as unknown as WireValue;\n}\n\nfunction continuationChecksum(envelope: ContinuationEnvelope): string {\n validateWireValue(withoutChecksum(envelope));\n return sha256Hex(canonicalJson(withoutChecksum(envelope)));\n}\n\nfunction createContinuationEnvelope(\n fields: Omit<\n ContinuationEnvelope,\n "formatVersion" | "engineVersion" | "generatorAbi" | "createdAt" | "checksum"\n > & {\n readonly engineVersion?: string;\n readonly generatorAbi?: string;\n readonly createdAt?: string;\n },\n): ContinuationEnvelope {\n const unsigned = {\n formatVersion: CONTINUATION_FORMAT_VERSION,\n runId: fields.runId,\n gateId: fields.gateId,\n gateKind: fields.gateKind,\n workflowId: fields.workflowId,\n bundleDigest: fields.bundleDigest,\n graphFingerprint: fields.graphFingerprint,\n engineVersion: fields.engineVersion ?? VERSION,\n generatorAbi: fields.generatorAbi ?? GENERATOR_ABI,\n location: fields.location,\n state: fields.state,\n receipts: fields.receipts,\n createdAt: fields.createdAt ?? new Date().toISOString(),\n } satisfies Omit<ContinuationEnvelope, "checksum">;\n validateWireValue(unsigned);\n const envelope = Object.assign({}, unsigned, { checksum: "" }) as ContinuationEnvelope;\n return { ...envelope, checksum: continuationChecksum(envelope) };\n}\n\n// ---------------------------------------------------------------------------\n// Structure: is this JSON shaped like an envelope at all?\n// ---------------------------------------------------------------------------\n\nfunction isPlainObjectRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== "object" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction hasExactlyTheKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {\n const actual = Object.keys(value).sort();\n const expected = [...keys].sort();\n return actual.length === expected.length && actual.every((key, index) => key === expected[index]);\n}\n\nfunction hasRequiredAndOptionalKeys(\n value: Record<string, unknown>,\n required: readonly string[],\n optional: readonly string[] = [],\n): boolean {\n const keys = Object.keys(value);\n return (\n required.every((key) => ownKeyOf(value, key)) &&\n keys.every((key) => required.includes(key) || optional.includes(key))\n );\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction isFrameAddress(value: unknown): value is WorkflowFrameAddress {\n return (\n isPlainObjectRecord(value) &&\n hasRequiredAndOptionalKeys(value, ["workflowId", "invocation"], ["callerNodeId", "callerExecutionIndex"]) &&\n typeof value.workflowId === "string" &&\n value.workflowId.length > 0 &&\n isNonNegativeInteger(value.invocation) &&\n (value.callerNodeId === undefined || typeof value.callerNodeId === "string") &&\n (value.callerExecutionIndex === undefined || isNonNegativeInteger(value.callerExecutionIndex)) &&\n ((value.callerNodeId === undefined && value.callerExecutionIndex === undefined) ||\n (typeof value.callerNodeId === "string" && isNonNegativeInteger(value.callerExecutionIndex)))\n );\n}\n\nfunction isScopeAddress(value: unknown): value is ScopeAddress {\n return (\n isPlainObjectRecord(value) &&\n hasRequiredAndOptionalKeys(\n value,\n ["parentNodeId", "parentExecutionIndex", "scopeName", "invocation"],\n ["loopIteration", "branchArm"],\n ) &&\n typeof value.parentNodeId === "string" &&\n value.parentNodeId.length > 0 &&\n isNonNegativeInteger(value.parentExecutionIndex) &&\n typeof value.scopeName === "string" &&\n value.scopeName.length > 0 &&\n isNonNegativeInteger(value.invocation) &&\n (value.loopIteration === undefined || isNonNegativeInteger(value.loopIteration)) &&\n (value.branchArm === undefined || typeof value.branchArm === "string")\n );\n}\n\nfunction isExecutionAddress(value: unknown): value is ExecutionAddress {\n return (\n isPlainObjectRecord(value) &&\n hasExactlyTheKeys(value, ["frames", "scopes", "branches", "nodeId", "nodeType", "executionIndex"]) &&\n Array.isArray(value.frames) &&\n value.frames.length > 0 &&\n value.frames.every(isFrameAddress) &&\n Array.isArray(value.scopes) &&\n value.scopes.every(isScopeAddress) &&\n Array.isArray(value.branches) &&\n value.branches.every(\n (branch) =>\n isPlainObjectRecord(branch) &&\n hasExactlyTheKeys(branch, ["workflowId", "frameDepth", "nodeId", "executionIndex", "arm"]) &&\n typeof branch.workflowId === "string" &&\n branch.workflowId.length > 0 &&\n isNonNegativeInteger(branch.frameDepth) &&\n typeof branch.nodeId === "string" &&\n branch.nodeId.length > 0 &&\n isNonNegativeInteger(branch.executionIndex) &&\n typeof branch.arm === "string" &&\n branch.arm.length > 0,\n ) &&\n typeof value.nodeId === "string" &&\n value.nodeId.length > 0 &&\n typeof value.nodeType === "string" &&\n value.nodeType.length > 0 &&\n isNonNegativeInteger(value.executionIndex)\n );\n}\n\nfunction isContinuationVariable(value: unknown): value is ContinuationVariable {\n return (\n isPlainObjectRecord(value) &&\n hasExactlyTheKeys(value, ["address", "portName", "value"]) &&\n isExecutionAddress(value.address) &&\n typeof value.portName === "string" &&\n value.portName.length > 0\n );\n}\n\nfunction isEffectReceipt(value: unknown): value is EffectReceipt {\n return (\n isPlainObjectRecord(value) &&\n hasExactlyTheKeys(value, ["address", "operationKey", "receipt"]) &&\n isExecutionAddress(value.address) &&\n typeof value.operationKey === "string" &&\n /^[0-9a-f]{64}$/.test(value.operationKey)\n );\n}\n\nfunction isContinuationState(value: unknown): value is ContinuationState {\n return (\n isPlainObjectRecord(value) &&\n hasExactlyTheKeys(value, ["completed", "variables", "nextBoundary"]) &&\n Array.isArray(value.completed) &&\n value.completed.every(isExecutionAddress) &&\n Array.isArray(value.variables) &&\n value.variables.every(isContinuationVariable) &&\n isExecutionAddress(value.nextBoundary)\n );\n}\n\nfunction isExactIsoUtc(value: string): boolean {\n const timestamp = Date.parse(value);\n return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;\n}\n\nfunction structurallyValidEnvelope(value: unknown): value is ContinuationEnvelope {\n if (!isPlainObjectRecord(value) || !hasExactlyTheKeys(value, ENVELOPE_KEYS)) return false;\n return !(\n typeof value.formatVersion !== "number" ||\n typeof value.runId !== "string" ||\n typeof value.gateId !== "string" ||\n !["approval", "input", "agent", "timer"].includes(value.gateKind as string) ||\n typeof value.workflowId !== "string" ||\n typeof value.bundleDigest !== "string" ||\n typeof value.graphFingerprint !== "string" ||\n typeof value.engineVersion !== "string" ||\n typeof value.generatorAbi !== "string" ||\n typeof value.createdAt !== "string" ||\n typeof value.checksum !== "string" ||\n value.runId.length === 0 ||\n !/^[0-9a-f]{64}$/.test(value.gateId) ||\n value.workflowId.length === 0 ||\n value.engineVersion.length === 0 ||\n value.generatorAbi.length === 0 ||\n !isExecutionAddress(value.location) ||\n !isContinuationState(value.state) ||\n !Array.isArray(value.receipts) ||\n !value.receipts.every(isEffectReceipt) ||\n !/^sha256:[0-9a-f]{64}$/.test(value.bundleDigest) ||\n !/^[0-9a-f]{64}$/.test(value.graphFingerprint) ||\n !/^[0-9a-f]{64}$/.test(value.checksum) ||\n !isExactIsoUtc(value.createdAt) ||\n executionAddressKey(value.location) !== executionAddressKey(value.state.nextBoundary)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Acceptance: the checks a host can make without the compiled graph.\n// ---------------------------------------------------------------------------\n\n/*\n * Take an envelope back from wherever a host kept it. It is parsed when given\n * as text, checked for shape, checksum, gate identity, engine and generator\n * versions, and bound to the run and workflow it is about to resume. What is\n * accepted is a frozen copy; the input is never trusted afterwards.\n *\n * This is what the compiled file offers a host of its own. The coordinator\'s\n * `decodeContinuation` does this and, having the compiled graph, more.\n */\nfunction acceptContinuation(input: unknown, identity: ContinuationIdentity): DecodedContinuation {\n let value: unknown = input;\n if (typeof input === "string") {\n if (utf8ByteLength(input) > MAX_CONTINUATION_BYTES) {\n return { accepted: false, reason: "oversized", message: "continuation exceeds maximum wire size" };\n }\n try {\n value = JSON.parse(input);\n } catch {\n return { accepted: false, reason: "malformed", message: "continuation is not valid JSON" };\n }\n }\n try {\n validateWireValue(value);\n } catch (error) {\n if (error instanceof WireValidationError) {\n return { accepted: false, reason: error.reason, message: error.message };\n }\n return { accepted: false, reason: "malformed", message: "continuation is not a wire value" };\n }\n if (!structurallyValidEnvelope(value)) {\n return { accepted: false, reason: "malformed", message: "continuation has missing, unknown, or invalid fields" };\n }\n if (value.formatVersion !== CONTINUATION_FORMAT_VERSION) {\n return { accepted: false, reason: "unsupported-format", message: `unsupported continuation format ${value.formatVersion}` };\n }\n if (continuationChecksum(value) !== value.checksum) {\n return { accepted: false, reason: "checksum-mismatch", message: "continuation checksum does not match" };\n }\n if (value.gateId !== durableGateId(value.runId, value.gateKind, value.location)) {\n return { accepted: false, reason: "malformed", message: "continuation gate identity does not match its execution address" };\n }\n if (value.engineVersion !== VERSION) {\n return { accepted: false, reason: "incompatible-engine", message: `continuation was written by engine ${value.engineVersion}, this is ${VERSION}` };\n }\n if (value.generatorAbi !== GENERATOR_ABI) {\n return { accepted: false, reason: "incompatible-generator", message: "continuation generator ABI does not match" };\n }\n if (value.runId !== identity.runId) {\n return { accepted: false, reason: "wrong-run", message: "continuation belongs to another run" };\n }\n if (value.workflowId !== identity.workflowId) {\n return { accepted: false, reason: "wrong-workflow", message: "continuation belongs to another workflow" };\n }\n if (identity.gateId !== undefined && value.gateId !== identity.gateId) {\n return { accepted: false, reason: "stale-gate", message: "continuation gate is stale or reordered" };\n }\n const accepted = cloneAndFreezeWireValue(value as unknown as WireValue) as unknown as AcceptedContinuationEnvelope;\n acceptedContinuations.add(accepted);\n return { accepted: true, envelope: accepted };\n}\n\nfunction assertAcceptedContinuation(\n value: ContinuationEnvelope,\n): asserts value is AcceptedContinuationEnvelope {\n if (!acceptedContinuations.has(value)) {\n throw new Error("ContinuationEnvelope must come from a successful decodeContinuation or acceptContinuation result");\n }\n}\n\n// ---- src/runtime/durable-execution.ts ------------------------------------------\n\ninterface GateResolution {\n readonly gateId: string;\n readonly value: WireValue;\n}\n\nfunction acceptGateResolution(resolution: GateResolution | undefined): GateResolution | undefined {\n if (resolution === undefined) return undefined;\n validateWireValue(resolution);\n const keys = Object.keys(resolution).sort();\n if (keys.length !== 2 || keys[0] !== \'gateId\' || keys[1] !== \'value\' || !/^[0-9a-f]{64}$/.test(resolution.gateId)) {\n throw new Error(\'resolution must contain exactly a canonical gateId and wire value\');\n }\n return cloneAndFreezeWireValue(resolution as unknown as WireValue) as unknown as GateResolution;\n}\n\ntype EffectRecovery =\n | { readonly kind: \'not-committed\' }\n | {\n readonly kind: \'committed\';\n readonly receipt: WireValue;\n readonly result: WireValue;\n }\n | { readonly kind: \'repeatable\' }\n | { readonly kind: \'ambiguous\' };\n\ninterface EffectAdapter {\n recover(operationKey: string, address: ExecutionAddress): Promise<EffectRecovery>;\n /* Persist the exact successful effect result before it enters a continuation. */\n commit?(\n operationKey: string,\n address: ExecutionAddress,\n execution: EffectExecution<WireValue>,\n ): Promise<void>;\n}\n\ninterface WorkflowRuntimeServices {\n readonly debugger?: TDebugger;\n readonly debugController?: DebugController;\n readonly mocks?: FwMockConfig;\n readonly workflowRegistry?: Readonly<Record<string, (...args: unknown[]) => unknown>>;\n readonly effectAdapter?: EffectAdapter;\n}\n\n/*\n * The engine as a generated body sees it. It is an interface, not the class,\n * so a runtime built by the package and one built by a compiled file\'s own\n * copy of the engine are interchangeable to the type checker: two classes\n * with private members never are.\n */\ninterface DurableEngine {\n address(\n runtime: Pick<WorkflowRuntime, \'frames\' | \'scopes\' | \'branches\'>,\n nodeId: string,\n nodeType: string,\n executionIndex: number,\n ): ExecutionAddress;\n /* Called once at the top of a root body: the workflow\'s name and graph identity. */\n bind(runtime: Pick<WorkflowRuntime, \'frames\'>, workflowId: string, graphFingerprint: string): void;\n shouldExecute(address: ExecutionAddress): boolean;\n commitNode(address: ExecutionAddress): void;\n setVariable(address: ExecutionAddress, portName: string, value: unknown): void;\n getVariable(address: ExecutionAddress, portName: string, allowAncestorLookup?: boolean): unknown;\n resolveGate(runtime: WorkflowRuntime, boundary: GateBoundary): WireValue;\n executeEffect<T extends WireValue>(\n runtime: WorkflowRuntime,\n boundary: EffectBoundary,\n execute: (operationKey: string) => Promise<EffectExecution<T>>,\n ): Promise<T>;\n assertResumeResolutionConsumed(): void;\n}\n\ninterface WorkflowRuntime {\n readonly runId: string;\n readonly abortSignal?: AbortSignal;\n readonly services: WorkflowRuntimeServices;\n readonly durable: DurableEngine;\n readonly frames: readonly WorkflowFrameAddress[];\n readonly scopes: readonly ScopeAddress[];\n readonly branches: readonly BranchAddress[];\n}\n\ninterface NodeExecutionRuntime {\n readonly nodeId: string;\n readonly runtime: WorkflowRuntime;\n readonly recursionDepth: number;\n createNestedRuntime(workflowId: string): WorkflowRuntime;\n}\n\ninterface CreateWorkflowRuntimeOptions {\n readonly runId: string;\n readonly workflowId: string;\n readonly abortSignal?: AbortSignal;\n readonly services?: WorkflowRuntimeServices;\n readonly continuation?: AcceptedContinuationEnvelope;\n readonly resolution?: GateResolution;\n /*\n * The identity of the compiled artifact this run executes, `sha256:<hex>`.\n * A coordinator that hashes the artifact passes it, and a continuation is\n * refused on resume when it names another. Left out, the engine derives one\n * from the workflow\'s own graph identity, so a host that keeps a\n * continuation and brings it back to the same compiled file needs nothing\n * more.\n */\n readonly bundleDigest?: string;\n}\n\ninterface GateBoundary {\n readonly kind: DurableGateKind;\n readonly nodeId: string;\n readonly nodeType: string;\n readonly executionIndex: number;\n readonly payload: WireValue;\n}\n\n/*\n * A gate answered from the mocks, or undefined to yield for real.\n *\n * Any gate is answered by its node id through `gates`, with its data outputs\n * as the object. The built-in agent and event gates are also answered from\n * their own sections, keyed by what the node was given as its first input\n * (the agent id, the event name), by `node:key`, or by `node:*`. The answer\n * becomes the gate\'s whole output envelope, control ports filled in, which\n * is exactly what a person\'s answer becomes in `buildGateResolution`.\n */\nfunction mockedGateAnswer(mocks: FwMockConfig | undefined, boundary: GateBoundary): WireValue | undefined {\n if (!mocks) return undefined;\n const { nodeId, nodeType } = boundary;\n const pick = (section: Record<string, object> | undefined, key: string | undefined): object | undefined => {\n if (!section) return undefined;\n if (key !== undefined && section[`${nodeId}:${key}`] !== undefined) return section[`${nodeId}:${key}`];\n if (section[`${nodeId}:*`] !== undefined) return section[`${nodeId}:*`];\n return key !== undefined ? section[key] : undefined;\n };\n const firstInput = (): string | undefined => {\n const p = boundary.payload as { arguments?: Array<{ value?: unknown; absent?: boolean }> } | null;\n const v = p?.arguments?.[0]?.value;\n return v === undefined || v === null ? undefined : String(v);\n };\n let data: object | undefined = mocks.gates?.[nodeId];\n if (data === undefined && nodeType === \'waitForAgent\') {\n const v = pick(mocks.agents, firstInput());\n if (v !== undefined) data = { agentResult: v };\n }\n if (data === undefined && nodeType === \'waitForEvent\') {\n const v = pick(mocks.events, firstInput());\n if (v !== undefined) data = { eventData: v };\n }\n // `fast` skips waits: a sleeping run wakes at once, as `delay` returns at once.\n if (data === undefined && nodeType === \'sleep\' && mocks.fast === true) {\n data = { wokeAt: new Date().toISOString() };\n }\n if (data === undefined || typeof data !== \'object\') return undefined;\n const value = { onSuccess: true, onFailure: false, ...(data as Record<string, unknown>) };\n validateWireValue(value);\n return value as WireValue;\n}\n\ninterface EffectBoundary {\n readonly nodeId: string;\n readonly nodeType: string;\n readonly executionIndex: number;\n}\n\ninterface EffectExecution<T extends WireValue> {\n readonly result: T;\n readonly receipt: WireValue;\n}\n\n/*\n * Thrown out of a generated body when a gate has to wait for the outside.\n * It carries everything a host needs to keep: the gate to answer, and the\n * continuation to bring back with the answer. `continuation` is present when\n * the body declared its identity (every body compiled by this version does);\n * a coordinator may also build its own from `state` and `receipts`.\n */\nclass DurableGateYield extends Error {\n readonly code = \'FLOW_WEAVER_DURABLE_GATE_YIELD\';\n\n constructor(\n readonly gate: DurableGate,\n readonly state: ContinuationState,\n readonly receipts: readonly EffectReceipt[],\n readonly continuation?: ContinuationEnvelope,\n ) {\n super(`Workflow yielded at ${gate.kind} gate "${gate.id}"`);\n this.name = \'DurableGateYield\';\n }\n}\n\nclass AmbiguousEffectError extends Error {\n readonly code = \'FLOW_WEAVER_AMBIGUOUS_EFFECT\';\n\n constructor(\n readonly operationKey: string,\n readonly address: ExecutionAddress,\n ) {\n super(`Effect "${operationKey}" has an ambiguous commit state`);\n this.name = \'AmbiguousEffectError\';\n }\n}\n\nfunction durableAddressKey(address: ExecutionAddress): string {\n return executionAddressKey(address);\n}\n\nfunction durableVariableKey(address: ExecutionAddress, portName: string): string {\n return `${executionAddressKey(address)}\\0${portName}`;\n}\n\nfunction cloneExecutionAddress(address: ExecutionAddress): ExecutionAddress {\n return {\n frames: address.frames.map((frame) => ({ ...frame })),\n scopes: address.scopes.map((scope) => ({ ...scope })),\n branches: address.branches.map((branch) => ({ ...branch })),\n nodeId: address.nodeId,\n nodeType: address.nodeType,\n executionIndex: address.executionIndex,\n };\n}\n\nfunction isCanonicalPrefix(candidate: readonly unknown[], current: readonly unknown[]): boolean {\n return (\n candidate.length <= current.length &&\n canonicalWireValue(candidate) === canonicalWireValue(current.slice(0, candidate.length))\n );\n}\n\nfunction isCanonicalEqual(left: readonly unknown[], right: readonly unknown[]): boolean {\n return left.length === right.length && canonicalWireValue(left) === canonicalWireValue(right);\n}\n\nfunction requireEffectRecovery(value: unknown, key: string, address: ExecutionAddress): EffectRecovery {\n try {\n validateWireValue(value);\n } catch {\n throw new AmbiguousEffectError(key, address);\n }\n if (typeof value !== \'object\' || value === null || Array.isArray(value)) {\n throw new AmbiguousEffectError(key, address);\n }\n const record = value as Record<string, unknown>;\n const kind = record.kind;\n if ((kind === \'not-committed\' || kind === \'repeatable\' || kind === \'ambiguous\') && Object.keys(record).length === 1) {\n return { kind };\n }\n if (\n kind === \'committed\' &&\n Object.keys(record).length === 3 &&\n Object.prototype.hasOwnProperty.call(record, \'receipt\') &&\n Object.prototype.hasOwnProperty.call(record, \'result\')\n ) {\n return {\n kind,\n receipt: record.receipt as WireValue,\n result: record.result as WireValue,\n };\n }\n throw new AmbiguousEffectError(key, address);\n}\n\nfunction requireEffectExecution<T extends WireValue>(\n value: unknown,\n key: string,\n address: ExecutionAddress,\n): EffectExecution<T> {\n try {\n validateWireValue(value);\n } catch {\n throw new AmbiguousEffectError(key, address);\n }\n if (typeof value !== \'object\' || value === null || Array.isArray(value)) {\n throw new AmbiguousEffectError(key, address);\n }\n const record = value as Record<string, unknown>;\n if (\n Object.keys(record).length !== 2 ||\n !Object.prototype.hasOwnProperty.call(record, \'result\') ||\n !Object.prototype.hasOwnProperty.call(record, \'receipt\')\n ) {\n throw new AmbiguousEffectError(key, address);\n }\n return {\n result: record.result as T,\n receipt: record.receipt as WireValue,\n };\n}\n\nclass DurableExecution implements DurableEngine {\n private readonly completed = new Map<string, ExecutionAddress>();\n private readonly variables = new Map<string, Omit<ContinuationVariable, \'value\'> & { readonly value: unknown }>();\n private readonly receipts = new Map<string, EffectReceipt>();\n private readonly resumeEnvelope?: ContinuationEnvelope;\n private readonly resolution?: GateResolution;\n private readonly givenBundleDigest?: string;\n private graphFingerprint?: string;\n private resolutionConsumed = false;\n\n constructor(\n readonly runId: string,\n readonly workflowId: string,\n continuation?: AcceptedContinuationEnvelope,\n resolution?: GateResolution,\n bundleDigest?: string,\n ) {\n if (continuation !== undefined) {\n assertAcceptedContinuation(continuation);\n if (continuation.runId !== runId) {\n throw new Error(\'accepted continuation belongs to another run\');\n }\n if (continuation.workflowId !== workflowId) {\n throw new Error(\'accepted continuation belongs to another workflow\');\n }\n }\n if (bundleDigest !== undefined && !/^sha256:[0-9a-f]{64}$/.test(bundleDigest)) {\n throw new Error(\'bundleDigest must use canonical sha256:<64hex> form\');\n }\n this.givenBundleDigest = bundleDigest;\n this.resumeEnvelope = continuation;\n this.resolution = acceptGateResolution(resolution);\n\n for (const address of continuation?.state.completed ?? []) {\n this.completed.set(durableAddressKey(address), cloneExecutionAddress(address));\n }\n for (const variable of continuation?.state.variables ?? []) {\n this.variables.set(durableVariableKey(variable.address, variable.portName), {\n address: cloneExecutionAddress(variable.address),\n portName: variable.portName,\n value: variable.value,\n });\n }\n for (const receipt of continuation?.receipts ?? []) {\n this.receipts.set(durableAddressKey(receipt.address), {\n address: cloneExecutionAddress(receipt.address),\n operationKey: receipt.operationKey,\n receipt: receipt.receipt,\n });\n }\n }\n\n address(\n runtime: Pick<WorkflowRuntime, \'frames\' | \'scopes\' | \'branches\'>,\n nodeId: string,\n nodeType: string,\n executionIndex: number,\n ): ExecutionAddress {\n return {\n frames: runtime.frames.map((frame) => ({ ...frame })),\n scopes: runtime.scopes.map((scope) => ({ ...scope })),\n branches: runtime.branches.map((branch) => ({ ...branch })),\n nodeId,\n nodeType,\n executionIndex,\n };\n }\n\n /*\n * The artifact identity this run executes under: the one the coordinator\n * gave, or one derived from the graph identity the body declared. Known\n * only after `bind`.\n */\n bundleDigest(): string | undefined {\n if (this.givenBundleDigest !== undefined) return this.givenBundleDigest;\n if (this.graphFingerprint === undefined) return undefined;\n return `sha256:${sha256Hex(`flow-weaver-inline\\0${this.workflowId}\\0${this.graphFingerprint}\\0${ENGINE_VERSION}`)}`;\n }\n\n bind(runtime: Pick<WorkflowRuntime, \'frames\'>, workflowId: string, graphFingerprint: string): void {\n // Nested bodies (a workflow used as a node, `invokeWorkflow`) bind too;\n // only the root frame names the run\'s graph.\n if (runtime.frames.length !== 1) return;\n if (workflowId !== this.workflowId) {\n throw new Error(`runtime was created for workflow "${this.workflowId}" but "${workflowId}" is running on it`);\n }\n this.graphFingerprint = graphFingerprint;\n if (this.resumeEnvelope === undefined) return;\n if (this.resumeEnvelope.graphFingerprint !== graphFingerprint) {\n const error = new Error(\'continuation belongs to another workflow graph\');\n error.name = \'ContinuationMismatchError\';\n throw error;\n }\n const bundleDigest = this.bundleDigest();\n if (bundleDigest !== undefined && this.resumeEnvelope.bundleDigest !== bundleDigest) {\n const error = new Error(\'continuation belongs to another bundle\');\n error.name = \'ContinuationMismatchError\';\n throw error;\n }\n }\n\n shouldExecute(address: ExecutionAddress): boolean {\n return !this.completed.has(durableAddressKey(address));\n }\n\n commitNode(address: ExecutionAddress): void {\n this.completed.set(durableAddressKey(address), cloneExecutionAddress(address));\n }\n\n setVariable(address: ExecutionAddress, portName: string, value: unknown): void {\n this.variables.set(durableVariableKey(address, portName), {\n address: cloneExecutionAddress(address),\n portName,\n value,\n });\n }\n\n getVariable(address: ExecutionAddress, portName: string, allowAncestorLookup = true): unknown {\n const exact = this.variables.get(durableVariableKey(address, portName));\n if (exact !== undefined) return exact.value;\n if (!allowAncestorLookup) return undefined;\n\n const candidates = [...this.variables.values()]\n .filter(\n (variable) =>\n variable.portName === portName &&\n variable.address.nodeId === address.nodeId &&\n variable.address.nodeType === address.nodeType &&\n variable.address.executionIndex === address.executionIndex &&\n isCanonicalPrefix(variable.address.frames, address.frames) &&\n isCanonicalPrefix(variable.address.scopes, address.scopes) &&\n isCanonicalPrefix(variable.address.branches, address.branches),\n )\n .sort(\n (left, right) =>\n right.address.frames.length +\n right.address.scopes.length +\n right.address.branches.length -\n (left.address.frames.length + left.address.scopes.length + left.address.branches.length),\n );\n const specificity = (candidate: (typeof candidates)[number]): number =>\n candidate.address.frames.length + candidate.address.scopes.length + candidate.address.branches.length;\n if (candidates.length > 1 && specificity(candidates[0]) === specificity(candidates[1])) {\n throw new Error(`Ambiguous durable variable address for ${address.nodeId}.${portName}`);\n }\n if (candidates[0] !== undefined) return candidates[0].value;\n\n // Generated workflows leave a selected control-flow branch before\n // projecting its outputs through Exit. A fresh continuation resume has no\n // process-local variable cache, so that projection must recover the one\n // value committed by the branch that actually ran. This is deliberately\n // narrower than ancestor lookup: it cannot cross workflow frames or\n // scopes, and more than one compatible descendant fails closed.\n const converged = [...this.variables.values()].filter(\n (variable) =>\n variable.portName === portName &&\n variable.address.nodeId === address.nodeId &&\n variable.address.nodeType === address.nodeType &&\n variable.address.executionIndex === address.executionIndex &&\n isCanonicalEqual(variable.address.frames, address.frames) &&\n isCanonicalEqual(variable.address.scopes, address.scopes) &&\n isCanonicalPrefix(address.branches, variable.address.branches),\n );\n if (converged.length > 1) {\n throw new Error(`Ambiguous durable variable address for ${address.nodeId}.${portName}`);\n }\n return converged[0]?.value;\n }\n\n resolveGate(runtime: WorkflowRuntime, boundary: GateBoundary): WireValue {\n validateWireValue(boundary.payload);\n // A mocked gate is answered here, on the first run and on every replay\n // alike, so it never pauses and never enters the resume bookkeeping below.\n const mocked = mockedGateAnswer(runtime.services.mocks, boundary);\n if (mocked !== undefined) return mocked;\n const address = this.address(runtime, boundary.nodeId, boundary.nodeType, boundary.executionIndex);\n const id = durableGateId(this.runId, boundary.kind, address);\n\n if (this.resumeEnvelope !== undefined && !this.resolutionConsumed) {\n if (\n durableAddressKey(this.resumeEnvelope.location) !== durableAddressKey(address) ||\n this.resumeEnvelope.gateId !== id ||\n this.resumeEnvelope.gateKind !== boundary.kind ||\n this.resolution === undefined ||\n this.resolution.gateId !== id\n ) {\n const error = new Error(\'Gate resolution is stale, reordered, or does not match the resume boundary\');\n error.name = \'StaleGateError\';\n throw error;\n }\n validateWireValue(this.resolution.value);\n this.resolutionConsumed = true;\n return this.resolution.value;\n }\n\n if (this.resumeEnvelope !== undefined && durableAddressKey(this.resumeEnvelope.location) === durableAddressKey(address)) {\n const error = new Error(\'The consumed resume gate was re-entered\');\n error.name = \'StaleGateError\';\n throw error;\n }\n\n const gate: DurableGate = {\n id,\n kind: boundary.kind,\n address,\n payload: boundary.payload,\n };\n const state = this.snapshot(address);\n const receipts = [...this.receipts.values()];\n const bundleDigest = this.bundleDigest();\n const continuation =\n this.graphFingerprint === undefined || bundleDigest === undefined\n ? undefined\n : createContinuationEnvelope({\n runId: this.runId,\n gateId: id,\n gateKind: boundary.kind,\n workflowId: this.workflowId,\n bundleDigest,\n graphFingerprint: this.graphFingerprint,\n location: address,\n state,\n receipts,\n });\n throw new DurableGateYield(gate, state, receipts, continuation);\n }\n\n async executeEffect<T extends WireValue>(\n runtime: WorkflowRuntime,\n boundary: EffectBoundary,\n execute: (operationKey: string) => Promise<EffectExecution<T>>,\n ): Promise<T> {\n const address = this.address(runtime, boundary.nodeId, boundary.nodeType, boundary.executionIndex);\n const key = operationKey(this.runId, address);\n const committed = this.receipts.get(durableAddressKey(address));\n if (committed !== undefined) {\n throw new Error(`Committed effect "${key}" reached its execution body`);\n }\n\n let recovery: EffectRecovery | undefined;\n try {\n recovery = await runtime.services.effectAdapter?.recover(key, address);\n } catch {\n throw new AmbiguousEffectError(key, address);\n }\n if (recovery === undefined) {\n throw new AmbiguousEffectError(key, address);\n }\n recovery = requireEffectRecovery(recovery, key, address);\n if (recovery.kind === \'ambiguous\') throw new AmbiguousEffectError(key, address);\n if (recovery.kind === \'committed\') {\n this.receipts.set(durableAddressKey(address), {\n address: cloneExecutionAddress(address),\n operationKey: key,\n receipt: recovery.receipt,\n });\n return recovery.result as T;\n }\n\n let executed: EffectExecution<T>;\n try {\n executed = requireEffectExecution<T>(await execute(key), key, address);\n } catch (error) {\n let afterFailure: EffectRecovery;\n try {\n afterFailure =\n (await runtime.services.effectAdapter?.recover(key, address)) ?? ({ kind: \'ambiguous\' } as const);\n } catch {\n throw new AmbiguousEffectError(key, address);\n }\n afterFailure = requireEffectRecovery(afterFailure, key, address);\n if (afterFailure.kind === \'committed\') {\n this.receipts.set(durableAddressKey(address), {\n address: cloneExecutionAddress(address),\n operationKey: key,\n receipt: afterFailure.receipt,\n });\n return afterFailure.result as T;\n }\n if (afterFailure.kind === \'ambiguous\') {\n throw new AmbiguousEffectError(key, address);\n }\n throw error;\n }\n if (runtime.services.effectAdapter?.commit !== undefined) {\n try {\n await runtime.services.effectAdapter.commit(key, address, executed);\n } catch {\n let committed: EffectRecovery;\n try {\n committed = requireEffectRecovery(\n await runtime.services.effectAdapter.recover(key, address),\n key,\n address,\n );\n } catch {\n throw new AmbiguousEffectError(key, address);\n }\n if (\n committed.kind !== \'committed\' ||\n canonicalWireValue(committed.receipt) !== canonicalWireValue(executed.receipt) ||\n canonicalWireValue(committed.result) !== canonicalWireValue(executed.result)\n ) throw new AmbiguousEffectError(key, address);\n this.receipts.set(durableAddressKey(address), {\n address: cloneExecutionAddress(address),\n operationKey: key,\n receipt: committed.receipt,\n });\n return committed.result as T;\n }\n }\n this.receipts.set(durableAddressKey(address), {\n address: cloneExecutionAddress(address),\n operationKey: key,\n receipt: executed.receipt,\n });\n return executed.result;\n }\n\n snapshot(nextBoundary: ExecutionAddress): ContinuationState {\n const completedKeys = new Set(this.completed.keys());\n return {\n completed: [...this.completed.values()].map(cloneExecutionAddress),\n variables: [...this.variables.values()]\n .filter((variable) => completedKeys.has(durableAddressKey(variable.address)))\n .map((variable) => {\n try {\n validateWireValue(variable.value);\n } catch (error) {\n throw new Error(\n `Continuation variable ${variable.address.nodeId}.${variable.portName} is not a wire value: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return {\n address: cloneExecutionAddress(variable.address),\n portName: variable.portName,\n value: variable.value,\n };\n }),\n nextBoundary: cloneExecutionAddress(nextBoundary),\n };\n }\n\n assertResumeResolutionConsumed(): void {\n if (this.resumeEnvelope !== undefined && !this.resolutionConsumed) {\n const error = new Error(\'Resumed execution completed or yielded without consuming its exact gate resolution\');\n error.name = \'UnconsumedGateResolutionError\';\n throw error;\n }\n }\n}\n\nfunction createWorkflowRuntime(options: CreateWorkflowRuntimeOptions): WorkflowRuntime {\n if (options.runId.trim().length === 0) {\n throw new Error(\'runId must be a non-empty coordinator-owned identity\');\n }\n const rootFrame: WorkflowFrameAddress = {\n workflowId: options.workflowId,\n invocation: 0,\n };\n return {\n runId: options.runId,\n abortSignal: options.abortSignal,\n services: options.services ?? {},\n durable: new DurableExecution(\n options.runId,\n options.workflowId,\n options.continuation,\n options.resolution,\n options.bundleDigest,\n ),\n frames: [rootFrame],\n scopes: [],\n branches: [],\n };\n}\n\nfunction createNestedWorkflowRuntime(\n runtime: WorkflowRuntime,\n workflowId: string,\n callerNodeId: string,\n callerExecutionIndex: number,\n invocation: number,\n): WorkflowRuntime {\n return {\n ...runtime,\n frames: [\n ...runtime.frames,\n {\n workflowId,\n invocation,\n callerNodeId,\n callerExecutionIndex,\n },\n ],\n scopes: runtime.scopes,\n branches: runtime.branches,\n };\n}\n\nfunction createScopedWorkflowRuntime(runtime: WorkflowRuntime, scope: ScopeAddress): WorkflowRuntime {\n return {\n ...runtime,\n scopes: [...runtime.scopes, scope],\n branches: runtime.branches,\n };\n}\n\nfunction isDurableGateYield(error: unknown): error is DurableGateYield {\n return (\n error instanceof DurableGateYield ||\n (typeof error === \'object\' &&\n error !== null &&\n (error as { code?: unknown }).code === \'FLOW_WEAVER_DURABLE_GATE_YIELD\')\n );\n}\n\nfunction isAmbiguousEffectError(error: unknown): error is AmbiguousEffectError {\n return (\n error instanceof AmbiguousEffectError ||\n (typeof error === \'object\' &&\n error !== null &&\n (error as { code?: unknown }).code === \'FLOW_WEAVER_AMBIGUOUS_EFFECT\')\n );\n}\n';
|
|
2382
|
+
var INLINE_EXECUTION_CONTEXT_SOURCE = "// ---- src/runtime/ExecutionContext.ts -------------------------------------------\n\n/*\n * Address of a variable in the execution context. Variables are stored under\n * the key `id:portName:executionIndex`.\n */\ninterface VariableAddress {\n id: string;\n portName: string;\n executionIndex: number;\n nodeTypeName?: string | undefined;\n scope?: string | undefined;\n side?: 'start' | 'exit' | undefined;\n /* Inputs remain live/debug-visible but are never serialized as durable outputs. */\n durable?: boolean | undefined;\n}\n\ninterface ExecutionInfo {\n id: string;\n index: number;\n parentIndex?: number | undefined;\n scopeName?: string | undefined;\n}\n\ntype VariableValue = unknown | (() => unknown) | (() => Promise<unknown>);\n\n/*\n * Variable storage with execution-scoped addressing, per-node execution\n * indices, pull execution (lazy evaluation), scopes and parallel lanes, debug\n * events, and the durable engine calls a generated body makes.\n *\n * Scope counters start at 0 on every construction. A resume replays the body\n * from its first node, skipping what the continuation holds, and so reaches\n * the same iteration ordinals the first run assigned\n * (`tests/continuation/durable-loops.test.ts`).\n */\nclass GeneratedExecutionContext {\n private variables: Map<string, VariableValue> = new Map();\n private executions: Map<string, ExecutionInfo> = new Map();\n private executionCounter: number = 0;\n private nodeExecutionCounts: Map<string, number> = new Map();\n private isAsync: boolean;\n // inline: development only\n private flowWeaverDebugger?: TDebugger | undefined;\n // inline: end\n private pullExecutors: Map<string, () => void | Promise<void>> = new Map();\n private nodeExecutionIndices: Map<string, number> = new Map();\n private runtime: WorkflowRuntime;\n private scopeInvocationCounts: Map<string, number> = new Map();\n private nestedInvocationCounts: Map<string, number> = new Map();\n private branchStack: BranchAddress[];\n private allowAncestorDurableVariables = true;\n\n /* `isAsync` is the workflow's async mode; `runtime` is the execution-scoped runtime. */\n constructor(isAsync: boolean = true, runtime: WorkflowRuntime) {\n this.isAsync = isAsync;\n // inline: development only\n this.flowWeaverDebugger = runtime.services.debugger;\n // inline: end\n this.runtime = runtime;\n this.branchStack = [...runtime.branches];\n }\n\n registerPullExecutor(id: string, executor: () => void | Promise<void>): void {\n this.pullExecutors.set(id, executor);\n }\n\n /* Record one execution of a node; each node counts its own executions from 0. */\n addExecution(id: string, parentIndex?: number, scopeName?: string): number {\n const index = this.nodeExecutionCounts.get(id) ?? 0;\n this.nodeExecutionCounts.set(id, index + 1);\n this.executionCounter++;\n this.executions.set(this.getExecutionKey(id, index), {\n id,\n index,\n parentIndex,\n scopeName,\n });\n this.nodeExecutionIndices.set(id, index);\n return index;\n }\n\n setVariable(address: VariableAddress, value: VariableValue): void | Promise<void> {\n const key = this.getVariableKey(address);\n this.variables.set(key, value);\n if (typeof value !== 'function' && address.durable !== false) {\n this.runtime.durable.setVariable(this.executionAddress(address), address.portName, value);\n }\n // inline: development only\n if (this.flowWeaverDebugger) {\n const actualValue = typeof value === 'function' ? value() : value;\n this.sendVariableSetEvent({\n identifier: {\n nodeTypeName: address.nodeTypeName || 'unknown',\n id: address.id,\n portName: address.portName,\n executionIndex: address.executionIndex,\n key: 'default',\n ...(address.scope && { scope: address.scope }),\n ...(address.side && { side: address.side }),\n },\n value: actualValue,\n });\n }\n // inline: end\n return this.isAsync ? Promise.resolve() : undefined;\n }\n\n getVariable(address: VariableAddress): unknown | Promise<unknown> {\n const executor = this.pullExecutors.get(address.id);\n if (executor) {\n if (!this.hasVariable(address)) {\n const result = executor();\n // Handle async executor (returns Promise)\n if (result instanceof Promise) {\n return result.then(() => {\n const trackedIndex = this.nodeExecutionIndices.get(address.id);\n const finalAddress = trackedIndex !== undefined ? { ...address, executionIndex: trackedIndex } : address;\n return this.retrieveVariable(finalAddress);\n });\n }\n // Handle sync executor (returns void)\n const trackedIndex = this.nodeExecutionIndices.get(address.id);\n const finalAddress = trackedIndex !== undefined ? { ...address, executionIndex: trackedIndex } : address;\n return this.retrieveVariable(finalAddress);\n }\n }\n return this.retrieveVariable(address);\n }\n\n private retrieveVariable(address: VariableAddress): unknown | Promise<unknown> {\n const key = this.getVariableKey(address);\n let value = this.variables.get(key);\n if (!this.variables.has(key)) {\n value = this.runtime.durable.getVariable(\n this.executionAddress(address),\n address.portName,\n this.allowAncestorDurableVariables,\n );\n if (value === undefined) {\n throw new Error(`Variable not found: ${address.id}.${address.portName}[${address.executionIndex}]`);\n }\n this.variables.set(key, value);\n }\n if (typeof value === 'function') {\n const result = value();\n if (result instanceof Promise) {\n return result;\n }\n return this.isAsync ? Promise.resolve(result) : result;\n }\n return this.isAsync ? Promise.resolve(value) : value;\n }\n\n hasVariable(address: VariableAddress): boolean {\n const key = this.getVariableKey(address);\n return (\n this.variables.has(key) ||\n this.runtime.durable.getVariable(\n this.executionAddress(address),\n address.portName,\n this.allowAncestorDurableVariables,\n ) !== undefined\n );\n }\n\n executionAddress(address: Pick<VariableAddress, 'id' | 'executionIndex' | 'nodeTypeName'>): ExecutionAddress {\n const runtime = this.getRuntime();\n return runtime.durable.address(runtime, address.id, address.nodeTypeName ?? address.id, address.executionIndex);\n }\n\n shouldExecute(nodeId: string, nodeType: string, executionIndex: number): boolean {\n const runtime = this.getRuntime();\n return runtime.durable.shouldExecute(runtime.durable.address(runtime, nodeId, nodeType, executionIndex));\n }\n\n commitNode(nodeId: string, nodeType: string, executionIndex: number): void {\n const runtime = this.getRuntime();\n runtime.durable.commitNode(runtime.durable.address(runtime, nodeId, nodeType, executionIndex));\n }\n\n /* The first thing a gated body does: name its workflow and graph to the engine. */\n bindWorkflow(workflowId: string, graphFingerprint: string): void {\n this.runtime.durable.bind(this.runtime, workflowId, graphFingerprint);\n }\n\n resolveGate(\n kind: DurableGateKind,\n nodeId: string,\n nodeType: string,\n executionIndex: number,\n payload: WireValue,\n ): WireValue {\n const runtime = this.getRuntime();\n return runtime.durable.resolveGate(runtime, { kind, nodeId, nodeType, executionIndex, payload });\n }\n\n executeEffect<T extends WireValue>(\n nodeId: string,\n nodeType: string,\n executionIndex: number,\n execute: (operationKey: string) => Promise<{ result: T; receipt: WireValue }>,\n ): Promise<T> {\n const runtime = this.getRuntime();\n return runtime.durable.executeEffect(runtime, { nodeId, nodeType, executionIndex }, execute);\n }\n\n createNestedRuntime(workflowId: string, callerNodeId: string, callerExecutionIndex: number): WorkflowRuntime {\n const invocation = this.nestedInvocationCounts.get(callerNodeId) ?? 0;\n this.nestedInvocationCounts.set(callerNodeId, invocation + 1);\n const parentRuntime = this.getRuntime();\n return {\n ...parentRuntime,\n frames: [...parentRuntime.frames, { workflowId, invocation, callerNodeId, callerExecutionIndex }],\n scopes: parentRuntime.scopes,\n };\n }\n\n enterBranch(nodeId: string, executionIndex: number, arm: string): void {\n const frameDepth = this.runtime.frames.length - 1;\n const workflowId = this.runtime.frames[frameDepth].workflowId;\n this.branchStack.push({ workflowId, frameDepth, nodeId, executionIndex, arm });\n }\n\n exitBranch(): void {\n this.branchStack.pop();\n }\n\n getRuntime(): WorkflowRuntime {\n return { ...this.runtime, branches: [...this.branchStack] };\n }\n\n /*\n * Fork the mutable bookkeeping for one lane of a parallel group. Durable\n * state stays execution-scoped and shared through `runtime`, while the\n * branch and scope address stacks are copied so concurrent lanes cannot\n * corrupt one another's continuation addresses.\n */\n forkParallel(): GeneratedExecutionContext {\n const parallelContext = new GeneratedExecutionContext(this.isAsync, this.getRuntime());\n parallelContext.variables = new Map(this.variables);\n parallelContext.executions = new Map(this.executions);\n parallelContext.executionCounter = this.executionCounter;\n parallelContext.pullExecutors = new Map(this.pullExecutors);\n parallelContext.nodeExecutionIndices = new Map(this.nodeExecutionIndices);\n parallelContext.nodeExecutionCounts = new Map(this.nodeExecutionCounts);\n parallelContext.scopeInvocationCounts = new Map(this.scopeInvocationCounts);\n parallelContext.nestedInvocationCounts = new Map(this.nestedInvocationCounts);\n parallelContext.allowAncestorDurableVariables = this.allowAncestorDurableVariables;\n return parallelContext;\n }\n\n mergeParallel(parallelContext: GeneratedExecutionContext): void {\n this.mergeScope(parallelContext);\n parallelContext.nodeExecutionIndices.forEach((index, id) => {\n this.nodeExecutionIndices.set(id, index);\n });\n parallelContext.scopeInvocationCounts.forEach((count, key) => {\n this.scopeInvocationCounts.set(key, Math.max(this.scopeInvocationCounts.get(key) ?? 0, count));\n });\n parallelContext.nestedInvocationCounts.forEach((count, key) => {\n this.nestedInvocationCounts.set(key, Math.max(this.nestedInvocationCounts.get(key) ?? 0, count));\n });\n }\n\n getExecution(id: string, index: number): ExecutionInfo | undefined {\n return this.executions.get(this.getExecutionKey(id, index));\n }\n\n /*\n * Create the execution context for one invocation of a scope (a loop body,\n * a per-port function). Each call is the next invocation of that scope key.\n * A clean scope (a per-port scope) starts with no variables and cannot read\n * its ancestors' durable variables; any other (a node-level scope) inherits\n * the parent's variables. `isAsyncOverride` is the scope's own async mode,\n * when it differs from the parent's.\n */\n createScope(\n _parentNodeName: string,\n _parentIndex: number,\n _scopeName: string,\n cleanScope: boolean = false,\n isAsyncOverride?: boolean,\n ): GeneratedExecutionContext {\n const effectiveIsAsync = isAsyncOverride !== undefined ? isAsyncOverride : this.isAsync;\n const scopeKey = `${_parentNodeName}:${_parentIndex}:${_scopeName}`;\n const scopeInvocation = this.scopeInvocationCounts.get(scopeKey) ?? 0;\n this.scopeInvocationCounts.set(scopeKey, scopeInvocation + 1);\n const parentRuntime = this.getRuntime();\n const scopedRuntime: WorkflowRuntime = {\n ...parentRuntime,\n scopes: [\n ...parentRuntime.scopes,\n {\n parentNodeId: _parentNodeName,\n parentExecutionIndex: _parentIndex,\n scopeName: _scopeName,\n invocation: scopeInvocation,\n loopIteration: scopeInvocation,\n },\n ],\n };\n const scopedContext = new GeneratedExecutionContext(effectiveIsAsync, scopedRuntime);\n scopedContext.variables = cleanScope ? new Map() : new Map(this.variables);\n scopedContext.allowAncestorDurableVariables = this.allowAncestorDurableVariables && !cleanScope;\n scopedContext.executions = new Map(this.executions);\n scopedContext.executionCounter = this.executionCounter;\n scopedContext.nodeExecutionCounts = new Map(this.nodeExecutionCounts);\n return scopedContext;\n }\n\n /* Merge a scope's variables, executions and counters back into this context. */\n mergeScope(scopedContext: GeneratedExecutionContext): void {\n scopedContext.executions.forEach((info, key) => {\n this.executions.set(key, info);\n });\n scopedContext.variables.forEach((value, key) => {\n this.variables.set(key, value);\n });\n this.executionCounter = Math.max(this.executionCounter, scopedContext.executionCounter);\n scopedContext.nodeExecutionCounts.forEach((count, id) => {\n this.nodeExecutionCounts.set(id, Math.max(this.nodeExecutionCounts.get(id) ?? 0, count));\n });\n }\n\n private getVariableKey(address: VariableAddress): string {\n return `${address.id}:${address.portName}:${address.executionIndex}`;\n }\n\n private getExecutionKey(id: string, index: number): string {\n return `${id}:${index}`;\n }\n\n getExecutionCount(): number {\n return this.executionCounter;\n }\n\n reset(): void {\n this.variables.clear();\n this.executions.clear();\n this.executionCounter = 0;\n this.nodeExecutionCounts.clear();\n }\n\n isAborted(): boolean {\n return this.runtime.abortSignal?.aborted ?? false;\n }\n\n /* Return the parent-owned signal without transferring ownership. */\n getAbortSignal(): AbortSignal | undefined {\n return this.runtime.abortSignal;\n }\n\n /* Throw a CancellationError, naming `nodeId` when given, if the run was aborted. */\n checkAborted(nodeId?: string): void {\n if (this.runtime.abortSignal?.aborted) {\n throw new CancellationError(\n `Workflow execution cancelled${nodeId ? ` at ${nodeId}` : ''}`,\n this.executionCounter,\n nodeId,\n );\n }\n }\n\n // inline: development only, a no-op stub in production\n /* Awaited by generated code, so a debugger can hold a node at a breakpoint. */\n async sendStatusChangedEvent(args: {\n nodeTypeName: string;\n id: string;\n scope?: string;\n side?: 'start' | 'exit';\n executionIndex: number;\n status: TStatusType;\n }): Promise<void> {\n if (this.flowWeaverDebugger) {\n await this.flowWeaverDebugger.sendEvent({\n type: 'STATUS_CHANGED',\n ...args,\n innerFlowInvocation: this.flowWeaverDebugger.innerFlowInvocation,\n });\n }\n }\n\n async sendLogErrorEvent(args: {\n nodeTypeName: string;\n id: string;\n scope?: string;\n side?: 'start' | 'exit';\n executionIndex: number;\n error: string;\n code?: string;\n }): Promise<void> {\n if (this.flowWeaverDebugger) {\n await this.flowWeaverDebugger.sendEvent({\n type: 'LOG_ERROR',\n ...args,\n innerFlowInvocation: this.flowWeaverDebugger.innerFlowInvocation,\n });\n }\n }\n\n async sendWorkflowCompletedEvent(args: {\n executionIndex: number;\n status: 'SUCCEEDED' | 'FAILED' | 'CANCELLED';\n result?: unknown;\n }): Promise<void> {\n if (this.flowWeaverDebugger) {\n await this.flowWeaverDebugger.sendEvent({\n type: 'WORKFLOW_COMPLETED',\n ...args,\n innerFlowInvocation: this.flowWeaverDebugger.innerFlowInvocation,\n });\n }\n }\n // inline: end\n // inline: development only\n\n private async sendVariableSetEvent(args: { identifier: TVariableIdentification; value: unknown }): Promise<void> {\n if (this.flowWeaverDebugger) {\n await this.flowWeaverDebugger.sendEvent({\n type: 'VARIABLE_SET',\n ...args,\n innerFlowInvocation: this.flowWeaverDebugger.innerFlowInvocation,\n });\n }\n }\n\n /* Return live debugger-visible variables without invoking lazy values. */\n inspectVariables(): Record<string, unknown> {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of this.variables) {\n vars[key] = typeof value === 'function' ? '[lazy value]' : value;\n }\n return vars;\n }\n // inline: end\n}\n";
|
|
2383
|
+
|
|
2384
|
+
// src/api/inline-runtime.ts
|
|
2385
|
+
var INLINE_ENGINE_EXPORTS = [
|
|
2386
|
+
"createWorkflowRuntime",
|
|
2387
|
+
"acceptContinuation",
|
|
2388
|
+
"createContinuationEnvelope",
|
|
2389
|
+
"isDurableGateYield",
|
|
2390
|
+
"isAmbiguousEffectError",
|
|
2391
|
+
"DurableGateYield",
|
|
2392
|
+
"AmbiguousEffectError",
|
|
2393
|
+
"CancellationError",
|
|
2394
|
+
"ENGINE_VERSION"
|
|
2395
|
+
];
|
|
2396
|
+
var INLINE_ENGINE_TYPE_EXPORTS = [
|
|
2397
|
+
"WorkflowRuntime",
|
|
2398
|
+
"WorkflowRuntimeServices",
|
|
2399
|
+
"CreateWorkflowRuntimeOptions",
|
|
2400
|
+
"ContinuationEnvelope",
|
|
2401
|
+
"DecodedContinuation",
|
|
2402
|
+
"DurableGate",
|
|
2403
|
+
"GateResolution",
|
|
2404
|
+
"EffectAdapter",
|
|
2405
|
+
"WireValue"
|
|
2406
|
+
];
|
|
2407
|
+
function generateInlineEngine(production) {
|
|
2408
|
+
const lines = [];
|
|
2409
|
+
lines.push("// ============================================================================");
|
|
2410
|
+
lines.push("// Durable Engine");
|
|
2411
|
+
lines.push("// The package's own engine, copied here so this file runs without it.");
|
|
2412
|
+
lines.push("// ============================================================================");
|
|
2413
|
+
lines.push("");
|
|
2414
|
+
lines.push(`const VERSION = ${JSON.stringify(VERSION)};`);
|
|
2415
|
+
if (!production) {
|
|
2416
|
+
lines.push("type DebugController = TDebugController;");
|
|
2417
|
+
}
|
|
2418
|
+
lines.push(
|
|
2419
|
+
"type FwMockConfig = { readonly events?: Readonly<Record<string, object>>, readonly invocations?: Readonly<Record<string, object>>, readonly agents?: Readonly<Record<string, object>>, readonly gates?: Readonly<Record<string, object>>, readonly fast?: boolean };"
|
|
2420
|
+
);
|
|
2421
|
+
lines.push("");
|
|
2422
|
+
const source = production ? INLINE_ENGINE_SOURCE.replace(/\bTDebugger\b/g, "unknown").replace(/\bDebugController\b/g, "unknown") : INLINE_ENGINE_SOURCE;
|
|
2423
|
+
lines.push(source);
|
|
2424
|
+
return lines.join("\n");
|
|
2425
|
+
}
|
|
2426
|
+
var DEVELOPMENT_REGION = /^([ \t]*)\/\/ inline: development only(, a no-op stub in production)?\n([\s\S]*?)^[ \t]*\/\/ inline: end\n/gm;
|
|
2427
|
+
var CLASS_METHOD = /^ {2}(?:async\s+)?([A-Za-z_$][\w$]*)\(/gm;
|
|
2428
|
+
function inlineExecutionContext(production, exportClasses) {
|
|
2429
|
+
let source = INLINE_EXECUTION_CONTEXT_SOURCE.replace(
|
|
2430
|
+
DEVELOPMENT_REGION,
|
|
2431
|
+
(_region, indent, stubbed, body) => {
|
|
2432
|
+
if (!production) return body;
|
|
2433
|
+
if (stubbed === void 0) return "";
|
|
2434
|
+
return Array.from(
|
|
2435
|
+
body.matchAll(CLASS_METHOD),
|
|
2436
|
+
([, name]) => [`${indent}${name}(_args: unknown): void {`, `${indent} // No-op in production mode`, `${indent}}`, ""].join("\n")
|
|
2437
|
+
).join("\n");
|
|
2438
|
+
}
|
|
2439
|
+
);
|
|
2440
|
+
if (/\/\/ inline:/.test(source)) {
|
|
2441
|
+
throw new Error("src/runtime/ExecutionContext.ts has an unbalanced `// inline:` marker");
|
|
2442
|
+
}
|
|
2443
|
+
if (exportClasses) {
|
|
2444
|
+
source = source.replace(/^class GeneratedExecutionContext\b/m, "export class GeneratedExecutionContext");
|
|
2445
|
+
}
|
|
2446
|
+
return source.trimEnd();
|
|
2447
|
+
}
|
|
2448
|
+
function stripTypeScript(code) {
|
|
2449
|
+
const { transformSync } = __require("esbuild");
|
|
2450
|
+
const result = transformSync(code, {
|
|
2451
|
+
loader: "ts",
|
|
2452
|
+
target: "es2020",
|
|
2453
|
+
// Keep the code readable (no minification)
|
|
2454
|
+
minify: false,
|
|
2455
|
+
// Preserve formatting as much as possible
|
|
2456
|
+
keepNames: true
|
|
2457
|
+
});
|
|
2458
|
+
return result.code;
|
|
2459
|
+
}
|
|
2460
|
+
function generateInlineRuntime(production, exportClasses = false, outputFormat = "typescript", moduleFormat = "esm") {
|
|
2461
|
+
const exportKeyword = exportClasses ? "export " : "";
|
|
2462
|
+
const lines = [];
|
|
2463
|
+
lines.push("// ============================================================================");
|
|
2464
|
+
lines.push("// Runtime Types");
|
|
2465
|
+
lines.push("// ============================================================================");
|
|
2466
|
+
lines.push("");
|
|
2467
|
+
lines.push("type TStatusType =");
|
|
2468
|
+
lines.push(' | "RUNNING"');
|
|
2469
|
+
lines.push(' | "SCHEDULED"');
|
|
2470
|
+
lines.push(' | "SUCCEEDED"');
|
|
2471
|
+
lines.push(' | "FAILED"');
|
|
2472
|
+
lines.push(' | "CANCELLED"');
|
|
2473
|
+
lines.push(' | "PENDING";');
|
|
2474
|
+
lines.push("");
|
|
2475
|
+
lines.push("type TVariableIdentification = {");
|
|
2476
|
+
lines.push(" nodeTypeName: string;");
|
|
2477
|
+
lines.push(" id: string;");
|
|
2478
|
+
lines.push(" scope?: string | undefined;");
|
|
2479
|
+
lines.push(' side?: "start" | "exit" | undefined;');
|
|
2480
|
+
lines.push(" portName: string;");
|
|
2481
|
+
lines.push(" executionIndex: number;");
|
|
2482
|
+
lines.push(" key?: string | undefined;");
|
|
2483
|
+
lines.push("};");
|
|
2484
|
+
lines.push("");
|
|
2485
|
+
if (!production) {
|
|
2486
|
+
lines.push("type TStatusChangedEvent = {");
|
|
2487
|
+
lines.push(' type: "STATUS_CHANGED";');
|
|
2488
|
+
lines.push(" nodeTypeName: string;");
|
|
2489
|
+
lines.push(" id: string;");
|
|
2490
|
+
lines.push(" scope?: string;");
|
|
2491
|
+
lines.push(' side?: "start" | "exit";');
|
|
2492
|
+
lines.push(" executionIndex: number;");
|
|
2493
|
+
lines.push(" status: TStatusType;");
|
|
2494
|
+
lines.push(" innerFlowInvocation?: boolean;");
|
|
2495
|
+
lines.push("};");
|
|
2496
|
+
lines.push("");
|
|
2497
|
+
lines.push("type TVariableSetEvent = {");
|
|
2498
|
+
lines.push(' type: "VARIABLE_SET";');
|
|
2499
|
+
lines.push(" identifier: TVariableIdentification;");
|
|
2500
|
+
lines.push(" value?: unknown;");
|
|
2501
|
+
lines.push(" innerFlowInvocation?: boolean;");
|
|
2502
|
+
lines.push("};");
|
|
2503
|
+
lines.push("");
|
|
2504
|
+
lines.push("type TErrorLogEvent = {");
|
|
2505
|
+
lines.push(' type: "LOG_ERROR";');
|
|
2506
|
+
lines.push(" nodeTypeName: string;");
|
|
2507
|
+
lines.push(" id: string;");
|
|
2508
|
+
lines.push(" scope?: string;");
|
|
2509
|
+
lines.push(' side?: "start" | "exit";');
|
|
2510
|
+
lines.push(" executionIndex: number;");
|
|
2511
|
+
lines.push(" error: string;");
|
|
2512
|
+
lines.push(" code?: string;");
|
|
2513
|
+
lines.push(" innerFlowInvocation?: boolean;");
|
|
2514
|
+
lines.push("};");
|
|
2515
|
+
lines.push("");
|
|
2516
|
+
lines.push("type TWorkflowCompletedEvent = {");
|
|
2517
|
+
lines.push(' type: "WORKFLOW_COMPLETED";');
|
|
2518
|
+
lines.push(" executionIndex: number;");
|
|
2519
|
+
lines.push(' status: "SUCCEEDED" | "FAILED" | "CANCELLED";');
|
|
2520
|
+
lines.push(" result?: unknown;");
|
|
2521
|
+
lines.push(" innerFlowInvocation?: boolean;");
|
|
2522
|
+
lines.push("};");
|
|
2523
|
+
lines.push("");
|
|
2524
|
+
lines.push(`${exportKeyword}type TEvent =`);
|
|
2525
|
+
lines.push(" | TStatusChangedEvent");
|
|
2526
|
+
lines.push(" | TVariableSetEvent");
|
|
2527
|
+
lines.push(" | TErrorLogEvent");
|
|
2528
|
+
lines.push(" | TWorkflowCompletedEvent;");
|
|
2529
|
+
lines.push("");
|
|
2530
|
+
lines.push(`${exportKeyword}type TDebugger = {`);
|
|
2531
|
+
lines.push(" sendEvent: (event: TEvent) => void | Promise<void>;");
|
|
2532
|
+
lines.push(" innerFlowInvocation: boolean;");
|
|
2533
|
+
lines.push(" sessionId?: string;");
|
|
2534
|
+
lines.push("};");
|
|
2535
|
+
lines.push("");
|
|
2536
|
+
lines.push("type TDebugController = {");
|
|
2537
|
+
lines.push(" beforeNode(nodeId: string, ctx: unknown): Promise<void> | void;");
|
|
2538
|
+
lines.push(" afterNode(nodeId: string, ctx: unknown): Promise<void> | void;");
|
|
2539
|
+
lines.push("};");
|
|
2540
|
+
lines.push("");
|
|
2541
|
+
}
|
|
2542
|
+
lines.push("// ============================================================================");
|
|
2543
|
+
lines.push("// Cancellation Error");
|
|
2544
|
+
lines.push("// ============================================================================");
|
|
2545
|
+
lines.push("");
|
|
2546
|
+
lines.push(`${exportKeyword}class CancellationError extends Error {`);
|
|
2547
|
+
lines.push(" public readonly executionIndex: number;");
|
|
2548
|
+
lines.push(" public readonly nodeId?: string;");
|
|
2549
|
+
lines.push(" public readonly timestamp: number;");
|
|
2550
|
+
lines.push("");
|
|
2551
|
+
lines.push(" constructor(");
|
|
2552
|
+
lines.push(" message: string = 'Workflow execution cancelled',");
|
|
2553
|
+
lines.push(" executionIndex: number = 0,");
|
|
2554
|
+
lines.push(" nodeId?: string,");
|
|
2555
|
+
lines.push(" timestamp: number = Date.now()");
|
|
2556
|
+
lines.push(" ) {");
|
|
2557
|
+
lines.push(" super(message);");
|
|
2558
|
+
lines.push(" this.name = 'CancellationError';");
|
|
2559
|
+
lines.push(" this.executionIndex = executionIndex;");
|
|
2560
|
+
lines.push(" this.nodeId = nodeId;");
|
|
2561
|
+
lines.push(" this.timestamp = timestamp;");
|
|
2562
|
+
lines.push(" }");
|
|
2563
|
+
lines.push("");
|
|
2564
|
+
lines.push(" static isCancellationError(error: unknown): error is CancellationError {");
|
|
2565
|
+
lines.push(" return (");
|
|
2566
|
+
lines.push(" error instanceof CancellationError ||");
|
|
2567
|
+
lines.push(" (error instanceof Error && error.name === 'CancellationError')");
|
|
2568
|
+
lines.push(" );");
|
|
2569
|
+
lines.push(" }");
|
|
2570
|
+
lines.push("}");
|
|
2571
|
+
lines.push("");
|
|
2572
|
+
lines.push("// ============================================================================");
|
|
2573
|
+
lines.push("// Execution Context");
|
|
2574
|
+
lines.push("// ============================================================================");
|
|
2575
|
+
lines.push("");
|
|
2576
|
+
lines.push(inlineExecutionContext(production, exportClasses));
|
|
2577
|
+
lines.push("");
|
|
2578
|
+
lines.push(generateInlineEngine(production));
|
|
2579
|
+
lines.push("");
|
|
2580
|
+
if (moduleFormat === "esm") {
|
|
2581
|
+
const values = INLINE_ENGINE_EXPORTS.filter((name) => !exportClasses || name !== "CancellationError");
|
|
2582
|
+
lines.push(`export { ${values.join(", ")} };`);
|
|
2583
|
+
lines.push(`export type { ${INLINE_ENGINE_TYPE_EXPORTS.join(", ")} };`);
|
|
2584
|
+
lines.push("");
|
|
2585
|
+
}
|
|
2586
|
+
const output = lines.join("\n");
|
|
2587
|
+
if (outputFormat === "javascript") {
|
|
2588
|
+
return stripTypeScript(output);
|
|
2589
|
+
}
|
|
2590
|
+
return output;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// src/api/in-place/runtime-section.ts
|
|
2594
|
+
function replaceOrInsertRuntimeSection(source, production) {
|
|
2595
|
+
return replaceOrInsertSection(
|
|
2596
|
+
source,
|
|
2597
|
+
MARKERS.RUNTIME_START,
|
|
2598
|
+
MARKERS.RUNTIME_END,
|
|
2599
|
+
generateRuntimeSection(production)
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
function generateRuntimeSection(production) {
|
|
2603
|
+
const lines = [];
|
|
2604
|
+
lines.push("// ============================================================================");
|
|
2605
|
+
lines.push("// DO NOT EDIT - This section is auto-generated by Flow Weaver");
|
|
2606
|
+
lines.push("// ============================================================================");
|
|
2607
|
+
lines.push("");
|
|
2608
|
+
lines.push(generateInlineRuntime(production));
|
|
2609
|
+
return lines.join("\n");
|
|
2610
|
+
}
|
|
2611
|
+
function replaceOrInsertSection(source, startMarker, endMarker, newContent) {
|
|
2612
|
+
const startIdx = source.indexOf(startMarker);
|
|
2613
|
+
const endIdx = source.indexOf(endMarker);
|
|
2614
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
2615
|
+
const before2 = source.slice(0, startIdx + startMarker.length);
|
|
2616
|
+
const after2 = source.slice(endIdx);
|
|
2617
|
+
const newCode = before2 + "\n" + newContent + "\n" + after2;
|
|
2618
|
+
const originalContent = source.slice(startIdx + startMarker.length, endIdx);
|
|
2619
|
+
const changed = originalContent.trim() !== newContent.trim();
|
|
2620
|
+
return { code: newCode, changed };
|
|
2621
|
+
}
|
|
2622
|
+
const insertIdx = findInsertPositionAfterImports(source);
|
|
2623
|
+
const before = source.slice(0, insertIdx);
|
|
2624
|
+
const after = source.slice(insertIdx);
|
|
2625
|
+
const newSection = ["", startMarker, newContent, endMarker, ""].join("\n");
|
|
2626
|
+
return {
|
|
2627
|
+
code: before + newSection + after,
|
|
2628
|
+
changed: true
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
function findInsertPositionAfterImports(source) {
|
|
2632
|
+
const sourceFile = parseSource(source);
|
|
2633
|
+
let lastImportEnd = 0;
|
|
2634
|
+
ts4.forEachChild(sourceFile, (node) => {
|
|
2635
|
+
if (ts4.isImportDeclaration(node)) {
|
|
2636
|
+
lastImportEnd = node.end;
|
|
2637
|
+
}
|
|
2638
|
+
});
|
|
2639
|
+
if (lastImportEnd > 0) {
|
|
2640
|
+
const nextNewline = source.indexOf("\n", lastImportEnd);
|
|
2641
|
+
return nextNewline !== -1 ? nextNewline + 1 : lastImportEnd;
|
|
2642
|
+
}
|
|
2643
|
+
return 0;
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
// src/api/in-place/signature.ts
|
|
2647
|
+
import * as ts5 from "typescript";
|
|
2648
|
+
function detectFunctionIsAsync(source, functionName) {
|
|
2649
|
+
const functionNode = findFunctionDeclaration(parseSource(source), functionName);
|
|
2650
|
+
return !!functionNode?.modifiers?.some((m) => m.kind === ts5.SyntaxKind.AsyncKeyword);
|
|
2651
|
+
}
|
|
2652
|
+
function ensureParamsParameter(source, functionName) {
|
|
2653
|
+
const functionNode = findFunctionDeclaration(parseSource(source), functionName);
|
|
2654
|
+
if (!functionNode) {
|
|
2655
|
+
return { code: source, changed: false };
|
|
2656
|
+
}
|
|
2657
|
+
const hasParams = functionNode.parameters.some(
|
|
2658
|
+
(param) => ts5.isIdentifier(param.name) && param.name.text === "params"
|
|
2659
|
+
);
|
|
2660
|
+
if (hasParams) {
|
|
2661
|
+
return { code: source, changed: false };
|
|
2662
|
+
}
|
|
2663
|
+
const PARAMS_DECL = "params: Record<string, unknown> = {}";
|
|
2664
|
+
const firstParam = functionNode.parameters[0];
|
|
2665
|
+
if (!firstParam) {
|
|
2666
|
+
const openParen = source.indexOf("(", functionNode.name?.end || 0);
|
|
2667
|
+
if (openParen === -1) {
|
|
2668
|
+
return { code: source, changed: false };
|
|
2669
|
+
}
|
|
2670
|
+
const before2 = source.slice(0, openParen + 1);
|
|
2671
|
+
const after2 = source.slice(openParen + 1);
|
|
2672
|
+
return { code: before2 + PARAMS_DECL + after2, changed: true };
|
|
2673
|
+
}
|
|
2674
|
+
const firstParamEnd = firstParam.end;
|
|
2675
|
+
const before = source.slice(0, firstParamEnd);
|
|
2676
|
+
const after = source.slice(firstParamEnd);
|
|
2677
|
+
return { code: before + ", " + PARAMS_DECL + after, changed: true };
|
|
2678
|
+
}
|
|
2679
|
+
function ensureRuntimeParameter(source, functionName) {
|
|
2680
|
+
const sourceFile = parseSource(source);
|
|
2681
|
+
const functionNode = findFunctionDeclaration(sourceFile, functionName);
|
|
2682
|
+
if (!functionNode) {
|
|
2683
|
+
return { code: source, changed: false };
|
|
2684
|
+
}
|
|
2685
|
+
const parameters = functionNode.parameters.slice(0, 2).map((parameter) => parameter.getText(sourceFile));
|
|
2686
|
+
parameters.push("__runtime__: WorkflowRuntime");
|
|
2687
|
+
const openParen = source.indexOf("(", functionNode.name?.end ?? 0);
|
|
2688
|
+
if (openParen === -1) return { code: source, changed: false };
|
|
2689
|
+
const closeParen = functionNode.parameters.end;
|
|
2690
|
+
const replacement = parameters.join(", ");
|
|
2691
|
+
const current = source.slice(openParen + 1, closeParen);
|
|
2692
|
+
if (current.trim() === replacement) return { code: source, changed: false };
|
|
2693
|
+
return {
|
|
2694
|
+
code: source.slice(0, openParen + 1) + replacement + source.slice(closeParen),
|
|
2695
|
+
changed: true
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
function ensureAsyncKeyword(source, functionName, shouldBeAsync) {
|
|
2699
|
+
if (!shouldBeAsync) {
|
|
2700
|
+
return { code: source, changed: false };
|
|
2701
|
+
}
|
|
2702
|
+
const functionNode = findFunctionDeclaration(parseSource(source), functionName);
|
|
2703
|
+
if (!functionNode) {
|
|
2704
|
+
return { code: source, changed: false };
|
|
2705
|
+
}
|
|
2706
|
+
const alreadyAsync = !!functionNode.modifiers?.some((m) => m.kind === ts5.SyntaxKind.AsyncKeyword);
|
|
2707
|
+
if (alreadyAsync) {
|
|
2708
|
+
return { code: source, changed: false };
|
|
2709
|
+
}
|
|
2710
|
+
const funcStart = functionNode.getStart();
|
|
2711
|
+
const textAfter = source.slice(funcStart);
|
|
2712
|
+
const functionKeywordOffset = textAfter.indexOf("function");
|
|
2713
|
+
if (functionKeywordOffset === -1) {
|
|
2714
|
+
return { code: source, changed: false };
|
|
2715
|
+
}
|
|
2716
|
+
const insertPos = funcStart + functionKeywordOffset;
|
|
2717
|
+
const before = source.slice(0, insertPos);
|
|
2718
|
+
const after = source.slice(insertPos);
|
|
2719
|
+
return {
|
|
2720
|
+
code: before + "async " + after,
|
|
2721
|
+
changed: true
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function ensurePromiseReturnType(source, functionName, shouldBeAsync) {
|
|
2725
|
+
if (!shouldBeAsync) {
|
|
2726
|
+
return { code: source, changed: false };
|
|
2727
|
+
}
|
|
2728
|
+
const functionNode = findFunctionDeclaration(parseSource(source), functionName);
|
|
2729
|
+
if (!functionNode || !functionNode.type) {
|
|
2730
|
+
return { code: source, changed: false };
|
|
2731
|
+
}
|
|
2732
|
+
const returnTypeText = source.slice(functionNode.type.pos, functionNode.type.end).trim();
|
|
2733
|
+
if (returnTypeText.startsWith("Promise<")) {
|
|
2734
|
+
return { code: source, changed: false };
|
|
2735
|
+
}
|
|
2736
|
+
const before = source.slice(0, functionNode.type.pos);
|
|
2737
|
+
const after = source.slice(functionNode.type.end);
|
|
2738
|
+
return {
|
|
2739
|
+
code: before + " Promise<" + returnTypeText + ">" + after,
|
|
2740
|
+
changed: true
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/api/in-place/workflow-jsdoc.ts
|
|
2745
|
+
import * as ts6 from "typescript";
|
|
2746
|
+
|
|
2747
|
+
// src/api/in-place/macro-coverage.ts
|
|
2748
|
+
function isConnectionCoveredByMacro(conn, macros) {
|
|
2749
|
+
for (const macro of macros) {
|
|
2750
|
+
if (macro.type === "map") {
|
|
2751
|
+
const [sourceNode, sourcePort] = macro.sourcePort.split(".");
|
|
2752
|
+
if ((conn.from.node === macro.instanceId || conn.from.node === macro.childId) && (conn.to.node === macro.instanceId || conn.to.node === macro.childId) && (conn.from.scope === "iterate" || conn.to.scope === "iterate")) {
|
|
2753
|
+
return true;
|
|
2754
|
+
}
|
|
2755
|
+
if (conn.from.node === sourceNode && conn.from.port === sourcePort && !conn.from.scope && conn.to.node === macro.instanceId && conn.to.port === "items" && !conn.to.scope) {
|
|
2756
|
+
return true;
|
|
2757
|
+
}
|
|
2758
|
+
} else if (macro.type === "path") {
|
|
2759
|
+
if (conn.from.scope || conn.to.scope) continue;
|
|
2760
|
+
const steps = macro.steps;
|
|
2761
|
+
const fromIdx = steps.findIndex((s) => s.node === conn.from.node);
|
|
2762
|
+
const toIdx = steps.findIndex((s) => s.node === conn.to.node);
|
|
2763
|
+
if (fromIdx === -1 || toIdx === -1 || fromIdx >= toIdx) continue;
|
|
2764
|
+
if (toIdx === fromIdx + 1) {
|
|
2765
|
+
const route = steps[fromIdx].route || "ok";
|
|
2766
|
+
if (conn.from.node === "Start" && conn.from.port === "execute" && conn.to.port === "execute") return true;
|
|
2767
|
+
if (conn.to.node === "Exit") {
|
|
2768
|
+
if (route === "fail" && conn.from.port === "onFailure" && conn.to.port === "onFailure") return true;
|
|
2769
|
+
if (route === "ok" && conn.from.port === "onSuccess" && conn.to.port === "onSuccess") return true;
|
|
2770
|
+
}
|
|
2771
|
+
if (route === "fail" && conn.from.port === "onFailure" && conn.to.port === "execute") return true;
|
|
2772
|
+
if (route === "ok" && conn.from.port === "onSuccess" && conn.to.port === "execute") return true;
|
|
2773
|
+
}
|
|
2774
|
+
if (isPathImpliedDataEdge(conn.from.node, conn.from.port, conn.to.node, conn.to.port)) {
|
|
2775
|
+
return true;
|
|
2776
|
+
}
|
|
2777
|
+
} else if (macro.type === "fanOut") {
|
|
2778
|
+
if (conn.from.scope || conn.to.scope) continue;
|
|
2779
|
+
if (conn.from.node === macro.source.node && conn.from.port === macro.source.port) {
|
|
2780
|
+
for (const target of macro.targets) {
|
|
2781
|
+
const targetPort = target.port ?? macro.source.port;
|
|
2782
|
+
if (conn.to.node === target.node && conn.to.port === targetPort) {
|
|
2783
|
+
return true;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
} else if (macro.type === "fanIn") {
|
|
2788
|
+
if (conn.from.scope || conn.to.scope) continue;
|
|
2789
|
+
if (conn.to.node === macro.target.node && conn.to.port === macro.target.port) {
|
|
2790
|
+
for (const source of macro.sources) {
|
|
2791
|
+
const sourcePort = source.port ?? macro.target.port;
|
|
2792
|
+
if (conn.from.node === source.node && conn.from.port === sourcePort) {
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
return false;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
// src/api/in-place/workflow-jsdoc.ts
|
|
2803
|
+
function replaceWorkflowJSDoc(source, ast, options = {}) {
|
|
2804
|
+
const sourceFile = parseSource(source);
|
|
2805
|
+
let functionNode;
|
|
2806
|
+
ts6.forEachChild(sourceFile, (node) => {
|
|
2807
|
+
if (functionNode) return;
|
|
2808
|
+
if (ts6.isFunctionDeclaration(node) && node.name?.text === ast.functionName) {
|
|
2809
|
+
functionNode = node;
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2812
|
+
if (!functionNode) {
|
|
2813
|
+
return { code: source, changed: false };
|
|
2814
|
+
}
|
|
2815
|
+
const functionStart = functionNode.getFullStart();
|
|
2816
|
+
const leadingComments = ts6.getLeadingCommentRanges(source, functionStart);
|
|
2817
|
+
if (!leadingComments || leadingComments.length === 0) {
|
|
2818
|
+
return { code: source, changed: false };
|
|
2819
|
+
}
|
|
2820
|
+
const jsdocComment = leadingComments.find(
|
|
2821
|
+
(c) => c.kind === ts6.SyntaxKind.MultiLineCommentTrivia && source.slice(c.pos, c.pos + 3) === "/**"
|
|
2822
|
+
);
|
|
2823
|
+
if (!jsdocComment) {
|
|
2824
|
+
return { code: source, changed: false };
|
|
2825
|
+
}
|
|
2826
|
+
const newJSDoc = generateWorkflowJSDoc(ast, { skipParamReturns: options.skipParamReturns });
|
|
2827
|
+
const originalJSDoc = source.slice(jsdocComment.pos, jsdocComment.end);
|
|
2828
|
+
if (originalJSDoc.trim() === newJSDoc.trim()) {
|
|
2829
|
+
return { code: source, changed: false };
|
|
2830
|
+
}
|
|
2831
|
+
const before = source.slice(0, jsdocComment.pos);
|
|
2832
|
+
const after = source.slice(jsdocComment.end);
|
|
2833
|
+
return {
|
|
2834
|
+
code: before + newJSDoc + after,
|
|
2835
|
+
changed: true
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
function generateWorkflowJSDoc(ast, options = {}) {
|
|
2839
|
+
const lines = [];
|
|
2840
|
+
const macroInstanceIds = /* @__PURE__ */ new Set();
|
|
2841
|
+
const macroChildIds = /* @__PURE__ */ new Set();
|
|
2842
|
+
const macroScopeNames = /* @__PURE__ */ new Set();
|
|
2843
|
+
const allCoerceInstanceIds = /* @__PURE__ */ new Set();
|
|
2844
|
+
if (ast.macros && ast.macros.length > 0) {
|
|
2845
|
+
for (const macro of ast.macros) {
|
|
2846
|
+
if (macro.type === "map") {
|
|
2847
|
+
macroInstanceIds.add(macro.instanceId);
|
|
2848
|
+
macroChildIds.add(macro.childId);
|
|
2849
|
+
macroScopeNames.add(`${macro.instanceId}.iterate`);
|
|
2850
|
+
} else if (macro.type === "coerce") {
|
|
2851
|
+
allCoerceInstanceIds.add(macro.instanceId);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
lines.push("/**");
|
|
2856
|
+
if (ast.description) {
|
|
2857
|
+
lines.push(...formatJSDocDescription(ast.description));
|
|
2858
|
+
lines.push(` *`);
|
|
2859
|
+
}
|
|
2860
|
+
lines.push(" * @flowWeaver workflow");
|
|
2861
|
+
if (ast.options?.strictTypes) {
|
|
2862
|
+
lines.push(" * @strictTypes");
|
|
2863
|
+
}
|
|
2864
|
+
if (ast.options?.autoConnect) {
|
|
2865
|
+
lines.push(" * @autoConnect");
|
|
2866
|
+
}
|
|
2867
|
+
if (ast.options?.trigger) {
|
|
2868
|
+
const t = ast.options.trigger;
|
|
2869
|
+
const parts = [];
|
|
2870
|
+
if (t.event) parts.push(`event="${t.event}"`);
|
|
2871
|
+
if (t.cron) parts.push(`cron="${t.cron}"`);
|
|
2872
|
+
if (parts.length > 0) lines.push(` * @trigger ${parts.join(" ")}`);
|
|
2873
|
+
}
|
|
2874
|
+
for (const r of ast.options?.http ?? []) lines.push(` * @http ${httpRouteText(r)}`);
|
|
2875
|
+
if (ast.options?.cancelOn) {
|
|
2876
|
+
const c = ast.options.cancelOn;
|
|
2877
|
+
let line = ` * @cancelOn event="${c.event}"`;
|
|
2878
|
+
if (c.match) line += ` match="${c.match}"`;
|
|
2879
|
+
if (c.timeout) line += ` timeout="${c.timeout}"`;
|
|
2880
|
+
lines.push(line);
|
|
2881
|
+
}
|
|
2882
|
+
if (ast.options?.retries !== void 0) {
|
|
2883
|
+
lines.push(` * @retries ${ast.options.retries}`);
|
|
2884
|
+
}
|
|
2885
|
+
if (ast.options?.timeout) {
|
|
2886
|
+
lines.push(` * @timeout "${ast.options.timeout}"`);
|
|
2887
|
+
}
|
|
2888
|
+
if (ast.options?.throttle) {
|
|
2889
|
+
const t = ast.options.throttle;
|
|
2890
|
+
let line = ` * @throttle limit=${t.limit}`;
|
|
2891
|
+
if (t.period) line += ` period="${t.period}"`;
|
|
2892
|
+
lines.push(line);
|
|
2893
|
+
}
|
|
2894
|
+
lines.push(...serializePackDeployAnnotations(ast.options?.deploy));
|
|
2895
|
+
if (ast.name && ast.name !== ast.functionName) {
|
|
2896
|
+
lines.push(` * @name ${ast.name}`);
|
|
2897
|
+
}
|
|
2898
|
+
const npmNodeTypes = ast.nodeTypes.filter((nt) => nt.importSource);
|
|
2899
|
+
const seenImportNames = /* @__PURE__ */ new Set();
|
|
2900
|
+
for (const npmType of npmNodeTypes) {
|
|
2901
|
+
if (seenImportNames.has(npmType.name)) continue;
|
|
2902
|
+
seenImportNames.add(npmType.name);
|
|
2903
|
+
lines.push(` * @fwImport ${npmType.name} ${fwImportFunctionName(npmType)} from "${npmType.importSource}"`);
|
|
2904
|
+
}
|
|
2905
|
+
for (const instance of ast.instances) {
|
|
2906
|
+
if (macroInstanceIds.has(instance.id)) continue;
|
|
2907
|
+
if (allCoerceInstanceIds.has(instance.id)) continue;
|
|
2908
|
+
const inst = instance;
|
|
2909
|
+
if (macroChildIds.has(inst.id) && inst.parent) {
|
|
2910
|
+
const stripped = { ...inst, parent: void 0 };
|
|
2911
|
+
lines.push(generateNodeInstanceTag(stripped));
|
|
2912
|
+
} else {
|
|
2913
|
+
lines.push(generateNodeInstanceTag(inst));
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
const authoredConnections = ast.connections.filter((conn) => !conn.derived);
|
|
2917
|
+
const existingMacros = filterStaleMacros(
|
|
2918
|
+
ast.macros || [],
|
|
2919
|
+
authoredConnections,
|
|
2920
|
+
ast.instances,
|
|
2921
|
+
ast.nodeTypes,
|
|
2922
|
+
ast.startPorts,
|
|
2923
|
+
ast.exitPorts
|
|
2924
|
+
);
|
|
2925
|
+
const survivingCoerceIds = /* @__PURE__ */ new Set();
|
|
2926
|
+
for (const macro of existingMacros) {
|
|
2927
|
+
if (macro.type === "coerce") survivingCoerceIds.add(macro.instanceId);
|
|
2928
|
+
}
|
|
2929
|
+
const droppedCoerceIds = /* @__PURE__ */ new Set();
|
|
2930
|
+
for (const id of allCoerceInstanceIds) {
|
|
2931
|
+
if (!survivingCoerceIds.has(id)) droppedCoerceIds.add(id);
|
|
2932
|
+
}
|
|
2933
|
+
const detected = detectSugarPatterns(
|
|
2934
|
+
authoredConnections,
|
|
2935
|
+
ast.instances,
|
|
2936
|
+
existingMacros,
|
|
2937
|
+
ast.nodeTypes,
|
|
2938
|
+
ast.startPorts,
|
|
2939
|
+
ast.exitPorts
|
|
2940
|
+
);
|
|
2941
|
+
const allMacros = [
|
|
2942
|
+
...existingMacros,
|
|
2943
|
+
...detected.paths
|
|
2944
|
+
];
|
|
2945
|
+
if (allMacros.length > 0) {
|
|
2946
|
+
for (const macro of allMacros) {
|
|
2947
|
+
if (macro.type === "map") {
|
|
2948
|
+
let mapLine = ` * @map ${macro.instanceId} ${macro.childId}`;
|
|
2949
|
+
if (macro.inputPort || macro.outputPort) {
|
|
2950
|
+
mapLine += `(${macro.inputPort} -> ${macro.outputPort})`;
|
|
2951
|
+
}
|
|
2952
|
+
mapLine += ` over ${macro.sourcePort}`;
|
|
2953
|
+
lines.push(mapLine);
|
|
2954
|
+
} else if (macro.type === "path") {
|
|
2955
|
+
const stepsStr = macro.steps.map((s) => s.route ? `${s.node}:${s.route}` : s.node).join(" -> ");
|
|
2956
|
+
lines.push(` * @path ${stepsStr}`);
|
|
2957
|
+
} else if (macro.type === "fanOut") {
|
|
2958
|
+
const src = `${macro.source.node}.${macro.source.port}`;
|
|
2959
|
+
const tgts = macro.targets.map((t) => t.port ? `${t.node}.${t.port}` : t.node).join(", ");
|
|
2960
|
+
lines.push(` * @fanOut ${src} -> ${tgts}`);
|
|
2961
|
+
} else if (macro.type === "fanIn") {
|
|
2962
|
+
const srcs = macro.sources.map((s) => s.port ? `${s.node}.${s.port}` : s.node).join(", ");
|
|
2963
|
+
const tgt = `${macro.target.node}.${macro.target.port}`;
|
|
2964
|
+
lines.push(` * @fanIn ${srcs} -> ${tgt}`);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
if (!ast.options?.autoConnect) {
|
|
2969
|
+
for (const conn of authoredConnections) {
|
|
2970
|
+
if (allMacros.length > 0 && isConnectionCoveredByMacro(conn, allMacros)) continue;
|
|
2971
|
+
if (droppedCoerceIds.has(conn.from.node) || droppedCoerceIds.has(conn.to.node)) continue;
|
|
2972
|
+
const fromScope = conn.from.scope ? `:${conn.from.scope}` : "";
|
|
2973
|
+
const toScope = conn.to.scope ? `:${conn.to.scope}` : "";
|
|
2974
|
+
lines.push(
|
|
2975
|
+
` * @connect ${conn.from.node}.${conn.from.port}${fromScope} -> ${conn.to.node}.${conn.to.port}${toScope}`
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
if (!options.skipParamReturns && ast.startPorts && Object.keys(ast.startPorts).length > 0) {
|
|
2980
|
+
for (const { name, port, writeOrder } of planPortTags(Object.entries(ast.startPorts))) {
|
|
2981
|
+
const paramTag = generateJSDocPortTag(name, port, "input", void 0, { writeOrder });
|
|
2982
|
+
lines.push(` * ${paramTag.replace("@input", "@param")}`);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
if (!options.skipParamReturns && ast.exitPorts && Object.keys(ast.exitPorts).length > 0) {
|
|
2986
|
+
for (const { name, port, writeOrder } of planPortTags(Object.entries(ast.exitPorts))) {
|
|
2987
|
+
const returnTag = generateJSDocPortTag(name, port, "output", void 0, { writeOrder });
|
|
2988
|
+
lines.push(` * ${returnTag.replace("@output", "@returns")}`);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
if (ast.scopes) {
|
|
2992
|
+
for (const [scopeName, children] of Object.entries(ast.scopes)) {
|
|
2993
|
+
if (macroScopeNames.has(scopeName)) continue;
|
|
2994
|
+
lines.push(` * @scope ${scopeName} [${children.join(", ")}]`);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
lines.push(" */");
|
|
2998
|
+
return lines.join("\n");
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// src/api/generate-in-place.ts
|
|
3002
|
+
function generateInPlace(sourceCode, ast, options = {}) {
|
|
3003
|
+
const {
|
|
3004
|
+
production = false,
|
|
3005
|
+
allWorkflows,
|
|
3006
|
+
skipParamReturns = false,
|
|
3007
|
+
annotationsOnly = false
|
|
3008
|
+
} = options;
|
|
3009
|
+
const durableSequential = validateDurableClosure(
|
|
3010
|
+
ast,
|
|
3011
|
+
allWorkflows ?? []
|
|
3012
|
+
).hasDurableGate;
|
|
3013
|
+
let result = sourceCode;
|
|
3014
|
+
let hasChanges = false;
|
|
3015
|
+
const apply = (edit) => {
|
|
3016
|
+
if (edit.changed) {
|
|
3017
|
+
result = edit.code;
|
|
3018
|
+
hasChanges = true;
|
|
3019
|
+
}
|
|
3020
|
+
};
|
|
3021
|
+
apply(syncNodeTypeFunctions(result, ast));
|
|
3022
|
+
if (!annotationsOnly) {
|
|
3023
|
+
apply(inlineBuiltInNodes(result, ast, production));
|
|
3024
|
+
}
|
|
3025
|
+
apply(removeOrphanedNodeTypeFunctions(result, ast, allWorkflows));
|
|
3026
|
+
apply(replaceWorkflowJSDoc(result, ast, { skipParamReturns }));
|
|
3027
|
+
if (annotationsOnly) {
|
|
3028
|
+
return { code: result, hasChanges: hasChanges && result !== sourceCode };
|
|
3029
|
+
}
|
|
3030
|
+
apply(ensureFwImportStatements(result, ast));
|
|
3031
|
+
apply(replaceOrInsertRuntimeSection(result, production));
|
|
3032
|
+
apply(ensureParamsParameter(result, ast.functionName));
|
|
3033
|
+
apply(ensureRuntimeParameter(result, ast.functionName));
|
|
3034
|
+
const nodesRequireAsync = shouldWorkflowBeAsync(ast, ast.nodeTypes);
|
|
3035
|
+
const sourceIsAsync = detectFunctionIsAsync(result, ast.functionName);
|
|
3036
|
+
const forceAsync = nodesRequireAsync || !production;
|
|
3037
|
+
const isAsync = forceAsync || sourceIsAsync;
|
|
3038
|
+
apply(ensureAsyncKeyword(result, ast.functionName, forceAsync));
|
|
3039
|
+
apply(ensurePromiseReturnType(result, ast.functionName, forceAsync));
|
|
3040
|
+
const identity = durableSequential ? { graphFingerprint: graphIdentity(ast, allWorkflows ?? []).graphFingerprint } : void 0;
|
|
3041
|
+
const functionBody = generateFunctionBody(ast, production, isAsync, durableSequential, identity);
|
|
3042
|
+
apply(replaceWorkflowFunctionBody(result, ast.functionName, functionBody));
|
|
3043
|
+
if (hasChanges && result === sourceCode) {
|
|
3044
|
+
hasChanges = false;
|
|
3045
|
+
}
|
|
3046
|
+
return { code: result, hasChanges };
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
export {
|
|
3050
|
+
bodyGenerator,
|
|
3051
|
+
INLINE_ENGINE_EXPORTS,
|
|
3052
|
+
stripTypeScript,
|
|
3053
|
+
generateInlineRuntime,
|
|
3054
|
+
graphIdentity,
|
|
3055
|
+
validateWorkflowAsync,
|
|
3056
|
+
generateInPlace
|
|
3057
|
+
};
|