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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +24 -11
  2. package/dist/{Browser-BBSySyjT.mjs → Browser-DRPthkNO.mjs} +4 -4
  3. package/dist/Browser-DRPthkNO.mjs.map +1 -0
  4. package/dist/Browser.d.mts +2 -2
  5. package/dist/Browser.mjs +1 -1
  6. package/dist/{BrowserEvent-BRu2juEz.d.mts → BrowserEvent-dq6FRT8w.d.mts} +9 -9
  7. package/dist/BrowserEvent.d.mts +1 -1
  8. package/dist/BrowserEvent.mjs +7 -12
  9. package/dist/BrowserEvent.mjs.map +1 -1
  10. package/dist/Cdp.mjs +2 -2
  11. package/dist/{Change-iMlcuzVD.d.mts → Change-DqING3tr.d.mts} +2 -2
  12. package/dist/Change.d.mts +1 -1
  13. package/dist/Chromium.mjs +2 -2
  14. package/dist/Expect.d.mts +1 -1
  15. package/dist/Expect.mjs +2 -2
  16. package/dist/Moment.d.mts +1 -1
  17. package/dist/Moment.mjs +1 -1
  18. package/dist/Motion-DND6I-64.d.mts +40 -0
  19. package/dist/Motion.d.mts +2 -40
  20. package/dist/Motion.mjs +1 -2
  21. package/dist/Motion.mjs.map +1 -1
  22. package/dist/{Page-B5cjaUp5.mjs → Page-C_VkBVwI.mjs} +5 -9
  23. package/dist/Page-C_VkBVwI.mjs.map +1 -0
  24. package/dist/{Page-DL9eylp3.d.mts → Page-jHmSJeOS.d.mts} +6 -13
  25. package/dist/Page.d.mts +1 -1
  26. package/dist/Page.mjs +1 -1
  27. package/dist/{Plan-6As6vcwT.d.mts → Plan-DLtRksuu.d.mts} +3 -3
  28. package/dist/{Plan-CXA2BW-G.mjs → Plan-X-JkACAB.mjs} +6 -9
  29. package/dist/Plan-X-JkACAB.mjs.map +1 -0
  30. package/dist/Plan.d.mts +1 -1
  31. package/dist/Plan.mjs +1 -1
  32. package/dist/Policy.d.mts +1 -1
  33. package/dist/Policy.mjs +1 -1
  34. package/dist/Presentation.d.mts +2 -2
  35. package/dist/Presentation.mjs +3 -2
  36. package/dist/Presentation.mjs.map +1 -1
  37. package/dist/Stage.d.mts +1 -1
  38. package/dist/Stage.mjs +3 -3
  39. package/dist/Stage.mjs.map +1 -1
  40. package/dist/{Supervisor-BE2sGHBK.mjs → Supervisor-BgWP8o9t.mjs} +116 -66
  41. package/dist/Supervisor-BgWP8o9t.mjs.map +1 -0
  42. package/dist/{Supervisor-W60pZWHh.d.mts → Supervisor-mVPeEUp8.d.mts} +33 -12
  43. package/dist/Supervisor.d.mts +2 -2
  44. package/dist/Supervisor.mjs +2 -2
  45. package/dist/{context-BAjRj7Xo.mjs → context-rYTk-3Cv.mjs} +68 -3
  46. package/dist/context-rYTk-3Cv.mjs.map +1 -0
  47. package/dist/index.d.mts +6 -6
  48. package/dist/index.mjs +4 -4
  49. package/dist/match.inpage-xOe9LNO0.mjs.map +1 -1
  50. package/dist/{page-B92QnRfT.mjs → page-ClEMFnBG.mjs} +48 -110
  51. package/dist/page-ClEMFnBG.mjs.map +1 -0
  52. package/package.json +1 -1
  53. package/src/Browser.ts +1 -1
  54. package/src/BrowserEvent.ts +7 -12
  55. package/src/Motion.ts +1 -2
  56. package/src/Page.ts +5 -9
  57. package/src/Plan.ts +1 -2
  58. package/src/Presentation.ts +2 -1
  59. package/src/Stage.ts +2 -1
  60. package/src/Supervisor.ts +168 -166
  61. package/src/internal/input/actions.ts +25 -33
  62. package/src/internal/input/dispatch.ts +3 -4
  63. package/src/internal/input/edit.inpage.ts +3 -9
  64. package/src/internal/input/evidence.inpage.ts +2 -20
  65. package/src/internal/input/guard.inpage.ts +9 -29
  66. package/src/internal/input/guard.ts +12 -1
  67. package/src/internal/input/targets.inpage.ts +4 -8
  68. package/src/internal/input/targets.ts +8 -11
  69. package/src/internal/page/context.ts +2 -2
  70. package/src/internal/pictures/capture.ts +4 -4
  71. package/src/internal/pictures/clock.ts +8 -3
  72. package/src/internal/pictures/pictures.ts +2 -1
  73. package/src/internal/reading/controls.inpage.ts +8 -1
  74. package/src/internal/reading/match.inpage.ts +2 -1
  75. package/src/internal/reading/outline.inpage.ts +7 -4
  76. package/src/internal/reading/reading.ts +5 -16
  77. package/src/internal/supervisor/lifecycle.ts +73 -20
  78. package/dist/Browser-BBSySyjT.mjs.map +0 -1
  79. package/dist/Page-B5cjaUp5.mjs.map +0 -1
  80. package/dist/Plan-CXA2BW-G.mjs.map +0 -1
  81. package/dist/Supervisor-BE2sGHBK.mjs.map +0 -1
  82. package/dist/context-BAjRj7Xo.mjs.map +0 -1
  83. package/dist/page-B92QnRfT.mjs.map +0 -1
