callman-core 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/contract/contractValidator.d.ts.map +1 -1
  2. package/dist/contract/contractValidator.js +13 -4
  3. package/dist/contract/contractValidator.js.map +1 -1
  4. package/dist/host/db/mysqlAdapter.d.ts.map +1 -1
  5. package/dist/host/db/mysqlAdapter.js +39 -9
  6. package/dist/host/db/mysqlAdapter.js.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/request/executeRequest.d.ts +1 -1
  12. package/dist/request/executeRequest.d.ts.map +1 -1
  13. package/dist/request/executeRequest.js +8 -1
  14. package/dist/request/executeRequest.js.map +1 -1
  15. package/dist/runner/runCollection.d.ts.map +1 -1
  16. package/dist/runner/runCollection.js +8 -3
  17. package/dist/runner/runCollection.js.map +1 -1
  18. package/dist/scenario-runner/adapters/request-executor.js +12 -3
  19. package/dist/scenario-runner/adapters/request-executor.js.map +1 -1
  20. package/dist/scenario-runner/adapters/script-executor.js +1 -1
  21. package/dist/scenario-runner/adapters/script-executor.js.map +1 -1
  22. package/dist/scenario-runner/adapters/types.d.ts +1 -0
  23. package/dist/scenario-runner/adapters/types.d.ts.map +1 -1
  24. package/dist/scenario-runner/boundSnapshot.d.ts +15 -0
  25. package/dist/scenario-runner/boundSnapshot.d.ts.map +1 -0
  26. package/dist/scenario-runner/boundSnapshot.js +67 -0
  27. package/dist/scenario-runner/boundSnapshot.js.map +1 -0
  28. package/dist/scenario-runner/runScenario.d.ts +1 -1
  29. package/dist/scenario-runner/runScenario.d.ts.map +1 -1
  30. package/dist/scenario-runner/runScenario.js +42 -25
  31. package/dist/scenario-runner/runScenario.js.map +1 -1
  32. package/dist/scenario-runner/runScenarioWithDataset.d.ts.map +1 -1
  33. package/dist/scenario-runner/runScenarioWithDataset.js +21 -6
  34. package/dist/scenario-runner/runScenarioWithDataset.js.map +1 -1
  35. package/dist/scenario-runner/types.d.ts +10 -0
  36. package/dist/scenario-runner/types.d.ts.map +1 -1
  37. package/dist/script/isolated-runner.d.ts +7 -1
  38. package/dist/script/isolated-runner.d.ts.map +1 -1
  39. package/dist/script/isolated-runner.js +11 -5
  40. package/dist/script/isolated-runner.js.map +1 -1
  41. package/dist/script/worker-entry.js +62 -28
  42. package/dist/script/worker-entry.js.map +1 -1
  43. package/dist/script/worker-pool.d.ts +60 -0
  44. package/dist/script/worker-pool.d.ts.map +1 -0
  45. package/dist/script/worker-pool.js +274 -0
  46. package/dist/script/worker-pool.js.map +1 -0
  47. package/dist/types/index.d.ts +11 -0
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist-cjs/contract/contractValidator.js +13 -4
  50. package/dist-cjs/contract/contractValidator.js.map +1 -1
  51. package/dist-cjs/host/db/mysqlAdapter.js +39 -9
  52. package/dist-cjs/host/db/mysqlAdapter.js.map +1 -1
  53. package/dist-cjs/index.js +4 -1
  54. package/dist-cjs/index.js.map +1 -1
  55. package/dist-cjs/request/executeRequest.js +8 -1
  56. package/dist-cjs/request/executeRequest.js.map +1 -1
  57. package/dist-cjs/runner/runCollection.js +8 -3
  58. package/dist-cjs/runner/runCollection.js.map +1 -1
  59. package/dist-cjs/scenario-runner/adapters/request-executor.js +12 -3
  60. package/dist-cjs/scenario-runner/adapters/request-executor.js.map +1 -1
  61. package/dist-cjs/scenario-runner/adapters/script-executor.js +1 -1
  62. package/dist-cjs/scenario-runner/adapters/script-executor.js.map +1 -1
  63. package/dist-cjs/scenario-runner/boundSnapshot.js +71 -0
  64. package/dist-cjs/scenario-runner/boundSnapshot.js.map +1 -0
  65. package/dist-cjs/scenario-runner/runScenario.js +42 -25
  66. package/dist-cjs/scenario-runner/runScenario.js.map +1 -1
  67. package/dist-cjs/scenario-runner/runScenarioWithDataset.js +21 -6
  68. package/dist-cjs/scenario-runner/runScenarioWithDataset.js.map +1 -1
  69. package/dist-cjs/script/isolated-runner.js +12 -5
  70. package/dist-cjs/script/isolated-runner.js.map +1 -1
  71. package/dist-cjs/script/worker-entry.js +62 -28
  72. package/dist-cjs/script/worker-entry.js.map +1 -1
  73. package/dist-cjs/script/worker-pool.js +312 -0
  74. package/dist-cjs/script/worker-pool.js.map +1 -0
  75. package/package.json +1 -1
