effect-browser 0.3.0-beta.8 → 0.3.0-beta.9

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 (55) hide show
  1. package/README.md +27 -0
  2. package/dist/{Browser-CNwK0r0q.mjs → Browser-Bc_LIqTC.mjs} +184 -16
  3. package/dist/Browser-Bc_LIqTC.mjs.map +1 -0
  4. package/dist/Browser.d.mts +16 -4
  5. package/dist/Browser.mjs +2 -2
  6. package/dist/{BrowserError-DXXIA6Rk.d.mts → BrowserError-Coy7EW1N.d.mts} +10 -5
  7. package/dist/BrowserError.d.mts +2 -2
  8. package/dist/BrowserError.mjs +10 -1
  9. package/dist/BrowserError.mjs.map +1 -1
  10. package/dist/Cdp.d.mts +1 -1
  11. package/dist/Cdp.mjs +3 -3
  12. package/dist/Chromium.d.mts +1 -1
  13. package/dist/Chromium.mjs +2 -2
  14. package/dist/Expect.d.mts +2 -2
  15. package/dist/Expect.mjs +2 -2
  16. package/dist/Moment.d.mts +1 -1
  17. package/dist/Moment.mjs +1 -1
  18. package/dist/{Page-D4281dIi.mjs → Page-B5cjaUp5.mjs} +2 -2
  19. package/dist/{Page-D4281dIi.mjs.map → Page-B5cjaUp5.mjs.map} +1 -1
  20. package/dist/{Page-C30aG2RQ.d.mts → Page-BlPjfibv.d.mts} +2 -2
  21. package/dist/Page.d.mts +1 -1
  22. package/dist/Page.mjs +1 -1
  23. package/dist/{Plan-BvCS9qAf.d.mts → Plan-BI8XGvDu.d.mts} +3 -3
  24. package/dist/{Plan-CyQNv49C.mjs → Plan-D1wrJVs8.mjs} +3 -3
  25. package/dist/{Plan-CyQNv49C.mjs.map → Plan-D1wrJVs8.mjs.map} +1 -1
  26. package/dist/Plan.d.mts +1 -1
  27. package/dist/Plan.mjs +1 -1
  28. package/dist/Policy.d.mts +1 -1
  29. package/dist/Policy.mjs +1 -1
  30. package/dist/Presentation.d.mts +1 -1
  31. package/dist/Presentation.mjs +1 -1
  32. package/dist/Stage.d.mts +2 -2
  33. package/dist/Stage.mjs +2 -2
  34. package/dist/{context-DAYjATgC.mjs → context-B6xr8nVi.mjs} +134 -10
  35. package/dist/context-B6xr8nVi.mjs.map +1 -0
  36. package/dist/index.d.mts +3 -3
  37. package/dist/index.mjs +3 -3
  38. package/dist/{page-DHeYWEs-.mjs → page-D-OedyBw.mjs} +59 -126
  39. package/dist/page-D-OedyBw.mjs.map +1 -0
  40. package/dist/{url-CcL0K7S0.mjs → url-Cd04Okid.mjs} +6 -4
  41. package/dist/url-Cd04Okid.mjs.map +1 -0
  42. package/package.json +1 -1
  43. package/src/Browser.ts +57 -16
  44. package/src/BrowserError.ts +9 -0
  45. package/src/internal/input/gate.ts +224 -0
  46. package/src/internal/input/guard.ts +3 -2
  47. package/src/internal/input/perform.ts +84 -32
  48. package/src/internal/input/replies.ts +52 -16
  49. package/src/internal/page/context.ts +21 -11
  50. package/src/internal/page/lane.ts +20 -10
  51. package/src/internal/timeline/events.ts +1 -0
  52. package/dist/Browser-CNwK0r0q.mjs.map +0 -1
  53. package/dist/context-DAYjATgC.mjs.map +0 -1
  54. package/dist/page-DHeYWEs-.mjs.map +0 -1
  55. package/dist/url-CcL0K7S0.mjs.map +0 -1
package/README.md CHANGED
@@ -678,6 +678,33 @@ how long it waited and how many operations were ahead, and replies not answered
678
678
  `Timeout`, as for a page that does not answer. `Page.failFast(effect)` runs operations that fail
679
679
  `Busy` at once instead of waiting. A policy hold leaves the page's turn free.
680
680
 
681
+ `browser.suspend({ drain })` withholds library input on every tab for the current scope. It first
682
+ drains operations already admitted, through their `Action` records and native acknowledgements.
683
+ A canceled call may already have sent input: its unanswered reply still prevents a successful
684
+ drain. New and queued writes fail `Suspended`, with `dispatched: false`, and record that refusal.
685
+ Already-held authority refuses input before invoking an input guard; a guard already waiting is
686
+ checked again at admission. Reads, captures and events continue.
687
+
688
+ ```ts
689
+ yield *
690
+ Effect.gen(function* () {
691
+ const paused = yield* browser.suspend();
692
+ // An event reader can resume from paused.since; paused.at is host monotonic time.
693
+ const page = yield* browser.firstPage;
694
+ yield* page.screenshot();
695
+ yield* operatorDone;
696
+ }).pipe(Effect.scoped);
697
+ // Input resumes after this scope, provided no other suspension still holds it.
698
+ ```
699
+
700
+ Suspensions nest, including scopes closed out of order. `drain` defaults to `navigationTimeout`
701
+ (30 seconds); zero acquires only when input is already idle. Waiters that join a drain share its
702
+ first deadline. Expiry fails all those waiters with `Busy` and restores admission; cancellation
703
+ abandons only that caller's claim. Browser loss fails acquisition with `Closed`. A failed
704
+ acquisition leaves no suspension behind. The gate controls library actions and navigation;
705
+ input from the provider's own viewer or direct native calls remains under the operator's control.
706
+ The consumer owns the operator's decisions and any durable workflow journal.
707
+
681
708
  Reads keep their work. An identical read asked while one is in flight on the page, with no action
682
709
  asked between them, joins it, and costs no call of its own. Each read runs in the page's scope under
683
710
  the action timeout whoever gives up, so a caller's own `Effect.timeout` loses nothing: a read that
@@ -1,10 +1,156 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { DialogShown, Disconnected, PageClosed, PageOpened, PageUntracked, RecordedEvent, SessionEnding } from "./BrowserEvent.mjs";
3
- import { BrowserError, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit } from "./BrowserError.mjs";
4
- import { c as FailFast, i as redact } from "./url-CcL0K7S0.mjs";
5
- import { i as failWith, s as undispatched, t as call } from "./context-DAYjATgC.mjs";
6
- import { n as make$3, o as mapping } from "./page-DHeYWEs-.mjs";
7
- import { Cause, Clock, Context, DateTime, Deferred, Duration, Effect, Exit, Option, Queue, Scope, Semaphore, Stream } from "effect";
3
+ import { BrowserError, Busy, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit, Suspended } from "./BrowserError.mjs";
4
+ import { i as failWith, s as undispatched, t as call } from "./context-B6xr8nVi.mjs";
5
+ import { c as FailFast, i as redact } from "./url-Cd04Okid.mjs";
6
+ import { n as make$4, o as mapping } from "./page-D-OedyBw.mjs";
7
+ import { Cause, Clock, Context, DateTime, Deferred, Duration, Effect, Exit, Option, Queue, Schema, Scope, Semaphore, Stream } from "effect";
8
+ //#region src/internal/input/gate.ts
9
+ /**
10
+ * A browser's input authority: stop admitting writes, drain admitted operations through their
11
+ * records and native replies, then retain the stop for its scopes. Reads never enter this gate.
12
+ */
13
+ const without = (set, id) => new Set([...set].filter((each) => each !== id));
14
+ const step = (state, input, stopped) => {
15
+ switch (input._tag) {
16
+ case "Enter": return state._tag === "Open" ? {
17
+ ...state,
18
+ inputs: /* @__PURE__ */ new Set([...state.inputs, input.id])
19
+ } : state;
20
+ case "End": {
21
+ if (state._tag === "Held") return state;
22
+ const inputs = without(state.inputs, input.id);
23
+ return state._tag === "Draining" && inputs.size === 0 ? {
24
+ _tag: "Held",
25
+ claims: state.claims,
26
+ stopped
27
+ } : {
28
+ ...state,
29
+ inputs
30
+ };
31
+ }
32
+ case "Suspend":
33
+ if (state._tag !== "Open") return {
34
+ ...state,
35
+ claims: /* @__PURE__ */ new Set([...state.claims, input.id])
36
+ };
37
+ return state.inputs.size === 0 ? {
38
+ _tag: "Held",
39
+ claims: /* @__PURE__ */ new Set([input.id]),
40
+ stopped
41
+ } : {
42
+ _tag: "Draining",
43
+ inputs: state.inputs,
44
+ claims: /* @__PURE__ */ new Set([input.id]),
45
+ startedAt: stopped.at,
46
+ deadline: stopped.at + input.drain
47
+ };
48
+ case "Leave": {
49
+ if (state._tag === "Open" || !state.claims.has(input.id)) return state;
50
+ const claims = without(state.claims, input.id);
51
+ return claims.size > 0 ? {
52
+ ...state,
53
+ claims
54
+ } : {
55
+ _tag: "Open",
56
+ inputs: state._tag === "Draining" ? state.inputs : /* @__PURE__ */ new Set()
57
+ };
58
+ }
59
+ case "Expire": return state._tag === "Draining" && stopped.at >= state.deadline ? {
60
+ _tag: "Open",
61
+ inputs: state.inputs
62
+ } : state;
63
+ }
64
+ };
65
+ const make$3 = (options) => {
66
+ let state = {
67
+ _tag: "Open",
68
+ inputs: /* @__PURE__ */ new Set()
69
+ };
70
+ let next = 0;
71
+ let refused = Deferred.makeUnsafe();
72
+ let ready = Deferred.makeUnsafe();
73
+ const error = (operation) => new BrowserError({
74
+ operation,
75
+ reason: options.closedBy() === void 0 ? new Suspended() : new Closed({ cause: options.closedBy() ?? "connection" }),
76
+ dispatched: false
77
+ });
78
+ const stopped = () => ({
79
+ since: options.sequence(),
80
+ at: options.now()
81
+ });
82
+ const apply = (input) => {
83
+ if (input._tag !== "Expire" && state._tag === "Draining" && options.now() >= state.deadline) apply({ _tag: "Expire" });
84
+ const before = state;
85
+ state = step(state, input, stopped());
86
+ if (input._tag === "Expire" && before._tag === "Draining" && state._tag === "Open") Deferred.doneUnsafe(ready, Exit.fail(new BrowserError({
87
+ operation: "suspend",
88
+ reason: new Busy({
89
+ waitedMillis: Math.round(options.now() - before.startedAt),
90
+ ahead: before.inputs.size
91
+ }),
92
+ dispatched: false
93
+ })));
94
+ if (before._tag === "Open" && state._tag !== "Open") Deferred.doneUnsafe(refused, Exit.void);
95
+ if (before._tag === "Draining" && state._tag === "Held") Deferred.doneUnsafe(ready, Exit.succeed(state.stopped));
96
+ if (before._tag !== "Open" && state._tag === "Open") {
97
+ refused = Deferred.makeUnsafe();
98
+ ready = Deferred.makeUnsafe();
99
+ }
100
+ return state;
101
+ };
102
+ const check = (operation) => Effect.suspend(() => state._tag === "Open" && options.closedBy() === void 0 ? Effect.void : Effect.fail(error(operation)));
103
+ const suspend = (drain = options.drain) => Effect.suspend(() => {
104
+ const duration = Duration.fromInput(drain);
105
+ if (Option.isNone(duration) || !Duration.isFinite(duration.value) || Duration.toMillis(duration.value) < 0) return Effect.fail(new BrowserError({
106
+ operation: "suspend",
107
+ reason: new InvalidRequest({ detail: "drain must be a finite nonnegative duration" }),
108
+ dispatched: false
109
+ }));
110
+ const id = ++next;
111
+ const leave = Effect.sync(() => apply({
112
+ _tag: "Leave",
113
+ id
114
+ }));
115
+ return Effect.acquireRelease(Effect.gen(function* () {
116
+ if (options.closedBy() !== void 0) return yield* error("suspend");
117
+ const current = apply({
118
+ _tag: "Suspend",
119
+ id,
120
+ drain: Duration.toMillis(duration.value)
121
+ });
122
+ if (current._tag === "Held") return current.stopped;
123
+ if (current._tag !== "Draining") return yield* Effect.die("suspension did not retain authority");
124
+ const pending = ready;
125
+ const expire = Effect.suspend(() => {
126
+ const left = current.deadline - options.now();
127
+ return left > 0 ? Effect.sleep(Duration.millis(left)).pipe(Effect.andThen(expire)) : Effect.sync(() => apply({ _tag: "Expire" })).pipe(Effect.andThen(Deferred.await(pending)));
128
+ });
129
+ const wait = Effect.raceFirst(Deferred.await(pending), expire);
130
+ return yield* Effect.raceFirst(wait, options.lost.pipe(Effect.flatMap(() => Effect.fail(error("suspend"))))).pipe(Effect.interruptible, Effect.filterOrFail(() => options.closedBy() === void 0, () => error("suspend")), Effect.onError(() => leave));
131
+ }), () => leave);
132
+ }).pipe(Effect.provideService(Clock.Clock, options.clock));
133
+ return {
134
+ check,
135
+ suspend,
136
+ refused: (operation) => Effect.suspend(() => state._tag === "Open" ? Deferred.await(refused).pipe(Effect.andThen(Effect.fail(error(operation)))) : Effect.fail(error(operation))),
137
+ enter: (operation) => Effect.suspend(() => {
138
+ if (state._tag !== "Open" || options.closedBy() !== void 0) return Effect.fail(error(operation));
139
+ const id = ++next;
140
+ apply({
141
+ _tag: "Enter",
142
+ id
143
+ });
144
+ return Effect.succeed(() => {
145
+ apply({
146
+ _tag: "End",
147
+ id
148
+ });
149
+ });
150
+ })
151
+ };
152
+ };
153
+ //#endregion
8
154
  //#region src/internal/page/openings.ts