package/dist/Stage.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as BrowserError } from "./BrowserError-Coy7EW1N.mjs";
2
2
  import { n as CaptureStats, o as ScreencastOptions, r as Frame } from "./Frame-DkNdjjjr.mjs";
3
- import { u as Page } from "./Page-DL9eylp3.mjs";
3
+ import { u as Page } from "./Page-jHmSJeOS.mjs";
4
4
  import { Duration, Effect, Option, Schema, Scope, Stream } from "effect";
5
5
  declare namespace Stage_d_exports {
6
6
  export { Cue, Options, Presented, Shown, Stage, make };
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-BAjRj7Xo.mjs";
4
- import { t as internalsOf } from "./page-B92QnRfT.mjs";
3
+ import { i as failWith, s as undispatched, u as monotonicMillis } from "./context-rYTk-3Cv.mjs";
4
+ import { t as internalsOf } from "./page-ClEMFnBG.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
  /**
@@ -193,7 +193,7 @@ const openFeed = (page, owner, clock, settings, current, publish) => Effect.gen(
193
193
  const make = Effect.fn("Stage.make")(function* (options = {}) {
194
194
  const clock = yield* Clock.Clock;
195
195
  const owner = yield* Effect.scope;
196
- const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;
196
+ const now = () => monotonicMillis(clock);
197
197
  const waited = Duration.fromInput(options.firstFrame ?? Duration.seconds(5));
198
198
  if (Option.isNone(waited) || !Duration.isFinite(waited.value) || !Duration.isPositive(waited.value)) return yield* failWith("stage", new InvalidRequest({ detail: "firstFrame must be a finite positive duration" }));
199
199
  const firstFrame = waited.value;
@@ -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 at nondecreasing host times; frames retain their capture times. */\nexport type Shown =\n | { readonly _tag: \"Painted\"; readonly frame: Frame; readonly at: number }\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 /** Capture-timed current-page paint; use `shown` for output time. Readers retain 16 frames. */\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 readonly at: number;\n}\n\ninterface Evidence {\n readonly feed: Feed;\n readonly frame: Frame;\n readonly value: Shown;\n}\n\nconst unavailable = () =>\n undispatched(\"cue\", new InvalidRequest({ detail: \"the cue's capture is no longer available\" }));\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(unavailable()),\n ),\n );\n\nconst firstOf = (feed: Feed, wait: Duration.Duration) =>\n Effect.suspend(() => {\n if (feed.state._tag === \"Stopped\") return Effect.fail(unavailable());\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 shownAt = Number.NEGATIVE_INFINITY;\n let seen: Pick<Paint, \"feed\" | \"frame\"> | undefined = current();\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<Evidence>();\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<Evidence>();\n const at = now();\n\n checkedAt = at;\n if (Result.isFailure(confirmed) && consequence(confirmed.failure).lost !== \"nothing\") {\n return Option.some<Evidence>({\n feed,\n frame,\n value: { _tag: \"Gone\", page: feed.page.id, at },\n });\n }\n\n return Option.some<Evidence>({\n feed,\n frame,\n value: {\n _tag: Result.isSuccess(confirmed) && confirmed.success ? \"Still\" : \"Stalled\",\n frame,\n at,\n },\n });\n });\n\n return Stream.merge(\n Stream.fromPubSub(output).pipe(\n Stream.map(({ feed, frame, at }): Evidence => ({\n feed,\n frame,\n value: { _tag: \"Painted\", frame, at },\n })),\n ),\n Stream.fromEffectRepeat(probe).pipe(\n Stream.filter(Option.isSome),\n Stream.map(({ value }) => value),\n ),\n ).pipe(\n // A probe can overtake queued paint while its reader is paused. It establishes nothing\n // about that reader's output until the matching frame has reached it.\n Stream.filter(({ feed, frame, value }) => {\n if (value._tag === \"Painted\" && gone !== feed) {\n seen = { feed, frame };\n\n return true;\n }\n // The subscription may have begun after its first publication.\n seen ??= { feed, frame };\n\n return value._tag !== \"Painted\" && seen?.feed === feed && seen.frame === frame;\n }),\n Stream.map(({ feed, value }) => {\n if (value._tag === \"Gone\") gone = feed;\n // A frame captured before a silence probe can arrive after the probe's evidence.\n shownAt = Math.max(shownAt, value.at);\n\n return value.at === shownAt ? value : { ...value, at: shownAt };\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, at = frame.hostTime) =>\n Effect.suspend(() => {\n painted = Math.max(painted, at);\n if (showing !== undefined) {\n showing.changedAt = now();\n showing.frame = frame;\n }\n\n return PubSub.publish(output, { feed, frame, at: painted });\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(unavailable());\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, at).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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"}
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 { monotonicMillis } from \"./internal/pictures/clock.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 at nondecreasing host times; frames retain their capture times. */\nexport type Shown =\n | { readonly _tag: \"Painted\"; readonly frame: Frame; readonly at: number }\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 /** Capture-timed current-page paint; use `shown` for output time. Readers retain 16 frames. */\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 readonly at: number;\n}\n\ninterface Evidence {\n readonly feed: Feed;\n readonly frame: Frame;\n readonly value: Shown;\n}\n\nconst unavailable = () =>\n undispatched(\"cue\", new InvalidRequest({ detail: \"the cue's capture is no longer available\" }));\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(unavailable()),\n ),\n );\n\nconst firstOf = (feed: Feed, wait: Duration.Duration) =>\n Effect.suspend(() => {\n if (feed.state._tag === \"Stopped\") return Effect.fail(unavailable());\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 shownAt = Number.NEGATIVE_INFINITY;\n let seen: Pick<Paint, \"feed\" | \"frame\"> | undefined = current();\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<Evidence>();\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<Evidence>();\n const at = now();\n\n checkedAt = at;\n if (Result.isFailure(confirmed) && consequence(confirmed.failure).lost !== \"nothing\") {\n return Option.some<Evidence>({\n feed,\n frame,\n value: { _tag: \"Gone\", page: feed.page.id, at },\n });\n }\n\n return Option.some<Evidence>({\n feed,\n frame,\n value: {\n _tag: Result.isSuccess(confirmed) && confirmed.success ? \"Still\" : \"Stalled\",\n frame,\n at,\n },\n });\n });\n\n return Stream.merge(\n Stream.fromPubSub(output).pipe(\n Stream.map(({ feed, frame, at }): Evidence => ({\n feed,\n frame,\n value: { _tag: \"Painted\", frame, at },\n })),\n ),\n Stream.fromEffectRepeat(probe).pipe(\n Stream.filter(Option.isSome),\n Stream.map(({ value }) => value),\n ),\n ).pipe(\n // A probe can overtake queued paint while its reader is paused. It establishes nothing\n // about that reader's output until the matching frame has reached it.\n Stream.filter(({ feed, frame, value }) => {\n if (value._tag === \"Painted\" && gone !== feed) {\n seen = { feed, frame };\n\n return true;\n }\n // The subscription may have begun after its first publication.\n seen ??= { feed, frame };\n\n return value._tag !== \"Painted\" && seen?.feed === feed && seen.frame === frame;\n }),\n Stream.map(({ feed, value }) => {\n if (value._tag === \"Gone\") gone = feed;\n // A frame captured before a silence probe can arrive after the probe's evidence.\n shownAt = Math.max(shownAt, value.at);\n\n return value.at === shownAt ? value : { ...value, at: shownAt };\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 = () => monotonicMillis(clock);\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, at = frame.hostTime) =>\n Effect.suspend(() => {\n painted = Math.max(painted, at);\n if (showing !== undefined) {\n showing.changedAt = now();\n showing.frame = frame;\n }\n\n return PubSub.publish(output, { feed, frame, at: painted });\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(unavailable());\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, at).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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"}
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { DisconnectCause } from "./BrowserEvent.mjs";
3
- import { Cause, Clock, DateTime, Deferred, Duration, Effect, Exit, FiberHandle, FiberMap, Option, Predicate, PubSub, Schedule, Schema, Scope, Semaphore, Stream, SubscriptionRef } from "effect";
3
+ import { Cause, Clock, Context, DateTime, Deferred, Duration, Effect, Exit, FiberHandle, FiberMap, Option, Predicate, PubSub, Schedule, Schema, Scope, Semaphore, Stream, SubscriptionRef } from "effect";
4
4
  //#region src/internal/supervisor/lifecycle.ts