@@ -0,0 +1,67 @@
1
+ // Size-capped deep copy of a runtime value for event context snapshots
2
+ // (`RunScenarioOptions.eventContextMode: "bounded"`).
3
+ //
4
+ // The caps are deliberately LOOSER than the backend report mapper's persist-
5
+ // time truncation (16 KB strings / 50 array items / 50 keys / depth 6), so
6
+ // everything the mapper keeps is still present verbatim after bounding —
7
+ // the persisted report is byte-identical whether bounding ran or not. What
8
+ // changes is what stays in the HEAP while the run is in flight: a bounded
9
+ // snapshot never holds a reference to a multi-megabyte response body.
10
+ export const DEFAULT_BOUND_SNAPSHOT_LIMITS = {
11
+ maxStringLength: 32 * 1024,
12
+ maxArrayItems: 100,
13
+ maxObjectKeys: 100,
14
+ maxDepth: 8,
15
+ };
16
+ const TRUNCATED = "…[truncated]";
17
+ const boundValueAt = (value, depth, limits) => {
18
+ if (value === null || value === undefined)
19
+ return value;
20
+ switch (typeof value) {
21
+ case "string":
22
+ return value.length > limits.maxStringLength
23
+ ? `${value.slice(0, limits.maxStringLength)}${TRUNCATED}`
24
+ : value;
25
+ case "number":
26
+ case "boolean":
27
+ return value;
28
+ case "bigint":
29
+ return value.toString();
30
+ case "function":
31
+ case "symbol":
32
+ return undefined;
33
+ default:
34
+ break;
35
+ }
36
+ if (value instanceof Date)
37
+ return new Date(value.getTime());
38
+ if (depth >= limits.maxDepth)
39
+ return TRUNCATED;
40
+ if (Array.isArray(value)) {
41
+ const kept = value.slice(0, limits.maxArrayItems).map((item) => boundValueAt(item, depth + 1, limits));
42
+ return kept;
43
+ }
44
+ if (typeof value.toJSON === "function") {
45
+ return boundValueAt(value.toJSON(), depth, limits);
46
+ }
47
+ const out = {};
48
+ let count = 0;
49
+ for (const [key, entry] of Object.entries(value)) {
50
+ if (count >= limits.maxObjectKeys)
51
+ break;
52
+ const bounded = boundValueAt(entry, depth + 1, limits);
53
+ if (bounded !== undefined) {
54
+ out[key] = bounded;
55
+ count += 1;
56
+ }
57
+ }
58
+ return out;
59
+ };
60
+ /**
61
+ * Returns a bounded deep copy of `value`. `undefined` and `null` pass
62
+ * through unchanged; typed callers cast the result back to their shape —
63
+ * bounding never changes the top-level structure, only trims what is nested
64
+ * inside it.
65
+ */
66
+ export const boundSnapshot = (value, limits = DEFAULT_BOUND_SNAPSHOT_LIMITS) => (value === null || value === undefined ? value : boundValueAt(value, 0, limits));
67
+ //# sourceMappingURL=boundSnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundSnapshot.js","sourceRoot":"","sources":["../../src/scenario-runner/boundSnapshot.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,sDAAsD;AACtD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,sEAAsE;AAStE,MAAM,CAAC,MAAM,6BAA6B,GAAwB;IAChE,eAAe,EAAE,EAAE,GAAG,IAAI;IAC1B,aAAa,EAAE,GAAG;IAClB,aAAa,EAAE,GAAG;IAClB,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF,MAAM,SAAS,GAAG,cAAc,CAAC;AAEjC,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,KAAa,EAAE,MAA2B,EAAW,EAAE;IAC3F,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe;gBAC1C,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE;gBACzD,CAAC,CAAC,KAAK,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC;QACnB;YACE,MAAM;IACV,CAAC;IACD,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5D,IAAI,KAAK,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAQ,KAA8B,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACjE,OAAO,YAAY,CAAE,KAAmC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QAC5E,IAAI,KAAK,IAAI,MAAM,CAAC,aAAa;YAAE,MAAM;QACzC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QACvD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;YACnB,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,KAAQ,EACR,SAA8B,6BAA6B,EACxD,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAO,CAAC,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { RunScenarioOptions, ScenarioRunReport } from "./types.js";
2
- export declare const runScenario: ({ scenario, environment, globals, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs, }: RunScenarioOptions) => Promise<ScenarioRunReport>;
2
+ export declare const runScenario: ({ scenario, environment, globals, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs, eventContextMode, }: RunScenarioOptions) => Promise<ScenarioRunReport>;
3
3
  //# sourceMappingURL=runScenario.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runScenario.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/runScenario.ts"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAMV,kBAAkB,EAUlB,iBAAiB,EAKlB,MAAM,YAAY,CAAC;AA2uBpB,eAAO,MAAM,WAAW,GAAU,mGAQ/B,kBAAkB,KAAG,OAAO,CAAC,iBAAiB,CAs/EhD,CAAC"}
