effect-browser 0.3.0-beta.11 → 0.3.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -10
- package/dist/{Browser-B4qncxe7.mjs → Browser-QiT6Cdn_.mjs} +3 -3
- package/dist/{Browser-B4qncxe7.mjs.map → Browser-QiT6Cdn_.mjs.map} +1 -1
- package/dist/Browser.mjs +1 -1
- package/dist/Cdp.mjs +2 -2
- package/dist/Chromium.mjs +2 -2
- package/dist/Expect.mjs +2 -2
- package/dist/Moment.mjs +1 -1
- package/dist/{Plan-Akcac63g.d.mts → Plan-BS0jpENY.d.mts} +14 -7
- package/dist/{Plan-D1wrJVs8.mjs → Plan-Bl2NM1D5.mjs} +21 -12
- package/dist/Plan-Bl2NM1D5.mjs.map +1 -0
- package/dist/Plan.d.mts +1 -1
- package/dist/Plan.mjs +1 -1
- package/dist/Presentation.mjs +1 -1
- package/dist/Stage.mjs +4 -4
- package/dist/Stage.mjs.map +1 -1
- package/dist/{context-B6xr8nVi.mjs → context-BAjRj7Xo.mjs} +3 -1
- package/dist/{context-B6xr8nVi.mjs.map → context-BAjRj7Xo.mjs.map} +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{page-BAf__WKX.mjs → page-ZxMhw16l.mjs} +7 -8
- package/dist/page-ZxMhw16l.mjs.map +1 -0
- package/package.json +1 -1
- package/src/Plan.ts +26 -15
- package/src/Stage.ts +12 -5
- package/src/internal/input/settle.ts +8 -3
- package/src/internal/page/context.ts +8 -0
- package/src/internal/page/navigation.ts +21 -15
- package/dist/Plan-D1wrJVs8.mjs.map +0 -1
- package/dist/page-BAf__WKX.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Plan-Bl2NM1D5.mjs","names":[],"sources":["../src/internal/reading/choose.ts","../src/Plan.ts"],"sourcesContent":["/**\n * Choosing the one element a recorded subject names, among those `find` gives for its role and\n * name. Context tells them apart, and an element that does not repeat every part of the recorded\n * context is another subject, so a sole element left once its own has gone, such as Alice's\n * \"Remove\" after Bob has left, drifts rather than stands in:\n *\n * - a row is read whole: a row named \"Wrapped BTC\" is not the row \"BTC\". Laid out again without\n * rows, as cards, one of the element's fields must start with the row's words;\n * - a column counts where the element is under one;\n * - a label or heading must read the same, or its words appear, whole and in order, in one of the\n * element's fields, as `near` reads them.\n *\n * The rest are ranked by how much of the recorded context they read the same. There is no\n * ordinal, so a tie stays a tie.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport type { Subject, SubjectContext } from \"../../BrowserEvent.ts\";\nimport type { Found } from \"../../Page.ts\";\nimport { match } from \"./match.inpage.ts\";\n\n/** Nothing on the page has the step's role and name. */\nexport class Missing extends Schema.TaggedError<Missing>()(\"Missing\", {}) {\n override get message() {\n return \"nothing on the page has the recorded role and name\";\n }\n}\n\n/** Several elements match the step's subject equally well, and nothing tells them apart. */\nexport class Ambiguous extends Schema.TaggedError<Ambiguous>()(\"Ambiguous\", {\n count: Schema.Int,\n}) {\n override get message() {\n return `${this.count} elements match the recorded subject equally well`;\n }\n}\n\n/** The page is not where the recording was: another site, or no element with its context. */\nexport class Drifted extends Schema.TaggedError<Drifted>()(\"Drifted\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\nconst { holds, normalize } = match();\nconst fields = [\"row\", \"column\", \"label\", \"heading\"] as const;\n\nconst said = (context: SubjectContext) =>\n Object.values(context).filter((value) => typeof value === \"string\");\n\n/** How an element repeats one recorded field: 2 read the same, 1 found as words, 0 not at all. */\nconst repeats = (field: (typeof fields)[number], value: string, context: SubjectContext) => {\n const own = context[field];\n\n if (own !== undefined && normalize(own) === normalize(value)) return 2;\n if (field === \"column\") return own === undefined ? 1 : 0;\n if (field === \"row\")\n return own === undefined &&\n said(context).some((other) => `${normalize(other)} `.startsWith(`${normalize(value)} `))\n ? 1\n : 0;\n\n return said(context).some((other) => holds(other, value)) ? 1 : 0;\n};\n\nexport const choose = (\n subject: Subject,\n found: ReadonlyArray<Found>,\n): Effect.Effect<Found, Missing | Ambiguous | Drifted> => {\n // `find` matched the role; a subject without one only matches elements without one.\n const named = found.filter((one) => subject.role !== null || one.subject.role === null);\n\n const kept = named.flatMap((one) => {\n let score = 0;\n\n for (const field of fields) {\n const value = subject.context[field];\n const repeated = value === undefined ? 0 : repeats(field, value, one.subject.context);\n\n if (value !== undefined && repeated === 0) return [];\n score += repeated;\n }\n\n return [{ one, score }];\n });\n\n const best = Math.max(...kept.map(({ score }) => score));\n const top = kept.filter(({ score }) => score === best);\n const [only] = top;\n\n if (named.length === 0) return Effect.fail(new Missing());\n if (kept.length === 0)\n return Effect.fail(new Drifted({ detail: \"no element named so repeats the recorded context\" }));\n\n return top.length === 1 && only !== undefined\n ? Effect.succeed(only.one)\n : Effect.fail(new Ambiguous({ count: top.length }));\n};\n","/**\n * A walk recorded once from a page's events and replayed later, on a fresh page, with no model\n * call.\n *\n * `fromEvents` keeps one page's completed actions with their subjects and the options they were\n * given, its navigations with both the address asked for and the one reached, and what was typed\n * as a named input slot, never the text itself. A navigation to an address whose credentials the\n * events withheld is an input slot too, so replay goes where the caller says, or nowhere, and\n * never to the address without them. `replay` takes the steps in turn. Before a step\n * that acts on the page, it waits for `Page.ready`; then it finds the one element the step's\n * subject names, by role and name among those that repeat all its recorded context, and acts on\n * it. Text typed into focus was typed into the focused field, which replay finds the same way, and\n * text typed into a secret field goes into nothing else. An element step never falls back to\n * coordinates, and a step recorded at a point acts there only while the element found for it still\n * covers that point. `take` and `replay` return receipts; failure preserves a stopping point and\n * earlier receipts. Unknown consequential input is never resent. Only a positive durable read-back\n * can reconcile it; restoration follows the same rule. The consumer journals begun steps before\n * calling, treating interruption or a missing receipt as unknown. Plain recordings use version 1;\n * authored expected effects use version 2. Old 0.2 plans and unknown versions do not decode.\n *\n * @since 0.3.0\n */\nimport { Duration, Effect, Exit, Option, Schedule, Schema } from \"effect\";\n\nimport { BrowserError, InvalidRequest } from \"./BrowserError.ts\";\nimport {\n type Action,\n ActionOptions,\n Box,\n type BrowserEvent,\n type Navigated,\n Subject,\n} from \"./BrowserEvent.ts\";\nimport { undispatched } from \"./internal/page/context.ts\";\nimport { Ambiguous, choose, Drifted, Missing } from \"./internal/reading/choose.ts\";\nimport {\n type Found,\n type Page,\n type Point,\n type ReadyOptions,\n redacted,\n type Target,\n} from \"./Page.ts\";\n\nexport { Ambiguous, Drifted, Missing } from \"./internal/reading/choose.ts\";\n\nconst ViewportPoint = Schema.Struct({ x: Schema.Finite, y: Schema.Finite });\n\nexport const StepAction = Schema.Literals([\n \"navigate\",\n \"back\",\n \"reload\",\n \"click\",\n \"hover\",\n \"drag\",\n \"type\",\n \"press\",\n \"scroll\",\n \"select\",\n]);\n\nexport type StepAction = typeof StepAction.Type;\n\nconst Query = Schema.Struct({\n role: Schema.optional(Schema.String),\n name: Schema.optional(Schema.String),\n text: Schema.optional(Schema.String),\n});\n\n/**\n * What a step leaves. `durable` asserts an author-chosen, lasting read-back of this operation,\n * such as its uniquely named saved receipt or exact operation URL. Only Appeared and an exact\n * Navigated URL allow it; a toast, absence or relative navigation cannot establish recovery.\n * Relative navigation measures observed change from dispatch, without asserting causality.\n */\nexport const Expected = Schema.Union([\n Schema.TaggedStruct(\"Checked\", { checked: Schema.Boolean }),\n Schema.TaggedStruct(\"Appeared\", {\n query: Query,\n durable: Schema.optional(Schema.Literal(true)),\n }),\n Schema.TaggedStruct(\"Gone\", {\n query: Query,\n durable: Schema.optional(Schema.Never),\n }),\n Schema.TaggedStruct(\"Navigated\", {\n url: Schema.String,\n durable: Schema.optional(Schema.Literal(true)),\n }),\n Schema.TaggedStruct(\"Navigated\", {\n url: Schema.optional(Schema.Never),\n durable: Schema.optional(Schema.Never),\n }),\n]);\n\nexport type Expected = typeof Expected.Type;\n\n/** One recorded step: the action, what it acted on, and what else it was asked. */\nexport class Step extends Schema.Class<Step>(\"effect-browser/Plan/Step\")({\n action: StepAction,\n /**\n * The ending address, or a later recorded navigation before the next action began. Empty when\n * unknown; a delayed navigation can be stronger arrival evidence than the action's ending URL.\n */\n url: Schema.String,\n /** A navigation's requested address, or the keys a press pressed. */\n target: Schema.optional(Schema.String),\n subject: Schema.optional(Subject),\n /** Where a drag ended. */\n to: Schema.optional(Subject),\n /** The viewport point a step aimed at, when it aimed at a point, and a drag's end point. */\n point: Schema.optional(ViewportPoint),\n toPoint: Schema.optional(ViewportPoint),\n /** The box of what a step aimed at by point found there, so replay aims at the same place in it. */\n box: Schema.optional(Box),\n /**\n * The input slot whose text a `type` step types, or whose address a `navigate` step goes to, as\n * its recorded address had a credential withheld.\n */\n input: Schema.optional(Schema.String),\n /** The slot's text went into a secret field, so replay types it into nothing else. */\n secret: Schema.optional(Schema.Literal(true)),\n options: Schema.optional(ActionOptions),\n expect: Schema.optional(Expected),\n}) {}\n\nconst PlanData = Schema.Struct({\n version: Schema.Literals([1, 2]),\n steps: Schema.Array(Step),\n}).check(\n Schema.makeFilter((plan) =>\n plan.version === (plan.steps.some((step) => step.expect !== undefined) ? 2 : 1)\n ? undefined\n : \"only plans with expected effects use version 2\",\n ),\n);\n\nexport class Plan extends Schema.Class<Plan>(\"effect-browser/Plan\")(PlanData) {\n /** Choose the encoding version from the steps; simple recordings remain version 1. */\n static new(steps: ReadonlyArray<Step>): Plan {\n return new Plan({ version: steps.some((step) => step.expect !== undefined) ? 2 : 1, steps });\n }\n\n /** The input slots its steps type, in order; replay needs text for each. */\n get inputs(): ReadonlyArray<string> {\n return this.steps.flatMap((step) => (step.input === undefined ? [] : [step.input]));\n }\n}\n\n/** One completed step, on the page's host clock. `verified` means its expected effect was seen. */\nexport class Receipt extends Schema.Class<Receipt>(\"effect-browser/Plan/Receipt\")({\n step: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n outcome: Schema.Literals([\"taken\", \"verified\", \"skipped\"]),\n startedAt: Schema.Finite,\n at: Schema.Finite,\n}) {}\n\n/** Durable stopping data. A begun step without a receipt must be restored as `unknown`. */\nexport class Stopped extends Schema.Class<Stopped>(\"effect-browser/Plan/Stopped\")({\n step: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n input: Schema.Literals([\"unsent\", \"unknown\"]),\n}) {}\n\n/** Repeating this step could repeat an unresolved effect; the consumer must reconcile it. */\nexport class Uncertain extends Schema.TaggedError<Uncertain>()(\"Uncertain\", {\n detail: Schema.String,\n}) {\n override get message() {\n return this.detail;\n }\n}\n\n/** Where replay stopped, counting steps from 0, why, and the steps that completed before it. */\nexport class ReplayError extends Schema.TaggedError<ReplayError>()(\"ReplayError\", {\n step: Schema.Int,\n reason: Schema.Union([Missing, Ambiguous, Drifted, BrowserError, Uncertain]),\n input: Schema.Literals([\"unsent\", \"unknown\"]),\n receipts: Schema.Array(Receipt),\n}) {\n get stopped(): Stopped {\n return new Stopped({ step: this.step, input: this.input });\n }\n\n override get message() {\n return `step ${this.step} failed: ${this.reason.message}`;\n }\n}\n\nconst isStep = (event: BrowserEvent): event is Action & { readonly name: StepAction } =>\n event._tag === \"Action\" &&\n event.ok &&\n (StepAction.literals as ReadonlyArray<string>).includes(event.name);\n\n// A point target reads `x,y`, as `{\"x\":…,\"y\":…}` inside a drag's; a ref reads like e12.\nconst pointIn = (target: string | undefined): Point | undefined => {\n const parsed = /^\\{?(?:\"x\":)?(-?[\\d.]+),(?:\"y\":)?(-?[\\d.]+)\\}?$/.exec(target ?? \"\");\n\n return parsed === null ? undefined : { x: Number(parsed[1]), y: Number(parsed[2]) };\n};\n\n/** A slot named for the field it fills, such as `email`, unique within the plan. */\nconst slotFor = (taken: Set<string>, field: string | undefined) => {\n const base =\n field\n ?.toLowerCase()\n .match(/[\\p{L}\\p{N}]+/gu)\n ?.join(\"-\") ?? \"input\";\n\n let slot = base;\n\n for (let count = 2; taken.has(slot); count++) slot = `${base}-${count}`;\n taken.add(slot);\n\n return slot;\n};\n\n/**\n * The walk one page's events record: the completed actions of the page that acted first, in\n * order. `page.recentEvents` gives them, or `browser.events()` read until the walk ends.\n */\nexport const fromEvents = (events: Iterable<BrowserEvent>): Plan => {\n const all = Array.from(events);\n const page = all.find((event) => event._tag === \"Action\")?.page;\n const mine = all.filter((event) => \"page\" in event && event.page === page);\n const steps = mine.filter(isStep);\n const opened = mine.find((event) => event._tag === \"PageOpened\")?.url ?? \"\";\n const moves = mine.filter((event): event is Navigated => event._tag === \"Navigated\");\n const taken = new Set<string>();\n\n // Where the page was just before a time: the last address it reached by then.\n const before = (time: number) => moves.findLast((move) => move.at <= time)?.url ?? opened;\n\n return new Plan({\n version: 1,\n steps: steps.map((action, index) => {\n const drag = action.name === \"drag\" ? (action.target ?? \"\").split(\" -> \") : undefined;\n const point = pointIn(drag === undefined ? action.target : drag[0]);\n const until = steps[index + 1]?.startedAt ?? Number.POSITIVE_INFINITY;\n const arrival = moves.findLast((move) => move.at > action.at && move.at <= until);\n\n // Only a navigation and a press keep their target: an address, or keys.\n return new Step({\n action: action.name,\n url: arrival?.url ?? action.url ?? before(until),\n target: action.name === \"navigate\" || action.name === \"press\" ? action.target : undefined,\n subject: action.subject,\n to: action.to,\n point,\n toPoint: pointIn(drag?.[1]),\n box: point === undefined ? undefined : action.box,\n input:\n action.name === \"type\"\n ? slotFor(taken, action.subject?.name)\n : action.name === \"navigate\" && action.target?.includes(redacted) === true\n ? slotFor(taken, \"address\")\n : undefined,\n secret: action.name === \"type\" && action.text === redacted ? true : undefined,\n options: action.options,\n });\n }),\n });\n};\n\n/**\n * The one element on the page that a subject names, by role, name and context (see `Plan`),\n * looking again for two seconds while the page may still be drawing it.\n */\nexport const locate = (page: Page, subject: Subject) =>\n page\n .find({\n name: subject.name,\n scope: \"document\",\n ...(subject.role === null ? {} : { role: subject.role }),\n })\n .pipe(\n Effect.flatMap((found) => choose(subject, found)),\n Effect.retry({\n schedule: Schedule.spaced(Duration.millis(250)),\n times: 8,\n while: (error) => error._tag !== \"BrowserError\",\n }),\n Effect.withSpan(\"Plan.locate\", { attributes: { role: subject.role ?? \"\" } }),\n );\n\nconst invalid = (detail: string) => undispatched(\"replay\", new InvalidRequest({ detail }));\n\n/**\n * Where the step acts: the element its subject names, or, for a step recorded at a point, the same\n * place within that element, as long as a press there reaches that element and not one over it.\n * An element out of view is first brought into it, by hovering it, as a press by ref would be.\n */\nconst aim = (page: Page, subject: Subject | undefined, point?: Point, recorded?: Box) => {\n if (subject === undefined) return Effect.fail(invalid(\"the step names no element\"));\n if (point === undefined) return Effect.map(locate(page, subject), ({ ref }): Target => ref);\n\n const reach = ({ ref, box }: Found) => {\n const at =\n recorded === undefined || recorded.width <= 0 || recorded.height <= 0\n ? point\n : {\n x: box.x + ((point.x - recorded.x) / recorded.width) * box.width,\n y: box.y + ((point.y - recorded.y) / recorded.height) * box.height,\n };\n\n return Effect.map(page.find({ at }), ([reached]) => (reached?.ref === ref ? at : undefined));\n };\n\n const missed = new Drifted({ detail: \"a press at the subject's point reaches another element\" });\n\n return locate(page, subject).pipe(\n Effect.flatMap((found) =>\n Effect.flatMap(reach(found), (at) =>\n at !== undefined\n ? Effect.succeed<Target>(at)\n : page.hover(found.ref).pipe(\n Effect.andThen(locate(page, subject)),\n Effect.flatMap(reach),\n Effect.flatMap((again) =>\n again === undefined ? Effect.fail(missed) : Effect.succeed<Target>(again),\n ),\n ),\n ),\n ),\n );\n};\n\nconst dispatch = (\n page: Page,\n step: Step,\n text: string,\n): Effect.Effect<void, Missing | Ambiguous | Drifted | BrowserError> => {\n const options = step.options ?? {};\n\n switch (step.action) {\n case \"navigate\":\n return page.goto(step.input === undefined ? (step.target ?? step.url) : text);\n case \"back\":\n return page.back;\n case \"reload\":\n return page.reload;\n case \"press\":\n return step.subject === undefined\n ? page.press(step.target ?? \"\", options)\n : Effect.flatMap(locate(page, step.subject), ({ ref }) =>\n page.press(step.target ?? \"\", { ...options, on: ref }),\n );\n case \"click\":\n return aim(page, step.subject, step.point, step.box).pipe(\n Effect.flatMap((at) => page.click(at, options)),\n Effect.asVoid,\n );\n case \"hover\":\n return Effect.flatMap(aim(page, step.subject, step.point, step.box), page.hover);\n case \"drag\":\n return Effect.all([\n aim(page, step.subject, step.point),\n aim(page, step.to, step.toPoint),\n ]).pipe(Effect.flatMap(([from, to]) => page.drag(from, to)));\n case \"select\":\n return aim(page, step.subject).pipe(\n Effect.flatMap((at) => page.select(typeof at === \"string\" ? at : \"\", options.values ?? [])),\n Effect.asVoid,\n );\n // A scroll or a typing step may name no element: the page, or whatever has focus.\n case \"scroll\":\n return step.subject === undefined\n ? page.scroll(options)\n : Effect.flatMap(aim(page, step.subject, step.point, step.box), (at) =>\n page.scroll({ ...options, at }),\n );\n case \"type\":\n return step.subject === undefined\n ? page.type(text, { ...options, secret: step.secret })\n : Effect.flatMap(aim(page, step.subject), (into) =>\n page.type(text, {\n ...options,\n into: typeof into === \"string\" ? into : undefined,\n secret: step.secret,\n }),\n );\n }\n};\n\nexport interface TakeOptions {\n /** How each step waits for the page first, or false not to wait. */\n readonly settle?: ReadyOptions | false | undefined;\n readonly inputs?: Readonly<Record<string, string>> | undefined;\n /**\n * Bound for recorded arrival and expected effect, each. Defaults to 5 seconds. Zero makes one\n * read without polling; that read retains the page operation's timeout.\n */\n readonly arrival?: Duration.Input | undefined;\n}\n\nexport interface ReplayOptions extends TakeOptions {\n readonly from?: number | Stopped | undefined;\n}\n\n/**\n * Input that can be repeated without repeating a commitment. `take` also validates a Checked\n * target's boolean state before input. Back/reload can resubmit a form.\n */\nexport const repeatable = (step: Step): boolean =>\n [\"navigate\", \"hover\", \"scroll\", \"select\"].includes(step.action) ||\n (step.action === \"type\" &&\n step.subject !== undefined &&\n step.options?.replace !== false &&\n step.options?.submit !== true) ||\n (step.action === \"click\" && step.expect?._tag === \"Checked\");\n\ntype StepError = Missing | Ambiguous | Drifted | BrowserError | Uncertain;\n\n// Zero waits make one read; positive waits bound all reads and never repeat input.\nconst within = (check: Effect.Effect<boolean, StepError>, millis: number) =>\n millis === 0\n ? check\n : check.pipe(\n Effect.repeat({ until: (held) => held, schedule: Schedule.spaced(Duration.millis(25)) }),\n Effect.timeoutOrElse({\n duration: Duration.millis(millis),\n orElse: () => Effect.succeed(false),\n }),\n );\n\nconst sameSite = (url: string, recorded: string) => {\n const origin = URL.parse(recorded)?.origin;\n\n return (\n recorded === \"\" ||\n (origin === undefined || origin === \"null\"\n ? url === recorded\n : URL.parse(url)?.origin === origin)\n );\n};\n\nconst arrived = (page: Page, urls: ReadonlyArray<string>, millis: number) =>\n within(\n Effect.map(page.url, (url) => urls.some((recorded) => sameSite(url, recorded))),\n millis,\n ).pipe(\n Effect.filterOrFail(\n (held) => held,\n () =>\n new Drifted({ detail: \"the page did not reach the recorded site within the arrival wait\" }),\n ),\n Effect.asVoid,\n );\n\nconst checkEffect = (\n page: Page,\n step: Step,\n first?: { readonly url: string; readonly document: number },\n) => {\n const expected = step.expect;\n\n if (expected === undefined) return Effect.succeed(false);\n switch (expected._tag) {\n case \"Checked\":\n return step.subject === undefined\n ? Effect.fail(invalid(\"a checked effect needs a subject\"))\n : Effect.flatMap(locate(page, step.subject), (found) =>\n found.state.checked === undefined\n ? Effect.fail(invalid(\"a checked effect needs a checkable subject\"))\n : Effect.succeed(found.state.checked === expected.checked),\n );\n case \"Navigated\":\n if (expected.url === undefined && first === undefined) return Effect.succeed(false);\n\n return Effect.map(page.state, (now) =>\n expected.url === undefined\n ? now.url !== first?.url || now.document !== first?.document\n : now.url === expected.url,\n );\n case \"Appeared\":\n case \"Gone\":\n return Effect.map(page.find({ ...expected.query, scope: \"document\" }), (found) =>\n expected._tag === \"Appeared\" ? found.length > 0 : found.length === 0,\n );\n }\n};\n\n/**\n * Take one step. Unknown non-repeatable input is never resent: only positive durable read-back\n * can skip it. An interrupted call remains interrupted; persist begun-before-call in the consumer\n * and treat a missing receipt as Stopped{input: \"unknown\"}, including after process restart.\n */\nexport const take = Effect.fn(\"Plan.take\")(function* (\n page: Page,\n plan: Plan,\n at: number | Stopped,\n options: TakeOptions = {},\n): Effect.fn.Return<Receipt, ReplayError> {\n const index = typeof at === \"number\" ? at : at.step;\n let input: Stopped[\"input\"] = typeof at === \"number\" ? \"unsent\" : at.input;\n\n const work = Effect.gen(function* (): Effect.fn.Return<Receipt, StepError> {\n const step = plan.steps[index];\n\n if (!Number.isInteger(index) || index < 0 || step === undefined)\n return yield* invalid(\"the step index is outside the plan\");\n const duration = Duration.fromInput(options.arrival ?? Duration.seconds(5));\n\n if (\n Option.isNone(duration) ||\n Duration.isNegative(duration.value) ||\n !Duration.isFinite(duration.value)\n )\n return yield* invalid(\"arrival must be a finite nonnegative duration\");\n const millis = Duration.toMillis(duration.value);\n const first = yield* page.state;\n\n const uncertain = new Uncertain({\n detail: \"the step may have been sent; reconcile its durable effect before continuing\",\n });\n\n const unresolved = input === \"unknown\" && !repeatable(step);\n const expected = step.expect;\n\n const durable =\n expected !== undefined &&\n (expected._tag === \"Appeared\" ||\n (expected._tag === \"Navigated\" && typeof expected.url === \"string\")) &&\n expected.durable === true;\n\n if (\n !durable &&\n (unresolved || (expected !== undefined && \"durable\" in expected && expected.durable === true))\n )\n return yield* uncertain;\n const settle = options.settle === false ? Effect.void : page.ready(options.settle);\n\n if (![\"navigate\", \"back\", \"reload\"].includes(step.action)) yield* settle;\n const before = plan.steps[index - 1]?.url;\n\n if (before !== undefined)\n yield* arrived(page, unresolved ? [before, step.url] : [before], millis);\n const check = checkEffect(page, step);\n\n const read =\n millis === 0\n ? check\n : check.pipe(\n Effect.timeoutOrElse({\n duration: Duration.millis(millis),\n orElse: () =>\n Effect.fail(new Drifted({ detail: \"the expected-effect read timed out\" })),\n }),\n );\n\n const held = yield* read.pipe(Effect.mapError((error) => (unresolved ? uncertain : error)));\n\n const receipt = (outcome: Receipt[\"outcome\"]) =>\n Effect.gen(function* () {\n if (index === plan.steps.length - 1) yield* settle;\n yield* arrived(page, [step.url], millis);\n const now = yield* page.state;\n\n return new Receipt({ step: index, outcome, startedAt: first.at, at: now.at });\n });\n\n if (held) return yield* receipt(\"skipped\");\n if (unresolved) return yield* uncertain;\n const text = step.input === undefined ? \"\" : options.inputs?.[step.input];\n\n if (text === undefined) return yield* invalid(`no text for the input \"${step.input ?? \"\"}\"`);\n const verify = checkEffect(page, step, yield* page.state);\n const acted = yield* Effect.exit(dispatch(page, step, text));\n\n if (Exit.isFailure(acted)) {\n const error = Exit.findErrorOption(acted);\n\n if (Option.isSome(error) && error.value._tag === \"BrowserError\" && error.value.dispatched) {\n input = \"unknown\";\n if (expected !== undefined && (repeatable(step) || durable)) {\n const observed = yield* Effect.exit(within(verify, millis));\n\n if (Exit.isSuccess(observed) && observed.value) return yield* receipt(\"verified\");\n }\n }\n\n return yield* Effect.failCause(acted.cause);\n }\n input = \"unknown\";\n if (expected !== undefined && !(yield* within(verify, millis)))\n return yield* new Drifted({ detail: \"the step's expected effect did not arrive\" });\n\n return yield* receipt(expected === undefined ? \"taken\" : \"verified\");\n });\n\n return yield* work.pipe(\n Effect.mapError(\n (reason) =>\n new ReplayError({\n step: index,\n reason,\n input,\n receipts: [],\n }),\n ),\n );\n});\n\nconst run = Effect.fnUntraced(function* (\n page: Page,\n plan: Plan,\n options: TakeOptions,\n from: number | Stopped,\n until: number,\n restoring: boolean,\n) {\n const receipts: Array<Receipt> = [];\n const start = typeof from === \"number\" ? from : from.step;\n\n if (\n !Number.isInteger(start) ||\n start < 0 ||\n start > plan.steps.length ||\n !Number.isInteger(until) ||\n until < start ||\n until > plan.steps.length\n )\n return yield* new ReplayError({\n step: start,\n input: \"unsent\",\n receipts,\n reason: invalid(\"the run bounds are outside the plan\"),\n });\n // Fresh runs still ask for all input slots before changing the page. Restoration checks first.\n if (!restoring && typeof from === \"number\") {\n const unfilled = plan.steps.findIndex(\n (step, index) =>\n index >= start && step.input !== undefined && options.inputs?.[step.input] === undefined,\n );\n\n if (unfilled !== -1)\n return yield* new ReplayError({\n step: start,\n input: \"unsent\",\n receipts,\n reason: invalid(`no text for the input \"${plan.steps[unfilled]?.input ?? \"\"}\"`),\n });\n }\n for (let index = start; index < until; index++) {\n const at = restoring\n ? new Stopped({ step: index, input: \"unknown\" })\n : index === start\n ? from\n : index;\n\n const receipt = yield* take(page, plan, at, options).pipe(\n Effect.mapError((error) => new ReplayError({ ...error, receipts: [...receipts] })),\n );\n\n receipts.push(receipt);\n }\n\n return receipts;\n});\n\n/** Take a plan's steps from a fresh index or durable stopping point, returning completed receipts. */\nexport const replay = (page: Page, plan: Plan, options: ReplayOptions = {}) =>\n run(page, plan, options, options.from ?? 0, plan.steps.length, false).pipe(\n Effect.withSpan(\"Plan.replay\"),\n );\n\n/** Restore prior steps on a fresh page; unresolved commitments require positive durable evidence. */\nexport const restore = (\n page: Page,\n plan: Plan,\n options: TakeOptions & { readonly upTo?: number | undefined } = {},\n) =>\n run(page, plan, options, 0, options.upTo ?? plan.steps.length, true).pipe(\n Effect.withSpan(\"Plan.restore\"),\n );\n"],"mappings":"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"}
|
package/dist/Plan.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as take, a as ReplayError, b as Missing, c as StepAction, d as Uncertain, f as fromEvents, g as restore, h as replay, i as Receipt, l as Stopped, m as repeatable, n as Plan, o as ReplayOptions, p as locate, s as Step, t as Expected, u as TakeOptions, v as Ambiguous, y as Drifted } from "./Plan-
|
|
1
|
+
import { _ as take, a as ReplayError, b as Missing, c as StepAction, d as Uncertain, f as fromEvents, g as restore, h as replay, i as Receipt, l as Stopped, m as repeatable, n as Plan, o as ReplayOptions, p as locate, s as Step, t as Expected, u as TakeOptions, v as Ambiguous, y as Drifted } from "./Plan-BS0jpENY.mjs";
|
|
2
2
|
export { Ambiguous, Drifted, Expected, Missing, Plan, Receipt, ReplayError, ReplayOptions, Step, StepAction, Stopped, TakeOptions, Uncertain, fromEvents, locate, repeatable, replay, restore, take };
|
package/dist/Plan.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as Drifted, a as ReplayError, c as Stopped, d as locate, f as repeatable, g as Ambiguous, h as take, i as Receipt, l as Uncertain, m as restore, n as Plan, o as Step, p as replay, s as StepAction, t as Expected, u as fromEvents, v as Missing } from "./Plan-
|
|
1
|
+
import { _ as Drifted, a as ReplayError, c as Stopped, d as locate, f as repeatable, g as Ambiguous, h as take, i as Receipt, l as Uncertain, m as restore, n as Plan, o as Step, p as replay, s as StepAction, t as Expected, u as fromEvents, v as Missing } from "./Plan-Bl2NM1D5.mjs";
|
|
2
2
|
export { Ambiguous, Drifted, Expected, Missing, Plan, Receipt, ReplayError, Step, StepAction, Stopped, Uncertain, fromEvents, locate, repeatable, replay, restore, take };
|
package/dist/Presentation.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { lognormal } from "./Motion.mjs";
|
|
3
|
-
import { a as performed, i as pause, r as viewOf, t as internalsOf } from "./page-
|
|
3
|
+
import { a as performed, i as pause, r as viewOf, t as internalsOf } from "./page-ZxMhw16l.mjs";
|
|
4
4
|
import { Clock, Duration, Effect, Fiber, MutableRef, Option, Semaphore } from "effect";
|
|
5
5
|
//#region src/Presentation.ts
|
|
6
6
|
/**
|
package/dist/Stage.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { Closed, InvalidRequest, Timeout, consequence } from "./BrowserError.mjs";
|
|
3
|
-
import { i as failWith, s as undispatched } from "./context-
|
|
4
|
-
import { t as internalsOf } from "./page-
|
|
3
|
+
import { i as failWith, s as undispatched } from "./context-BAjRj7Xo.mjs";
|
|
4
|
+
import { t as internalsOf } from "./page-ZxMhw16l.mjs";
|
|
5
5
|
import { Cause, Clock, Deferred, Duration, Effect, Exit, Fiber, Option, PubSub, Result, Schedule, Schema, Scope, Stream } from "effect";
|
|
6
6
|
//#region src/Stage.ts
|
|
7
7
|
/**
|
|
@@ -207,11 +207,11 @@ const make = Effect.fn("Stage.make")(function* (options = {}) {
|
|
|
207
207
|
}),
|
|
208
208
|
changedAt: at
|
|
209
209
|
};
|
|
210
|
-
return publish(feed, frame).pipe(Effect.
|
|
210
|
+
return publish(feed, frame).pipe(Effect.as(showing.presented));
|
|
211
211
|
}).pipe(Effect.uninterruptible);
|
|
212
212
|
const readyToSwitch = firstOf(feed, firstFrame).pipe(Effect.andThen(switchTo), Effect.provideService(Clock.Clock, clock));
|
|
213
213
|
const before = old === void 0 ? void 0 : internalsOf(old.feed.page);
|
|
214
|
-
return yield* before === void 0 ? readyToSwitch : before.afterInput(readyToSwitch);
|
|
214
|
+
return yield* (before === void 0 ? readyToSwitch : before.afterInput(readyToSwitch)).pipe(Effect.ensuring(Effect.suspend(() => showing?.feed === feed && old !== void 0 ? stop(old.feed) : Effect.void)));
|
|
215
215
|
}).pipe(Effect.provideService(Clock.Clock, clock)));
|
|
216
216
|
});
|
|
217
217
|
const cue = (page) => Effect.map(openFeed(page, owner, clock, settings, () => showing?.feed, publish), (feed) => ({
|
package/dist/Stage.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stage.mjs","names":[],"sources":["../src/Stage.ts"],"sourcesContent":["/**\n * An output's capture ownership. Cue a page while preparing an event; commit when that event\n * happens. A prepared cue belongs to its caller's scope, and a committed one to the stage.\n * Commits serialize, drain old-page input and refuse stale first frames. `shown` distinguishes\n * confirmed silence, uncertain silence and loss without treating capture silence as evidence.\n * @since 0.3.0\n */\nimport {\n Cause,\n Clock,\n Deferred,\n Duration,\n Effect,\n Exit,\n Fiber,\n Option,\n PubSub,\n Result,\n Schedule,\n Schema,\n Scope,\n Stream,\n} from \"effect\";\n\nimport { type BrowserError, Closed, consequence, InvalidRequest, Timeout } from \"./BrowserError.ts\";\nimport type { CaptureStats, Frame, ScreencastOptions } from \"./Frame.ts\";\nimport { failWith, undispatched } from \"./internal/page/context.ts\";\nimport { internalsOf } from \"./internal/page/page.ts\";\nimport type { Page } from \"./Page.ts\";\n\nexport interface Options extends ScreencastOptions {\n /** Bound for each first-frame wait. Defaults to 5 seconds. */\n readonly firstFrame?: Duration.Input | undefined;\n}\n\n/** A switch that took effect, on the clock shared by browser events and frames. */\nexport class Presented extends Schema.Class<Presented>(\"effect-browser/Presented\")({\n page: Schema.String,\n at: Schema.Finite,\n latency: Schema.Finite,\n}) {}\n\nexport interface Cue {\n readonly page: Page;\n /** A frame of the current document, or the capture's failure or first-frame timeout. */\n readonly ready: Effect.Effect<void, BrowserError>;\n /** Commit on the event's host time, or now. Interruption before switching leaves the cue usable. */\n readonly commit: (options?: {\n readonly at?: number | undefined;\n }) => Effect.Effect<Presented, BrowserError>;\n}\n\n/** What the output establishes about its last image; loss ends this page's evidence. */\nexport type Shown =\n | { readonly _tag: \"Painted\"; readonly frame: Frame }\n | { readonly _tag: \"Still\"; readonly frame: Frame; readonly at: number }\n | { readonly _tag: \"Stalled\"; readonly frame: Frame; readonly at: number }\n | { readonly _tag: \"Gone\"; readonly page: string; readonly at: number };\n\nexport interface Stage {\n readonly present: (\n page: Page,\n options?: { readonly at?: number | undefined },\n ) => Effect.Effect<Presented, BrowserError>;\n readonly cue: (page: Page) => Effect.Effect<Cue, BrowserError, Scope.Scope>;\n /** Paint and, after every finite positive hold without paint, current evidence or loss. */\n readonly shown: (options: { readonly hold: Duration.Input }) => Stream.Stream<Shown>;\n /** Current-page paint; a reader more than 16 frames behind loses the oldest. */\n readonly frames: Stream.Stream<Frame>;\n readonly current: Effect.Effect<Option.Option<Page>>;\n readonly stats: (options?: {\n readonly window?: Duration.Input | undefined;\n }) => Effect.Effect<Option.Option<CaptureStats>, BrowserError>;\n}\n\ntype FeedState =\n | { readonly _tag: \"Warming\" }\n | { readonly _tag: \"Ready\"; readonly latest: Frame | undefined }\n | { readonly _tag: \"Stopped\" };\ninterface Feed {\n readonly page: Page;\n readonly scope: Scope.Closeable;\n readonly changed: PubSub.PubSub<Frame>;\n state: FeedState;\n fiber: Fiber.Fiber<void, BrowserError> | undefined;\n}\ninterface Paint {\n readonly feed: Feed;\n readonly frame: Frame;\n}\n\nconst closed = () => undispatched(\"cue\", new Closed({ cause: \"released\" }));\n\n/** The sole writer of a cue's capture lifecycle; presentation ownership belongs to the stage. */\nconst update = (\n feed: Feed,\n input: { readonly _tag: \"Paint\"; readonly frame: Frame } | { readonly _tag: \"Reset\" | \"Stop\" },\n) => {\n if (feed.state._tag === \"Stopped\") return;\n if (input._tag === \"Stop\") feed.state = { _tag: \"Stopped\" };\n else if (input._tag === \"Paint\") feed.state = { _tag: \"Ready\", latest: input.frame };\n else if (feed.state._tag === \"Ready\") feed.state = { _tag: \"Ready\", latest: undefined };\n};\n\nconst restartable = (error: BrowserError) => {\n const { lost, repeat } = consequence(error);\n\n return lost === \"nothing\" && repeat !== \"pointless\";\n};\n\nconst stop = (feed: Feed) =>\n Effect.suspend(() => {\n update(feed, { _tag: \"Stop\" });\n\n return Scope.close(feed.scope, Exit.void);\n });\n\nconst frameOf = (feed: Feed) => {\n const frame = feed.state._tag === \"Ready\" ? feed.state.latest : undefined;\n const internals = internalsOf(feed.page);\n\n return frame !== undefined && (internals === undefined || internals.current(frame))\n ? frame\n : undefined;\n};\n\nconst ended = (feed: Feed): Effect.Effect<never, BrowserError> =>\n feed.fiber === undefined\n ? Effect.never\n : Fiber.await(feed.fiber).pipe(\n Effect.flatMap((exit) =>\n Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)\n ? Effect.failCause(exit.cause)\n : Effect.fail(closed()),\n ),\n );\n\nconst firstOf = (feed: Feed, wait: Duration.Duration) =>\n Effect.suspend(() => {\n if (feed.state._tag === \"Stopped\") return Effect.fail(closed());\n if (feed.page.playwright.isClosed()) return failWith(\"cue\", new Closed({ cause: \"page\" }));\n if (frameOf(feed) !== undefined) return Effect.void;\n\n return Effect.raceFirst(\n Stream.fromPubSub(feed.changed).pipe(\n Stream.filter(() => frameOf(feed) !== undefined),\n Stream.runHead,\n Effect.asVoid,\n ),\n ended(feed),\n ).pipe(\n Effect.timeoutOrElse({\n duration: wait,\n orElse: () => failWith(\"cue\", new Timeout({ millis: Duration.toMillis(wait) })),\n }),\n );\n });\n\n// A timer can wake a fraction early; a commit never precedes its event.\nconst until = (at: number, now: () => number): Effect.Effect<void> =>\n Effect.suspend(() =>\n at > now()\n ? Effect.sleep(Duration.millis(Math.ceil(at - now()))).pipe(Effect.andThen(until(at, now)))\n : Effect.void,\n );\n\n/** Reserve the next turn before waking it; a fresh caller cannot overtake a queued commit. */\nconst ordered = () => {\n const waiting: Array<Deferred.Deferred<void>> = [];\n\n return <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.acquireUseRelease(\n Effect.sync(() => {\n const turn = Deferred.makeUnsafe<void>();\n\n waiting.push(turn);\n if (waiting.length === 1) Deferred.doneUnsafe(turn, Exit.void);\n\n return turn;\n }),\n (turn) => Deferred.await(turn).pipe(Effect.andThen(effect)),\n (turn) =>\n Effect.sync(() => {\n const at = waiting.indexOf(turn);\n\n waiting.splice(at, 1);\n if (at === 0 && waiting[0] !== undefined) Deferred.doneUnsafe(waiting[0], Exit.void);\n }),\n );\n};\n\n/** A terminal capture can know the connection is gone before the page receives its close event. */\nconst confirm = (feed: Feed, frame: Frame) => {\n const exit = feed.fiber?.pollUnsafe();\n\n const failure =\n exit !== undefined && Exit.isFailure(exit) ? Cause.findErrorOption(exit.cause) : Option.none();\n\n if (Option.isSome(failure) && consequence(failure.value).lost !== \"nothing\")\n return Effect.fail(failure.value);\n\n return feed.page.playwright.isClosed()\n ? failWith(\"shown\", new Closed({ cause: \"page\" }))\n : (internalsOf(feed.page)?.confirm(frame) ?? Effect.succeed(false));\n};\n\n/** Each subscriber confirms silence; a new frame or commit invalidates a probe. */\nconst evidence = (\n output: PubSub.PubSub<Paint>,\n current: () =>\n | { readonly feed: Feed; readonly frame: Frame; readonly changedAt: number }\n | undefined,\n now: () => number,\n hold: Duration.Input,\n): Stream.Stream<Shown> =>\n Stream.unwrap(\n Effect.sync(() => {\n const parsed = Duration.fromInput(hold);\n\n if (\n Option.isNone(parsed) ||\n !Duration.isFinite(parsed.value) ||\n !Duration.isPositive(parsed.value)\n )\n throw new RangeError(\"shown hold must be a finite positive duration\");\n const millis = Duration.toMillis(parsed.value);\n let checkedAt = now();\n let gone: Feed | undefined;\n\n const probe = Effect.gen(function* () {\n yield* until(Math.max(checkedAt, current()?.changedAt ?? checkedAt) + millis, now);\n checkedAt = now();\n const before = current();\n const frame = before?.frame;\n\n if (\n before === undefined ||\n frame === undefined ||\n gone === before.feed ||\n checkedAt - before.changedAt < millis\n )\n return Option.none<Shown>();\n const { feed } = before;\n\n const confirmed = yield* Effect.result(confirm(feed, frame));\n\n if (current()?.feed !== feed || current()?.frame !== frame) return Option.none<Shown>();\n const at = now();\n\n checkedAt = at;\n if (Result.isFailure(confirmed) && consequence(confirmed.failure).lost !== \"nothing\") {\n gone = feed;\n\n return Option.some<Shown>({ _tag: \"Gone\", page: feed.page.id, at });\n }\n\n return Option.some<Shown>({\n _tag: Result.isSuccess(confirmed) && confirmed.success ? \"Still\" : \"Stalled\",\n frame,\n at,\n });\n });\n\n return Stream.merge(\n Stream.fromPubSub(output).pipe(\n Stream.filter(({ feed }) => gone !== feed),\n Stream.map(({ frame }): Shown => ({ _tag: \"Painted\", frame })),\n ),\n Stream.fromEffectRepeat(probe).pipe(\n Stream.filter(Option.isSome),\n Stream.map(({ value }) => value),\n ),\n );\n }),\n );\n\nconst openFeed = (\n page: Page,\n owner: Scope.Scope,\n clock: Clock.Clock,\n settings: ScreencastOptions,\n current: () => Feed | undefined,\n publish: (feed: Feed, frame: Frame) => Effect.Effect<unknown>,\n) =>\n Effect.gen(function* () {\n const scope = yield* Scope.fork(owner);\n const started = yield* Deferred.make<void>();\n\n const feed: Feed = {\n page,\n scope,\n changed: yield* PubSub.sliding<Frame>({ capacity: 1, replay: 1 }),\n state: { _tag: \"Warming\" },\n fiber: undefined,\n };\n\n yield* Scope.addFinalizer(\n scope,\n Effect.sync(() => {\n update(feed, { _tag: \"Stop\" });\n }),\n );\n yield* Effect.addFinalizer(() => (current() === feed ? Effect.void : stop(feed)));\n\n const accept = (frame: Frame) =>\n Effect.suspend(() => {\n update(feed, { _tag: \"Paint\", frame });\n\n return (current() === feed ? publish(feed, frame) : Effect.void).pipe(\n Effect.andThen(PubSub.publish(feed.changed, frame)),\n );\n });\n\n const capture = Effect.scoped(\n Effect.gen(function* () {\n update(feed, { _tag: \"Reset\" });\n\n const internals = internalsOf(page);\n\n const { stream, current: frame } = yield* (\n internals?.capture(settings) ??\n Effect.succeed({ stream: page.screencast(settings), current: undefined })\n );\n\n if (frame !== undefined && internals?.current(frame)) yield* accept(frame);\n yield* Deferred.succeed(started, undefined);\n yield* Stream.runForEach(stream, accept);\n }),\n ).pipe(\n Effect.ensuring(Effect.sync(() => update(feed, { _tag: \"Reset\" }))),\n Effect.retry({\n while: (error) => feed.state._tag === \"Ready\" && restartable(error),\n schedule: Schedule.spaced(\"500 millis\"),\n }),\n );\n\n feed.fiber = yield* capture.pipe(\n Effect.provideService(Clock.Clock, clock),\n Effect.forkIn(scope),\n );\n yield* Effect.raceFirst(Deferred.await(started), ended(feed)).pipe(\n Effect.onError(() => stop(feed)),\n );\n\n return feed;\n });\n\nexport const make = Effect.fn(\"Stage.make\")(function* (\n options: Options = {},\n): Effect.fn.Return<Stage, BrowserError, Scope.Scope> {\n const clock = yield* Clock.Clock;\n const owner = yield* Effect.scope;\n const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;\n const waited = Duration.fromInput(options.firstFrame ?? Duration.seconds(5));\n\n if (\n Option.isNone(waited) ||\n !Duration.isFinite(waited.value) ||\n !Duration.isPositive(waited.value)\n )\n return yield* failWith(\n \"stage\",\n new InvalidRequest({ detail: \"firstFrame must be a finite positive duration\" }),\n );\n const firstFrame = waited.value;\n const settings = { quality: options.quality, size: options.size };\n const output = yield* PubSub.sliding<Paint>(16);\n const switching = ordered();\n\n let showing:\n | { readonly feed: Feed; readonly presented: Presented; frame: Frame; changedAt: number }\n | undefined;\n\n let painted = Number.NEGATIVE_INFINITY;\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(output));\n\n const publish = (feed: Feed, frame: Frame) =>\n Effect.suspend(() => {\n painted = Math.max(painted, frame.hostTime);\n if (showing !== undefined) {\n showing.changedAt = now();\n showing.frame = frame;\n }\n\n return PubSub.publish(output, { feed, frame });\n });\n\n const commit = (feed: Feed, options: { readonly at?: number | undefined } = {}) =>\n Effect.suspend(() => {\n const asked = options.at ?? now();\n\n return switching(\n Effect.gen(function* () {\n if (!Number.isFinite(asked))\n return yield* failWith(\n \"cue\",\n new InvalidRequest({ detail: \"at must be finite host milliseconds\" }),\n );\n yield* firstOf(feed, firstFrame);\n const old = showing;\n\n if (old?.feed === feed) return old.presented;\n\n yield* Effect.raceFirst(until(asked, now), ended(feed));\n\n const switchTo = Effect.suspend(() => {\n const frame = frameOf(feed);\n\n if (frame === undefined) return Effect.fail(closed());\n const at = Math.max(now(), asked, frame.hostTime, painted);\n\n showing = {\n feed,\n frame,\n presented: new Presented({ page: feed.page.id, at, latency: at - asked }),\n changedAt: at,\n };\n\n return publish(feed, frame).pipe(\n Effect.andThen(old === undefined ? Effect.void : stop(old.feed)),\n Effect.as(showing.presented),\n );\n }).pipe(Effect.uninterruptible);\n\n const readyToSwitch = firstOf(feed, firstFrame).pipe(\n Effect.andThen(switchTo),\n Effect.provideService(Clock.Clock, clock),\n );\n\n const before = old === undefined ? undefined : internalsOf(old.feed.page);\n\n return yield* before === undefined ? readyToSwitch : before.afterInput(readyToSwitch);\n }).pipe(Effect.provideService(Clock.Clock, clock)),\n );\n });\n\n const cue: Stage[\"cue\"] = (page) =>\n Effect.map(\n openFeed(page, owner, clock, settings, () => showing?.feed, publish),\n (feed) => ({\n page,\n ready: firstOf(feed, firstFrame).pipe(Effect.provideService(Clock.Clock, clock)),\n commit: (options) => commit(feed, options),\n }),\n );\n\n return {\n cue,\n present: (page, options) =>\n Effect.suspend(() => {\n const asked = options?.at ?? now();\n\n return showing?.feed.page === page\n ? Effect.as(firstOf(showing.feed, firstFrame), showing.presented)\n : Effect.scoped(Effect.flatMap(cue(page), (prepared) => prepared.commit({ at: asked })));\n }),\n shown: ({ hold }) =>\n evidence(output, () => showing, now, hold).pipe(Stream.provideService(Clock.Clock, clock)),\n frames: Stream.fromPubSub(output).pipe(Stream.map(({ frame }) => frame)),\n current: Effect.sync(() => Option.fromNullishOr(showing?.feed.page)),\n stats: (options = {}) =>\n showing === undefined\n ? Effect.succeedNone\n : Effect.asSome(showing.feed.page.captureStats({ window: options.window ?? \"1 second\" })),\n } satisfies Stage;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Stage.mjs","names":[],"sources":["../src/Stage.ts"],"sourcesContent":["/**\n * An output's capture ownership. Cue a page while preparing an event; commit when that event\n * happens. A prepared cue belongs to its caller's scope, and a committed one to the stage.\n * Commits serialize, drain old-page input and refuse stale first frames. `shown` distinguishes\n * confirmed silence, uncertain silence and loss without treating capture silence as evidence.\n * @since 0.3.0\n */\nimport {\n Cause,\n Clock,\n Deferred,\n Duration,\n Effect,\n Exit,\n Fiber,\n Option,\n PubSub,\n Result,\n Schedule,\n Schema,\n Scope,\n Stream,\n} from \"effect\";\n\nimport { type BrowserError, Closed, consequence, InvalidRequest, Timeout } from \"./BrowserError.ts\";\nimport type { CaptureStats, Frame, ScreencastOptions } from \"./Frame.ts\";\nimport { failWith, undispatched } from \"./internal/page/context.ts\";\nimport { internalsOf } from \"./internal/page/page.ts\";\nimport type { Page } from \"./Page.ts\";\n\nexport interface Options extends ScreencastOptions {\n /** Bound for each first-frame wait. Defaults to 5 seconds. */\n readonly firstFrame?: Duration.Input | undefined;\n}\n\n/** A switch that took effect, on the clock shared by browser events and frames. */\nexport class Presented extends Schema.Class<Presented>(\"effect-browser/Presented\")({\n page: Schema.String,\n at: Schema.Finite,\n latency: Schema.Finite,\n}) {}\n\nexport interface Cue {\n readonly page: Page;\n /** A frame of the current document, or the capture's failure or first-frame timeout. */\n readonly ready: Effect.Effect<void, BrowserError>;\n /** Commit on the event's host time, or now. Interruption before switching leaves the cue usable. */\n readonly commit: (options?: {\n readonly at?: number | undefined;\n }) => Effect.Effect<Presented, BrowserError>;\n}\n\n/** What the output establishes about its last image; loss ends this page's evidence. */\nexport type Shown =\n | { readonly _tag: \"Painted\"; readonly frame: Frame }\n | { readonly _tag: \"Still\"; readonly frame: Frame; readonly at: number }\n | { readonly _tag: \"Stalled\"; readonly frame: Frame; readonly at: number }\n | { readonly _tag: \"Gone\"; readonly page: string; readonly at: number };\n\nexport interface Stage {\n readonly present: (\n page: Page,\n options?: { readonly at?: number | undefined },\n ) => Effect.Effect<Presented, BrowserError>;\n readonly cue: (page: Page) => Effect.Effect<Cue, BrowserError, Scope.Scope>;\n /** Paint and, after every finite positive hold without paint, current evidence or loss. */\n readonly shown: (options: { readonly hold: Duration.Input }) => Stream.Stream<Shown>;\n /** Current-page paint; a reader more than 16 frames behind loses the oldest. */\n readonly frames: Stream.Stream<Frame>;\n readonly current: Effect.Effect<Option.Option<Page>>;\n readonly stats: (options?: {\n readonly window?: Duration.Input | undefined;\n }) => Effect.Effect<Option.Option<CaptureStats>, BrowserError>;\n}\n\ntype FeedState =\n | { readonly _tag: \"Warming\" }\n | { readonly _tag: \"Ready\"; readonly latest: Frame | undefined }\n | { readonly _tag: \"Stopped\" };\ninterface Feed {\n readonly page: Page;\n readonly scope: Scope.Closeable;\n readonly changed: PubSub.PubSub<Frame>;\n state: FeedState;\n fiber: Fiber.Fiber<void, BrowserError> | undefined;\n}\ninterface Paint {\n readonly feed: Feed;\n readonly frame: Frame;\n}\n\nconst closed = () => undispatched(\"cue\", new Closed({ cause: \"released\" }));\n\n/** The sole writer of a cue's capture lifecycle; presentation ownership belongs to the stage. */\nconst update = (\n feed: Feed,\n input: { readonly _tag: \"Paint\"; readonly frame: Frame } | { readonly _tag: \"Reset\" | \"Stop\" },\n) => {\n if (feed.state._tag === \"Stopped\") return;\n if (input._tag === \"Stop\") feed.state = { _tag: \"Stopped\" };\n else if (input._tag === \"Paint\") feed.state = { _tag: \"Ready\", latest: input.frame };\n else if (feed.state._tag === \"Ready\") feed.state = { _tag: \"Ready\", latest: undefined };\n};\n\nconst restartable = (error: BrowserError) => {\n const { lost, repeat } = consequence(error);\n\n return lost === \"nothing\" && repeat !== \"pointless\";\n};\n\nconst stop = (feed: Feed) =>\n Effect.suspend(() => {\n update(feed, { _tag: \"Stop\" });\n\n return Scope.close(feed.scope, Exit.void);\n });\n\nconst frameOf = (feed: Feed) => {\n const frame = feed.state._tag === \"Ready\" ? feed.state.latest : undefined;\n const internals = internalsOf(feed.page);\n\n return frame !== undefined && (internals === undefined || internals.current(frame))\n ? frame\n : undefined;\n};\n\nconst ended = (feed: Feed): Effect.Effect<never, BrowserError> =>\n feed.fiber === undefined\n ? Effect.never\n : Fiber.await(feed.fiber).pipe(\n Effect.flatMap((exit) =>\n Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)\n ? Effect.failCause(exit.cause)\n : Effect.fail(closed()),\n ),\n );\n\nconst firstOf = (feed: Feed, wait: Duration.Duration) =>\n Effect.suspend(() => {\n if (feed.state._tag === \"Stopped\") return Effect.fail(closed());\n if (feed.page.playwright.isClosed()) return failWith(\"cue\", new Closed({ cause: \"page\" }));\n if (frameOf(feed) !== undefined) return Effect.void;\n\n return Effect.raceFirst(\n Stream.fromPubSub(feed.changed).pipe(\n Stream.filter(() => frameOf(feed) !== undefined),\n Stream.runHead,\n Effect.asVoid,\n ),\n ended(feed),\n ).pipe(\n Effect.timeoutOrElse({\n duration: wait,\n orElse: () => failWith(\"cue\", new Timeout({ millis: Duration.toMillis(wait) })),\n }),\n );\n });\n\n// A timer can wake a fraction early; a commit never precedes its event.\nconst until = (at: number, now: () => number): Effect.Effect<void> =>\n Effect.suspend(() =>\n at > now()\n ? Effect.sleep(Duration.millis(Math.ceil(at - now()))).pipe(Effect.andThen(until(at, now)))\n : Effect.void,\n );\n\n/** Reserve the next turn before waking it; a fresh caller cannot overtake a queued commit. */\nconst ordered = () => {\n const waiting: Array<Deferred.Deferred<void>> = [];\n\n return <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.acquireUseRelease(\n Effect.sync(() => {\n const turn = Deferred.makeUnsafe<void>();\n\n waiting.push(turn);\n if (waiting.length === 1) Deferred.doneUnsafe(turn, Exit.void);\n\n return turn;\n }),\n (turn) => Deferred.await(turn).pipe(Effect.andThen(effect)),\n (turn) =>\n Effect.sync(() => {\n const at = waiting.indexOf(turn);\n\n waiting.splice(at, 1);\n if (at === 0 && waiting[0] !== undefined) Deferred.doneUnsafe(waiting[0], Exit.void);\n }),\n );\n};\n\n/** A terminal capture can know the connection is gone before the page receives its close event. */\nconst confirm = (feed: Feed, frame: Frame) => {\n const exit = feed.fiber?.pollUnsafe();\n\n const failure =\n exit !== undefined && Exit.isFailure(exit) ? Cause.findErrorOption(exit.cause) : Option.none();\n\n if (Option.isSome(failure) && consequence(failure.value).lost !== \"nothing\")\n return Effect.fail(failure.value);\n\n return feed.page.playwright.isClosed()\n ? failWith(\"shown\", new Closed({ cause: \"page\" }))\n : (internalsOf(feed.page)?.confirm(frame) ?? Effect.succeed(false));\n};\n\n/** Each subscriber confirms silence; a new frame or commit invalidates a probe. */\nconst evidence = (\n output: PubSub.PubSub<Paint>,\n current: () =>\n | { readonly feed: Feed; readonly frame: Frame; readonly changedAt: number }\n | undefined,\n now: () => number,\n hold: Duration.Input,\n): Stream.Stream<Shown> =>\n Stream.unwrap(\n Effect.sync(() => {\n const parsed = Duration.fromInput(hold);\n\n if (\n Option.isNone(parsed) ||\n !Duration.isFinite(parsed.value) ||\n !Duration.isPositive(parsed.value)\n )\n throw new RangeError(\"shown hold must be a finite positive duration\");\n const millis = Duration.toMillis(parsed.value);\n let checkedAt = now();\n let gone: Feed | undefined;\n\n const probe = Effect.gen(function* () {\n yield* until(Math.max(checkedAt, current()?.changedAt ?? checkedAt) + millis, now);\n checkedAt = now();\n const before = current();\n const frame = before?.frame;\n\n if (\n before === undefined ||\n frame === undefined ||\n gone === before.feed ||\n checkedAt - before.changedAt < millis\n )\n return Option.none<Shown>();\n const { feed } = before;\n\n const confirmed = yield* Effect.result(confirm(feed, frame));\n\n if (current()?.feed !== feed || current()?.frame !== frame) return Option.none<Shown>();\n const at = now();\n\n checkedAt = at;\n if (Result.isFailure(confirmed) && consequence(confirmed.failure).lost !== \"nothing\") {\n gone = feed;\n\n return Option.some<Shown>({ _tag: \"Gone\", page: feed.page.id, at });\n }\n\n return Option.some<Shown>({\n _tag: Result.isSuccess(confirmed) && confirmed.success ? \"Still\" : \"Stalled\",\n frame,\n at,\n });\n });\n\n return Stream.merge(\n Stream.fromPubSub(output).pipe(\n Stream.filter(({ feed }) => gone !== feed),\n Stream.map(({ frame }): Shown => ({ _tag: \"Painted\", frame })),\n ),\n Stream.fromEffectRepeat(probe).pipe(\n Stream.filter(Option.isSome),\n Stream.map(({ value }) => value),\n ),\n );\n }),\n );\n\nconst openFeed = (\n page: Page,\n owner: Scope.Scope,\n clock: Clock.Clock,\n settings: ScreencastOptions,\n current: () => Feed | undefined,\n publish: (feed: Feed, frame: Frame) => Effect.Effect<unknown>,\n) =>\n Effect.gen(function* () {\n const scope = yield* Scope.fork(owner);\n const started = yield* Deferred.make<void>();\n\n const feed: Feed = {\n page,\n scope,\n changed: yield* PubSub.sliding<Frame>({ capacity: 1, replay: 1 }),\n state: { _tag: \"Warming\" },\n fiber: undefined,\n };\n\n yield* Scope.addFinalizer(\n scope,\n Effect.sync(() => {\n update(feed, { _tag: \"Stop\" });\n }),\n );\n yield* Effect.addFinalizer(() => (current() === feed ? Effect.void : stop(feed)));\n\n const accept = (frame: Frame) =>\n Effect.suspend(() => {\n update(feed, { _tag: \"Paint\", frame });\n\n return (current() === feed ? publish(feed, frame) : Effect.void).pipe(\n Effect.andThen(PubSub.publish(feed.changed, frame)),\n );\n });\n\n const capture = Effect.scoped(\n Effect.gen(function* () {\n update(feed, { _tag: \"Reset\" });\n\n const internals = internalsOf(page);\n\n const { stream, current: frame } = yield* (\n internals?.capture(settings) ??\n Effect.succeed({ stream: page.screencast(settings), current: undefined })\n );\n\n if (frame !== undefined && internals?.current(frame)) yield* accept(frame);\n yield* Deferred.succeed(started, undefined);\n yield* Stream.runForEach(stream, accept);\n }),\n ).pipe(\n Effect.ensuring(Effect.sync(() => update(feed, { _tag: \"Reset\" }))),\n Effect.retry({\n while: (error) => feed.state._tag === \"Ready\" && restartable(error),\n schedule: Schedule.spaced(\"500 millis\"),\n }),\n );\n\n feed.fiber = yield* capture.pipe(\n Effect.provideService(Clock.Clock, clock),\n Effect.forkIn(scope),\n );\n yield* Effect.raceFirst(Deferred.await(started), ended(feed)).pipe(\n Effect.onError(() => stop(feed)),\n );\n\n return feed;\n });\n\nexport const make = Effect.fn(\"Stage.make\")(function* (\n options: Options = {},\n): Effect.fn.Return<Stage, BrowserError, Scope.Scope> {\n const clock = yield* Clock.Clock;\n const owner = yield* Effect.scope;\n const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;\n const waited = Duration.fromInput(options.firstFrame ?? Duration.seconds(5));\n\n if (\n Option.isNone(waited) ||\n !Duration.isFinite(waited.value) ||\n !Duration.isPositive(waited.value)\n )\n return yield* failWith(\n \"stage\",\n new InvalidRequest({ detail: \"firstFrame must be a finite positive duration\" }),\n );\n const firstFrame = waited.value;\n const settings = { quality: options.quality, size: options.size };\n const output = yield* PubSub.sliding<Paint>(16);\n const switching = ordered();\n\n let showing:\n | { readonly feed: Feed; readonly presented: Presented; frame: Frame; changedAt: number }\n | undefined;\n\n let painted = Number.NEGATIVE_INFINITY;\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(output));\n\n const publish = (feed: Feed, frame: Frame) =>\n Effect.suspend(() => {\n painted = Math.max(painted, frame.hostTime);\n if (showing !== undefined) {\n showing.changedAt = now();\n showing.frame = frame;\n }\n\n return PubSub.publish(output, { feed, frame });\n });\n\n const commit = (feed: Feed, options: { readonly at?: number | undefined } = {}) =>\n Effect.suspend(() => {\n const asked = options.at ?? now();\n\n return switching(\n Effect.gen(function* () {\n if (!Number.isFinite(asked))\n return yield* failWith(\n \"cue\",\n new InvalidRequest({ detail: \"at must be finite host milliseconds\" }),\n );\n yield* firstOf(feed, firstFrame);\n const old = showing;\n\n if (old?.feed === feed) return old.presented;\n\n yield* Effect.raceFirst(until(asked, now), ended(feed));\n\n const switchTo = Effect.suspend(() => {\n const frame = frameOf(feed);\n\n if (frame === undefined) return Effect.fail(closed());\n const at = Math.max(now(), asked, frame.hostTime, painted);\n\n showing = {\n feed,\n frame,\n presented: new Presented({ page: feed.page.id, at, latency: at - asked }),\n changedAt: at,\n };\n\n return publish(feed, frame).pipe(Effect.as(showing.presented));\n }).pipe(Effect.uninterruptible);\n\n const readyToSwitch = firstOf(feed, firstFrame).pipe(\n Effect.andThen(switchTo),\n Effect.provideService(Clock.Clock, clock),\n );\n\n const before = old === undefined ? undefined : internalsOf(old.feed.page);\n\n return yield* (\n before === undefined ? readyToSwitch : before.afterInput(readyToSwitch)\n ).pipe(\n // The cut owns old-page input through publication. Capture cleanup has its own bound\n // and must finish even if the caller leaves, without turning a completed cut into Timeout.\n Effect.ensuring(\n Effect.suspend(() =>\n showing?.feed === feed && old !== undefined ? stop(old.feed) : Effect.void,\n ),\n ),\n );\n }).pipe(Effect.provideService(Clock.Clock, clock)),\n );\n });\n\n const cue: Stage[\"cue\"] = (page) =>\n Effect.map(\n openFeed(page, owner, clock, settings, () => showing?.feed, publish),\n (feed) => ({\n page,\n ready: firstOf(feed, firstFrame).pipe(Effect.provideService(Clock.Clock, clock)),\n commit: (options) => commit(feed, options),\n }),\n );\n\n return {\n cue,\n present: (page, options) =>\n Effect.suspend(() => {\n const asked = options?.at ?? now();\n\n return showing?.feed.page === page\n ? Effect.as(firstOf(showing.feed, firstFrame), showing.presented)\n : Effect.scoped(Effect.flatMap(cue(page), (prepared) => prepared.commit({ at: asked })));\n }),\n shown: ({ hold }) =>\n evidence(output, () => showing, now, hold).pipe(Stream.provideService(Clock.Clock, clock)),\n frames: Stream.fromPubSub(output).pipe(Stream.map(({ frame }) => frame)),\n current: Effect.sync(() => Option.fromNullishOr(showing?.feed.page)),\n stats: (options = {}) =>\n showing === undefined\n ? Effect.succeedNone\n : Effect.asSome(showing.feed.page.captureStats({ window: options.window ?? \"1 second\" })),\n } satisfies Stage;\n});\n"],"mappings":"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"}
|
|
@@ -224,6 +224,7 @@ const make = (options, scope) => {
|
|
|
224
224
|
duration,
|
|
225
225
|
orElse: () => failWith(operation, new Timeout({ millis: Duration.toMillis(duration) }))
|
|
226
226
|
});
|
|
227
|
+
const stopLoading = native("stopLoading", () => send("Page.stopLoading")).pipe(within("stopLoading", options.settings.navigationTimeout), Effect.ignore);
|
|
227
228
|
const activity = {
|
|
228
229
|
inputAt: 0,
|
|
229
230
|
documentAt: Number.NEGATIVE_INFINITY
|
|
@@ -251,6 +252,7 @@ const make = (options, scope) => {
|
|
|
251
252
|
protocol,
|
|
252
253
|
native,
|
|
253
254
|
within,
|
|
255
|
+
stopLoading,
|
|
254
256
|
activity,
|
|
255
257
|
noteInput
|
|
256
258
|
};
|
|
@@ -258,4 +260,4 @@ const make = (options, scope) => {
|
|
|
258
260
|
//#endregion
|
|
259
261
|
export { make as a, Current as c, failWith as i, make$1 as l, contextGone as n, reasonOf as o, decodeWith as r, undispatched as s, call as t };
|
|
260
262
|
|
|
261
|
-
//# sourceMappingURL=context-
|
|
263
|
+
//# sourceMappingURL=context-BAjRj7Xo.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-B6xr8nVi.mjs","names":["make","Url.redactWithin","Lane.make"],"sources":["../src/internal/input/replies.ts","../src/internal/page/context.ts"],"sourcesContent":["/**\n * Bounded input replies owned by the page, including after an action is interrupted.\n * Canceling a caller cannot cancel a protocol command already sent. The next action waits for\n * those replies, while a stopped run releases only held keys or buttons whose release was never submitted.\n */\nimport { Context, Data, Effect } from \"effect\";\n\nimport { maximumSamples } from \"../../Motion.ts\";\n\nexport const capacity = 64;\n\nexport class InputFailure extends Data.TaggedError(\"InputFailure\")<{\n readonly cause: unknown;\n}> {}\n\nexport interface Run {\n /** A native call made by this operation: cancellation cannot cancel its actual reply. */\n readonly track: <A>(reply: Promise<A>) => Promise<A>;\n /** End ownership after the record and all this operation's native replies have settled. */\n readonly afterIdle: (ended: () => void) => void;\n /** Reserve a complete stroke, including its releases, before sending its first event. */\n readonly reserve: (commands: number) => Effect.Effect<void, InputFailure>;\n /** Admit every sample before a motion starts its published clock; ordinary input stays bounded separately. */\n readonly reserveMotion: (commands: number) => Effect.Effect<void, InputFailure>;\n readonly send: (command: () => Promise<unknown>) => Effect.Effect<void, InputFailure>;\n readonly down: (\n key: string,\n press: () => Promise<unknown>,\n release: () => Promise<unknown>,\n ) => Effect.Effect<void, InputFailure>;\n readonly up: (key: string) => Effect.Effect<void, InputFailure>;\n readonly drain: Effect.Effect<void, InputFailure>;\n /** Stop future input and submit missing releases without waiting indefinitely for replies. */\n readonly close: Effect.Effect<void>;\n}\n\n/** Native page calls join their operation's reply ownership, including script mutations. */\nexport const Current = Context.Reference<Run | undefined>(\"effect-browser/internal/input/Current\", {\n defaultValue: () => undefined,\n});\n\nexport const make = () => {\n const outstanding = new Set<Promise<void>>();\n\n // A timed-out action keeps capacity until its real replies arrive. Waiting for them sends\n // nothing, so cancellation here cannot leave a detached continuation that later types.\n const idle = Effect.gen(function* () {\n while (outstanding.size > 0) yield* Effect.promise(() => Promise.all(outstanding));\n });\n\n const begin = Effect.gen(function* () {\n yield* idle;\n const pending = new Set<Promise<void>>();\n const held: Array<{ readonly key: string; readonly release: () => Promise<unknown> }> = [];\n let failure: InputFailure | undefined;\n let reserved = 0;\n let closed = false;\n const observed = new WeakMap<Promise<unknown>, { input: boolean }>();\n const endings = new Set<() => void>();\n\n const track = <A>(response: Promise<A>, input = false): Promise<A> => {\n const existing = observed.get(response);\n\n if (existing !== undefined) {\n existing.input ||= input;\n\n return response;\n }\n const entry = { input };\n\n observed.set(response, entry);\n\n const reply = response\n .then(\n () => undefined,\n (cause: unknown) => {\n if (entry.input) failure ??= new InputFailure({ cause });\n },\n )\n .then(() => {\n pending.delete(reply);\n outstanding.delete(reply);\n if (pending.size === 0) {\n for (const end of endings) end();\n endings.clear();\n }\n });\n\n pending.add(reply);\n outstanding.add(reply);\n\n return response;\n };\n\n const requireOpen = Effect.suspend(() =>\n closed\n ? Effect.fail(new InputFailure({ cause: new Error(\"the input run has stopped\") }))\n : Effect.void,\n );\n\n const requireSuccess = Effect.suspend(() =>\n failure === undefined ? Effect.void : Effect.fail(failure),\n );\n\n const submit = (command: () => Promise<unknown>) => {\n if (reserved < 1) throw new Error(\"input must reserve capacity before dispatch\");\n reserved--;\n let response: Promise<unknown>;\n\n try {\n response = command();\n } catch (cause) {\n failure ??= new InputFailure({ cause });\n\n return;\n }\n\n void track(response, true);\n };\n\n const reserve = (commands: number, maximum: number, limit: number) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n if (!Number.isInteger(commands) || commands < 1 || commands > maximum)\n return yield* Effect.die(new Error(\"invalid input reservation\"));\n while (outstanding.size + reserved + commands > limit) {\n yield* Effect.promise(() => Promise.race(outstanding));\n yield* requireOpen;\n yield* requireSuccess;\n }\n reserved += commands;\n });\n\n const run: Run = {\n track,\n afterIdle: (end) => {\n if (pending.size === 0) end();\n else endings.add(end);\n },\n reserve: (commands) => reserve(commands, capacity, capacity),\n // A dense stroke cannot wait for replies midway without changing its published timing.\n // Its finite reservation also leaves room for the ordinary pool's already-owned releases.\n reserveMotion: (commands) => reserve(commands, maximumSamples, capacity + maximumSamples),\n send: (command) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n yield* Effect.sync(() => submit(command));\n yield* requireSuccess;\n }),\n down: (key, press, release) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n yield* Effect.sync(() => {\n held.push({ key, release });\n submit(press);\n });\n yield* requireSuccess;\n }),\n up: (key) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* Effect.sync(() => {\n const index = held.findLastIndex((entry) => entry.key === key);\n const entry = held[index];\n\n if (entry === undefined) return;\n // Submission owns the release, even if its reply fails. Retrying an uncertain\n // release could run page handlers twice, so cleanup never sends it again.\n held.splice(index, 1);\n submit(entry.release);\n });\n yield* requireSuccess;\n }),\n drain: Effect.gen(function* () {\n while (pending.size > 0) yield* Effect.promise(() => Promise.all(pending));\n yield* requireSuccess;\n }),\n close: Effect.sync(() => {\n if (closed) return;\n closed = true;\n for (const entry of held.toReversed()) submit(entry.release);\n held.length = 0;\n reserved = 0;\n }),\n };\n\n return run;\n });\n\n /** `idle` lets an action wait, in its page's turn, for the page's earlier replies. */\n return { idle, begin };\n};\n","/**\n * What a page's parts share: the browser state they act on, the page's own lane, pointer and\n * clock, and how they call the browser and report its failures. The assembly in `page.ts` builds\n * one context per page.\n */\nimport {\n Clock,\n Context,\n Duration,\n Effect,\n Fiber,\n MutableRef,\n Option,\n Schema,\n type Scope,\n} from \"effect\";\nimport type { CDPSession, Page as PlaywrightPage } from \"playwright-core\";\n\nimport type { CaptureSource } from \"../../Browser.ts\";\nimport { BrowserError, Closed, Failed, type Reason, Timeout } from \"../../BrowserError.ts\";\nimport type { BrowserEvent } from \"../../BrowserEvent.ts\";\nimport type { InputGuard, Point } from \"../../Page.ts\";\nimport type { Gate } from \"../input/gate.ts\";\nimport { Current } from \"../input/replies.ts\";\nimport type * as BrowserClock from \"../pictures/clock.ts\";\nimport * as Lane from \"./lane.ts\";\nimport type { Openings } from \"./openings.ts\";\nimport * as Url from \"./url.ts\";\n\n/** Why a page is gone. */\nexport type ClosedCause = Closed[\"cause\"];\n\n/** What every page of a browser shares, from its options. */\nexport interface Settings {\n readonly actionTimeout: Duration.Duration;\n readonly policyTimeout: Duration.Duration;\n readonly navigationTimeout: Duration.Duration;\n /** How long screencast frames stay in `recentFrames`, measured back from the newest. */\n readonly frameHistory: Duration.Duration;\n readonly guard: InputGuard | undefined;\n}\n\nexport interface MakeOptions {\n /** The page's CDP target id, which is also its main frame's id. */\n readonly id: string;\n /** The id of the browser's session, which its frames carry. */\n readonly session: string;\n /** This tracking connection, distinct from the provider session and capture socket. */\n readonly connection: string;\n /** The page's address when the browser began tracking it. */\n readonly url: string;\n readonly playwright: PlaywrightPage;\n readonly cdp: CDPSession;\n /** A call's reply, or a failure as for a closed page once the browser is lost first. */\n readonly untilLost: <A>(reply: Promise<A>) => Promise<A>;\n readonly settings: Settings;\n readonly clock: Clock.Clock;\n /** Browser-wide input authority; direct internal page fixtures need not provide one. */\n readonly inputGate?: Gate | undefined;\n /** The browser's epoch mapping: its first capture measures it, and later ones renew it. */\n readonly mapping: BrowserClock.Mapping;\n readonly publish: (event: BrowserEvent) => number;\n /** This page's retained events, oldest first. */\n readonly recentEvents: Effect.Effect<ReadonlyArray<BrowserEvent>>;\n /** Registration of the actual popups this page's current input opened. */\n readonly openings: Openings;\n /** Succeeds once the page's own session holds focus emulation, which keeps it painting behind. */\n readonly focused: Effect.Effect<void, BrowserError>;\n /** Succeeds once the page's own session has the Page domain on, so it sees every later commit. */\n readonly paging: (operation: string) => Effect.Effect<void, BrowserError>;\n /** Why the page is gone, once it is: its browser's loss, else its own close or crash. */\n readonly closedBy: () => ClosedCause;\n /** Where the page's screencast runs, if not on its own session. */\n readonly capture?: CaptureSource | undefined;\n}\n\nconst messageOf = (cause: unknown): string =>\n cause instanceof Error ? cause.message : typeof cause === \"string\" ? cause : \"unknown error\";\n\nconst closedPattern =\n /has been closed|Target closed|Session closed|browser has disconnected|Target page, context or browser/i;\n\n/**\n * Map a Playwright or protocol failure to a reason, `Closed` with `closed` as its cause for a page\n * or browser that is gone. A call that gives Playwright a timeout passes the same bound, so its\n * `Timeout` reports it; without one, Playwright's own message stays.\n */\nexport const reasonOf = (\n cause: unknown,\n timeoutMillis?: number,\n closed: ClosedCause = \"connection\",\n): Reason => {\n const message = messageOf(cause);\n\n if (closedPattern.test(message)) return new Closed({ cause: closed });\n if (cause instanceof Error && cause.name === \"TimeoutError\" && timeoutMillis !== undefined)\n return new Timeout({ millis: timeoutMillis });\n const line = message.split(\"\\n\")[0] ?? message;\n\n return new Failed({ detail: Url.redactWithin(line.replace(/^[\\w.]+: /, \"\")) });\n};\n\n/**\n * One Playwright or protocol call, failing undispatched. A closed page says why it is gone as the\n * browser knows then: the library's own calls fail with the browser's loss, and Playwright fails\n * its calls on a dropped connection after reporting the context closed.\n */\nexport const call = <A>(\n operation: string,\n run: () => Promise<A>,\n closedBy: () => ClosedCause,\n timeoutMillis?: number,\n) =>\n Effect.tryPromise({\n try: run,\n catch: (cause) => undispatched(operation, reasonOf(cause, timeoutMillis, closedBy())),\n });\n\nexport const contextGone = (error: BrowserError) =>\n error.reason._tag === \"Failed\" &&\n /Cannot find context|Execution context was destroyed|__effectBrowser|Inspected target navigated/i.test(\n error.reason.detail,\n );\n\n/**\n * What a page operation spent on the page's own protocol session: its calls, the bytes of their\n * parameters and results, and how long at least one of them awaited its reply. An operation inside\n * another counts toward both.\n */\ninterface Cost {\n readonly enclosing: Cost | undefined;\n calls: number;\n bytesOut: number;\n bytesIn: number;\n waitedMillis: number;\n awaiting: number;\n awaitingSince: number;\n}\n\n/** The cost of the page operation the current fiber runs, if any. */\nconst CurrentCost = Context.Reference<Cost | undefined>(\n \"effect-browser/internal/page/CurrentCost\",\n {\n defaultValue: () => undefined,\n },\n);\n\n/** Track the promise returned to the action, after tracing wraps it, so input counts once. */\nconst track = <A>(reply: Promise<A>) => Fiber.getCurrent()?.getRef(Current)?.track(reply) ?? reply;\n\n// A message's size on the wire, give or take its envelope.\nconst sizeOf = (value: unknown) =>\n value === undefined ? 0 : Buffer.byteLength(JSON.stringify(value));\n\n/** A failure of `operation` before any of its input reached the browser. */\nexport const undispatched = (operation: string, reason: Reason) =>\n new BrowserError({ operation, reason, dispatched: false });\n\nexport const failWith = (operation: string, reason: Reason) =>\n Effect.fail(undispatched(operation, reason));\n\nexport const decodeWith =\n <A>(operation: string, schema: Schema.Codec<A, unknown>) =>\n (value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(\n Effect.mapError((error) => undispatched(operation, new Failed({ detail: error.message }))),\n );\n\nexport const make = (options: MakeOptions, scope: Scope.Scope) => {\n const { id, clock } = options;\n const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;\n // Deadlines and pacing guard the page's lane, so page operations run on the owner's clock; a\n // caller's clock, such as a TestClock, cannot stall or stretch them.\n const owned = Effect.provideService(Clock.Clock, clock);\n\n // This page's spans name it and report what the operation cost on the protocol. Fine-grained\n // ones carry a level below the default Info, so an application keeps only the coarse ones by\n // raising `Tracer.MinimumTraceLevel`.\n const span =\n (name: string, attributes: Record<string, unknown> = {}, level?: \"Debug\" | \"Trace\") =>\n <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.gen(function* () {\n const cost: Cost = {\n enclosing: yield* CurrentCost,\n calls: 0,\n bytesOut: 0,\n bytesIn: 0,\n waitedMillis: 0,\n awaiting: 0,\n awaitingSince: 0,\n };\n\n return yield* effect.pipe(\n Effect.provideService(CurrentCost, cost),\n Effect.ensuring(\n Effect.suspend(() =>\n Effect.annotateCurrentSpan({\n calls: cost.calls,\n bytesOut: cost.bytesOut,\n bytesIn: cost.bytesIn,\n waitedMillis: Math.round(cost.waitedMillis * 10) / 10,\n }),\n ),\n ),\n );\n }).pipe(\n Effect.withSpan(\n name,\n { attributes: { page: id, ...attributes }, ...(level === undefined ? {} : { level }) },\n { captureStackTrace: false },\n ),\n );\n\n // Every call on the page's own protocol session, counted into the operation that sends it and\n // the operations around that one. It is looked up when the call is sent, so input pipelined\n // from inside an operation counts toward it too.\n const send: CDPSession[\"send\"] = (method, params) => {\n const cost = Fiber.getCurrent()?.getRef(CurrentCost);\n const sentAt = now();\n const bytesOut = sizeOf(params);\n\n for (let each = cost; each !== undefined; each = each.enclosing) {\n each.calls++;\n each.bytesOut += bytesOut;\n if (each.awaiting++ === 0) each.awaitingSince = sentAt;\n }\n const reply = options.untilLost(options.cdp.send(method, params));\n\n if (cost === undefined) return track(reply);\n\n const settle = (bytesIn: number) => {\n const at = now();\n\n for (let each: Cost | undefined = cost; each !== undefined; each = each.enclosing) {\n each.bytesIn += bytesIn;\n if (--each.awaiting === 0) each.waitedMillis += at - each.awaitingSince;\n }\n };\n\n return track(\n reply.then(\n (result) => {\n settle(sizeOf(result));\n\n return result;\n },\n (cause: unknown) => {\n settle(0);\n\n return Promise.reject(cause);\n },\n ),\n );\n };\n\n // Every protocol or Playwright call. Errors are undispatched here; `perform` marks them\n // dispatched once input has gone out.\n const native = <A>(operation: string, run: () => Promise<A>, timeoutMillis?: number) =>\n call(operation, () => track(run()), options.closedBy, timeoutMillis);\n\n /** Fail with `Timeout` once `duration` has passed, the action timeout unless another is given. */\n const within =\n (operation: string, duration: Duration.Input = options.settings.actionTimeout) =>\n <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.timeoutOrElse(effect, {\n duration,\n orElse: () => failWith(operation, new Timeout({ millis: Duration.toMillis(duration) })),\n });\n\n // The latest submitted input or page change. A read that must follow it waits for the action in\n // flight, so only paint from after its latest input is current. An action ends after its input\n // was handled, so if that input changed the page, newer paint follows; a lost final paint is\n // bounded by recency instead. Paint from before the current document began, when the page's own\n // session saw the main frame commit it, shows a page the tab has left, so no read reuses it,\n // whatever it asks.\n const activity = { inputAt: 0, documentAt: Number.NEGATIVE_INFINITY };\n\n const noteInput = () => {\n activity.inputAt = Math.max(activity.inputAt, now());\n };\n\n const lane = Lane.make({\n now,\n actionTimeout: options.settings.actionTimeout,\n documentAt: () => activity.documentAt,\n gone: (operation) => undispatched(operation, new Closed({ cause: options.closedBy() })),\n scope,\n inputGate: options.inputGate,\n });\n\n // Where this page's latest move left its pointer, which is where its next plain glide starts and\n // where a held button is released. Its lane orders the page's input, and Chromium keeps a\n // pointer per page too.\n const pointer = MutableRef.make(Option.none<Point>());\n\n // Named, so that the declarations of what holds it need not spell out Playwright's protocol types.\n const protocol: Pick<CDPSession, \"send\"> = { send };\n\n return {\n ...options,\n lane,\n pointer,\n now,\n owned,\n span,\n protocol,\n native,\n within,\n activity,\n noteInput,\n };\n};\n\nexport type PageContext = ReturnType<typeof make>;\n"],"mappings":";;;;AAWA,IAAa,eAAb,cAAkC,KAAK,YAAY,cAAc,CAAC,CAE/D,CAAC;;AAwBJ,MAAa,UAAU,QAAQ,UAA2B,yCAAyC,EACjG,oBAAoB,KAAA,EACtB,CAAC;AAED,MAAaA,eAAa;CACxB,MAAM,8BAAc,IAAI,IAAmB;CAI3C,MAAM,OAAO,OAAO,IAAI,aAAa;EACnC,OAAO,YAAY,OAAO,GAAG,OAAO,OAAO,cAAc,QAAQ,IAAI,WAAW,CAAC;CACnF,CAAC;;CAiJD,OAAO;EAAE;EAAM,OA/ID,OAAO,IAAI,aAAa;GACpC,OAAO;GACP,MAAM,0BAAU,IAAI,IAAmB;GACvC,MAAM,OAAkF,CAAC;GACzF,IAAI;GACJ,IAAI,WAAW;GACf,IAAI,SAAS;GACb,MAAM,2BAAW,IAAI,QAA8C;GACnE,MAAM,0BAAU,IAAI,IAAgB;GAEpC,MAAM,SAAY,UAAsB,QAAQ,UAAsB;IACpE,MAAM,WAAW,SAAS,IAAI,QAAQ;IAEtC,IAAI,aAAa,KAAA,GAAW;KAC1B,SAAS,UAAU;KAEnB,OAAO;IACT;IACA,MAAM,QAAQ,EAAE,MAAM;IAEtB,SAAS,IAAI,UAAU,KAAK;IAE5B,MAAM,QAAQ,SACX,WACO,KAAA,IACL,UAAmB;KAClB,IAAI,MAAM,OAAO,YAAY,IAAI,aAAa,EAAE,MAAM,CAAC;IACzD,CACF,CAAC,CACA,WAAW;KACV,QAAQ,OAAO,KAAK;KACpB,YAAY,OAAO,KAAK;KACxB,IAAI,QAAQ,SAAS,GAAG;MACtB,KAAK,MAAM,OAAO,SAAS,IAAI;MAC/B,QAAQ,MAAM;KAChB;IACF,CAAC;IAEH,QAAQ,IAAI,KAAK;IACjB,YAAY,IAAI,KAAK;IAErB,OAAO;GACT;GAEA,MAAM,cAAc,OAAO,cACzB,SACI,OAAO,KAAK,IAAI,aAAa,EAAE,uBAAO,IAAI,MAAM,2BAA2B,EAAE,CAAC,CAAC,IAC/E,OAAO,IACb;GAEA,MAAM,iBAAiB,OAAO,cAC5B,YAAY,KAAA,IAAY,OAAO,OAAO,OAAO,KAAK,OAAO,CAC3D;GAEA,MAAM,UAAU,YAAoC;IAClD,IAAI,WAAW,GAAG,MAAM,IAAI,MAAM,6CAA6C;IAC/E;IACA,IAAI;IAEJ,IAAI;KACF,WAAW,QAAQ;IACrB,SAAS,OAAO;KACd,YAAY,IAAI,aAAa,EAAE,MAAM,CAAC;KAEtC;IACF;IAEA,MAAW,UAAU,IAAI;GAC3B;GAEA,MAAM,WAAW,UAAkB,SAAiB,UAClD,OAAO,IAAI,aAAa;IACtB,OAAO;IACP,OAAO;IACP,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,KAAK,WAAW,SAC5D,OAAO,OAAO,OAAO,oBAAI,IAAI,MAAM,2BAA2B,CAAC;IACjE,OAAO,YAAY,OAAO,WAAW,WAAW,OAAO;KACrD,OAAO,OAAO,cAAc,QAAQ,KAAK,WAAW,CAAC;KACrD,OAAO;KACP,OAAO;IACT;IACA,YAAY;GACd,CAAC;GAyDH,OAAO;IAtDL;IACA,YAAY,QAAQ;KAClB,IAAI,QAAQ,SAAS,GAAG,IAAI;UACvB,QAAQ,IAAI,GAAG;IACtB;IACA,UAAU,aAAa,QAAQ,UAAA,IAAA,EAA4B;IAG3D,gBAAgB,aAAa,QAAQ,UAAU,gBAAA,KAA2B,cAAc;IACxF,OAAO,YACL,OAAO,IAAI,aAAa;KACtB,OAAO;KACP,OAAO;KACP,OAAO,OAAO,WAAW,OAAO,OAAO,CAAC;KACxC,OAAO;IACT,CAAC;IACH,OAAO,KAAK,OAAO,YACjB,OAAO,IAAI,aAAa;KACtB,OAAO;KACP,OAAO;KACP,OAAO,OAAO,WAAW;MACvB,KAAK,KAAK;OAAE;OAAK;MAAQ,CAAC;MAC1B,OAAO,KAAK;KACd,CAAC;KACD,OAAO;IACT,CAAC;IACH,KAAK,QACH,OAAO,IAAI,aAAa;KACtB,OAAO;KACP,OAAO,OAAO,WAAW;MACvB,MAAM,QAAQ,KAAK,eAAe,UAAU,MAAM,QAAQ,GAAG;MAC7D,MAAM,QAAQ,KAAK;MAEnB,IAAI,UAAU,KAAA,GAAW;MAGzB,KAAK,OAAO,OAAO,CAAC;MACpB,OAAO,MAAM,OAAO;KACtB,CAAC;KACD,OAAO;IACT,CAAC;IACH,OAAO,OAAO,IAAI,aAAa;KAC7B,OAAO,QAAQ,OAAO,GAAG,OAAO,OAAO,cAAc,QAAQ,IAAI,OAAO,CAAC;KACzE,OAAO;IACT,CAAC;IACD,OAAO,OAAO,WAAW;KACvB,IAAI,QAAQ;KACZ,SAAS;KACT,KAAK,MAAM,SAAS,KAAK,WAAW,GAAG,OAAO,MAAM,OAAO;KAC3D,KAAK,SAAS;KACd,WAAW;IACb,CAAC;GAGM;EACX,CAGmB;CAAE;AACvB;;;;;;;;ACtHA,MAAM,aAAa,UACjB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,UAAU,WAAW,QAAQ;AAE/E,MAAM,gBACJ;;;;;;AAOF,MAAa,YACX,OACA,eACA,SAAsB,iBACX;CACX,MAAM,UAAU,UAAU,KAAK;CAE/B,IAAI,cAAc,KAAK,OAAO,GAAG,OAAO,IAAI,OAAO,EAAE,OAAO,OAAO,CAAC;CACpE,IAAI,iBAAiB,SAAS,MAAM,SAAS,kBAAkB,kBAAkB,KAAA,GAC/E,OAAO,IAAI,QAAQ,EAAE,QAAQ,cAAc,CAAC;CAC9C,MAAM,OAAO,QAAQ,MAAM,IAAI,CAAC,CAAC,MAAM;CAEvC,OAAO,IAAI,OAAO,EAAE,QAAQC,aAAiB,KAAK,QAAQ,aAAa,EAAE,CAAC,EAAE,CAAC;AAC/E;;;;;;AAOA,MAAa,QACX,WACA,KACA,UACA,kBAEA,OAAO,WAAW;CAChB,KAAK;CACL,QAAQ,UAAU,aAAa,WAAW,SAAS,OAAO,eAAe,SAAS,CAAC,CAAC;AACtF,CAAC;AAEH,MAAa,eAAe,UAC1B,MAAM,OAAO,SAAS,YACtB,kGAAkG,KAChG,MAAM,OAAO,MACf;;AAkBF,MAAM,cAAc,QAAQ,UAC1B,4CACA,EACE,oBAAoB,KAAA,EACtB,CACF;;AAGA,MAAM,SAAY,UAAsB,MAAM,WAAW,CAAC,EAAE,OAAO,OAAO,CAAC,EAAE,MAAM,KAAK,KAAK;AAG7F,MAAM,UAAU,UACd,UAAU,KAAA,IAAY,IAAI,OAAO,WAAW,KAAK,UAAU,KAAK,CAAC;;AAGnE,MAAa,gBAAgB,WAAmB,WAC9C,IAAI,aAAa;CAAE;CAAW;CAAQ,YAAY;AAAM,CAAC;AAE3D,MAAa,YAAY,WAAmB,WAC1C,OAAO,KAAK,aAAa,WAAW,MAAM,CAAC;AAE7C,MAAa,cACP,WAAmB,YACtB,UACC,OAAO,oBAAoB,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KACxC,OAAO,UAAU,UAAU,aAAa,WAAW,IAAI,OAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,CAAC,CAAC,CAC3F;AAEJ,MAAa,QAAQ,SAAsB,UAAuB;CAChE,MAAM,EAAE,IAAI,UAAU;CACtB,MAAM,YAAY,OAAO,MAAM,yBAAyB,CAAC,IAAI;CAG7D,MAAM,QAAQ,OAAO,eAAe,MAAM,OAAO,KAAK;CAKtD,MAAM,QACH,MAAc,aAAsC,CAAC,GAAG,WAC/C,WACR,OAAO,IAAI,aAAa;EACtB,MAAM,OAAa;GACjB,WAAW,OAAO;GAClB,OAAO;GACP,UAAU;GACV,SAAS;GACT,cAAc;GACd,UAAU;GACV,eAAe;EACjB;EAEA,OAAO,OAAO,OAAO,KACnB,OAAO,eAAe,aAAa,IAAI,GACvC,OAAO,SACL,OAAO,cACL,OAAO,oBAAoB;GACzB,OAAO,KAAK;GACZ,UAAU,KAAK;GACf,SAAS,KAAK;GACd,cAAc,KAAK,MAAM,KAAK,eAAe,EAAE,IAAI;EACrD,CAAC,CACH,CACF,CACF;CACF,CAAC,CAAC,CAAC,KACD,OAAO,SACL,MACA;EAAE,YAAY;GAAE,MAAM;GAAI,GAAG;EAAW;EAAG,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;CAAG,GACrF,EAAE,mBAAmB,MAAM,CAC7B,CACF;CAKJ,MAAM,QAA4B,QAAQ,WAAW;EACnD,MAAM,OAAO,MAAM,WAAW,CAAC,EAAE,OAAO,WAAW;EACnD,MAAM,SAAS,IAAI;EACnB,MAAM,WAAW,OAAO,MAAM;EAE9B,KAAK,IAAI,OAAO,MAAM,SAAS,KAAA,GAAW,OAAO,KAAK,WAAW;GAC/D,KAAK;GACL,KAAK,YAAY;GACjB,IAAI,KAAK,eAAe,GAAG,KAAK,gBAAgB;EAClD;EACA,MAAM,QAAQ,QAAQ,UAAU,QAAQ,IAAI,KAAK,QAAQ,MAAM,CAAC;EAEhE,IAAI,SAAS,KAAA,GAAW,OAAO,MAAM,KAAK;EAE1C,MAAM,UAAU,YAAoB;GAClC,MAAM,KAAK,IAAI;GAEf,KAAK,IAAI,OAAyB,MAAM,SAAS,KAAA,GAAW,OAAO,KAAK,WAAW;IACjF,KAAK,WAAW;IAChB,IAAI,EAAE,KAAK,aAAa,GAAG,KAAK,gBAAgB,KAAK,KAAK;GAC5D;EACF;EAEA,OAAO,MACL,MAAM,MACH,WAAW;GACV,OAAO,OAAO,MAAM,CAAC;GAErB,OAAO;EACT,IACC,UAAmB;GAClB,OAAO,CAAC;GAER,OAAO,QAAQ,OAAO,KAAK;EAC7B,CACF,CACF;CACF;CAIA,MAAM,UAAa,WAAmB,KAAuB,kBAC3D,KAAK,iBAAiB,MAAM,IAAI,CAAC,GAAG,QAAQ,UAAU,aAAa;;CAGrE,MAAM,UACH,WAAmB,WAA2B,QAAQ,SAAS,mBACtD,WACR,OAAO,cAAc,QAAQ;EAC3B;EACA,cAAc,SAAS,WAAW,IAAI,QAAQ,EAAE,QAAQ,SAAS,SAAS,QAAQ,EAAE,CAAC,CAAC;CACxF,CAAC;CAQL,MAAM,WAAW;EAAE,SAAS;EAAG,YAAY,OAAO;CAAkB;CAEpE,MAAM,kBAAkB;EACtB,SAAS,UAAU,KAAK,IAAI,SAAS,SAAS,IAAI,CAAC;CACrD;CAEA,MAAM,OAAOC,OAAU;EACrB;EACA,eAAe,QAAQ,SAAS;EAChC,kBAAkB,SAAS;EAC3B,OAAO,cAAc,aAAa,WAAW,IAAI,OAAO,EAAE,OAAO,QAAQ,SAAS,EAAE,CAAC,CAAC;EACtF;EACA,WAAW,QAAQ;CACrB,CAAC;CAKD,MAAM,UAAU,WAAW,KAAK,OAAO,KAAY,CAAC;CAGpD,MAAM,WAAqC,EAAE,KAAK;CAElD,OAAO;EACL,GAAG;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"context-BAjRj7Xo.mjs","names":["make","Url.redactWithin","Lane.make"],"sources":["../src/internal/input/replies.ts","../src/internal/page/context.ts"],"sourcesContent":["/**\n * Bounded input replies owned by the page, including after an action is interrupted.\n * Canceling a caller cannot cancel a protocol command already sent. The next action waits for\n * those replies, while a stopped run releases only held keys or buttons whose release was never submitted.\n */\nimport { Context, Data, Effect } from \"effect\";\n\nimport { maximumSamples } from \"../../Motion.ts\";\n\nexport const capacity = 64;\n\nexport class InputFailure extends Data.TaggedError(\"InputFailure\")<{\n readonly cause: unknown;\n}> {}\n\nexport interface Run {\n /** A native call made by this operation: cancellation cannot cancel its actual reply. */\n readonly track: <A>(reply: Promise<A>) => Promise<A>;\n /** End ownership after the record and all this operation's native replies have settled. */\n readonly afterIdle: (ended: () => void) => void;\n /** Reserve a complete stroke, including its releases, before sending its first event. */\n readonly reserve: (commands: number) => Effect.Effect<void, InputFailure>;\n /** Admit every sample before a motion starts its published clock; ordinary input stays bounded separately. */\n readonly reserveMotion: (commands: number) => Effect.Effect<void, InputFailure>;\n readonly send: (command: () => Promise<unknown>) => Effect.Effect<void, InputFailure>;\n readonly down: (\n key: string,\n press: () => Promise<unknown>,\n release: () => Promise<unknown>,\n ) => Effect.Effect<void, InputFailure>;\n readonly up: (key: string) => Effect.Effect<void, InputFailure>;\n readonly drain: Effect.Effect<void, InputFailure>;\n /** Stop future input and submit missing releases without waiting indefinitely for replies. */\n readonly close: Effect.Effect<void>;\n}\n\n/** Native page calls join their operation's reply ownership, including script mutations. */\nexport const Current = Context.Reference<Run | undefined>(\"effect-browser/internal/input/Current\", {\n defaultValue: () => undefined,\n});\n\nexport const make = () => {\n const outstanding = new Set<Promise<void>>();\n\n // A timed-out action keeps capacity until its real replies arrive. Waiting for them sends\n // nothing, so cancellation here cannot leave a detached continuation that later types.\n const idle = Effect.gen(function* () {\n while (outstanding.size > 0) yield* Effect.promise(() => Promise.all(outstanding));\n });\n\n const begin = Effect.gen(function* () {\n yield* idle;\n const pending = new Set<Promise<void>>();\n const held: Array<{ readonly key: string; readonly release: () => Promise<unknown> }> = [];\n let failure: InputFailure | undefined;\n let reserved = 0;\n let closed = false;\n const observed = new WeakMap<Promise<unknown>, { input: boolean }>();\n const endings = new Set<() => void>();\n\n const track = <A>(response: Promise<A>, input = false): Promise<A> => {\n const existing = observed.get(response);\n\n if (existing !== undefined) {\n existing.input ||= input;\n\n return response;\n }\n const entry = { input };\n\n observed.set(response, entry);\n\n const reply = response\n .then(\n () => undefined,\n (cause: unknown) => {\n if (entry.input) failure ??= new InputFailure({ cause });\n },\n )\n .then(() => {\n pending.delete(reply);\n outstanding.delete(reply);\n if (pending.size === 0) {\n for (const end of endings) end();\n endings.clear();\n }\n });\n\n pending.add(reply);\n outstanding.add(reply);\n\n return response;\n };\n\n const requireOpen = Effect.suspend(() =>\n closed\n ? Effect.fail(new InputFailure({ cause: new Error(\"the input run has stopped\") }))\n : Effect.void,\n );\n\n const requireSuccess = Effect.suspend(() =>\n failure === undefined ? Effect.void : Effect.fail(failure),\n );\n\n const submit = (command: () => Promise<unknown>) => {\n if (reserved < 1) throw new Error(\"input must reserve capacity before dispatch\");\n reserved--;\n let response: Promise<unknown>;\n\n try {\n response = command();\n } catch (cause) {\n failure ??= new InputFailure({ cause });\n\n return;\n }\n\n void track(response, true);\n };\n\n const reserve = (commands: number, maximum: number, limit: number) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n if (!Number.isInteger(commands) || commands < 1 || commands > maximum)\n return yield* Effect.die(new Error(\"invalid input reservation\"));\n while (outstanding.size + reserved + commands > limit) {\n yield* Effect.promise(() => Promise.race(outstanding));\n yield* requireOpen;\n yield* requireSuccess;\n }\n reserved += commands;\n });\n\n const run: Run = {\n track,\n afterIdle: (end) => {\n if (pending.size === 0) end();\n else endings.add(end);\n },\n reserve: (commands) => reserve(commands, capacity, capacity),\n // A dense stroke cannot wait for replies midway without changing its published timing.\n // Its finite reservation also leaves room for the ordinary pool's already-owned releases.\n reserveMotion: (commands) => reserve(commands, maximumSamples, capacity + maximumSamples),\n send: (command) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n yield* Effect.sync(() => submit(command));\n yield* requireSuccess;\n }),\n down: (key, press, release) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* requireSuccess;\n yield* Effect.sync(() => {\n held.push({ key, release });\n submit(press);\n });\n yield* requireSuccess;\n }),\n up: (key) =>\n Effect.gen(function* () {\n yield* requireOpen;\n yield* Effect.sync(() => {\n const index = held.findLastIndex((entry) => entry.key === key);\n const entry = held[index];\n\n if (entry === undefined) return;\n // Submission owns the release, even if its reply fails. Retrying an uncertain\n // release could run page handlers twice, so cleanup never sends it again.\n held.splice(index, 1);\n submit(entry.release);\n });\n yield* requireSuccess;\n }),\n drain: Effect.gen(function* () {\n while (pending.size > 0) yield* Effect.promise(() => Promise.all(pending));\n yield* requireSuccess;\n }),\n close: Effect.sync(() => {\n if (closed) return;\n closed = true;\n for (const entry of held.toReversed()) submit(entry.release);\n held.length = 0;\n reserved = 0;\n }),\n };\n\n return run;\n });\n\n /** `idle` lets an action wait, in its page's turn, for the page's earlier replies. */\n return { idle, begin };\n};\n","/**\n * What a page's parts share: the browser state they act on, the page's own lane, pointer and\n * clock, and how they call the browser and report its failures. The assembly in `page.ts` builds\n * one context per page.\n */\nimport {\n Clock,\n Context,\n Duration,\n Effect,\n Fiber,\n MutableRef,\n Option,\n Schema,\n type Scope,\n} from \"effect\";\nimport type { CDPSession, Page as PlaywrightPage } from \"playwright-core\";\n\nimport type { CaptureSource } from \"../../Browser.ts\";\nimport { BrowserError, Closed, Failed, type Reason, Timeout } from \"../../BrowserError.ts\";\nimport type { BrowserEvent } from \"../../BrowserEvent.ts\";\nimport type { InputGuard, Point } from \"../../Page.ts\";\nimport type { Gate } from \"../input/gate.ts\";\nimport { Current } from \"../input/replies.ts\";\nimport type * as BrowserClock from \"../pictures/clock.ts\";\nimport * as Lane from \"./lane.ts\";\nimport type { Openings } from \"./openings.ts\";\nimport * as Url from \"./url.ts\";\n\n/** Why a page is gone. */\nexport type ClosedCause = Closed[\"cause\"];\n\n/** What every page of a browser shares, from its options. */\nexport interface Settings {\n readonly actionTimeout: Duration.Duration;\n readonly policyTimeout: Duration.Duration;\n readonly navigationTimeout: Duration.Duration;\n /** How long screencast frames stay in `recentFrames`, measured back from the newest. */\n readonly frameHistory: Duration.Duration;\n readonly guard: InputGuard | undefined;\n}\n\nexport interface MakeOptions {\n /** The page's CDP target id, which is also its main frame's id. */\n readonly id: string;\n /** The id of the browser's session, which its frames carry. */\n readonly session: string;\n /** This tracking connection, distinct from the provider session and capture socket. */\n readonly connection: string;\n /** The page's address when the browser began tracking it. */\n readonly url: string;\n readonly playwright: PlaywrightPage;\n readonly cdp: CDPSession;\n /** A call's reply, or a failure as for a closed page once the browser is lost first. */\n readonly untilLost: <A>(reply: Promise<A>) => Promise<A>;\n readonly settings: Settings;\n readonly clock: Clock.Clock;\n /** Browser-wide input authority; direct internal page fixtures need not provide one. */\n readonly inputGate?: Gate | undefined;\n /** The browser's epoch mapping: its first capture measures it, and later ones renew it. */\n readonly mapping: BrowserClock.Mapping;\n readonly publish: (event: BrowserEvent) => number;\n /** This page's retained events, oldest first. */\n readonly recentEvents: Effect.Effect<ReadonlyArray<BrowserEvent>>;\n /** Registration of the actual popups this page's current input opened. */\n readonly openings: Openings;\n /** Succeeds once the page's own session holds focus emulation, which keeps it painting behind. */\n readonly focused: Effect.Effect<void, BrowserError>;\n /** Succeeds once the page's own session has the Page domain on, so it sees every later commit. */\n readonly paging: (operation: string) => Effect.Effect<void, BrowserError>;\n /** Why the page is gone, once it is: its browser's loss, else its own close or crash. */\n readonly closedBy: () => ClosedCause;\n /** Where the page's screencast runs, if not on its own session. */\n readonly capture?: CaptureSource | undefined;\n}\n\nconst messageOf = (cause: unknown): string =>\n cause instanceof Error ? cause.message : typeof cause === \"string\" ? cause : \"unknown error\";\n\nconst closedPattern =\n /has been closed|Target closed|Session closed|browser has disconnected|Target page, context or browser/i;\n\n/**\n * Map a Playwright or protocol failure to a reason, `Closed` with `closed` as its cause for a page\n * or browser that is gone. A call that gives Playwright a timeout passes the same bound, so its\n * `Timeout` reports it; without one, Playwright's own message stays.\n */\nexport const reasonOf = (\n cause: unknown,\n timeoutMillis?: number,\n closed: ClosedCause = \"connection\",\n): Reason => {\n const message = messageOf(cause);\n\n if (closedPattern.test(message)) return new Closed({ cause: closed });\n if (cause instanceof Error && cause.name === \"TimeoutError\" && timeoutMillis !== undefined)\n return new Timeout({ millis: timeoutMillis });\n const line = message.split(\"\\n\")[0] ?? message;\n\n return new Failed({ detail: Url.redactWithin(line.replace(/^[\\w.]+: /, \"\")) });\n};\n\n/**\n * One Playwright or protocol call, failing undispatched. A closed page says why it is gone as the\n * browser knows then: the library's own calls fail with the browser's loss, and Playwright fails\n * its calls on a dropped connection after reporting the context closed.\n */\nexport const call = <A>(\n operation: string,\n run: () => Promise<A>,\n closedBy: () => ClosedCause,\n timeoutMillis?: number,\n) =>\n Effect.tryPromise({\n try: run,\n catch: (cause) => undispatched(operation, reasonOf(cause, timeoutMillis, closedBy())),\n });\n\nexport const contextGone = (error: BrowserError) =>\n error.reason._tag === \"Failed\" &&\n /Cannot find context|Execution context was destroyed|__effectBrowser|Inspected target navigated/i.test(\n error.reason.detail,\n );\n\n/**\n * What a page operation spent on the page's own protocol session: its calls, the bytes of their\n * parameters and results, and how long at least one of them awaited its reply. An operation inside\n * another counts toward both.\n */\ninterface Cost {\n readonly enclosing: Cost | undefined;\n calls: number;\n bytesOut: number;\n bytesIn: number;\n waitedMillis: number;\n awaiting: number;\n awaitingSince: number;\n}\n\n/** The cost of the page operation the current fiber runs, if any. */\nconst CurrentCost = Context.Reference<Cost | undefined>(\n \"effect-browser/internal/page/CurrentCost\",\n {\n defaultValue: () => undefined,\n },\n);\n\n/** Track the promise returned to the action, after tracing wraps it, so input counts once. */\nconst track = <A>(reply: Promise<A>) => Fiber.getCurrent()?.getRef(Current)?.track(reply) ?? reply;\n\n// A message's size on the wire, give or take its envelope.\nconst sizeOf = (value: unknown) =>\n value === undefined ? 0 : Buffer.byteLength(JSON.stringify(value));\n\n/** A failure of `operation` before any of its input reached the browser. */\nexport const undispatched = (operation: string, reason: Reason) =>\n new BrowserError({ operation, reason, dispatched: false });\n\nexport const failWith = (operation: string, reason: Reason) =>\n Effect.fail(undispatched(operation, reason));\n\nexport const decodeWith =\n <A>(operation: string, schema: Schema.Codec<A, unknown>) =>\n (value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(\n Effect.mapError((error) => undispatched(operation, new Failed({ detail: error.message }))),\n );\n\nexport const make = (options: MakeOptions, scope: Scope.Scope) => {\n const { id, clock } = options;\n const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;\n // Deadlines and pacing guard the page's lane, so page operations run on the owner's clock; a\n // caller's clock, such as a TestClock, cannot stall or stretch them.\n const owned = Effect.provideService(Clock.Clock, clock);\n\n // This page's spans name it and report what the operation cost on the protocol. Fine-grained\n // ones carry a level below the default Info, so an application keeps only the coarse ones by\n // raising `Tracer.MinimumTraceLevel`.\n const span =\n (name: string, attributes: Record<string, unknown> = {}, level?: \"Debug\" | \"Trace\") =>\n <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.gen(function* () {\n const cost: Cost = {\n enclosing: yield* CurrentCost,\n calls: 0,\n bytesOut: 0,\n bytesIn: 0,\n waitedMillis: 0,\n awaiting: 0,\n awaitingSince: 0,\n };\n\n return yield* effect.pipe(\n Effect.provideService(CurrentCost, cost),\n Effect.ensuring(\n Effect.suspend(() =>\n Effect.annotateCurrentSpan({\n calls: cost.calls,\n bytesOut: cost.bytesOut,\n bytesIn: cost.bytesIn,\n waitedMillis: Math.round(cost.waitedMillis * 10) / 10,\n }),\n ),\n ),\n );\n }).pipe(\n Effect.withSpan(\n name,\n { attributes: { page: id, ...attributes }, ...(level === undefined ? {} : { level }) },\n { captureStackTrace: false },\n ),\n );\n\n // Every call on the page's own protocol session, counted into the operation that sends it and\n // the operations around that one. It is looked up when the call is sent, so input pipelined\n // from inside an operation counts toward it too.\n const send: CDPSession[\"send\"] = (method, params) => {\n const cost = Fiber.getCurrent()?.getRef(CurrentCost);\n const sentAt = now();\n const bytesOut = sizeOf(params);\n\n for (let each = cost; each !== undefined; each = each.enclosing) {\n each.calls++;\n each.bytesOut += bytesOut;\n if (each.awaiting++ === 0) each.awaitingSince = sentAt;\n }\n const reply = options.untilLost(options.cdp.send(method, params));\n\n if (cost === undefined) return track(reply);\n\n const settle = (bytesIn: number) => {\n const at = now();\n\n for (let each: Cost | undefined = cost; each !== undefined; each = each.enclosing) {\n each.bytesIn += bytesIn;\n if (--each.awaiting === 0) each.waitedMillis += at - each.awaitingSince;\n }\n };\n\n return track(\n reply.then(\n (result) => {\n settle(sizeOf(result));\n\n return result;\n },\n (cause: unknown) => {\n settle(0);\n\n return Promise.reject(cause);\n },\n ),\n );\n };\n\n // Every protocol or Playwright call. Errors are undispatched here; `perform` marks them\n // dispatched once input has gone out.\n const native = <A>(operation: string, run: () => Promise<A>, timeoutMillis?: number) =>\n call(operation, () => track(run()), options.closedBy, timeoutMillis);\n\n /** Fail with `Timeout` once `duration` has passed, the action timeout unless another is given. */\n const within =\n (operation: string, duration: Duration.Input = options.settings.actionTimeout) =>\n <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.timeoutOrElse(effect, {\n duration,\n orElse: () => failWith(operation, new Timeout({ millis: Duration.toMillis(duration) })),\n });\n\n // Stop a navigation whose bounded wait failed. The stop and original mutation keep their\n // actual replies owned; this cleanup does not turn an unanswered acknowledgement into success.\n const stopLoading = native(\"stopLoading\", () => send(\"Page.stopLoading\")).pipe(\n within(\"stopLoading\", options.settings.navigationTimeout),\n Effect.ignore,\n );\n\n // The latest submitted input or page change. A read that must follow it waits for the action in\n // flight, so only paint from after its latest input is current. An action ends after its input\n // was handled, so if that input changed the page, newer paint follows; a lost final paint is\n // bounded by recency instead. Paint from before the current document began, when the page's own\n // session saw the main frame commit it, shows a page the tab has left, so no read reuses it,\n // whatever it asks.\n const activity = { inputAt: 0, documentAt: Number.NEGATIVE_INFINITY };\n\n const noteInput = () => {\n activity.inputAt = Math.max(activity.inputAt, now());\n };\n\n const lane = Lane.make({\n now,\n actionTimeout: options.settings.actionTimeout,\n documentAt: () => activity.documentAt,\n gone: (operation) => undispatched(operation, new Closed({ cause: options.closedBy() })),\n scope,\n inputGate: options.inputGate,\n });\n\n // Where this page's latest move left its pointer, which is where its next plain glide starts and\n // where a held button is released. Its lane orders the page's input, and Chromium keeps a\n // pointer per page too.\n const pointer = MutableRef.make(Option.none<Point>());\n\n // Named, so that the declarations of what holds it need not spell out Playwright's protocol types.\n const protocol: Pick<CDPSession, \"send\"> = { send };\n\n return {\n ...options,\n lane,\n pointer,\n now,\n owned,\n span,\n protocol,\n native,\n within,\n stopLoading,\n activity,\n noteInput,\n };\n};\n\nexport type PageContext = ReturnType<typeof make>;\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -9,7 +9,7 @@ import { t as Cdp_d_exports } from "./Cdp.mjs";
|
|
|
9
9
|
import { t as Chromium_d_exports } from "./Chromium.mjs";
|
|
10
10
|
import { t as Expect_d_exports } from "./Expect.mjs";
|
|
11
11
|
import { t as Motion_d_exports } from "./Motion.mjs";
|
|
12
|
-
import { r as Plan_d_exports } from "./Plan-
|
|
12
|
+
import { r as Plan_d_exports } from "./Plan-BS0jpENY.mjs";
|
|
13
13
|
import { t as Policy_d_exports } from "./Policy.mjs";
|
|
14
14
|
import { t as Presentation_d_exports } from "./Presentation.mjs";
|
|
15
15
|
import { t as Stage_d_exports } from "./Stage.mjs";
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as Motion_exports } from "./Motion.mjs";
|
|
2
2
|
import { t as BrowserEvent_exports } from "./BrowserEvent.mjs";
|
|
3
3
|
import { t as BrowserError_exports } from "./BrowserError.mjs";
|
|
4
|
-
import { n as Browser_exports } from "./Browser-
|
|
4
|
+
import { n as Browser_exports } from "./Browser-QiT6Cdn_.mjs";
|
|
5
5
|
import { t as Frame_exports } from "./Frame.mjs";
|
|
6
6
|
import { a as Page_exports } from "./Page-B5cjaUp5.mjs";
|
|
7
7
|
import { t as Snapshot_exports } from "./Snapshot.mjs";
|
|
@@ -10,7 +10,7 @@ import { t as Moment_exports } from "./Moment.mjs";
|
|
|
10
10
|
import { t as Cdp_exports } from "./Cdp.mjs";
|
|
11
11
|
import { t as Chromium_exports } from "./Chromium.mjs";
|
|
12
12
|
import { t as Expect_exports } from "./Expect.mjs";
|
|
13
|
-
import { r as Plan_exports } from "./Plan-
|
|
13
|
+
import { r as Plan_exports } from "./Plan-Bl2NM1D5.mjs";
|
|
14
14
|
import { t as Policy_exports } from "./Policy.mjs";
|
|
15
15
|
import { t as Presentation_exports } from "./Presentation.mjs";
|
|
16
16
|
import { t as Stage_exports } from "./Stage.mjs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Plan } from "./Motion.mjs";
|
|
2
2
|
import { Action, Box, CursorChanged, KeyChanged, Navigated, PageLoaded, PointerPressed, PointerReleased, Subject, SubjectContext, TextInserted, TrackEvent, TrackPerformed, TrackPlanned, WheelScrolled } from "./BrowserEvent.mjs";
|
|
3
3
|
import { BrowserError, Failed, InvalidRequest, NavigationFailed, NotActionable, NotFound, PolicyTimeout, StaleRef, Timeout } from "./BrowserError.mjs";
|
|
4
|
-
import { a as make$19, c as Current, i as failWith, l as make$18, n as contextGone, o as reasonOf, r as decodeWith, s as undispatched } from "./context-
|
|
4
|
+
import { a as make$19, c as Current, i as failWith, l as make$18, n as contextGone, o as reasonOf, r as decodeWith, s as undispatched } from "./context-BAjRj7Xo.mjs";
|
|
5
5
|
import { i as redact, n as parse, s as Correlation, t as addresses } from "./url-Cd04Okid.mjs";
|
|
6
6
|
import { BrowserPaint, CaptureStats, Frame, Image, Screenshot } from "./Frame.mjs";
|
|
7
7
|
import { _ as evidence, c as State, g as guard, h as ValidatedInputResultSchema, i as InputRequest, l as Text, m as PreparedInputResultSchema, o as Region, p as redacted, r as Found, s as ResolvedTarget, u as Zoom } from "./Page-B5cjaUp5.mjs";
|
|
@@ -3163,7 +3163,7 @@ const make$11 = (page, bridge) => {
|
|
|
3163
3163
|
});
|
|
3164
3164
|
/** Wait for the page to settle after input sent since `before`. */
|
|
3165
3165
|
const settle = (operation, before) => Effect.gen(function* () {
|
|
3166
|
-
yield* bridge.evaluate(operation, scriptCall("settle")).pipe(Effect.catchIf(contextGone, () => Effect.void));
|
|
3166
|
+
yield* bridge.evaluate(operation, scriptCall("settle")).pipe(Effect.provideService(Current, void 0), Effect.catchIf(contextGone, () => Effect.void), Effect.onError(() => page.stopLoading));
|
|
3167
3167
|
while (document() > before.document && parsed < document()) yield* Deferred.await(changed);
|
|
3168
3168
|
}).pipe(Effect.timeoutOrElse({
|
|
3169
3169
|
duration: settling$1,
|
|
@@ -4860,7 +4860,7 @@ const make$2 = (page, perform, preparePolicy) => {
|
|
|
4860
4860
|
return perform(name, {
|
|
4861
4861
|
target: url,
|
|
4862
4862
|
input: false
|
|
4863
|
-
}, settings.navigationTimeout, preparePolicy(name, { target: url }, [], { destination: url }), (marks) => marks.sent.pipe(Effect.andThen(native(name, run)), Effect.mapError((error) => error.reason._tag === "Failed" && /net::|NS_ERROR|Cannot navigate/i.test(error.reason.detail) ? new BrowserError({
|
|
4863
|
+
}, settings.navigationTimeout, preparePolicy(name, { target: url }, [], { destination: url }), (marks) => marks.sent.pipe(Effect.andThen(native(name, run).pipe(Effect.onError(() => page.stopLoading))), Effect.mapError((error) => error.reason._tag === "Failed" && /net::|NS_ERROR|Cannot navigate/i.test(error.reason.detail) ? new BrowserError({
|
|
4864
4864
|
operation: name,
|
|
4865
4865
|
reason: new NavigationFailed({
|
|
4866
4866
|
url,
|
|
@@ -4901,16 +4901,15 @@ const make$2 = (page, perform, preparePolicy) => {
|
|
|
4901
4901
|
const onNavigation = () => {
|
|
4902
4902
|
Deferred.doneUnsafe(navigated, Effect.void);
|
|
4903
4903
|
};
|
|
4904
|
-
yield* Effect.acquireUseRelease(Effect.sync(() => playwright.on("framenavigated", onNavigation)), () => Effect.gen(function* () {
|
|
4905
|
-
yield* marks.sent;
|
|
4904
|
+
yield* Effect.acquireUseRelease(Effect.sync(() => playwright.on("framenavigated", onNavigation)), () => marks.sent.pipe(Effect.andThen(Effect.gen(function* () {
|
|
4906
4905
|
yield* native("back", () => send("Page.navigateToHistoryEntry", { entryId: previous.id }));
|
|
4907
4906
|
yield* traversingHistory.pipe(Effect.repeat({
|
|
4908
4907
|
schedule: Schedule.spaced(Duration.millis(50)),
|
|
4909
4908
|
until: (latest) => latest.entries[latest.currentIndex]?.id !== current.id
|
|
4910
4909
|
}));
|
|
4911
4910
|
yield* Deferred.await(navigated);
|
|
4912
|
-
|
|
4913
|
-
|
|
4911
|
+
yield* native("back", () => playwright.waitForLoadState("domcontentloaded", { timeout: 0 })).pipe(Effect.provideService(Current, void 0));
|
|
4912
|
+
}).pipe(Effect.onError(() => page.stopLoading)))), () => Effect.sync(() => playwright.off("framenavigated", onNavigation)));
|
|
4914
4913
|
});
|
|
4915
4914
|
const goto = (url) => {
|
|
4916
4915
|
const parsed = parse(url);
|
|
@@ -5072,4 +5071,4 @@ const make = Effect.fnUntraced(function* (options) {
|
|
|
5072
5071
|
//#endregion
|
|
5073
5072
|
export { performed as a, pause as i, make as n, mapping as o, viewOf as r, internalsOf as t };
|
|
5074
5073
|
|
|
5075
|
-
//# sourceMappingURL=page-
|
|
5074
|
+
//# sourceMappingURL=page-ZxMhw16l.mjs.map
|