effect-browser 0.3.0-beta.15 → 0.3.0-beta.19

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.
Files changed (96) hide show
  1. package/README.md +18 -6
  2. package/dist/{Browser-BBSySyjT.mjs → Browser-H63wiekr.mjs} +5 -5
  3. package/dist/Browser-H63wiekr.mjs.map +1 -0
  4. package/dist/Browser.d.mts +2 -2
  5. package/dist/Browser.mjs +1 -1
  6. package/dist/{BrowserEvent-BRu2juEz.d.mts → BrowserEvent-dq6FRT8w.d.mts} +9 -9
  7. package/dist/BrowserEvent.d.mts +1 -1
  8. package/dist/BrowserEvent.mjs +7 -12
  9. package/dist/BrowserEvent.mjs.map +1 -1
  10. package/dist/Cdp.mjs +2 -2
  11. package/dist/{Change-iMlcuzVD.d.mts → Change-DqING3tr.d.mts} +2 -2
  12. package/dist/Change.d.mts +1 -1
  13. package/dist/Chromium.mjs +2 -2
  14. package/dist/Expect.d.mts +1 -1
  15. package/dist/Expect.mjs +2 -2
  16. package/dist/Moment.d.mts +1 -1
  17. package/dist/Moment.mjs +3 -3
  18. package/dist/Moment.mjs.map +1 -1
  19. package/dist/Motion-DND6I-64.d.mts +40 -0
  20. package/dist/Motion.d.mts +2 -40
  21. package/dist/Motion.mjs +1 -2
  22. package/dist/Motion.mjs.map +1 -1
  23. package/dist/{Page-B5cjaUp5.mjs → Page-CfB6Zn1p.mjs} +7 -15
  24. package/dist/Page-CfB6Zn1p.mjs.map +1 -0
  25. package/dist/{Page-DL9eylp3.d.mts → Page-ku6j5t-v.d.mts} +23 -25
  26. package/dist/Page.d.mts +1 -1
  27. package/dist/Page.mjs +1 -1
  28. package/dist/{Plan-CXA2BW-G.mjs → Plan-C-d5QYS8.mjs} +6 -9
  29. package/dist/Plan-C-d5QYS8.mjs.map +1 -0
  30. package/dist/{Plan-6As6vcwT.d.mts → Plan-DJ4hEEna.d.mts} +3 -3
  31. package/dist/Plan.d.mts +1 -1
  32. package/dist/Plan.mjs +1 -1
  33. package/dist/Policy.d.mts +1 -1
  34. package/dist/Policy.mjs +1 -1
  35. package/dist/Presentation.d.mts +2 -2
  36. package/dist/Presentation.mjs +3 -2
  37. package/dist/Presentation.mjs.map +1 -1
  38. package/dist/Stage.d.mts +1 -1
  39. package/dist/Stage.mjs +8 -7
  40. package/dist/Stage.mjs.map +1 -1
  41. package/dist/{Supervisor-DaGlQVjv.mjs → Supervisor-2mPjIYYs.mjs} +44 -19
  42. package/dist/Supervisor-2mPjIYYs.mjs.map +1 -0
  43. package/dist/{Supervisor-5tZ47hR5.d.mts → Supervisor-mVPeEUp8.d.mts} +12 -5
  44. package/dist/Supervisor.d.mts +1 -1
  45. package/dist/Supervisor.mjs +1 -1
  46. package/dist/{context-BAjRj7Xo.mjs → context-w3NJPRLo.mjs} +81 -9
  47. package/dist/context-w3NJPRLo.mjs.map +1 -0
  48. package/dist/index.d.mts +6 -6
  49. package/dist/index.mjs +4 -4
  50. package/dist/match.inpage-xOe9LNO0.mjs.map +1 -1
  51. package/dist/{page-B92QnRfT.mjs → page-BKwfF6z-.mjs} +213 -218
  52. package/dist/page-BKwfF6z-.mjs.map +1 -0
  53. package/package.json +1 -1
  54. package/src/Browser.ts +1 -1
  55. package/src/BrowserEvent.ts +7 -12
  56. package/src/Moment.ts +3 -6
  57. package/src/Motion.ts +1 -2
  58. package/src/Page.ts +18 -20
  59. package/src/Plan.ts +1 -2
  60. package/src/Presentation.ts +2 -1
  61. package/src/Stage.ts +10 -17
  62. package/src/Supervisor.ts +9 -9
  63. package/src/internal/input/actions.ts +53 -97
  64. package/src/internal/input/dispatch.ts +25 -11
  65. package/src/internal/input/edit.inpage.ts +26 -11
  66. package/src/internal/input/evidence.inpage.ts +1 -21
  67. package/src/internal/input/guard.inpage.ts +9 -29
  68. package/src/internal/input/guard.ts +15 -10
  69. package/src/internal/input/perform.ts +14 -10
  70. package/src/internal/input/pointer.ts +3 -6
  71. package/src/internal/input/replies.ts +19 -8
  72. package/src/internal/input/settle.ts +3 -2
  73. package/src/internal/input/targets.inpage.ts +6 -13
  74. package/src/internal/input/targets.ts +13 -32
  75. package/src/internal/page/bridge.ts +2 -2
  76. package/src/internal/page/context.ts +15 -3
  77. package/src/internal/page/navigation.ts +3 -12
  78. package/src/internal/pictures/capture.ts +11 -4
  79. package/src/internal/pictures/clock.ts +8 -3
  80. package/src/internal/pictures/motion.ts +65 -0
  81. package/src/internal/pictures/pictures.ts +22 -17
  82. package/src/internal/pictures/utilsBundle.d.ts +9 -0
  83. package/src/internal/reading/controls.inpage.ts +8 -1
  84. package/src/internal/reading/match.inpage.ts +2 -1
  85. package/src/internal/reading/outline.inpage.ts +7 -4
  86. package/src/internal/reading/reading.ts +10 -25
  87. package/src/internal/reading/ready.ts +66 -14
  88. package/src/internal/supervisor/lifecycle.ts +56 -13
  89. package/src/internal/timeline/events.ts +5 -8
  90. package/src/internal/timeline/window.ts +3 -8
  91. package/dist/Browser-BBSySyjT.mjs.map +0 -1
  92. package/dist/Page-B5cjaUp5.mjs.map +0 -1
  93. package/dist/Plan-CXA2BW-G.mjs.map +0 -1
  94. package/dist/Supervisor-DaGlQVjv.mjs.map +0 -1
  95. package/dist/context-BAjRj7Xo.mjs.map +0 -1
  96. package/dist/page-B92QnRfT.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-browser",