1
+ {"version":3,"file":"runScenario.d.ts","sourceRoot":"","sources":["../../src/scenario-runner/runScenario.ts"],"names":[],"mappings":"AA2DA,OAAO,KAAK,EAMV,kBAAkB,EAUlB,iBAAiB,EAKlB,MAAM,YAAY,CAAC;AAmvBpB,eAAO,MAAM,WAAW,GAAU,qHAS/B,kBAAkB,KAAG,OAAO,CAAC,iBAAiB,CAggFhD,CAAC"}
@@ -4,6 +4,7 @@ import { evaluateAssertionConfig, validateAssertionConfig, } from "./assertion.j
4
4
  import { createSubScenarioStepId, buildRuntimeStepDescriptors, DEFAULT_END_LOOP_DELAY_MS, DEFAULT_END_LOOP_ITERATIONS, flattenScenarioNodesForContext, MAX_END_LOOP_ITERATIONS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_MAX_ATTEMPTS, MAX_POLL_ATTEMPTS, DEFAULT_FOREACH_MAX_ITERATIONS, MAX_FOREACH_ITERATIONS, DEFAULT_WS_CONNECT_TIMEOUT_MS, MAX_WS_CONNECT_TIMEOUT_MS, DEFAULT_WS_EXPECT_TIMEOUT_MS, MAX_WS_EXPECT_TIMEOUT_MS, MAX_WS_WAIT_DURATION_MS, MAX_WS_STEPS, DEFAULT_WS_CLOSE_CODE, } from "./graphTraversal.js";
5
5
  import { evaluateWebsocketExpect } from "./websocket.js";
6
6
  import { executeWithScenarioNodePolicy } from "./executionPolicyRunner.js";
7
+ import { boundSnapshot } from "./boundSnapshot.js";
7
8
  import { getLegacyScenarioNodeExecutionPolicy, getScenarioNodeExecutionPolicy, supportsScenarioNodeExecutionPolicy, } from "./nodePolicy.js";
8
9
  import { buildScenarioExecutionGraph, buildScenarioTemplateValues, collectExclusiveBranchNodeIds, evaluateScenarioExpression, resolveScenarioJsonTemplateString, resolveScenarioTemplateString, } from "./templateResolver.js";
9
10
  import { executeWaitNode, ScenarioWaitNodeCancelledError, ScenarioWaitNodeValidationError, } from "./waitNodeExecutor.js";
@@ -110,19 +111,24 @@ const createInputSnapshot = (context) => ({
110
111
  result: context.redisResult ?? null,
111
112
  },
112
113
  });
