@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/index.js CHANGED
@@ -236,13 +236,16 @@ var FlowGramNode = /* @__PURE__ */ ((FlowGramNode22) => {
236
236
  FlowGramNode22["Start"] = "start";
237
237
  FlowGramNode22["End"] = "end";
238
238
  FlowGramNode22["LLM"] = "llm";
239
- FlowGramNode22["code"] = "code";
239
+ FlowGramNode22["Code"] = "code";
240
240
  FlowGramNode22["Condition"] = "condition";
241
241
  FlowGramNode22["Loop"] = "loop";
242
242
  FlowGramNode22["Comment"] = "comment";
243
243
  FlowGramNode22["Group"] = "group";
244
244
  FlowGramNode22["BlockStart"] = "block-start";
245
245
  FlowGramNode22["BlockEnd"] = "block-end";
246
+ FlowGramNode22["HTTP"] = "http";
247
+ FlowGramNode22["Break"] = "break";
248
+ FlowGramNode22["Continue"] = "continue";
246
249
  return FlowGramNode22;
247
250
  })(FlowGramNode || {});
248
251
  var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
@@ -262,6 +265,15 @@ var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
262
265
  ConditionOperation2["IS_FALSE"] = "is_false";
263
266
  return ConditionOperation2;
264
267
  })(ConditionOperation || {});
268
+ var HTTPBodyType = /* @__PURE__ */ ((HTTPBodyType2) => {
269
+ HTTPBodyType2["None"] = "none";
270
+ HTTPBodyType2["FormData"] = "form-data";
271
+ HTTPBodyType2["XWwwFormUrlencoded"] = "x-www-form-urlencoded";
272
+ HTTPBodyType2["RawText"] = "raw-text";
273
+ HTTPBodyType2["JSON"] = "JSON";
274
+ HTTPBodyType2["Binary"] = "binary";
275
+ return HTTPBodyType2;
276
+ })(HTTPBodyType || {});
265
277
  var IEngine = Symbol.for("Engine");
266
278
  var IExecutor = Symbol.for("Executor");
267
279
  var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
@@ -640,6 +652,12 @@ var LoopExecutor = class {
640
652
  } catch (e) {
641
653
  throw new Error(`Loop block execute error`);
642
654
  }
655
+ if (this.isBreak(subContext)) {
656
+ break;
657
+ }
658
+ if (this.isContinue(subContext)) {
659
+ continue;
660
+ }
643
661
  const blockOutput = this.getBlockOutput(context, subContext);
644
662
  blockOutputs.push(blockOutput);
645
663
  }
@@ -723,69 +741,25 @@ var LoopExecutor = class {
723
741
  const loopOutputsDeclare = loopNodeData.loopOutputs ?? {};
724
742
  return loopOutputsDeclare;
725
743
  }
744
+ isBreak(subContext) {
745
+ return subContext.cache.get("loop-break") === true;
746
+ }
747
+ isContinue(subContext) {
748
+ return subContext.cache.get("loop-continue") === true;
749
+ }
726
750
  };
727
751
 
728
752
  // src/nodes/llm/index.ts
729
753
  var import_lodash_es2 = require("lodash-es");
730
754
  var import_openai = require("@langchain/openai");
731
755
  var import_messages = require("@langchain/core/messages");