5
5
  /**
6
6
  * A supervisor's life as one state and one transition function, and the generation states it
@@ -34,13 +34,15 @@ var Lost = class extends Schema.TaggedClass()("Lost", {
34
34
  }) {};
35
35
  /**
36
36
  * It never opened: its last try failed with `cause`, which the provider deems definite, or after
37
- * which the reopen schedule gave up.
37
+ * which the reopen schedule gave up. `released` combines this generation's failed-try cleanup,
38
+ * retaining the first unconfirmed end; it is absent when no try reported cleanup.
38
39
  */
39
40
  var Down = class extends Schema.TaggedClass()("Down", {
40
41
  detail: Schema.String,
41
- cause: Schema.Defect()
42
+ cause: Schema.Defect(),
43
+ released: Schema.optional(Released)
42
44
  }) {};
43
- /** It ended. `released` says how its release went; one retired while opening has none. */
45
+ /** It ended. `released` includes reported cleanup when retired while opening, if any. */
44
46
  var Closed = class extends Schema.TaggedClass()("Closed", { released: Schema.optional(Released) }) {};
45
47
  /**
46
48
  * It was serving as the supervisor's scope closed, and was left running, not released: its scope
@@ -62,14 +64,12 @@ const start = () => ({
62
64
  _tag: "Opening",
63
65
  number: 1,
64
66
  serving: void 0,
65
- draining: /* @__PURE__ */ new Map(),
66
- latest: void 0
67
+ draining: /* @__PURE__ */ new Map()
67
68
  },
