@flowgram.ai/runtime-js 0.2.26 → 0.2.28

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/dist/esm/index.js CHANGED
@@ -195,13 +195,16 @@ var FlowGramNode = /* @__PURE__ */ ((FlowGramNode22) => {
195
195
  FlowGramNode22["Start"] = "start";
196
196
  FlowGramNode22["End"] = "end";
197
197
  FlowGramNode22["LLM"] = "llm";
198
- FlowGramNode22["code"] = "code";
198
+ FlowGramNode22["Code"] = "code";
199
199
  FlowGramNode22["Condition"] = "condition";
200
200
  FlowGramNode22["Loop"] = "loop";
201
201
  FlowGramNode22["Comment"] = "comment";
202
202
  FlowGramNode22["Group"] = "group";
203
203
  FlowGramNode22["BlockStart"] = "block-start";
204
204
  FlowGramNode22["BlockEnd"] = "block-end";
205
+ FlowGramNode22["HTTP"] = "http";
206
+ FlowGramNode22["Break"] = "break";
207
+ FlowGramNode22["Continue"] = "continue";
205
208
  return FlowGramNode22;
206
209
  })(FlowGramNode || {});
207
210
  var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
@@ -221,6 +224,15 @@ var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
221
224
  ConditionOperation2["IS_FALSE"] = "is_false";
222
225
  return ConditionOperation2;
223
226
  })(ConditionOperation || {});
227
+ var HTTPBodyType = /* @__PURE__ */ ((HTTPBodyType2) => {
228
+ HTTPBodyType2["None"] = "none";
229
+ HTTPBodyType2["FormData"] = "form-data";
230
+ HTTPBodyType2["XWwwFormUrlencoded"] = "x-www-form-urlencoded";
231
+ HTTPBodyType2["RawText"] = "raw-text";
232
+ HTTPBodyType2["JSON"] = "JSON";
233
+ HTTPBodyType2["Binary"] = "binary";
234
+ return HTTPBodyType2;
235
+ })(HTTPBodyType || {});
224
236
  var IEngine = Symbol.for("Engine");
225
237
  var IExecutor = Symbol.for("Executor");
226
238
  var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
@@ -599,6 +611,12 @@ var LoopExecutor = class {
599
611
  } catch (e) {
600
612
  throw new Error(`Loop block execute error`);
601
613
  }
614
+ if (this.isBreak(subContext)) {
615
+ break;
616
+ }
617
+ if (this.isContinue(subContext)) {
618
+ continue;
619
+ }
602
620
  const blockOutput = this.getBlockOutput(context, subContext);
603
621
  blockOutputs.push(blockOutput);
604
622
  }
@@ -682,69 +700,25 @@ var LoopExecutor = class {
682
700
  const loopOutputsDeclare = loopNodeData.loopOutputs ?? {};
683
701
  return loopOutputsDeclare;
684
702
  }
703
+ isBreak(subContext) {
704
+ return subContext.cache.get("loop-break") === true;
705
+ }
706
+ isContinue(subContext) {
707
+ return subContext.cache.get("loop-continue") === true;
708
+ }
685
709
  };
686
710
 
687
711
  // src/nodes/llm/index.ts
688
712
  import { isNil as isNil2 } from "lodash-es";
689
713
  import { ChatOpenAI } from "@langchain/openai";
690
714
  import { SystemMessage, HumanMessage } from "@langchain/core/messages";