732
-
733
- // src/nodes/llm/api-validator.ts
734
- var APIValidator;
735
- ((APIValidator2) => {
736
- APIValidator2.isValidFormat = (apiHost) => {
737
- if (!apiHost || typeof apiHost !== "string") {
738
- return false;
739
- }
740
- try {
741
- const url = new URL(apiHost);
742
- return url.protocol === "http:" || url.protocol === "https:";
743
- } catch (error) {
744
- return false;
745
- }
746
- };
747
- APIValidator2.isExist = async (apiHost) => {
748
- try {
749
- const controller = new AbortController();
750
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
751
- await fetch(apiHost, {
752
- method: "HEAD",
753
- // Use HEAD to minimize data transfer
754
- signal: controller.signal,
755
- // Disable following redirects to get the actual host response
756
- redirect: "manual"
757
- });
758
- clearTimeout(timeoutId);
759
- return true;
760
- } catch (error) {
761
- if (error.name === "AbortError") {
762
- return false;
763
- }
764
- const errorMessage = error.message?.toLowerCase() || "";
765
- const networkFailurePatterns = [
766
- "network error",
767
- "connection refused",
768
- "dns",
769
- "resolve",
770
- "timeout",
771
- "unreachable"
772
- ];
773
- const isNetworkFailure = networkFailurePatterns.some(
774
- (pattern) => errorMessage.includes(pattern)
775
- );
776
- return !isNetworkFailure;
777
- }
778
- };
779
- })(APIValidator || (APIValidator = {}));
780
-
781
- // src/nodes/llm/index.ts
782
756
  var LLMExecutor = class {
783
757
  constructor() {
784
758
  this.type = FlowGramNode.LLM;
785
759
  }
786
760
  async execute(context) {
787
761
  const inputs = context.inputs;
788
- await this.checkInputs(inputs);
762
+ this.checkInputs(inputs);
789
763
  const { modelName, temperature, apiKey, apiHost, systemPrompt, prompt } = inputs;
790
764
  const model = new import_openai.ChatOpenAI({
791
765
  modelName,
@@ -793,7 +767,8 @@ var LLMExecutor = class {
793
767
  apiKey,
794
768
  configuration: {
795
769
  baseURL: apiHost
796
- }
770
+ },
771
+ maxRetries: 3
797
772
  });
798
773
  const messages = [];
799
774
  if (systemPrompt) {
@@ -817,7 +792,7 @@ var LLMExecutor = class {
817
792
  }
818
793
  };
819
794
  }
820
- async checkInputs(inputs) {
795
+ checkInputs(inputs) {
821
796
  const { modelName, temperature, apiKey, apiHost, prompt } = inputs;
822
797
  const missingInputs = [];
823
798
  if (!modelName) missingInputs.push("modelName");
@@ -828,12 +803,236 @@ var LLMExecutor = class {
828
803
  if (missingInputs.length > 0) {
829
804
  throw new Error(`LLM node missing required inputs: "${missingInputs.join('", "')}"`);
830
805
  }
831
- if (!APIValidator.isValidFormat(apiHost)) {
806
+ this.checkApiHost(apiHost);
807
+ }
808
+ checkApiHost(apiHost) {
809
+ if (!apiHost || typeof apiHost !== "string") {
832
810
  throw new Error(`Invalid API host format - ${apiHost}`);
833
811
  }
834
- const apiHostExists = await APIValidator.isExist(apiHost);
835
- if (!apiHostExists) {
836
- throw new Error(`Unreachable API host - ${apiHost}`);
812
+ const url = new URL(apiHost);
813
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
814
+ throw new Error(`Invalid API host protocol - ${url.protocol}`);
815
+ }
816
+ }
817
+ };
818
+
819
+ // src/nodes/http/index.ts
820
+ var HTTPExecutor = class {
821
+ constructor() {
822
+ this.type = FlowGramNode.HTTP;
823
+ }
824
+ async execute(context) {
825
+ const inputs = this.parseInputs(context);
826
+ const response = await this.request(inputs);
827
+ const responseHeaders = {};
828
+ response.headers.forEach((value, key) => {
829
+ responseHeaders[key] = value;
830
+ });
831
+ const responseBody = await response.text();
832
+ return {
833
+ outputs: {
834
+ headers: responseHeaders,
835
+ statusCode: response.status,
836
+ body: responseBody
837
+ }
838
+ };
839
+ }
840
+ async request(inputs) {
841
+ const { method, url, headers, params, bodyType, body, retryTimes, timeout } = inputs;
842
+ const urlWithParams = this.buildUrlWithParams(url, params);
843
+ const requestOptions = {
844
+ method,
845
+ headers: this.prepareHeaders(headers, bodyType),
846
+ signal: AbortSignal.timeout(timeout)
847
+ };
848
+ if (method !== "GET" && method !== "HEAD" && body) {
849
+ requestOptions.body = this.prepareBody(body, bodyType);
850
+ }
851
+ let lastError = null;
852
+ for (let attempt = 0; attempt <= retryTimes; attempt++) {
853
+ try {
854
+ const response = await fetch(urlWithParams, requestOptions);
855
+ return response;
856
+ } catch (error) {
857
+ lastError = error;
858
+ if (attempt < retryTimes) {
859
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
860
+ }
861
+ }
862
+ }
863
+ throw lastError || new Error("HTTP request failed after all retry attempts");
864
+ }
865
+ parseInputs(context) {
866
+ const httpNode = context.node;
867
+ const method = httpNode.data.api.method;
868
+ const urlVariable = context.runtime.state.parseTemplate(httpNode.data.api.url);
869
+ if (!urlVariable) {
870
+ throw new Error("HTTP url is required");
871
+ }
872
+ const url = urlVariable.value;
873
+ const headers = context.runtime.state.parseInputs({
874
+ values: httpNode.data.headersValues,
875
+ declare: httpNode.data.headers
876
+ });
877
+ const params = context.runtime.state.parseInputs({
878
+ values: httpNode.data.paramsValues,
879
+ declare: httpNode.data.params
880
+ });
881
+ const body = this.parseBody(context);
882
+ const retryTimes = httpNode.data.timeout.retryTimes;
883
+ const timeout = httpNode.data.timeout.timeout;
884
+ const inputs = {
885
+ method,
886
+ url,
887
+ headers,
888
+ params,
889
+ bodyType: body.bodyType,
890
+ body: body.body,
891
+ retryTimes,
892
+ timeout
893
+ };
894
+ context.snapshot.update({
895
+ inputs: JSON.parse(JSON.stringify(inputs))
896
+ });
897
+ return inputs;
898
+ }
899
+ parseBody(context) {
900
+ const httpNode = context.node;
901
+ const bodyType = httpNode.data.body.bodyType;
902
+ if (bodyType === HTTPBodyType.None) {
903
+ return {
904
+ bodyType,
905
+ body: ""
906
+ };
907
+ }
908
+ if (bodyType === HTTPBodyType.JSON) {
909
+ if (!httpNode.data.body.json) {
910
+ throw new Error("HTTP json body is required");
911
+ }
912
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
913
+ if (!jsonVariable) {
914
+ throw new Error("HTTP json body is required");
915
+ }
916
+ return {
917
+ bodyType,
918
+ body: jsonVariable.value
919
+ };
920
+ }
921
+ if (bodyType === HTTPBodyType.FormData) {
922
+ if (!httpNode.data.body.formData || !httpNode.data.body.formDataValues) {
923
+ throw new Error("HTTP form-data body is required");
924
+ }
925
+ const formData = context.runtime.state.parseInputs({
926
+ values: httpNode.data.body.formDataValues,
927
+ declare: httpNode.data.body.formData
928
+ });
929
+ return {
930
+ bodyType,
931
+ body: JSON.stringify(formData)
932
+ };
933
+ }
934
+ if (bodyType === HTTPBodyType.RawText) {
935
+ if (!httpNode.data.body.json) {
936
+ throw new Error("HTTP json body is required");
937
+ }
938
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
939
+ if (!jsonVariable) {
940
+ throw new Error("HTTP json body is required");
941
+ }
942
+ return {
943
+ bodyType,
944
+ body: jsonVariable.value
945
+ };
946
+ }
947
+ if (bodyType === HTTPBodyType.Binary) {
948
+ if (!httpNode.data.body.binary) {
949
+ throw new Error("HTTP binary body is required");
950
+ }
951
+ const binaryVariable = context.runtime.state.parseTemplate(httpNode.data.body.binary);
952
+ if (!binaryVariable) {
953
+ throw new Error("HTTP binary body is required");
954
+ }
955
+ return {
956
+ bodyType,
957
+ body: binaryVariable.value
958
+ };
959
+ }
960
+ if (bodyType === HTTPBodyType.XWwwFormUrlencoded) {
961
+ if (!httpNode.data.body.xWwwFormUrlencoded || !httpNode.data.body.xWwwFormUrlencodedValues) {
962
+ throw new Error("HTTP x-www-form-urlencoded body is required");
963
+ }
964
+ const xWwwFormUrlencoded = context.runtime.state.parseInputs({
965
+ values: httpNode.data.body.xWwwFormUrlencodedValues,
966
+ declare: httpNode.data.body.xWwwFormUrlencoded
967
+ });
968
+ return {
969
+ bodyType,
970
+ body: JSON.stringify(xWwwFormUrlencoded)
971
+ };
972
+ }
973
+ throw new Error(`HTTP invalid body type "${bodyType}"`);
974
+ }
975
+ buildUrlWithParams(url, params) {
976
+ const urlObj = new URL(url);
977
+ Object.entries(params).forEach(([key, value]) => {
978
+ if (value !== void 0 && value !== null && value !== "") {
979
+ urlObj.searchParams.set(key, value);
980
+ }
981
+ });
982
+ return urlObj.toString();
983
+ }
984
+ prepareHeaders(headers, bodyType) {
985
+ const preparedHeaders = { ...headers };
986
+ if (!preparedHeaders["Content-Type"] && !preparedHeaders["content-type"]) {
987
+ switch (bodyType) {
988
+ case HTTPBodyType.JSON:
989
+ preparedHeaders["Content-Type"] = "application/json";
990
+ break;
991
+ case HTTPBodyType.FormData:
992
+ break;
993
+ case HTTPBodyType.XWwwFormUrlencoded:
994
+ preparedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
995
+ break;
996
+ case HTTPBodyType.RawText:
997
+ preparedHeaders["Content-Type"] = "text/plain";
998
+ break;
999
+ case HTTPBodyType.Binary:
1000
+ preparedHeaders["Content-Type"] = "application/octet-stream";
1001
+ break;
1002
+ }
1003
+ }
1004
+ return preparedHeaders;
1005
+ }
1006
+ prepareBody(body, bodyType) {
1007
+ switch (bodyType) {
1008
+ case HTTPBodyType.JSON:
1009
+ return body;
1010
+ case HTTPBodyType.FormData:
1011
+ const formData = new FormData();
1012
+ try {
1013
+ const data = JSON.parse(body);
1014
+ Object.entries(data).forEach(([key, value]) => {
1015
+ formData.append(key, String(value));
1016
+ });
1017
+ } catch (error) {
1018
+ throw new Error("Invalid FormData body format");
1019
+ }
1020
+ return formData;
1021
+ case HTTPBodyType.XWwwFormUrlencoded:
1022
+ try {
1023
+ const data = JSON.parse(body);
1024
+ const params = new URLSearchParams();
1025
+ Object.entries(data).forEach(([key, value]) => {
1026
+ params.append(key, String(value));
1027
+ });
1028
+ return params.toString();
1029
+ } catch (error) {
1030
+ throw new Error("Invalid x-www-form-urlencoded body format");
1031
+ }
1032
+ case HTTPBodyType.RawText:
1033
+ case HTTPBodyType.Binary:
1034
+ default:
1035
+ return body;
837
1036
  }
838
1037
  }
839
1038
  };
@@ -873,6 +1072,19 @@ var BlockEndExecutor = class {
873
1072
  }
874
1073
  };
875
1074
 
1075
+ // src/nodes/continue/index.ts
1076
+ var ContinueExecutor = class {
1077
+ constructor() {
1078
+ this.type = FlowGramNode.Continue;
1079
+ }
1080
+ async execute(context) {
1081
+ context.runtime.cache.set("loop-continue", true);
1082
+ return {
1083
+ outputs: {}
1084
+ };
1085
+ }
1086
+ };
1087
+
876
1088
  // src/nodes/condition/index.ts
877
1089
  var import_lodash_es9 = require("lodash-es");
878
1090
 
@@ -1164,7 +1376,7 @@ var ConditionExecutor = class {
1164
1376
  `Condition left type "${condition.leftType}" has no operator "${condition.operator}"`
1165
1377
  );
1166
1378
  }
1167
- if (ruleType !== condition.rightType) {
1379
+ if (!WorkflowRuntimeType.isTypeEqual(ruleType, condition.rightType)) {
1168
1380
  return false;
1169
1381
  }
1170
1382
  return true;
@@ -1179,6 +1391,81 @@ var ConditionExecutor = class {
1179
1391
  }
1180
1392
  };
1181
1393
 
1394
+ // src/nodes/code/index.ts
1395
+ var CodeExecutor = class {
1396
+ constructor() {
1397
+ this.type = FlowGramNode.Code;
1398
+ }
1399
+ async execute(context) {
1400
+ const inputs = this.parseInputs(context);
1401
+ if (inputs.script.language === "javascript") {
1402
+ return this.javascript(inputs);
1403
+ }
1404
+ throw new Error(`Unsupported code language "${inputs.script.language}"`);
1405
+ }
1406
+ parseInputs(context) {
1407
+ const codeNode = context.node;
1408
+ const params = context.inputs;
1409
+ const { language, content } = codeNode.data.script;
1410
+ if (!content) {
1411
+ throw new Error("Code content is required");
1412
+ }
1413
+ return {
1414
+ params,
1415
+ script: {
1416
+ language,
1417
+ content
1418
+ }
1419
+ };
1420
+ }
1421
+ async javascript(inputs) {
1422
+ const { params = {}, script } = inputs;
1423
+ try {
1424
+ const executeCode = new Function(
1425
+ "params",
1426
+ `
1427
+ 'use strict';
1428
+
1429
+ ${script.content}
1430
+
1431
+ // Ensure main function exists
1432
+ if (typeof main !== 'function') {
1433
+ throw new Error('main function is required in the script');
1434
+ }
1435
+
1436
+ // Execute main function with params
1437
+ return main({ params });
1438
+ `
1439
+ );
1440
+ const timeoutPromise = new Promise((_, reject) => {
1441
+ setTimeout(() => {
1442
+ reject(new Error("Code execution timeout: exceeded 1 minute"));
1443
+ }, 1e3 * 60);
1444
+ });
1445
+ const result = await Promise.race([executeCode(params), timeoutPromise]);
1446
+ const outputs = result && typeof result === "object" && !Array.isArray(result) ? result : { result };
1447
+ return {
1448
+ outputs
1449
+ };
1450
+ } catch (error) {
1451
+ throw new Error(`Code execution failed: ${error.message}`);
1452
+ }
1453
+ }
1454
+ };
1455
+
1456
+ // src/nodes/break/index.ts
1457
+ var BreakExecutor = class {
1458
+ constructor() {
1459
+ this.type = FlowGramNode.Break;
1460
+ }
1461
+ async execute(context) {
1462
+ context.runtime.cache.set("loop-break", true);
1463
+ return {
1464
+ outputs: {}
1465
+ };
1466
+ }
1467
+ };
1468
+
1182
1469
  // src/nodes/index.ts
1183
1470
  var WorkflowRuntimeNodeExecutors = [
1184
1471
  StartExecutor,
@@ -1187,7 +1474,11 @@ var WorkflowRuntimeNodeExecutors = [
1187
1474
  ConditionExecutor,
1188
1475
  LoopExecutor,
1189
1476
  BlockStartExecutor,
1190
- BlockEndExecutor
1477
+ BlockEndExecutor,
1478
+ HTTPExecutor,
1479
+ CodeExecutor,
1480
+ BreakExecutor,
1481
+ ContinueExecutor
1191
1482
  ];
1192
1483
 
1193
1484
  // src/domain/validation/validators/cycle-detection.ts
@@ -1606,6 +1897,29 @@ var WorkflowRuntimeMessageCenter = class {
1606
1897
  }
1607
1898
  };
1608
1899
 
1900
+ // src/domain/cache/index.ts
1901
+ var WorkflowRuntimeCache = class {
1902
+ init() {
1903
+ this.map = /* @__PURE__ */ new Map();
1904
+ }
1905
+ dispose() {
1906
+ this.map.clear();
1907
+ }
1908
+ get(key) {
1909
+ return this.map.get(key);
1910
+ }
1911
+ set(key, value) {
1912
+ this.map.set(key, value);
1913
+ return this;
1914
+ }
1915
+ delete(key) {
1916
+ return this.map.delete(key);
1917
+ }
1918
+ has(key) {
1919
+ return this.map.has(key);
1920
+ }
1921
+ };
1922
+
1609
1923
  // src/domain/variable/variable-store/index.ts
1610
1924
  var import_lodash_es10 = require("lodash-es");
1611
1925
 
@@ -1831,26 +2145,10 @@ var WorkflowRuntimeState = class {
1831
2145
  getNodeInputs(node) {
1832
2146
  const inputsDeclare = node.declare.inputs;
1833
2147
  const inputsValues = node.declare.inputsValues;
1834
- if (!inputsDeclare || !inputsValues) {
1835
- return {};
1836
- }
1837
- return Object.entries(inputsValues).reduce((prev, [key, inputValue]) => {
1838
- const typeInfo = inputsDeclare.properties?.[key];
1839
- if (!typeInfo) {
1840
- return prev;
1841
- }
1842
- const expectType = typeInfo.type;
1843
- const result = this.parseValue(inputValue);
1844
- if (!result) {
1845
- return prev;
1846
- }
1847
- const { value, type } = result;
1848
- if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
1849
- return prev;
1850
- }
1851
- prev[key] = value;
1852
- return prev;
1853
- }, {});
2148
+ return this.parseInputs({
2149
+ values: inputsValues,
2150
+ declare: inputsDeclare
2151
+ });
1854
2152
  }
1855
2153
  setNodeOutputs(params) {
1856
2154
  const { node, outputs } = params;
@@ -1874,6 +2172,29 @@ var WorkflowRuntimeState = class {
1874
2172
  });
1875
2173
  });
