effect-browser 0.3.0-beta.13 → 0.3.0-beta.15
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 +59 -19
- package/dist/{Browser-QiT6Cdn_.mjs → Browser-BBSySyjT.mjs} +2 -2
- package/dist/{Browser-QiT6Cdn_.mjs.map → Browser-BBSySyjT.mjs.map} +1 -1
- package/dist/Browser.d.mts +1 -1
- package/dist/Browser.mjs +1 -1
- package/dist/Cdp.mjs +1 -1
- package/dist/Chromium.mjs +1 -1
- package/dist/Expect.d.mts +1 -1
- package/dist/Expect.mjs +1 -1
- package/dist/{Frame-Di2PxyF7.d.mts → Frame-DkNdjjjr.d.mts} +3 -5
- package/dist/Frame.d.mts +1 -1
- package/dist/Frame.mjs +2 -4
- package/dist/Frame.mjs.map +1 -1
- package/dist/Moment.d.mts +1 -1
- package/dist/Moment.mjs +7 -3
- package/dist/Moment.mjs.map +1 -1
- package/dist/{Page-B7X6DQUn.d.mts → Page-DL9eylp3.d.mts} +2 -2
- package/dist/Page.d.mts +1 -1
- package/dist/{Plan-BS0jpENY.d.mts → Plan-6As6vcwT.d.mts} +3 -3
- package/dist/{Plan-Bl2NM1D5.mjs → Plan-CXA2BW-G.mjs} +4 -6
- package/dist/Plan-CXA2BW-G.mjs.map +1 -0
- package/dist/Plan.d.mts +1 -1
- package/dist/Plan.mjs +1 -1
- package/dist/Policy.d.mts +1 -1
- package/dist/Presentation.d.mts +1 -1
- package/dist/Presentation.mjs +1 -1
- package/dist/Stage.d.mts +5 -4
- package/dist/Stage.mjs +53 -20
- package/dist/Stage.mjs.map +1 -1
- package/dist/{Supervisor-CYWFK3vM.d.mts → Supervisor-5tZ47hR5.d.mts} +40 -10
- package/dist/{Supervisor-TNKNOVzu.mjs → Supervisor-DaGlQVjv.mjs} +120 -68
- package/dist/Supervisor-DaGlQVjv.mjs.map +1 -0
- package/dist/Supervisor.d.mts +2 -2
- package/dist/Supervisor.mjs +2 -2
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/{page-ZxMhw16l.mjs → page-B92QnRfT.mjs} +196 -166
- package/dist/page-B92QnRfT.mjs.map +1 -0
- package/package.json +1 -1
- package/src/Frame.ts +2 -4
- package/src/Moment.ts +14 -3
- package/src/Plan.ts +7 -6
- package/src/Stage.ts +61 -21
- package/src/Supervisor.ts +205 -172
- package/src/internal/pictures/capture.ts +172 -188
- package/src/internal/pictures/pictures.ts +50 -73
- package/src/internal/pictures/quiescence.ts +94 -0
- package/src/internal/reading/ready.ts +9 -3
- package/src/internal/supervisor/lifecycle.ts +40 -15
- package/dist/Plan-Bl2NM1D5.mjs.map +0 -1
- package/dist/Supervisor-TNKNOVzu.mjs.map +0 -1
- package/dist/page-ZxMhw16l.mjs.map +0 -1
package/src/Supervisor.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import {
|
|
22
22
|
Cause,
|
|
23
23
|
Clock,
|
|
24
|
+
Context,
|
|
24
25
|
DateTime,
|
|
25
26
|
Deferred,
|
|
26
27
|
Duration,
|
|
@@ -92,9 +93,32 @@ export interface Opened<A = void> {
|
|
|
92
93
|
readonly release?: Effect.Effect<Lifecycle.Released> | undefined;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
/** Facts available before an acquisition attempt; awaiting `after` never changes release order. */
|
|
97
|
+
export interface Admission {
|
|
98
|
+
readonly number: number;
|
|
99
|
+
/**
|
|
100
|
+
* Every earlier session that stopped serving, including failed attempts and older drains.
|
|
101
|
+
* Waits for all pending releases; an Unconfirmed end remains visible to later attempts.
|
|
102
|
+
* Undefined before any release. A still-serving nonexclusive predecessor is excluded.
|
|
103
|
+
*/
|
|
104
|
+
readonly after: Effect.Effect<Lifecycle.Released> | undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Optional service supplied during `open`. Providers report failed-attempt cleanup here before
|
|
109
|
+
* their scope closes; successful generations instead return `Opened.release`. Reporting does
|
|
110
|
+
* not release resources or replace the original acquisition failure.
|
|
111
|
+
*/
|
|
112
|
+
export class Releasing extends Context.Service<
|
|
113
|
+
Releasing,
|
|
114
|
+
{ readonly released: (outcome: Lifecycle.Released) => Effect.Effect<void> }
|
|
115
|
+
>()("effect-browser/Supervisor/Releasing") {}
|
|
116
|
+
|
|
95
117
|
export interface Options<E, R, A = void> {
|
|
96
118
|
/** Opens one generation in the scope it is given. */
|
|
97
119
|
readonly open: Effect.Effect<Opened<A>, E, R>;
|
|
120
|
+
/** Runs before every open attempt, including the first; failures follow `reopen` and `definite`. */
|
|
121
|
+
readonly admit?: ((opening: Admission) => Effect.Effect<void, E, R>) | undefined;
|
|
98
122
|
/** When to try a failed open again. Defaults to exponential from 1 second, for 5 minutes. */
|
|
99
123
|
readonly reopen?: Schedule.Schedule<unknown, E> | undefined;
|
|
100
124
|
/**
|
|
@@ -132,6 +156,8 @@ export interface Lease<A> {
|
|
|
132
156
|
readonly generation: number;
|
|
133
157
|
readonly browser: Browser.Service;
|
|
134
158
|
readonly value: A;
|
|
159
|
+
/** Declare this serving generation unusable and revoke it now; stale declarations do nothing. */
|
|
160
|
+
readonly lose: (detail: string) => Effect.Effect<void>;
|
|
135
161
|
/** First notice that serving ended; loss or retirement may revoke before its deadline. */
|
|
136
162
|
readonly ending: Effect.Effect<Superseded>;
|
|
137
163
|
/** An independent scope; its resources close before provider release. Fails once revoked. */
|
|
@@ -167,13 +193,17 @@ interface Held<A> {
|
|
|
167
193
|
readonly opened: Opened<A>;
|
|
168
194
|
readonly ending: Deferred.Deferred<Superseded>;
|
|
169
195
|
readonly revoked: Deferred.Deferred<Superseded>;
|
|
196
|
+
readonly released: Deferred.Deferred<Lifecycle.Released>;
|
|
170
197
|
readonly scope: Scope.Closeable;
|
|
171
198
|
}
|
|
172
199
|
|
|
173
200
|
type State<A> = Lifecycle.State<Held<A>, Hold>;
|
|
174
|
-
type
|
|
175
|
-
|
|
176
|
-
) => Effect.Effect<
|
|
201
|
+
type Input<A> = Lifecycle.Input<Held<A>, Hold>;
|
|
202
|
+
type Step<A> = Lifecycle.Step<Held<A>, Hold>;
|
|
203
|
+
type Apply<A> = (input: Input<A>) => Effect.Effect<Step<A>>;
|
|
204
|
+
|
|
205
|
+
const superseded = (generation: number, { reason, revokeAt }: Lifecycle.Draining) =>
|
|
206
|
+
new Superseded({ generation, reason, revokeAt });
|
|
177
207
|
|
|
178
208
|
const retired = () => new Unavailable({ reason: "retired", detail: "the supervisor was retired" });
|
|
179
209
|
|
|
@@ -183,15 +213,6 @@ const describe = (cause: Cause.Cause<unknown>) => {
|
|
|
183
213
|
return Predicate.isError(error) ? error.message : String(error);
|
|
184
214
|
};
|
|
185
215
|
|
|
186
|
-
const unavailable = (
|
|
187
|
-
resolution: Extract<Lifecycle.Resolution<unknown>, { readonly _tag: "Unavailable" }>,
|
|
188
|
-
) =>
|
|
189
|
-
new Unavailable({
|
|
190
|
-
reason: resolution.reason,
|
|
191
|
-
detail: resolution.detail,
|
|
192
|
-
cause: resolution.cause,
|
|
193
|
-
});
|
|
194
|
-
|
|
195
216
|
const timedOut = () =>
|
|
196
217
|
new Unavailable({ reason: "opening", detail: "no generation opened within the wait" });
|
|
197
218
|
|
|
@@ -216,7 +237,7 @@ const waitFor = <A, H>(
|
|
|
216
237
|
const result = (resolution: Lifecycle.Resolution<A>) =>
|
|
217
238
|
resolution._tag === "Serve"
|
|
218
239
|
? Effect.succeed(resolution.live)
|
|
219
|
-
: Effect.fail(
|
|
240
|
+
: Effect.fail(new Unavailable(resolution));
|
|
220
241
|
|
|
221
242
|
return Effect.suspend(() => {
|
|
222
243
|
const current = Lifecycle.resolve(state.value, target);
|
|
@@ -259,25 +280,26 @@ const leasing = <A>(
|
|
|
259
280
|
now: () => number,
|
|
260
281
|
clock: Clock.Clock,
|
|
261
282
|
) => {
|
|
262
|
-
const borrow = (hold: Hold, wait: Duration.Duration)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
283
|
+
const borrow = Effect.fnUntraced(function* (hold: Hold, wait: Duration.Duration) {
|
|
284
|
+
const until = now() + Duration.toMillis(wait);
|
|
285
|
+
|
|
286
|
+
for (;;) {
|
|
287
|
+
const step = yield* apply({ _tag: "Hold", hold });
|
|
288
|
+
|
|
289
|
+
if (step.borrowed !== undefined) return step.borrowed;
|
|
290
|
+
yield* waitFor(state, Duration.millis(Math.max(0, until - now())), clock).pipe(
|
|
291
|
+
Effect.interruptible,
|
|
292
|
+
);
|
|
293
|
+
if (now() >= until) return yield* timedOut();
|
|
294
|
+
}
|
|
295
|
+
});
|
|
276
296
|
|
|
277
297
|
const leaseOf = (live: Lifecycle.Live<Held<A>>): Lease<A> => ({
|
|
278
298
|
generation: live.number,
|
|
279
299
|
browser: live.value.opened.browser,
|
|
280
300
|
value: live.value.opened.value,
|
|
301
|
+
lose: (detail) =>
|
|
302
|
+
apply({ _tag: "Lost", number: live.number, cause: "declared", detail }).pipe(Effect.asVoid),
|
|
281
303
|
ending: Deferred.await(live.value.ending),
|
|
282
304
|
retain: Effect.uninterruptible(
|
|
283
305
|
Effect.gen(function* () {
|
|
@@ -310,9 +332,18 @@ const leasing = <A>(
|
|
|
310
332
|
apply({ _tag: "Let", number: live.number, hold }).pipe(Effect.asVoid),
|
|
311
333
|
);
|
|
312
334
|
|
|
335
|
+
const revoked = Deferred.await(live.value.revoked).pipe(Effect.flatMap(Effect.fail));
|
|
336
|
+
|
|
313
337
|
return yield* Effect.raceFirst(
|
|
314
|
-
work(leaseOf(live))
|
|
315
|
-
|
|
338
|
+
work(leaseOf(live)).pipe(
|
|
339
|
+
// Declaring loss and returning can finish before the race's other fiber runs.
|
|
340
|
+
Effect.tap(() =>
|
|
341
|
+
Deferred.isDone(live.value.revoked).pipe(
|
|
342
|
+
Effect.flatMap((done) => (done ? revoked : Effect.void)),
|
|
343
|
+
),
|
|
344
|
+
),
|
|
345
|
+
),
|
|
346
|
+
revoked,
|
|
316
347
|
);
|
|
317
348
|
}),
|
|
318
349
|
);
|
|
@@ -338,16 +369,12 @@ const acquireGeneration = <A, E, R>(
|
|
|
338
369
|
scope,
|
|
339
370
|
ending: Deferred.makeUnsafe<Superseded>(),
|
|
340
371
|
revoked: Deferred.makeUnsafe<Superseded>(),
|
|
372
|
+
released: Deferred.makeUnsafe<Lifecycle.Released>(),
|
|
341
373
|
})),
|
|
342
374
|
),
|
|
343
375
|
),
|
|
344
376
|
);
|
|
345
377
|
|
|
346
|
-
const changedUntil = <A>(
|
|
347
|
-
state: SubscriptionRef.SubscriptionRef<State<A>>,
|
|
348
|
-
predicate: (current: State<A>) => boolean,
|
|
349
|
-
) => SubscriptionRef.changes(state).pipe(Stream.filter(predicate), Stream.runHead, Effect.asVoid);
|
|
350
|
-
|
|
351
378
|
const watchGeneration = <A>(
|
|
352
379
|
live: Lifecycle.Live<Held<A>>,
|
|
353
380
|
rotateBefore: Duration.Duration | undefined,
|
|
@@ -369,53 +396,62 @@ const watchGeneration = <A>(
|
|
|
369
396
|
return Effect.all([loss, rotation], { concurrency: 2, discard: true });
|
|
370
397
|
};
|
|
371
398
|
|
|
372
|
-
//
|
|
373
|
-
const releaseGeneration = <A>(
|
|
399
|
+
// After revocation, retained resources and use finalizers let go before the provider.
|
|
400
|
+
const releaseGeneration = Effect.fnUntraced(function* <A>(
|
|
374
401
|
command: Extract<Lifecycle.Command<Held<A>>, { readonly _tag: "Release" | "Leave" }>,
|
|
375
402
|
state: SubscriptionRef.SubscriptionRef<State<A>>,
|
|
376
403
|
apply: Apply<A>,
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
revokeAt: ending.revokeAt,
|
|
387
|
-
}),
|
|
388
|
-
);
|
|
389
|
-
const holds = Lifecycle.lookup(state.value, live.number)?.holds ?? [];
|
|
390
|
-
|
|
391
|
-
const cleaned = yield* Effect.forEach(
|
|
392
|
-
holds,
|
|
393
|
-
(hold) =>
|
|
394
|
-
Effect.exit(hold._tag === "Retained" ? Scope.close(hold.scope, Exit.void) : Effect.void),
|
|
395
|
-
{ concurrency: 8 },
|
|
396
|
-
);
|
|
404
|
+
) {
|
|
405
|
+
const { live } = command;
|
|
406
|
+
|
|
407
|
+
const cleaned = yield* Effect.forEach(
|
|
408
|
+
Lifecycle.lookup(state.value, live.number)?.holds ?? [],
|
|
409
|
+
(hold) =>
|
|
410
|
+
Effect.exit(hold._tag === "Retained" ? Scope.close(hold.scope, Exit.void) : Effect.void),
|
|
411
|
+
{ concurrency: 8 },
|
|
412
|
+
);
|
|
397
413
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
414
|
+
yield* SubscriptionRef.changes(state).pipe(
|
|
415
|
+
Stream.filter((current) => (Lifecycle.lookup(current, live.number)?.holds.size ?? 0) === 0),
|
|
416
|
+
Stream.runHead,
|
|
417
|
+
);
|
|
402
418
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
),
|
|
419
|
+
const released =
|
|
420
|
+
command._tag === "Leave"
|
|
421
|
+
? undefined
|
|
422
|
+
: yield* (live.value.opened.release ?? Effect.succeed(new Lifecycle.Settled())).pipe(
|
|
423
|
+
Effect.catchCause((cause) =>
|
|
424
|
+
Effect.succeed(
|
|
425
|
+
new Lifecycle.Unconfirmed({ detail: `the release failed: ${describe(cause)}` }),
|
|
411
426
|
),
|
|
412
|
-
)
|
|
427
|
+
),
|
|
428
|
+
);
|
|
413
429
|
|
|
414
|
-
|
|
430
|
+
const closed = yield* Effect.exit(Scope.close(live.value.scope, Exit.void));
|
|
415
431
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
432
|
+
yield* apply({ _tag: "Released", number: live.number, released });
|
|
433
|
+
if (released !== undefined) yield* Deferred.succeed(live.value.released, released);
|
|
434
|
+
yield* Exit.asVoidAll([...cleaned, closed]);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const admissionAfter = <A>(state: State<A>) => {
|
|
438
|
+
const released = state.released;
|
|
439
|
+
|
|
440
|
+
const pending = [...state.draining.values()]
|
|
441
|
+
.filter((drain) => drain._tag !== "Leaving")
|
|
442
|
+
.map(({ live }) => live.value.released);
|
|
443
|
+
|
|
444
|
+
return pending.length === 0 && released === undefined
|
|
445
|
+
? undefined
|
|
446
|
+
: Effect.gen(function* () {
|
|
447
|
+
let outcome = released;
|
|
448
|
+
|
|
449
|
+
for (const release of pending)
|
|
450
|
+
outcome = Lifecycle.combine(outcome, yield* Deferred.await(release));
|
|
451
|
+
|
|
452
|
+
return outcome ?? new Lifecycle.Settled();
|
|
453
|
+
});
|
|
454
|
+
};
|
|
419
455
|
|
|
420
456
|
const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
|
|
421
457
|
options: MakeOptions<E, R, A>,
|
|
@@ -425,7 +461,6 @@ const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
|
|
|
425
461
|
const context = yield* Effect.context<Exclude<R, Scope.Scope>>();
|
|
426
462
|
const own = yield* Scope.fork(yield* Effect.scope);
|
|
427
463
|
const exclusive = options.exclusive ?? false;
|
|
428
|
-
const definite = options.definite ?? (() => false);
|
|
429
464
|
|
|
430
465
|
const reopen: Schedule.Schedule<unknown, E> =
|
|
431
466
|
options.reopen ??
|
|
@@ -449,118 +484,116 @@ const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
|
|
|
449
484
|
const runOpen = yield* FiberHandle.runtime(opening)();
|
|
450
485
|
const runRelease = yield* FiberMap.runtime(releases)();
|
|
451
486
|
|
|
452
|
-
const apply = (
|
|
453
|
-
input:
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
.withPermit(
|
|
457
|
-
Effect.gen(function* () {
|
|
458
|
-
const current = state.value;
|
|
459
|
-
const step = Lifecycle.transition(current, input, exclusive, now(), handover);
|
|
487
|
+
const apply: Apply<A | void> = Effect.fnUntraced(
|
|
488
|
+
function* (input: Input<A | void>) {
|
|
489
|
+
const current = state.value;
|
|
490
|
+
const step = Lifecycle.transition(current, input, exclusive, now(), handover);
|
|
460
491
|
|
|
461
|
-
|
|
462
|
-
|
|
492
|
+
if (step.state !== current) yield* SubscriptionRef.set(state, step.state);
|
|
493
|
+
yield* perform(step);
|
|
463
494
|
|
|
464
|
-
|
|
495
|
+
return step;
|
|
496
|
+
},
|
|
497
|
+
(effect) => applying.withPermit(effect),
|
|
498
|
+
Effect.uninterruptible,
|
|
499
|
+
Effect.provideService(Clock.Clock, clock),
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
const perform = Effect.fnUntraced(function* (step: Step<A | void>): Effect.fn.Return<void> {
|
|
503
|
+
for (const [number, generation] of step.events)
|
|
504
|
+
PubSub.publishUnsafe(events, [
|
|
505
|
+
new Generation({
|
|
506
|
+
number,
|
|
507
|
+
at: clock.currentTimeMillisUnsafe(),
|
|
508
|
+
hostTime: now(),
|
|
509
|
+
state: generation,
|
|
465
510
|
}),
|
|
466
|
-
)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
yield* Effect.forkIn(
|
|
487
|
-
Effect.provideContext(watchGeneration(command.live, rotateBefore, apply), context),
|
|
488
|
-
command.live.value.scope,
|
|
489
|
-
);
|
|
490
|
-
break;
|
|
491
|
-
case "Drain": {
|
|
492
|
-
const ending = new Superseded({
|
|
493
|
-
generation: command.live.number,
|
|
494
|
-
reason: command.ending.reason,
|
|
495
|
-
revokeAt: command.ending.revokeAt,
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
yield* Deferred.succeed(command.live.value.ending, ending);
|
|
499
|
-
yield* Effect.forkIn(
|
|
500
|
-
until(ending.revokeAt, now).pipe(
|
|
501
|
-
Effect.andThen(
|
|
502
|
-
Effect.suspend(() => apply({ _tag: "Revoke", number: command.live.number })),
|
|
503
|
-
),
|
|
511
|
+
]);
|
|
512
|
+
for (const command of step.commands)
|
|
513
|
+
switch (command._tag) {
|
|
514
|
+
case "Open":
|
|
515
|
+
runOpen(openGeneration(command.number));
|
|
516
|
+
break;
|
|
517
|
+
case "Watch":
|
|
518
|
+
yield* Effect.forkIn(
|
|
519
|
+
Effect.provideContext(watchGeneration(command.live, rotateBefore, apply), context),
|
|
520
|
+
command.live.value.scope,
|
|
521
|
+
);
|
|
522
|
+
break;
|
|
523
|
+
case "Drain": {
|
|
524
|
+
const ending = superseded(command.live.number, command.ending);
|
|
525
|
+
|
|
526
|
+
yield* Deferred.succeed(command.live.value.ending, ending);
|
|
527
|
+
yield* Effect.forkIn(
|
|
528
|
+
until(ending.revokeAt, now).pipe(
|
|
529
|
+
Effect.andThen(
|
|
530
|
+
Effect.suspend(() => apply({ _tag: "Revoke", number: command.live.number })),
|
|
504
531
|
),
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
case "Release":
|
|
510
|
-
case "Leave":
|
|
511
|
-
runRelease(command.live.number, releaseGeneration(command, state, apply));
|
|
512
|
-
break;
|
|
532
|
+
),
|
|
533
|
+
command.live.value.scope,
|
|
534
|
+
);
|
|
535
|
+
break;
|
|
513
536
|
}
|
|
514
|
-
|
|
537
|
+
case "Release":
|
|
538
|
+
case "Leave":
|
|
539
|
+
yield* Deferred.succeed(
|
|
540
|
+
command.live.value.revoked,
|
|
541
|
+
superseded(command.live.number, command.ending),
|
|
542
|
+
);
|
|
543
|
+
runRelease(command.live.number, releaseGeneration(command, state, apply));
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
const openGeneration = (number: number) => {
|
|
549
|
+
const openOnce = acquireGeneration(
|
|
550
|
+
Effect.gen(function* () {
|
|
551
|
+
const after = admissionAfter(state.value);
|
|
552
|
+
|
|
553
|
+
if (exclusive && after !== undefined) yield* after;
|
|
554
|
+
yield* options.admit?.({ number, after }) ?? Effect.void;
|
|
515
555
|
|
|
516
|
-
|
|
556
|
+
return yield* options.open.pipe(
|
|
557
|
+
Effect.provideService(Releasing, {
|
|
558
|
+
released: (released) =>
|
|
559
|
+
apply({ _tag: "AttemptReleased", released }).pipe(Effect.asVoid),
|
|
560
|
+
}),
|
|
561
|
+
);
|
|
562
|
+
}),
|
|
563
|
+
own,
|
|
564
|
+
).pipe(Effect.provideContext(context));
|
|
517
565
|
|
|
518
|
-
|
|
519
|
-
Effect.uninterruptibleMask((restore) =>
|
|
566
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
520
567
|
restore(
|
|
521
|
-
(
|
|
522
|
-
|
|
523
|
-
:
|
|
524
|
-
)
|
|
525
|
-
Effect.andThen(
|
|
526
|
-
Effect.retry(openOnce, { schedule: reopen, while: (error) => !definite(error) }),
|
|
527
|
-
),
|
|
528
|
-
),
|
|
568
|
+
Effect.retry(openOnce, {
|
|
569
|
+
schedule: reopen,
|
|
570
|
+
while: (error) => !options.definite?.(error),
|
|
571
|
+
}),
|
|
529
572
|
).pipe(
|
|
530
573
|
Effect.exit,
|
|
531
|
-
Effect.flatMap((exit) =>
|
|
532
|
-
Exit.isSuccess(exit)
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
),
|
|
574
|
+
Effect.flatMap((exit) => {
|
|
575
|
+
if (Exit.isSuccess(exit))
|
|
576
|
+
return apply({ _tag: "Opened", live: { number, value: exit.value } });
|
|
577
|
+
const cause = exit.cause;
|
|
578
|
+
|
|
579
|
+
return apply(
|
|
580
|
+
Cause.hasInterruptsOnly(cause)
|
|
581
|
+
? { _tag: "Abandoned", number }
|
|
582
|
+
: { _tag: "Failed", number, detail: describe(cause), cause: Cause.squash(cause) },
|
|
583
|
+
);
|
|
584
|
+
}),
|
|
543
585
|
),
|
|
544
586
|
);
|
|
545
|
-
|
|
546
|
-
const use = leasing(state, apply, waitTimeout, now, clock);
|
|
587
|
+
};
|
|
547
588
|
|
|
548
589
|
const end = yield* Effect.cached(PubSub.publish(events, Exit.void));
|
|
549
590
|
|
|
550
|
-
const stop = (input:
|
|
551
|
-
apply(input)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
Deferred.isDone(releases.deferred).pipe(
|
|
557
|
-
Effect.flatMap((failed) => (failed ? FiberMap.join(releases) : Effect.void)),
|
|
558
|
-
),
|
|
559
|
-
),
|
|
560
|
-
),
|
|
561
|
-
Effect.ensuring(end),
|
|
562
|
-
Effect.asVoid,
|
|
563
|
-
);
|
|
591
|
+
const stop = Effect.fnUntraced(function* (input: Input<A | void>) {
|
|
592
|
+
yield* apply(input);
|
|
593
|
+
yield* FiberHandle.clear(opening);
|
|
594
|
+
yield* FiberMap.awaitEmpty(releases);
|
|
595
|
+
if (yield* Deferred.isDone(releases.deferred)) yield* FiberMap.join(releases);
|
|
596
|
+
}, Effect.ensuring(end));
|
|
564
597
|
|
|
565
598
|
const retire = yield* Effect.cached(stop({ _tag: "Retire" }));
|
|
566
599
|
|
|
@@ -569,7 +602,7 @@ const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
|
|
|
569
602
|
|
|
570
603
|
return {
|
|
571
604
|
browser: Effect.map(waitFor(state, waitTimeout, clock), (live) => live.value.opened.browser),
|
|
572
|
-
use,
|
|
605
|
+
use: leasing(state, apply, waitTimeout, now, clock),
|
|
573
606
|
states: Stream.fromPubSubTake(events),
|
|
574
607
|
rotate: apply({ _tag: "Rotate" }).pipe(
|
|
575
608
|
Effect.flatMap(({ reply }) =>
|