691
-
692
- // src/nodes/llm/api-validator.ts
693
- var APIValidator;
694
- ((APIValidator2) => {
695
- APIValidator2.isValidFormat = (apiHost) => {
696
- if (!apiHost || typeof apiHost !== "string") {
697
- return false;
698
- }
699
- try {
700
- const url = new URL(apiHost);
701
- return url.protocol === "http:" || url.protocol === "https:";
702
- } catch (error) {
703
- return false;
704
- }
705
- };
706
- APIValidator2.isExist = async (apiHost) => {
707
- try {
708
- const controller = new AbortController();
709
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
710
- await fetch(apiHost, {
711
- method: "HEAD",
712
- // Use HEAD to minimize data transfer
713
- signal: controller.signal,
714
- // Disable following redirects to get the actual host response
715
- redirect: "manual"
716
- });
717
- clearTimeout(timeoutId);
718
- return true;
719
- } catch (error) {
720
- if (error.name === "AbortError") {
721
- return false;
722
- }
723
- const errorMessage = error.message?.toLowerCase() || "";
724
- const networkFailurePatterns = [
725
- "network error",
726
- "connection refused",
727
- "dns",
728
- "resolve",
729
- "timeout",
730
- "unreachable"
731
- ];
732
- const isNetworkFailure = networkFailurePatterns.some(
733
- (pattern) => errorMessage.includes(pattern)
734
- );
735
- return !isNetworkFailure;
736
- }
737
- };
738
- })(APIValidator || (APIValidator = {}));
739
-
740
- // src/nodes/llm/index.ts
741
715
  var LLMExecutor = class {
742
716
  constructor() {
743
717
  this.type = FlowGramNode.LLM;
744
718
  }
745
719
  async execute(context) {
746
720
  const inputs = context.inputs;
747
- await this.checkInputs(inputs);
721
+ this.checkInputs(inputs);
748
722
  const { modelName, temperature, apiKey, apiHost, systemPrompt, prompt } = inputs;
749
723
  const model = new ChatOpenAI({
750
724
  modelName,
@@ -752,7 +726,8 @@ var LLMExecutor = class {
752
726
  apiKey,
753
727
  configuration: {
754
728
  baseURL: apiHost
755
- }
729
+ },
730
+ maxRetries: 3
756
731
  });
757
732
  const messages = [];
758
733
  if (systemPrompt) {
@@ -776,7 +751,7 @@ var LLMExecutor = class {
776
751
  }
777
752
  };
778
753
  }
779
- async checkInputs(inputs) {
754
+ checkInputs(inputs) {
780
755
  const { modelName, temperature, apiKey, apiHost, prompt } = inputs;
781
756
  const missingInputs = [];
782
757
  if (!modelName) missingInputs.push("modelName");
@@ -787,12 +762,236 @@ var LLMExecutor = class {
787
762
  if (missingInputs.length > 0) {
788
763
  throw new Error(`LLM node missing required inputs: "${missingInputs.join('", "')}"`);
789
764
  }
790
- if (!APIValidator.isValidFormat(apiHost)) {
765
+ this.checkApiHost(apiHost);
766
+ }
767
+ checkApiHost(apiHost) {
768
+ if (!apiHost || typeof apiHost !== "string") {
791
769
  throw new Error(`Invalid API host format - ${apiHost}`);
792
770
  }
793
- const apiHostExists = await APIValidator.isExist(apiHost);
794
- if (!apiHostExists) {
795
- throw new Error(`Unreachable API host - ${apiHost}`);
771
+ const url = new URL(apiHost);
772
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
773
+ throw new Error(`Invalid API host protocol - ${url.protocol}`);
774
+ }
775
+ }
776
+ };
777
+
778
+ // src/nodes/http/index.ts
779
+ var HTTPExecutor = class {
780
+ constructor() {
781
+ this.type = FlowGramNode.HTTP;
782
+ }
783
+ async execute(context) {
784
+ const inputs = this.parseInputs(context);
785
+ const response = await this.request(inputs);
786
+ const responseHeaders = {};
787
+ response.headers.forEach((value, key) => {
788
+ responseHeaders[key] = value;
789
+ });
790
+ const responseBody = await response.text();
791
+ return {
792
+ outputs: {
793
+ headers: responseHeaders,
794
+ statusCode: response.status,
795
+ body: responseBody
796
+ }
797
+ };
798
+ }
799
+ async request(inputs) {
800
+ const { method, url, headers, params, bodyType, body, retryTimes, timeout } = inputs;
801
+ const urlWithParams = this.buildUrlWithParams(url, params);
802
+ const requestOptions = {
803
+ method,
804
+ headers: this.prepareHeaders(headers, bodyType),
805
+ signal: AbortSignal.timeout(timeout)
806
+ };
807
+ if (method !== "GET" && method !== "HEAD" && body) {
808
+ requestOptions.body = this.prepareBody(body, bodyType);
809
+ }
810
+ let lastError = null;
811
+ for (let attempt = 0; attempt <= retryTimes; attempt++) {
812
+ try {
813
+ const response = await fetch(urlWithParams, requestOptions);
814
+ return response;
815
+ } catch (error) {
816
+ lastError = error;
817
+ if (attempt < retryTimes) {
818
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
819
+ }
820
+ }
821
+ }
822
+ throw lastError || new Error("HTTP request failed after all retry attempts");
823
+ }
824
+ parseInputs(context) {
825
+ const httpNode = context.node;
826
+ const method = httpNode.data.api.method;
827
+ const urlVariable = context.runtime.state.parseTemplate(httpNode.data.api.url);
828
+ if (!urlVariable) {
829
+ throw new Error("HTTP url is required");
830
+ }
831
+ const url = urlVariable.value;
832
+ const headers = context.runtime.state.parseInputs({
833
+ values: httpNode.data.headersValues,
834
+ declare: httpNode.data.headers
835
+ });
836
+ const params = context.runtime.state.parseInputs({
837
+ values: httpNode.data.paramsValues,
838
+ declare: httpNode.data.params
839
+ });
840
+ const body = this.parseBody(context);
841
+ const retryTimes = httpNode.data.timeout.retryTimes;
842
+ const timeout = httpNode.data.timeout.timeout;
843
+ const inputs = {
844
+ method,
845
+ url,
846
+ headers,
847
+ params,
848
+ bodyType: body.bodyType,
849
+ body: body.body,
850
+ retryTimes,
851
+ timeout
852
+ };
853
+ context.snapshot.update({
854
+ inputs: JSON.parse(JSON.stringify(inputs))
855
+ });
856
+ return inputs;
857
+ }
858
+ parseBody(context) {
859
+ const httpNode = context.node;
860
+ const bodyType = httpNode.data.body.bodyType;
861
+ if (bodyType === HTTPBodyType.None) {
862
+ return {
863
+ bodyType,
864
+ body: ""
865
+ };
866
+ }
867
+ if (bodyType === HTTPBodyType.JSON) {
868
+ if (!httpNode.data.body.json) {
869
+ throw new Error("HTTP json body is required");
870
+ }
871
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
872
+ if (!jsonVariable) {
873
+ throw new Error("HTTP json body is required");
874
+ }
875
+ return {
876
+ bodyType,
877
+ body: jsonVariable.value
878
+ };
879
+ }
880
+ if (bodyType === HTTPBodyType.FormData) {
881
+ if (!httpNode.data.body.formData || !httpNode.data.body.formDataValues) {
882
+ throw new Error("HTTP form-data body is required");
883
+ }
884
+ const formData = context.runtime.state.parseInputs({
885
+ values: httpNode.data.body.formDataValues,
886
+ declare: httpNode.data.body.formData
887
+ });
888
+ return {
889
+ bodyType,
890
+ body: JSON.stringify(formData)
891
+ };
892
+ }
893
+ if (bodyType === HTTPBodyType.RawText) {
894
+ if (!httpNode.data.body.json) {
895
+ throw new Error("HTTP json body is required");
896
+ }
897
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
898
+ if (!jsonVariable) {
899
+ throw new Error("HTTP json body is required");
900
+ }
901
+ return {
902
+ bodyType,
903
+ body: jsonVariable.value
904
+ };
905
+ }
906
+ if (bodyType === HTTPBodyType.Binary) {
907
+ if (!httpNode.data.body.binary) {
908
+ throw new Error("HTTP binary body is required");
909
+ }
910
+ const binaryVariable = context.runtime.state.parseTemplate(httpNode.data.body.binary);
911
+ if (!binaryVariable) {
912
+ throw new Error("HTTP binary body is required");
913
+ }
914
+ return {
915
+ bodyType,
916
+ body: binaryVariable.value
917
+ };
918
+ }
919
+ if (bodyType === HTTPBodyType.XWwwFormUrlencoded) {
920
+ if (!httpNode.data.body.xWwwFormUrlencoded || !httpNode.data.body.xWwwFormUrlencodedValues) {
921
+ throw new Error("HTTP x-www-form-urlencoded body is required");
922
+ }
923
+ const xWwwFormUrlencoded = context.runtime.state.parseInputs({
924
+ values: httpNode.data.body.xWwwFormUrlencodedValues,
925
+ declare: httpNode.data.body.xWwwFormUrlencoded
926
+ });
927
+ return {
928
+ bodyType,
929
+ body: JSON.stringify(xWwwFormUrlencoded)
930
+ };
931
+ }
932
+ throw new Error(`HTTP invalid body type "${bodyType}"`);
933
+ }
934
+ buildUrlWithParams(url, params) {
935
+ const urlObj = new URL(url);
936
+ Object.entries(params).forEach(([key, value]) => {
937
+ if (value !== void 0 && value !== null && value !== "") {
938
+ urlObj.searchParams.set(key, value);
939
+ }
940
+ });
941
+ return urlObj.toString();
942
+ }
943
+ prepareHeaders(headers, bodyType) {
944
+ const preparedHeaders = { ...headers };
945
+ if (!preparedHeaders["Content-Type"] && !preparedHeaders["content-type"]) {
946
+ switch (bodyType) {
947
+ case HTTPBodyType.JSON:
948
+ preparedHeaders["Content-Type"] = "application/json";
949
+ break;
950
+ case HTTPBodyType.FormData:
951
+ break;
952
+ case HTTPBodyType.XWwwFormUrlencoded:
953
+ preparedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
954
+ break;
955
+ case HTTPBodyType.RawText:
956
+ preparedHeaders["Content-Type"] = "text/plain";
957
+ break;
958
+ case HTTPBodyType.Binary:
959
+ preparedHeaders["Content-Type"] = "application/octet-stream";
960
+ break;
961
+ }
962
+ }
963
+ return preparedHeaders;
964
+ }
965
+ prepareBody(body, bodyType) {
966
+ switch (bodyType) {
967
+ case HTTPBodyType.JSON:
968
+ return body;
969
+ case HTTPBodyType.FormData:
970
+ const formData = new FormData();
971
+ try {
972
+ const data = JSON.parse(body);
973
+ Object.entries(data).forEach(([key, value]) => {
974
+ formData.append(key, String(value));
975
+ });
976
+ } catch (error) {
977
+ throw new Error("Invalid FormData body format");
978
+ }
979
+ return formData;
980
+ case HTTPBodyType.XWwwFormUrlencoded:
981
+ try {
982
+ const data = JSON.parse(body);
983
+ const params = new URLSearchParams();
984
+ Object.entries(data).forEach(([key, value]) => {
985
+ params.append(key, String(value));
986
+ });
987
+ return params.toString();
988
+ } catch (error) {
989
+ throw new Error("Invalid x-www-form-urlencoded body format");
990
+ }
991
+ case HTTPBodyType.RawText:
992
+ case HTTPBodyType.Binary:
993
+ default:
994
+ return body;
796
995
  }
797
996
  }
798
997
  };
@@ -832,6 +1031,19 @@ var BlockEndExecutor = class {
832
1031
  }
833
1032
  };
834
1033
 
1034
+ // src/nodes/continue/index.ts
1035
+ var ContinueExecutor = class {
1036
+ constructor() {
1037
+ this.type = FlowGramNode.Continue;
1038
+ }
1039
+ async execute(context) {
1040
+ context.runtime.cache.set("loop-continue", true);
1041
+ return {
1042
+ outputs: {}
1043
+ };
1044
+ }
1045
+ };
1046
+
835
1047
  // src/nodes/condition/index.ts
836
1048
  import { isNil as isNil9 } from "lodash-es";
837
1049
 
@@ -1123,7 +1335,7 @@ var ConditionExecutor = class {
1123
1335
  `Condition left type "${condition.leftType}" has no operator "${condition.operator}"`
1124
1336
  );
1125
1337
  }
1126
- if (ruleType !== condition.rightType) {
1338
+ if (!WorkflowRuntimeType.isTypeEqual(ruleType, condition.rightType)) {
1127
1339
  return false;
1128
1340
  }
1129
1341
  return true;
@@ -1138,6 +1350,81 @@ var ConditionExecutor = class {
1138
1350
  }
1139
1351
  };
1140
1352
 
1353
+ // src/nodes/code/index.ts
1354
+ var CodeExecutor = class {
1355
+ constructor() {
1356
+ this.type = FlowGramNode.Code;
1357
+ }
1358
+ async execute(context) {
1359
+ const inputs = this.parseInputs(context);
1360
+ if (inputs.script.language === "javascript") {
1361
+ return this.javascript(inputs);
1362
+ }
1363
+ throw new Error(`Unsupported code language "${inputs.script.language}"`);
1364
+ }
1365
+ parseInputs(context) {
1366
+ const codeNode = context.node;
1367
+ const params = context.inputs;
1368
+ const { language, content } = codeNode.data.script;
1369
+ if (!content) {
1370
+ throw new Error("Code content is required");
1371
+ }
1372
+ return {
1373
+ params,
1374
+ script: {
1375
+ language,
1376
+ content
1377
+ }
1378
+ };
1379
+ }
1380
+ async javascript(inputs) {
1381
+ const { params = {}, script } = inputs;
1382
+ try {
1383
+ const executeCode = new Function(
1384
+ "params",
1385
+ `
1386
+ 'use strict';
1387
+
1388
+ ${script.content}
1389
+
1390
+ // Ensure main function exists
1391
+ if (typeof main !== 'function') {
1392
+ throw new Error('main function is required in the script');
1393
+ }
1394
+
1395
+ // Execute main function with params
1396
+ return main({ params });
1397
+ `
1398
+ );
1399
+ const timeoutPromise = new Promise((_, reject) => {
1400
+ setTimeout(() => {
1401
+ reject(new Error("Code execution timeout: exceeded 1 minute"));
1402
+ }, 1e3 * 60);
1403
+ });
1404
+ const result = await Promise.race([executeCode(params), timeoutPromise]);
1405
+ const outputs = result && typeof result === "object" && !Array.isArray(result) ? result : { result };
1406
+ return {
1407
+ outputs
1408
+ };
1409
+ } catch (error) {
1410
+ throw new Error(`Code execution failed: ${error.message}`);
1411
+ }
1412
+ }
1413
+ };
1414
+
1415
+ // src/nodes/break/index.ts
1416
+ var BreakExecutor = class {
1417
+ constructor() {
1418
+ this.type = FlowGramNode.Break;
1419
+ }
1420
+ async execute(context) {
1421
+ context.runtime.cache.set("loop-break", true);
1422
+ return {
1423
+ outputs: {}
1424
+ };
1425
+ }
1426
+ };
1427
+
1141
1428
  // src/nodes/index.ts
1142
1429
  var WorkflowRuntimeNodeExecutors = [
1143
1430
  StartExecutor,
@@ -1146,7 +1433,11 @@ var WorkflowRuntimeNodeExecutors = [
1146
1433
  ConditionExecutor,
1147
1434
  LoopExecutor,
1148
1435
  BlockStartExecutor,
1149
- BlockEndExecutor
1436
+ BlockEndExecutor,
1437
+ HTTPExecutor,
1438
+ CodeExecutor,
1439
+ BreakExecutor,
1440
+ ContinueExecutor
1150
1441
  ];
1151
1442
 
1152
1443
  // src/domain/validation/validators/cycle-detection.ts
@@ -1565,6 +1856,29 @@ var WorkflowRuntimeMessageCenter = class {
1565
1856
  }
1566
1857
  };
1567
1858
 
1859
+ // src/domain/cache/index.ts
1860
+ var WorkflowRuntimeCache = class {
1861
+ init() {
1862
+ this.map = /* @__PURE__ */ new Map();
1863
+ }
1864
+ dispose() {
1865
+ this.map.clear();
1866
+ }
1867
+ get(key) {
1868
+ return this.map.get(key);
1869
+ }
1870
+ set(key, value) {
1871
+ this.map.set(key, value);
1872
+ return this;
1873
+ }
1874
+ delete(key) {
1875
+ return this.map.delete(key);
1876
+ }
1877
+ has(key) {
1878
+ return this.map.has(key);
1879
+ }
1880
+ };
1881
+
1568
1882
  // src/domain/variable/variable-store/index.ts
1569
1883
  import { get, set } from "lodash-es";
1570
1884
 
@@ -1790,26 +2104,10 @@ var WorkflowRuntimeState = class {
1790
2104
  getNodeInputs(node) {
1791
2105
  const inputsDeclare = node.declare.inputs;
1792
2106
  const inputsValues = node.declare.inputsValues;
1793
- if (!inputsDeclare || !inputsValues) {
1794
- return {};
1795
- }
1796
- return Object.entries(inputsValues).reduce((prev, [key, inputValue]) => {
1797
- const typeInfo = inputsDeclare.properties?.[key];
1798
- if (!typeInfo) {
1799
- return prev;
1800
- }
1801
- const expectType = typeInfo.type;
1802
- const result = this.parseValue(inputValue);
1803
- if (!result) {
1804
- return prev;
1805
- }
1806
- const { value, type } = result;
1807
- if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
1808
- return prev;
1809
- }
1810
- prev[key] = value;
1811
- return prev;
1812
- }, {});
2107
+ return this.parseInputs({
2108
+ values: inputsValues,
2109
+ declare: inputsDeclare
2110
+ });
1813
2111
  }
1814
2112
  setNodeOutputs(params) {
1815
2113
  const { node, outputs } = params;
@@ -1833,6 +2131,29 @@ var WorkflowRuntimeState = class {
1833
2131
  });
1834
2132
  });