1876
2174
  }
2175
+ parseInputs(params) {
2176
+ const { values, declare } = params;
2177
+ if (!declare || !values) {
2178
+ return {};
2179
+ }
2180
+ return Object.entries(values).reduce((prev, [key, inputValue]) => {
2181
+ const typeInfo = declare.properties?.[key];
2182
+ if (!typeInfo) {
2183
+ return prev;
2184
+ }
2185
+ const expectType = typeInfo.type;
2186
+ const result = this.parseValue(inputValue);
2187
+ if (!result) {
2188
+ return prev;
2189
+ }
2190
+ const { value, type } = result;
2191
+ if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
2192
+ return prev;
2193
+ }
2194
+ prev[key] = value;
2195
+ return prev;
2196
+ }, {});
2197
+ }
1877
2198
  parseRef(ref) {
1878
2199
  if (ref?.type !== "ref") {
1879
2200
  throw new Error(`Invalid ref value: ${ref}`);
@@ -2407,6 +2728,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2407
2728
  constructor(data) {
2408
2729
  this.subContexts = [];
2409
2730
  this.id = uuid();
2731
+ this.cache = data.cache;
2410
2732
  this.document = data.document;
2411
2733
  this.variableStore = data.variableStore;
2412
2734
  this.state = data.state;
@@ -2418,6 +2740,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2418
2740
  }
2419
2741
  init(params) {
2420
2742
  const { schema, inputs } = params;
2743
+ this.cache.init();
2421
2744
  this.document.init(schema);
2422
2745
  this.variableStore.init();
2423
2746
  this.state.init();
@@ -2432,6 +2755,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2432
2755
  subContext.dispose();
2433
2756
  });
2434
2757
  this.subContexts = [];
2758
+ this.cache.dispose();
2435
2759
  this.document.dispose();
2436
2760
  this.variableStore.dispose();
2437
2761
  this.state.dispose();
@@ -2442,10 +2766,12 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2442
2766
  this.reporter.dispose();
2443
2767
  }
