effect-browser 0.3.0-beta.13 → 0.3.0-beta.14

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 (52) hide show
  1. package/README.md +50 -19
  2. package/dist/{Browser-QiT6Cdn_.mjs → Browser-BBSySyjT.mjs} +2 -2
  3. package/dist/{Browser-QiT6Cdn_.mjs.map → Browser-BBSySyjT.mjs.map} +1 -1
  4. package/dist/Browser.d.mts +1 -1
  5. package/dist/Browser.mjs +1 -1
  6. package/dist/Cdp.mjs +1 -1
  7. package/dist/Chromium.mjs +1 -1
  8. package/dist/Expect.d.mts +1 -1
  9. package/dist/Expect.mjs +1 -1
  10. package/dist/{Frame-Di2PxyF7.d.mts → Frame-DkNdjjjr.d.mts} +3 -5
  11. package/dist/Frame.d.mts +1 -1
  12. package/dist/Frame.mjs +2 -4
  13. package/dist/Frame.mjs.map +1 -1
  14. package/dist/Moment.d.mts +1 -1
  15. package/dist/Moment.mjs +7 -3
  16. package/dist/Moment.mjs.map +1 -1
  17. package/dist/{Page-B7X6DQUn.d.mts → Page-DL9eylp3.d.mts} +2 -2
  18. package/dist/Page.d.mts +1 -1
  19. package/dist/{Plan-BS0jpENY.d.mts → Plan-6As6vcwT.d.mts} +3 -3
  20. package/dist/{Plan-Bl2NM1D5.mjs → Plan-CXA2BW-G.mjs} +4 -6
  21. package/dist/Plan-CXA2BW-G.mjs.map +1 -0
  22. package/dist/Plan.d.mts +1 -1
  23. package/dist/Plan.mjs +1 -1
  24. package/dist/Policy.d.mts +1 -1
  25. package/dist/Presentation.d.mts +1 -1
  26. package/dist/Presentation.mjs +1 -1
  27. package/dist/Stage.d.mts +5 -4
  28. package/dist/Stage.mjs +53 -20
  29. package/dist/Stage.mjs.map +1 -1
  30. package/dist/{Supervisor-TNKNOVzu.mjs → Supervisor-BE2sGHBK.mjs} +67 -41
  31. package/dist/Supervisor-BE2sGHBK.mjs.map +1 -0
  32. package/dist/{Supervisor-CYWFK3vM.d.mts → Supervisor-W60pZWHh.d.mts} +24 -8
  33. package/dist/Supervisor.d.mts +2 -2
  34. package/dist/Supervisor.mjs +1 -1
  35. package/dist/index.d.mts +4 -4
  36. package/dist/index.mjs +3 -3
  37. package/dist/{page-ZxMhw16l.mjs → page-B92QnRfT.mjs} +196 -166
  38. package/dist/page-B92QnRfT.mjs.map +1 -0
  39. package/package.json +1 -1
  40. package/src/Frame.ts +2 -4
  41. package/src/Moment.ts +14 -3
  42. package/src/Plan.ts +7 -6
  43. package/src/Stage.ts +61 -21
  44. package/src/Supervisor.ts +69 -37
  45. package/src/internal/pictures/capture.ts +172 -188
  46. package/src/internal/pictures/pictures.ts +50 -73
  47. package/src/internal/pictures/quiescence.ts +94 -0
  48. package/src/internal/reading/ready.ts +9 -3
  49. package/src/internal/supervisor/lifecycle.ts +27 -12
  50. package/dist/Plan-Bl2NM1D5.mjs.map +0 -1
  51. package/dist/Supervisor-TNKNOVzu.mjs.map +0 -1
  52. package/dist/page-ZxMhw16l.mjs.map +0 -1
package/README.md CHANGED
@@ -193,7 +193,9 @@ also holds through a stalled connection.
193
193
 
194
194
  `Moment.capture` is a window that ends at a picture of the page now. It takes the picture first,
195
195
  then the window up to its paint, from where a previous window or moment ended, so the changes hold
196
- nothing the picture does not show. A picture, a read of changes or an outline that cannot be had is
196
+ nothing the picture does not show. A cached picture from before the latest action finished is
197
+ replaced with a fresh screenshot, keeping a navigation's triggering action in the moment.
198
+ A picture, a read of changes or an outline that cannot be had is
197
199
  missing from the moment, with why, and the moment is still made. `Moment.toPrompt` leads with what
198
200
  changed: news and what settled first, then what was still changing, by the moment's account, with
199
201
  the cells of a column that changed together on one line; a change that kept changing says whether
@@ -214,8 +216,8 @@ the options they were given, its navigations with the address asked for and the
214
216
  what was typed as an input slot named for its field, never the text itself. A navigation to an
215
217
  address with a credential withheld is an input slot too, `address`, so replay goes where the caller
