@scenarist/core 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +755 -28
- package/dist/adapters/in-memory-registry.d.ts +18 -0
- package/dist/adapters/in-memory-registry.d.ts.map +1 -0
- package/dist/adapters/in-memory-registry.js +25 -0
- package/dist/adapters/in-memory-sequence-tracker.d.ts +28 -0
- package/dist/adapters/in-memory-sequence-tracker.d.ts.map +1 -0
- package/dist/adapters/in-memory-sequence-tracker.js +82 -0
- package/dist/adapters/in-memory-state-manager.d.ts +24 -0
- package/dist/adapters/in-memory-state-manager.d.ts.map +1 -0
- package/dist/adapters/in-memory-state-manager.js +81 -0
- package/dist/adapters/in-memory-store.d.ts +18 -0
- package/dist/adapters/in-memory-store.d.ts.map +1 -0
- package/dist/adapters/in-memory-store.js +25 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +4 -0
- package/dist/constants/headers.d.ts +10 -0
- package/dist/constants/headers.d.ts.map +1 -0
- package/dist/constants/headers.js +9 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/contracts/framework-adapter.d.ts +118 -0
- package/dist/contracts/framework-adapter.d.ts.map +1 -0
- package/dist/contracts/framework-adapter.js +1 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/domain/config-builder.d.ts +9 -0
- package/dist/domain/config-builder.d.ts.map +1 -0
- package/dist/domain/config-builder.js +20 -0
- package/dist/domain/index.d.ts +5 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +4 -0
- package/dist/domain/path-extraction.d.ts +17 -0
- package/dist/domain/path-extraction.d.ts.map +1 -0
- package/dist/domain/path-extraction.js +60 -0
- package/dist/domain/regex-matching.d.ts +20 -0
- package/dist/domain/regex-matching.d.ts.map +1 -0
- package/dist/domain/regex-matching.js +27 -0
- package/dist/domain/response-selector.d.ts +22 -0
- package/dist/domain/response-selector.d.ts.map +1 -0
- package/dist/domain/response-selector.js +337 -0
- package/dist/domain/scenario-manager.d.ts +20 -0
- package/dist/domain/scenario-manager.d.ts.map +1 -0
- package/dist/domain/scenario-manager.js +90 -0
- package/dist/domain/template-replacement.d.ts +11 -0
- package/dist/domain/template-replacement.d.ts.map +1 -0
- package/dist/domain/template-replacement.js +94 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/ports/driven/request-context.d.ts +43 -0
- package/dist/ports/driven/request-context.d.ts.map +1 -0
- package/dist/ports/driven/request-context.js +1 -0
- package/dist/ports/driven/response-selector.d.ts +34 -0
- package/dist/ports/driven/response-selector.d.ts.map +1 -0
- package/dist/ports/driven/response-selector.js +9 -0
- package/dist/ports/driven/scenario-registry.d.ts +46 -0
- package/dist/ports/driven/scenario-registry.d.ts.map +1 -0
- package/dist/ports/driven/scenario-registry.js +1 -0
- package/dist/ports/driven/scenario-store.d.ts +33 -0
- package/dist/ports/driven/scenario-store.d.ts.map +1 -0
- package/dist/ports/driven/scenario-store.js +1 -0
- package/dist/ports/driven/sequence-tracker.d.ts +49 -0
- package/dist/ports/driven/sequence-tracker.d.ts.map +1 -0
- package/dist/ports/driven/sequence-tracker.js +1 -0
- package/dist/ports/driven/state-manager.d.ts +56 -0
- package/dist/ports/driven/state-manager.d.ts.map +1 -0
- package/dist/ports/driven/state-manager.js +1 -0
- package/dist/ports/driving/scenario-manager.d.ts +99 -0
- package/dist/ports/driving/scenario-manager.d.ts.map +1 -0
- package/dist/ports/driving/scenario-manager.js +1 -0
- package/dist/ports/index.d.ts +8 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +1 -0
- package/dist/schemas/index.d.ts +18 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +17 -0
- package/dist/schemas/match-criteria.d.ts +27 -0
- package/dist/schemas/match-criteria.d.ts.map +1 -0
- package/dist/schemas/match-criteria.js +71 -0
- package/dist/schemas/scenario-definition.d.ts +276 -0
- package/dist/schemas/scenario-definition.d.ts.map +1 -0
- package/dist/schemas/scenario-definition.js +78 -0
- package/dist/schemas/scenario-requests.d.ts +33 -0
- package/dist/schemas/scenario-requests.d.ts.map +1 -0
- package/dist/schemas/scenario-requests.js +29 -0
- package/dist/schemas/scenarios-object.d.ts +91 -0
- package/dist/schemas/scenarios-object.d.ts.map +1 -0
- package/dist/schemas/scenarios-object.js +17 -0
- package/dist/types/config.d.ts +70 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/scenario.d.ts +141 -0
- package/dist/types/scenario.d.ts.map +1 -0
- package/dist/types/scenario.js +1 -0
- package/package.json +67 -7
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary port for extracting context from HTTP requests.
|
|
3
|
+
* Framework adapters implement this to provide test ID extraction.
|
|
4
|
+
*
|
|
5
|
+
* **Implementation Pattern:**
|
|
6
|
+
* Implementations should accept ScenaristConfig to determine which headers
|
|
7
|
+
* to read and what defaults to apply.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { SCENARIST_TEST_ID_HEADER } from '@scenarist/core';
|
|
12
|
+
*
|
|
13
|
+
* class ExpressRequestContext implements RequestContext {
|
|
14
|
+
* constructor(
|
|
15
|
+
* private readonly req: Request,
|
|
16
|
+
* private readonly defaultTestId: string
|
|
17
|
+
* ) {}
|
|
18
|
+
*
|
|
19
|
+
* getTestId(): string {
|
|
20
|
+
* const header = this.req.headers[SCENARIST_TEST_ID_HEADER];
|
|
21
|
+
* return typeof header === 'string' ? header : this.defaultTestId;
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface RequestContext {
|
|
27
|
+
/**
|
|
28
|
+
* Extract the test ID from the request.
|
|
29
|
+
* This enables test isolation.
|
|
30
|
+
*/
|
|
31
|
+
getTestId(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get all request headers.
|
|
34
|
+
* Useful for debugging and logging.
|
|
35
|
+
*/
|
|
36
|
+
getHeaders(): Record<string, string | string[] | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the request hostname.
|
|
39
|
+
* Used to determine passthrough behavior.
|
|
40
|
+
*/
|
|
41
|
+
getHostname(): string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,IAAI,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAE5D;;;OAGG;IACH,WAAW,IAAI,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ScenaristMockWithParams, ScenaristResponse, HttpRequestContext, ScenaristResult } from "../../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error type for response selection failures.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ResponseSelectionError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Secondary port for response selection.
|
|
10
|
+
* Determines which mock response to return based on request content.
|
|
11
|
+
*
|
|
12
|
+
* Phase 1 (Current): Stateless matching on body/headers/query
|
|
13
|
+
* Phase 2 (Future): Sequence tracking (first call, second call, etc.)
|
|
14
|
+
* Phase 3 (Future): Stateful responses (increment counters, etc.)
|
|
15
|
+
*
|
|
16
|
+
* The interface is defined as a port to allow:
|
|
17
|
+
* - Default implementation: Stateless content matching (createResponseSelector)
|
|
18
|
+
* - Sequence-aware implementation: Track call counts per mock
|
|
19
|
+
* - State-aware implementation: Maintain state across requests
|
|
20
|
+
* - Test doubles: Mock response selection for adapter tests
|
|
21
|
+
*/
|
|
22
|
+
export interface ResponseSelector {
|
|
23
|
+
/**
|
|
24
|
+
* Select a response from candidate mocks based on request content.
|
|
25
|
+
*
|
|
26
|
+
* @param testId - Test ID for sequence/state tracking
|
|
27
|
+
* @param scenarioId - Scenario ID for sequence/state tracking
|
|
28
|
+
* @param context - Request context (method, url, body, headers, query)
|
|
29
|
+
* @param mocks - Candidate mocks with extracted params (already filtered by URL/method)
|
|
30
|
+
* @returns ScenaristResult with selected ScenaristResponse or error if no match found
|
|
31
|
+
*/
|
|
32
|
+
selectResponse(testId: string, scenarioId: string, context: HttpRequestContext, mocks: ReadonlyArray<ScenaristMockWithParams>): ScenaristResult<ScenaristResponse, ResponseSelectionError>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=response-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-selector.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/response-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAE9B;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;OAQG;IACH,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,aAAa,CAAC,uBAAuB,CAAC,GAC5C,eAAe,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAC;CAC/D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ScenaristScenario } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Secondary port for scenario registry.
|
|
4
|
+
* Manages the catalog of available scenarios.
|
|
5
|
+
*
|
|
6
|
+
* This is separate from ScenarioStore to maintain architectural purity:
|
|
7
|
+
* - ScenarioRegistry: what scenarios exist (the catalog)
|
|
8
|
+
* - ScenarioStore: which scenario each test is using (active state)
|
|
9
|
+
*
|
|
10
|
+
* ScenarioDefinitions are serializable, enabling:
|
|
11
|
+
* - InMemoryScenarioRegistry: Map-based registry (default, fastest)
|
|
12
|
+
* - RedisScenarioRegistry: Distributed scenarios across processes
|
|
13
|
+
* - FileSystemScenarioRegistry: Load scenarios from JSON/YAML files
|
|
14
|
+
* - RemoteScenarioRegistry: Fetch scenarios from REST API
|
|
15
|
+
* - DatabaseScenarioRegistry: Store scenarios in PostgreSQL/MongoDB
|
|
16
|
+
*
|
|
17
|
+
* At runtime, ScenaristMocks are converted to MSW HttpHandlers.
|
|
18
|
+
*/
|
|
19
|
+
export interface ScenarioRegistry {
|
|
20
|
+
/**
|
|
21
|
+
* Register a scenario definition.
|
|
22
|
+
* The definition.id is used as the unique identifier.
|
|
23
|
+
* Makes the scenario available for use.
|
|
24
|
+
*/
|
|
25
|
+
register(definition: ScenaristScenario): void;
|
|
26
|
+
/**
|
|
27
|
+
* Retrieve a registered scenario definition by ID.
|
|
28
|
+
* Returns undefined if scenario not found.
|
|
29
|
+
*/
|
|
30
|
+
get(id: string): ScenaristScenario | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a scenario ID is registered.
|
|
33
|
+
*/
|
|
34
|
+
has(id: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* List all registered scenario definitions.
|
|
37
|
+
* Useful for debugging, dev tools, and scenario discovery.
|
|
38
|
+
*/
|
|
39
|
+
list(): ReadonlyArray<ScenaristScenario>;
|
|
40
|
+
/**
|
|
41
|
+
* Remove a scenario from the registry.
|
|
42
|
+
* Does not affect already-active scenarios in the store.
|
|
43
|
+
*/
|
|
44
|
+
unregister(id: string): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=scenario-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-registry.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/scenario-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE9C;;;OAGG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAE/C;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAEzB;;;OAGG;IACH,IAAI,IAAI,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAEzC;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ActiveScenario } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Secondary port for scenario storage.
|
|
4
|
+
* Implementations determine where active scenarios are stored.
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* - InMemoryScenarioStore: Map-based storage (single process)
|
|
8
|
+
* - RedisScenarioStore: Redis-based storage (distributed tests)
|
|
9
|
+
*/
|
|
10
|
+
export interface ScenarioStore {
|
|
11
|
+
/**
|
|
12
|
+
* Store an active scenario for a test ID.
|
|
13
|
+
*/
|
|
14
|
+
set(testId: string, scenario: ActiveScenario): void;
|
|
15
|
+
/**
|
|
16
|
+
* Retrieve an active scenario for a test ID.
|
|
17
|
+
*/
|
|
18
|
+
get(testId: string): ActiveScenario | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a test ID has an active scenario.
|
|
21
|
+
*/
|
|
22
|
+
has(testId: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Remove the active scenario for a test ID.
|
|
25
|
+
*/
|
|
26
|
+
delete(testId: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Clear all active scenarios.
|
|
29
|
+
* Use with caution - affects all test IDs.
|
|
30
|
+
*/
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=scenario-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-store.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/scenario-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAEpD;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAEhD;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAE7B;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sequence position state for a specific mock.
|
|
3
|
+
*/
|
|
4
|
+
export type SequencePosition = {
|
|
5
|
+
readonly position: number;
|
|
6
|
+
readonly exhausted: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Secondary port for sequence position tracking.
|
|
10
|
+
*
|
|
11
|
+
* Tracks which response in a sequence should be returned next for each
|
|
12
|
+
* (testId + scenarioId + mockIndex) combination.
|
|
13
|
+
*
|
|
14
|
+
* This port enables:
|
|
15
|
+
* - Default implementation: In-memory tracking (Map-based)
|
|
16
|
+
* - Redis implementation: Distributed testing across processes
|
|
17
|
+
* - File-based implementation: Persistent state across restarts
|
|
18
|
+
* - Test doubles: Mock sequence tracking for adapter tests
|
|
19
|
+
*/
|
|
20
|
+
export interface SequenceTracker {
|
|
21
|
+
/**
|
|
22
|
+
* Get the current sequence position for a specific mock.
|
|
23
|
+
*
|
|
24
|
+
* @param testId - Test ID for isolation
|
|
25
|
+
* @param scenarioId - Scenario ID
|
|
26
|
+
* @param mockIndex - Index of the mock in the scenario's mocks array
|
|
27
|
+
* @returns Current position (0-indexed) and exhaustion status
|
|
28
|
+
*/
|
|
29
|
+
getPosition(testId: string, scenarioId: string, mockIndex: number): SequencePosition;
|
|
30
|
+
/**
|
|
31
|
+
* Advance the sequence position for a specific mock.
|
|
32
|
+
*
|
|
33
|
+
* @param testId - Test ID for isolation
|
|
34
|
+
* @param scenarioId - Scenario ID
|
|
35
|
+
* @param mockIndex - Index of the mock in the scenario's mocks array
|
|
36
|
+
* @param totalResponses - Total number of responses in the sequence
|
|
37
|
+
* @param repeatMode - Repeat behavior ('last' | 'cycle' | 'none')
|
|
38
|
+
*/
|
|
39
|
+
advance(testId: string, scenarioId: string, mockIndex: number, totalResponses: number, repeatMode: 'last' | 'cycle' | 'none'): void;
|
|
40
|
+
/**
|
|
41
|
+
* Reset all sequence positions for a specific test ID.
|
|
42
|
+
*
|
|
43
|
+
* Called when switching scenarios to ensure sequences start fresh.
|
|
44
|
+
*
|
|
45
|
+
* @param testId - Test ID to reset
|
|
46
|
+
*/
|
|
47
|
+
reset(testId: string): void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sequence-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequence-tracker.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/sequence-tracker.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,WAAW,CACT,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAAC;IAEpB;;;;;;;;OAQG;IACH,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,CAAC;IAER;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateManager port for managing runtime state in stateful mocks (Phase 3).
|
|
3
|
+
*
|
|
4
|
+
* Implementations store state per test ID, enabling:
|
|
5
|
+
* - Capturing values from requests
|
|
6
|
+
* - Injecting state into responses via templates
|
|
7
|
+
* - State isolation between concurrent tests
|
|
8
|
+
*
|
|
9
|
+
* State values MUST be JSON-serializable to support distributed implementations:
|
|
10
|
+
* - ✅ Primitives: string, number, boolean, null
|
|
11
|
+
* - ✅ Objects and arrays (plain data)
|
|
12
|
+
* - ❌ Functions, classes, symbols, undefined, circular references
|
|
13
|
+
*
|
|
14
|
+
* This enables multiple implementations:
|
|
15
|
+
* - InMemoryStateManager (default): Fast, single-process
|
|
16
|
+
* - RedisStateManager (future): Distributed testing
|
|
17
|
+
* - FileSystemStateManager (future): Persistent state
|
|
18
|
+
*/
|
|
19
|
+
export interface StateManager {
|
|
20
|
+
/**
|
|
21
|
+
* Get a state value for a specific test ID.
|
|
22
|
+
*
|
|
23
|
+
* @param testId - Test identifier for state isolation
|
|
24
|
+
* @param key - State key (can be nested path like 'user.profile.name')
|
|
25
|
+
* @returns Value from state, or undefined if not found
|
|
26
|
+
*/
|
|
27
|
+
get(testId: string, key: string): unknown;
|
|
28
|
+
/**
|
|
29
|
+
* Set a state value for a specific test ID.
|
|
30
|
+
*
|
|
31
|
+
* @param testId - Test identifier for state isolation
|
|
32
|
+
* @param key - State key (supports nested paths)
|
|
33
|
+
* @param value - Value to store (must be JSON-serializable)
|
|
34
|
+
*/
|
|
35
|
+
set(testId: string, key: string, value: unknown): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get all state for a specific test ID.
|
|
38
|
+
*
|
|
39
|
+
* @param testId - Test identifier
|
|
40
|
+
* @returns Complete state object for this test ID
|
|
41
|
+
*/
|
|
42
|
+
getAll(testId: string): Record<string, unknown>;
|
|
43
|
+
/**
|
|
44
|
+
* Reset all state for a specific test ID.
|
|
45
|
+
*
|
|
46
|
+
* Called automatically by ScenarioManager when switching scenarios to prevent
|
|
47
|
+
* state pollution between scenarios. Tests may also call this manually to reset
|
|
48
|
+
* state mid-test.
|
|
49
|
+
*
|
|
50
|
+
* Note: New test IDs start with empty state automatically (no reset needed).
|
|
51
|
+
*
|
|
52
|
+
* @param testId - Test identifier
|
|
53
|
+
*/
|
|
54
|
+
reset(testId: string): void;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=state-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-manager.d.ts","sourceRoot":"","sources":["../../../src/ports/driven/state-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE1C;;;;;;OAMG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAEvD;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhD;;;;;;;;;;OAUG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ScenaristScenario, ActiveScenario, ScenaristResult } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Primary port for scenario management.
|
|
4
|
+
*
|
|
5
|
+
* ScenarioManager is a coordinator/facade that orchestrates interactions between
|
|
6
|
+
* ScenarioRegistry (catalog of available scenarios) and ScenarioStore (active
|
|
7
|
+
* scenarios per test ID). It enforces business rules and provides a unified API.
|
|
8
|
+
*
|
|
9
|
+
* **Architectural Role:**
|
|
10
|
+
* - Coordinates between registry and store
|
|
11
|
+
* - Enforces business rules (e.g., can't activate non-existent scenarios)
|
|
12
|
+
* - Provides test isolation via test IDs
|
|
13
|
+
* - Main entry point for scenario operations
|
|
14
|
+
*
|
|
15
|
+
* **Implementation Pattern:**
|
|
16
|
+
* Implementations accept ScenarioRegistry and ScenarioStore as constructor
|
|
17
|
+
* parameters (dependency injection), never creating them internally.
|
|
18
|
+
*
|
|
19
|
+
* Note: Config is NOT needed here - it's used by adapters (middleware, RequestContext)
|
|
20
|
+
* to handle HTTP concerns. ScenarioManager is pure domain logic.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // Factory function accepts both ports as dependencies
|
|
25
|
+
* const manager = createScenarioManager({
|
|
26
|
+
* registry: new InMemoryScenarioRegistry(),
|
|
27
|
+
* store: new InMemoryScenarioStore(),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* **Thread Safety:**
|
|
32
|
+
* Implementations must be thread-safe for concurrent test execution.
|
|
33
|
+
*/
|
|
34
|
+
export interface ScenarioManager {
|
|
35
|
+
/**
|
|
36
|
+
* Register a scenario definition in the catalog.
|
|
37
|
+
*
|
|
38
|
+
* Delegates to: ScenarioRegistry.register()
|
|
39
|
+
*
|
|
40
|
+
* @param definition The scenario definition to register
|
|
41
|
+
* @throws Error if scenario ID is already registered
|
|
42
|
+
*/
|
|
43
|
+
registerScenario(definition: ScenaristScenario): void;
|
|
44
|
+
/**
|
|
45
|
+
* Switch to a different scenario for a specific test ID.
|
|
46
|
+
*
|
|
47
|
+
* Business rule: Validates scenario exists in registry before activating.
|
|
48
|
+
* Delegates to: ScenarioRegistry.get() then ScenarioStore.set()
|
|
49
|
+
*
|
|
50
|
+
* This enables test isolation - different tests can run different scenarios.
|
|
51
|
+
*
|
|
52
|
+
* @param testId Unique identifier for the test
|
|
53
|
+
* @param scenarioId ID of the scenario to activate
|
|
54
|
+
* @returns Success or error if scenario not found in registry
|
|
55
|
+
*/
|
|
56
|
+
switchScenario(testId: string, scenarioId: string): ScenaristResult<void, Error>;
|
|
57
|
+
/**
|
|
58
|
+
* Get the currently active scenario reference for a test ID.
|
|
59
|
+
*
|
|
60
|
+
* Delegates to: ScenarioStore.get()
|
|
61
|
+
*
|
|
62
|
+
* Note: This returns only the reference (scenarioId).
|
|
63
|
+
* To get the full ScenarioDefinition, use getScenarioById().
|
|
64
|
+
*
|
|
65
|
+
* @param testId Unique identifier for the test
|
|
66
|
+
* @returns Active scenario reference or undefined if no scenario active
|
|
67
|
+
*/
|
|
68
|
+
getActiveScenario(testId: string): ActiveScenario | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* List all registered scenario definitions from the catalog.
|
|
71
|
+
*
|
|
72
|
+
* Delegates to: ScenarioRegistry.list()
|
|
73
|
+
*
|
|
74
|
+
* Useful for debugging and dev tools.
|
|
75
|
+
*
|
|
76
|
+
* @returns Array of all registered scenario definitions
|
|
77
|
+
*/
|
|
78
|
+
listScenarios(): ReadonlyArray<ScenaristScenario>;
|
|
79
|
+
/**
|
|
80
|
+
* Clear the active scenario for a specific test ID.
|
|
81
|
+
*
|
|
82
|
+
* Delegates to: ScenarioStore.delete()
|
|
83
|
+
*
|
|
84
|
+
* Useful for cleanup after tests.
|
|
85
|
+
*
|
|
86
|
+
* @param testId Unique identifier for the test
|
|
87
|
+
*/
|
|
88
|
+
clearScenario(testId: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Get a registered scenario definition by ID without activating it.
|
|
91
|
+
*
|
|
92
|
+
* Delegates to: ScenarioRegistry.get()
|
|
93
|
+
*
|
|
94
|
+
* @param id Scenario definition ID
|
|
95
|
+
* @returns Scenario definition or undefined if not found
|
|
96
|
+
*/
|
|
97
|
+
getScenarioById(id: string): ScenaristScenario | undefined;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=scenario-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-manager.d.ts","sourceRoot":"","sources":["../../../src/ports/driving/scenario-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,gBAAgB,CAAC,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEtD;;;;;;;;;;;OAWG;IACH,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAE9D;;;;;;;;OAQG;IACH,aAAa,IAAI,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAElD;;;;;;;;OAQG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;CAC5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { ScenarioManager } from './driving/scenario-manager.js';
|
|
2
|
+
export type { ScenarioRegistry } from './driven/scenario-registry.js';
|
|
3
|
+
export type { ScenarioStore } from './driven/scenario-store.js';
|
|
4
|
+
export type { RequestContext } from './driven/request-context.js';
|
|
5
|
+
export type { ResponseSelector } from './driven/response-selector.js';
|
|
6
|
+
export type { SequenceTracker, SequencePosition } from './driven/sequence-tracker.js';
|
|
7
|
+
export type { StateManager } from './driven/state-manager.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ports/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACtF,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation schemas for domain data.
|
|
3
|
+
*
|
|
4
|
+
* **Architectural Principle:**
|
|
5
|
+
* Schemas in this directory define domain validation rules.
|
|
6
|
+
* Framework adapters use these schemas at trust boundaries (external → internal).
|
|
7
|
+
*
|
|
8
|
+
* **Pattern:**
|
|
9
|
+
* 1. Define schema using Zod
|
|
10
|
+
* 2. Export both schema and derived type
|
|
11
|
+
* 3. Adapters import and apply at trust boundaries
|
|
12
|
+
* 4. NEVER duplicate schemas in adapters
|
|
13
|
+
*/
|
|
14
|
+
export { ScenarioRequestSchema, type ScenarioRequest } from './scenario-requests.js';
|
|
15
|
+
export { ScenariosObjectSchema } from './scenarios-object.js';
|
|
16
|
+
export { HttpMethodSchema, ScenaristResponseSchema, MatchValueSchema, ScenaristMatchSchema, RepeatModeSchema, ScenaristSequenceSchema, ScenaristCaptureConfigSchema, ScenaristUrlPatternSchema, ScenaristMockSchema, ScenaristScenarioSchema, type HttpMethod, type ScenaristResponse, type MatchValue, type ScenaristMatch, type RepeatMode, type ScenaristSequence, type ScenaristCaptureConfig, type ScenaristUrlPattern, type ScenaristMock, type ScenaristScenario, } from './scenario-definition.js';
|
|
17
|
+
export { SerializedRegexSchema, type SerializedRegex } from './match-criteria.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,EAEvB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime validation schemas for domain data.
|
|
3
|
+
*
|
|
4
|
+
* **Architectural Principle:**
|
|
5
|
+
* Schemas in this directory define domain validation rules.
|
|
6
|
+
* Framework adapters use these schemas at trust boundaries (external → internal).
|
|
7
|
+
*
|
|
8
|
+
* **Pattern:**
|
|
9
|
+
* 1. Define schema using Zod
|
|
10
|
+
* 2. Export both schema and derived type
|
|
11
|
+
* 3. Adapters import and apply at trust boundaries
|
|
12
|
+
* 4. NEVER duplicate schemas in adapters
|
|
13
|
+
*/
|
|
14
|
+
export { ScenarioRequestSchema } from './scenario-requests.js';
|
|
15
|
+
export { ScenariosObjectSchema } from './scenarios-object.js';
|
|
16
|
+
export { HttpMethodSchema, ScenaristResponseSchema, MatchValueSchema, ScenaristMatchSchema, RepeatModeSchema, ScenaristSequenceSchema, ScenaristCaptureConfigSchema, ScenaristUrlPatternSchema, ScenaristMockSchema, ScenaristScenarioSchema, } from './scenario-definition.js';
|
|
17
|
+
export { SerializedRegexSchema } from './match-criteria.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Schema for serialized regex patterns.
|
|
4
|
+
*
|
|
5
|
+
* **Security Notes:**
|
|
6
|
+
* - Uses redos-detector to prevent ReDoS attacks
|
|
7
|
+
* - Validates regex flags to prevent injection
|
|
8
|
+
* - Source must be non-empty string
|
|
9
|
+
*
|
|
10
|
+
* **Serialization:**
|
|
11
|
+
* Regex patterns are serialized as plain objects to maintain JSON compatibility.
|
|
12
|
+
* This allows scenarios to be stored in files, databases, or transmitted over network.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const regex: SerializedRegex = {
|
|
17
|
+
* source: '/api/products',
|
|
18
|
+
* flags: 'i'
|
|
19
|
+
* };
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare const SerializedRegexSchema: z.ZodObject<{
|
|
23
|
+
source: z.ZodString;
|
|
24
|
+
flags: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type SerializedRegex = z.infer<typeof SerializedRegexSchema>;
|
|
27
|
+
//# sourceMappingURL=match-criteria.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-criteria.d.ts","sourceRoot":"","sources":["../../src/schemas/match-criteria.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2CxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB;;;iBAWhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { isSafePattern } from 'redos-detector';
|
|
3
|
+
/**
|
|
4
|
+
* Zod schemas for match criteria with regex support.
|
|
5
|
+
*
|
|
6
|
+
* **Security:**
|
|
7
|
+
* - ReDoS protection via redos-detector
|
|
8
|
+
* - Only safe regex flag characters allowed
|
|
9
|
+
* - Timeout protection handled separately in domain layer
|
|
10
|
+
*
|
|
11
|
+
* **Pattern:**
|
|
12
|
+
* Serialize regex as { source: string, flags?: string } for JSON compatibility.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Valid regex flags (safe subset of JavaScript regex flags).
|
|
16
|
+
*
|
|
17
|
+
* Supported flags:
|
|
18
|
+
* - g: global match
|
|
19
|
+
* - i: case insensitive
|
|
20
|
+
* - m: multiline
|
|
21
|
+
* - s: dotAll (. matches newlines)
|
|
22
|
+
* - u: unicode
|
|
23
|
+
* - v: unicode sets
|
|
24
|
+
* - y: sticky
|
|
25
|
+
*/
|
|
26
|
+
const VALID_REGEX_FLAGS = /^[gimsuvy]*$/;
|
|
27
|
+
/**
|
|
28
|
+
* Validates that a regex pattern is safe from ReDoS attacks.
|
|
29
|
+
*
|
|
30
|
+
* Uses redos-detector to analyze the pattern for exponential backtracking.
|
|
31
|
+
* Patterns that could cause catastrophic backtracking are rejected.
|
|
32
|
+
*
|
|
33
|
+
* @param pattern - The regex pattern to validate
|
|
34
|
+
* @returns true if the pattern is safe, false otherwise
|
|
35
|
+
*/
|
|
36
|
+
const isPatternSafeFromReDoS = (pattern) => {
|
|
37
|
+
const result = isSafePattern(pattern);
|
|
38
|
+
return result.safe;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Schema for serialized regex patterns.
|
|
42
|
+
*
|
|
43
|
+
* **Security Notes:**
|
|
44
|
+
* - Uses redos-detector to prevent ReDoS attacks
|
|
45
|
+
* - Validates regex flags to prevent injection
|
|
46
|
+
* - Source must be non-empty string
|
|
47
|
+
*
|
|
48
|
+
* **Serialization:**
|
|
49
|
+
* Regex patterns are serialized as plain objects to maintain JSON compatibility.
|
|
50
|
+
* This allows scenarios to be stored in files, databases, or transmitted over network.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const regex: SerializedRegex = {
|
|
55
|
+
* source: '/api/products',
|
|
56
|
+
* flags: 'i'
|
|
57
|
+
* };
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export const SerializedRegexSchema = z.object({
|
|
61
|
+
source: z
|
|
62
|
+
.string()
|
|
63
|
+
.min(1, 'Regex source must not be empty')
|
|
64
|
+
.refine(isPatternSafeFromReDoS, {
|
|
65
|
+
message: 'Regex pattern is unsafe (ReDoS vulnerability detected)',
|
|
66
|
+
}),
|
|
67
|
+
flags: z
|
|
68
|
+
.string()
|
|
69
|
+
.regex(VALID_REGEX_FLAGS, 'Invalid regex flags (only g, i, m, s, u, v, y allowed)')
|
|
70
|
+
.optional(),
|
|
71
|
+
});
|