1835
2133
  }
2134
+ parseInputs(params) {
2135
+ const { values, declare } = params;
2136
+ if (!declare || !values) {
2137
+ return {};
2138
+ }
2139
+ return Object.entries(values).reduce((prev, [key, inputValue]) => {
2140
+ const typeInfo = declare.properties?.[key];
2141
+ if (!typeInfo) {
2142
+ return prev;
2143
+ }
2144
+ const expectType = typeInfo.type;
2145
+ const result = this.parseValue(inputValue);
2146
+ if (!result) {
2147
+ return prev;
2148
+ }
2149
+ const { value, type } = result;
2150
+ if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
2151
+ return prev;
2152
+ }
2153
+ prev[key] = value;
2154
+ return prev;
2155
+ }, {});
2156
+ }
1836
2157
  parseRef(ref) {
1837
2158
  if (ref?.type !== "ref") {
1838
2159
  throw new Error(`Invalid ref value: ${ref}`);
@@ -2366,6 +2687,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2366
2687
  constructor(data) {
2367
2688
  this.subContexts = [];
2368
2689
  this.id = uuid();
2690
+ this.cache = data.cache;
2369
2691
  this.document = data.document;
2370
2692
  this.variableStore = data.variableStore;
2371
2693
  this.state = data.state;
@@ -2377,6 +2699,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2377
2699
  }
2378
2700
  init(params) {
2379
2701
  const { schema, inputs } = params;
2702
+ this.cache.init();
2380
2703
  this.document.init(schema);
2381
2704
  this.variableStore.init();
2382
2705
  this.state.init();
@@ -2391,6 +2714,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2391
2714
  subContext.dispose();
2392
2715
  });
2393
2716
  this.subContexts = [];
2717
+ this.cache.dispose();
2394
2718
  this.document.dispose();
2395
2719
  this.variableStore.dispose();
2396
2720
  this.state.dispose();
@@ -2401,10 +2725,12 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2401
2725
  this.reporter.dispose();
2402
2726
  }
