@scenarist/core 0.1.2 → 0.1.3

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 (62) hide show
  1. package/README.md +179 -94
  2. package/dist/adapters/in-memory-registry.d.ts +2 -2
  3. package/dist/adapters/in-memory-sequence-tracker.d.ts +2 -2
  4. package/dist/adapters/in-memory-sequence-tracker.d.ts.map +1 -1
  5. package/dist/adapters/in-memory-sequence-tracker.js +5 -5
  6. package/dist/adapters/in-memory-state-manager.d.ts +1 -1
  7. package/dist/adapters/in-memory-state-manager.d.ts.map +1 -1
  8. package/dist/adapters/in-memory-state-manager.js +34 -14
  9. package/dist/adapters/in-memory-store.d.ts +2 -2
  10. package/dist/adapters/index.d.ts +4 -4
  11. package/dist/adapters/index.d.ts.map +1 -1
  12. package/dist/adapters/index.js +4 -4
  13. package/dist/constants/headers.js +1 -1
  14. package/dist/constants/index.d.ts +1 -1
  15. package/dist/constants/index.js +1 -1
  16. package/dist/contracts/framework-adapter.d.ts +17 -3
  17. package/dist/contracts/framework-adapter.d.ts.map +1 -1
  18. package/dist/contracts/index.d.ts +1 -1
  19. package/dist/domain/config-builder.d.ts +1 -1
  20. package/dist/domain/config-builder.d.ts.map +1 -1
  21. package/dist/domain/config-builder.js +4 -4
  22. package/dist/domain/index.d.ts +4 -4
  23. package/dist/domain/index.js +4 -4
  24. package/dist/domain/path-extraction.d.ts +1 -1
  25. package/dist/domain/path-extraction.d.ts.map +1 -1
  26. package/dist/domain/path-extraction.js +24 -9
  27. package/dist/domain/regex-matching.d.ts +1 -1
  28. package/dist/domain/regex-matching.js +1 -1
  29. package/dist/domain/response-selector.d.ts.map +1 -1
  30. package/dist/domain/response-selector.js +10 -5
  31. package/dist/domain/scenario-manager.d.ts.map +1 -1
  32. package/dist/domain/scenario-manager.js +3 -3
  33. package/dist/domain/template-replacement.d.ts.map +1 -1
  34. package/dist/domain/template-replacement.js +7 -7
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.js +4 -4
  37. package/dist/ports/driven/scenario-registry.d.ts +1 -1
  38. package/dist/ports/driven/scenario-store.d.ts +1 -1
  39. package/dist/ports/driven/sequence-tracker.d.ts +1 -1
  40. package/dist/ports/driving/scenario-manager.d.ts +1 -1
  41. package/dist/ports/driving/scenario-manager.d.ts.map +1 -1
  42. package/dist/ports/index.d.ts +7 -7
  43. package/dist/ports/index.d.ts.map +1 -1
  44. package/dist/schemas/index.d.ts +4 -4
  45. package/dist/schemas/index.d.ts.map +1 -1
  46. package/dist/schemas/index.js +4 -4
  47. package/dist/schemas/match-criteria.d.ts +1 -1
  48. package/dist/schemas/match-criteria.d.ts.map +1 -1
  49. package/dist/schemas/match-criteria.js +5 -5
  50. package/dist/schemas/scenario-definition.d.ts +1 -1
  51. package/dist/schemas/scenario-definition.d.ts.map +1 -1
  52. package/dist/schemas/scenario-definition.js +27 -9
  53. package/dist/schemas/scenario-requests.d.ts +1 -1
  54. package/dist/schemas/scenario-requests.js +2 -2
  55. package/dist/schemas/scenarios-object.d.ts +1 -1
  56. package/dist/schemas/scenarios-object.js +3 -3
  57. package/dist/types/config.d.ts +2 -2
  58. package/dist/types/config.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +3 -3
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/types/scenario.d.ts +2 -2
  62. package/package.json +1 -2