216
218
  says, never to the address without its credential. `Plan.replay(page,
217
- plan, { inputs })` takes the steps in turn: before each that acts, it waits for `Page.ready`
218
- (`settle` sets how, or `false` not to wait), finds the one element the step's subject names with
219
+ plan, { inputs })` takes the steps in turn: before each step except an opening navigate, back or
220
+ reload, it waits for `Page.ready` (`settle` sets how, or `false` not to wait), finds the element with
219
221
  `Plan.locate`, and acts on it. An element that does not repeat every part of the recorded context
220
222
  is another subject: its row read whole, so a row named "Wrapped BTC" is not the row "BTC" (laid out
221
223
  again without rows, as cards, one of its fields must start with the row's words), its column where
@@ -238,7 +240,9 @@ Each `Action` now records its ending address, so a bounded event history can sti
238
240
  of later actions after its earlier navigation has expired.
239
241
 
240
242
  `Plan.take(page, plan, index)` takes one step and returns a `Receipt`: the step index, host-clock
241
- `startedAt` and `at`, and `taken`, `verified` or `skipped`. `Plan.replay` returns those receipts;
243
+ `startedAt` and `at`, and `taken`, `verified` or `skipped`. Readiness is a precondition of the next
244
+ step, including later navigation, never a wait after the last step. Call `page.ready` after replay
245
+ when the final page must be still. `Plan.replay` returns those receipts;
242
246
  a `ReplayError` includes completed receipts and a serializable `stopped` value with the next step
243
247
  and whether its input is `unsent` or `unknown`. Resume with `{ from: error.stopped }`. A numeric
244
248
  index means fresh input, so never convert an unresolved stopping point into a number to retry it.
@@ -483,6 +487,10 @@ The consumer can stop performance between `Plan.take` steps for a clean cut. Int
483
487
  the switch changes nothing and leaves the cue usable. A committed switch keeps ownership through
484
488
  outgoing capture cleanup, even if its caller is interrupted then.
485
489
 
490
+ Releasing an uncommitted cue ends its capture. Its later `ready` or `commit` fails with
491
+ `InvalidRequest`, whose consequence is `{ lost: "nothing", repeat: "pointless" }`; the page and
492
+ browser remain usable. A capture that completes without a failure gives the same outcome.
493
+
486
494
  `Presented.at` records when the switch took effect, no earlier than the event, the incoming frame
487
495
  or the last frame shown; `latency` is its delay after the requested `at`. `firstFrame` bounds a
488
496
  wait for current paint (5 seconds by default). Old-page input admission, drain and the final frame
@@ -494,18 +502,22 @@ use that capture's current frame without waiting for invented new paint.
494
502
 
495
503
  `stage.shown({ hold: "1 second" })` describes the output without inventing new frames:
496
504
 
497
- - `Painted` carries a frame when it is published to the output.
505
+ - `Painted` carries a frame and its output time `at`: the switch's `Presented.at` for its first
506
+ frame, and `frame.hostTime` for subsequent timely paint. A late frame's output time is held at
507
+ the preceding output time; its capture time stays unchanged.
498
508
  - `Still` follows a quiet interval only after a fresh page/capture round trip, with no pending
499
509
  capture acknowledgement, the same active capture and current document, and no newer frame.
500
510
  - `Stalled` says silence could not be confirmed. A timeout is uncertainty, not stillness.
501
511
  - `Gone` says the page or browser is lost. It is emitted once for that feed, with nothing more
502
512
  until another page is committed.
503
513
 
504
- `hold` must be finite and positive. Continuous paint restarts the silence interval. A failed
514
+ Every `shown` value carries `at`, which never decreases along a subscription, including across
515
+ silence and switches. `hold` must be finite and positive. Continuous paint restarts the silence interval. A failed
505
516
  capture can restart while its page/browser still stands, but old paint cannot pass for a new
506
517
  capture's first frame. These guarantees depend on Chromium reporting its paint; they do not
507
- prove that every canvas update was delivered. `frames` remains the paint-only stream with bounded
508
- reader queues, so subscribe before presenting to receive the first image. The consumer owns
518
+ prove that every canvas update was delivered. `frames` remains the capture-timed paint-only stream:
519
+ its `hostTime` can go backwards at a switch to a prepared cue. Use `shown`'s `at` to schedule output.
520
+ Both streams have bounded reader queues, so subscribe before presenting to receive the first image. The consumer owns
509
521
  encoding, redaction, slates and any repetition of a confirmed held image.
510
522
 
511
523
  Local Chromium verifies preparation, ownership, input barriers and evidence. Earlier hosted
@@ -595,7 +607,7 @@ timestamp field.
595
607
  minute: received and accepted frames, missing timestamps, `late` frames (Chromium encodes several at
596
608
  once and finished these after a newer one; they are dropped, never reordered, and a busy machine
597
609
  makes more of them), frames dropped for their size (`foreignSize`) or for coming during one of the
598
- library's own clipped pictures (`duringPictures`), frames a reader `lost` by falling behind, and
610
+ library's own pictures (`duringPictures`), frames a reader `lost` by falling behind, and
599
611
  paint-time gap totals/minimum/maximum/last. `ackBacklog` is the acknowledgements still unanswered:
600
612
  Chromium sends a frame only while few are, so a backlog over a slow connection means fewer frames.
601
613
  Chromium draws a clipped or scaled screenshot of the page, taken
@@ -605,15 +617,17 @@ returning drops them, while the page reporting a viewport of their size, or thei
605
617
  delivers them as a real resize, so a page that cannot answer, or that measures in other units as
606
618
  under browser zoom, is never stalled. `foreignSize` therefore also counts some of the page's own
607
619
  frames: those of a new size still unconfirmed when another size replaced it, when the capture
608
- stopped, or beyond the 16 held. The library's own clipped pictures are left out while they are
609
- taken: from the picture's call until 50 ms after its reply, by when a frame arrived or by its paint
610
- time widened by the clock's uncertainty either side. A crop on the page's own session, as over CDP
611
- and on Browserbase, is drawn at the crop's own size, so there only frames of another size are left
612
- out, and the page's own keep reaching readers. Where Playwright emulates the viewport, as
613
- `Chromium.layer` does, a crop keeps the device's size, so every frame in that window is left out,
614
- the page's own included: the capture pauses for the picture's round trip, plus 50 ms, plus twice
615
- the clock's uncertainty, about 100 ms a zoom locally, and a crop with the viewport's own
616
- proportions never reaches readers either. A clip someone else takes passes where Chromium keeps
620
+ stopped, or beyond the 16 held. Before taking any new library picture, including an uncropped
621
+ forced screenshot, the running native capture stops. It restarts with the same readers and settings
622
+ only after the actual screenshot promise and viewport restoration settle, and after reading the
623
+ current viewport. A frame received during that pause is acknowledged and discarded as
624
+ `duringPictures`; the pause can produce no callbacks, leaving the count at zero. Cached-frame reuse
625
+ still costs no call. A forced picture with capture running adds stop, viewport refresh and restart
626
+ calls to its usual cost. Caller cancellation retains the actual native replies: the next picture or
627
+ capture waits for them, cleanup is bounded to two seconds, and unconfirmed restoration fails the
628
+ capture rather than resuming it early. Readiness requires current post-resume frame evidence, so
629
+ paused capture cannot certify that a still-painting page is quiet.
630
+ A clip someone else takes passes where Chromium keeps
617
631
  the device's size during it and the clip has the viewport's proportions, and on a page that paints
618
632
  nothing after it, a clip of any shape lasts long enough to pass for a resize.
619
633
  Each page's own session holds focus emulation, so a tab behind another keeps painting and its
@@ -815,7 +829,7 @@ const program = Effect.gen(function* () {
815
829
  ```