113
- const createContextSnapshot = (context) => ({
114
- environment: { ...context.environment },
115
- globals: { ...context.globals },
116
- workflowContext: { ...context.workflowContext },
117
- nodeMetaByLabel: cloneNodeMetaByLabel(context.nodeMetaByLabel),
118
- workflowMeta: cloneWorkflowExecutionMeta(context.workflowMeta),
119
- responseRoot: context.responseRoot ? { ...context.responseRoot } : null,
120
- lastResponse: context.lastResponse,
121
- dbResult: context.dbResult,
122
- kafkaEvent: context.kafkaEvent,
123
- kafkaCapture: context.kafkaCapture,
124
- redisResult: context.redisResult,
125
- });
114
+ const createContextSnapshot = (context, mode = "full") => {
115
+ // "bounded": the payload-bearing slots are size-capped copies instead of
116
+ // references, so the event log never pins a node's full response body.
117
+ const bound = mode === "bounded" ? boundSnapshot : (value) => value;
118
+ return {
119
+ environment: { ...context.environment },
120
+ globals: { ...context.globals },
121
+ workflowContext: bound({ ...context.workflowContext }),
122
+ nodeMetaByLabel: cloneNodeMetaByLabel(context.nodeMetaByLabel),
123
+ workflowMeta: cloneWorkflowExecutionMeta(context.workflowMeta),
124
+ responseRoot: context.responseRoot ? bound({ ...context.responseRoot }) : null,
125
+ lastResponse: bound(context.lastResponse),
126
+ dbResult: bound(context.dbResult),
127
+ kafkaEvent: bound(context.kafkaEvent),
128
+ kafkaCapture: bound(context.kafkaCapture),
129
+ redisResult: bound(context.redisResult),
130
+ };
131
+ };
126
132
  const buildKafkaEventValue = (message) => {
127
133
  const fallbackValue = {
128
134
  value: message.valueJson ?? message.valueText,
@@ -311,6 +317,7 @@ const createStepExecutionRecord = ({ stepId, stepType, title, }) => ({
311
317
  errors: [],
312
318
  durationMs: null,
313
319
  statusCode: null,
320
+ statusExpectation: null,
314
321
  errorMessage: null,
315
322
  response: null,
316
323
  testResults: [],
@@ -487,6 +494,7 @@ const updateRecordFromEvent = (records, event) => {
487
494
  errors: event.errors ?? current.errors,
488
495
  durationMs: event.durationMs ?? current.durationMs,
489
496
  statusCode: event.statusCode ?? current.statusCode,
497
+ statusExpectation: event.statusExpectation ?? current.statusExpectation,
490
498
  errorMessage: event.type === "step:fail"
491
499
  ? event.errorMessage
492
500
  : current.errorMessage,
@@ -555,7 +563,7 @@ const callHandler = async (handlers, event) => {
555
563
  return;
556
564
  }
557
565
  };
558
- export const runScenario = async ({ scenario, environment = {}, globals = {}, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs = DEFAULT_MIN_NODE_VISUAL_STATE_MS, }) => {
566
+ export const runScenario = async ({ scenario, environment = {}, globals = {}, runtimeAdapters, eventHandlers, signal, minNodeVisualStateMs = DEFAULT_MIN_NODE_VISUAL_STATE_MS, eventContextMode = "full", }) => {
559
567
  const runId = createRunId();
560
568
  const startedAtMs = Date.now();
561
569
  const startedAt = new Date(startedAtMs).toISOString();
@@ -566,6 +574,9 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
566
574
  const localStatuses = new Map(runtimeStepIds.map((stepId) => [stepId, "idle"]));
567
575
  const records = new Map();
568
576
  const events = [];
577
+ // Every emitted event carries a context snapshot; `runtimeContext` is
578
+ // assigned just below and is always initialised before the first emit.
579
+ const snapshotContext = () => createContextSnapshot(runtimeContext, eventContextMode);
569
580
  const initialWorkflowMeta = createInitialWorkflowMeta({
570
581
  startedAt,
571
582
  totalNodes: runtimeStepIds.length,
@@ -861,6 +872,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
861
872
  const executionStartedAtMs = Date.now();
862
873
  let inputData = createInputSnapshot(runtimeContext);
863
874
  let statusCode = null;
875
+ let statusExpectation = null;
864
876
  let response = null;
865
877
  let outputData = null;
866
878
  let testResults = [];
@@ -974,7 +986,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
974
986
  runId,
975
987
  stepId,
976
988
  node,
977
- context: createContextSnapshot(runtimeContext),
989
+ context: snapshotContext(),
978
990
  kafkaCapturePlans,
979
991
  captureFromTimestampMs: startedAtMs,
980
992
  signal,
@@ -988,6 +1000,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
988
1000
  runtimeContext.responseRoot = requestResult.responseRoot;
989
1001
  response = requestResult.response;
990
1002
  statusCode = requestResult.response.status;
1003
+ statusExpectation = requestResult.statusExpectation ?? null;
991
1004
  outputData = requestResult.outputData;
992
1005
  testResults = requestResult.testResults;
993
1006
  contractResults = requestResult.contractResults;
@@ -1034,7 +1047,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1034
1047
  },
1035
1048
  }
1036
1049
  : {}),
1037
- context: createContextSnapshot(runtimeContext),
1050
+ context: snapshotContext(),
1038
1051
  signal,
1039
1052
  });
1040
1053
  runtimeContext.dbResult = dbResult.dbResult;
@@ -1067,7 +1080,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1067
1080
  runId,
1068
1081
  stepId,
1069
1082
  node,
1070
- context: createContextSnapshot(runtimeContext),
1083
+ context: snapshotContext(),
1071
1084
  signal,
1072
1085
  });
1073
1086
  scriptSource = scriptResult.scriptSource;
@@ -1091,7 +1104,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1091
1104
  stepId,
1092
1105
  node,
1093
1106
  resolvedCommands,
1094
- context: createContextSnapshot(runtimeContext),
1107
+ context: snapshotContext(),
1095
1108
  signal,
1096
1109
  });