2403
2727
  sub() {
2728
+ const cache = new WorkflowRuntimeCache();
2404
2729
  const variableStore = new WorkflowRuntimeVariableStore();
2405
2730
  variableStore.setParent(this.variableStore);
2406
2731
  const state = new WorkflowRuntimeState(variableStore);
2407
2732
  const contextData = {
2733
+ cache,
2408
2734
  document: this.document,
2409
2735
  ioCenter: this.ioCenter,
2410
2736
  snapshotCenter: this.snapshotCenter,
@@ -2416,11 +2742,13 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2416
2742
  };
2417
2743
  const subContext = new _WorkflowRuntimeContext(contextData);
2418
2744
  this.subContexts.push(subContext);
2745
+ subContext.cache.init();
2419
2746
  subContext.variableStore.init();
2420
2747
  subContext.state.init();
2421
2748
  return subContext;
2422
2749
  }
2423
2750
  static create() {
2751
+ const cache = new WorkflowRuntimeCache();
2424
2752
  const document = new WorkflowRuntimeDocument();
2425
2753
  const variableStore = new WorkflowRuntimeVariableStore();
2426
2754
  const state = new WorkflowRuntimeState(variableStore);
@@ -2435,6 +2763,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2435
2763
  messageCenter
2436
2764
  );
