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

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 (70) hide show
  1. package/README.md +94 -3
  2. package/dist/Browser-CiXHT1yO.d.mts +129 -0
  3. package/dist/{Browser-H63wiekr.mjs → Browser-CxXUjxOp.mjs} +10 -19
  4. package/dist/Browser-CxXUjxOp.mjs.map +1 -0
  5. package/dist/Browser.d.mts +2 -129
  6. package/dist/Browser.mjs +1 -1
  7. package/dist/Cdp-CzCRnMlM.d.mts +10 -0
  8. package/dist/Cdp.d.mts +5 -26
  9. package/dist/Cdp.mjs +4 -45
  10. package/dist/Cdp.mjs.map +1 -1
  11. package/dist/Chromium.d.mts +1 -1
  12. package/dist/Chromium.mjs +3 -3
  13. package/dist/Chromium.mjs.map +1 -1
  14. package/dist/Expect-u3P_ENLX.d.mts +62 -0
  15. package/dist/Expect.d.mts +2 -62
  16. package/dist/Expect.mjs +2 -2
  17. package/dist/LICENSE-jpeg-js +28 -0
  18. package/dist/Moment.mjs +1 -1
  19. package/dist/Page-BP02bmNv.mjs +264 -0
  20. package/dist/{Page-CfB6Zn1p.mjs.map → Page-BP02bmNv.mjs.map} +1 -1
  21. package/dist/Page.mjs +1 -1
  22. package/dist/{Plan-C-d5QYS8.mjs → Plan-BG1WifW2.mjs} +48 -4
  23. package/dist/Plan-BG1WifW2.mjs.map +1 -0
  24. package/dist/Plan.mjs +1 -1
  25. package/dist/Policy-BGWjbqCl.d.mts +76 -0
  26. package/dist/Policy.d.mts +2 -76
  27. package/dist/Policy.mjs +1 -1
  28. package/dist/Presentation-DxVikn9_.d.mts +48 -0
  29. package/dist/Presentation.d.mts +2 -48
  30. package/dist/Presentation.mjs +2 -2
  31. package/dist/Stage-NHp8N81k.d.mts +65 -0
  32. package/dist/Stage.d.mts +2 -65
  33. package/dist/Stage.mjs +2 -2
  34. package/dist/{Supervisor-mVPeEUp8.d.mts → Supervisor-CfRxbaso.d.mts} +2 -2
  35. package/dist/Supervisor.d.mts +1 -1
  36. package/dist/cdp-BMpYMWYe.mjs +69 -0
  37. package/dist/cdp-BMpYMWYe.mjs.map +1 -0
  38. package/dist/cdp-DL7XfEj5.d.mts +41 -0
  39. package/dist/{context-w3NJPRLo.mjs → context-EGhOqjNN.mjs} +5 -5
  40. package/dist/context-EGhOqjNN.mjs.map +1 -0
  41. package/dist/index.d.mts +7 -6
  42. package/dist/index.mjs +4 -3
  43. package/dist/page-PTM8m5wC.mjs +4057 -0
  44. package/dist/page-PTM8m5wC.mjs.map +1 -0
  45. package/dist/{url-Cd04Okid.mjs → url-Bsp2y55M.mjs} +2 -2
  46. package/dist/{url-Cd04Okid.mjs.map → url-Bsp2y55M.mjs.map} +1 -1
  47. package/dist/workerd/Cdp.d.mts +3 -0
  48. package/dist/workerd/Cdp.mjs +18 -0
  49. package/dist/workerd/Cdp.mjs.map +1 -0
  50. package/dist/workerd/index.d.mts +16 -0
  51. package/dist/workerd/index.mjs +17 -0
  52. package/package.json +15 -4
  53. package/src/Browser.ts +20 -30
  54. package/src/Cdp.ts +7 -92
  55. package/src/index.ts +1 -15
  56. package/src/internal/cdp.ts +134 -0
  57. package/src/internal/page/bridge.ts +2 -2
  58. package/src/internal/page/context.ts +21 -24
  59. package/src/internal/page/identity.ts +6 -25
  60. package/src/internal/pictures/motion.ts +3 -3
  61. package/src/workerd/Cdp.ts +11 -0
  62. package/src/workerd/index.ts +17 -0
  63. package/dist/Browser-H63wiekr.mjs.map +0 -1
  64. package/dist/Page-CfB6Zn1p.mjs +0 -610
  65. package/dist/Plan-C-d5QYS8.mjs.map +0 -1
  66. package/dist/context-w3NJPRLo.mjs.map +0 -1
  67. package/dist/match.inpage-xOe9LNO0.mjs +0 -48
  68. package/dist/match.inpage-xOe9LNO0.mjs.map +0 -1
  69. package/dist/page-BKwfF6z-.mjs +0 -5099
  70. package/dist/page-BKwfF6z-.mjs.map +0 -1