1097
1110
  runtimeContext.redisResult = redisResult.redisResult;
@@ -1143,7 +1156,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1143
1156
  position: node.position,
1144
1157
  data: { label: node.data.label, config: pollConfig.request },
1145
1158
  },
1146
- context: createContextSnapshot(runtimeContext),
1159
+ context: snapshotContext(),
1147
1160
  kafkaCapturePlans: [],
1148
1161
  captureFromTimestampMs: startedAtMs,
1149
1162
  signal,
@@ -1157,6 +1170,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1157
1170
  runtimeContext.responseRoot = tickResult.responseRoot;
1158
1171
  response = tickResult.response;
1159
1172
  statusCode = tickResult.response.status;
1173
+ statusExpectation = tickResult.statusExpectation ?? null;
1160
1174
  testResults = tickResult.testResults;
1161
1175
  contractResults = tickResult.contractResults;
1162
1176
  contractSummary = tickResult.contractSummary;
@@ -1182,7 +1196,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1182
1196
  },
1183
1197
  resolvedQuery,
1184
1198
  resolvedParams,
1185
- context: createContextSnapshot(runtimeContext),
1199
+ context: snapshotContext(),
1186
1200
  signal,
1187
1201
  });
1188
1202
  runtimeContext.dbResult = tickResult.dbResult;
@@ -1205,7 +1219,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1205
1219
  data: { label: node.data.label, config: pollConfig.redis },
1206
1220
  },
1207
1221
  resolvedCommands,
1208
- context: createContextSnapshot(runtimeContext),
1222
+ context: snapshotContext(),
1209
1223
  signal,
1210
1224
  });
1211
1225
  runtimeContext.redisResult = tickResult.redisResult;
@@ -1223,7 +1237,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1223
1237
  position: node.position,
1224
1238
  data: { label: node.data.label, config: pollConfig.script },
1225
1239
  },
1226
- context: createContextSnapshot(runtimeContext),
1240
+ context: snapshotContext(),
1227
1241
  signal,
1228
1242
  });
1229
1243
  scriptSource = tickResult.scriptSource;
@@ -1346,7 +1360,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1346
1360
  runId,
1347
1361
  stepId,
1348
1362
  node,
1349
- context: createContextSnapshot(runtimeContext),
1363
+ context: snapshotContext(),
1350
1364
  deliveryRequest,
1351
1365
  signal,
1352
1366
  });
@@ -1642,7 +1656,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
1642
1656
  runId,
1643
1657
  stepId,
1644
1658
  node,
1645
- context: createContextSnapshot(runtimeContext),
1659
+ context: snapshotContext(),
1646
1660
  signal,
1647
1661
  });
1648
1662
  throwIfStopped(signal);
@@ -2212,6 +2226,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2212
2226
  errorMessage: policyResult.finalErrorMessage ?? "Scenario node failed",
2213
2227
  durationMs,
2214
2228
  statusCode,
2229
+ statusExpectation,
2215
2230
  response,
2216
2231
  testResults,
2217
2232
  contractResults,
@@ -2294,6 +2309,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2294
2309
  "Scenario node failed but execution continued.",
2295
2310
  durationMs,
2296
2311
  statusCode,
2312
+ statusExpectation,
2297
2313
  response,
2298
2314
  testResults,
2299
2315
  contractResults,
@@ -2371,6 +2387,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2371
2387
  nodeMeta,
2372
2388
  durationMs,
2373
2389
  statusCode,
2390
+ statusExpectation,
2374
2391
  response,
2375
2392
  testResults,
2376
2393
  contractResults,
@@ -2631,7 +2648,7 @@ export const runScenario = async ({ scenario, environment = {}, globals = {}, ru
2631
2648
  durationMs: Date.now() - startedAtMs,
2632
2649
  totalSteps: runtimeStepIds.length,
2633
2650
  completedSteps: Array.from(localStatuses.values()).filter(isTerminalStatus).length,
2634
- context: createContextSnapshot(runtimeContext),
2651
+ context: snapshotContext(),
2635
2652
  workflowMeta: cloneWorkflowExecutionMeta(finalWorkflowMeta),
2636
2653
  nodeExecutions: runtimeStepIds.map((stepId) => {
2637
2654
  const descriptor = runtimeStepDescriptors.find((entry) => entry.stepId === stepId);