68
69
  events: [[1, new Opening()]],
69
70
  commands: [{
70
71
  _tag: "Open",
71
- number: 1,
72
- after: void 0
72
+ number: 1
73
73
  }]
74
74
  });
75
75
  const servingOf = (state) => state._tag === "Retired" ? void 0 : state.serving;
@@ -149,20 +149,19 @@ const holds = (change, input) => {
149
149
  const next = (change, number, reopen) => {
150
150
  const { exclusive } = change;
151
151
  const current = servingOf(change.state);
152
- const after = change.state.latest;
153
152
  if (current !== void 0 && (reopen || exclusive)) end(change, current, reopen ? "lost" : "rotated");
154
153
  change.state = {
155
154
  _tag: "Opening",
156
155
  number,
157
156
  serving: reopen || exclusive ? void 0 : current,
158
157
  draining: change.state.draining,
159
- latest: after
158
+ released: change.state.released,
159
+ attempts: change.state.attempts
160
160
  };
161
161
  change.events.push([number, reopen ? new Reopening() : new Opening()]);
162
162
  change.commands.push({
163
163
  _tag: "Open",
164
- number,
165
- after
164
+ number
166
165
  });
167
166
  };
168
167
  const lost = (change, input) => {
@@ -188,6 +187,17 @@ const lost = (change, input) => {
188
187
  }
189
188
  }
190
189
  };
190
+ /** End one opening without confusing its cleanup with preceding generations' releases. */
191
+ const finishAttempt = (change, number) => {
192
+ const attempts = new Map(change.state.attempts);
193
+ const released = attempts.get(number);
194
+ attempts.delete(number);
195
+ change.state = {
196
+ ...change.state,
197
+ attempts
198
+ };
199
+ return released;
200
+ };
191
201
  /** Pure admission and lifecycle changes. A rejected hold preserves state identity. */