package/README.md CHANGED
@@ -41,6 +41,93 @@ with it, since its pipe closes, but leaves Playwright's temporary profile behind
41
41
  | `Plan` | A walk recorded from a page's events, replayed on a fresh page by subject |
42
42
  | `Supervisor` | A browser kept open across losses and session ends, as generations |
43
43
 
44
+ ## Runtime CDP connectors
45
+
46
+ `Cdp.open` and `Cdp.layer` accept `connect: Cdp.Connector`, a callback receiving the endpoint and
47
+ `{ timeout, headers, signal }` and returning a real Playwright CDP `Browser`. Node uses
48
+ `playwright-core`'s `chromium.connectOverCDP` by default. A consumer can supply a connector using
49
+ its runtime's WebSocket implementation to avoid Playwright's Node WebSocket transport.
50
+
51
+ ```ts
52
+ import { Redacted } from "effect";
53
+ import * as Cdp from "effect-browser/Cdp";
54
+
55
+ declare const connect: Cdp.Connector; // The consumer's runtime-compatible Playwright connector.
56
+
57
+ const Remote = Cdp.layer({ endpoint: Redacted.make("wss://browser.example/cdp"), connect });
58
+ ```
59
+
60
+ For Bun, this connector uses the global `WebSocket` and Playwright 1.63's public transport
61
+ argument. It accepts `ws:` and `wss:` endpoints. Standard `WebSocket` cannot add custom headers,
62
+ so this example refuses them; a consumer needing headers supplies a runtime-specific connector.
63
+ The same example is exercised against local Chromium under Node and Bun, without a hosted session.
64
+
65
+ ```ts
66
+ import * as Cdp from "effect-browser/Cdp";
67
+ import { chromium, type ConnectOverCDPTransport } from "playwright-core";
68
+
69
+ const nativeConnect: Cdp.Connector = async (endpoint, { timeout, headers, signal }) => {
70
+ if (headers !== undefined)
71
+ throw new Error("This native connector does not support custom headers");
72
+ if (signal.aborted) throw new Error("CDP opening canceled");
73
+ const socket = new WebSocket(endpoint);
74
+
75
+ const transport: ConnectOverCDPTransport = {
76
+ send: (message) => socket.send(JSON.stringify(message)),
77
+ close: () => socket.close(),
78
+ };
79
+
80
+ const abort = () => socket.close();
81
+
82
+ signal.addEventListener("abort", abort, { once: true });
83
+ socket.addEventListener("message", (event) => {
84
+ transport.onmessage?.(JSON.parse(String(event.data)) as object);
85
+ });
86
+ socket.addEventListener("close", () => {
87
+ signal.removeEventListener("abort", abort);
88
+ transport.onclose?.();
89
+ });
90
+
91
+ try {
92
+ await new Promise<void>((resolve, reject) => {
93
+ socket.addEventListener("open", () => resolve(), { once: true });
94
+ socket.addEventListener("error", () => reject(new Error("Native CDP WebSocket failed")), {
95
+ once: true,
96
+ });
97
+ socket.addEventListener("close", () => reject(new Error("Native CDP WebSocket closed")), {
98
+ once: true,
99
+ });
100
+ });
101
+
102
+ return await chromium.connectOverCDP(transport, { timeout });
103
+ } catch (cause) {
104
+ signal.removeEventListener("abort", abort);
105
+ socket.close();
106
+
107
+ throw cause;
108
+ }
109
+ };
110
+ ```
111
+
112
+ Pass `nativeConnect` as `connect` to `Cdp.open` or `Cdp.layer`, or to the Browserbase options.
113
+
114
+ The `workerd` export condition selects a CDP entry without the Node transport. It requires
115
+ `connect`; omitting it fails with `BrowserError` reason `InvalidRequest`. The Workers root entry
116
+ excludes local `Chromium`; import `effect-browser/Cdp` for remote connections. Bundlers must select
117
+ the `workerd` condition, and Workers must enable `nodejs_compat`. The consumer still supplies and qualifies the runtime's Playwright
118
+ implementation against this package's Playwright peer; the seam does not certify a different
119
+ Playwright version or provide a Cloudflare connector.
120
+
121
+ The callback receives a finite positive `connectTimeoutMillis`, 30 seconds by default, and an
122
+ `AbortSignal`. Opening enforces that deadline independently, fails with reason `Timeout` and aborts
123
+ the signal when it expires or the caller is interrupted. A connector should stop its own pending
124
+ work when aborted. A browser it returns late is disconnected, as is one whose opening fails or
125
+ whose scope closes. Its `close()` must disconnect the CDP connection without ending the remote
126
+ browser; do not return a locally launched browser. Endpoints and headers can contain credentials:
127
+ keep them out of logs and model inputs. Connect failures retain typed reasons and redact the
128
+ endpoint's userinfo, path and query. A failure diagnostic that echoes a nonempty supplied header
129
+ value is withheld entirely, including in serialized errors.
130
+
44
131
  A snapshot outlines the viewport, or the whole page, with a ref such as `e12` on each control, and