816
830
 
817
831
  `use(work, { wait })` atomically borrows the serving generation and runs `work` in a scope of its
818
- own. The lease gives its `generation`, `browser`, `value`, an `ending` notice and `retain`.
832
+ own. The lease gives its `generation`, `browser`, `value`, an `ending` notice, `retain` and `lose`.
819
833
  `wait` defaults to `waitTimeout` (two minutes); zero either borrows immediately or fails
820
834
  `Unavailable`. Cancelling the caller closes its work scope and lets its hold go. Ordinary work
821
835
  errors stay ordinary work errors. `browser` still returns an unheld browser: use it only when the
@@ -860,8 +874,25 @@ before acquisition starts. Call `rotate` outside `use` for a clean cut: an exclu
860
874
  inside `use` waits for its own hold to end or be revoked.
861
875
 
862
876
  A loss publishes `Lost` with the browser's `disconnected` cause and revokes its holds immediately.
877
+ When the consumer detects an unusable page or capture while the browser stays connected,
878
+ `lease.lose(detail)` applies the same transition with `cause: "declared"` and that detail. It stops
879
+ serving the generation and revokes work without a handover wait, including work declaring its own
880
+ loss and returning immediately. Declaring loss does not wait for work cleanup or provider release.
881
+ Repeated declarations and declarations from draining or closed generations do nothing.
863
882
  It reopens on `reopen`, which also retries an open failure until the schedule ends. A provider's
864
883
  `definite` failure goes `Down` at once, with the cause also available through `Unavailable`.
884
+
885
+ `admit({ number, after })` runs before every open attempt, including the first and each retry,
886
+ inside the opening scope. It can await the last opened generation's release through `after`,
887
+ even after an earlier reopen failed, to enforce a policy such as stopping after `Unconfirmed`.
888
+ The first generation and a healthy nonexclusive rotation have no `after`: that rotation keeps
889
+ the outgoing generation serving until its replacement opens. After a loss, or an exclusive
890
+ rotation, `after` exposes the outgoing release. One policy can therefore await every available
891
+ `after` without delaying healthy nonexclusive rotations. This is checked again before each
892
+ retry, including when the serving generation was lost during a failed rotation. Admission
893
+ failures follow `reopen` and `definite`, and retirement interrupts admission. Ignoring `after`
894
+ adds no release wait.
895
+
865
896
  `retire` revokes immediately, stops acquisition and waits for every generation to release.
866
897
  Closing the supervisor's scope does the same unless `keep` is set: then only the serving
867
898
  generation skips provider release, `Kept`; its application scopes still close. Other generations