192
202
  const transition = (state, input, exclusive, now = 0, handover = 3e4) => {
193
203
  const change = {
@@ -214,6 +224,7 @@ const transition = (state, input, exclusive, now = 0, handover = 3e4) => {
214
224
  if (lookup(state, live.number) !== void 0) break;
215
225
  if (state._tag !== "Opening" || state.number !== live.number) end(change, live, "retired");
216
226
  else {
227
+ finishAttempt(change, live.number);
217
228
  change.events.push([live.number, new Open()]);
218
229
  change.commands.push({
219
230
  _tag: "Watch",
@@ -224,7 +235,8 @@ const transition = (state, input, exclusive, now = 0, handover = 3e4) => {
224
235
  _tag: "Open",
225
236
  serving: live,
226
237
  draining: change.state.draining,
227
- latest: input.live
238
+ released: change.state.released,
239
+ attempts: change.state.attempts
228
240
  };
229
241
  }
230
242
  break;
@@ -239,23 +251,34 @@ const transition = (state, input, exclusive, now = 0, handover = 3e4) => {
239
251
  };
240
252
  change.events.push([number, new Down({
241
253
  detail,
242
- cause
254
+ cause,
255
+ released: finishAttempt(change, number)
243
256
  })]);
244
257
  break;
245
258
  }
246
259
  case "Abandoned":
247
- change.events.push([input.number, new Closed({})]);
260
+ change.events.push([input.number, new Closed({ released: finishAttempt(change, input.number) })]);
261
+ break;
262
+ case "AttemptReleased":
263
+ change.state = {
264
+ ...state,
265
+ released: combine(state.released, input.released),
266
+ attempts: new Map(state.attempts).set(input.number, combine(state.attempts?.get(input.number), input.released))
267
+ };
248
268
  break;
249
269
  case "Released": {
250
- const drain = state.draining.get(input.number);
270
+ const { number, released } = input;
271
+ const drain = state.draining.get(number);
251
272
  if (drain === void 0 || drain._tag === "Draining" || drain.live.holds.size > 0) break;
252
273
  const draining = new Map(state.draining);
253
- draining.delete(input.number);
274
+ draining.delete(number);
254
275
  change.state = {
255
276
  ...state,
256
- draining
277
+ draining,
278
+ released: combine(state.released, released)
257
279
  };
258
- change.events.push([input.number, input.released === void 0 ? new Kept() : new Closed({ released: input.released })]);
280
+ const outcome = combine(finishAttempt(change, number), released);
281
+ change.events.push([number, released === void 0 ? new Kept() : new Closed({ released: outcome })]);
259
282
  break;
260
283
  }
261
284
  case "Lost":
@@ -274,11 +297,15 @@ const transition = (state, input, exclusive, now = 0, handover = 3e4) => {
274
297
  change.state = {
275
298
  _tag: "Retired",
276
299
  draining: change.state.draining,
277
- latest: void 0
300
+ released: change.state.released,
301
+ attempts: change.state.attempts
278
302
  };
279
303
  }
280
304
  return change;
281
305
  };
306
+ function combine(before, next) {
307
+ return before?._tag === "Unconfirmed" ? before : next ?? before;
308
+ }
282
309
  /** An unheld browser resolution; use obtains its hold atomically through Hold instead. */
283
310
  const resolve = (state, target) => {
284
311
  if (state._tag === "Retired") return {
@@ -330,6 +357,7 @@ var Supervisor_exports = /* @__PURE__ */ __exportAll({
330
357
  Open: () => Open,
331
358
  Opening: () => Opening,
332
359
  Released: () => Released,
360
+ Releasing: () => Releasing,
333
361
  Reopening: () => Reopening,
334
362
  Settled: () => Settled,
335
363
  Superseded: () => Superseded,
@@ -361,6 +389,13 @@ var Unavailable = class extends Schema.TaggedError()("Unavailable", {
361
389
  return `no browser: ${this.detail}`;
362
390
  }
363
391
  };
392
+ /**
393
+ * Optional service supplied during `open`. Providers report failed-attempt cleanup here before
394
+ * their scope closes; successful generations instead return `Opened.release`. Reporting does
395
+ * not release resources or replace the original acquisition failure. Terminal `Down` and opening
396
+ * `Closed` states publish the reported outcomes of their own generation's tries.
397
+ */
398
+ var Releasing = class extends Context.Service()("effect-browser/Supervisor/Releasing") {};
364
399
  /** Work whose generation was revoked; input receipts still determine whether input reached a site. */
365
400
  var Superseded = class extends Schema.TaggedError()("Superseded", {
366
401
  generation: Schema.Int,
@@ -384,11 +419,6 @@ const describe = (cause) => {
384
419
  const error = Cause.squash(cause);
385
420
  return Predicate.isError(error) ? error.message : String(error);
386
421
  };
387
- const unavailable = (resolution) => new Unavailable({
388
- reason: resolution.reason,
389
- detail: resolution.detail,
390
- cause: resolution.cause
391
- });
392
422
  const timedOut = () => new Unavailable({
393
423
  reason: "opening",
394
424
  detail: "no generation opened within the wait"
@@ -399,7 +429,7 @@ const bounded = (input, name) => Effect.suspend(() => {
399
429
  });
400
430
  /** Resolve immediately before starting a bounded wait, including when its duration is zero. */
401
431
  const waitFor = (state, wait, clock, target) => {
402
- const result = (resolution) => resolution._tag === "Serve" ? Effect.succeed(resolution.live) : Effect.fail(unavailable(resolution));
432
+ const result = (resolution) => resolution._tag === "Serve" ? Effect.succeed(resolution.live) : Effect.fail(new Unavailable(resolution));
403
433
  return Effect.suspend(() => {
404
434
  const current = resolve(state.value, target);
405
435
  return current === void 0 ? SubscriptionRef.changes(state).pipe(Stream.map((state) => resolve(state, target)), Stream.filter(Predicate.isNotUndefined), Stream.runHead, Effect.flatMap(Option.match({
@@ -420,7 +450,7 @@ const untilRotation = (expiresAt, before) => Clock.currentTimeMillis.pipe(Effect
420
450
  const until = (at, now) => Effect.suspend(() => now() >= at ? Effect.void : Effect.sleep(Duration.millis(Math.ceil(at - now()))).pipe(Effect.andThen(until(at, now))));
421
451
  /** Borrowed work and transferred scopes hold a generation until cleanup lets it go. */
422
452
  const leasing = (state, apply, waitTimeout, now, clock) => {
423
- const borrow = (hold, wait) => Effect.gen(function* () {
453
+ const borrow = Effect.fnUntraced(function* (hold, wait) {
424
454
  const until = now() + Duration.toMillis(wait);
425
455
  for (;;) {
426
456
  const step = yield* apply({
@@ -488,7 +518,6 @@ const acquireGeneration = (open, own) => Effect.uninterruptibleMask((restore) =>
488
518
  revoked: Deferred.makeUnsafe(),
489
519
  released: Deferred.makeUnsafe()
490
520
  })))));
491
- const changedUntil = (state, predicate) => SubscriptionRef.changes(state).pipe(Stream.filter(predicate), Stream.runHead, Effect.asVoid);
492
521
  const watchGeneration = (live, rotateBefore, apply) => {
493
522
  const { browser } = live.value.opened;
494
523
  const loss = browser.disconnected.pipe(Effect.flatMap((cause) => apply({
@@ -505,11 +534,10 @@ const watchGeneration = (live, rotateBefore, apply) => {
505
534
  discard: true
506
535
  });
507
536
  };
508
- const releaseGeneration = (command, state, apply) => Effect.gen(function* () {
537
+ const releaseGeneration = Effect.fnUntraced(function* (command, state, apply) {
509
538
  const { live } = command;
510
- const holds = lookup(state.value, live.number)?.holds ?? [];
511
- const cleaned = yield* Effect.forEach(holds, (hold) => Effect.exit(hold._tag === "Retained" ? Scope.close(hold.scope, Exit.void) : Effect.void), { concurrency: 8 });
512
- yield* changedUntil(state, (current) => (lookup(current, live.number)?.holds.size ?? 0) === 0);
539
+ const cleaned = yield* Effect.forEach(lookup(state.value, live.number)?.holds ?? [], (hold) => Effect.exit(hold._tag === "Retained" ? Scope.close(hold.scope, Exit.void) : Effect.void), { concurrency: 8 });
540
+ yield* SubscriptionRef.changes(state).pipe(Stream.filter((current) => (lookup(current, live.number)?.holds.size ?? 0) === 0), Stream.runHead);
513
541
  const released = command._tag === "Leave" ? void 0 : yield* (live.value.opened.release ?? Effect.succeed(new Settled())).pipe(Effect.catchCause((cause) => Effect.succeed(new Unconfirmed({ detail: `the release failed: ${describe(cause)}` }))));
514
542
  const closed = yield* Effect.exit(Scope.close(live.value.scope, Exit.void));
515
543
  yield* apply({
@@ -520,29 +548,21 @@ const releaseGeneration = (command, state, apply) => Effect.gen(function* () {
520
548
  if (released !== void 0) yield* Deferred.succeed(live.value.released, released);
521
549
  yield* Exit.asVoidAll([...cleaned, closed]);
522
550
  });
523
- const openedInput = (number, exit) => Exit.isSuccess(exit) ? {
524
- _tag: "Opened",
525
- live: {
526
- number,
527
- value: exit.value
528
- }
529
- } : Cause.hasInterruptsOnly(exit.cause) ? {
530
- _tag: "Abandoned",
531
- number
532
- } : {
533
- _tag: "Failed",
534
- number,
535
- detail: describe(exit.cause),
536
- cause: Cause.squash(exit.cause)
551
+ const admissionAfter = (state) => {
552
+ const released = state.released;
553
+ const pending = [...state.draining.values()].filter((drain) => drain._tag !== "Leaving").map(({ live }) => live.value.released);
554
+ return pending.length === 0 && released === void 0 ? void 0 : Effect.gen(function* () {
555
+ let outcome = released;
556
+ for (const release of pending) outcome = combine(outcome, yield* Deferred.await(release));
557
+ return outcome ?? new Settled();
558
+ });
537
559
  };
538
- const admissionAfter = (previous, state) => previous === void 0 || state._tag !== "Retired" && state.serving?.number === previous.number ? void 0 : Deferred.await(previous.value.released);
539
560
  const makeScoped = Effect.fn("Supervisor.make")(function* (options) {
540
561
  const clock = yield* Clock.Clock;
541
562
  const now = () => Number(clock.monotonicTimeNanosUnsafe()) / 1e6;
542
563
  const context = yield* Effect.context();
543
564
  const own = yield* Scope.fork(yield* Effect.scope);
544
565
  const exclusive = options.exclusive ?? false;
545
- const definite = options.definite ?? (() => false);
546
566
  const reopen = options.reopen ?? Schedule.exponential("1 second").pipe(Schedule.upTo({ duration: Duration.minutes(5) }));
547
567
  const rotateBefore = options.rotateBefore === void 0 ? void 0 : Duration.fromInputUnsafe(options.rotateBefore);
548
568
  const waitTimeout = yield* bounded(options.waitTimeout ?? Duration.minutes(2), "waitTimeout");
@@ -555,14 +575,14 @@ const makeScoped = Effect.fn("Supervisor.make")(function* (options) {
555
575
  const releases = yield* FiberMap.make();
556
576
  const runOpen = yield* FiberHandle.runtime(opening)();
557
577
  const runRelease = yield* FiberMap.runtime(releases)();
558
- const apply = (input) => applying.withPermit(Effect.gen(function* () {
578
+ const apply = Effect.fnUntraced(function* (input) {
559
579
  const current = state.value;
560
580
  const step = transition(current, input, exclusive, now(), handover);
561
581
  if (step.state !== current) yield* SubscriptionRef.set(state, step.state);
562
582
  yield* perform(step);
563
583
  return step;
564
- })).pipe(Effect.uninterruptible, Effect.provideService(Clock.Clock, clock));
565
- const perform = (step) => Effect.gen(function* () {
584
+ }, (effect) => applying.withPermit(effect), Effect.uninterruptible, Effect.provideService(Clock.Clock, clock));
585
+ const perform = Effect.fnUntraced(function* (step) {
566
586
  for (const [number, generation] of step.events) PubSub.publishUnsafe(events, [new Generation({
567
587
  number,
568
588
  at: clock.currentTimeMillisUnsafe(),
@@ -571,7 +591,7 @@ const makeScoped = Effect.fn("Supervisor.make")(function* (options) {
571
591
  })]);
572
592
  for (const command of step.commands) switch (command._tag) {
573
593
  case "Open":
574
- runOpen(openGeneration(command.number, command.after));
594
+ runOpen(openGeneration(command.number));
575
595
  break;
576
596
  case "Watch":
577
597
  yield* Effect.forkIn(Effect.provideContext(watchGeneration(command.live, rotateBefore, apply), context), command.live.value.scope);
@@ -591,26 +611,56 @@ const makeScoped = Effect.fn("Supervisor.make")(function* (options) {
591
611
  runRelease(command.live.number, releaseGeneration(command, state, apply));
592
612
  }
593
613
  });
594
- const openGeneration = (number, previous) => {
595
- const after = previous === void 0 ? void 0 : Deferred.await(previous.value.released);
596
- const openOnce = acquireGeneration(Effect.suspend(() => options.admit?.({
597
- number,
598
- after: admissionAfter(previous, state.value)
599
- }) ?? Effect.void).pipe(Effect.andThen(options.open)), own).pipe(Effect.provideContext(context));
600
- return Effect.uninterruptibleMask((restore) => restore((exclusive && after !== void 0 ? after : Effect.void).pipe(Effect.andThen(Effect.retry(openOnce, {
614
+ const openGeneration = (number) => {
615
+ const openOnce = acquireGeneration(Effect.gen(function* () {
616
+ const after = admissionAfter(state.value);
617
+ if (exclusive && after !== void 0) yield* after;
618
+ yield* options.admit?.({
619
+ number,
620
+ after
621
+ }) ?? Effect.void;
622
+ return yield* options.open.pipe(Effect.provideService(Releasing, { released: (released) => apply({
623
+ _tag: "AttemptReleased",
624
+ number,
625
+ released
626
+ }).pipe(Effect.asVoid) }));
627
+ }), own).pipe(Effect.provideContext(context));
628
+ return Effect.uninterruptibleMask((restore) => restore(Effect.retry(openOnce, {
601
629
  schedule: reopen,
602
- while: (error) => !definite(error)
603
- })))).pipe(Effect.exit, Effect.flatMap((exit) => apply(openedInput(number, exit)))));
630
+ while: (error) => !options.definite?.(error)
631
+ })).pipe(Effect.exit, Effect.flatMap((exit) => {
632
+ if (Exit.isSuccess(exit)) return apply({
633
+ _tag: "Opened",
634
+ live: {
635
+ number,
636
+ value: exit.value
637
+ }
638
+ });
639
+ const cause = exit.cause;
640
+ return apply(Cause.hasInterruptsOnly(cause) ? {
641
+ _tag: "Abandoned",
642
+ number
643
+ } : {
644
+ _tag: "Failed",
645
+ number,
646
+ detail: describe(cause),
647
+ cause: Cause.squash(cause)
648
+ });
649
+ })));
604
650
  };
605
- const use = leasing(state, apply, waitTimeout, now, clock);
606
651
  const end = yield* Effect.cached(PubSub.publish(events, Exit.void));
607
- const stop = (input) => apply(input).pipe(Effect.andThen(FiberHandle.clear(opening)), Effect.andThen(FiberMap.awaitEmpty(releases)), Effect.andThen(Effect.suspend(() => Deferred.isDone(releases.deferred).pipe(Effect.flatMap((failed) => failed ? FiberMap.join(releases) : Effect.void)))), Effect.ensuring(end), Effect.asVoid);
652
+ const stop = Effect.fnUntraced(function* (input) {
653
+ yield* apply(input);
654
+ yield* FiberHandle.clear(opening);
655
+ yield* FiberMap.awaitEmpty(releases);
656
+ if (yield* Deferred.isDone(releases.deferred)) yield* FiberMap.join(releases);
657
+ }, Effect.ensuring(end));
608
658
  const retire = yield* Effect.cached(stop({ _tag: "Retire" }));
609
659
  yield* Effect.addFinalizer(() => options.keep === true ? stop({ _tag: "Keep" }) : retire);
610
660
  yield* perform(started);
611
661
  return {
612
662
  browser: Effect.map(waitFor(state, waitTimeout, clock), (live) => live.value.opened.browser),
613
- use,
663
+ use: leasing(state, apply, waitTimeout, now, clock),
614
664
  states: Stream.fromPubSubTake(events),
615
665
  rotate: apply({ _tag: "Rotate" }).pipe(Effect.flatMap(({ reply }) => reply === void 0 ? Effect.fail(retired()) : Effect.map(waitFor(state, waitTimeout, clock, reply), (live) => live.value.opened.browser))),
616
666
  retire
@@ -620,6 +670,6 @@ function make(options) {
620
670
  return makeScoped(options);
621
671
  }
622
672
  //#endregion
623
- export { Unconfirmed as _, make as a, Draining as c, Lost as d, Open as f, Settled as g, Reopening as h, Unavailable as i, GenerationState as l, Released as m, Superseded as n, Closed as o, Opening as p, Supervisor_exports as r, Down as s, Generation as t, Kept as u };
673
+ export { Settled as _, Unavailable as a, Down as c, Kept as d, Lost as f, Reopening as g, Released as h, Supervisor_exports as i, Draining as l, Opening as m, Releasing as n, make as o, Open as p, Superseded as r, Closed as s, Generation as t, GenerationState as u, Unconfirmed as v };
624
674
 
625
- //# sourceMappingURL=Supervisor-BE2sGHBK.mjs.map
675
+ //# sourceMappingURL=Supervisor-BgWP8o9t.mjs.map