@@ -1,5 +1,5 @@
1
- import type { ScenarioRegistry } from '../ports/index.js';
2
- import type { ScenaristScenario } from '../types/index.js';
1
+ import type { ScenarioRegistry } from "../ports/index.js";
2
+ import type { ScenaristScenario } from "../types/index.js";
3
3
  /**
4
4
  * In-memory implementation of ScenarioRegistry using a Map.
5
5
  * Suitable for single-process testing and development.
@@ -1,4 +1,4 @@
1
- import type { SequenceTracker, SequencePosition } from '../ports/driven/sequence-tracker.js';
1
+ import type { SequenceTracker, SequencePosition } from "../ports/driven/sequence-tracker.js";
2
2
  /**
3
3
  * In-memory implementation of SequenceTracker.
4
4
  *
@@ -17,7 +17,7 @@ export declare class InMemorySequenceTracker implements SequenceTracker {
17
17
  */
18
18
  private getKey;
19
19
  getPosition(testId: string, scenarioId: string, mockIndex: number): SequencePosition;
20
- advance(testId: string, scenarioId: string, mockIndex: number, totalResponses: number, repeatMode: 'last' | 'cycle' | 'none'): void;
20
+ advance(testId: string, scenarioId: string, mockIndex: number, totalResponses: number, repeatMode: "last" | "cycle" | "none"): void;
21
21
  reset(testId: string): void;
22
22
  }
