awaitly 1.0.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 +1278 -0
- package/dist/batch.cjs +2 -0
- package/dist/batch.cjs.map +1 -0
- package/dist/batch.d.cts +197 -0
- package/dist/batch.d.ts +197 -0
- package/dist/batch.js +2 -0
- package/dist/batch.js.map +1 -0
- package/dist/circuit-breaker.cjs +2 -0
- package/dist/circuit-breaker.cjs.map +1 -0
- package/dist/circuit-breaker.d.cts +208 -0
- package/dist/circuit-breaker.d.ts +208 -0
- package/dist/circuit-breaker.js +2 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/conditional.cjs +2 -0
- package/dist/conditional.cjs.map +1 -0
- package/dist/conditional.d.cts +249 -0
- package/dist/conditional.d.ts +249 -0
- package/dist/conditional.js +2 -0
- package/dist/conditional.js.map +1 -0
- package/dist/core-BuTBsR0x.d.cts +2325 -0
- package/dist/core-BuTBsR0x.d.ts +2325 -0
- package/dist/core.cjs +2 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +3 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/devtools.cjs +11 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +176 -0
- package/dist/devtools.d.ts +176 -0
- package/dist/devtools.js +11 -0
- package/dist/devtools.js.map +1 -0
- package/dist/duration.cjs +2 -0
- package/dist/duration.cjs.map +1 -0
- package/dist/duration.d.cts +246 -0
- package/dist/duration.d.ts +246 -0
- package/dist/duration.js +2 -0
- package/dist/duration.js.map +1 -0
- package/dist/hitl.cjs +2 -0
- package/dist/hitl.cjs.map +1 -0
- package/dist/hitl.d.cts +337 -0
- package/dist/hitl.d.ts +337 -0
- package/dist/hitl.js +2 -0
- package/dist/hitl.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/match.cjs +2 -0
- package/dist/match.cjs.map +1 -0
- package/dist/match.d.cts +209 -0
- package/dist/match.d.ts +209 -0
- package/dist/match.js +2 -0
- package/dist/match.js.map +1 -0
- package/dist/otel.cjs +2 -0
- package/dist/otel.cjs.map +1 -0
- package/dist/otel.d.cts +185 -0
- package/dist/otel.d.ts +185 -0
- package/dist/otel.js +2 -0
- package/dist/otel.js.map +1 -0
- package/dist/persistence.cjs +2 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +572 -0
- package/dist/persistence.d.ts +572 -0
- package/dist/persistence.js +2 -0
- package/dist/persistence.js.map +1 -0
- package/dist/policies.cjs +2 -0
- package/dist/policies.cjs.map +1 -0
- package/dist/policies.d.cts +378 -0
- package/dist/policies.d.ts +378 -0
- package/dist/policies.js +2 -0
- package/dist/policies.js.map +1 -0
- package/dist/ratelimit.cjs +2 -0
- package/dist/ratelimit.cjs.map +1 -0
- package/dist/ratelimit.d.cts +279 -0
- package/dist/ratelimit.d.ts +279 -0
- package/dist/ratelimit.js +2 -0
- package/dist/ratelimit.js.map +1 -0
- package/dist/reliability.cjs +2 -0
- package/dist/reliability.cjs.map +1 -0
- package/dist/reliability.d.cts +5 -0
- package/dist/reliability.d.ts +5 -0
- package/dist/reliability.js +2 -0
- package/dist/reliability.js.map +1 -0
- package/dist/resource.cjs +2 -0
- package/dist/resource.cjs.map +1 -0
- package/dist/resource.d.cts +171 -0
- package/dist/resource.d.ts +171 -0
- package/dist/resource.js +2 -0
- package/dist/resource.js.map +1 -0
- package/dist/retry.cjs +2 -0
- package/dist/retry.cjs.map +1 -0
- package/dist/retry.d.cts +2 -0
- package/dist/retry.d.ts +2 -0
- package/dist/retry.js +2 -0
- package/dist/retry.js.map +1 -0
- package/dist/saga.cjs +2 -0
- package/dist/saga.cjs.map +1 -0
- package/dist/saga.d.cts +231 -0
- package/dist/saga.d.ts +231 -0
- package/dist/saga.js +2 -0
- package/dist/saga.js.map +1 -0
- package/dist/schedule.cjs +2 -0
- package/dist/schedule.cjs.map +1 -0
- package/dist/schedule.d.cts +387 -0
- package/dist/schedule.d.ts +387 -0
- package/dist/schedule.js +2 -0
- package/dist/schedule.js.map +1 -0
- package/dist/tagged-error.cjs +2 -0
- package/dist/tagged-error.cjs.map +1 -0
- package/dist/tagged-error.d.cts +252 -0
- package/dist/tagged-error.d.ts +252 -0
- package/dist/tagged-error.js +2 -0
- package/dist/tagged-error.js.map +1 -0
- package/dist/testing.cjs +2 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +228 -0
- package/dist/testing.d.ts +228 -0
- package/dist/testing.js +2 -0
- package/dist/testing.js.map +1 -0
- package/dist/visualize.cjs +1573 -0
- package/dist/visualize.cjs.map +1 -0
- package/dist/visualize.d.cts +1415 -0
- package/dist/visualize.d.ts +1415 -0
- package/dist/visualize.js +1573 -0
- package/dist/visualize.js.map +1 -0
- package/dist/webhook.cjs +2 -0
- package/dist/webhook.cjs.map +1 -0
- package/dist/webhook.d.cts +469 -0
- package/dist/webhook.d.ts +469 -0
- package/dist/webhook.js +2 -0
- package/dist/webhook.js.map +1 -0
- package/dist/workflow-entry-C6nH8ByN.d.ts +858 -0
- package/dist/workflow-entry-RRTlSg_4.d.cts +858 -0
- package/dist/workflow.cjs +2 -0
- package/dist/workflow.cjs.map +1 -0
- package/dist/workflow.d.cts +2 -0
- package/dist/workflow.d.ts +2 -0
- package/dist/workflow.js +2 -0
- package/dist/workflow.js.map +1 -0
- package/docs/advanced.md +1548 -0
- package/docs/api.md +513 -0
- package/docs/coming-from-neverthrow.md +1013 -0
- package/docs/match.md +417 -0
- package/docs/pino-logging-example.md +396 -0
- package/docs/policies.md +508 -0
- package/docs/resource-management.md +509 -0
- package/docs/schedule.md +467 -0
- package/docs/tagged-error.md +785 -0
- package/docs/visualization.md +430 -0
- package/docs/visualize-examples.md +330 -0
- package/package.json +227 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { R as Result, A as AsyncResult, S as StepOptions, W as WorkflowEvent } from './core-BuTBsR0x.cjs';
|
|
2
|
+
import { A as AnyResultFn, E as ErrorsOfDeps } from './workflow-entry-RRTlSg_4.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* awaitly/testing
|
|
6
|
+
*
|
|
7
|
+
* Deterministic Workflow Testing Harness.
|
|
8
|
+
* Provides tools for scripting step outcomes and asserting workflow behavior.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A scripted outcome for a step.
|
|
13
|
+
*/
|
|
14
|
+
type ScriptedOutcome<T = unknown, E = unknown> = {
|
|
15
|
+
type: "ok";
|
|
16
|
+
value: T;
|
|
17
|
+
} | {
|
|
18
|
+
type: "err";
|
|
19
|
+
error: E;
|
|
20
|
+
} | {
|
|
21
|
+
type: "throw";
|
|
22
|
+
error: unknown;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Step invocation record.
|
|
26
|
+
*/
|
|
27
|
+
interface StepInvocation {
|
|
28
|
+
/** Step name */
|
|
29
|
+
name?: string;
|
|
30
|
+
/** Step key */
|
|
31
|
+
key?: string;
|
|
32
|
+
/** Invocation order (0-indexed) */
|
|
33
|
+
order: number;
|
|
34
|
+
/** Timestamp when step was invoked */
|
|
35
|
+
timestamp: number;
|
|
36
|
+
/** Duration in milliseconds */
|
|
37
|
+
durationMs?: number;
|
|
38
|
+
/** Result of the step */
|
|
39
|
+
result?: Result<unknown, unknown>;
|
|
40
|
+
/** Whether the step was from cache */
|
|
41
|
+
cached?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Assertion result.
|
|
45
|
+
*/
|
|
46
|
+
interface AssertionResult {
|
|
47
|
+
passed: boolean;
|
|
48
|
+
message: string;
|
|
49
|
+
expected?: unknown;
|
|
50
|
+
actual?: unknown;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Test harness options.
|
|
54
|
+
*/
|
|
55
|
+
interface TestHarnessOptions {
|
|
56
|
+
/** Whether to record step invocations */
|
|
57
|
+
recordInvocations?: boolean;
|
|
58
|
+
/** Custom clock for deterministic timing */
|
|
59
|
+
clock?: () => number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Mock step function that returns scripted outcomes.
|
|
63
|
+
*/
|
|
64
|
+
type MockStep<E> = {
|
|
65
|
+
/** Execute with a Result-returning operation */
|
|
66
|
+
<T, StepE extends E>(operation: () => Result<T, StepE> | AsyncResult<T, StepE>, options?: StepOptions | string): Promise<T>;
|
|
67
|
+
/** Execute with a direct Result */
|
|
68
|
+
<T, StepE extends E>(result: Result<T, StepE> | AsyncResult<T, StepE>, options?: StepOptions | string): Promise<T>;
|
|
69
|
+
/** step.try for catching throws */
|
|
70
|
+
try: <T, Err extends E>(operation: () => T | Promise<T>, options: {
|
|
71
|
+
error: Err;
|
|
72
|
+
name?: string;
|
|
73
|
+
key?: string;
|
|
74
|
+
} | {
|
|
75
|
+
onError: (cause: unknown) => Err;
|
|
76
|
+
name?: string;
|
|
77
|
+
key?: string;
|
|
78
|
+
}) => Promise<T>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Workflow test harness interface.
|
|
82
|
+
*/
|
|
83
|
+
interface WorkflowHarness<E, Deps> {
|
|
84
|
+
/**
|
|
85
|
+
* Script step outcomes in order.
|
|
86
|
+
* Each outcome will be returned for the corresponding step invocation.
|
|
87
|
+
*/
|
|
88
|
+
script(outcomes: ScriptedOutcome[]): void;
|
|
89
|
+
/**
|
|
90
|
+
* Script a specific step outcome by name or key.
|
|
91
|
+
*/
|
|
92
|
+
scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void;
|
|
93
|
+
/**
|
|
94
|
+
* Run the workflow with scripted outcomes.
|
|
95
|
+
*/
|
|
96
|
+
run<T>(fn: (step: MockStep<E>, deps: Deps) => Promise<T>): Promise<Result<T, E | unknown>>;
|
|
97
|
+
/**
|
|
98
|
+
* Run the workflow with input.
|
|
99
|
+
*/
|
|
100
|
+
runWithInput<T, TInput>(input: TInput, fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>): Promise<Result<T, E | unknown>>;
|
|
101
|
+
/**
|
|
102
|
+
* Get recorded step invocations.
|
|
103
|
+
*/
|
|
104
|
+
getInvocations(): StepInvocation[];
|
|
105
|
+
/**
|
|
106
|
+
* Assert that steps were invoked in order.
|
|
107
|
+
*/
|
|
108
|
+
assertSteps(expectedNames: string[]): AssertionResult;
|
|
109
|
+
/**
|
|
110
|
+
* Assert that a step was invoked with specific options.
|
|
111
|
+
*/
|
|
112
|
+
assertStepCalled(nameOrKey: string): AssertionResult;
|
|
113
|
+
/**
|
|
114
|
+
* Assert that a step was NOT invoked.
|
|
115
|
+
*/
|
|
116
|
+
assertStepNotCalled(nameOrKey: string): AssertionResult;
|
|
117
|
+
/**
|
|
118
|
+
* Assert the workflow result.
|
|
119
|
+
*/
|
|
120
|
+
assertResult<T>(result: Result<T, unknown>, expected: Result<T, unknown>): AssertionResult;
|
|
121
|
+
/**
|
|
122
|
+
* Clear all state for a new test.
|
|
123
|
+
*/
|
|
124
|
+
reset(): void;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a test harness for a workflow.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const harness = createWorkflowHarness({ fetchUser, chargeCard });
|
|
132
|
+
*
|
|
133
|
+
* // Script step outcomes
|
|
134
|
+
* harness.script([
|
|
135
|
+
* { type: 'ok', value: { id: '1', name: 'Alice' } },
|
|
136
|
+
* { type: 'ok', value: { transactionId: 'tx_123' } },
|
|
137
|
+
* ]);
|
|
138
|
+
*
|
|
139
|
+
* // Run the workflow
|
|
140
|
+
* const result = await harness.run(async (step, { fetchUser, chargeCard }) => {
|
|
141
|
+
* const user = await step(() => fetchUser('1'), 'fetch-user');
|
|
142
|
+
* const charge = await step(() => chargeCard(100), 'charge-card');
|
|
143
|
+
* return { user, charge };
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* // Assert
|
|
147
|
+
* expect(result.ok).toBe(true);
|
|
148
|
+
* harness.assertSteps(['fetch-user', 'charge-card']);
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
declare function createWorkflowHarness<Deps extends Record<string, AnyResultFn>>(deps: Deps, options?: TestHarnessOptions): WorkflowHarness<ErrorsOfDeps<Deps>, Deps>;
|
|
152
|
+
/**
|
|
153
|
+
* Create a mock Result-returning function.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const fetchUser = createMockFn<User, 'NOT_FOUND'>();
|
|
158
|
+
*
|
|
159
|
+
* fetchUser.returns(ok({ id: '1', name: 'Alice' }));
|
|
160
|
+
* // or
|
|
161
|
+
* fetchUser.returnsOnce(ok({ id: '1', name: 'Alice' }));
|
|
162
|
+
* fetchUser.returnsOnce(err('NOT_FOUND'));
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function createMockFn<T, E>(): MockFunction<T, E>;
|
|
166
|
+
/**
|
|
167
|
+
* Mock function interface.
|
|
168
|
+
*/
|
|
169
|
+
interface MockFunction<T, E> {
|
|
170
|
+
(...args: unknown[]): AsyncResult<T, E>;
|
|
171
|
+
/** Set the default return value */
|
|
172
|
+
returns(result: Result<T, E>): MockFunction<T, E>;
|
|
173
|
+
/** Queue a return value for the next call */
|
|
174
|
+
returnsOnce(result: Result<T, E>): MockFunction<T, E>;
|
|
175
|
+
/** Get all call arguments */
|
|
176
|
+
getCalls(): unknown[][];
|
|
177
|
+
/** Get the number of times the function was called */
|
|
178
|
+
getCallCount(): number;
|
|
179
|
+
/** Reset the mock */
|
|
180
|
+
reset(): void;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Workflow snapshot for comparison.
|
|
184
|
+
*/
|
|
185
|
+
interface WorkflowSnapshot {
|
|
186
|
+
/** Step invocations */
|
|
187
|
+
invocations: StepInvocation[];
|
|
188
|
+
/** Final result */
|
|
189
|
+
result: Result<unknown, unknown>;
|
|
190
|
+
/** Events emitted */
|
|
191
|
+
events?: WorkflowEvent<unknown>[];
|
|
192
|
+
/** Total duration */
|
|
193
|
+
durationMs?: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Create a snapshot of a workflow execution.
|
|
197
|
+
*/
|
|
198
|
+
declare function createSnapshot(invocations: StepInvocation[], result: Result<unknown, unknown>, events?: WorkflowEvent<unknown>[]): WorkflowSnapshot;
|
|
199
|
+
/**
|
|
200
|
+
* Compare two workflow snapshots.
|
|
201
|
+
*/
|
|
202
|
+
declare function compareSnapshots(snapshot1: WorkflowSnapshot, snapshot2: WorkflowSnapshot): {
|
|
203
|
+
equal: boolean;
|
|
204
|
+
differences: string[];
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Create a deterministic clock for testing.
|
|
208
|
+
*/
|
|
209
|
+
declare function createTestClock(startTime?: number): {
|
|
210
|
+
now: () => number;
|
|
211
|
+
advance: (ms: number) => void;
|
|
212
|
+
set: (time: number) => void;
|
|
213
|
+
reset: () => void;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Helper to create ok outcomes.
|
|
217
|
+
*/
|
|
218
|
+
declare function okOutcome<T>(value: T): ScriptedOutcome<T, never>;
|
|
219
|
+
/**
|
|
220
|
+
* Helper to create err outcomes.
|
|
221
|
+
*/
|
|
222
|
+
declare function errOutcome<E>(error: E): ScriptedOutcome<never, E>;
|
|
223
|
+
/**
|
|
224
|
+
* Helper to create throw outcomes.
|
|
225
|
+
*/
|
|
226
|
+
declare function throwOutcome(error: unknown): ScriptedOutcome<never, never>;
|
|
227
|
+
|
|
228
|
+
export { type AssertionResult, type MockFunction, type MockStep, type ScriptedOutcome, type StepInvocation, type TestHarnessOptions, type WorkflowHarness, type WorkflowSnapshot, compareSnapshots, createMockFn, createSnapshot, createTestClock, createWorkflowHarness, errOutcome, okOutcome, throwOutcome };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { R as Result, A as AsyncResult, S as StepOptions, W as WorkflowEvent } from './core-BuTBsR0x.js';
|
|
2
|
+
import { A as AnyResultFn, E as ErrorsOfDeps } from './workflow-entry-C6nH8ByN.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* awaitly/testing
|
|
6
|
+
*
|
|
7
|
+
* Deterministic Workflow Testing Harness.
|
|
8
|
+
* Provides tools for scripting step outcomes and asserting workflow behavior.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A scripted outcome for a step.
|
|
13
|
+
*/
|
|
14
|
+
type ScriptedOutcome<T = unknown, E = unknown> = {
|
|
15
|
+
type: "ok";
|
|
16
|
+
value: T;
|
|
17
|
+
} | {
|
|
18
|
+
type: "err";
|
|
19
|
+
error: E;
|
|
20
|
+
} | {
|
|
21
|
+
type: "throw";
|
|
22
|
+
error: unknown;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Step invocation record.
|
|
26
|
+
*/
|
|
27
|
+
interface StepInvocation {
|
|
28
|
+
/** Step name */
|
|
29
|
+
name?: string;
|
|
30
|
+
/** Step key */
|
|
31
|
+
key?: string;
|
|
32
|
+
/** Invocation order (0-indexed) */
|
|
33
|
+
order: number;
|
|
34
|
+
/** Timestamp when step was invoked */
|
|
35
|
+
timestamp: number;
|
|
36
|
+
/** Duration in milliseconds */
|
|
37
|
+
durationMs?: number;
|
|
38
|
+
/** Result of the step */
|
|
39
|
+
result?: Result<unknown, unknown>;
|
|
40
|
+
/** Whether the step was from cache */
|
|
41
|
+
cached?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Assertion result.
|
|
45
|
+
*/
|
|
46
|
+
interface AssertionResult {
|
|
47
|
+
passed: boolean;
|
|
48
|
+
message: string;
|
|
49
|
+
expected?: unknown;
|
|
50
|
+
actual?: unknown;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Test harness options.
|
|
54
|
+
*/
|
|
55
|
+
interface TestHarnessOptions {
|
|
56
|
+
/** Whether to record step invocations */
|
|
57
|
+
recordInvocations?: boolean;
|
|
58
|
+
/** Custom clock for deterministic timing */
|
|
59
|
+
clock?: () => number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Mock step function that returns scripted outcomes.
|
|
63
|
+
*/
|
|
64
|
+
type MockStep<E> = {
|
|
65
|
+
/** Execute with a Result-returning operation */
|
|
66
|
+
<T, StepE extends E>(operation: () => Result<T, StepE> | AsyncResult<T, StepE>, options?: StepOptions | string): Promise<T>;
|
|
67
|
+
/** Execute with a direct Result */
|
|
68
|
+
<T, StepE extends E>(result: Result<T, StepE> | AsyncResult<T, StepE>, options?: StepOptions | string): Promise<T>;
|
|
69
|
+
/** step.try for catching throws */
|
|
70
|
+
try: <T, Err extends E>(operation: () => T | Promise<T>, options: {
|
|
71
|
+
error: Err;
|
|
72
|
+
name?: string;
|
|
73
|
+
key?: string;
|
|
74
|
+
} | {
|
|
75
|
+
onError: (cause: unknown) => Err;
|
|
76
|
+
name?: string;
|
|
77
|
+
key?: string;
|
|
78
|
+
}) => Promise<T>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Workflow test harness interface.
|
|
82
|
+
*/
|
|
83
|
+
interface WorkflowHarness<E, Deps> {
|
|
84
|
+
/**
|
|
85
|
+
* Script step outcomes in order.
|
|
86
|
+
* Each outcome will be returned for the corresponding step invocation.
|
|
87
|
+
*/
|
|
88
|
+
script(outcomes: ScriptedOutcome[]): void;
|
|
89
|
+
/**
|
|
90
|
+
* Script a specific step outcome by name or key.
|
|
91
|
+
*/
|
|
92
|
+
scriptStep(nameOrKey: string, outcome: ScriptedOutcome): void;
|
|
93
|
+
/**
|
|
94
|
+
* Run the workflow with scripted outcomes.
|
|
95
|
+
*/
|
|
96
|
+
run<T>(fn: (step: MockStep<E>, deps: Deps) => Promise<T>): Promise<Result<T, E | unknown>>;
|
|
97
|
+
/**
|
|
98
|
+
* Run the workflow with input.
|
|
99
|
+
*/
|
|
100
|
+
runWithInput<T, TInput>(input: TInput, fn: (step: MockStep<E>, deps: Deps, input: TInput) => Promise<T>): Promise<Result<T, E | unknown>>;
|
|
101
|
+
/**
|
|
102
|
+
* Get recorded step invocations.
|
|
103
|
+
*/
|
|
104
|
+
getInvocations(): StepInvocation[];
|
|
105
|
+
/**
|
|
106
|
+
* Assert that steps were invoked in order.
|
|
107
|
+
*/
|
|
108
|
+
assertSteps(expectedNames: string[]): AssertionResult;
|
|
109
|
+
/**
|
|
110
|
+
* Assert that a step was invoked with specific options.
|
|
111
|
+
*/
|
|
112
|
+
assertStepCalled(nameOrKey: string): AssertionResult;
|
|
113
|
+
/**
|
|
114
|
+
* Assert that a step was NOT invoked.
|
|
115
|
+
*/
|
|
116
|
+
assertStepNotCalled(nameOrKey: string): AssertionResult;
|
|
117
|
+
/**
|
|
118
|
+
* Assert the workflow result.
|
|
119
|
+
*/
|
|
120
|
+
assertResult<T>(result: Result<T, unknown>, expected: Result<T, unknown>): AssertionResult;
|
|
121
|
+
/**
|
|
122
|
+
* Clear all state for a new test.
|
|
123
|
+
*/
|
|
124
|
+
reset(): void;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a test harness for a workflow.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const harness = createWorkflowHarness({ fetchUser, chargeCard });
|
|
132
|
+
*
|
|
133
|
+
* // Script step outcomes
|
|
134
|
+
* harness.script([
|
|
135
|
+
* { type: 'ok', value: { id: '1', name: 'Alice' } },
|
|
136
|
+
* { type: 'ok', value: { transactionId: 'tx_123' } },
|
|
137
|
+
* ]);
|
|
138
|
+
*
|
|
139
|
+
* // Run the workflow
|
|
140
|
+
* const result = await harness.run(async (step, { fetchUser, chargeCard }) => {
|
|
141
|
+
* const user = await step(() => fetchUser('1'), 'fetch-user');
|
|
142
|
+
* const charge = await step(() => chargeCard(100), 'charge-card');
|
|
143
|
+
* return { user, charge };
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* // Assert
|
|
147
|
+
* expect(result.ok).toBe(true);
|
|
148
|
+
* harness.assertSteps(['fetch-user', 'charge-card']);
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
declare function createWorkflowHarness<Deps extends Record<string, AnyResultFn>>(deps: Deps, options?: TestHarnessOptions): WorkflowHarness<ErrorsOfDeps<Deps>, Deps>;
|
|
152
|
+
/**
|
|
153
|
+
* Create a mock Result-returning function.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const fetchUser = createMockFn<User, 'NOT_FOUND'>();
|
|
158
|
+
*
|
|
159
|
+
* fetchUser.returns(ok({ id: '1', name: 'Alice' }));
|
|
160
|
+
* // or
|
|
161
|
+
* fetchUser.returnsOnce(ok({ id: '1', name: 'Alice' }));
|
|
162
|
+
* fetchUser.returnsOnce(err('NOT_FOUND'));
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function createMockFn<T, E>(): MockFunction<T, E>;
|
|
166
|
+
/**
|
|
167
|
+
* Mock function interface.
|
|
168
|
+
*/
|
|
169
|
+
interface MockFunction<T, E> {
|
|
170
|
+
(...args: unknown[]): AsyncResult<T, E>;
|
|
171
|
+
/** Set the default return value */
|
|
172
|
+
returns(result: Result<T, E>): MockFunction<T, E>;
|
|
173
|
+
/** Queue a return value for the next call */
|
|
174
|
+
returnsOnce(result: Result<T, E>): MockFunction<T, E>;
|
|
175
|
+
/** Get all call arguments */
|
|
176
|
+
getCalls(): unknown[][];
|
|
177
|
+
/** Get the number of times the function was called */
|
|
178
|
+
getCallCount(): number;
|
|
179
|
+
/** Reset the mock */
|
|
180
|
+
reset(): void;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Workflow snapshot for comparison.
|
|
184
|
+
*/
|
|
185
|
+
interface WorkflowSnapshot {
|
|
186
|
+
/** Step invocations */
|
|
187
|
+
invocations: StepInvocation[];
|
|
188
|
+
/** Final result */
|
|
189
|
+
result: Result<unknown, unknown>;
|
|
190
|
+
/** Events emitted */
|
|
191
|
+
events?: WorkflowEvent<unknown>[];
|
|
192
|
+
/** Total duration */
|
|
193
|
+
durationMs?: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Create a snapshot of a workflow execution.
|
|
197
|
+
*/
|
|
198
|
+
declare function createSnapshot(invocations: StepInvocation[], result: Result<unknown, unknown>, events?: WorkflowEvent<unknown>[]): WorkflowSnapshot;
|
|
199
|
+
/**
|
|
200
|
+
* Compare two workflow snapshots.
|
|
201
|
+
*/
|
|
202
|
+
declare function compareSnapshots(snapshot1: WorkflowSnapshot, snapshot2: WorkflowSnapshot): {
|
|
203
|
+
equal: boolean;
|
|
204
|
+
differences: string[];
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Create a deterministic clock for testing.
|
|
208
|
+
*/
|
|
209
|
+
declare function createTestClock(startTime?: number): {
|
|
210
|
+
now: () => number;
|
|
211
|
+
advance: (ms: number) => void;
|
|
212
|
+
set: (time: number) => void;
|
|
213
|
+
reset: () => void;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Helper to create ok outcomes.
|
|
217
|
+
*/
|
|
218
|
+
declare function okOutcome<T>(value: T): ScriptedOutcome<T, never>;
|
|
219
|
+
/**
|
|
220
|
+
* Helper to create err outcomes.
|
|
221
|
+
*/
|
|
222
|
+
declare function errOutcome<E>(error: E): ScriptedOutcome<never, E>;
|
|
223
|
+
/**
|
|
224
|
+
* Helper to create throw outcomes.
|
|
225
|
+
*/
|
|
226
|
+
declare function throwOutcome(error: unknown): ScriptedOutcome<never, never>;
|
|
227
|
+
|
|
228
|
+
export { type AssertionResult, type MockFunction, type MockStep, type ScriptedOutcome, type StepInvocation, type TestHarnessOptions, type WorkflowHarness, type WorkflowSnapshot, compareSnapshots, createMockFn, createSnapshot, createTestClock, createWorkflowHarness, errOutcome, okOutcome, throwOutcome };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var D=e=>({ok:!0,value:e}),b=(e,i)=>({ok:!1,error:e,...i?.cause!==void 0?{cause:i.cause}:{}});var ae=e=>typeof e=="object"&&e!==null&&e.type==="UNEXPECTED_ERROR",W=Symbol.for("step_timeout_marker");function ee(e){return typeof e!="object"||e===null?!1:e.type==="STEP_TIMEOUT"?!0:W in e}function ie(e){if(!(typeof e!="object"||e===null)){if(e.type==="STEP_TIMEOUT"){let i=e;return{timeoutMs:i.timeoutMs,stepName:i.stepName,stepKey:i.stepKey,attempt:i.attempt}}if(W in e)return e[W]}}var re=Symbol("early-exit");function pe(e,i){return{[re]:!0,error:e,meta:i}}function ce(e){return typeof e=="object"&&e!==null&&e[re]===!0}var oe=Symbol("mapper-exception");function le(e){return{[oe]:!0,thrown:e}}function Ee(e){return typeof e=="object"&&e!==null&&e[oe]===!0}function ye(e){return typeof e=="string"?{name:e}:e??{}}function G(e,i){let{backoff:p,initialDelay:d,maxDelay:m,jitter:v}=i,l;switch(p){case"fixed":l=d;break;case"linear":l=d*e;break;case"exponential":l=d*Math.pow(2,e-1);break}if(l=Math.min(l,m),v){let u=l*.25*Math.random();l=l+u}return Math.floor(l)}function z(e){return new Promise(i=>setTimeout(i,e))}var te=Symbol("timeout");async function me(e,i,p){let d=new AbortController,m=i.error??{type:"STEP_TIMEOUT",stepName:p.name,stepKey:p.key,timeoutMs:i.ms,attempt:p.attempt},v,l=new Promise((I,S)=>{v=setTimeout(()=>{d.abort(),S({[te]:!0,error:m})},i.ms)}),u;i.signal?u=Promise.resolve(e(d.signal)):u=Promise.resolve(e());try{return await Promise.race([u,l])}catch(I){if(typeof I=="object"&&I!==null&&I[te]===!0){let S=I.error;if(typeof S=="object"&&S!==null&&S.type!=="STEP_TIMEOUT"){let F={timeoutMs:i.ms,stepName:p.name,stepKey:p.key,attempt:p.attempt};W in S?S[W]=F:Object.defineProperty(S,W,{value:F,enumerable:!1,writable:!0,configurable:!1})}throw S}throw I}finally{clearTimeout(v)}}var V={backoff:"exponential",initialDelay:100,maxDelay:3e4,jitter:!0,retryOn:()=>!0,onRetry:()=>{}};async function ne(e,i){let{onError:p,onEvent:d,catchUnexpected:m,workflowId:v,context:l}=i&&typeof i=="object"?i:{},u=v??crypto.randomUUID(),I=!p&&!m,S=[],F=0,N=a=>a??`step_${++F}`,k=a=>{let s=a.context!==void 0||l===void 0?a:{...a,context:l};if(s.type==="step_success"){let c=s.stepId;for(let E=S.length-1;E>=0;E--){let h=S[E];if(h.type==="race"&&!h.winnerId){h.winnerId=c;break}}}d?.(s,l)},U=pe,X=a=>ce(a),$=(a,s)=>I?s?.origin==="result"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:a,...s.resultCause!==void 0?{cause:s.resultCause}:{}}}:s?.origin==="throw"?{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"throw",error:a,thrown:s.thrown}}:{type:"UNEXPECTED_ERROR",cause:{type:"STEP_FAILURE",origin:"result",error:a}}:a,H=a=>({type:"UNEXPECTED_ERROR",cause:a.meta.origin==="result"?{type:"STEP_FAILURE",origin:"result",error:a.error,...a.meta.resultCause!==void 0?{cause:a.meta.resultCause}:{}}:{type:"STEP_FAILURE",origin:"throw",error:a.error,thrown:a.meta.thrown}});try{let s=function(y,r){let n=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let t=performance.now(),o=!1;S.push({scopeId:n,type:"parallel"});let g=()=>{if(o)return;o=!0;let T=S.findIndex(w=>w.scopeId===n);T!==-1&&S.splice(T,1),k({type:"scope_end",workflowId:u,scopeId:n,ts:Date.now(),durationMs:performance.now()-t})};k({type:"scope_start",workflowId:u,scopeId:n,scopeType:"parallel",name:y,ts:Date.now()});try{let T=await r();if(g(),!T.ok)throw p?.(T.error,y,l),U(T.error,{origin:"result",resultCause:T.cause});return T.value}catch(T){throw g(),T}})()},c=function(y,r){let n=Object.keys(y),t=r.name??`Parallel(${n.join(", ")})`,o=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let g=performance.now(),T=!1;S.push({scopeId:o,type:"parallel"});let w=()=>{if(T)return;T=!0;let R=S.findIndex(x=>x.scopeId===o);R!==-1&&S.splice(R,1),k({type:"scope_end",workflowId:u,scopeId:o,ts:Date.now(),durationMs:performance.now()-g})};k({type:"scope_start",workflowId:u,scopeId:o,scopeType:"parallel",name:t,ts:Date.now()});try{let R=await new Promise(O=>{if(n.length===0){O([]);return}let M=!1,A=n.length,L=new Array(n.length);for(let _=0;_<n.length;_++){let j=n[_],Y=_;Promise.resolve(y[j]()).catch(C=>b({type:"PROMISE_REJECTED",cause:C},{cause:{type:"PROMISE_REJECTION",reason:C}})).then(C=>{if(!M){if(!C.ok){M=!0,O([{key:j,result:C}]);return}L[Y]={key:j,result:C},A--,A===0&&O(L)}})}});w();let x={};for(let{key:O,result:M}of R){if(!M.ok)throw p?.(M.error,O,l),U(M.error,{origin:"result",resultCause:M.cause});x[O]=M.value}return x}catch(R){throw w(),R}})()};var Q=s,Z=c;let a=(y,r)=>(async()=>{let n=ye(r),{name:t,key:o,retry:g,timeout:T}=n,w=N(o),R=d,x=R?performance.now():0;if(!(typeof y=="function")){if(g&&g.attempts>1)throw new Error("step: retry options require a function operation. Direct Promise/Result values cannot be re-executed on retry. Wrap your operation in a function: step(() => yourOperation, { retry: {...} })");if(T)throw new Error("step: timeout options require a function operation. Direct Promise/Result values cannot be wrapped with timeout after they've started. Wrap your operation in a function: step(() => yourOperation, { timeout: {...} })")}let A={attempts:Math.max(1,g?.attempts??1),backoff:g?.backoff??V.backoff,initialDelay:g?.initialDelay??V.initialDelay,maxDelay:g?.maxDelay??V.maxDelay,jitter:g?.jitter??V.jitter,retryOn:g?.retryOn??V.retryOn,onRetry:g?.onRetry??V.onRetry};d&&k({type:"step_start",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now()});let L;for(let C=1;C<=A.attempts;C++){let ue=R?performance.now():0;try{let f;if(typeof y=="function"?T?f=await me(y,T,{name:t,key:o,attempt:C}):f=await y():f=await y,f.ok){let K=performance.now()-x;return k({type:"step_success",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:K}),o&&k({type:"step_complete",workflowId:u,stepKey:o,name:t,ts:Date.now(),durationMs:K,result:f}),f.value}if(L=f,C<A.attempts&&A.retryOn(f.error,C)){let K=G(C,A);k({type:"step_retry",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),attempt:C+1,maxAttempts:A.attempts,delayMs:K,error:f.error}),A.onRetry(f.error,C,K),await z(K);continue}A.attempts>1&&k({type:"step_retries_exhausted",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:performance.now()-x,attempts:C,lastError:f.error});break}catch(f){let K=performance.now()-ue;if(X(f))throw k({type:"step_aborted",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:K}),f;if(ee(f)){let P=ie(f),q=T?.ms??P?.timeoutMs??0;if(k({type:"step_timeout",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),timeoutMs:q,attempt:C}),C<A.attempts&&A.retryOn(f,C)){let B=G(C,A);k({type:"step_retry",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),attempt:C+1,maxAttempts:A.attempts,delayMs:B,error:f}),A.onRetry(f,C,B),await z(B);continue}A.attempts>1&&k({type:"step_retries_exhausted",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:performance.now()-x,attempts:C,lastError:f})}if(C<A.attempts&&A.retryOn(f,C)){let P=G(C,A);k({type:"step_retry",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),attempt:C+1,maxAttempts:A.attempts,delayMs:P,error:f}),A.onRetry(f,C,P),await z(P);continue}A.attempts>1&&!ee(f)&&k({type:"step_retries_exhausted",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:performance.now()-x,attempts:C,lastError:f});let J=performance.now()-x;if(m){let P;try{P=m(f)}catch(q){throw le(q)}throw k({type:"step_error",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:J,error:P}),o&&k({type:"step_complete",workflowId:u,stepKey:o,name:t,ts:Date.now(),durationMs:J,result:b(P,{cause:f}),meta:{origin:"throw",thrown:f}}),p?.(P,t,l),U(P,{origin:"throw",thrown:f})}else{let P={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:f}};throw k({type:"step_error",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:J,error:P}),o&&k({type:"step_complete",workflowId:u,stepKey:o,name:t,ts:Date.now(),durationMs:J,result:b(P,{cause:f}),meta:{origin:"throw",thrown:f}}),f}}}let _=L,j=performance.now()-x,Y=$(_.error,{origin:"result",resultCause:_.cause});throw k({type:"step_error",workflowId:u,stepId:w,stepKey:o,name:t,ts:Date.now(),durationMs:j,error:Y}),o&&k({type:"step_complete",workflowId:u,stepKey:o,name:t,ts:Date.now(),durationMs:j,result:_,meta:{origin:"result",resultCause:_.cause}}),p?.(_.error,t,l),U(_.error,{origin:"result",resultCause:_.cause})})();a.try=(y,r)=>{let n=r.name,t=r.key,o=N(t),g="error"in r?()=>r.error:r.onError,T=d;return(async()=>{let w=T?performance.now():0;d&&k({type:"step_start",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now()});try{let R=await y(),x=performance.now()-w;return k({type:"step_success",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now(),durationMs:x}),t&&k({type:"step_complete",workflowId:u,stepKey:t,name:n,ts:Date.now(),durationMs:x,result:D(R)}),R}catch(R){let x=g(R),O=performance.now()-w,M=$(x,{origin:"throw",thrown:R});throw k({type:"step_error",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now(),durationMs:O,error:M}),t&&k({type:"step_complete",workflowId:u,stepKey:t,name:n,ts:Date.now(),durationMs:O,result:b(x,{cause:R}),meta:{origin:"throw",thrown:R}}),p?.(x,n,l),U(x,{origin:"throw",thrown:R})}})()},a.fromResult=(y,r)=>{let n=r.name,t=r.key,o=N(t),g="error"in r?()=>r.error:r.onError,T=d;return(async()=>{let w=T?performance.now():0;d&&k({type:"step_start",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now()});let R=await y();if(R.ok){let x=performance.now()-w;return k({type:"step_success",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now(),durationMs:x}),t&&k({type:"step_complete",workflowId:u,stepKey:t,name:n,ts:Date.now(),durationMs:x,result:D(R.value)}),R.value}else{let x=g(R.error),O=performance.now()-w,M=$(x,{origin:"result",resultCause:R.error});throw k({type:"step_error",workflowId:u,stepId:o,stepKey:t,name:n,ts:Date.now(),durationMs:O,error:M}),t&&k({type:"step_complete",workflowId:u,stepKey:t,name:n,ts:Date.now(),durationMs:O,result:b(x,{cause:R.error}),meta:{origin:"result",resultCause:R.error}}),p?.(x,n,l),U(x,{origin:"result",resultCause:R.error})}})()},a.retry=(y,r)=>a(y,{name:r.name,key:r.key,retry:{attempts:r.attempts,backoff:r.backoff,initialDelay:r.initialDelay,maxDelay:r.maxDelay,jitter:r.jitter,retryOn:r.retryOn,onRetry:r.onRetry},timeout:r.timeout}),a.withTimeout=(y,r)=>a(y,{name:r.name,key:r.key,timeout:r}),a.parallel=((...y)=>{if(typeof y[0]=="string"){let r=y[0],n=y[1];return s(r,n)}else{let r=y[0],n=y[1]??{};return c(r,n)}}),a.race=(y,r)=>{let n=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let t=performance.now(),o=!1,g={scopeId:n,type:"race",winnerId:void 0};S.push(g);let T=()=>{if(o)return;o=!0;let w=S.findIndex(R=>R.scopeId===n);w!==-1&&S.splice(w,1),k({type:"scope_end",workflowId:u,scopeId:n,ts:Date.now(),durationMs:performance.now()-t,winnerId:g.winnerId})};k({type:"scope_start",workflowId:u,scopeId:n,scopeType:"race",name:y,ts:Date.now()});try{let w=await r();if(T(),!w.ok)throw p?.(w.error,y,l),U(w.error,{origin:"result",resultCause:w.cause});return w.value}catch(w){throw T(),w}})()},a.allSettled=(y,r)=>{let n=`scope_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return(async()=>{let t=performance.now(),o=!1;S.push({scopeId:n,type:"allSettled"});let g=()=>{if(o)return;o=!0;let T=S.findIndex(w=>w.scopeId===n);T!==-1&&S.splice(T,1),k({type:"scope_end",workflowId:u,scopeId:n,ts:Date.now(),durationMs:performance.now()-t})};k({type:"scope_start",workflowId:u,scopeId:n,scopeType:"allSettled",name:y,ts:Date.now()});try{let T=await r();if(g(),!T.ok)throw p?.(T.error,y,l),U(T.error,{origin:"result",resultCause:T.cause});return T.value}catch(T){throw g(),T}})()};let h=await e(a);return D(h)}catch(a){if(Ee(a))throw a.thrown;if(X(a)){let c=a.meta.origin==="throw"?a.meta.thrown:a.meta.resultCause;if(m||p)return b(a.error,{cause:c});if(ae(a.error))return b(a.error,{cause:c});let E=H(a);return b(E,{cause:c})}if(m){let c=m(a);return p?.(c,"unexpected",l),b(c,{cause:a})}let s={type:"UNEXPECTED_ERROR",cause:{type:"UNCAUGHT_EXCEPTION",thrown:a}};return p?.(s,"unexpected",l),b(s,{cause:a})}}ne.strict=(e,i)=>ne(e,i);function se(e){return typeof e=="object"&&e!==null&&"__earlyExit"in e&&e.__earlyExit===!0}function ke(e,i={}){let{recordInvocations:p=!0,clock:d=Date.now}=i,m=[],v=new Map,l=0,u=[];function I(s){m=[...s],l=0,v.clear()}function S(s,c){v.set(s,c)}function F(s){if(s&&v.has(s))return v.get(s);if(l<m.length)return m[l++]}function N(){let s=async(c,E)=>{let h=typeof E=="string"?{name:E}:E??{},y=h.name??h.key,r=d(),n={name:h.name,key:h.key,order:u.length,timestamp:r};p&&u.push(n);let t=F(y);if(t)switch(n.durationMs=d()-r,t.type){case"ok":return n.result=D(t.value),t.value;case"err":throw n.result=b(t.error),{__earlyExit:!0,error:t.error};case"throw":throw t.error}let o=typeof c=="function"?await c():await c;if(n.durationMs=d()-r,n.result=o,!o.ok)throw{__earlyExit:!0,error:o.error};return o.value};return s.try=async(c,E)=>{let h=E.name??E.key,y=d(),r={name:E.name,key:E.key,order:u.length,timestamp:y};p&&u.push(r);let n=F(h);if(n)switch(r.durationMs=d()-y,n.type){case"ok":return r.result=D(n.value),n.value;case"err":throw r.result=b(n.error),{__earlyExit:!0,error:n.error};case"throw":throw n.error}try{let t=await c();return r.durationMs=d()-y,r.result=D(t),t}catch(t){r.durationMs=d()-y;let o="error"in E?E.error:E.onError(t);throw r.result=b(o),{__earlyExit:!0,error:o}}},s}async function k(s){let c=N();try{let E=await s(c,e);return D(E)}catch(E){return se(E)?b(E.error):b({type:"UNEXPECTED_ERROR",cause:E})}}async function U(s,c){let E=N();try{let h=await c(E,e,s);return D(h)}catch(h){return se(h)?b(h.error):b({type:"UNEXPECTED_ERROR",cause:h})}}function X(){return[...u]}function $(s){let c=u.map(h=>h.name??h.key??"unnamed").filter(h=>h!=="unnamed"),E=JSON.stringify(c)===JSON.stringify(s);return{passed:E,message:E?`Steps invoked in order: ${s.join(", ")}`:`Expected steps [${s.join(", ")}] but got [${c.join(", ")}]`,expected:s,actual:c}}function H(s){let c=u.some(E=>E.name===s||E.key===s);return{passed:c,message:c?`Step "${s}" was invoked`:`Step "${s}" was NOT invoked`,expected:s,actual:c}}function Q(s){let c=u.some(E=>E.name===s||E.key===s);return{passed:!c,message:c?`Step "${s}" was invoked but should not have been`:`Step "${s}" was correctly NOT invoked`,expected:"not called",actual:c?"called":"not called"}}function Z(s,c){let E=s.ok===c.ok&&(s.ok?JSON.stringify(s.value)===JSON.stringify(c.value):JSON.stringify(s.error)===JSON.stringify(c.error));return{passed:E,message:E?"Result matches expected":"Result does not match expected",expected:c,actual:s}}function a(){m=[],v.clear(),l=0,u=[]}return{script:I,scriptStep:S,run:k,runWithInput:U,getInvocations:X,assertSteps:$,assertStepCalled:H,assertStepNotCalled:Q,assertResult:Z,reset:a}}function we(){let e,i=[],p=[],d=((...m)=>{if(p.push(m),i.length>0)return Promise.resolve(i.shift());if(e)return Promise.resolve(e);throw new Error("Mock function called without configured return value")});return d.returns=m=>(e=m,d),d.returnsOnce=m=>(i.push(m),d),d.getCalls=()=>[...p],d.getCallCount=()=>p.length,d.reset=()=>{e=void 0,i.length=0,p.length=0},d}function Te(e,i,p){let d=e.reduce((m,v)=>m+(v.durationMs??0),0);return{invocations:e.map(m=>({...m,timestamp:0})),result:i,events:p?.map(m=>({...m,ts:0})),durationMs:d}}function de(e,i){let p=[];e.invocations.length!==i.invocations.length&&p.push(`Invocation count: ${e.invocations.length} vs ${i.invocations.length}`);let d=Math.max(e.invocations.length,i.invocations.length);for(let m=0;m<d;m++){let v=e.invocations[m],l=i.invocations[m];if(!v){p.push(`Step ${m}: missing in first snapshot`);continue}if(!l){p.push(`Step ${m}: missing in second snapshot`);continue}v.name!==l.name&&p.push(`Step ${m} name: "${v.name}" vs "${l.name}"`),v.key!==l.key&&p.push(`Step ${m} key: "${v.key}" vs "${l.key}"`),v.result?.ok!==l.result?.ok&&p.push(`Step ${m} result: ${v.result?.ok?"ok":"err"} vs ${l.result?.ok?"ok":"err"}`)}return e.result.ok!==i.result.ok&&p.push(`Final result: ${e.result.ok?"ok":"err"} vs ${i.result.ok?"ok":"err"}`),{equal:p.length===0,differences:p}}function fe(e=0){let i=e;return{now:()=>i,advance:p=>{i+=p},set:p=>{i=p},reset:()=>{i=e}}}function Re(e){return{type:"ok",value:e}}function Ce(e){return{type:"err",error:e}}function xe(e){return{type:"throw",error:e}}export{de as compareSnapshots,we as createMockFn,Te as createSnapshot,fe as createTestClock,ke as createWorkflowHarness,Ce as errOutcome,Re as okOutcome,xe as throwOutcome};
|
|
2
|
+
//# sourceMappingURL=testing.js.map
|