45
132
  lists those controls as values, `Snapshot.Control`: each one's ref, kind, name and what it holds,
46
133
  a select's options, and whether it is disabled, checked or takes typed text. `within` reads only
@@ -347,9 +434,8 @@ text and the facts as trusted, and the page's text only as evidence. It never se
347
434
  words. A judgement only adds to structure: a `secret` fact counts whatever the judge reads. A judge
348
435
  that fails or exceeds `timeout` (30 seconds) leaves the input unjudged. `make` then denies the input
349
436
  if it has any fact, with the judge's failure as the `PolicyDenied` cause, and lets an input without
350
- facts, such as a same-origin link, go ahead. Through `@effect/ai-openrouter` 4.0.0, structured output needs
351
- `strictJsonSchema: true` in the model's config, or OpenRouter drops the response format and every
352
- review fails to decode.
437
+ facts, such as a same-origin link, go ahead. With `@effect/ai-openrouter`, set
438
+ `strictJsonSchema: true` in the model's config for structured judge outputs.
353
439
 
354
440
  Holds use `policyTimeout`, a finite positive duration defaulting to five minutes, separately from
355
441
  the action timeout. They do not keep the page locked. After approval, the library verifies the same
@@ -931,6 +1017,11 @@ generations and, with `keep`, can adopt one it previously kept.
931
1017
  Every module is also an entry point, such as `effect-browser/Page`. The
