pure-effect 0.18.0 → 0.20.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/CHANGELOG.md +21 -0
- package/README.md +678 -306
- package/index.d.ts +82 -22
- package/index.js +625 -48
- package/package.json +2 -1
package/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type AskState<T, E = unknown, Ctx = unknown> = {
|
|
|
35
35
|
// Every optional option and hook takes `undefined`, which keeps its default, under `exactOptionalPropertyTypes` too.
|
|
36
36
|
export type RetryOptions = {
|
|
37
37
|
/** Tries after the first, so `2` makes at most 3 calls. A positive integer, 3 by default. */
|
|
38
|
-
|
|
38
|
+
retries?: number | undefined;
|
|
39
39
|
/** Milliseconds before the first retry, 100 by default. */
|
|
40
40
|
delay?: number | undefined;
|
|
41
41
|
/** Multiplies the wait before each later retry, so `2` doubles it. 1 by default, the same wait each time. */
|
|
@@ -58,8 +58,8 @@ export type RetryState<T, E = unknown, Ctx = unknown, R = T> = {
|
|
|
58
58
|
export type RetryExhaustedError<Thrown = unknown> = {
|
|
59
59
|
retryExhausted: true;
|
|
60
60
|
lastError: Thrown;
|
|
61
|
-
/** The `
|
|
62
|
-
|
|
61
|
+
/** The `retries` option, so the retried steps ran one more time than this. */
|
|
62
|
+
retries: number;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
export type ParallelOptions = {
|
|
@@ -82,7 +82,7 @@ export type ParallelState<
|
|
|
82
82
|
effects: { [K in keyof T]: Effect<T[K], BranchError, Ctx> };
|
|
83
83
|
next(values: [...V]): Effect<R, E, Ctx>;
|
|
84
84
|
/** A frozen copy of the options the `Parallel` was built with. */
|
|
85
|
-
options
|
|
85
|
+
options: Readonly<ParallelOptions>;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export type Effect<T, E = unknown, Ctx = unknown> =
|
|
@@ -194,6 +194,9 @@ export declare function Command<R, T = R, E = unknown, Ctx = unknown>(
|
|
|
194
194
|
/** The name a Command is known by in traces, replays and spans: `meta.name`, else `cmd.name`, else `'anonymous'`. */
|
|
195
195
|
export declare function commandName(command: CommandState<any, any, any, any>): string;
|
|
196
196
|
|
|
197
|
+
/** Reads the context passed to `runEffect`. With no argument it succeeds with the context; name its type as `Ask<AppCtx>()`. */
|
|
198
|
+
export declare function Ask<Ctx = unknown>(): AskState<Ctx, never, Ctx>;
|
|
199
|
+
|
|
197
200
|
/** Reads the context passed to `runEffect`. Give the value, error and context types, or none. */
|
|
198
201
|
export declare function Ask<T = never, E = never, Ctx = unknown, N extends AnyEffect = Effect<T, E, Ctx>>(
|
|
199
202
|
next: (context: Ctx) => N
|
|
@@ -1245,23 +1248,70 @@ export declare function effectPipe<
|
|
|
1245
1248
|
>;
|
|
1246
1249
|
// END effectPipe overloads.
|
|
1247
1250
|
|
|
1251
|
+
// The symbol and the iterator's yield type exist only in the declarations, never at runtime: together they are how
|
|
1252
|
+
// `effectGen` reads each step's Effect type out of the body's yield type.
|
|
1253
|
+
declare const stepEffect: unique symbol;
|
|
1254
|
+
declare const stepContext: unique symbol;
|
|
1255
|
+
|
|
1256
|
+
/** A step for `yield*` inside `effectGen`, which gives back the Effect's value. Using it any other way throws. */
|
|
1257
|
+
// The context is also a function parameter, which TypeScript compares the opposite way to the Effect, so of two steps
|
|
1258
|
+
// it merges in a generator's yield type it can only drop the one that reads less context.
|
|
1259
|
+
export type Step<X extends AnyEffect, C = EffectContext<X>> = {
|
|
1260
|
+
readonly [stepEffect]?: X;
|
|
1261
|
+
readonly [stepContext]?: (context: C) => void;
|
|
1262
|
+
[Symbol.iterator](): Generator<Step<X, C>, EffectValue<X>, unknown>;
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
/** Wraps an Effect for `yield*` inside `effectGen`: `const user = yield* step(findUser(email))`. */
|
|
1266
|
+
export declare function step<X extends AnyEffect>(effect: X): Step<X>;
|
|
1267
|
+
|
|
1268
|
+
type StepEffectOf<Y> = Y extends Step<infer X, any> ? X : never;
|
|
1269
|
+
// An `any` return would match both branches and turn the error union into `any`, so it counts as a plain value.
|
|
1270
|
+
type GenIsAny<R> = 0 extends 1 & R ? true : false;
|
|
1271
|
+
type ReturnedEffect<R> = GenIsAny<R> extends true ? never : R extends AnyEffect ? R : never;
|
|
1272
|
+
type GenValue<R> = GenIsAny<R> extends true ? any : R extends AnyEffect ? EffectValue<R> : R;
|
|
1273
|
+
type GenError<Y, R> = EffectError<StepEffectOf<Y>> | EffectError<ReturnedEffect<R>>;
|
|
1274
|
+
// A step that reads no context gives `unknown`, which would erase the others in an intersection, so it is dropped. Each
|
|
1275
|
+
// context is boxed, so one that is itself a union is intersected whole rather than member by member.
|
|
1276
|
+
type GenNeeds<X> = X extends AnyEffect
|
|
1277
|
+
? EffectContext<X> extends infer C
|
|
1278
|
+
? unknown extends C
|
|
1279
|
+
? never
|
|
1280
|
+
: { context: C }
|
|
1281
|
+
: never
|
|
1282
|
+
: never;
|
|
1283
|
+
type GenIntersection<U> = (U extends any ? (u: U) => void : never) extends (i: infer I) => void ? I : never;
|
|
1284
|
+
type GenContext<Y, R> = [GenNeeds<StepEffectOf<Y> | ReturnedEffect<R>>] extends [never]
|
|
1285
|
+
? unknown
|
|
1286
|
+
: GenIntersection<GenNeeds<StepEffectOf<Y> | ReturnedEffect<R>>> extends { context: infer C }
|
|
1287
|
+
? C
|
|
1288
|
+
: unknown;
|
|
1289
|
+
|
|
1290
|
+
/**
|
|
1291
|
+
* Builds a flow function from a generator function, written with `yield* step(effect)` where `async` code writes
|
|
1292
|
+
* `await`. A plain return value is a `Success`; return a `Failure` to stop.
|
|
1293
|
+
*/
|
|
1294
|
+
export declare function effectGen<I, Y extends Step<any, any>, R>(
|
|
1295
|
+
body: (input: I) => Generator<Y, R, unknown>
|
|
1296
|
+
): (input: I) => Effect<GenValue<R>, GenError<Y, R>, GenContext<Y, R>>;
|
|
1297
|
+
|
|
1248
1298
|
/**
|
|
1249
1299
|
* Wraps each Command, and each `Parallel`. Call `op` and return its result; a hook that calls another passes `path` on.
|
|
1250
1300
|
*/
|
|
1251
1301
|
export type StepRunner = (
|
|
1252
1302
|
name: string,
|
|
1253
|
-
type:
|
|
1303
|
+
type: 'Command' | 'Parallel',
|
|
1254
1304
|
op: (decision?: ParallelDecision) => Promise<unknown>,
|
|
1255
1305
|
path: string
|
|
1256
1306
|
) => Promise<unknown>;
|
|
1257
1307
|
|
|
1258
1308
|
/**
|
|
1259
|
-
* Which branch, if any, cancelled a `Parallel`, and how many branches it had; `branch: null` means an
|
|
1260
|
-
* did. A replay refuses a `Parallel` whose count differs, and a decision without one is not checked.
|
|
1309
|
+
* Which branch, if any, cancelled a `Parallel`, whether it threw, and how many branches it had; `branch: null` means an
|
|
1310
|
+
* enclosing one did. A replay refuses a `Parallel` whose count differs, and a decision without one is not checked.
|
|
1261
1311
|
*/
|
|
1262
1312
|
export type ParallelDecision =
|
|
1263
1313
|
| { cancelled: false; branches?: number | undefined }
|
|
1264
|
-
| { cancelled: true; branch: number | null; branches?: number | undefined };
|
|
1314
|
+
| { cancelled: true; branch: number | null; branches?: number | undefined; threw?: true | undefined };
|
|
1265
1315
|
|
|
1266
1316
|
/**
|
|
1267
1317
|
* `flowName` is `context.flowName`, or `''` when the context has none. `initialInput` is what the flow was called with
|
|
@@ -1317,7 +1367,7 @@ export type ReplayStep = {
|
|
|
1317
1367
|
/** `meta.name`, `cmd.name`, or 'anonymous'; 'Parallel' for a `Parallel`'s step. */
|
|
1318
1368
|
name: string;
|
|
1319
1369
|
/** `'Command'`, or `'Parallel'` for a `Parallel`'s decision. */
|
|
1320
|
-
type:
|
|
1370
|
+
type: 'Command' | 'Parallel';
|
|
1321
1371
|
/** The step's position in the flow, the same in every run; a Resolver should key off it. */
|
|
1322
1372
|
path: string;
|
|
1323
1373
|
};
|
|
@@ -1392,16 +1442,15 @@ export type RecordOptions<Ctx = unknown> = RecorderOptions & {
|
|
|
1392
1442
|
};
|
|
1393
1443
|
|
|
1394
1444
|
/**
|
|
1395
|
-
* Runs a flow for real and returns its outcome with a trace
|
|
1396
|
-
*
|
|
1445
|
+
* Runs a flow for real and returns its outcome with a trace. The trace holds the flow's input when `effectPipe` or
|
|
1446
|
+
* `effectGen` built it.
|
|
1397
1447
|
*/
|
|
1398
|
-
export declare function recordEffect<
|
|
1399
|
-
|
|
1400
|
-
initialInput: I,
|
|
1448
|
+
export declare function recordEffect<T, E = unknown, Ctx = unknown>(
|
|
1449
|
+
effect: Effect<T, E, Ctx>,
|
|
1401
1450
|
...options: unknown extends Ctx
|
|
1402
1451
|
? [options?: RecordOptions<CheckedContext<Ctx>>]
|
|
1403
1452
|
: [options: RecordOptions<CheckedContext<Ctx>> & { context: RunContext<CheckedContext<Ctx>> }]
|
|
1404
|
-
): Promise<{ result: SuccessState<T> | FailureState<E>; trace: TraceLog<
|
|
1453
|
+
): Promise<{ result: SuccessState<T> | FailureState<E>; trace: TraceLog<unknown, Ctx> }>;
|
|
1405
1454
|
|
|
1406
1455
|
export interface ReplayOptions<Ctx = unknown> {
|
|
1407
1456
|
/** Context for `Ask`; defaults to the one the trace recorded. */
|
|
@@ -1448,13 +1497,24 @@ export declare function replayEffect<T, E = unknown, Ctx = unknown>(
|
|
|
1448
1497
|
: [options: ReplayOptions<CheckedContext<Ctx>> & { context: RunContext<CheckedContext<Ctx>> }]
|
|
1449
1498
|
): Promise<Omit<Replay<T, E>, 'unreached'> & { unreached?: TraceEntry[] }>;
|
|
1450
1499
|
|
|
1451
|
-
|
|
1500
|
+
export interface TimeTravelOptions<Ctx = unknown> {
|
|
1501
|
+
/** Receives each narrated line; `console.log` by default. */
|
|
1502
|
+
log?: ((...args: any[]) => void) | undefined;
|
|
1503
|
+
/** Context for `Ask`; defaults to the one the trace recorded. */
|
|
1504
|
+
context?: RunContext<Ctx> | undefined;
|
|
1505
|
+
/** The build replaying the trace; a trace recorded at another build gets a warning. */
|
|
1506
|
+
version?: string | undefined;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Replays a trace and logs each step with its recorded timing, warning about what will not replay as recorded. Returns
|
|
1511
|
+
* what `replayEffect` returns for a trace.
|
|
1512
|
+
*/
|
|
1452
1513
|
export declare function timeTravel<T, E = unknown, Ctx = unknown>(
|
|
1453
1514
|
flowFn: (input: any) => Effect<T, E, Ctx>,
|
|
1454
1515
|
traceLog: TraceLog,
|
|
1455
|
-
options?:
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
): Promise<SuccessState<T> | FailureState<E>>;
|
|
1516
|
+
options?: TimeTravelOptions<CheckedContext<Ctx>>
|
|
1517
|
+
): Promise<Replay<T, E>>;
|
|
1518
|
+
|
|
1519
|
+
// Only the declarations marked `export` are public; without this, a declaration file exports every top-level one.
|
|
1520
|
+
export {};
|