@expressive/steps 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/index.d.ts +203 -60
- package/dist/index.js +25 -4
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/skills/expressive-steps/SKILL.md +234 -0
package/README.md
CHANGED
|
@@ -55,4 +55,4 @@ const { orderId } = await order; // runs run(), typed by its return
|
|
|
55
55
|
|
|
56
56
|
A failed `courier.book` unwinds newest first: `refund`, then `release`, then every taken-back step's fields restored.
|
|
57
57
|
|
|
58
|
-
Reference: [docs/](docs/README.md), and the
|
|
58
|
+
Reference: [docs/](docs/README.md), and the package's agent skill ([skills/expressive-steps](skills/expressive-steps/SKILL.md)).
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,14 @@ import { Model } from "@expressive/mvc";
|
|
|
2
2
|
|
|
3
3
|
//#region src/steps.d.ts
|
|
4
4
|
declare namespace Steps {
|
|
5
|
+
/**
|
|
6
|
+
* Where the instance stands - reactive through `then.status`.
|
|
7
|
+
*
|
|
8
|
+
* `idle` - no run, or the last ended cleanly. `running`. `waiting` - parked on an open wait.
|
|
9
|
+
* `stopped`, `aborted`, `failed` - how the last run ended; held until the next one starts.
|
|
10
|
+
*/
|
|
5
11
|
type Status = 'idle' | 'running' | 'waiting' | 'stopped' | 'aborted' | 'failed';
|
|
6
|
-
/**
|
|
12
|
+
/** The run's result - what `run` resolves with; types `x.run()` and `await x`. */
|
|
7
13
|
type Result<T> = T extends {
|
|
8
14
|
run(...args: any[]): infer R;
|
|
9
15
|
} ? Awaited<R> : unknown;
|
|
@@ -21,12 +27,18 @@ declare namespace Steps {
|
|
|
21
27
|
/** Caught by `.catch()`: a step given the error (its key), `then` for a function, or `true` for none. */
|
|
22
28
|
rescue?: string | true;
|
|
23
29
|
}
|
|
24
|
-
/** What a step
|
|
30
|
+
/** What calling a step inside a step returns - inert, not awaitable; scheduled once the caller settles. */
|
|
25
31
|
type Token = Readonly<Node>;
|
|
32
|
+
/**
|
|
33
|
+
* A committed step, as `after` receives it - keys and arguments, no values.
|
|
34
|
+
*
|
|
35
|
+
* `id` is `key#n`, e.g. `pay#1`. The Runner keeps no journal - collect entries in `after`.
|
|
36
|
+
*/
|
|
26
37
|
interface Entry {
|
|
27
38
|
id: string;
|
|
28
39
|
key: string;
|
|
29
40
|
args: unknown[];
|
|
41
|
+
/** What the step scheduled. */
|
|
30
42
|
next: Node[];
|
|
31
43
|
/** Fields the step wrote, collections changed in place included. */
|
|
32
44
|
wrote: string[];
|
|
@@ -49,6 +61,7 @@ declare namespace Steps {
|
|
|
49
61
|
/** An `end` the runtime added - the plan drained without one. */
|
|
50
62
|
implicit?: true;
|
|
51
63
|
}
|
|
64
|
+
/** An open wait, as listed by `then.waiting`. */
|
|
52
65
|
interface Wait {
|
|
53
66
|
id: string;
|
|
54
67
|
/** Key of the step which opened the wait. */
|
|
@@ -60,23 +73,41 @@ declare namespace Steps {
|
|
|
60
73
|
/** When a duration wait closes, in epoch milliseconds. */
|
|
61
74
|
until?: number;
|
|
62
75
|
}
|
|
63
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* A `then.on` handler.
|
|
78
|
+
*
|
|
79
|
+
* A function - given the error, or nothing on a cancellation; process-local. For a durable run:
|
|
80
|
+
* a step method uncalled - given the same, recorded - or steps called for it, an array a group.
|
|
81
|
+
*/
|
|
64
82
|
type Handler = {
|
|
65
83
|
handler(reason?: unknown): unknown;
|
|
66
84
|
}['handler'] | Promise<void> | readonly Promise<void>[];
|
|
67
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Handlers for the outcomes of a step and everything it schedules.
|
|
87
|
+
*
|
|
88
|
+
* Per step they fire `catch`, `abort`, `reset`, `close`; across steps deepest first, newest first.
|
|
89
|
+
* Handlers run before fields are restored and see the step's partial writes; keys they write are kept.
|
|
90
|
+
*/
|
|
68
91
|
interface On {
|
|
69
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* The step is taken back - reset its effects.
|
|
94
|
+
*
|
|
95
|
+
* A failure leaving its subtree, `abort()`, a lost race; not `stop()`. Fields are restored after it.
|
|
96
|
+
*/
|
|
70
97
|
reset?: Handler;
|
|
71
98
|
/**
|
|
72
|
-
* A failure below
|
|
73
|
-
*
|
|
74
|
-
*
|
|
99
|
+
* A failure below that no closer `.catch` took - return to recover, throw to pass it on.
|
|
100
|
+
*
|
|
101
|
+
* Recovering unwinds the failed part of the subtree; the step's own effects stay and its `reset`
|
|
102
|
+
* does not run; steps it schedules replace the rest. One per step; runs before a `.catch` on the
|
|
103
|
+
* step's call. `then.retry()` replays the step.
|
|
75
104
|
*/
|
|
76
105
|
catch?: Handler;
|
|
77
106
|
/**
|
|
78
|
-
* The subtree ended without completing
|
|
79
|
-
*
|
|
107
|
+
* The subtree ended without completing - `commit()`'s counterpart.
|
|
108
|
+
*
|
|
109
|
+
* Given the error of a failure no `catch` recovered; nothing for a cancellation (`abort()`, `stop()`,
|
|
110
|
+
* a lost race) - `then.status` tells which. Skips a clean end, a recovered failure and `then.end()`.
|
|
80
111
|
*/
|
|
81
112
|
abort?: Handler;
|
|
82
113
|
/** The subtree ended, any way - a finally over it, last. */
|
|
@@ -86,6 +117,7 @@ declare namespace Steps {
|
|
|
86
117
|
type Aborted = InstanceType<typeof Steps.Aborted>;
|
|
87
118
|
/** Why a run or subtree ended: `stop()`. */
|
|
88
119
|
type Stopped = InstanceType<typeof Steps.Stopped>;
|
|
120
|
+
/** A plain copy of the controller's reactive fields - `then.get()`. */
|
|
89
121
|
interface Snapshot {
|
|
90
122
|
status: Status;
|
|
91
123
|
active: string | undefined;
|
|
@@ -96,8 +128,14 @@ declare namespace Steps {
|
|
|
96
128
|
}
|
|
97
129
|
interface Controller<T extends Model = Model> {
|
|
98
130
|
/**
|
|
99
|
-
* Inside a step: park
|
|
100
|
-
*
|
|
131
|
+
* Inside a step: park the plan for a duration - units summed.
|
|
132
|
+
*
|
|
133
|
+
* A wait nothing answers, journaled `wait#n` with `args: [ms]`. The next step in sequence runs once
|
|
134
|
+
* it passes - backoff, timeouts, nudges.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* this.then({ sec: 30 });
|
|
138
|
+
* this.retryUpload();
|
|
101
139
|
*/
|
|
102
140
|
<This>(this: This, duration: {
|
|
103
141
|
ms?: number;
|
|
@@ -107,123 +145,228 @@ declare namespace Steps {
|
|
|
107
145
|
day?: number;
|
|
108
146
|
}): Promise<void>;
|
|
109
147
|
/**
|
|
110
|
-
* Inside a step:
|
|
111
|
-
*
|
|
148
|
+
* Inside a step: one parallel group of `each`, called per item.
|
|
149
|
+
*
|
|
150
|
+
* `each` is a function issuing a step, or a step itself. The next step in sequence runs once every
|
|
151
|
+
* member, and all it schedules, is done.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* this.then(this.files, this.upload);
|
|
155
|
+
* this.then(this.sites, (site) => this.scan(site, 'deep'));
|
|
112
156
|
*/
|
|
113
157
|
<I, This>(this: This, items: Iterable<I>, each: (item: I, index: number) => unknown): Promise<void>;
|
|
114
158
|
/**
|
|
115
|
-
* Inside a step: park
|
|
116
|
-
*
|
|
117
|
-
*
|
|
159
|
+
* Inside a step: park the plan until one of these steps is called.
|
|
160
|
+
*
|
|
161
|
+
* Status `waiting`. Answering steps read `undefined` from outside unless an open wait accepts them.
|
|
162
|
+
* An outside call answers the wait whose opening step's first argument `===` its own, else the
|
|
163
|
+
* oldest; its Promise resolves when that answer's subtree is done. A call no wait accepts throws.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* this.then(this.approve, this.reject);
|
|
118
167
|
*/
|
|
119
168
|
<This>(this: This, ...steps: ((...args: any[]) => unknown)[]): Promise<void>;
|
|
120
169
|
/**
|
|
121
|
-
* Inside a step: run these tokens as one parallel group
|
|
122
|
-
*
|
|
170
|
+
* Inside a step: run these tokens as one parallel group.
|
|
171
|
+
*
|
|
172
|
+
* The next step in sequence runs once every member, and all it schedules, is done. Beside steps
|
|
173
|
+
* uncalled, each called step is a racer, started when the plan reaches the wait: the first answer
|
|
174
|
+
* wins and keeps all it did; losers' signals abort, what they scheduled drops, and each losing
|
|
175
|
+
* branch unwinds - resets run, fields restored.
|
|
123
176
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* settles, then each losing branch unwinds - resets run, fields restored. The winner keeps all it did.
|
|
177
|
+
* @example
|
|
178
|
+
* this.then(this.ship(order), this.receipt(order));
|
|
179
|
+
* this.then(this.allow, this.deny, this.timeout(call));
|
|
128
180
|
*/
|
|
129
181
|
<This>(this: This, ...steps: unknown[]): Promise<void>;
|
|
130
182
|
/** The instance this controller runs. */
|
|
131
183
|
readonly self: T;
|
|
184
|
+
/** Where the instance stands - see {@link Steps.Status}. */
|
|
132
185
|
readonly status: Steps.Status;
|
|
133
186
|
/** Key of a step in flight - the latest invoked, with a group in flight. */
|
|
134
187
|
readonly active: string | undefined;
|
|
135
188
|
/** Arguments of each `run()` waiting for the active run to finish. */
|
|
136
189
|
readonly queue: readonly unknown[][];
|
|
137
|
-
/** Open waits, oldest first. */
|
|
190
|
+
/** Open waits, oldest first - durations included. */
|
|
138
191
|
readonly waiting: readonly Steps.Wait[];
|
|
139
192
|
/** Runs finished - ended, failed, stopped or aborted. */
|
|
140
193
|
readonly runs: number;
|
|
194
|
+
/** What failed the last run; cleared when the next starts. */
|
|
141
195
|
readonly error: unknown;
|
|
142
196
|
/** A plain snapshot of status, active, queue, waiting, runs and error. */
|
|
143
197
|
get(): Steps.Snapshot;
|
|
144
198
|
/**
|
|
145
|
-
* End the run
|
|
146
|
-
*
|
|
199
|
+
* End the run early, keeping everything.
|
|
200
|
+
*
|
|
201
|
+
* Inside a step, where the plan reaches it; from outside, now. Steps in flight settle, what they
|
|
202
|
+
* schedule drops, only `close` fires; `run`'s tail then builds the result from fields. Takes no
|
|
203
|
+
* value - write why it ended to a field first. To halt now, `stop()` or `abort()`.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* if (this.found) this.then.end();
|
|
207
|
+
* else this.next();
|
|
147
208
|
*/
|
|
148
209
|
end(): void;
|
|
149
210
|
/**
|
|
150
|
-
* Inside a step: a gate
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
211
|
+
* Inside a step: a gate - only `max` at once; what the step schedules after it holds a slot until done.
|
|
212
|
+
*
|
|
213
|
+
* Queues FIFO. `max` as a function of `$`, the tracking proxy, is re-read live; `0` pauses. `key`
|
|
214
|
+
* shares the queue: an object across groups and instances (`only(1, this.manager)` is a mutex), any
|
|
215
|
+
* other value across this instance. Default: the enclosing group's, else the instance's. A delay
|
|
216
|
+
* before queueing is a duration ahead of it.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* this.then.only(($) => $.concurrency);
|
|
220
|
+
* this.upload(file);
|
|
156
221
|
*/
|
|
157
222
|
only(max: number | (($: T) => number), key?: unknown): void;
|
|
158
223
|
/**
|
|
159
|
-
* Halt, keeping what was done
|
|
160
|
-
*
|
|
161
|
-
*
|
|
224
|
+
* Halt, keeping what was done - status `stopped`.
|
|
225
|
+
*
|
|
226
|
+
* Pending steps and queued runs drop; in-flight steps settle and commit. The run, the queue and
|
|
227
|
+
* in-flight signals reject with a `Steps.Stopped` - `reason` as its `cause`, and its message when a string.
|
|
228
|
+
* `set(null)` on the instance stops too.
|
|
162
229
|
*/
|
|
163
230
|
stop(reason?: unknown): void;
|
|
164
231
|
/**
|
|
165
|
-
* Halt and unwind, as a failure would - status `
|
|
166
|
-
*
|
|
167
|
-
*
|
|
232
|
+
* Halt and unwind, as a failure would - status `aborted`.
|
|
233
|
+
*
|
|
234
|
+
* The run's resets run, newest first, and fields are restored. The run and queue reject with a
|
|
235
|
+
* `Steps.Aborted`, `reason` as for `stop`. A reset which throws fails the run: its error, given the
|
|
236
|
+
* `Steps.Aborted` as `cause`.
|
|
168
237
|
*/
|
|
169
238
|
abort(reason?: unknown): void;
|
|
170
|
-
/**
|
|
239
|
+
/**
|
|
240
|
+
* Inside a step: a point of no return.
|
|
241
|
+
*
|
|
242
|
+
* The step is not caught, retried, restored or undone; an unwind stops here and what came before stands.
|
|
243
|
+
*/
|
|
171
244
|
commit(): void;
|
|
172
|
-
/**
|
|
245
|
+
/**
|
|
246
|
+
* Inside a step: handlers for the outcomes of this step and all it schedules - see {@link Steps.On}.
|
|
247
|
+
*
|
|
248
|
+
* Register right after the effect it undoes. An unknown key throws.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* const hold = await inventory.hold(this.cart.items);
|
|
252
|
+
*
|
|
253
|
+
* this.then.on({ reset: () => inventory.release(hold) });
|
|
254
|
+
* this.pay();
|
|
255
|
+
*/
|
|
173
256
|
on(handlers: Steps.On): void;
|
|
174
257
|
/** Inside a `.catch` handler: restore the caught branch's fields now; writes after it stand. */
|
|
175
258
|
restore(): void;
|
|
176
259
|
/**
|
|
177
|
-
* Inside a step: its `AbortSignal` -
|
|
178
|
-
*
|
|
260
|
+
* Inside a step: its `AbortSignal` - pass it to the call the step awaits.
|
|
261
|
+
*
|
|
262
|
+
* Aborted on `stop()` or `abort()`, an outside `end`, a failure elsewhere, a race lost, and once the
|
|
263
|
+
* step settles. A step rejecting with its reason is not a failure.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* this.page = await fetch(this.url, { signal: this.then.signal });
|
|
179
267
|
*/
|
|
180
268
|
readonly signal: AbortSignal;
|
|
181
|
-
/** Inside a step: how many times `then.retry()` replayed it - 0 on a first run. In a handler, the failed one's. */
|
|
269
|
+
/** Inside a step: how many times `then.retry()` replayed it - `0` on a first run. In a handler, the failed one's. */
|
|
182
270
|
readonly retried: number;
|
|
183
271
|
/**
|
|
184
|
-
* Inside a `.catch` handler: schedule what the catch is attached to again
|
|
185
|
-
*
|
|
186
|
-
*
|
|
272
|
+
* Inside a `.catch` handler: schedule what the catch is attached to again, under the same handler.
|
|
273
|
+
*
|
|
274
|
+
* The failed step or the whole group, with its recorded args. The handler holds the policy; backoff
|
|
275
|
+
* belongs in the retried step. Throws outside a handler.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* this.scan(site).catch((error) => {
|
|
279
|
+
* if (this.then.retried >= 3) throw error;
|
|
280
|
+
* this.then.retry();
|
|
281
|
+
* });
|
|
187
282
|
*/
|
|
188
283
|
retry(): void;
|
|
189
284
|
}
|
|
190
285
|
interface Thenable<T extends Model = Model> {
|
|
191
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Join the active run - or, idle, call `run()`; settles with the last queued run's result.
|
|
288
|
+
*
|
|
289
|
+
* Rejects if idle and `run` takes arguments - give an optional one a default.
|
|
290
|
+
*/
|
|
192
291
|
<R1 = Steps.Result<T>, R2 = never>(onfulfilled?: ((value: Steps.Result<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
|
|
193
292
|
then: Steps.Runner<T>;
|
|
194
293
|
}
|
|
195
|
-
/**
|
|
196
|
-
* The controller, and a thenable - so the instance with it as `then`. Awaiting it joins the
|
|
197
|
-
* active run; idle, it calls `run()`, or rejects if `run` takes arguments.
|
|
198
|
-
*/
|
|
294
|
+
/** The controller, and a thenable - so the instance, with it as `then`, is awaitable. */
|
|
199
295
|
type Runner<T extends Model = Model> = Controller<T> & Thenable<T>;
|
|
200
296
|
}
|
|
201
|
-
/**
|
|
202
|
-
* A runnable Model: its methods are steps, `run()` starts a run and awaiting the instance joins
|
|
203
|
-
* it, resolving with what `run` returns. The members below are the contract - each optional.
|
|
204
|
-
*/
|
|
205
297
|
declare class Aborted extends Error {
|
|
206
298
|
name: string;
|
|
207
299
|
}
|
|
208
300
|
declare class Stopped extends Error {
|
|
209
301
|
name: string;
|
|
210
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* A runnable Model: every method is a step, a step schedules the next by calling it.
|
|
305
|
+
*
|
|
306
|
+
* Fields carry data between steps. Each step is atomic and registers its own reset; a failure unwinds
|
|
307
|
+
* what committed. `run()` starts a run; awaiting the instance joins it. In-process only.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* class Checkout extends Steps {
|
|
311
|
+
* cart!: Cart;
|
|
312
|
+
* charge?: Charge = undefined;
|
|
313
|
+
*
|
|
314
|
+
* async run() {
|
|
315
|
+
* this.pay();
|
|
316
|
+
* await this;
|
|
317
|
+
* return this.charge!.id;
|
|
318
|
+
* }
|
|
319
|
+
*
|
|
320
|
+
* protected async pay() {
|
|
321
|
+
* this.charge = await stripe.charge(this.cart.total);
|
|
322
|
+
* this.then.on({ reset: () => stripe.refund(this.charge!.id) });
|
|
323
|
+
* }
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* const id = await Checkout.new({ cart });
|
|
327
|
+
*/
|
|
211
328
|
declare class Steps extends Model {
|
|
212
329
|
/** Why a run or subtree ended: `abort()`, or a lost race. */
|
|
213
330
|
static readonly Aborted: typeof Aborted;
|
|
214
331
|
/** Why a run or subtree ended: `stop()`. */
|
|
215
332
|
static readonly Stopped: typeof Stopped;
|
|
216
|
-
/**
|
|
333
|
+
/**
|
|
334
|
+
* The controller - plan tokens inside a step, status and control from outside.
|
|
335
|
+
*
|
|
336
|
+
* Its fields are reactive through the instance: `x.get(($) => $.then.status)`, or read in a render.
|
|
337
|
+
* Enumerable - `{ ...x.then }` snapshots; not in `instance.get()`. Awaiting the instance awaits it.
|
|
338
|
+
*/
|
|
217
339
|
readonly then: Steps.Runner<this>;
|
|
218
340
|
constructor(...args: Model.Args);
|
|
219
341
|
/**
|
|
220
|
-
* The entry
|
|
221
|
-
*
|
|
342
|
+
* The entry - starts a run, or queues one while a run is active; settles with what it returns.
|
|
343
|
+
*
|
|
344
|
+
* Must be `async`; the only step callable from outside. Before `await this`: field writes and step
|
|
345
|
+
* calls only. After it, the tail: read fields, return or throw - step calls throw. Without `await this`
|
|
346
|
+
* it settles at once. `return await this` is a TS circularity - return from fields. Inside a step,
|
|
347
|
+
* `this.run()` throws.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* async run(url: string) {
|
|
351
|
+
* this.fetch(url);
|
|
352
|
+
* await this;
|
|
353
|
+
* return this.rows;
|
|
354
|
+
* }
|
|
222
355
|
*/
|
|
223
356
|
run?(...args: any[]): Promise<unknown>;
|
|
224
|
-
/**
|
|
357
|
+
/** Hook: before each step. Synchronous, the raw instance as `this`; writes are unjournaled. */
|
|
225
358
|
protected before?(step: string, args: unknown[]): void;
|
|
226
|
-
/**
|
|
359
|
+
/**
|
|
360
|
+
* Hook: after each step commits, given its entry - the only journal there is.
|
|
361
|
+
*
|
|
362
|
+
* Synchronous, the raw instance as `this`; writes are unjournaled. A try a catch took, and the failure
|
|
363
|
+
* ending the run, arrive with `entry.error`; a called `end` arrives too, an implicit one does not.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* protected after(step: string, entry: Steps.Entry) {
|
|
367
|
+
* this.log.push(entry);
|
|
368
|
+
* }
|
|
369
|
+
*/
|
|
227
370
|
protected after?(step: string, entry: Steps.Entry): void;
|
|
228
371
|
}
|
|
229
372
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -135,16 +135,37 @@ function install(self) {
|
|
|
135
135
|
listener(self, (key) => void (key === REFRESH && wire()));
|
|
136
136
|
self.get(null, () => control.stop());
|
|
137
137
|
}
|
|
138
|
-
/**
|
|
139
|
-
* A runnable Model: its methods are steps, `run()` starts a run and awaiting the instance joins
|
|
140
|
-
* it, resolving with what `run` returns. The members below are the contract - each optional.
|
|
141
|
-
*/
|
|
142
138
|
var Aborted = class extends Error {
|
|
143
139
|
name = "Aborted";
|
|
144
140
|
};
|
|
145
141
|
var Stopped = class extends Error {
|
|
146
142
|
name = "Stopped";
|
|
147
143
|
};
|
|
144
|
+
/**
|
|
145
|
+
* A runnable Model: every method is a step, a step schedules the next by calling it.
|
|
146
|
+
*
|
|
147
|
+
* Fields carry data between steps. Each step is atomic and registers its own reset; a failure unwinds
|
|
148
|
+
* what committed. `run()` starts a run; awaiting the instance joins it. In-process only.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* class Checkout extends Steps {
|
|
152
|
+
* cart!: Cart;
|
|
153
|
+
* charge?: Charge = undefined;
|
|
154
|
+
*
|
|
155
|
+
* async run() {
|
|
156
|
+
* this.pay();
|
|
157
|
+
* await this;
|
|
158
|
+
* return this.charge!.id;
|
|
159
|
+
* }
|
|
160
|
+
*
|
|
161
|
+
* protected async pay() {
|
|
162
|
+
* this.charge = await stripe.charge(this.cart.total);
|
|
163
|
+
* this.then.on({ reset: () => stripe.refund(this.charge!.id) });
|
|
164
|
+
* }
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* const id = await Checkout.new({ cart });
|
|
168
|
+
*/
|
|
148
169
|
var Steps = class extends Model {
|
|
149
170
|
/** Why a run or subtree ended: `abort()`, or a lost race. */
|
|
150
171
|
static Aborted = Aborted;
|