2444
2768
  sub() {
2769
+ const cache = new WorkflowRuntimeCache();
2445
2770
  const variableStore = new WorkflowRuntimeVariableStore();
2446
2771
  variableStore.setParent(this.variableStore);
2447
2772
  const state = new WorkflowRuntimeState(variableStore);
2448
2773
  const contextData = {
2774
+ cache,
2449
2775
  document: this.document,
2450
2776
  ioCenter: this.ioCenter,
2451
2777
  snapshotCenter: this.snapshotCenter,
@@ -2457,11 +2783,13 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2457
2783
  };
2458
2784
  const subContext = new _WorkflowRuntimeContext(contextData);
2459
2785
  this.subContexts.push(subContext);
2786
+ subContext.cache.init();
2460
2787
  subContext.variableStore.init();
2461
2788
  subContext.state.init();
2462
2789
  return subContext;
2463
2790
  }
2464
2791
  static create() {
2792
+ const cache = new WorkflowRuntimeCache();
2465
2793
  const document = new WorkflowRuntimeDocument();
2466
2794
  const variableStore = new WorkflowRuntimeVariableStore();
2467
2795
  const state = new WorkflowRuntimeState(variableStore);
@@ -2476,6 +2804,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2476
2804
  messageCenter
2477
2805
  );