932
1018
  [repository README](https://github.com/mannyc2/effect-agent-browserbase#readme) has examples.
933
1019
 
1020
+ The published build embeds the complete page script as a closed JavaScript source string. Consumer
1021
+ bundlers can preserve function names or minify the library without changing that script's dependencies.
1022
+ The `@effect-browser/source` export condition is for this workspace's development tools; consumer
1023
+ bundles should use the default built exports, which also supply the Workers-specific packaging.
1024
+
934
1025
  ## Tracing
935
1026
 
936
1027
  Operations are spans in Effect's tracer, so an application that installs an exporter sees them in
@@ -0,0 +1,129 @@
1
+ import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
+ import { a as BrowserEvent_d_exports, c as DisconnectCause, i as BrowserEvent, v as RecordedEvent } from "./BrowserEvent-dq6FRT8w.mjs";
3
+ import { c as InputGuard, d as Page_d_exports, u as Page$1 } from "./Page-ku6j5t-v.mjs";
4
+ import { Context, DateTime, Duration, Effect, Option, Schema, Scope, Stream } from "effect";
5
+ import { BrowserContext } from "playwright-core";
6
+ declare namespace Browser_d_exports {
7
+ export { Browser$1 as Browser, CaptureSource, EventOptions, InitScript, Options, Service, SuspendOptions, Suspension, make };
8
+ }
9
+ /** A script that documents run before their own, such as a consent-banner remover. */
10
+ interface InitScript {
11
+ /**
12
+ * The documents that run it, by a pattern their address (`location.href`) matches; every
13
+ * document and frame when left out. Under a `match`, the source runs as a block, so its own
14
+ * top-level `let`, `const` and classes stay its own.
15
+ */
16
+ readonly match?: RegExp | undefined;
17
+ readonly source: string;
18
+ }
19
+ interface Options {
20
+ /**
21
+ * Bound on each action; finite and positive. Defaults to 10 seconds. What a presenter's view
22
+ * takes to show an action, such as its glides and typing at a person's pace, does not count
23
+ * against it.
24
+ */
25
+ readonly actionTimeout?: Duration.Input | undefined;
26
+ /** Bound on each navigation; finite and positive. Defaults to 30 seconds. */
27
+ readonly navigationTimeout?: Duration.Input | undefined;
28
+ /**
29
+ * How long each page keeps screencast frames for `recentFrames`, measured back from the newest;
30
+ * finite and positive. Defaults to 5 seconds, a moment's default window.
31
+ */
32
+ readonly frameHistory?: Duration.Input | undefined;
33
+ /** Events retained for replay and `recentEvents`. A positive safe integer, defaulting to 4,096. */
34
+ readonly eventHistory?: number | undefined;
35
+ /** Allow, deny or hold each input or navigation before it reaches the page. Defaults to allow. */
36
+ readonly guard?: InputGuard | undefined;
37
+ /** A separate bound for policy holds, outside action timeouts. Defaults to 5 minutes. */
38
+ readonly policyTimeout?: Duration.Input | undefined;
39
+ /** Scripts that new documents run before their own, each where its `match` allows. */
40
+ readonly initScripts?: ReadonlyArray<InitScript> | undefined;
41
+ /**
42
+ * The most pages the browser keeps open, those a site opens included, which are never refused. At
43
+ * it, `newPage` waits for one to close, within the action timeout, then fails `Limit`; under
44
+ * `Page.failFast` it fails at once. A positive safe integer; no limit when left out.
45
+ */
46
+ readonly maxPages?: number | undefined;
47
+ }
48
+ interface EventOptions {
49
+ /** Resume after this sequence; 0 replays from the beginning if it is still retained. */
50
+ readonly after?: number | undefined;
51
+ }
52
+ 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
+ declare class Suspension extends Suspension_base {}
62
+ /**
63
+ * A connection to the browser apart from the one that drives its pages, for their screencasts,
64
+ * as a provider that reaches the browser over a network supplies it: a frame and its
65
+ * acknowledgement then never wait behind a large message, such as an upload or a read's answer,
66
+ * on the connection that drives the page. It is read-only. A page's session on it sends the
67
+ * capture's commands alone, and the page's own session keeps its focus emulation, which keeps a
68
+ * tab behind painting.
69
+ */
70
+ interface CaptureSource {
71
+ /**
72
+ * A session on the page's target, for the scope. `event` gets the session's events in the order
73
+ * the browser sent them, its target's detach among them, until the scope closes; `lost` gets
74
+ * the connection's failure, once, if it fails first. Its calls never throw: a failure rejects.
75
+ */
76
+ readonly attach: (target: string, listener: {
77
+ readonly event: (method: string, params: unknown) => void;
78
+ readonly lost: (error: BrowserError) => void;
79
+ }) => Effect.Effect<(method: "Page.enable" | "Page.getFrameTree" | "Page.startScreencast" | "Page.screencastFrameAck" | "Page.stopScreencast", params?: Record<string, unknown>) => Promise<unknown>, BrowserError, Scope.Scope>;
80
+ }
81
+ interface Service {
82
+ /** The provider's session id, or a local id. */
83
+ readonly id: string;
84
+ /** Fresh for this connection, even when reattaching the same provider session and page. */
85
+ readonly connection: string;
86
+ readonly provider: string;
87
+ /** When the provider ends the session on its own, as a hosted session's timeout does. */
88
+ readonly expiresAt: DateTime.Utc | undefined;
89
+ /** Host monotonic milliseconds on the clock shared by this browser’s events and frames. */
90
+ readonly now: Effect.Effect<number>;
91
+ /** Drain admitted input and withhold new input for this scope; reads and capture continue. */
92
+ readonly suspend: (options?: SuspendOptions) => Effect.Effect<Suspension, BrowserError, Scope.Scope>;
93
+ /** The Playwright context, for anything this API does not cover. Never give it to a model. */
94
+ readonly context: BrowserContext;
95
+ /** Open pages in the order this browser began tracking them. */
96
+ readonly pages: Effect.Effect<ReadonlyArray<Page$1>>;
97
+ /** The open page with this id, its CDP target id, as stored before a reconnect. */
98
+ readonly page: (id: string) => Effect.Effect<Option.Option<Page$1>>;
99
+ /** The first open page, opening one when there is none. */
100
+ readonly firstPage: Effect.Effect<Page$1, BrowserError>;
101
+ readonly newPage: (url?: string) => Effect.Effect<Page$1, BrowserError>;
102
+ /**
103
+ * Completes once the browser is lost to its owner, with why: its connection dropped, its
104
+ * provider ended its session, or its owner's scope released it.
105
+ */
106
+ readonly disconnected: Effect.Effect<DisconnectCause>;
107
+ /** Live events, or replay after a cursor. A reader that falls behind fails with EventHistoryExpired. */
108
+ readonly events: (options?: EventOptions) => Stream.Stream<RecordedEvent, BrowserError>;
109
+ /** The latest `eventHistory` events, oldest first. */
110
+ readonly recentEvents: Effect.Effect<ReadonlyArray<BrowserEvent>>;
111
+ }
112
+ declare const Browser_base: Context.ServiceClass<Browser$1, "effect-browser/Browser", Service>;
113
+ declare class Browser$1 extends Browser_base {}
114
+ /**
115
+ * Build the service over a context the provider owns. The provider closes the context; this only
116
+ * tracks its pages and events, for as long as the surrounding scope is open. Each page's protocol
117
+ * session, capture and listeners end when that page closes or when the scope does, so a closed
118
+ * tab retains nothing and the context keeps none of them afterwards.
119
+ */
120
+ declare const make: (context: BrowserContext, info: {
121
+ readonly id: string;
122
+ readonly provider: string;
123
+ readonly expiresAt?: DateTime.Utc | undefined;
124
+ /** Where pages' screencasts run, if not on each page's own session. */
125
+ readonly capture?: CaptureSource | undefined;
126
+ }, options?: Options | undefined) => Effect.Effect<Service, BrowserError, Scope.Scope>;
127
+ //#endregion
128
+ export { InitScript as a, SuspendOptions as c, EventOptions as i, Suspension as l, Browser_d_exports as n, Options as o, CaptureSource as r, Service as s, Browser$1 as t, make as u };
129
+ //# sourceMappingURL=Browser-CiXHT1yO.d.mts.map
@@ -1,10 +1,10 @@
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
3
  import { BrowserError, Busy, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit, Suspended } from "./BrowserError.mjs";
4
- import { c as undispatched, d as monotonicMillis, i as failWith, s as refuse, t as call, u as mapping } from "./context-w3NJPRLo.mjs";
5
- import { c as FailFast, i as redact } from "./url-Cd04Okid.mjs";
6
- import { n as make$5 } from "./page-BKwfF6z-.mjs";
7
- import { Cause, Clock, Context, Crypto, DateTime, Deferred, Duration, Effect, Exit, Option, PlatformError, Queue, Schema, Scope, Semaphore, Stream } from "effect";
4
+ import { c as undispatched, d as monotonicMillis, i as failWith, s as refuse, t as call, u as mapping } from "./context-EGhOqjNN.mjs";
5
+ import { r as redact, s as FailFast } from "./url-Bsp2y55M.mjs";
6
+ import { n as make$5 } from "./page-PTM8m5wC.mjs";
7
+ import { Cause, Clock, Context, Crypto, DateTime, Deferred, Duration, Effect, Exit, Option, Queue, Schema, Scope, Semaphore, Stream } from "effect";
8
8
  //#region src/internal/input/gate.ts
9
9
  /**
10
10
  * A browser's input authority: stop admitting writes, drain admitted operations through their
@@ -153,22 +153,13 @@ const make$4 = (options) => {
153
153
  //#endregion
154
154
  //#region src/internal/page/identity.ts
155
155
  /** Connection identity uses the supplied Crypto service, or the host's Web Crypto on Node/Bun. */
156
- const native = () => Crypto.make({
157
- randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)),
158
- digest: (algorithm, data) => Effect.tryPromise({
159
- try: () => globalThis.crypto.subtle.digest(algorithm, new Uint8Array(data)),
160
- catch: (cause) => PlatformError.systemError({
161
- module: "Crypto",
162
- method: "digest",
163
- _tag: "Unknown",
164
- description: "Could not compute digest",
165
- cause
166
- })
167
- }).pipe(Effect.map((bytes) => new Uint8Array(bytes)))
168
- });
156
+ const failed = () => undispatched("make", new Failed({ detail: "Could not name connection" }));
169
157
  const make$3 = Effect.gen(function* () {
170
158
  const supplied = yield* Effect.serviceOption(Crypto.Crypto);
171
- return yield* Option.getOrElse(supplied, native).randomUUIDv4.pipe(Effect.mapError(() => undispatched("make", new Failed({ detail: "Could not name connection" }))));
159
+ return yield* Option.isSome(supplied) ? supplied.value.randomUUIDv4().pipe(Effect.mapError(failed)) : Effect.try({
160
+ try: () => globalThis.crypto.randomUUID(),
161
+ catch: failed
162
+ });
172
163
  });