@@ -3,7 +3,7 @@ import { DialogShown, Disconnected, PageClosed, PageOpened, PageUntracked, Recor
3
3
  import { BrowserError, Busy, Closed, EventHistoryExpired, Failed, InvalidRequest, Limit, Suspended } from "./BrowserError.mjs";
4
4
  import { i as failWith, s as undispatched, t as call } from "./context-BAjRj7Xo.mjs";
5
5
  import { c as FailFast, i as redact } from "./url-Cd04Okid.mjs";
6
- import { n as make$5, o as mapping } from "./page-ZxMhw16l.mjs";
6
+ import { n as make$5, o as mapping } from "./page-B92QnRfT.mjs";
7
7
  import { Cause, Clock, Context, Crypto, DateTime, Deferred, Duration, Effect, Exit, Option, PlatformError, Queue, Schema, Scope, Semaphore, Stream } from "effect";
8
8
  //#region src/internal/input/gate.ts
9
9
  /**
@@ -625,4 +625,4 @@ const make = Effect.fn("Browser.make")(function* (context, info, options = {}) {
625
625
  //#endregion
626
626
  export { make as i, Browser_exports as n, Suspension as r, Browser as t };
627
627
 
628
- //# sourceMappingURL=Browser-QiT6Cdn_.mjs.map
628
+ //# sourceMappingURL=Browser-BBSySyjT.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Browser-QiT6Cdn_.mjs","names":["make","make","make","make","Url.redact","Identity.make","mapping","BrowserClock.mapping","Timeline.make","Gate.make","PageImpl.make","Openings.make"],"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, PlatformError } from \"effect\";\n\nimport { Failed } from \"../../BrowserError.ts\";\nimport { undispatched } from \"./context.ts\";\n\n// The core owns this tiny platform adapter to keep Chromium/CDP usable without another package\n// or a new required service. Browser construction captures it once; there is no global counter.\nconst native = () =>\n Crypto.make({\n randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)),\n digest: (algorithm, data) =>\n Effect.tryPromise({\n try: () => globalThis.crypto.subtle.digest(algorithm, new Uint8Array(data)),\n catch: (cause) =>\n PlatformError.systemError({\n module: \"Crypto\",\n method: \"digest\",\n _tag: \"Unknown\",\n description: \"Could not compute digest\",\n cause,\n }),\n }).pipe(Effect.map((bytes) => new Uint8Array(bytes))),\n });\n\nexport const make = Effect.gen(function* () {\n const supplied = yield* Effect.serviceOption(Crypto.Crypto);\n const crypto = Option.getOrElse(supplied, native);\n\n return yield* crypto.randomUUIDv4.pipe(\n Effect.mapError(() =>\n undispatched(\"make\", new Failed({ detail: \"Could not name connection\" })),\n ),\n );\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, 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 * 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<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 connection = yield* Identity.make;\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 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 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
+ {"version":3,"file":"Browser-BBSySyjT.mjs","names":["make","make","make","make","Url.redact","Identity.make","mapping","BrowserClock.mapping","Timeline.make","Gate.make","PageImpl.make","Openings.make"],"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, PlatformError } from \"effect\";\n\nimport { Failed } from \"../../BrowserError.ts\";\nimport { undispatched } from \"./context.ts\";\n\n// The core owns this tiny platform adapter to keep Chromium/CDP usable without another package\n// or a new required service. Browser construction captures it once; there is no global counter.\nconst native = () =>\n Crypto.make({\n randomBytes: (size) => globalThis.crypto.getRandomValues(new Uint8Array(size)),\n digest: (algorithm, data) =>\n Effect.tryPromise({\n try: () => globalThis.crypto.subtle.digest(algorithm, new Uint8Array(data)),\n catch: (cause) =>\n PlatformError.systemError({\n module: \"Crypto\",\n method: \"digest\",\n _tag: \"Unknown\",\n description: \"Could not compute digest\",\n cause,\n }),\n }).pipe(Effect.map((bytes) => new Uint8Array(bytes))),\n });\n\nexport const make = Effect.gen(function* () {\n const supplied = yield* Effect.serviceOption(Crypto.Crypto);\n const crypto = Option.getOrElse(supplied, native);\n\n return yield* crypto.randomUUIDv4.pipe(\n Effect.mapError(() =>\n undispatched(\"make\", new Failed({ detail: \"Could not name connection\" })),\n ),\n );\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, 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 * 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<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 connection = yield* Identity.make;\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 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 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,6 +1,6 @@
1
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-B7X6DQUn.mjs";
3
+ import { c as InputGuard, d as Page_d_exports, u as Page$1 } from "./Page-DL9eylp3.mjs";
4
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 {
package/dist/Browser.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { i as make, r as Suspension, t as Browser } from "./Browser-QiT6Cdn_.mjs";
1
+ import { i as make, r as Suspension, t as Browser } from "./Browser-BBSySyjT.mjs";
2
2
  export { Browser, Suspension, make };
package/dist/Cdp.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { Failed } from "./BrowserError.mjs";
3
- import { i as make, t as Browser } from "./Browser-QiT6Cdn_.mjs";
3
+ import { i as make, t as Browser } from "./Browser-BBSySyjT.mjs";
4
4
  import { o as reasonOf, s as undispatched } from "./context-BAjRj7Xo.mjs";
5
5
  import { r as quoted } from "./url-Cd04Okid.mjs";
6
6
  import { Effect, Layer, Redacted } from "effect";
package/dist/Chromium.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { Failed } from "./BrowserError.mjs";
3
- import { i as make, t as Browser } from "./Browser-QiT6Cdn_.mjs";
3
+ import { i as make, t as Browser } from "./Browser-BBSySyjT.mjs";
4
4
  import { o as reasonOf, s as undispatched } from "./context-BAjRj7Xo.mjs";
5
5
  import { Effect, Layer } from "effect";
6
6
  import { chromium } from "playwright-core";
package/dist/Expect.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
- import { a as FindQuery, u as Page } from "./Page-B7X6DQUn.mjs";
2
+ import { a as FindQuery, u as Page } from "./Page-DL9eylp3.mjs";
3
3
  import { Duration, Effect } from "effect";
4
4
  declare namespace Expect_d_exports {
5
5
  export { Attempt, Expectation, Options, Outcome, appeared, attempt, changed, gone, navigated };
package/dist/Expect.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { InvalidRequest } from "./BrowserError.mjs";
3
3
  import { s as undispatched } from "./context-BAjRj7Xo.mjs";
4
- import { t as internalsOf } from "./page-ZxMhw16l.mjs";
4
+ import { t as internalsOf } from "./page-B92QnRfT.mjs";
5
5
  import { Cause, Duration, Effect, Exit, Option, Schedule } from "effect";
6
6
  //#region src/Expect.ts
7
7
  /**
@@ -69,10 +69,8 @@ declare const CaptureStats_base: Schema.Class<CaptureStats, Schema.Struct<{
69
69
  */
70
70
  readonly foreignSize: Schema.Int;
71
71
  /**
72
- * Frames dropped because they may have been painted, or arrived, while the library took a clipped
73
- * or scaled picture of the page, such as a zoom: Chromium draws those into the screencast too.
74
- * Where Playwright emulates the viewport, the page's own frames from that time are among them; a
75
- * crop on the page's own session leaves out only frames of another size than the page's.
72
+ * Frames discarded while native capture is paused for a library picture and its restoration.
73
+ * A pause can produce no callbacks, so this count may remain zero even when pictures were taken.
76
74
  */
77
75
  readonly duringPictures: Schema.Int;
78
76
  /**
@@ -116,4 +114,4 @@ interface ScreencastOptions {
116
114
  }
117
115
  //#endregion
118
116
  export { Image as a, Frame_d_exports as i, CaptureStats as n, ScreencastOptions as o, Frame as r, Screenshot as s, BrowserPaint as t };
119
- //# sourceMappingURL=Frame-Di2PxyF7.d.mts.map
117
+ //# sourceMappingURL=Frame-DkNdjjjr.d.mts.map
package/dist/Frame.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as Image, n as CaptureStats, o as ScreencastOptions, r as Frame, s as Screenshot, t as BrowserPaint } from "./Frame-Di2PxyF7.mjs";
1
+ import { a as Image, n as CaptureStats, o as ScreencastOptions, r as Frame, s as Screenshot, t as BrowserPaint } from "./Frame-DkNdjjjr.mjs";
2
2
  export { BrowserPaint, CaptureStats, Frame, Image, ScreencastOptions, Screenshot };
package/dist/Frame.mjs CHANGED
@@ -94,10 +94,8 @@ var CaptureStats = class extends Schema.Class("effect-browser/CaptureStats")({
94
94
  */
95
95
  foreignSize: Count,
96
96
  /**
97
- * Frames dropped because they may have been painted, or arrived, while the library took a clipped
98
- * or scaled picture of the page, such as a zoom: Chromium draws those into the screencast too.
99
- * Where Playwright emulates the viewport, the page's own frames from that time are among them; a
100
- * crop on the page's own session leaves out only frames of another size than the page's.
97
+ * Frames discarded while native capture is paused for a library picture and its restoration.
98
+ * A pause can produce no callbacks, so this count may remain zero even when pictures were taken.
101
99
  */
102
100
  duringPictures: Count,
103
101
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Frame.mjs","names":[],"sources":["../src/Frame.ts"],"sourcesContent":["/**\n * Pictures of a page: screenshots, and screencast frames stamped with the browser's clock.\n *\n * @since 0.3.0\n */\nimport { Schema } from \"effect\";\n\n/** An encoded picture with its pixel dimensions. Crops also need their viewport origin. */\nexport class Image extends Schema.Class<Image>(\"effect-browser/Image\")({\n data: Schema.Uint8Array,\n mediaType: Schema.Literals([\"image/jpeg\", \"image/png\"]),\n width: Schema.Finite,\n height: Schema.Finite,\n}) {}\n\nconst Nonnegative = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));\nconst Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));\n\n/** Native CDP paint time mapped to the owning host clock, with transport uncertainty retained. */\nexport class BrowserPaint extends Schema.TaggedClass<BrowserPaint>()(\"BrowserPaint\", {\n timestamp: Schema.Finite,\n hostTime: Schema.Finite,\n uncertaintyMillis: Nonnegative,\n}) {}\n\n/** A screenshot's host capture interval; it has no native browser paint timestamp. */\nexport class Screenshot extends Schema.TaggedClass<Screenshot>()(\"Screenshot\", {\n hostTime: Schema.Finite,\n uncertaintyMillis: Nonnegative,\n}) {}\n\n/**\n * An image and its timing evidence. Paint time and screenshot capture time cannot be confused in\n * serialized data. `receivedAt` records delivery on the owning host's monotonic clock; it does\n * not make an old paint current.\n */\nexport class Frame extends Schema.Class<Frame>(\"effect-browser/Frame\")({\n page: Schema.String,\n /** The page's browser session: its provider's session id, as `Browser.Service.id` gives it. */\n session: Schema.String,\n /** Owning Browser connection; document counters restart on adoption or reattachment. */\n connection: Schema.String.check(Schema.isNonEmpty()),\n data: Schema.Uint8Array,\n timing: Schema.Union([BrowserPaint, Screenshot]),\n receivedAt: Schema.Finite,\n width: Schema.Finite,\n height: Schema.Finite,\n /**\n * The page's document committed before the frame arrived, as `Navigated` counts them. Just\n * after a commit a browser can still show the old document's paint, so a frame may show the\n * document before its own, never one after it.\n */\n document: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** The page's address when the frame arrived. */\n url: Schema.String,\n}) {\n get timestamp(): number | undefined {\n return this.timing._tag === \"BrowserPaint\" ? this.timing.timestamp : undefined;\n }\n\n get hostTime(): number {\n return this.timing.hostTime;\n }\n\n get image(): Image {\n return new Image({\n data: this.data,\n mediaType: \"image/jpeg\",\n width: this.width,\n height: this.height,\n });\n }\n}\n\n/**\n * A page's capture counts, over its life or a window of the latest minute: what Chromium sent,\n * what readers got, and why the rest went no further. Frames that came late are counted apart from\n * those a reader lost.\n */\nexport class CaptureStats extends Schema.Class<CaptureStats>(\"effect-browser/CaptureStats\")({\n received: Count,\n accepted: Count,\n /**\n * Frames Chromium finished encoding after a newer one, dropped rather than reordered. It encodes\n * several at once, so a busy machine makes more of them; none was a paint readers missed.\n */\n late: Count,\n missingTimestamp: Count,\n /**\n * Frames dropped for their size. Most are other captures' frames, which a clipped screenshot\n * draws into a screencast. The rest are the page's own frames of a new size, dropped while still\n * unconfirmed: when another size replaced it, when its capture stopped, or beyond the 16 held.\n */\n foreignSize: Count,\n /**\n * Frames dropped because they may have been painted, or arrived, while the library took a clipped\n * or scaled picture of the page, such as a zoom: Chromium draws those into the screencast too.\n * Where Playwright emulates the viewport, the page's own frames from that time are among them; a\n * crop on the page's own session leaves out only frames of another size than the page's.\n */\n duringPictures: Count,\n /**\n * Frames a reader missed by falling behind. Each reader counts its own, so with several readers\n * this can exceed the frames accepted.\n */\n lost: Count,\n /**\n * Acknowledgements of frames sent and not yet answered, now, whatever the window. Chromium sends\n * a frame only while few are unacknowledged, so a backlog means fewer frames.\n */\n ackBacklog: Count,\n /** Paint-time gaps between consecutive accepted frames. */\n gaps: Schema.Struct({\n count: Count,\n totalMillis: Nonnegative,\n minMillis: Schema.NullOr(Nonnegative),\n maxMillis: Schema.NullOr(Nonnegative),\n lastMillis: Schema.NullOr(Nonnegative),\n }),\n}) {}\n\n/**\n * Settings for a page's one native capture. Readers that join a running capture share its\n * settings: one without options accepts them, and one whose explicit options differ fails with\n * `InvalidRequest`.\n */\nexport interface ScreencastOptions {\n /** JPEG quality, 0 to 100. Defaults to 80. */\n readonly quality?: number | undefined;\n /** Frames are scaled down to fit this box. Defaults to the viewport in CSS pixels. */\n readonly size?: { readonly width: number; readonly height: number } | undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAQA,IAAa,QAAb,cAA2B,OAAO,MAAa,sBAAsB,CAAC,CAAC;CACrE,MAAM,OAAO;CACb,WAAW,OAAO,SAAS,CAAC,cAAc,WAAW,CAAC;CACtD,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB,CAAC,CAAC,CAAC,CAAC;AAEJ,MAAM,cAAc,OAAO,OAAO,MAAM,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,QAAQ,OAAO,IAAI,MAAM,OAAO,uBAAuB,CAAC,CAAC;;AAG/D,IAAa,eAAb,cAAkC,OAAO,YAA0B,CAAC,CAAC,gBAAgB;CACnF,WAAW,OAAO;CAClB,UAAU,OAAO;CACjB,mBAAmB;AACrB,CAAC,CAAC,CAAC,CAAC;;AAGJ,IAAa,aAAb,cAAgC,OAAO,YAAwB,CAAC,CAAC,cAAc;CAC7E,UAAU,OAAO;CACjB,mBAAmB;AACrB,CAAC,CAAC,CAAC,CAAC;;;;;;AAOJ,IAAa,QAAb,cAA2B,OAAO,MAAa,sBAAsB,CAAC,CAAC;CACrE,MAAM,OAAO;;CAEb,SAAS,OAAO;;CAEhB,YAAY,OAAO,OAAO,MAAM,OAAO,WAAW,CAAC;CACnD,MAAM,OAAO;CACb,QAAQ,OAAO,MAAM,CAAC,cAAc,UAAU,CAAC;CAC/C,YAAY,OAAO;CACnB,OAAO,OAAO;CACd,QAAQ,OAAO;;;;;;CAMf,UAAU,OAAO,IAAI,MAAM,OAAO,uBAAuB,CAAC,CAAC;;CAE3D,KAAK,OAAO;AACd,CAAC,CAAC,CAAC;CACD,IAAI,YAAgC;EAClC,OAAO,KAAK,OAAO,SAAS,iBAAiB,KAAK,OAAO,YAAY,KAAA;CACvE;CAEA,IAAI,WAAmB;EACrB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,QAAe;EACjB,OAAO,IAAI,MAAM;GACf,MAAM,KAAK;GACX,WAAW;GACX,OAAO,KAAK;GACZ,QAAQ,KAAK;EACf,CAAC;CACH;AACF;;;;;;AAOA,IAAa,eAAb,cAAkC,OAAO,MAAoB,6BAA6B,CAAC,CAAC;CAC1F,UAAU;CACV,UAAU;;;;;CAKV,MAAM;CACN,kBAAkB;;;;;;CAMlB,aAAa;;;;;;;CAOb,gBAAgB;;;;;CAKhB,MAAM;;;;;CAKN,YAAY;;CAEZ,MAAM,OAAO,OAAO;EAClB,OAAO;EACP,aAAa;EACb,WAAW,OAAO,OAAO,WAAW;EACpC,WAAW,OAAO,OAAO,WAAW;EACpC,YAAY,OAAO,OAAO,WAAW;CACvC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Frame.mjs","names":[],"sources":["../src/Frame.ts"],"sourcesContent":["/**\n * Pictures of a page: screenshots, and screencast frames stamped with the browser's clock.\n *\n * @since 0.3.0\n */\nimport { Schema } from \"effect\";\n\n/** An encoded picture with its pixel dimensions. Crops also need their viewport origin. */\nexport class Image extends Schema.Class<Image>(\"effect-browser/Image\")({\n data: Schema.Uint8Array,\n mediaType: Schema.Literals([\"image/jpeg\", \"image/png\"]),\n width: Schema.Finite,\n height: Schema.Finite,\n}) {}\n\nconst Nonnegative = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0));\nconst Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));\n\n/** Native CDP paint time mapped to the owning host clock, with transport uncertainty retained. */\nexport class BrowserPaint extends Schema.TaggedClass<BrowserPaint>()(\"BrowserPaint\", {\n timestamp: Schema.Finite,\n hostTime: Schema.Finite,\n uncertaintyMillis: Nonnegative,\n}) {}\n\n/** A screenshot's host capture interval; it has no native browser paint timestamp. */\nexport class Screenshot extends Schema.TaggedClass<Screenshot>()(\"Screenshot\", {\n hostTime: Schema.Finite,\n uncertaintyMillis: Nonnegative,\n}) {}\n\n/**\n * An image and its timing evidence. Paint time and screenshot capture time cannot be confused in\n * serialized data. `receivedAt` records delivery on the owning host's monotonic clock; it does\n * not make an old paint current.\n */\nexport class Frame extends Schema.Class<Frame>(\"effect-browser/Frame\")({\n page: Schema.String,\n /** The page's browser session: its provider's session id, as `Browser.Service.id` gives it. */\n session: Schema.String,\n /** Owning Browser connection; document counters restart on adoption or reattachment. */\n connection: Schema.String.check(Schema.isNonEmpty()),\n data: Schema.Uint8Array,\n timing: Schema.Union([BrowserPaint, Screenshot]),\n receivedAt: Schema.Finite,\n width: Schema.Finite,\n height: Schema.Finite,\n /**\n * The page's document committed before the frame arrived, as `Navigated` counts them. Just\n * after a commit a browser can still show the old document's paint, so a frame may show the\n * document before its own, never one after it.\n */\n document: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** The page's address when the frame arrived. */\n url: Schema.String,\n}) {\n get timestamp(): number | undefined {\n return this.timing._tag === \"BrowserPaint\" ? this.timing.timestamp : undefined;\n }\n\n get hostTime(): number {\n return this.timing.hostTime;\n }\n\n get image(): Image {\n return new Image({\n data: this.data,\n mediaType: \"image/jpeg\",\n width: this.width,\n height: this.height,\n });\n }\n}\n\n/**\n * A page's capture counts, over its life or a window of the latest minute: what Chromium sent,\n * what readers got, and why the rest went no further. Frames that came late are counted apart from\n * those a reader lost.\n */\nexport class CaptureStats extends Schema.Class<CaptureStats>(\"effect-browser/CaptureStats\")({\n received: Count,\n accepted: Count,\n /**\n * Frames Chromium finished encoding after a newer one, dropped rather than reordered. It encodes\n * several at once, so a busy machine makes more of them; none was a paint readers missed.\n */\n late: Count,\n missingTimestamp: Count,\n /**\n * Frames dropped for their size. Most are other captures' frames, which a clipped screenshot\n * draws into a screencast. The rest are the page's own frames of a new size, dropped while still\n * unconfirmed: when another size replaced it, when its capture stopped, or beyond the 16 held.\n */\n foreignSize: Count,\n /**\n * Frames discarded while native capture is paused for a library picture and its restoration.\n * A pause can produce no callbacks, so this count may remain zero even when pictures were taken.\n */\n duringPictures: Count,\n /**\n * Frames a reader missed by falling behind. Each reader counts its own, so with several readers\n * this can exceed the frames accepted.\n */\n lost: Count,\n /**\n * Acknowledgements of frames sent and not yet answered, now, whatever the window. Chromium sends\n * a frame only while few are unacknowledged, so a backlog means fewer frames.\n */\n ackBacklog: Count,\n /** Paint-time gaps between consecutive accepted frames. */\n gaps: Schema.Struct({\n count: Count,\n totalMillis: Nonnegative,\n minMillis: Schema.NullOr(Nonnegative),\n maxMillis: Schema.NullOr(Nonnegative),\n lastMillis: Schema.NullOr(Nonnegative),\n }),\n}) {}\n\n/**\n * Settings for a page's one native capture. Readers that join a running capture share its\n * settings: one without options accepts them, and one whose explicit options differ fails with\n * `InvalidRequest`.\n */\nexport interface ScreencastOptions {\n /** JPEG quality, 0 to 100. Defaults to 80. */\n readonly quality?: number | undefined;\n /** Frames are scaled down to fit this box. Defaults to the viewport in CSS pixels. */\n readonly size?: { readonly width: number; readonly height: number } | undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAQA,IAAa,QAAb,cAA2B,OAAO,MAAa,sBAAsB,CAAC,CAAC;CACrE,MAAM,OAAO;CACb,WAAW,OAAO,SAAS,CAAC,cAAc,WAAW,CAAC;CACtD,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB,CAAC,CAAC,CAAC,CAAC;AAEJ,MAAM,cAAc,OAAO,OAAO,MAAM,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,QAAQ,OAAO,IAAI,MAAM,OAAO,uBAAuB,CAAC,CAAC;;AAG/D,IAAa,eAAb,cAAkC,OAAO,YAA0B,CAAC,CAAC,gBAAgB;CACnF,WAAW,OAAO;CAClB,UAAU,OAAO;CACjB,mBAAmB;AACrB,CAAC,CAAC,CAAC,CAAC;;AAGJ,IAAa,aAAb,cAAgC,OAAO,YAAwB,CAAC,CAAC,cAAc;CAC7E,UAAU,OAAO;CACjB,mBAAmB;AACrB,CAAC,CAAC,CAAC,CAAC;;;;;;AAOJ,IAAa,QAAb,cAA2B,OAAO,MAAa,sBAAsB,CAAC,CAAC;CACrE,MAAM,OAAO;;CAEb,SAAS,OAAO;;CAEhB,YAAY,OAAO,OAAO,MAAM,OAAO,WAAW,CAAC;CACnD,MAAM,OAAO;CACb,QAAQ,OAAO,MAAM,CAAC,cAAc,UAAU,CAAC;CAC/C,YAAY,OAAO;CACnB,OAAO,OAAO;CACd,QAAQ,OAAO;;;;;;CAMf,UAAU,OAAO,IAAI,MAAM,OAAO,uBAAuB,CAAC,CAAC;;CAE3D,KAAK,OAAO;AACd,CAAC,CAAC,CAAC;CACD,IAAI,YAAgC;EAClC,OAAO,KAAK,OAAO,SAAS,iBAAiB,KAAK,OAAO,YAAY,KAAA;CACvE;CAEA,IAAI,WAAmB;EACrB,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,QAAe;EACjB,OAAO,IAAI,MAAM;GACf,MAAM,KAAK;GACX,WAAW;GACX,OAAO,KAAK;GACZ,QAAQ,KAAK;EACf,CAAC;CACH;AACF;;;;;;AAOA,IAAa,eAAb,cAAkC,OAAO,MAAoB,6BAA6B,CAAC,CAAC;CAC1F,UAAU;CACV,UAAU;;;;;CAKV,MAAM;CACN,kBAAkB;;;;;;CAMlB,aAAa;;;;;CAKb,gBAAgB;;;;;CAKhB,MAAM;;;;;CAKN,YAAY;;CAEZ,MAAM,OAAO,OAAO;EAClB,OAAO;EACP,aAAa;EACb,WAAW,OAAO,OAAO,WAAW;EACpC,WAAW,OAAO,OAAO,WAAW;EACpC,YAAY,OAAO,OAAO,WAAW;CACvC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC"}
package/dist/Moment.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as Account, F as account, I as capture, L as toPrompt, M as Moment, P as Window, j as CaptureOptions } from "./Page-B7X6DQUn.mjs";
1
+ import { A as Account, F as account, I as capture, L as toPrompt, M as Moment, P as Window, j as CaptureOptions } from "./Page-DL9eylp3.mjs";
2
2
  export { Account, CaptureOptions, Moment, Window, account, capture, toPrompt };