2478
2806
  return new _WorkflowRuntimeContext({
2807
+ cache,
2479
2808
  document,
2480
2809
  variableStore,
2481
2810
  state,
@@ -2533,7 +2862,8 @@ var WorkflowRuntimeEngine = class {
2533
2862
  node,
2534
2863
  inputs,
2535
2864
  runtime: context,
2536
- container: WorkflowRuntimeContainer.instance
2865
+ container: WorkflowRuntimeContainer.instance,
2866
+ snapshot
2537
2867
  });
2538
2868
  if (context.statusCenter.workflow.terminated) {
2539
2869
  return;
@@ -2614,7 +2944,13 @@ var WorkflowRuntimeEngine = class {
2614
2944
  }
2615
2945
  async executeNext(params) {
2616
2946
  const { context, node, nextNodes } = params;
2617
- if (node.type === FlowGramNode.End || node.type === FlowGramNode.BlockEnd) {
2947
+ const terminatingNodeTypes = [
2948
+ FlowGramNode.End,
2949
+ FlowGramNode.BlockEnd,
2950
+ FlowGramNode.Break,
2951
+ FlowGramNode.Continue
2952
+ ];
2953
+ if (terminatingNodeTypes.includes(node.type)) {
2618
2954
  return;
2619
2955
  }
2620
2956
  if (nextNodes.length === 0) {