173
164
  //#endregion
174
165
  //#region src/internal/page/openings.ts
@@ -625,4 +616,4 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
625
616
  //#endregion
626
617
  export { make as i, Browser_exports as n, Suspension as r, Browser as t };
627
618
 
628
- //# sourceMappingURL=Browser-H63wiekr.mjs.map
619
+ //# sourceMappingURL=Browser-CxXUjxOp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Browser-CxXUjxOp.mjs","names":["make","make","make","make","Events.DialogShown","Url.redact","Events.PageUntracked","Events.PageClosed","Identity.make","BrowserClock.monotonicMillis","mapping","BrowserClock.mapping","Timeline.make","Events.SessionEnding","Events.Disconnected","Gate.make","PageImpl.make","Openings.make","Events.PageOpened"],"sources":["../src/internal/input/gate.ts","../src/internal/page/identity.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","/** Connection identity uses the supplied Crypto service, or the host's Web Crypto on Node/Bun. */\nimport { Crypto, Effect, Option } from \"effect\";\n\nimport { Failed } from \"../../BrowserError.ts\";\nimport { undispatched } from \"./context.ts\";\n\nconst failed = () => undispatched(\"make\", new Failed({ detail: \"Could not name connection\" }));\n\nexport const make = Effect.gen(function* () {\n const supplied = yield* Effect.serviceOption(Crypto.Crypto);\n\n return yield* Option.isSome(supplied)\n ? supplied.value.randomUUIDv4().pipe(Effect.mapError(failed))\n : // oxlint-disable-next-line no-restricted-properties -- this host fallback runs only when the captured Effect Crypto service is absent\n Effect.try({ try: () => globalThis.crypto.randomUUID(), catch: failed });\n});\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 } from \"../../BrowserError.ts\";\nimport { type BrowserEvent, RecordedEvent } from \"../../BrowserEvent.ts\";\nimport { refuse, 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.fromEffect(\n refuse(\n \"events\",\n \"the event cursor must be a nonnegative integer no greater than the latest sequence\",\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 * as Events from \"./BrowserEvent.ts\";\nimport * as Gate from \"./internal/input/gate.ts\";\nimport { call, failWith, type Settings, undispatched } from \"./internal/page/context.ts\";\nimport * as Identity from \"./internal/page/identity.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 /** Fresh for this connection, even when reattaching the same provider session and page. */\n readonly connection: 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<Events.DisconnectCause>;\n /** Live events, or replay after a cursor. A reader that falls behind fails with EventHistoryExpired. */\n readonly events: (options?: EventOptions) => Stream.Stream<Events.RecordedEvent, BrowserError>;\n /** The latest `eventHistory` events, oldest first. */\n readonly recentEvents: Effect.Effect<ReadonlyArray<Events.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: Events.DisconnectCause) => void,\n) {\n let lostBy: Events.DisconnectCause | undefined;\n const lost = yield* Deferred.make<Events.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: Events.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: Events.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 Events.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: Events.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 Events.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<Events.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: () => Events.DisconnectCause | undefined,\n publish: (event: Events.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: Events.PageClosed[\"cause\"];\n }>();\n\n yield* Queue.take(closing).pipe(\n Effect.tap(({ page, cause }) =>\n Effect.sync(() => {\n if (lostBy() === undefined) publish(new Events.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 connection = yield* Identity.make;\n const now = () => BrowserClock.monotonicMillis(clock);\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 Events.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 Events.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 connection,\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 Events.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 connection,\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,129 +1,2 @@
1
- import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
- import { c as DisconnectCause, i as BrowserEvent, v as RecordedEvent } from "./BrowserEvent-dq6FRT8w.mjs";
3
- import { c as InputGuard, d as Page_d_exports, u as Page$1 } from "./Page-ku6j5t-v.mjs";
4
- import { Context, DateTime, Duration, Effect, Option, Schema, Scope, Stream } from "effect";
5
- import { BrowserContext } from "playwright-core";
6
- declare namespace Browser_d_exports {
7
- export { Browser, CaptureSource, EventOptions, InitScript, Options, Service, SuspendOptions, Suspension, make };
8
- }
9
- /** A script that documents run before their own, such as a consent-banner remover. */
10
- export interface InitScript {
11
- /**
12
- * The documents that run it, by a pattern their address (`location.href`) matches; every
13
- * document and frame when left out. Under a `match`, the source runs as a block, so its own
14
- * top-level `let`, `const` and classes stay its own.
15
- */
16
- readonly match?: RegExp | undefined;
17
- readonly source: string;
18
- }
19
- export interface Options {
20
- /**
21
- * Bound on each action; finite and positive. Defaults to 10 seconds. What a presenter's view
22
- * takes to show an action, such as its glides and typing at a person's pace, does not count
23
- * against it.
24
- */
25
- readonly actionTimeout?: Duration.Input | undefined;
26
- /** Bound on each navigation; finite and positive. Defaults to 30 seconds. */
27
- readonly navigationTimeout?: Duration.Input | undefined;
28
- /**
29
- * How long each page keeps screencast frames for `recentFrames`, measured back from the newest;
30
- * finite and positive. Defaults to 5 seconds, a moment's default window.
31
- */
32
- readonly frameHistory?: Duration.Input | undefined;
33
- /** Events retained for replay and `recentEvents`. A positive safe integer, defaulting to 4,096. */
34
- readonly eventHistory?: number | undefined;
35
- /** Allow, deny or hold each input or navigation before it reaches the page. Defaults to allow. */
36
- readonly guard?: InputGuard | undefined;
37
- /** A separate bound for policy holds, outside action timeouts. Defaults to 5 minutes. */
38
- readonly policyTimeout?: Duration.Input | undefined;
39
- /** Scripts that new documents run before their own, each where its `match` allows. */
40
- readonly initScripts?: ReadonlyArray<InitScript> | undefined;
41
- /**
42
- * The most pages the browser keeps open, those a site opens included, which are never refused. At
43
- * it, `newPage` waits for one to close, within the action timeout, then fails `Limit`; under
44
- * `Page.failFast` it fails at once. A positive safe integer; no limit when left out.
45
- */
46
- readonly maxPages?: number | undefined;
47
- }
48
- export interface EventOptions {
49
- /** Resume after this sequence; 0 replays from the beginning if it is still retained. */
50
- readonly after?: number | undefined;
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 {}
62
- /**
63
- * A connection to the browser apart from the one that drives its pages, for their screencasts,
64
- * as a provider that reaches the browser over a network supplies it: a frame and its
65
- * acknowledgement then never wait behind a large message, such as an upload or a read's answer,
66
- * on the connection that drives the page. It is read-only. A page's session on it sends the
67
- * capture's commands alone, and the page's own session keeps its focus emulation, which keeps a
68
- * tab behind painting.
69
- */
70
- export interface CaptureSource {
71
- /**
72
- * A session on the page's target, for the scope. `event` gets the session's events in the order
73
- * the browser sent them, its target's detach among them, until the scope closes; `lost` gets
74
- * the connection's failure, once, if it fails first. Its calls never throw: a failure rejects.
75
- */
76
- readonly attach: (target: string, listener: {
77
- readonly event: (method: string, params: unknown) => void;
78
- readonly lost: (error: BrowserError) => void;
79
- }) => Effect.Effect<(method: "Page.enable" | "Page.getFrameTree" | "Page.startScreencast" | "Page.screencastFrameAck" | "Page.stopScreencast", params?: Record<string, unknown>) => Promise<unknown>, BrowserError, Scope.Scope>;
80
- }
81
- export interface Service {
82
- /** The provider's session id, or a local id. */
83
- readonly id: string;
84
- /** Fresh for this connection, even when reattaching the same provider session and page. */
85
- readonly connection: string;
86
- readonly provider: string;
87
- /** When the provider ends the session on its own, as a hosted session's timeout does. */
88
- readonly expiresAt: DateTime.Utc | undefined;
89
- /** Host monotonic milliseconds on the clock shared by this browser’s events and frames. */
90
- readonly now: Effect.Effect<number>;
91
- /** Drain admitted input and withhold new input for this scope; reads and capture continue. */
92
- readonly suspend: (options?: SuspendOptions) => Effect.Effect<Suspension, BrowserError, Scope.Scope>;
93
- /** The Playwright context, for anything this API does not cover. Never give it to a model. */
94
- readonly context: BrowserContext;
95
- /** Open pages in the order this browser began tracking them. */
96
- readonly pages: Effect.Effect<ReadonlyArray<Page$1>>;
97
- /** The open page with this id, its CDP target id, as stored before a reconnect. */
98
- readonly page: (id: string) => Effect.Effect<Option.Option<Page$1>>;
99
- /** The first open page, opening one when there is none. */
100
- readonly firstPage: Effect.Effect<Page$1, BrowserError>;
101
- readonly newPage: (url?: string) => Effect.Effect<Page$1, BrowserError>;
102
- /**
103
- * Completes once the browser is lost to its owner, with why: its connection dropped, its
104
- * provider ended its session, or its owner's scope released it.
105
- */
106
- readonly disconnected: Effect.Effect<DisconnectCause>;
107
- /** Live events, or replay after a cursor. A reader that falls behind fails with EventHistoryExpired. */
108
- readonly events: (options?: EventOptions) => Stream.Stream<RecordedEvent, BrowserError>;
109
- /** The latest `eventHistory` events, oldest first. */
110
- readonly recentEvents: Effect.Effect<ReadonlyArray<BrowserEvent>>;
111
- }
112
- declare const Browser_base: Context.ServiceClass<Browser, "effect-browser/Browser", Service>;
113
- export declare class Browser extends Browser_base {}
114
- /**
115
- * Build the service over a context the provider owns. The provider closes the context; this only
116
- * tracks its pages and events, for as long as the surrounding scope is open. Each page's protocol
117
- * session, capture and listeners end when that page closes or when the scope does, so a closed
118
- * tab retains nothing and the context keeps none of them afterwards.
119
- */
120
- export declare const make: (context: BrowserContext, info: {
121
- readonly id: string;
122
- readonly provider: string;
123
- readonly expiresAt?: DateTime.Utc | undefined;
124
- /** Where pages' screencasts run, if not on each page's own session. */
125
- readonly capture?: CaptureSource | undefined;
126
- }, options?: Options | undefined) => Effect.Effect<Service, BrowserError, Scope.Scope>;
127
- //#endregion
128
- export { Browser_d_exports as t };
129
- //# sourceMappingURL=Browser.d.mts.map
1
+ import { a as InitScript, c as SuspendOptions, i as EventOptions, l as Suspension, o as Options, r as CaptureSource, s as Service, t as Browser, u as make } from "./Browser-CiXHT1yO.mjs";
2
+ export { Browser, CaptureSource, EventOptions, InitScript, Options, Service, SuspendOptions, Suspension, make };
package/dist/Browser.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { i as make, r as Suspension, t as Browser } from "./Browser-H63wiekr.mjs";
1
+ import { i as make, r as Suspension, t as Browser } from "./Browser-CxXUjxOp.mjs";
2
2
  export { Browser, Suspension, make };
@@ -0,0 +1,10 @@
1
+ import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
+ import { s as Service, t as Browser } from "./Browser-CiXHT1yO.mjs";
3
+ import { n as Connector, r as Options, t as ConnectOptions } from "./cdp-DL7XfEj5.mjs";
4
+ declare namespace Cdp_d_exports {
5
+ export { ConnectOptions, Connector, Options, layer, open };
6
+ }
7
+ declare const open: (options: Options) => import("effect/Effect").Effect<Service, BrowserError, import("effect/Scope").Scope>, layer: (options: Options) => import("effect/Layer").Layer<Browser, BrowserError>;
8
+ //#endregion
9
+ export { layer as n, open as r, Cdp_d_exports as t };
10
+ //# sourceMappingURL=Cdp-CzCRnMlM.d.mts.map
package/dist/Cdp.d.mts CHANGED
@@ -1,32 +1,11 @@
1
1
  import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
- import { Browser, CaptureSource, Options as Options$1, Service, t as Browser_d_exports } from "./Browser.mjs";
3
- import { DateTime, Effect, Layer, Redacted } from "effect";
2
+ import { s as Service, t as Browser } from "./Browser-CiXHT1yO.mjs";
3
+ import { n as Connector, r as Options, t as ConnectOptions } from "./cdp-DL7XfEj5.mjs";
4
4
  declare namespace Cdp_d_exports {
5
- export { Options, layer, open };
6
- }
7
- export interface Options extends Options$1 {
8
- /**
9
- * `ws://`, `wss://` or `http://` DevTools endpoint. Often a credential, so it may be redacted;
10
- * connect failures never include its userinfo, path or query.
11
- */
12
- readonly endpoint: string | Redacted.Redacted<string>;
13
- readonly headers?: Readonly<Record<string, string>> | undefined;
14
- /** Identifies the session in `Browser.id`. Defaults to `"cdp"`. */
15
- readonly id?: string | undefined;
16
- readonly provider?: string | undefined;
17
- /** Bound on connecting. Defaults to 30 seconds. */
18
- readonly connectTimeoutMillis?: number | undefined;
19
- /** When the provider ends the session on its own, as a hosted session's timeout does. */
20
- readonly expiresAt?: DateTime.Utc | undefined;
21
- /**
22
- * A connection apart from this one for the pages' screencasts, as a hosted provider supplies.
23
- * Without one, each page's capture runs on its own session.
24
- */
25
- readonly capture?: CaptureSource | undefined;
5
+ export { ConnectOptions, Connector, Options, layer, open };
26
6
  }
27
7
  /** Connect and open a `Browser` over the endpoint's first context. */
28
- export declare const open: (options: Options) => Effect.Effect<Service, BrowserError, import("effect/Scope").Scope>;
29
- export declare const layer: (options: Options) => Layer.Layer<Browser, BrowserError>;
8
+ export declare const open: (options: Options) => import("effect/Effect").Effect<Service, BrowserError, import("effect/Scope").Scope>, layer: (options: Options) => import("effect/Layer").Layer<Browser, BrowserError>;
30
9
  //#endregion
31
- export { Cdp_d_exports as t };
10
+ export { type ConnectOptions, type Connector, type Options, Cdp_d_exports as t };
32
11
  //# sourceMappingURL=Cdp.d.mts.map