23
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-sequence-tracker.d.ts","sourceRoot":"","sources":["../../src/adapters/in-memory-sequence-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;GAQG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;;IAM1D;;;OAGG;IACH,OAAO,CAAC,MAAM;IAId,WAAW,CACT,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,gBAAgB;IAYnB,OAAO,CACL,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GACpC,IAAI;IA0CP,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAM5B;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,QAAO,eAEhD,CAAC"}
1
+ {"version":3,"file":"in-memory-sequence-tracker.d.ts","sourceRoot":"","sources":["../../src/adapters/in-memory-sequence-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;GAQG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;;IAM1D;;;OAGG;IACH,OAAO,CAAC,MAAM;IAQd,WAAW,CACT,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,gBAAgB;IAYnB,OAAO,CACL,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GACpC,IAAI;IA0CP,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAM5B;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,QAAO,eAEhD,CAAC"}
@@ -35,21 +35,21 @@ export class InMemorySequenceTracker {
35
35
  // Handle different repeat modes
36
36
  if (nextPosition >= totalResponses) {
37
37
  switch (repeatMode) {
38
- case 'last':
38
+ case "last":
39
39
  // Stay at last position (don't increment beyond last)
40
40
  this.positions.set(key, {
41
41
  position: totalResponses - 1,
42
42
  exhausted: false,
43
43
  });
44
44
  break;
45
- case 'cycle':
45
+ case "cycle":
46
46
  // Wrap back to first position
47
47
  this.positions.set(key, {
48
48
  position: 0,
49
49
  exhausted: false,
50
50
  });
51
51
  break;
52
- case 'none':
52
+ case "none":
53
53
  // Mark as exhausted
54
54
  this.positions.set(key, {
55
55
  position: totalResponses,
@@ -69,8 +69,8 @@ export class InMemorySequenceTracker {
69
69
  reset(testId) {
70
70
  const prefix = `${testId}:`;
71
71
  Array.from(this.positions.keys())
72
- .filter(key => key.startsWith(prefix))
73
- .forEach(key => this.positions.delete(key));
72
+ .filter((key) => key.startsWith(prefix))
73
+ .forEach((key) => this.positions.delete(key));
74
74
  }
75
75
  }
76
76
  /**
@@ -1,4 +1,4 @@
1
- import type { StateManager } from '../ports/driven/state-manager.js';
1
+ import type { StateManager } from "../ports/driven/state-manager.js";
2
2
  /**
3
3
  * In-memory implementation of StateManager port.
4
4
  * Fast, single-process state storage for stateful mocks.
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-state-manager.d.ts","sourceRoot":"","sources":["../../src/adapters/in-memory-state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAErE;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;IAEtE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAUzC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAyBtD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI/C,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,cAAc;CAmBvB;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,YAE7C,CAAC"}
1
+ {"version":3,"file":"in-memory-state-manager.d.ts","sourceRoot":"","sources":["../../src/adapters/in-memory-state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAMrE;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;IAEtE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAUzC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAyBtD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI/C,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,cAAc;IAwCtB,OAAO,CAAC,cAAc;CA4BvB;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,YAE7C,CAAC"}
@@ -1,3 +1,5 @@
1
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
2
+ const isDangerousKey = (key) => DANGEROUS_KEYS.has(key);
1
3
  /**
2
4
  * In-memory implementation of StateManager port.
3
5
  * Fast, single-process state storage for stateful mocks.
@@ -12,20 +14,20 @@ export class InMemoryStateManager {
12
14
  if (!testState) {
13
15
  return undefined;
14
16
  }
15
- const path = key.split('.');
17
+ const path = key.split(".");
16
18
  return this.getNestedValue(testState, path);
17
19
  }
18
20
  set(testId, key, value) {
19
21
  const testState = this.getOrCreateTestState(testId);
20
22
  // Guard: Normal set (no array syntax)
21
- if (!key.endsWith('[]')) {
22
- const path = key.split('.');
23
+ if (!key.endsWith("[]")) {
24
+ const path = key.split(".");
23
25
  this.setNestedValue(testState, path, value);
24
26
  return;
25
27
  }
26
28
  // Array append syntax
27
29
  const actualKey = key.slice(0, -2);
28
- const path = actualKey.split('.');
30
+ const path = actualKey.split(".");
29
31
  const currentValue = this.get(testId, actualKey);
30
32
  // Guard: If current value is already an array, append immutably
31
33
  if (Array.isArray(currentValue)) {
@@ -52,32 +54,50 @@ export class InMemoryStateManager {
52
54
  setNestedValue(obj, path, value) {
53
55
  const key = path[0];
54
56
  // Guard: Prevent prototype pollution attacks
55
- if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
57
+ if (isDangerousKey(key)) {
56
58
  return;
57
59
  }
58
60
  if (path.length === 1) {
59
- obj[key] = value;
61
+ Object.defineProperty(obj, key, {
62
+ value,
63
+ writable: true,
64
+ enumerable: true,
65
+ configurable: true,
66
+ });
60
67
  return;
61
68
  }
62
- if (typeof obj[key] !== 'object' || obj[key] === null || Array.isArray(obj[key])) {
63
- obj[key] = {};
69
+ const existingValue = Object.hasOwn(obj, key) ? obj[key] : undefined;
70
+ if (typeof existingValue !== "object" ||
71
+ existingValue === null ||
72
+ Array.isArray(existingValue)) {
73
+ Object.defineProperty(obj, key, {
74
+ value: {},
75
+ writable: true,
76
+ enumerable: true,
77
+ configurable: true,
78
+ });
64
79
  }
65
- this.setNestedValue(obj[key], path.slice(1), value);
80
+ const nested = obj[key];
81
+ this.setNestedValue(nested, path.slice(1), value);
66
82
  }
67
83
  getNestedValue(obj, path) {
68
84
  const key = path[0];
69
85
  // Guard: Prevent prototype pollution attacks
70
- if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
86
+ if (isDangerousKey(key)) {
87
+ return undefined;
88
+ }
89
+ // Guard: Only access own properties, not inherited ones
90
+ if (!Object.hasOwn(obj, key)) {
71
91
  return undefined;
72
92
  }
93
+ const value = obj[key];
73
94
  if (path.length === 1) {
74
- return obj[key];
95
+ return value;
75
96
  }
76
- const nested = obj[key];
77
- if (typeof nested !== 'object' || nested === null || Array.isArray(nested)) {
97
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
78
98
  return undefined;
79
99
  }
80
- return this.getNestedValue(nested, path.slice(1));
100
+ return this.getNestedValue(value, path.slice(1));
81
101
  }
82
102
  }
83
103
  /**
@@ -1,5 +1,5 @@
1
- import type { ScenarioStore } from '../ports/index.js';
2
- import type { ActiveScenario } from '../types/index.js';
1
+ import type { ScenarioStore } from "../ports/index.js";
2
+ import type { ActiveScenario } from "../types/index.js";
3
3
  /**
4
4
  * In-memory implementation of ScenarioStore using a Map.
5
5
  * Suitable for single-process testing.
@@ -1,5 +1,5 @@
1
- export { InMemoryScenarioRegistry } from './in-memory-registry.js';
2
- export { InMemoryScenarioStore } from './in-memory-store.js';
3
- export { InMemorySequenceTracker, createInMemorySequenceTracker } from './in-memory-sequence-tracker.js';
4
- export { InMemoryStateManager, createInMemoryStateManager } from './in-memory-state-manager.js';
1
+ export { InMemoryScenarioRegistry } from "./in-memory-registry.js";
2
+ export { InMemoryScenarioStore } from "./in-memory-store.js";
3
+ export { InMemorySequenceTracker, createInMemorySequenceTracker, } from "./in-memory-sequence-tracker.js";
4
+ export { InMemoryStateManager, createInMemoryStateManager, } from "./in-memory-state-manager.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,4 @@
1
- export { InMemoryScenarioRegistry } from './in-memory-registry.js';
2
- export { InMemoryScenarioStore } from './in-memory-store.js';
3
- export { InMemorySequenceTracker, createInMemorySequenceTracker } from './in-memory-sequence-tracker.js';
4
- export { InMemoryStateManager, createInMemoryStateManager } from './in-memory-state-manager.js';
1
+ export { InMemoryScenarioRegistry } from "./in-memory-registry.js";
2
+ export { InMemoryScenarioStore } from "./in-memory-store.js";
3
+ export { InMemorySequenceTracker, createInMemorySequenceTracker, } from "./in-memory-sequence-tracker.js";
4
+ export { InMemoryStateManager, createInMemoryStateManager, } from "./in-memory-state-manager.js";
@@ -6,4 +6,4 @@
6
6
  *
7
7
  * This is a fixed constant - not configurable by users.
8
8
  */
9
- export const SCENARIST_TEST_ID_HEADER = 'x-scenarist-test-id';
9
+ export const SCENARIST_TEST_ID_HEADER = "x-scenarist-test-id";
@@ -1,2 +1,2 @@
1
- export { SCENARIST_TEST_ID_HEADER } from './headers.js';
1
+ export { SCENARIST_TEST_ID_HEADER } from "./headers.js";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export { SCENARIST_TEST_ID_HEADER } from './headers.js';
1
+ export { SCENARIST_TEST_ID_HEADER } from "./headers.js";
@@ -1,6 +1,8 @@
1
- import type { ScenaristScenario, ActiveScenario, ScenaristResult, ScenaristConfigInput, ScenaristConfig, ScenaristScenarios, ScenarioIds } from '../types/index.js';
2
- import type { ScenarioRegistry } from '../ports/driven/scenario-registry.js';
3
- import type { ScenarioStore } from '../ports/driven/scenario-store.js';
1
+ import type { ScenaristScenario, ActiveScenario, ScenaristResult, ScenaristConfigInput, ScenaristConfig, ScenaristScenarios, ScenarioIds } from "../types/index.js";
2
+ import type { ScenarioRegistry } from "../ports/driven/scenario-registry.js";
3
+ import type { ScenarioStore } from "../ports/driven/scenario-store.js";
4
+ import type { StateManager } from "../ports/driven/state-manager.js";
5
+ import type { SequenceTracker } from "../ports/driven/sequence-tracker.js";
4
6
  /**
5
7
  * Base configuration options that all framework adapters must support.
6
8
  *
@@ -35,6 +37,18 @@ export type BaseAdapterOptions<T extends ScenaristScenarios = ScenaristScenarios
35
37
  * If not provided, InMemoryScenarioStore will be used.
36
38
  */
37
39
  readonly store?: ScenarioStore;
40
+ /**
41
+ * Custom state manager implementation.
42
+ *
43
+ * If not provided, an in-memory state manager will be used.
44
+ */
45
+ readonly stateManager?: StateManager;
46
+ /**
47
+ * Custom sequence tracker implementation.
48
+ *
49
+ * If not provided, an in-memory sequence tracker will be used.
50
+ */
51
+ readonly sequenceTracker?: SequenceTracker;
38
52
  };
39
53
  /**
40
54
  * The contract that all Scenarist adapters must satisfy.
@@ -1 +1 @@
1
- {"version":3,"file":"framework-adapter.d.ts","sourceRoot":"","sources":["../../src/contracts/framework-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,IAC9E,oBAAoB,CAAC,CAAC,CAAC,GAAG;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAErC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,CAC1B,WAAW,GAAG,OAAO,EACrB,UAAU,SAAS,kBAAkB,GAAG,kBAAkB,IACxD;IACF;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IAEjC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,CACvB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,KAChC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAElC;;OAEG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;IAE3E;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,CACxB,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,KAChC,iBAAiB,GAAG,SAAS,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAE/D;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC,CAAC"}
1
+ {"version":3,"file":"framework-adapter.d.ts","sourceRoot":"","sources":["../../src/contracts/framework-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,kBAAkB,CAC5B,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,IAC/C,oBAAoB,CAAC,CAAC,CAAC,GAAG;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAErC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAE/B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAErC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,CAC1B,WAAW,GAAG,OAAO,EACrB,UAAU,SAAS,kBAAkB,GAAG,kBAAkB,IACxD;IACF;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IAEjC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,CACvB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,KAChC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAElC;;OAEG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;IAE3E;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,CACxB,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC,KAChC,iBAAiB,GAAG,SAAS,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAE/D;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC,CAAC"}
@@ -1,2 +1,2 @@
1
- export type { ScenaristAdapter, BaseAdapterOptions, } from './framework-adapter.js';
1
+ export type { ScenaristAdapter, BaseAdapterOptions, } from "./framework-adapter.js";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,4 @@
1
- import type { ScenaristConfig, ScenaristConfigInput, ScenaristScenarios } from '../types/index.js';
1
+ import type { ScenaristConfig, ScenaristConfigInput, ScenaristScenarios } from "../types/index.js";
2
2
  /**
3
3
  * Build a complete config from partial user input.
4
4
  * Applies sensible defaults for missing values.
@@ -1 +1 @@
1
- {"version":3,"file":"config-builder.d.ts","sourceRoot":"","sources":["../../src/domain/config-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGnG;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,kBAAkB,EACtD,OAAO,oBAAoB,CAAC,CAAC,CAAC,KAC7B,eAaF,CAAC"}
1
+ {"version":3,"file":"config-builder.d.ts","sourceRoot":"","sources":["../../src/domain/config-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAG3B;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,kBAAkB,EACtD,OAAO,oBAAoB,CAAC,CAAC,CAAC,KAC7B,eAaF,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ScenariosObjectSchema } from '../schemas/index.js';
1
+ import { ScenariosObjectSchema } from "../schemas/index.js";
2
2
  /**
3
3
  * Build a complete config from partial user input.
4
4
  * Applies sensible defaults for missing values.
@@ -12,9 +12,9 @@ export const buildConfig = (input) => {
12
12
  enabled: input.enabled,
13
13
  strictMode: input.strictMode ?? false,
14
14
  endpoints: {
15
- setScenario: input.endpoints?.setScenario ?? '/__scenario__',
16
- getScenario: input.endpoints?.getScenario ?? '/__scenario__',
15
+ setScenario: input.endpoints?.setScenario ?? "/__scenario__",
16
+ getScenario: input.endpoints?.getScenario ?? "/__scenario__",
17
17
  },
18
- defaultTestId: input.defaultTestId ?? 'default-test',
18
+ defaultTestId: input.defaultTestId ?? "default-test",
19
19
  };
20
20
  };
@@ -1,5 +1,5 @@
1
- export { createScenarioManager } from './scenario-manager.js';
2
- export { buildConfig } from './config-builder.js';
3
- export { createResponseSelector } from './response-selector.js';
4
- export { matchesRegex } from './regex-matching.js';
1
+ export { createScenarioManager } from "./scenario-manager.js";
2
+ export { buildConfig } from "./config-builder.js";
3
+ export { createResponseSelector } from "./response-selector.js";
4
+ export { matchesRegex } from "./regex-matching.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,4 @@
1
- export { createScenarioManager } from './scenario-manager.js';
2
- export { buildConfig } from './config-builder.js';
3
- export { createResponseSelector } from './response-selector.js';
4
- export { matchesRegex } from './regex-matching.js';
1
+ export { createScenarioManager } from "./scenario-manager.js";
2
+ export { buildConfig } from "./config-builder.js";
3
+ export { createResponseSelector } from "./response-selector.js";
4
+ export { matchesRegex } from "./regex-matching.js";
@@ -1,4 +1,4 @@
1
- import type { HttpRequestContext } from '../types/scenario.js';
1
+ import type { HttpRequestContext } from "../types/scenario.js";
2
2
  /**
3
3
  * Extracts a value from HttpRequestContext based on a path expression.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"path-extraction.d.ts","sourceRoot":"","sources":["../../src/domain/path-extraction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,kBAAkB,EAAE,MAAM,MAAM,KAAG,OA+B3E,CAAC"}
1
+ {"version":3,"file":"path-extraction.d.ts","sourceRoot":"","sources":["../../src/domain/path-extraction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAM/D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,kBAAkB,EAC3B,MAAM,MAAM,KACX,OAyBF,CAAC"}
@@ -1,3 +1,5 @@
1
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
2
+ const isDangerousKey = (key) => DANGEROUS_KEYS.has(key);
1
3
  /**
2
4
  * Extracts a value from HttpRequestContext based on a path expression.
3
5
  *
@@ -13,29 +15,34 @@
13
15
  * @returns Extracted value, or undefined if path not found
14
16
  */
15
17
  export const extractFromPath = (context, path) => {
16
- const segments = path.split('.');
18
+ const segments = path.split(".");
17
19
  // Guard: Need at least 2 segments (prefix.field)
18
20
  if (segments.length < 2) {
19
21
  return undefined;
20
22
  }
21
23
  const prefix = segments[0];
22
24
  // Guard: Must be valid prefix
23
- if (prefix !== 'body' && prefix !== 'headers' && prefix !== 'query') {
25
+ if (prefix !== "body" && prefix !== "headers" && prefix !== "query") {
24
26
  return undefined;
25
27
  }
26
28
  const remainingPath = segments.slice(1);
27
- const sourceMap = {
28
- body: context.body,
29
- headers: context.headers,
30
- query: context.query,
31
- };
32
- const source = sourceMap[prefix];
29
+ const source = getSourceForPrefix(context, prefix);
33
30
  // Guard: Source must exist
34
31
  if (source === undefined || source === null) {
35
32
  return undefined;
36
33
  }
37
34
  return traversePath(source, remainingPath);
38
35
  };
36
+ const getSourceForPrefix = (context, prefix) => {
37
+ switch (prefix) {
38
+ case "body":
39
+ return context.body;
40
+ case "headers":
41
+ return context.headers;
42
+ case "query":
43
+ return context.query;
44
+ }
45
+ };
39
46
  /**
40
47
  * Traverses a nested object path.
41
48
  *
@@ -49,10 +56,18 @@ const traversePath = (obj, path) => {
49
56
  return obj;
50
57
  }
51
58
  // Guard: Can only traverse objects
52
- if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
59
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
53
60
  return undefined;
54
61
  }
55
62
  const key = path[0];
63
+ // Guard: Prevent prototype pollution attacks
64
+ if (isDangerousKey(key)) {
65
+ return undefined;
66
+ }
67
+ // Guard: Only access own properties, not inherited ones
68
+ if (!Object.hasOwn(obj, key)) {
69
+ return undefined;
70
+ }
56
71
  const record = obj;
57
72
  const value = record[key];
58
73
  // Recursively traverse remaining path
@@ -1,4 +1,4 @@
1
- import type { SerializedRegex } from '../schemas/match-criteria.js';
1
+ import type { SerializedRegex } from "../schemas/match-criteria.js";
2
2
  /**
3
3
  * Match a value against a regex pattern.
4
4
  *
@@ -21,7 +21,7 @@ export const matchesRegex = (value, pattern) => {
21
21
  return regex.test(value);
22
22
  }
23
23
  catch (error) {
24
- console.error('Regex matching error:', error);
24
+ console.error("Regex matching error:", error);
25
25
  return false;
26
26
  }
27
27
  };
@@ -1 +1 @@
1
- {"version":3,"file":"response-selector.d.ts","sourceRoot":"","sources":["../../src/domain/response-selector.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAazF;;GAEG;AACH,KAAK,6BAA6B,GAAG;IACnC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAS,6BAAkC,KAC1C,gBAwHF,CAAC"}
1
+ {"version":3,"file":"response-selector.d.ts","sourceRoot":"","sources":["../../src/domain/response-selector.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACb,MAAM,mBAAmB,CAAC;AAa3B;;GAEG;AACH,KAAK,6BAA6B,GAAG;IACnC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAS,6BAAkC,KAC1C,gBAiIF,CAAC"}
@@ -41,7 +41,8 @@ export const createResponseSelector = (options = {}) => {
41
41
  if (matchesCriteria(context, mock.match)) {
42
42
  // Match criteria always have higher priority than fallbacks
43
43
  // Base specificity ensures even 1 field beats any fallback
44
- const specificity = SPECIFICITY_RANGES.MATCH_CRITERIA_BASE + calculateSpecificity(mock.match);
44
+ const specificity = SPECIFICITY_RANGES.MATCH_CRITERIA_BASE +
45
+ calculateSpecificity(mock.match);
45
46
  // Keep this mock if it's more specific than current best
46
47
  // (or if no best match yet)
47
48
  if (!bestMatch || specificity > bestMatch.specificity) {
@@ -61,7 +62,11 @@ export const createResponseSelector = (options = {}) => {
61
62
  // For equal specificity fallbacks, last wins
62
63
  // This allows active scenario mocks to override default mocks
63
64
  // Applies to both simple fallbacks (0) and sequence fallbacks (1)
64
- bestMatch = { mockWithParams, mockIndex, specificity: fallbackSpecificity };
65
+ bestMatch = {
66
+ mockWithParams,
67
+ mockIndex,
68
+ specificity: fallbackSpecificity,
69
+ };
65
70
  }
66
71
  }
67
72
  // Return the best matching mock
@@ -126,7 +131,7 @@ const selectResponseFromMock = (testId, scenarioId, mockIndex, mock, sequenceTra
126
131
  // so position should always be valid here
127
132
  const response = mock.sequence.responses[position];
128
133
  // Advance position for next call
129
- const repeatMode = mock.sequence.repeat || 'last';
134
+ const repeatMode = mock.sequence.repeat || "last";
130
135
  sequenceTracker.advance(testId, scenarioId, mockIndex, mock.sequence.responses.length, repeatMode);
131
136
  return response;
132
137
  }
@@ -216,7 +221,7 @@ const matchesBody = (requestBody, criteriaBody) => {
216
221
  for (const [key, criteriaValue] of Object.entries(criteriaBody)) {
217
222
  const requestValue = body[key];
218
223
  // Convert to string for matching (type coercion like headers/query)
219
- const stringValue = requestValue == null ? '' : String(requestValue);
224
+ const stringValue = requestValue == null ? "" : String(requestValue);
220
225
  if (!matchesValue(stringValue, criteriaValue)) {
221
226
  return false;
222
227
  }
@@ -272,7 +277,7 @@ const matchesValue = (requestValue, criteriaValue) => {
272
277
  return requestValue === String(criteriaValue);
273
278
  }
274
279
  if (criteriaValue == null) {
275
- return requestValue === '';
280
+ return requestValue === "";
276
281
  }
277
282
  const strategyValue = criteriaValue;
278
283
  if (strategyValue.equals !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"scenario-manager.d.ts","sourceRoot":"","sources":["../../src/domain/scenario-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,YAAY,EACb,MAAM,mBAAmB,CAAC;AA6B3B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GAAI,qDAKnC;IACD,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,KAAG,eAgFH,CAAC"}
1
+ {"version":3,"file":"scenario-manager.d.ts","sourceRoot":"","sources":["../../src/domain/scenario-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,YAAY,EACb,MAAM,mBAAmB,CAAC;AAkC3B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GAAI,qDAKnC;IACD,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,KAAG,eAgFH,CAAC"}
@@ -37,9 +37,9 @@ export const createScenarioManager = ({ registry, store, stateManager, sequenceT
37
37
  // Validate scenario definition at trust boundary
38
38
  const validationResult = ScenaristScenarioSchema.safeParse(definition);
39
39
  if (!validationResult.success) {
40
- const errorMessages = validationResult.error.issues.map((err) => `${err.path.join('.')}: ${err.message}`);
41
- const scenarioId = definition?.id || '<unknown>';
42
- throw new ScenarioValidationError(`Invalid scenario definition for '${scenarioId}': ${errorMessages.join(', ')}`, errorMessages);
40
+ const errorMessages = validationResult.error.issues.map((err) => `${err.path.join(".")}: ${err.message}`);
41
+ const scenarioId = definition?.id || "<unknown>";
42
+ throw new ScenarioValidationError(`Invalid scenario definition for '${scenarioId}': ${errorMessages.join(", ")}`, errorMessages);
43
43
  }
44
44
  const existing = registry.get(definition.id);
45
45
  // Allow re-registering the exact same scenario object (idempotent)
@@ -1 +1 @@
1
- {"version":3,"file":"template-replacement.d.ts","sourceRoot":"","sources":["../../src/domain/template-replacement.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,cAAc,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,OAwDtF,CAAC"}
1
+ {"version":3,"file":"template-replacement.d.ts","sourceRoot":"","sources":["../../src/domain/template-replacement.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,OAAO,EACd,cAAc,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACpC,OA8DF,CAAC"}
@@ -10,11 +10,11 @@
10
10
  export const applyTemplates = (value, templateData) => {
11
11
  // Backward compatibility: If templateData doesn't have 'state' or 'params' keys,
12
12
  // treat it as a flat state object and wrap it
13
- const normalizedData = (templateData.state !== undefined || templateData.params !== undefined)
13
+ const normalizedData = templateData.state !== undefined || templateData.params !== undefined
14
14
  ? templateData
15
15
  : { state: templateData, params: {} };
16
16
  // Guard: Handle strings (base case)
17
- if (typeof value === 'string') {
17
+ if (typeof value === "string") {
18
18
  // Check if entire string is a single pure template (no surrounding text)
19
19
  // Supports both {{state.key}} and {{params.key}}
20
20
  // Using {1,256} limit to prevent ReDoS attacks with malicious input
@@ -46,7 +46,7 @@ export const applyTemplates = (value, templateData) => {
46
46
  return value.map((item) => applyTemplates(item, normalizedData));
47
47
  }
48
48
  // Guard: Handle objects recursively
49
- if (typeof value === 'object' && value !== null) {
49
+ if (typeof value === "object" && value !== null) {
50
50
  const result = {};
51
51
  for (const [key, val] of Object.entries(value)) {
52
52
  result[key] = applyTemplates(val, normalizedData);
@@ -69,19 +69,19 @@ const resolveTemplatePath = (templateData, prefix, path) => {
69
69
  // Get the root object (state or params)
70
70
  const root = templateData[prefix];
71
71
  // Guard: Prefix doesn't exist (e.g., no params provided)
72
- if (root === undefined || typeof root !== 'object' || root === null) {
72
+ if (root === undefined || typeof root !== "object" || root === null) {
73
73
  return undefined;
74
74
  }
75
75
  // Resolve nested path within root object
76
- const segments = path.split('.');
76
+ const segments = path.split(".");
77
77
  let current = root;
78
78
  for (const segment of segments) {
79
79
  // Guard: Can't traverse non-objects
80
- if (typeof current !== 'object' || current === null) {
80
+ if (typeof current !== "object" || current === null) {
81
81
  return undefined;
82
82
  }
83
83
  // Handle arrays with .length property
84
- if (Array.isArray(current) && segment === 'length') {
84
+ if (Array.isArray(current) && segment === "length") {
85
85
  return current.length;
86
86
  }
87
87
  // Traverse object
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export type * from './types/index.js';
2
- export * from './schemas/index.js';
3
- export * from './constants/index.js';
4
- export type * from './ports/index.js';
5
- export type * from './contracts/index.js';
6
- export * from './domain/index.js';
7
- export * from './adapters/index.js';
1
+ export type * from "./types/index.js";
2
+ export * from "./schemas/index.js";
3
+ export * from "./constants/index.js";
4
+ export type * from "./ports/index.js";
5
+ export type * from "./contracts/index.js";
6
+ export * from "./domain/index.js";
7
+ export * from "./adapters/index.js";
8
8
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // Schemas (runtime validation)
2
- export * from './schemas/index.js';
2
+ export * from "./schemas/index.js";
3
3
  // Constants
4
- export * from './constants/index.js';
4
+ export * from "./constants/index.js";
5
5
  // Domain (implementations)
6
- export * from './domain/index.js';
6
+ export * from "./domain/index.js";
7
7
  // Adapters (default implementations)
8
- export * from './adapters/index.js';
8
+ export * from "./adapters/index.js";
@@ -1,4 +1,4 @@
1
- import type { ScenaristScenario } from '../../types/index.js';
1
+ import type { ScenaristScenario } from "../../types/index.js";
2
2
  /**
3
3
  * Secondary port for scenario registry.
4
4
  * Manages the catalog of available scenarios.
@@ -1,4 +1,4 @@
1
- import type { ActiveScenario } from '../../types/index.js';
1
+ import type { ActiveScenario } from "../../types/index.js";
2
2
  /**
3
3
  * Secondary port for scenario storage.
4
4
  * Implementations determine where active scenarios are stored.
@@ -36,7 +36,7 @@ export interface SequenceTracker {
36
36
  * @param totalResponses - Total number of responses in the sequence
37
37
  * @param repeatMode - Repeat behavior ('last' | 'cycle' | 'none')
38
38
  */
39
- advance(testId: string, scenarioId: string, mockIndex: number, totalResponses: number, repeatMode: 'last' | 'cycle' | 'none'): void;
39
+ advance(testId: string, scenarioId: string, mockIndex: number, totalResponses: number, repeatMode: "last" | "cycle" | "none"): void;
40
40
  /**
41
41
  * Reset all sequence positions for a specific test ID.
42
42
  *