3
- "version": "0.3.0-beta.15",
3
+ "version": "0.3.0-beta.19",
4
4
  "description": "Effect browser sessions, page control, screencast and moments over Playwright and CDP.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/Browser.ts CHANGED
@@ -492,7 +492,7 @@ export const make = Effect.fn("Browser.make")(function* (
492
492
  ) {
493
493
  const clock = yield* Clock.Clock;
494
494
  const connection = yield* Identity.make;
495
- const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;
495
+ const now = () => BrowserClock.monotonicMillis(clock);
496
496
  const { settings, eventHistory, maxPages } = yield* settingsOf(options);
497
497
  // Measured on first need: the first capture of any page measures it, and input never waits.
498
498
  const mapping = BrowserClock.mapping(now);
@@ -119,8 +119,7 @@ const Button = Schema.Literals(["left", "right", "middle"]);
119
119
 
120
120
  /** An element's box in viewport CSS pixels; it may lie partly or wholly outside the viewport. */
121
121
  export const Box = Schema.Struct({
122
- x: Schema.Finite,
123
- y: Schema.Finite,
122
+ ...Motion.Point.fields,
124
123
  width: Schema.Finite,
125
124
  height: Schema.Finite,
126
125
  });
@@ -169,8 +168,8 @@ export class Action extends Schema.TaggedClass<Action>()("Action", {
169
168
  /** For an action at a point, the box of what it found there. */
170
169
  box: Schema.optional(Box),
171
170
  text: Schema.optional(Schema.String),
172
- x: Schema.optional(Schema.Finite),
173
- y: Schema.optional(Schema.Finite),
171
+ x: Schema.optional(Motion.Point.fields.x),
172
+ y: Schema.optional(Motion.Point.fields.y),
174
173
  ok: Schema.Boolean,
175
174
  dispatched: Schema.Boolean,
176
175
  error: Schema.optional(Schema.String),
@@ -196,16 +195,14 @@ export class TrackPerformed extends Schema.TaggedClass<TrackPerformed>()("TrackP
196
195
  page: Schema.String,
197
196
  plan: Sequence,
198
197
  dispatched: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
199
- x: Schema.Finite,
200
- y: Schema.Finite,
198
+ ...Motion.Point.fields,
201
199
  complete: Schema.Boolean,
202
200
  }) {}
203
201
 
204
202
  export class PointerPressed extends Schema.TaggedClass<PointerPressed>()("PointerPressed", {
205
203
  at: Schema.Finite,
206
204
  page: Schema.String,
207
- x: Schema.Finite,
208
- y: Schema.Finite,
205
+ ...Motion.Point.fields,
209
206
  button: Button,
210
207
  clickCount: Schema.Int.check(Schema.isGreaterThan(0)),
211
208
  }) {}
@@ -213,8 +210,7 @@ export class PointerPressed extends Schema.TaggedClass<PointerPressed>()("Pointe
213
210
  export class PointerReleased extends Schema.TaggedClass<PointerReleased>()("PointerReleased", {
214
211
  at: Schema.Finite,
215
212
  page: Schema.String,
216
- x: Schema.Finite,
217
- y: Schema.Finite,
213
+ ...Motion.Point.fields,
218
214
  button: Button,
219
215
  clickCount: Schema.Int.check(Schema.isGreaterThan(0)),
220
216
  }) {}
@@ -222,8 +218,7 @@ export class PointerReleased extends Schema.TaggedClass<PointerReleased>()("Poin
222
218
  export class WheelScrolled extends Schema.TaggedClass<WheelScrolled>()("WheelScrolled", {
223
219
  at: Schema.Finite,
224
220
  page: Schema.String,
225
- x: Schema.Finite,
226
- y: Schema.Finite,
221
+ ...Motion.Point.fields,
227
222
  dx: Schema.Finite,
228
223
  dy: Schema.Finite,
229
224
  }) {}
package/src/Moment.ts CHANGED
@@ -27,11 +27,11 @@
27
27
  import { Duration, Effect, Option, Result, Schema } from "effect";
28
28
  import { Prompt } from "effect/ai";
29
29
 
30
- import { BrowserError, InvalidRequest } from "./BrowserError.ts";
30
+ import { BrowserError } from "./BrowserError.ts";
31
31
  import { type Action, BrowserEvent } from "./BrowserEvent.ts";
32
32
  import { type Change, Changes } from "./Change.ts";
33
33
  import { Frame } from "./Frame.ts";
34
- import { undispatched } from "./internal/page/context.ts";
34
+ import { refuse } from "./internal/page/context.ts";
35
35
  import { describe, named, quoted, where } from "./internal/timeline/words.ts";
36
36
  import type * as Page from "./Page.ts";
37
37
  import { Snapshot, type SnapshotOptions } from "./Snapshot.ts";
@@ -116,10 +116,7 @@ export const capture = Effect.fn("Moment.capture")(function* (
116
116
  const count = options.frames ?? 2;
117
117
 
118
118
  if (!Number.isSafeInteger(count) || count < 1)
119
- return yield* undispatched(
120
- "moment",
121
- new InvalidRequest({ detail: "frames must be a positive safe integer" }),
122
- );
119
+ return yield* refuse("moment", "frames must be a positive safe integer");
123
120
  const outline = options.snapshot ?? false;
124
121
 
125
122
  // The picture comes first, at no call with a running screencast, and the window ends at its
package/src/Motion.ts CHANGED
@@ -13,8 +13,7 @@ export const Point = Schema.Struct({ x: Schema.Finite, y: Schema.Finite });
13
13
  export type Point = typeof Point.Type;
14
14
 
15
15
  export const Sample = Schema.Struct({
16
- x: Schema.Finite,
17
- y: Schema.Finite,
16
+ ...Point.fields,
18
17
  afterMillis: Schema.Finite.check(
19
18
  Schema.isGreaterThanOrEqualTo(0),
20
19
  Schema.isLessThanOrEqualTo(maximumDurationMillis),
package/src/Page.ts CHANGED
@@ -19,7 +19,7 @@ import { type Duration, Effect, Schema, type Stream } from "effect";
19
19
  import type { Page as PlaywrightPage } from "playwright-core";
20
20
 
21
21
  import { type BrowserError, type PolicyDenied } from "./BrowserError.ts";
22
- import { Box, type BrowserEvent, PageLoaded, Subject, SubjectContext } from "./BrowserEvent.ts";
22
+ import { Box, type BrowserEvent, PageLoaded, Subject } from "./BrowserEvent.ts";
23
23
  import type { Changes } from "./Change.ts";
24
24
  import { type CaptureStats, Frame, Image, type ScreencastOptions } from "./Frame.ts";
25
25
  import { FormFieldSchema } from "./internal/input/evidence.inpage.ts";
@@ -27,12 +27,10 @@ import * as Guard from "./internal/input/guard.inpage.ts";
27
27
  import { Correlation, FailFast } from "./internal/page/lane.ts";
28
28
  import * as Url from "./internal/page/url.ts";
29
29
  import type { Window } from "./Moment.ts";
30
+ import * as Motion from "./Motion.ts";
30
31
  import { type Snapshot, type SnapshotOptions } from "./Snapshot.ts";
31
32
 
32
- export interface Point {
33
- readonly x: number;
34
- readonly y: number;
35
- }
33
+ export type Point = Motion.Point;
36
34
 
37
35
  /** A ref from a snapshot, such as `"e12"`, or a viewport point. */
38
36
  export type Target = string | Point;
@@ -54,13 +52,9 @@ export class Zoom extends Schema.Class<Zoom>("effect-browser/Zoom")({
54
52
 
55
53
  /** The target as it read before input. Point coordinates are never snapped to another position. */
56
54
  export class ResolvedTarget extends Schema.Class<ResolvedTarget>("effect-browser/ResolvedTarget")({
57
- point: Schema.Struct({ x: Schema.Finite, y: Schema.Finite }),
55
+ point: Motion.Point,
56
+ ...Subject.fields,
58
57
  element: Schema.String,
59
- /** The element's lowercase tag name. */
60
- tag: Schema.String,
61
- role: Schema.NullOr(Schema.String),
62
- name: Schema.String,
63
- context: SubjectContext,
64
58
  /** For a point target, the box of what it found there. */
65
59
  box: Schema.optional(Box),
66
60
  cursor: Schema.String,
@@ -131,9 +125,7 @@ export interface ScreenshotOptions {
131
125
  /** The oldest a reused frame may be, as for `frame`. Defaults to 250 milliseconds. */
132
126
  readonly maxAge?: Duration.Input | undefined;
133
127
  /** A region of the viewport, captured in CSS pixels at the viewport's own scale; always new. */
134
- readonly clip?:
135
- | { readonly x: number; readonly y: number; readonly width: number; readonly height: number }
136
- | undefined;
128
+ readonly clip?: Box | undefined;
137
129
  /** JPEG quality, 0 to 100. Defaults to 80. */
138
130
  readonly quality?: number | undefined;
139
131
  /** `"jpeg"`, the default, or `"png"`, which is always a new picture. */
@@ -226,8 +218,11 @@ export interface WaitCondition {
226
218
  }
227
219
 
228
220
  export interface ReadyOptions {
229
- /** What must stay still during quietMillis: screen paint (default), or recorded content. */
230
- readonly still?: "screen" | "content" | undefined;
221
+ /**
222
+ * What must stay still during quietMillis: screen paint (default); recorded content; or the
223
+ * `"foreground"`, the screen but for what was already moving before the page's latest input.
224
+ */
225
+ readonly still?: "screen" | "content" | "foreground" | undefined;
231
226
  /** How long the selected evidence must stay still; screen paint by default. */
232
227
  readonly quietMillis?: number | undefined;
233
228
  readonly timeout?: Duration.Input | undefined;
@@ -335,7 +330,7 @@ export class InputRequest extends Schema.Class<InputRequest>("effect-browser/Inp
335
330
  target: Schema.optional(Schema.String),
336
331
  element: Schema.optional(Schema.String),
337
332
  /** Literal pixel targets only; ref coordinates are resolved after the policy allows them. */
338
- point: Schema.optional(Schema.Struct({ x: Schema.Finite, y: Schema.Finite })),
333
+ point: Schema.optional(Motion.Point),
339
334
  text: Schema.optional(Schema.String),
340
335
  role: Schema.optional(Schema.NullOr(Schema.String)),
341
336
  name: Schema.optional(Schema.String),
@@ -497,9 +492,12 @@ export interface Page {
497
492
  * view have loaded, and the viewport shows something. With `quietMillis`, the screen must then
498
493
  * also stay still that long: nothing in view changed, where the page's changes are recorded, and
499
494
  * no frame comes, counted from the first frame of a capture the wait starts itself. A canvas
500
- * that keeps drawing, such as a live chart, is never still, and a page
501
- * is never ready without a painted frame, as a hidden tab may be. `Timeout` after `timeout`,
502
- * 15 seconds by default.
495
+ * that keeps drawing, such as a live chart, is never still. With `still: "foreground"`, frames
496
+ * may keep coming, but nothing may move in them outside what moved in the frames of the 1.5 s
497
+ * before the page's latest input, such as an idle animation; reels set spinning, or a counter
498
+ * set rolling, by the input keep the wait going. Without those frames, as where they are older
499
+ * than the browser's `frameHistory`, nothing may move at all. A page is never ready without a
500
+ * painted frame, as a hidden tab may be. `Timeout` after `timeout`, 15 seconds by default.
503
501
  */
504
502
  readonly ready: (options?: ReadyOptions) => Effect.Effect<void, BrowserError>;
505
503
 
package/src/Plan.ts CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  } from "./BrowserEvent.ts";
34
34
  import { undispatched } from "./internal/page/context.ts";
35
35
  import { Ambiguous, choose, Drifted, Missing } from "./internal/reading/choose.ts";
36
+ import { Point as ViewportPoint } from "./Motion.ts";
36
37
  import {
37
38
  type Found,
38
39
  type Page,
@@ -44,8 +45,6 @@ import {
44
45
 
45
46
  export { Ambiguous, Drifted, Missing } from "./internal/reading/choose.ts";
46
47
 
47
- const ViewportPoint = Schema.Struct({ x: Schema.Finite, y: Schema.Finite });
48
-
49
48
  export const StepAction = Schema.Literals([
50
49
  "navigate",
51
50
  "back",
@@ -20,6 +20,7 @@ import { Clock, Duration, Effect, Fiber, MutableRef, Option, Semaphore } from "e
20
20
  import type { BrowserError } from "./BrowserError.ts";
21
21
  import * as Style from "./internal/input/style.ts";
22
22
  import { type Internals, internalsOf, viewOf } from "./internal/page/page.ts";
23
+ import { monotonicMillis } from "./internal/pictures/clock.ts";
23
24
  import * as Motion from "./Motion.ts";
24
25
  import type { Page, Point, Target } from "./Page.ts";
25
26
 
@@ -81,7 +82,7 @@ export const make = Effect.fn("Presentation.make")(function* (options: Options =
81
82
  const pacing = options.pacing ?? human;
82
83
  const clock = yield* Clock.Clock;
83
84
  const scope = yield* Effect.scope;
84
- const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;
85
+ const now = () => monotonicMillis(clock);
85
86
  const pointer = MutableRef.make(Option.none<Point>());
86
87
  const hand = yield* Semaphore.make(1);
87
88
 
package/src/Stage.ts CHANGED
@@ -24,8 +24,9 @@ import {
24
24
 
25
25
  import { type BrowserError, Closed, consequence, InvalidRequest, Timeout } from "./BrowserError.ts";
26
26
  import type { CaptureStats, Frame, ScreencastOptions } from "./Frame.ts";
27
- import { failWith, undispatched } from "./internal/page/context.ts";
27
+ import { failWith, refuse, undispatched } from "./internal/page/context.ts";
28
28
  import { internalsOf } from "./internal/page/page.ts";
29
+ import { monotonicMillis } from "./internal/pictures/clock.ts";
29
30
  import type { Page } from "./Page.ts";
30
31
 
31
32
  export interface Options extends ScreencastOptions {
@@ -239,16 +240,14 @@ const evidence = (
239
240
 
240
241
  const probe = Effect.gen(function* () {
241
242
  yield* until(Math.max(checkedAt, current()?.changedAt ?? checkedAt) + millis, now);
242
- checkedAt = now();
243
243
  const before = current();
244
244
  const frame = before?.frame;
245
245
 
246
- if (
247
- before === undefined ||
248
- frame === undefined ||
249
- gone === before.feed ||
250
- checkedAt - before.changedAt < millis
251
- )
246
+ // Paint during the wait moves the deadline; an early wake is not a silence check.
247
+ if (before !== undefined && now() - before.changedAt < millis)
248
+ return Option.none<Evidence>();
249
+ checkedAt = now();
250
+ if (before === undefined || frame === undefined || gone === before.feed)
252
251
  return Option.none<Evidence>();
253
252
  const { feed } = before;
254
253
 
@@ -390,7 +389,7 @@ export const make = Effect.fn("Stage.make")(function* (
390
389
  ): Effect.fn.Return<Stage, BrowserError, Scope.Scope> {
391
390
  const clock = yield* Clock.Clock;
392
391
  const owner = yield* Effect.scope;
393
- const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;
392
+ const now = () => monotonicMillis(clock);
394
393
  const waited = Duration.fromInput(options.firstFrame ?? Duration.seconds(5));
395
394
 
396
395
  if (
@@ -398,10 +397,7 @@ export const make = Effect.fn("Stage.make")(function* (
398
397
  !Duration.isFinite(waited.value) ||
399
398
  !Duration.isPositive(waited.value)
400
399
  )
401
- return yield* failWith(
402
- "stage",
403
- new InvalidRequest({ detail: "firstFrame must be a finite positive duration" }),
404
- );
400
+ return yield* refuse("stage", "firstFrame must be a finite positive duration");
405
401
  const firstFrame = waited.value;
406
402
  const settings = { quality: options.quality, size: options.size };
407
403
  const output = yield* PubSub.sliding<Paint>(16);
@@ -433,10 +429,7 @@ export const make = Effect.fn("Stage.make")(function* (
433
429
  return switching(
434
430
  Effect.gen(function* () {
435
431
  if (!Number.isFinite(asked))
436
- return yield* failWith(
437
- "cue",
438
- new InvalidRequest({ detail: "at must be finite host milliseconds" }),
439
- );
432
+ return yield* refuse("cue", "at must be finite host milliseconds");
440
433
  yield* firstOf(feed, firstFrame);
441
434
  const old = showing;
442
435
 
package/src/Supervisor.ts CHANGED
@@ -107,7 +107,8 @@ export interface Admission {
107
107
  /**
108
108
  * Optional service supplied during `open`. Providers report failed-attempt cleanup here before
109
109
  * their scope closes; successful generations instead return `Opened.release`. Reporting does
110
- * not release resources or replace the original acquisition failure.
110
+ * not release resources or replace the original acquisition failure. Terminal `Down` and opening
111
+ * `Closed` states publish the reported outcomes of their own generation's tries.
111
112
  */
112
113
  export class Releasing extends Context.Service<
113
114
  Releasing,
@@ -556,7 +557,7 @@ const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
556
557
  return yield* options.open.pipe(
557
558
  Effect.provideService(Releasing, {
558
559
  released: (released) =>
559
- apply({ _tag: "AttemptReleased", released }).pipe(Effect.asVoid),
560
+ apply({ _tag: "AttemptReleased", number, released }).pipe(Effect.asVoid),
560
561
  }),
561
562
  );
562
563
  }),
@@ -597,21 +598,20 @@ const makeScoped = Effect.fn("Supervisor.make")(function* <E, R, A>(
597
598
 
598
599
  const retire = yield* Effect.cached(stop({ _tag: "Retire" }));
599
600
 
601
+ // The browser of the generation serving, or of the one `target` names once it serves.
602
+ const browserOf = (target?: number) =>
603
+ Effect.map(waitFor(state, waitTimeout, clock, target), (live) => live.value.opened.browser);
604
+
600
605
  yield* Effect.addFinalizer(() => (options.keep === true ? stop({ _tag: "Keep" }) : retire));
601
606
  yield* perform(started);
602
607
 
603
608
  return {
604
- browser: Effect.map(waitFor(state, waitTimeout, clock), (live) => live.value.opened.browser),
609
+ browser: browserOf(),
605
610
  use: leasing(state, apply, waitTimeout, now, clock),
606
611
  states: Stream.fromPubSubTake(events),
607
612
  rotate: apply({ _tag: "Rotate" }).pipe(
608
613
  Effect.flatMap(({ reply }) =>
609
- reply === undefined
610
- ? Effect.fail(retired())
611
- : Effect.map(
612
- waitFor(state, waitTimeout, clock, reply),
613
- (live) => live.value.opened.browser,
614
- ),
614
+ reply === undefined ? Effect.fail(retired()) : browserOf(reply),
615
615
  ),
616
616
  ),
617
617
  retire,