2437
2765
  return new _WorkflowRuntimeContext({
2766
+ cache,
2438
2767
  document,
2439
2768
  variableStore,
2440
2769
  state,
@@ -2492,7 +2821,8 @@ var WorkflowRuntimeEngine = class {
2492
2821
  node,
2493
2822
  inputs,
2494
2823
  runtime: context,
2495
- container: WorkflowRuntimeContainer.instance
2824
+ container: WorkflowRuntimeContainer.instance,
2825
+ snapshot
2496
2826
  });
2497
2827
  if (context.statusCenter.workflow.terminated) {
2498
2828
  return;
@@ -2573,7 +2903,13 @@ var WorkflowRuntimeEngine = class {
2573
2903
  }
2574
2904
  async executeNext(params) {
2575
2905
  const { context, node, nextNodes } = params;
2576
- if (node.type === FlowGramNode.End || node.type === FlowGramNode.BlockEnd) {
2906
+ const terminatingNodeTypes = [
2907
+ FlowGramNode.End,
2908
+ FlowGramNode.BlockEnd,
2909
+ FlowGramNode.Break,
2910
+ FlowGramNode.Continue
2911
+ ];
2912
+ if (terminatingNodeTypes.includes(node.type)) {
2577
2913
  return;
2578
2914
  }
2579
2915
  if (nextNodes.length === 0) {