9
155
  /** A page's input waits for the actual tabs it opened to finish the browser's registration. */
10
156
  const make$2 = Effect.fnUntraced(function* (playwright, cdp, registrations, closedBy) {
@@ -106,6 +252,7 @@ const make$1 = (capacity) => {
106
252
  }));
107
253
  return {
108
254
  publish,
255
+ sequence: () => sequence,
109
256
  recent: Effect.sync(() => records),
110
257
  stream,
111
258
  close: Effect.sync(() => {
@@ -129,8 +276,14 @@ const make$1 = (capacity) => {
129
276
  */
130
277
  var Browser_exports = /* @__PURE__ */ __exportAll({
131
278
  Browser: () => Browser,
279
+ Suspension: () => Suspension,
132
280
  make: () => make
133
281
  });
282
+ /** When admitted input finished; events after `since` describe the handoff interval. */
283
+ var Suspension = class extends Schema.Class("effect-browser/Suspension")({
284
+ since: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
285
+ at: Schema.Finite
286
+ }) {};
134
287
  var Browser = class extends Context.Service()("effect-browser/Browser") {};
135
288
  /** The settings every page shares, and how many events the browser keeps, from its options. */
136
289
  const settingsOf = Effect.fnUntraced(function* (options) {
@@ -304,6 +457,18 @@ const sentNow = (sent) => {
304
457
  sent.catch(() => void 0);
305
458
  return sent;
306
459
  };
460
+ /** Close tab resources after publishing their close; a browser loss speaks for all its tabs. */
461
+ const closures = (lostBy, publish, now) => Effect.gen(function* () {
462
+ const closing = yield* Queue.unbounded();
463
+ yield* Queue.take(closing).pipe(Effect.tap(({ page, cause }) => Effect.sync(() => {
464
+ if (lostBy() === void 0) publish(new PageClosed({
465
+ at: now(),
466
+ page,
467
+ cause
468
+ }));
469
+ })), Effect.flatMap(({ scope }) => Scope.close(scope, Exit.void)), Effect.forever, Effect.forkScoped);
470
+ return closing;
471
+ });
307
472
  /**
308
473
  * Build the service over a context the provider owns. The provider closes the context; this only
309
474
  * tracks its pages and events, for as long as the surrounding scope is open. Each page's protocol
@@ -326,20 +491,21 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
326
491
  at: now(),
327
492
  cause
328
493
  })));
494
+ const inputGate = make$3({
495
+ now,
496
+ sequence: timeline.sequence,
497
+ closedBy: lostBy,
498
+ lost,
499
+ clock,
500
+ drain: settings.navigationTimeout
501
+ });
329
502
  const registry = /* @__PURE__ */ new Map();
330
503
  const registrations = /* @__PURE__ */ new WeakMap();
331
504
  const registering = yield* Semaphore.make(1);
332
505
  const pagesScope = yield* Scope.fork(yield* Scope.Scope);
333
506
  const release = releaseNative(clock);
334
507
  const budget = pageBudget(maxPages, () => registry.size, settings.actionTimeout, clock);
335
- const closing = yield* Queue.unbounded();
336
- yield* Queue.take(closing).pipe(Effect.tap(({ page, cause }) => Effect.sync(() => {
337
- if (lostBy() === void 0) publish(new PageClosed({
338
- at: now(),
339
- page,
340
- cause
341
- }));
342
- })), Effect.flatMap(({ scope }) => Scope.close(scope, Exit.void)), Effect.forever, Effect.forkScoped);
508
+ const closing = yield* closures(lostBy, publish, now);
343
509
  const register = (playwright) => registering.withPermits(1)(Effect.gen(function* () {
344
510
  const known = registry.get(playwright);
345
511
  if (known !== void 0) return known;
@@ -355,7 +521,7 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
355
521
  const paging = sentNow(send("Page.enable"));
356
522
  const { targetInfo } = yield* native("newPage", () => send("Target.getTargetInfo"));
357
523
  const id = targetInfo.targetId;
358
- const page = yield* make$3({
524
+ const page = yield* make$4({
359
525
  id,
360
526
  session: info.id,
361
527
  url: targetInfo.url,
@@ -363,6 +529,7 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
363
529
  cdp,
364
530
  untilLost,
365
531
  settings,
532
+ inputGate,
366
533
  clock,
367
534
  mapping: mapping$1,
368
535
  publish,
@@ -417,6 +584,7 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
417
584
  provider: info.provider,
418
585
  expiresAt: info.expiresAt,
419
586
  now: Effect.sync(now),
587
+ suspend: (options = {}) => Effect.map(inputGate.suspend(options.drain), (held) => new Suspension(held)),
420
588
  context,
421
589
  pages,
422
590
  page: (id) => Effect.map(pages, (open) => Option.fromNullishOr(open.find((page) => page.id === id))),
@@ -432,6 +600,6 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
432
600
  return Browser.of(service);
433
601
  });
434
602
  //#endregion
435
- export { Browser_exports as n, make as r, Browser as t };
603
+ export { make as i, Browser_exports as n, Suspension as r, Browser as t };
436
604
 
437
- //# sourceMappingURL=Browser-CNwK0r0q.mjs.map
605
+ //# sourceMappingURL=Browser-Bc_LIqTC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Browser-Bc_LIqTC.mjs","names":["make","make","make","Url.redact","mapping","BrowserClock.mapping","Timeline.make","Gate.make","PageImpl.make","Openings.make"],"sources":["../src/internal/input/gate.ts","../src/internal/page/openings.ts","../src/internal/timeline/events.ts","../src/Browser.ts"],"sourcesContent":["/**\n * A browser's input authority: stop admitting writes, drain admitted operations through their\n * records and native replies, then retain the stop for its scopes. Reads never enter this gate.\n */\nimport { Clock, Deferred, Duration, Effect, Exit, Option, type Scope } from \"effect\";\n\nimport { BrowserError, Busy, Closed, InvalidRequest, Suspended } from \"../../BrowserError.ts\";\nimport type { DisconnectCause } from \"../../BrowserEvent.ts\";\n\ninterface Stopped {\n readonly since: number;\n readonly at: number;\n}\n\ntype State =\n | { readonly _tag: \"Open\"; readonly inputs: ReadonlySet<number> }\n | {\n readonly _tag: \"Draining\";\n readonly inputs: ReadonlySet<number>;\n readonly claims: ReadonlySet<number>;\n readonly startedAt: number;\n readonly deadline: number;\n }\n | { readonly _tag: \"Held\"; readonly claims: ReadonlySet<number>; readonly stopped: Stopped };\n\ntype Input =\n | { readonly _tag: \"Enter\" | \"End\" | \"Leave\"; readonly id: number }\n | { readonly _tag: \"Suspend\"; readonly id: number; readonly drain: number }\n | { readonly _tag: \"Expire\" };\n\nconst without = (set: ReadonlySet<number>, id: number) =>\n new Set([...set].filter((each) => each !== id));\n\nconst step = (state: State, input: Input, stopped: Stopped): State => {\n switch (input._tag) {\n case \"Enter\":\n return state._tag === \"Open\"\n ? { ...state, inputs: new Set([...state.inputs, input.id]) }\n : state;\n case \"End\": {\n if (state._tag === \"Held\") return state;\n const inputs = without(state.inputs, input.id);\n\n return state._tag === \"Draining\" && inputs.size === 0\n ? { _tag: \"Held\", claims: state.claims, stopped }\n : { ...state, inputs };\n }\n case \"Suspend\":\n if (state._tag !== \"Open\") return { ...state, claims: new Set([...state.claims, input.id]) };\n\n return state.inputs.size === 0\n ? { _tag: \"Held\", claims: new Set([input.id]), stopped }\n : {\n _tag: \"Draining\",\n inputs: state.inputs,\n claims: new Set([input.id]),\n startedAt: stopped.at,\n deadline: stopped.at + input.drain,\n };\n case \"Leave\": {\n if (state._tag === \"Open\" || !state.claims.has(input.id)) return state;\n const claims = without(state.claims, input.id);\n\n return claims.size > 0\n ? { ...state, claims }\n : { _tag: \"Open\", inputs: state._tag === \"Draining\" ? state.inputs : new Set() };\n }\n case \"Expire\":\n return state._tag === \"Draining\" && stopped.at >= state.deadline\n ? { _tag: \"Open\", inputs: state.inputs }\n : state;\n }\n};\n\nexport const make = (options: {\n readonly now: () => number;\n readonly sequence: () => number;\n readonly closedBy: () => DisconnectCause | undefined;\n readonly lost: Effect.Effect<DisconnectCause>;\n readonly clock: Clock.Clock;\n readonly drain: Duration.Duration;\n}) => {\n let state: State = { _tag: \"Open\", inputs: new Set() };\n let next = 0;\n let refused = Deferred.makeUnsafe<void>();\n let ready = Deferred.makeUnsafe<Stopped, BrowserError>();\n\n const error = (operation: string) =>\n new BrowserError({\n operation,\n reason:\n options.closedBy() === undefined\n ? new Suspended()\n : new Closed({ cause: options.closedBy() ?? \"connection\" }),\n dispatched: false,\n });\n\n const stopped = (): Stopped => ({ since: options.sequence(), at: options.now() });\n\n // Every change to authority happens here, synchronously with its admission or scope closure.\n const apply = (input: Input): State => {\n if (input._tag !== \"Expire\" && state._tag === \"Draining\" && options.now() >= state.deadline)\n apply({ _tag: \"Expire\" });\n const before = state;\n\n state = step(state, input, stopped());\n if (input._tag === \"Expire\" && before._tag === \"Draining\" && state._tag === \"Open\")\n Deferred.doneUnsafe(\n ready,\n Exit.fail(\n new BrowserError({\n operation: \"suspend\",\n reason: new Busy({\n waitedMillis: Math.round(options.now() - before.startedAt),\n ahead: before.inputs.size,\n }),\n dispatched: false,\n }),\n ),\n );\n if (before._tag === \"Open\" && state._tag !== \"Open\") Deferred.doneUnsafe(refused, Exit.void);\n if (before._tag === \"Draining\" && state._tag === \"Held\")\n Deferred.doneUnsafe(ready, Exit.succeed(state.stopped));\n if (before._tag !== \"Open\" && state._tag === \"Open\") {\n refused = Deferred.makeUnsafe();\n ready = Deferred.makeUnsafe();\n }\n\n return state;\n };\n\n const check = (operation: string) =>\n Effect.suspend(() =>\n state._tag === \"Open\" && options.closedBy() === undefined\n ? Effect.void\n : Effect.fail(error(operation)),\n );\n\n const suspend = (\n drain: Duration.Input = options.drain,\n ): Effect.Effect<Stopped, BrowserError, Scope.Scope> =>\n Effect.suspend(() => {\n const duration = Duration.fromInput(drain);\n\n if (\n Option.isNone(duration) ||\n !Duration.isFinite(duration.value) ||\n Duration.toMillis(duration.value) < 0\n )\n return Effect.fail(\n new BrowserError({\n operation: \"suspend\",\n reason: new InvalidRequest({ detail: \"drain must be a finite nonnegative duration\" }),\n dispatched: false,\n }),\n );\n const id = ++next;\n const leave = Effect.sync(() => apply({ _tag: \"Leave\", id }));\n\n return Effect.acquireRelease(\n Effect.gen(function* () {\n if (options.closedBy() !== undefined) return yield* error(\"suspend\");\n const current = apply({ _tag: \"Suspend\", id, drain: Duration.toMillis(duration.value) });\n\n if (current._tag === \"Held\") return current.stopped;\n if (current._tag !== \"Draining\")\n return yield* Effect.die(\"suspension did not retain authority\");\n const pending = ready;\n\n // Native completion and timer callbacks both check the same monotonic deadline. A\n // timer may wake a fraction early, so it sleeps the remaining time instead of hanging.\n const expire: Effect.Effect<Stopped, BrowserError> = Effect.suspend(() => {\n const left = current.deadline - options.now();\n\n return left > 0\n ? Effect.sleep(Duration.millis(left)).pipe(Effect.andThen(expire))\n : Effect.sync(() => apply({ _tag: \"Expire\" })).pipe(\n Effect.andThen(Deferred.await(pending)),\n );\n });\n\n const wait = Effect.raceFirst(Deferred.await(pending), expire);\n\n return yield* Effect.raceFirst(\n wait,\n options.lost.pipe(Effect.flatMap(() => Effect.fail(error(\"suspend\")))),\n ).pipe(\n Effect.interruptible,\n Effect.filterOrFail(\n () => options.closedBy() === undefined,\n () => error(\"suspend\"),\n ),\n Effect.onError(() => leave),\n );\n }),\n () => leave,\n );\n }).pipe(Effect.provideService(Clock.Clock, options.clock));\n\n return {\n check,\n suspend,\n refused: (operation: string): Effect.Effect<never, BrowserError> =>\n Effect.suspend(() =>\n state._tag === \"Open\"\n ? Deferred.await(refused).pipe(Effect.andThen(Effect.fail(error(operation))))\n : Effect.fail(error(operation)),\n ),\n enter: (operation: string): Effect.Effect<() => void, BrowserError> =>\n Effect.suspend(() => {\n if (state._tag !== \"Open\" || options.closedBy() !== undefined)\n return Effect.fail(error(operation));\n const id = ++next;\n\n apply({ _tag: \"Enter\", id });\n\n return Effect.succeed(() => {\n apply({ _tag: \"End\", id });\n });\n }),\n };\n};\n\nexport type Gate = ReturnType<typeof make>;\n","/** A page's input waits for the actual tabs it opened to finish the browser's registration. */\nimport { Deferred, Effect, Exit } from \"effect\";\nimport type { CDPSession, Page as PlaywrightPage } from \"playwright-core\";\n\nimport { BrowserError, Closed, Failed, type Reason } from \"../../BrowserError.ts\";\n\n/** How the browser's registration of each tab it was told of ended, once it has. */\nexport type Registrations = WeakMap<PlaywrightPage, Deferred.Deferred<void, BrowserError>>;\n\nexport interface Mark {\n windows: number;\n readonly registrations: Array<Effect.Effect<void, BrowserError>>;\n changed: Deferred.Deferred<void>;\n}\n\nexport const make = Effect.fnUntraced(function* (\n playwright: PlaywrightPage,\n cdp: CDPSession,\n registrations: Registrations,\n closedBy: () => Closed[\"cause\"],\n) {\n const failed = (reason: Reason) =>\n Exit.fail(new BrowserError({ operation: \"newPage\", reason, dispatched: false }));\n\n let active: Mark | undefined;\n const gone = Deferred.makeUnsafe<never, BrowserError>();\n\n const windowOpened = () => {\n if (active !== undefined) active.windows += 1;\n };\n\n const popup = (opened: PlaywrightPage) => {\n if (active === undefined) return;\n // Playwright emits the context's page event first, so its owner already has this exact\n // popup's registration. A window notification alone has no target id and cannot name it.\n const registered = registrations.get(opened);\n\n active.registrations.push(\n registered === undefined\n ? failed(new Failed({ detail: \"the popup has no registration\" }))\n : Deferred.await(registered),\n );\n Deferred.doneUnsafe(active.changed, Exit.void);\n active.changed = Deferred.makeUnsafe<void>();\n };\n\n const clear = Effect.sync(() => {\n active = undefined;\n });\n\n yield* Effect.acquireRelease(\n Effect.sync(() => {\n cdp.on(\"Page.windowOpen\", windowOpened);\n playwright.on(\"popup\", popup);\n }),\n () =>\n Effect.sync(() => {\n active = undefined;\n cdp.off(\"Page.windowOpen\", windowOpened);\n playwright.off(\"popup\", popup);\n Deferred.doneUnsafe(gone, failed(new Closed({ cause: closedBy() })));\n }),\n );\n\n return {\n mark: () => {\n const mark: Mark = { windows: 0, registrations: [], changed: Deferred.makeUnsafe<void>() };\n\n active = mark;\n\n return mark;\n },\n clear,\n settle: (mark: Mark) =>\n Effect.gen(function* () {\n // A blocked window or a tab that closes as it opens may never become a Playwright popup.\n // Discovery and registration both share the input's deadline, so an unresolved opening\n // fails with potentially dispatched input rather than claiming the tab has been tracked.\n while (mark.registrations.length < mark.windows) yield* Deferred.await(mark.changed);\n // A real popup has a known registration; its failure stays a failure, and its wait shares\n // the input's deadline rather than quietly returning before the browser tracks it.\n yield* Effect.forEach(mark.registrations, (registration) => registration);\n }).pipe(Effect.raceFirst(Deferred.await(gone)), Effect.ensuring(clear)),\n };\n});\n\nexport type Openings = Effect.Success<ReturnType<typeof make>>;\n","/**\n * The browser's bounded event history and replay cursors. Readers share the same records and\n * wakeup generation, so a delayed reader cannot block input or retain an unbounded private queue.\n */\nimport { Cause, Deferred, Effect, Stream } from \"effect\";\n\nimport { type BrowserError, EventHistoryExpired, InvalidRequest } from \"../../BrowserError.ts\";\nimport { type BrowserEvent, RecordedEvent } from \"../../BrowserEvent.ts\";\nimport { undispatched } from \"../page/context.ts\";\n\n/** The owning Browser validates capacity and closes this timeline with its scope. */\nexport const make = (capacity: number) => {\n let records: ReadonlyArray<RecordedEvent> = [];\n let sequence = 0;\n let closed = false;\n let changed = Deferred.makeUnsafe<void>();\n\n const wake = () => {\n const previous = changed;\n\n changed = Deferred.makeUnsafe<void>();\n Deferred.doneUnsafe(previous, Effect.void);\n };\n\n const publish = (event: BrowserEvent): number => {\n // Native callbacks may finish after their owning scope. They must not revive closed readers.\n if (closed) return sequence;\n sequence += 1;\n const recorded = new RecordedEvent({ sequence, event });\n\n records = records.length < capacity ? [...records, recorded] : [...records.slice(1), recorded];\n wake();\n\n return sequence;\n };\n\n const stream = (after?: number): Stream.Stream<RecordedEvent, BrowserError> =>\n Stream.unwrap(\n Effect.sync(() => {\n // This runs for each subscription, rather than when the reusable stream is constructed.\n let cursor = after ?? sequence;\n\n if (!Number.isSafeInteger(cursor) || cursor < 0 || cursor > sequence)\n return Stream.fail(\n undispatched(\n \"events\",\n new InvalidRequest({\n detail:\n \"the event cursor must be a nonnegative integer no greater than the latest sequence\",\n }),\n ),\n );\n\n return Stream.fromEffectRepeat(\n Effect.gen(function* () {\n while (true) {\n const oldest = records[0]?.sequence ?? sequence + 1;\n\n if (cursor < oldest - 1)\n return yield* undispatched(\n \"events\",\n new EventHistoryExpired({ after: cursor, oldest, latest: sequence }),\n );\n const next = records[cursor - oldest + 1];\n\n if (next !== undefined) {\n cursor = next.sequence;\n\n return next;\n }\n if (closed) return yield* Cause.done();\n // Capture the generation in the same synchronous read as the empty history check.\n // A publish before await completes this exact Deferred, so no wakeup can be missed.\n const waiting = changed;\n\n yield* Deferred.await(waiting);\n }\n }),\n );\n }),\n );\n\n return {\n publish,\n sequence: () => sequence,\n recent: Effect.sync(() => records),\n stream,\n close: Effect.sync(() => {\n if (closed) return;\n closed = true;\n wake();\n }),\n };\n};\n","/**\n * A live browser: its pages and what happens in it.\n *\n * `Browser` is a service with one implementation, built by a provider over a Playwright browser\n * context: `Chromium` launches one locally, `Cdp` connects to any DevTools endpoint and\n * `effect-browserbase` allocates a hosted one. Code written against `Browser` runs on all three.\n * A page is named by its CDP target id, so a new connection to the same browser finds it again.\n *\n * @since 0.3.0\n */\nimport {\n Clock,\n Context,\n DateTime,\n Deferred,\n Duration,\n Effect,\n Exit,\n Option,\n Queue,\n Schema,\n Scope,\n Semaphore,\n type Stream,\n} from \"effect\";\nimport type { BrowserContext, Dialog, Page as PlaywrightPage } from \"playwright-core\";\n\nimport { type BrowserError, Failed, InvalidRequest, Limit } from \"./BrowserError.ts\";\nimport {\n type BrowserEvent,\n DialogShown,\n Disconnected,\n type DisconnectCause,\n PageClosed,\n PageOpened,\n PageUntracked,\n type RecordedEvent,\n SessionEnding,\n} from \"./BrowserEvent.ts\";\nimport * as Gate from \"./internal/input/gate.ts\";\nimport { call, failWith, type Settings, undispatched } from \"./internal/page/context.ts\";\nimport { FailFast } from \"./internal/page/lane.ts\";\nimport * as Openings from \"./internal/page/openings.ts\";\nimport * as PageImpl from \"./internal/page/page.ts\";\nimport * as Url from \"./internal/page/url.ts\";\nimport * as BrowserClock from \"./internal/pictures/clock.ts\";\nimport * as Timeline from \"./internal/timeline/events.ts\";\nimport type * as Page from \"./Page.ts\";\n\n/** A script that documents run before their own, such as a consent-banner remover. */\nexport interface InitScript {\n /**\n * The documents that run it, by a pattern their address (`location.href`) matches; every\n * document and frame when left out. Under a `match`, the source runs as a block, so its own\n * top-level `let`, `const` and classes stay its own.\n */\n readonly match?: RegExp | undefined;\n readonly source: string;\n}\n\nexport interface Options {\n /**\n * Bound on each action; finite and positive. Defaults to 10 seconds. What a presenter's view\n * takes to show an action, such as its glides and typing at a person's pace, does not count\n * against it.\n */\n readonly actionTimeout?: Duration.Input | undefined;\n /** Bound on each navigation; finite and positive. Defaults to 30 seconds. */\n readonly navigationTimeout?: Duration.Input | undefined;\n /**\n * How long each page keeps screencast frames for `recentFrames`, measured back from the newest;\n * finite and positive. Defaults to 5 seconds, a moment's default window.\n */\n readonly frameHistory?: Duration.Input | undefined;\n /** Events retained for replay and `recentEvents`. A positive safe integer, defaulting to 4,096. */\n readonly eventHistory?: number | undefined;\n /** Allow, deny or hold each input or navigation before it reaches the page. Defaults to allow. */\n readonly guard?: Page.InputGuard | undefined;\n /** A separate bound for policy holds, outside action timeouts. Defaults to 5 minutes. */\n readonly policyTimeout?: Duration.Input | undefined;\n /** Scripts that new documents run before their own, each where its `match` allows. */\n readonly initScripts?: ReadonlyArray<InitScript> | undefined;\n /**\n * The most pages the browser keeps open, those a site opens included, which are never refused. At\n * it, `newPage` waits for one to close, within the action timeout, then fails `Limit`; under\n * `Page.failFast` it fails at once. A positive safe integer; no limit when left out.\n */\n readonly maxPages?: number | undefined;\n}\n\nexport interface EventOptions {\n /** Resume after this sequence; 0 replays from the beginning if it is still retained. */\n readonly after?: number | undefined;\n}\n\nexport interface SuspendOptions {\n /** Finite, nonnegative drain bound; defaults to the browser's navigationTimeout. */\n readonly drain?: Duration.Input | undefined;\n}\n\n/** When admitted input finished; events after `since` describe the handoff interval. */\nexport class Suspension extends Schema.Class<Suspension>(\"effect-browser/Suspension\")({\n since: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n at: Schema.Finite,\n}) {}\n\n/**\n * A connection to the browser apart from the one that drives its pages, for their screencasts,\n * as a provider that reaches the browser over a network supplies it: a frame and its\n * acknowledgement then never wait behind a large message, such as an upload or a read's answer,\n * on the connection that drives the page. It is read-only. A page's session on it sends the\n * capture's commands alone, and the page's own session keeps its focus emulation, which keeps a\n * tab behind painting.\n */\nexport interface CaptureSource {\n /**\n * A session on the page's target, for the scope. `event` gets the session's events in the order\n * the browser sent them, its target's detach among them, until the scope closes; `lost` gets\n * the connection's failure, once, if it fails first. Its calls never throw: a failure rejects.\n */\n readonly attach: (\n target: string,\n listener: {\n readonly event: (method: string, params: unknown) => void;\n readonly lost: (error: BrowserError) => void;\n },\n ) => Effect.Effect<\n (\n method:\n | \"Page.enable\"\n | \"Page.getFrameTree\"\n | \"Page.startScreencast\"\n | \"Page.screencastFrameAck\"\n | \"Page.stopScreencast\",\n params?: Record<string, unknown>,\n ) => Promise<unknown>,\n BrowserError,\n Scope.Scope\n >;\n}\n\nexport interface Service {\n /** The provider's session id, or a local id. */\n readonly id: string;\n readonly provider: string;\n /** When the provider ends the session on its own, as a hosted session's timeout does. */\n readonly expiresAt: DateTime.Utc | undefined;\n /** Host monotonic milliseconds on the clock shared by this browser’s events and frames. */\n readonly now: Effect.Effect<number>;\n /** Drain admitted input and withhold new input for this scope; reads and capture continue. */\n readonly suspend: (\n options?: SuspendOptions,\n ) => Effect.Effect<Suspension, BrowserError, Scope.Scope>;\n /** The Playwright context, for anything this API does not cover. Never give it to a model. */\n readonly context: BrowserContext;\n /** Open pages in the order this browser began tracking them. */\n readonly pages: Effect.Effect<ReadonlyArray<Page.Page>>;\n /** The open page with this id, its CDP target id, as stored before a reconnect. */\n readonly page: (id: string) => Effect.Effect<Option.Option<Page.Page>>;\n /** The first open page, opening one when there is none. */\n readonly firstPage: Effect.Effect<Page.Page, BrowserError>;\n readonly newPage: (url?: string) => Effect.Effect<Page.Page, BrowserError>;\n /**\n * Completes once the browser is lost to its owner, with why: its connection dropped, its\n * provider ended its session, or its owner's scope released it.\n */\n readonly disconnected: Effect.Effect<DisconnectCause>;\n /** Live events, or replay after a cursor. A reader that falls behind fails with EventHistoryExpired. */\n readonly events: (options?: EventOptions) => Stream.Stream<RecordedEvent, BrowserError>;\n /** The latest `eventHistory` events, oldest first. */\n readonly recentEvents: Effect.Effect<ReadonlyArray<BrowserEvent>>;\n}\n\nexport class Browser extends Context.Service<Browser, Service>()(\"effect-browser/Browser\") {}\n\n/** The settings every page shares, and how many events the browser keeps, from its options. */\nconst settingsOf = Effect.fnUntraced(function* (options: Options) {\n const invalid = (detail: string) => undispatched(\"make\", new InvalidRequest({ detail }));\n\n // Every bound guards a lock or a held key, so each must be a real, finite deadline.\n const bound = (name: string, input: Duration.Input | undefined, fallback: Duration.Duration) => {\n const decoded = Duration.fromInput(input ?? fallback);\n\n return Option.isSome(decoded) &&\n Number.isFinite(Duration.toMillis(decoded.value)) &&\n Duration.isPositive(decoded.value)\n ? Effect.succeed(decoded.value)\n : Effect.fail(invalid(`${name} must be finite and greater than zero`));\n };\n\n const settings: Settings = {\n actionTimeout: yield* bound(\"actionTimeout\", options.actionTimeout, Duration.seconds(10)),\n navigationTimeout: yield* bound(\n \"navigationTimeout\",\n options.navigationTimeout,\n Duration.seconds(30),\n ),\n frameHistory: yield* bound(\"frameHistory\", options.frameHistory, Duration.seconds(5)),\n guard: options.guard,\n policyTimeout: yield* bound(\"policyTimeout\", options.policyTimeout, Duration.minutes(5)),\n };\n\n const eventHistory = options.eventHistory ?? 4096;\n const { maxPages } = options;\n\n if (!Number.isSafeInteger(eventHistory) || eventHistory < 1)\n return yield* invalid(\"eventHistory must be a positive safe integer\");\n if (maxPages !== undefined && (!Number.isSafeInteger(maxPages) || maxPages < 1))\n return yield* invalid(\"maxPages must be a positive safe integer\");\n\n return { settings, eventHistory, maxPages };\n});\n\n/**\n * A provider that ends a session at `expiresAt` by its own clock can cut the connection a little\n * before this host's clock gets there.\n */\nconst endMargin = Duration.seconds(5);\n\n/**\n * The browser's loss, once: its context closing from the other side, at or after the session's\n * end or before it, or `lose`, as the owner's scope releases it. Playwright leaves the calls in\n * flight on the library's own sessions unanswered when the connection drops, so `untilLost` fails\n * each with the browser, as a closed page's call fails.\n */\nconst watchLoss = Effect.fnUntraced(function* (\n context: BrowserContext,\n expiresAt: DateTime.Utc | undefined,\n clock: Clock.Clock,\n disconnected: (cause: DisconnectCause) => void,\n) {\n let lostBy: DisconnectCause | undefined;\n const lost = yield* Deferred.make<DisconnectCause>();\n const inFlight = new Set<(cause: Error) => void>();\n const gone = () => new Error(\"Target page, context or browser has been closed\");\n\n const lose = (cause: DisconnectCause) => {\n if (lostBy !== undefined) return;\n lostBy = cause;\n for (const reject of inFlight) reject(gone());\n disconnected(cause);\n Deferred.doneUnsafe(lost, Exit.succeed(cause));\n };\n\n const untilLost = <A>(reply: Promise<A>): Promise<A> => {\n if (lostBy !== undefined) {\n void reply.catch(() => undefined);\n\n return Promise.reject(gone());\n }\n\n return new Promise<A>((resolve, reject) => {\n inFlight.add(reject);\n void reply.then(resolve, reject).finally(() => inFlight.delete(reject));\n });\n };\n\n const ended = () =>\n expiresAt !== undefined &&\n clock.currentTimeMillisUnsafe() >=\n DateTime.toEpochMillis(expiresAt) - Duration.toMillis(endMargin);\n\n const onClose = () => lose(ended() ? \"session\" : \"connection\");\n\n context.on(\"close\", onClose);\n yield* Effect.addFinalizer(() => Effect.sync(() => context.off(\"close\", onClose)));\n if (context.browser()?.isConnected() === false) onClose();\n\n const native = <A>(operation: string, run: () => Promise<A>) =>\n call(operation, run, () => lostBy ?? \"page\");\n\n return { lostBy: () => lostBy, lost: Deferred.await(lost), lose, untilLost, native };\n});\n\n/**\n * What a page's own Playwright events publish, its close and crash aside, while its scope is\n * open. Dialogs are answered: alerts accepted, others dismissed. The page's own session tells its\n * documents' commits and loads.\n */\nconst listen = (\n playwright: PlaywrightPage,\n page: string,\n publish: (event: BrowserEvent) => number,\n now: () => number,\n on: { readonly close: () => void; readonly crash: () => void },\n) => {\n const dialog = (shown: Dialog) => {\n const kind = shown.type();\n const answer = kind === \"alert\" || kind === \"beforeunload\" ? \"accepted\" : \"dismissed\";\n const message = shown.message().slice(0, 500);\n\n publish(new DialogShown({ at: now(), page, kind, message, answer }));\n (answer === \"accepted\" ? shown.accept() : shown.dismiss()).catch(() => undefined);\n };\n\n return Effect.acquireRelease(\n Effect.sync(() => {\n playwright.on(\"close\", on.close);\n playwright.on(\"crash\", on.crash);\n playwright.on(\"dialog\", dialog);\n }),\n () =>\n Effect.sync(() => {\n playwright.off(\"close\", on.close);\n playwright.off(\"crash\", on.crash);\n playwright.off(\"dialog\", dialog);\n }),\n );\n};\n\n/**\n * Register each page the site opens, such as a popup, as Playwright reports it, for as long as the\n * scope is open. One that could not be tracked is published, unless it closed as it opened.\n */\nconst followOpened = Effect.fnUntraced(function* (\n context: BrowserContext,\n register: (playwright: PlaywrightPage) => Effect.Effect<Page.Page, BrowserError>,\n publish: (event: BrowserEvent) => number,\n now: () => number,\n registrations: Openings.Registrations,\n) {\n const opened = yield* Queue.unbounded<PlaywrightPage>();\n\n const onPage = (playwright: PlaywrightPage) => {\n registrations.set(playwright, Deferred.makeUnsafe<void, BrowserError>());\n Queue.offerUnsafe(opened, playwright);\n };\n\n const untracked = (playwright: PlaywrightPage) => (error: BrowserError) =>\n Effect.sync(() => {\n const url = Url.redact(playwright.url());\n\n if (!playwright.isClosed())\n publish(new PageUntracked({ at: now(), url, detail: error.message }));\n });\n\n context.on(\"page\", onPage);\n yield* Effect.addFinalizer(() => Effect.sync(() => context.off(\"page\", onPage)));\n yield* Queue.take(opened).pipe(\n Effect.flatMap((playwright) =>\n register(playwright).pipe(\n Effect.asVoid,\n Effect.onExit((exit) =>\n Effect.sync(() => {\n const done = registrations.get(playwright);\n\n if (done !== undefined) Deferred.doneUnsafe(done, exit);\n }),\n ),\n Effect.catch(untracked(playwright)),\n ),\n ),\n Effect.forever,\n Effect.forkScoped,\n );\n});\n\n/** Release something native, on the owner's clock, giving up after a second: cleanup only. */\nconst releaseNative = (clock: Clock.Clock) => (run: () => Promise<unknown>) =>\n Effect.tryPromise(run).pipe(\n Effect.interruptible,\n Effect.timeoutOrElse({ duration: Duration.seconds(1), orElse: () => Effect.void }),\n Effect.provideService(Clock.Clock, clock),\n Effect.ignore,\n );\n\n/** A page's own events among the browser's retained ones, and the openings of tabs it opened. */\nconst eventsOf = (recent: Effect.Effect<ReadonlyArray<RecordedEvent>>, page: string) =>\n Effect.map(recent, (records) =>\n records.flatMap(({ event }) =>\n (\"page\" in event && event.page === page) ||\n (event._tag === \"PageOpened\" && event.opener === page)\n ? [event]\n : [],\n ),\n );\n\n/** A script as its documents run it: everywhere, or as a block where its `match` allows. */\nconst scoped = ({ match, source }: InitScript) =>\n match === undefined ? source : `if (${String(match)}.test(location.href)) {\\n${source}\\n}`;\n\n/**\n * At most `maxPages` open pages, those a site opened included, which are never refused: an open\n * past it waits for one to close, within `wait` on the owner's clock, then fails `Limit`, or fails\n * at once under `FailFast`. An open counts until its page is tracked.\n */\nconst pageBudget = (\n maxPages: number | undefined,\n tracked: () => number,\n wait: Duration.Duration,\n clock: Clock.Clock,\n) => {\n let opening = 0;\n // Completed as a page closes or an open ends, so the opens waiting look again.\n let roomMade = Deferred.makeUnsafe<void>();\n\n const makeRoom = () => {\n Deferred.doneUnsafe(roomMade, Exit.void);\n roomMade = Deferred.makeUnsafe<void>();\n };\n\n const full = (limit: number) => tracked() + opening >= limit;\n\n const room = (limit: number) =>\n Effect.gen(function* () {\n const refused = undispatched(\"newPage\", new Limit({ maxPages: limit }));\n\n if ((yield* FailFast) && full(limit)) return yield* refused;\n yield* Effect.gen(function* () {\n while (full(limit)) yield* Deferred.await(roomMade);\n opening += 1;\n }).pipe(\n Effect.timeoutOrElse({ duration: wait, orElse: () => Effect.fail(refused) }),\n Effect.provideService(Clock.Clock, clock),\n );\n });\n\n const open = <A, E, R>(opened: Effect.Effect<A, E, R>) =>\n maxPages === undefined\n ? opened\n : Effect.uninterruptibleMask((restore) =>\n restore(room(maxPages)).pipe(\n Effect.andThen(\n restore(opened).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n opening -= 1;\n makeRoom();\n }),\n ),\n ),\n ),\n ),\n );\n\n return { makeRoom, open };\n};\n\n/** A call sent now and awaited later, by whatever needs its answer. */\nconst sentNow = <A>(sent: Promise<A>) => {\n void sent.catch(() => undefined);\n\n return sent;\n};\n\n/** Close tab resources after publishing their close; a browser loss speaks for all its tabs. */\nconst closures = (\n lostBy: () => DisconnectCause | undefined,\n publish: (event: BrowserEvent) => number,\n now: () => number,\n) =>\n Effect.gen(function* () {\n const closing = yield* Queue.unbounded<{\n readonly scope: Scope.Closeable;\n readonly page: string;\n readonly cause: PageClosed[\"cause\"];\n }>();\n\n yield* Queue.take(closing).pipe(\n Effect.tap(({ page, cause }) =>\n Effect.sync(() => {\n if (lostBy() === undefined) publish(new PageClosed({ at: now(), page, cause }));\n }),\n ),\n Effect.flatMap(({ scope }) => Scope.close(scope, Exit.void)),\n Effect.forever,\n Effect.forkScoped,\n );\n\n return closing;\n });\n\n/**\n * Build the service over a context the provider owns. The provider closes the context; this only\n * tracks its pages and events, for as long as the surrounding scope is open. Each page's protocol\n * session, capture and listeners end when that page closes or when the scope does, so a closed\n * tab retains nothing and the context keeps none of them afterwards.\n */\nexport const make = Effect.fn(\"Browser.make\")(function* (\n context: BrowserContext,\n info: {\n readonly id: string;\n readonly provider: string;\n readonly expiresAt?: DateTime.Utc | undefined;\n /** Where pages' screencasts run, if not on each page's own session. */\n readonly capture?: CaptureSource | undefined;\n },\n options: Options = {},\n) {\n const clock = yield* Clock.Clock;\n const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;\n const { settings, eventHistory, maxPages } = yield* settingsOf(options);\n // Measured on first need: the first capture of any page measures it, and input never waits.\n const mapping = BrowserClock.mapping(now);\n\n const timeline = Timeline.make(eventHistory);\n\n yield* Effect.addFinalizer(() => timeline.close);\n const publish = timeline.publish;\n\n if (info.expiresAt !== undefined)\n publish(new SessionEnding({ at: now(), expiresAt: info.expiresAt }));\n\n const { lostBy, lost, lose, untilLost, native } = yield* watchLoss(\n context,\n info.expiresAt,\n clock,\n (cause) => publish(new Disconnected({ at: now(), cause })),\n );\n\n const inputGate = Gate.make({\n now,\n sequence: timeline.sequence,\n closedBy: lostBy,\n lost,\n clock,\n drain: settings.navigationTimeout,\n });\n\n const registry = new Map<PlaywrightPage, Page.Page>();\n const registrations: Openings.Registrations = new WeakMap();\n const registering = yield* Semaphore.make(1);\n // The pages' scopes close after the browser is marked released, so what fails as they close\n // says why.\n const pagesScope = yield* Scope.fork(yield* Scope.Scope);\n\n const release = releaseNative(clock);\n const budget = pageBudget(maxPages, () => registry.size, settings.actionTimeout, clock);\n\n // Each tab's scope closes with the tab or the browser. A dropped connection closes every page,\n // then the context, and `Disconnected` stands for all of them: the queue hands a close to its\n // fiber in a later turn, once Playwright has reported the context's.\n const closing = yield* closures(lostBy, publish, now);\n\n const register = (playwright: PlaywrightPage) =>\n registering.withPermits(1)(\n Effect.gen(function* () {\n const known = registry.get(playwright);\n\n if (known !== undefined) return known;\n if (playwright.isClosed())\n return yield* failWith(\"newPage\", new Failed({ detail: \"the page closed as it opened\" }));\n const scope = yield* Scope.fork(pagesScope);\n\n return yield* Effect.gen(function* () {\n let crashed = false;\n const closedBy = () => lostBy() ?? (crashed ? \"crashed\" : \"page\");\n const cdp = yield* native(\"newPage\", () => context.newCDPSession(playwright));\n\n yield* Effect.addFinalizer(() => release(() => cdp.detach()));\n // The page's name, its target id, costs one round trip, which the calls that keep it\n // painting behind other tabs and report its documents share. A renderer busy with its\n // first script answers those two late, so what needs them waits, and registering does not.\n const send: typeof cdp.send = (method, params) => untilLost(cdp.send(method, params));\n const focusing = sentNow(send(\"Emulation.setFocusEmulationEnabled\", { enabled: true }));\n const paging = sentNow(send(\"Page.enable\"));\n const { targetInfo } = yield* native(\"newPage\", () => send(\"Target.getTargetInfo\"));\n const id = targetInfo.targetId;\n\n const page = yield* PageImpl.make({\n id,\n session: info.id,\n url: targetInfo.url,\n playwright,\n cdp,\n untilLost,\n settings,\n inputGate,\n clock,\n mapping,\n publish,\n recentEvents: eventsOf(timeline.recent, id),\n openings: yield* Openings.make(playwright, cdp, registrations, closedBy),\n focused: call(\"focus\", () => focusing, closedBy).pipe(Effect.asVoid),\n paging: (operation) => call(operation, () => paging, closedBy).pipe(Effect.asVoid),\n closedBy,\n capture: info.capture,\n });\n\n const close = () => {\n if (registry.get(playwright) !== page) return;\n registry.delete(playwright);\n budget.makeRoom();\n Queue.offerUnsafe(closing, { scope, page: id, cause: crashed ? \"crashed\" : \"page\" });\n };\n\n // Playwright drives a crashed page no more and its calls never return, so it is closed:\n // they fail at once, `Closed` because it crashed.\n const crash = () => {\n crashed = true;\n void playwright.close().catch(() => undefined);\n };\n\n registry.set(playwright, page);\n publish(\n new PageOpened({\n at: now(),\n page: id,\n url: Url.redact(targetInfo.url),\n opener: targetInfo.openerId,\n }),\n );\n yield* listen(playwright, id, publish, now, { close, crash });\n if (playwright.isClosed()) close();\n\n return page;\n }).pipe(\n Scope.provide(scope),\n Effect.onError(() => Scope.close(scope, Exit.void)),\n );\n }),\n );\n\n // Playwright registers its init scripts as it sets up each page, before a popup's first document\n // runs, and they cost no call per document.\n for (const script of options.initScripts ?? [])\n yield* native(\"initScripts\", () => context.addInitScript({ content: scoped(script) }));\n yield* followOpened(context, register, publish, now, registrations);\n for (const existing of context.pages()) yield* register(existing);\n\n const pages = Effect.sync(() => [...registry.values()]);\n\n const newPage = Effect.fn(\"Browser.newPage\")(function* (url?: string) {\n const page = yield* budget.open(\n native(\"newPage\", () => context.newPage()).pipe(\n Effect.flatMap((playwright) =>\n register(playwright).pipe(Effect.onError(() => release(() => playwright.close()))),\n ),\n ),\n );\n\n // A rejected or interrupted navigation must not leave the newly allocated blank tab behind.\n if (url !== undefined)\n yield* page.goto(url).pipe(Effect.onError(() => page.close.pipe(Effect.ignore)));\n\n return page;\n });\n\n // Added last, so it runs first as the owner's scope closes.\n yield* Effect.addFinalizer(() => Effect.sync(() => lose(\"released\")));\n\n const service: Service = {\n id: info.id,\n provider: info.provider,\n expiresAt: info.expiresAt,\n now: Effect.sync(now),\n suspend: (options = {}) =>\n Effect.map(inputGate.suspend(options.drain), (held) => new Suspension(held)),\n context,\n pages,\n page: (id) =>\n Effect.map(pages, (open) => Option.fromNullishOr(open.find((page) => page.id === id))),\n firstPage: pages.pipe(\n Effect.map((open) => Option.fromNullishOr(open[0])),\n Effect.flatMap(Option.match({ onNone: () => newPage(), onSome: Effect.succeed })),\n ),\n newPage,\n disconnected: lost,\n events: (eventOptions = {}) => timeline.stream(eventOptions.after),\n recentEvents: timeline.recent.pipe(\n Effect.map((records) => records.map((record) => record.event)),\n ),\n };\n\n return Browser.of(service);\n});\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { t as BrowserError } from "./BrowserError-DXXIA6Rk.mjs";
1
+ import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
2
  import { c as DisconnectCause, i as BrowserEvent, v as RecordedEvent } from "./BrowserEvent-BRu2juEz.mjs";
3
- import { c as InputGuard, d as Page_d_exports, u as Page$1 } from "./Page-C30aG2RQ.mjs";
4
- import { Context, DateTime, Duration, Effect, Option, Scope, Stream } from "effect";
3
+ import { c as InputGuard, d as Page_d_exports, u as Page$1 } from "./Page-BlPjfibv.mjs";
4
+ import { Context, DateTime, Duration, Effect, Option, Schema, Scope, Stream } from "effect";
5
5
  import { BrowserContext } from "playwright-core";
6
6
  declare namespace Browser_d_exports {
7
- export { Browser, CaptureSource, EventOptions, InitScript, Options, Service, make };
7
+ export { Browser, CaptureSource, EventOptions, InitScript, Options, Service, SuspendOptions, Suspension, make };
8
8
  }
9
9
  /** A script that documents run before their own, such as a consent-banner remover. */
10
10
  export interface InitScript {
@@ -49,6 +49,16 @@ export interface EventOptions {
49
49
  /** Resume after this sequence; 0 replays from the beginning if it is still retained. */
50
50
  readonly after?: number | undefined;
51
51
  }
52
+ export interface SuspendOptions {
53
+ /** Finite, nonnegative drain bound; defaults to the browser's navigationTimeout. */
54
+ readonly drain?: Duration.Input | undefined;
55
+ }
56
+ declare const Suspension_base: Schema.Class<Suspension, Schema.Struct<{
57
+ readonly since: Schema.Int;
58
+ readonly at: Schema.Finite;
59
+ }>, {}>;
60
+ /** When admitted input finished; events after `since` describe the handoff interval. */
61
+ export declare class Suspension extends Suspension_base {}
52
62
  /**
53
63
  * A connection to the browser apart from the one that drives its pages, for their screencasts,
54
64
  * as a provider that reaches the browser over a network supplies it: a frame and its
@@ -76,6 +86,8 @@ export interface Service {
76
86
  readonly expiresAt: DateTime.Utc | undefined;
77
87
  /** Host monotonic milliseconds on the clock shared by this browser’s events and frames. */
78
88
  readonly now: Effect.Effect<number>;
89
+ /** Drain admitted input and withhold new input for this scope; reads and capture continue. */
90
+ readonly suspend: (options?: SuspendOptions) => Effect.Effect<Suspension, BrowserError, Scope.Scope>;
79
91
  /** The Playwright context, for anything this API does not cover. Never give it to a model. */
80
92
  readonly context: BrowserContext;
81
93
  /** Open pages in the order this browser began tracking them. */
package/dist/Browser.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { r as make, t as Browser } from "./Browser-CNwK0r0q.mjs";
2
- export { Browser, make };
1
+ import { i as make, r as Suspension, t as Browser } from "./Browser-Bc_LIqTC.mjs";
2
+ export { Browser, Suspension, make };
@@ -1,6 +1,6 @@
1
1
  import { Schema } from "effect";
2
2
  declare namespace BrowserError_d_exports {
3
- export { BrowserError, Busy, Closed, Consequence, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Timeout, consequence };
3
+ export { BrowserError, Busy, Closed, Consequence, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Suspended, Timeout, consequence };
4
4
  }
5
5
  declare const Timeout_base: Schema.Class<Timeout, Schema.TaggedStruct<"Timeout", {
6
6
  readonly millis: Schema.Finite;
@@ -21,6 +21,11 @@ declare const Busy_base: Schema.Class<Busy, Schema.TaggedStruct<"Busy", {
21
21
  declare class Busy extends Busy_base {
22
22
  get message(): string;
23
23
  }
24
+ declare const Suspended_base: Schema.Class<Suspended, Schema.TaggedStruct<"Suspended", {}>, import("effect/Cause").YieldableError>;
25
+ /** Input is withheld by a scoped suspension; nothing was dispatched. */
26
+ declare class Suspended extends Suspended_base {
27
+ get message(): string;
28
+ }
24
29
  declare const Limit_base: Schema.Class<Limit, Schema.TaggedStruct<"Limit", {
25
30
  readonly maxPages: Schema.Int;
26
31
  }>, import("effect/Cause").YieldableError>;
@@ -106,11 +111,11 @@ declare const Failed_base: Schema.Class<Failed, Schema.TaggedStruct<"Failed", {
106
111
  declare class Failed extends Failed_base {
107
112
  get message(): string;
108
113
  }
109
- declare const Reason: Schema.Union<readonly [typeof Timeout, typeof Busy, typeof Limit, typeof StaleRef, typeof NotFound, typeof NotActionable, typeof NavigationFailed, typeof Closed, typeof InvalidRequest, typeof PolicyDenied, typeof PolicyTimeout, typeof EventHistoryExpired, typeof Failed]>;
114
+ declare const Reason: Schema.Union<readonly [typeof Timeout, typeof Busy, typeof Suspended, typeof Limit, typeof StaleRef, typeof NotFound, typeof NotActionable, typeof NavigationFailed, typeof Closed, typeof InvalidRequest, typeof PolicyDenied, typeof PolicyTimeout, typeof EventHistoryExpired, typeof Failed]>;
110
115
  type Reason = typeof Reason.Type;
111
116
  declare const BrowserError_base: Schema.Class<BrowserError, Schema.TaggedStruct<"BrowserError", {
112
117
  readonly operation: Schema.String;
113
- readonly reason: Schema.Union<readonly [typeof Timeout, typeof Busy, typeof Limit, typeof StaleRef, typeof NotFound, typeof NotActionable, typeof NavigationFailed, typeof Closed, typeof InvalidRequest, typeof PolicyDenied, typeof PolicyTimeout, typeof EventHistoryExpired, typeof Failed]>;
118
+ readonly reason: Schema.Union<readonly [typeof Timeout, typeof Busy, typeof Suspended, typeof Limit, typeof StaleRef, typeof NotFound, typeof NotActionable, typeof NavigationFailed, typeof Closed, typeof InvalidRequest, typeof PolicyDenied, typeof PolicyTimeout, typeof EventHistoryExpired, typeof Failed]>;
114
119
  readonly dispatched: Schema.Boolean;
115
120
  }>, import("effect/Cause").YieldableError>;
116
121
  declare class BrowserError extends BrowserError_base {
@@ -131,5 +136,5 @@ interface Consequence {
131
136
  /** What `error` leaves, from its reason and whether it was dispatched alone. */
132
137
  declare const consequence: ({ reason, dispatched }: BrowserError) => Consequence;
133
138
  //#endregion
134
- export { Timeout as _, Consequence as a, InvalidRequest as c, NotActionable as d, NotFound as f, StaleRef as g, Reason as h, Closed as i, Limit as l, PolicyTimeout as m, BrowserError_d_exports as n, EventHistoryExpired as o, PolicyDenied as p, Busy as r, Failed as s, BrowserError as t, NavigationFailed as u, consequence as v };
135
- //# sourceMappingURL=BrowserError-DXXIA6Rk.d.mts.map
139
+ export { Suspended as _, Consequence as a, InvalidRequest as c, NotActionable as d, NotFound as f, StaleRef as g, Reason as h, Closed as i, Limit as l, PolicyTimeout as m, BrowserError_d_exports as n, EventHistoryExpired as o, PolicyDenied as p, Busy as r, Failed as s, BrowserError as t, NavigationFailed as u, Timeout as v, consequence as y };
140
+ //# sourceMappingURL=BrowserError-Coy7EW1N.d.mts.map
@@ -1,2 +1,2 @@
1
- import { _ as Timeout, a as Consequence, c as InvalidRequest, d as NotActionable, f as NotFound, g as StaleRef, h as Reason, i as Closed, l as Limit, m as PolicyTimeout, o as EventHistoryExpired, p as PolicyDenied, r as Busy, s as Failed, t as BrowserError, u as NavigationFailed, v as consequence } from "./BrowserError-DXXIA6Rk.mjs";
2
- export { BrowserError, Busy, Closed, Consequence, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Timeout, consequence };
1
+ import { _ as Suspended, a as Consequence, c as InvalidRequest, d as NotActionable, f as NotFound, g as StaleRef, h as Reason, i as Closed, l as Limit, m as PolicyTimeout, o as EventHistoryExpired, p as PolicyDenied, r as Busy, s as Failed, t as BrowserError, u as NavigationFailed, v as Timeout, y as consequence } from "./BrowserError-Coy7EW1N.mjs";
2
+ export { BrowserError, Busy, Closed, Consequence, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Suspended, Timeout, consequence };
@@ -27,6 +27,7 @@ var BrowserError_exports = /* @__PURE__ */ __exportAll({
27
27
  PolicyTimeout: () => PolicyTimeout,
28
28
  Reason: () => Reason,
29
29
  StaleRef: () => StaleRef,
30
+ Suspended: () => Suspended,
30
31
  Timeout: () => Timeout,
31
32
  consequence: () => consequence
32
33
  });
@@ -50,6 +51,12 @@ var Busy = class extends Schema.TaggedError()("Busy", {
50
51
  return `the page was busy: it waited ${this.waitedMillis} ms behind ${this.ahead} other ${others}`;
51
52
  }
52
53
  };
54
+ /** Input is withheld by a scoped suspension; nothing was dispatched. */
55
+ var Suspended = class extends Schema.TaggedError()("Suspended", {}) {
56
+ get message() {
57
+ return "browser input is suspended";
58
+ }
59
+ };
53
60
  /** The browser has as many pages open as `maxPages` allows, and none closed in time. */
54
61
  var Limit = class extends Schema.TaggedError()("Limit", { maxPages: Schema.Int }) {
55
62
  get message() {
@@ -144,6 +151,7 @@ var Failed = class extends Schema.TaggedError()("Failed", { detail: Schema.Strin
144
151
  const Reason = Schema.Union([
145
152
  Timeout,
146
153
  Busy,
154
+ Suspended,
147
155
  Limit,
148
156
  StaleRef,
149
157
  NotFound,
@@ -174,6 +182,7 @@ const consequence = ({ reason, dispatched }) => {
174
182
  repeat
175
183
  };
176
184
  case "Busy":
185
+ case "Suspended":
177
186
  case "Timeout":
178
187
  case "Failed":
179
188
  case "NavigationFailed": return {
@@ -197,6 +206,6 @@ const consequence = ({ reason, dispatched }) => {
197
206
  }
198
207
  };
199
208
  //#endregion
200
- export { BrowserError, Busy, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Timeout, consequence, BrowserError_exports as t };
209
+ export { BrowserError, Busy, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit, NavigationFailed, NotActionable, NotFound, PolicyDenied, PolicyTimeout, Reason, StaleRef, Suspended, Timeout, consequence, BrowserError_exports as t };
201
210
 
202
211
  //# sourceMappingURL=BrowserError.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserError.mjs","names":[],"sources":["../src/BrowserError.ts"],"sourcesContent":["/**\n * The one error every browser operation fails with.\n *\n * `reason` says what went wrong and `dispatched` says whether the action's own input (a press,\n * key, wheel, selection or hover, not the pointer travelling toward a press) or a navigation\n * reached the browser first. `consequence` says what that leaves: what was lost, and whether the\n * call can be repeated. `message` is a sentence for operators.\n *\n * @since 0.3.0\n */\nimport { Schema } from \"effect\";\n\nimport { DisconnectCause } from \"./BrowserEvent.ts\";\n\n/** The operation did not finish in time. */\nexport class Timeout extends Schema.TaggedError<Timeout>()(\"Timeout\", {\n millis: Schema.Finite,\n}) {\n override get message() {\n return `timed out after ${this.millis} ms`;\n }\n}\n\n/**\n * The operation waited its turn behind others on its page and was not given one in time, or was\n * asked to fail fast: the page may be well, only busy.\n */\nexport class Busy extends Schema.TaggedError<Busy>()(\"Busy\", {\n waitedMillis: Schema.Finite,\n /** The operations ahead of it: those holding the page and those asked before it. */\n ahead: Schema.Int,\n}) {\n override get message() {\n const others = this.ahead === 1 ? \"operation\" : \"operations\";\n\n return `the page was busy: it waited ${this.waitedMillis} ms behind ${this.ahead} other ${others}`;\n }\n}\n\n/** The browser has as many pages open as `maxPages` allows, and none closed in time. */\nexport class Limit extends Schema.TaggedError<Limit>()(\"Limit\", {\n maxPages: Schema.Int,\n}) {\n override get message() {\n return `the browser already has ${this.maxPages} pages open, as many as it allows`;\n }\n}\n\n/** A ref from an earlier snapshot no longer names an element on the page. */\nexport class StaleRef extends Schema.TaggedError<StaleRef>()(\"StaleRef\", {\n ref: Schema.String,\n}) {\n override get message() {\n return `${this.ref} is not on the page any more`;\n }\n}\n\n/** What the operation looked for is not on the page. */\nexport class NotFound extends Schema.TaggedError<NotFound>()(\"NotFound\", {\n target: Schema.String,\n}) {\n override get message() {\n return `${this.target} was not found`;\n }\n}\n\n/** The element exists but cannot take this input: covered, disabled, hidden or the wrong kind. */\nexport class NotActionable extends Schema.TaggedError<NotActionable>()(\"NotActionable\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\n/** A navigation failed, for example on a DNS error or a refused connection. */\nexport class NavigationFailed extends Schema.TaggedError<NavigationFailed>()(\"NavigationFailed\", {\n url: Schema.String,\n detail: Schema.String,\n}) {\n override get message() {\n return `could not load ${this.url}: ${this.detail}`;\n }\n}\n\nconst closedBy = {\n page: \"the page was closed\",\n crashed: \"the page crashed\",\n connection: \"the connection to the browser was lost\",\n session: \"the browser's session ended\",\n released: \"the browser was released\",\n};\n\n/**\n * The page is gone: it was closed (`page`) or crashed, or its browser was lost for one of the\n * browser's `DisconnectCause`s.\n */\nexport class Closed extends Schema.TaggedError<Closed>()(\"Closed\", {\n cause: Schema.Literals([\"page\", \"crashed\", ...DisconnectCause.literals]),\n}) {\n override get message() {\n return closedBy[this.cause];\n }\n}\n\n/** The request itself is invalid, such as a ref that is not a ref or a point off the screen. */\nexport class InvalidRequest extends Schema.TaggedError<InvalidRequest>()(\"InvalidRequest\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\n/** The input policy refused the action before any input was sent. */\nexport class PolicyDenied extends Schema.TaggedError<PolicyDenied>()(\"PolicyDenied\", {\n detail: Schema.String,\n /** What made the policy deny, such as the failure of the judge it asked. */\n cause: Schema.optional(Schema.Defect()),\n}) {\n override get message() {\n return `the input policy denied this action: ${this.detail}`;\n }\n}\n\n/** The input policy did not release its hold within its own bound. */\nexport class PolicyTimeout extends Schema.TaggedError<PolicyTimeout>()(\"PolicyTimeout\", {\n millis: Schema.Finite,\n}) {\n override get message() {\n return `the input policy did not allow this action within ${this.millis} ms`;\n }\n}\n\n/** The requested event cursor fell behind the browser's bounded replay history. */\nexport class EventHistoryExpired extends Schema.TaggedError<EventHistoryExpired>()(\n \"EventHistoryExpired\",\n {\n after: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n oldest: Schema.Int.check(Schema.isGreaterThan(0)),\n latest: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n },\n) {\n override get message() {\n return `event history after ${this.after} expired; retained sequences are ${this.oldest}–${this.latest}`;\n }\n}\n\n/** The browser, the connection or the provider failed in a way the other reasons do not cover. */\nexport class Failed extends Schema.TaggedError<Failed>()(\"Failed\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\nexport const Reason = Schema.Union([\n Timeout,\n Busy,\n Limit,\n StaleRef,\n NotFound,\n NotActionable,\n NavigationFailed,\n Closed,\n InvalidRequest,\n PolicyDenied,\n PolicyTimeout,\n EventHistoryExpired,\n Failed,\n]);\n\nexport type Reason = typeof Reason.Type;\n\nexport class BrowserError extends Schema.TaggedError<BrowserError>()(\"BrowserError\", {\n operation: Schema.String,\n reason: Reason,\n dispatched: Schema.Boolean,\n}) {\n override get message() {\n return `${this.operation} failed${this.dispatched ? \" after it was sent\" : \"\"}: ${this.reason.message}`;\n }\n}\n\n/**\n * What a failure leaves. `lost` is `\"page\"` when the page is gone and its browser stands, and\n * `\"session\"` when the browser is gone with its pages. `repeat` says whether the call can be made\n * again, on what still stands: `\"safe\"`, as nothing reached the browser; `\"check\"`, as it may have\n * taken effect, so look first, as an `Expect` attempt does; `\"pointless\"`, as it fails again unless\n * something changes, such as\n * a new ref; or `\"resume\"`, reading events again from a newer cursor.\n */\nexport interface Consequence {\n readonly lost: \"nothing\" | \"page\" | \"session\";\n readonly repeat: \"safe\" | \"check\" | \"pointless\" | \"resume\";\n}\n\n/** What `error` leaves, from its reason and whether it was dispatched alone. */\nexport const consequence = ({ reason, dispatched }: BrowserError): Consequence => {\n const repeat = dispatched ? \"check\" : \"safe\";\n\n switch (reason._tag) {\n case \"Closed\":\n return {\n lost: reason.cause === \"page\" || reason.cause === \"crashed\" ? \"page\" : \"session\",\n repeat,\n };\n case \"Busy\":\n case \"Timeout\":\n case \"Failed\":\n case \"NavigationFailed\":\n return { lost: \"nothing\", repeat };\n case \"EventHistoryExpired\":\n return { lost: \"nothing\", repeat: \"resume\" };\n case \"StaleRef\":\n case \"NotFound\":\n case \"NotActionable\":\n case \"InvalidRequest\":\n case \"Limit\":\n case \"PolicyDenied\":\n case \"PolicyTimeout\":\n return { lost: \"nothing\", repeat: \"pointless\" };\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"BrowserError.mjs","names":[],"sources":["../src/BrowserError.ts"],"sourcesContent":["/**\n * The one error every browser operation fails with.\n *\n * `reason` says what went wrong and `dispatched` says whether the action's own input (a press,\n * key, wheel, selection or hover, not the pointer travelling toward a press) or a navigation\n * reached the browser first. `consequence` says what that leaves: what was lost, and whether the\n * call can be repeated. `message` is a sentence for operators.\n *\n * @since 0.3.0\n */\nimport { Schema } from \"effect\";\n\nimport { DisconnectCause } from \"./BrowserEvent.ts\";\n\n/** The operation did not finish in time. */\nexport class Timeout extends Schema.TaggedError<Timeout>()(\"Timeout\", {\n millis: Schema.Finite,\n}) {\n override get message() {\n return `timed out after ${this.millis} ms`;\n }\n}\n\n/**\n * The operation waited its turn behind others on its page and was not given one in time, or was\n * asked to fail fast: the page may be well, only busy.\n */\nexport class Busy extends Schema.TaggedError<Busy>()(\"Busy\", {\n waitedMillis: Schema.Finite,\n /** The operations ahead of it: those holding the page and those asked before it. */\n ahead: Schema.Int,\n}) {\n override get message() {\n const others = this.ahead === 1 ? \"operation\" : \"operations\";\n\n return `the page was busy: it waited ${this.waitedMillis} ms behind ${this.ahead} other ${others}`;\n }\n}\n\n/** Input is withheld by a scoped suspension; nothing was dispatched. */\nexport class Suspended extends Schema.TaggedError<Suspended>()(\"Suspended\", {}) {\n override get message() {\n return \"browser input is suspended\";\n }\n}\n\n/** The browser has as many pages open as `maxPages` allows, and none closed in time. */\nexport class Limit extends Schema.TaggedError<Limit>()(\"Limit\", {\n maxPages: Schema.Int,\n}) {\n override get message() {\n return `the browser already has ${this.maxPages} pages open, as many as it allows`;\n }\n}\n\n/** A ref from an earlier snapshot no longer names an element on the page. */\nexport class StaleRef extends Schema.TaggedError<StaleRef>()(\"StaleRef\", {\n ref: Schema.String,\n}) {\n override get message() {\n return `${this.ref} is not on the page any more`;\n }\n}\n\n/** What the operation looked for is not on the page. */\nexport class NotFound extends Schema.TaggedError<NotFound>()(\"NotFound\", {\n target: Schema.String,\n}) {\n override get message() {\n return `${this.target} was not found`;\n }\n}\n\n/** The element exists but cannot take this input: covered, disabled, hidden or the wrong kind. */\nexport class NotActionable extends Schema.TaggedError<NotActionable>()(\"NotActionable\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\n/** A navigation failed, for example on a DNS error or a refused connection. */\nexport class NavigationFailed extends Schema.TaggedError<NavigationFailed>()(\"NavigationFailed\", {\n url: Schema.String,\n detail: Schema.String,\n}) {\n override get message() {\n return `could not load ${this.url}: ${this.detail}`;\n }\n}\n\nconst closedBy = {\n page: \"the page was closed\",\n crashed: \"the page crashed\",\n connection: \"the connection to the browser was lost\",\n session: \"the browser's session ended\",\n released: \"the browser was released\",\n};\n\n/**\n * The page is gone: it was closed (`page`) or crashed, or its browser was lost for one of the\n * browser's `DisconnectCause`s.\n */\nexport class Closed extends Schema.TaggedError<Closed>()(\"Closed\", {\n cause: Schema.Literals([\"page\", \"crashed\", ...DisconnectCause.literals]),\n}) {\n override get message() {\n return closedBy[this.cause];\n }\n}\n\n/** The request itself is invalid, such as a ref that is not a ref or a point off the screen. */\nexport class InvalidRequest extends Schema.TaggedError<InvalidRequest>()(\"InvalidRequest\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\n/** The input policy refused the action before any input was sent. */\nexport class PolicyDenied extends Schema.TaggedError<PolicyDenied>()(\"PolicyDenied\", {\n detail: Schema.String,\n /** What made the policy deny, such as the failure of the judge it asked. */\n cause: Schema.optional(Schema.Defect()),\n}) {\n override get message() {\n return `the input policy denied this action: ${this.detail}`;\n }\n}\n\n/** The input policy did not release its hold within its own bound. */\nexport class PolicyTimeout extends Schema.TaggedError<PolicyTimeout>()(\"PolicyTimeout\", {\n millis: Schema.Finite,\n}) {\n override get message() {\n return `the input policy did not allow this action within ${this.millis} ms`;\n }\n}\n\n/** The requested event cursor fell behind the browser's bounded replay history. */\nexport class EventHistoryExpired extends Schema.TaggedError<EventHistoryExpired>()(\n \"EventHistoryExpired\",\n {\n after: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n oldest: Schema.Int.check(Schema.isGreaterThan(0)),\n latest: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n },\n) {\n override get message() {\n return `event history after ${this.after} expired; retained sequences are ${this.oldest}–${this.latest}`;\n }\n}\n\n/** The browser, the connection or the provider failed in a way the other reasons do not cover. */\nexport class Failed extends Schema.TaggedError<Failed>()(\"Failed\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\nexport const Reason = Schema.Union([\n Timeout,\n Busy,\n Suspended,\n Limit,\n StaleRef,\n NotFound,\n NotActionable,\n NavigationFailed,\n Closed,\n InvalidRequest,\n PolicyDenied,\n PolicyTimeout,\n EventHistoryExpired,\n Failed,\n]);\n\nexport type Reason = typeof Reason.Type;\n\nexport class BrowserError extends Schema.TaggedError<BrowserError>()(\"BrowserError\", {\n operation: Schema.String,\n reason: Reason,\n dispatched: Schema.Boolean,\n}) {\n override get message() {\n return `${this.operation} failed${this.dispatched ? \" after it was sent\" : \"\"}: ${this.reason.message}`;\n }\n}\n\n/**\n * What a failure leaves. `lost` is `\"page\"` when the page is gone and its browser stands, and\n * `\"session\"` when the browser is gone with its pages. `repeat` says whether the call can be made\n * again, on what still stands: `\"safe\"`, as nothing reached the browser; `\"check\"`, as it may have\n * taken effect, so look first, as an `Expect` attempt does; `\"pointless\"`, as it fails again unless\n * something changes, such as\n * a new ref; or `\"resume\"`, reading events again from a newer cursor.\n */\nexport interface Consequence {\n readonly lost: \"nothing\" | \"page\" | \"session\";\n readonly repeat: \"safe\" | \"check\" | \"pointless\" | \"resume\";\n}\n\n/** What `error` leaves, from its reason and whether it was dispatched alone. */\nexport const consequence = ({ reason, dispatched }: BrowserError): Consequence => {\n const repeat = dispatched ? \"check\" : \"safe\";\n\n switch (reason._tag) {\n case \"Closed\":\n return {\n lost: reason.cause === \"page\" || reason.cause === \"crashed\" ? \"page\" : \"session\",\n repeat,\n };\n case \"Busy\":\n case \"Suspended\":\n case \"Timeout\":\n case \"Failed\":\n case \"NavigationFailed\":\n return { lost: \"nothing\", repeat };\n case \"EventHistoryExpired\":\n return { lost: \"nothing\", repeat: \"resume\" };\n case \"StaleRef\":\n case \"NotFound\":\n case \"NotActionable\":\n case \"InvalidRequest\":\n case \"Limit\":\n case \"PolicyDenied\":\n case \"PolicyTimeout\":\n return { lost: \"nothing\", repeat: \"pointless\" };\n }\n};\n"],"mappings":"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"}