effect-browser 0.2.0-beta.0 → 0.2.0-beta.1

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 (40) hide show
  1. package/README.md +150 -10
  2. package/dist/{Association-zlgInoaj.mjs → Association-BsSfBYVx.mjs} +14 -10
  3. package/dist/Association-BsSfBYVx.mjs.map +1 -0
  4. package/dist/{Bootstrap-Be-2Ddmh.mjs → Bootstrap-CeQOWBy6.mjs} +6 -4
  5. package/dist/Bootstrap-CeQOWBy6.mjs.map +1 -0
  6. package/dist/Bootstrap.mjs +1 -1
  7. package/dist/Browser.d.mts +13 -1
  8. package/dist/Browser.mjs.map +1 -1
  9. package/dist/{BrowserData-DJAnUHoZ.d.mts → BrowserData-CRuxEZcn.d.mts} +90 -10
  10. package/dist/BrowserData.d.mts +2 -2
  11. package/dist/BrowserData.mjs +85 -7
  12. package/dist/BrowserData.mjs.map +1 -1
  13. package/dist/BrowserRuntime-Coz-5DYb.d.mts +143 -0
  14. package/dist/{BrowserRuntime-fZz1b0VI.mjs → BrowserRuntime-DViKNUdD.mjs} +2819 -2206
  15. package/dist/BrowserRuntime-DViKNUdD.mjs.map +1 -0
  16. package/dist/BrowserRuntime.d.mts +3 -139
  17. package/dist/BrowserRuntime.mjs +1 -1
  18. package/dist/Capture.d.mts +15 -1
  19. package/dist/Capture.mjs +58 -22
  20. package/dist/Capture.mjs.map +1 -1
  21. package/dist/{CaptureData-AmqVP_Ba.mjs → CaptureData-B5-mXlyq.mjs} +24 -6
  22. package/dist/CaptureData-B5-mXlyq.mjs.map +1 -0
  23. package/dist/CaptureData.d.mts +11 -2
  24. package/dist/CaptureData.mjs +1 -1
  25. package/dist/Chromium.d.mts +4 -3
  26. package/dist/Chromium.mjs +22 -16
  27. package/dist/Chromium.mjs.map +1 -1
  28. package/dist/Errors.d.mts +2 -2
  29. package/dist/Errors.mjs +1 -0
  30. package/dist/Errors.mjs.map +1 -1
  31. package/dist/PageControl.d.mts +1 -1
  32. package/dist/PageControl.mjs +1 -1
  33. package/dist/Testing.d.mts +437 -0
  34. package/dist/Testing.mjs +1561 -0
  35. package/dist/Testing.mjs.map +1 -0
  36. package/package.json +5 -1
  37. package/dist/Association-zlgInoaj.mjs.map +0 -1
  38. package/dist/Bootstrap-Be-2Ddmh.mjs.map +0 -1
  39. package/dist/BrowserRuntime-fZz1b0VI.mjs.map +0 -1
  40. package/dist/CaptureData-AmqVP_Ba.mjs.map +0 -1
@@ -1,139 +1,3 @@
1
- import { BrowserError, InitializationError } from "./Errors.mjs";
2
- import { S as Observation, a as BrowserPolicy, c as ClickRequest, m as InlineFile, n as AutomationOptions, q as Viewport, t as ActionResult } from "./BrowserData-DJAnUHoZ.mjs";
3
- import { BrowserSession, OpenOptions } from "./Browser.mjs";
4
- import { Effect, Option, Redacted, Schema, Scope } from "effect";
5
- //#region src/internal/browser/ConnectionCleanup.d.ts
6
- /** Evidence about this connection, independent of the lifetime of its browser process. */
7
- type ConnectionState = "closed" | "failed" | "not-connected" | "pending";
8
- /** Captured teardown operations supplied to the lifetime which owns this connection. */
9
- interface ConnectionCleanup {
10
- readonly fence: Effect.Effect<void>;
11
- readonly capture: Effect.Effect<void, BrowserError>;
12
- readonly initialization: Effect.Effect<void, BrowserError | InitializationError>;
13
- readonly disconnect: Effect.Effect<ConnectionState, BrowserError>;
14
- }
15
- export declare const noConnection: ConnectionCleanup;
16
- type CleanupStep<A> = {
17
- readonly _tag: "Success";
18
- readonly value: A;
19
- } | {
20
- readonly _tag: "Failure";
21
- readonly reason: "timeout" | "interrupted" | "failed";
22
- };
23
- /** Every step settles independently, so one failure never suppresses subsequent teardown. */
24
- export declare const cleanupStep: <A, E, R>(action: Effect.Effect<A, E, R>, timeoutMillis: number) => Effect.Effect<CleanupStep<A>, never, R>;
25
- /** Bounded host notification; reporting cannot change the cleanup evidence it describes. */
26
- export declare const reported: <A, E>(effect: Effect.Effect<A, E>) => Effect.Effect<void>;
27
- //#endregion
28
- //#region src/BrowserRuntime.d.ts
29
- /** Opaque engine configuration. Only this constructor's issued values can acquire a runtime. */
30
- export interface BrowserBinding {
31
- readonly _tag: "BrowserBinding";
32
- }
33
- export interface PlaywrightOptions {
34
- /** A trusted integration validates its own endpoint authority before routing the connection. */
35
- readonly resolveEndpoint?: (connection: {
36
- readonly url: Redacted.Redacted<string>;
37
- }) => Effect.Effect<string, BrowserError>;
38
- /** Trusted instrumentation of the existing native connection, before the owner starts using it. */
39
- readonly onConnected?: (connection: {
40
- readonly native: unknown;
41
- readonly endpoint: string;
42
- }) => void;
43
- }
44
- /** A maintained Playwright connection, loaded lazily. No driver or protocol capability is returned. */
45
- export declare const playwright: (options?: PlaywrightOptions) => BrowserBinding;
46
- /**
47
- * An integration owns endpoint authority and the meaning of release. Its source must register
48
- * release in the supplied Scope before returning; the runtime owns only the connection below it.
49
- */
50
- export interface Lifetime {
51
- readonly reference: unknown;
52
- readonly connection: (remainingMillis: number) => Effect.Effect<Redacted.Redacted<string>, BrowserError>;
53
- readonly release: Effect.Effect<unknown>;
54
- readonly cleanupResult: Effect.Effect<Option.Option<unknown>>;
55
- readonly closeChecked: Effect.Effect<void, BrowserError>;
56
- /**
57
- * Memory-only evidence that canonical owned cleanup terminated native browser control.
58
- * Omission is unknown. Borrowed disconnection alone never establishes this fact.
59
- */
60
- readonly controlRetired?: Effect.Effect<boolean>;
61
- readonly verifyReconnect?: Effect.Effect<void, BrowserError>;
62
- }
63
- export type Source<L extends Lifetime, E, R = never> = (cleanup: ConnectionCleanup, lifetimeDeadline: number) => Effect.Effect<L, E, R | Scope.Scope>;
64
- export interface RuntimeOptions {
65
- readonly implementation: string;
66
- readonly binding?: BrowserBinding;
67
- readonly automation?: AutomationOptions;
68
- readonly viewport?: Viewport;
69
- readonly preserveViewport?: boolean;
70
- readonly keepAlive?: boolean;
71
- }
72
- export interface AcquireOptions<E = never, R = never> extends OpenOptions<E, R> {
73
- /** Attach to an existing page without creating a tab or changing its viewport. */
74
- readonly existingTarget?: {
75
- readonly targetId?: string;
76
- };
77
- }
78
- /**
79
- * Resolved host input for an integration constructing its own runtime. Stored paths must already
80
- * have been authorized by that integration; a session cannot recover these operations afterwards.
81
- */
82
- export type ResolvedFileSelection = {
83
- readonly _tag: "Inline";
84
- readonly files: ReadonlyArray<InlineFile>;
85
- } | {
86
- readonly _tag: "Stored";
87
- readonly paths: ReadonlyArray<string>;
88
- };
89
- export interface FileRequest {
90
- readonly selector: string;
91
- readonly selection: ResolvedFileSelection;
92
- }
93
- declare const DownloadEvent_base: Schema.Class<DownloadEvent, Schema.Struct<{
94
- readonly downloadId: Schema.NonEmptyString;
95
- readonly filename: Schema.NonEmptyString;
96
- readonly state: Schema.Literals<readonly ["completed", "failed"]>;
97
- }>, {}>;
98
- export declare class DownloadEvent extends DownloadEvent_base {}
99
- /**
100
- * Modeled host operations for the integration creating this connection. Native authority stays
101
- * inside the owner; the integration adds only the capabilities its public session supports.
102
- */
103
- export interface Integration<Reference> {
104
- readonly clickForDownload: (request: ClickRequest) => Effect.Effect<DownloadEvent, BrowserError>;
105
- readonly selectFiles: (request: FileRequest) => Effect.Effect<ActionResult, BrowserError>;
106
- readonly clickForFileSelection: (request: FileRequest) => Effect.Effect<ActionResult, BrowserError>;
107
- readonly liveView: <A>(issue: Effect.Effect<A, BrowserError>) => Effect.Effect<A, BrowserError>;
108
- readonly beginHandoff: <A>(issue: Effect.Effect<A, BrowserError>) => Effect.Effect<{
109
- readonly token: Redacted.Redacted<string>;
110
- readonly view: A;
111
- }, BrowserError>;
112
- readonly resume: (token: Redacted.Redacted<string>, operatorReleasedControl: boolean) => Effect.Effect<Observation, BrowserError>;
113
- readonly detach: Effect.Effect<{
114
- readonly reference: Reference;
115
- readonly targetId: string;
116
- }, BrowserError>;
117
- readonly reconnect: (operatorReleasedControl: boolean) => Effect.Effect<Observation, BrowserError | InitializationError>;
118
- }
119
- export interface Connection<Reference, E = never> {
120
- /** Preserve this object's identity when adding an integration's public capabilities. */
121
- readonly session: BrowserSession<E>;
122
- readonly operations: Integration<Reference>;
123
- }
124
- export interface Acquisition<L extends Lifetime, E = never> {
125
- readonly reference: L["reference"];
126
- readonly lifetime: L;
127
- readonly failure: Effect.Effect<never, E | InitializationError>;
128
- readonly close: L["release"];
129
- readonly connect: Effect.Effect<Connection<L["reference"], E>, BrowserError | InitializationError | E>;
130
- }
131
- export interface Runtime {
132
- readonly acquire: <L extends Lifetime, AE, AR, E = never, R = never>(policy: BrowserPolicy, source: Source<L, AE, AR>, request?: AcquireOptions<E, R>) => Effect.Effect<Acquisition<L, E>, AE | BrowserError, Scope.Scope | AR | Exclude<R, Scope.Scope>>;
133
- }
134
- /** Validate immutable connection configuration without loading a peer or acquiring a lifetime. */
135
- export declare const make: (options: RuntimeOptions) => Effect.Effect<Runtime, BrowserError, never>;
136
- //#endregion
137
- export type { CleanupStep, ConnectionCleanup, ConnectionState };
138
- export {};
139
- //# sourceMappingURL=BrowserRuntime.d.mts.map
1
+ import { _ as ConnectionCleanup, a as DownloadEvent, b as noConnection, c as Lifetime, d as Runtime, f as RuntimeOptions, g as CleanupStep, h as playwright, i as Connection, l as PlaywrightOptions, m as make, n as Acquisition, o as FileRequest, p as Source, r as BrowserBinding, s as Integration, t as AcquireOptions, u as ResolvedFileSelection, v as ConnectionState, x as reported, y as cleanupStep } from "./BrowserRuntime-Coz-5DYb.mjs";
2
+ export { AcquireOptions, Acquisition, BrowserBinding, type CleanupStep, Connection, type ConnectionCleanup, type ConnectionState, DownloadEvent, FileRequest, Integration, Lifetime, PlaywrightOptions, ResolvedFileSelection, Runtime, RuntimeOptions, Source, cleanupStep, make, noConnection, playwright, reported };
3
+ export {};
@@ -1,2 +1,2 @@
1
- import { a as noConnection, i as cleanupStep, n as make, o as reported, r as playwright, t as DownloadEvent } from "./BrowserRuntime-fZz1b0VI.mjs";
1
+ import { a as cleanupStep, n as make, o as noConnection, r as playwright, s as reported, t as DownloadEvent } from "./BrowserRuntime-DViKNUdD.mjs";
2
2
  export { DownloadEvent, cleanupStep, make, noConnection, playwright, reported };
@@ -1,7 +1,7 @@
1
1
  import { BrowserError } from "./Errors.mjs";
2
2
  import { BrowserSession } from "./Browser.mjs";
3
3
  import { CaptureOptions, CaptureSize, CaptureSnapshot, CaptureSummary, CapturedFrame } from "./CaptureData.mjs";
4
- import { Effect, Scope, Stream } from "effect";
4
+ import { Crypto, Effect, PlatformError, Scope, Stream } from "effect";
5
5
  //#region src/Capture.d.ts
6
6
  /** Live stream/Effect capabilities intentionally have no data schema or serialization contract. */
7
7
  export interface CaptureInterval {
@@ -34,6 +34,20 @@ export declare const start: <E>(session: BrowserSession<E>, options?: CaptureOpt
34
34
  * explicit stop acknowledgement, interval snapshots and the final capture summary.
35
35
  */
36
36
  export declare const stream: <E>(session: BrowserSession<E>, options?: CaptureOptions) => Stream.Stream<CapturedFrame, BrowserError>;
37
+ /** A `multipart/x-mixed-replace` body and the content type that names its boundary. */
38
+ export interface MultipartBody<E, R> {
39
+ readonly contentType: string;
40
+ readonly body: Stream.Stream<Uint8Array, E, R>;
41
+ }
42
+ /**
43
+ * Frames as motion JPEG for an `<img>`: one `multipart/x-mixed-replace` response, as WHATWG HTML
44
+ * defines it for images. Each frame is followed at once by the next part's delimiter and headers,
45
+ * because a browser shows a part only when it has read the headers of the one after it; without
46
+ * that, a still page's last picture would never be shown. Parts carry no metadata, so nothing
47
+ * but the pictures reaches a viewer. The boundary is drawn from `Crypto` for each response, so a
48
+ * page cannot shape its pixels into it. Call it once per viewer.
49
+ */
50
+ export declare const multipart: <E, R>(frames: Stream.Stream<CapturedFrame, E, R>) => Effect.Effect<MultipartBody<E, R>, PlatformError.PlatformError, Crypto.Crypto>;
37
51
  //#endregion
38
52
  export { CaptureOptions, CaptureSize, CaptureSnapshot, CaptureSummary, CapturedFrame };
39
53
  export {};
package/dist/Capture.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { PageInfo } from "./BrowserData.mjs";
2
1
  import { BrowserError, Reasons } from "./Errors.mjs";
3
- import { n as captureParent, r as jpegGeometry } from "./Association-zlgInoaj.mjs";
4
- import { a as CapturedFrame, i as CaptureSummary, n as CaptureSize, o as CaptureDefaults, r as CaptureSnapshot, s as CaptureLimits, t as CaptureOptions } from "./CaptureData-AmqVP_Ba.mjs";
5
- import { Cause, Clock, Deferred, Effect, Exit, Queue, Schema, Stream } from "effect";
2
+ import { PageInfo } from "./BrowserData.mjs";
3
+ import { n as captureParent, r as jpegGeometry } from "./Association-BsSfBYVx.mjs";
4
+ import { a as CapturedFrame, c as CaptureMaxima, i as CaptureSummary, n as CaptureSize, o as CaptureDefaults, r as CaptureSnapshot, s as CaptureLimits, t as CaptureOptions } from "./CaptureData-B5-mXlyq.mjs";
5
+ import { Cause, Clock, Crypto, Deferred, Effect, Exit, Option, Queue, Schema, Stream } from "effect";
6
6
  //#region src/internal/capture/FrameBuffer.ts
7
7
  /** Callback-side bounded retention. No Promise, Effect, blocked producer, or per-frame fiber. */
8
8
  var FrameBuffer = class {
@@ -15,7 +15,7 @@ var FrameBuffer = class {
15
15
  highWaterBytes = 0;
16
16
  highWaterFrames = 0;
17
17
  constructor(maximumFrames, maximumBytes) {
18
- if (!Number.isSafeInteger(maximumFrames) || maximumFrames < 1 || maximumFrames > 64 || !Number.isSafeInteger(maximumBytes) || maximumBytes < 1 || maximumBytes > 67108864) throw new RangeError("Invalid capture retention limits");
18
+ if (!Number.isSafeInteger(maximumFrames) || maximumFrames < 1 || maximumFrames > CaptureMaxima.frames || !Number.isSafeInteger(maximumBytes) || maximumBytes < 1 || maximumBytes > 67108864) throw new RangeError("Invalid capture retention limits");
19
19
  this.maximumFrames = maximumFrames;
20
20
  this.maximumBytes = maximumBytes;
21
21
  }
@@ -81,7 +81,7 @@ const Metadata = Schema.Struct({
81
81
  maximum: 16384
82
82
  }))
83
83
  });
84
- const parseMetadata = Schema.decodeUnknownSync(Metadata);
84
+ const decodeMetadata = Schema.decodeUnknownOption(Metadata);
85
85
  /** Private seam for deterministic callback/lifetime tests. The public start accepts a real live session. */
86
86
  const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
87
87
  const maxFrames = options.maxFrames ?? CaptureDefaults.maxFrames;
@@ -132,8 +132,11 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
132
132
  const nextDocument = (url) => {
133
133
  if (ended) return;
134
134
  document++;
135
- if (documentBoundaries.length >= MaxDocumentBoundaries) documentBoundariesTruncated = true;
136
- else documentBoundaries.push({
135
+ if (documentBoundaries.length >= MaxDocumentBoundaries) {
136
+ documentBoundaries.shift();
137
+ documentBoundariesTruncated = true;
138
+ }
139
+ documentBoundaries.push({
137
140
  document,
138
141
  observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),
139
142
  afterSequence: received === 0 ? null : received - 1,
@@ -183,17 +186,21 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
183
186
  };
184
187
  const stopNative = (yield* Effect.cached(Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
185
188
  finish("stopped");
186
- if (startPromise !== void 0 && !startSettled) yield* restore(Effect.tryPromise({
187
- try: () => startPromise,
188
- catch: () => BrowserError.make({
189
- operation: "capture-start",
190
- reason: Reasons.Provider.make({}),
191
- outcome: "unknown"
192
- })
193
- }).pipe(Effect.timeout(2e3), Effect.exit));
189
+ if (startPromise !== void 0 && !startSettled) {
190
+ const starting = startPromise;
191
+ yield* restore(Effect.tryPromise({
192
+ try: () => starting,
193
+ catch: () => BrowserError.make({
194
+ operation: "capture-start",
195
+ reason: Reasons.Provider.make({}),
196
+ outcome: "unknown"
197
+ })
198
+ }).pipe(Effect.timeout(2e3), Effect.exit));
199
+ }
194
200
  if (source !== void 0) {
201
+ const stopping = source;
195
202
  const stopped = yield* restore(Effect.tryPromise({
196
- try: () => source.stop(),
203
+ try: () => stopping.stop(),
197
204
  catch: () => BrowserError.make({
198
205
  operation: "capture-stop",
199
206
  reason: Reasons.Provider.make({}),
@@ -221,7 +228,17 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
221
228
  }
222
229
  const sequence = received++;
223
230
  try {
224
- const meta = parseMetadata(frame);
231
+ const decoded = decodeMetadata(frame);
232
+ if (Option.isNone(decoded)) {
233
+ rejected++;
234
+ finish("malformed-frame", BrowserError.make({
235
+ operation: "capture",
236
+ reason: Reasons.Malformed.make({}),
237
+ outcome: "undispatched"
238
+ }));
239
+ return;
240
+ }
241
+ const meta = decoded.value;
225
242
  if (!(frame.data instanceof Uint8Array)) {
226
243
  rejected++;
227
244
  finish("malformed-frame", BrowserError.make({
@@ -334,7 +351,8 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
334
351
  yield* parent.owner.guard("capture-start", (ticket) => Effect.gen(function* () {
335
352
  const resolved = yield* parent.resolve(ticket, options.target);
336
353
  target = Object.freeze(resolved.target);
337
- source = resolved.source;
354
+ const resolvedSource = resolved.source;
355
+ source = resolvedSource;
338
356
  leaseKey = resolved.key;
339
357
  if (parent.captureLeases.has(leaseKey)) return yield* BrowserError.make({
340
358
  operation: "capture",
@@ -372,7 +390,7 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
372
390
  startSettled = false;
373
391
  yield* Effect.tryPromise({
374
392
  try: () => {
375
- startPromise = source.start({
393
+ startPromise = resolvedSource.start({
376
394
  receive,
377
395
  quality,
378
396
  invalidate: (why) => lease?.invalidate(why),
@@ -384,7 +402,7 @@ const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
384
402
  });
385
403
  startPromise.then(() => {
386
404
  startSettled = true;
387
- if (cleanupFinished && nativeStop === "unconfirmed" && leaseKey !== void 0 && parent.captureLeases.get(leaseKey) === lease) source.stop().catch(() => {});
405
+ if (cleanupFinished && nativeStop === "unconfirmed" && leaseKey !== void 0 && parent.captureLeases.get(leaseKey) === lease) resolvedSource.stop().catch(() => {});
388
406
  }, () => {
389
407
  startSettled = true;
390
408
  });
@@ -470,7 +488,25 @@ const start = (session, options = {}) => Effect.suspend(() => {
470
488
  * explicit stop acknowledgement, interval snapshots and the final capture summary.
471
489
  */
472
490
  const stream = (session, options = {}) => Stream.unwrap(start(session, options).pipe(Effect.map((interval) => interval.frames)));
491
+ /**
492
+ * Frames as motion JPEG for an `<img>`: one `multipart/x-mixed-replace` response, as WHATWG HTML
493
+ * defines it for images. Each frame is followed at once by the next part's delimiter and headers,
494
+ * because a browser shows a part only when it has read the headers of the one after it; without
495
+ * that, a still page's last picture would never be shown. Parts carry no metadata, so nothing
496
+ * but the pictures reaches a viewer. The boundary is drawn from `Crypto` for each response, so a
497
+ * page cannot shape its pixels into it. Call it once per viewer.
498
+ */
499
+ const multipart = (frames) => Effect.map(Effect.flatMap(Crypto.Crypto, (crypto) => crypto.randomUUIDv4), (uuid) => {
500
+ const boundary = `frame-${uuid.replaceAll("-", "")}`;
501
+ const encoder = new TextEncoder();
502
+ const opening = encoder.encode(`--${boundary}\r\nContent-Type: image/jpeg\r\n\r\n`);
503
+ const next = encoder.encode(`\r\n--${boundary}\r\nContent-Type: image/jpeg\r\n\r\n`);
504
+ return {
505
+ contentType: `multipart/x-mixed-replace; boundary=${boundary}`,
506
+ body: Stream.make(opening).pipe(Stream.concat(frames.pipe(Stream.flatMap((frame) => Stream.make(frame.bytes, next)))), Stream.concat(Stream.make(encoder.encode(`--${boundary}--\r\n`))))
507
+ };
508
+ });
473
509
  //#endregion
474
- export { CaptureOptions, CaptureSize, CaptureSnapshot, CaptureSummary, CapturedFrame, start, stream };
510
+ export { CaptureOptions, CaptureSize, CaptureSnapshot, CaptureSummary, CapturedFrame, multipart, start, stream };
475
511
 
476
512
  //# sourceMappingURL=Capture.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Capture.mjs","names":[],"sources":["../src/internal/capture/FrameBuffer.ts","../src/internal/capture/Capture.ts","../src/Capture.ts"],"sourcesContent":["/** Callback-side bounded retention. No Promise, Effect, blocked producer, or per-frame fiber. */\nexport class FrameBuffer<A extends { readonly bytes: Uint8Array }> {\n readonly maximumFrames: number;\n readonly maximumBytes: number;\n private readonly items: A[] = [];\n private retained = 0;\n dropped = 0;\n received = 0;\n highWaterBytes = 0;\n highWaterFrames = 0;\n\n constructor(maximumFrames: number, maximumBytes: number) {\n if (\n !Number.isSafeInteger(maximumFrames) ||\n maximumFrames < 1 ||\n maximumFrames > 64 ||\n !Number.isSafeInteger(maximumBytes) ||\n maximumBytes < 1 ||\n maximumBytes > 64 * 1024 * 1024\n ) {\n throw new RangeError(\"Invalid capture retention limits\");\n }\n this.maximumFrames = maximumFrames;\n this.maximumBytes = maximumBytes;\n }\n\n offer(frame: A): boolean {\n this.received++;\n if (frame.bytes.byteLength > this.maximumBytes) {\n this.dropped++;\n\n return false;\n }\n while (\n this.items.length >= this.maximumFrames ||\n this.retained + frame.bytes.byteLength > this.maximumBytes\n ) {\n const removed = this.items.shift();\n\n if (removed === undefined) break;\n this.retained -= removed.bytes.byteLength;\n this.dropped++;\n }\n this.items.push(frame);\n this.retained += frame.bytes.byteLength;\n this.highWaterBytes = Math.max(this.highWaterBytes, this.retained);\n this.highWaterFrames = Math.max(this.highWaterFrames, this.items.length);\n\n return true;\n }\n\n take(): A | undefined {\n const frame = this.items.shift();\n\n if (frame !== undefined) this.retained -= frame.bytes.byteLength;\n\n return frame;\n }\n\n clear(): void {\n this.items.length = 0;\n this.retained = 0;\n }\n\n get size(): number {\n return this.items.length;\n }\n get bytes(): number {\n return this.retained;\n }\n}\n","import { Cause, Clock, Deferred, Effect, Exit, Queue, Schema, Stream } from \"effect\";\n\nimport type { Target } from \"../../BrowserData.ts\";\nimport type { CaptureInterval } from \"../../Capture.ts\";\nimport {\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n type CaptureOptions,\n type CapturedFrame,\n} from \"../../CaptureData.ts\";\nimport { BrowserError, Reasons } from \"../../Errors.ts\";\nimport { type CaptureLease, type CaptureParent } from \"../browser/Association.ts\";\nimport type { CaptureSource, NativeFrame } from \"../browser/Driver.ts\";\nimport { jpegGeometry } from \"../browser/Images.ts\";\nimport { FrameBuffer } from \"./FrameBuffer.ts\";\nimport { CaptureDefaults, CaptureLimits } from \"./Options.ts\";\n\nconst MaxParentCaptures = 4;\nconst MaxParentBufferedBytes = 64 * 1024 * 1024;\nconst MaxDocumentBoundaries = 64;\nconst MaxDocumentUrlLength = 8192;\n\n/** An over-long address is recorded as null rather than cut into one that was never shown. */\nconst documentUrl = (url: unknown): string | null =>\n typeof url === \"string\" && url.length <= MaxDocumentUrlLength ? url : null;\n\n/**\n * Chromium stamps a screencast frame on its UI thread, encodes it on an unsequenced thread\n * pool, and emits it when that encode completes. It admits a new frame while at most two are\n * unacknowledged (`kMaxScreencastFramesInFlight`), so up to three encode at once and two\n * frames stamped close together can complete in either order. Every frame stamped before an\n * accepted one was already in flight when that one was stamped, so at most two late frames\n * can arrive in a row. A longer run is source time that really went backwards.\n */\nconst MaxConsecutiveLateFrames = 2;\n\n// Synchronous Schema decoding at the callback boundary; no Effect/Fiber is allocated for each frame.\nconst Metadata = Schema.Struct({\n timestamp: Schema.Finite.check(Schema.isGreaterThan(0)),\n viewportWidth: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 16384 })),\n viewportHeight: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 16384 })),\n});\n\nconst parseMetadata = Schema.decodeUnknownSync(Metadata);\n\n/** Private seam for deterministic callback/lifetime tests. The public start accepts a real live session. */\nexport const startCapture = Effect.fnUntraced(function* (\n parent: CaptureParent,\n options: CaptureOptions = {},\n) {\n const maxFrames = options.maxFrames ?? CaptureDefaults.maxFrames;\n const maxBytes = options.maxBufferedBytes ?? CaptureDefaults.maxBufferedBytes;\n const maxFrameBytes = options.maxFrameBytes ?? CaptureDefaults.maxFrameBytes;\n const duration = options.maxDurationMillis ?? CaptureDefaults.maxDurationMillis;\n const quality = options.quality ?? CaptureDefaults.quality;\n\n const size =\n options.size === undefined\n ? undefined\n : yield* Schema.decodeEffect(CaptureSize)(options.size, { onExcessProperty: \"error\" }).pipe(\n Effect.map(({ width, height }) => Object.freeze({ width, height })),\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n );\n\n yield* Schema.decodeEffect(CaptureLimits)({\n maxFrames,\n maxBufferedBytes: maxBytes,\n maxFrameBytes,\n maxDurationMillis: duration,\n quality,\n }).pipe(\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n );\n const clock = yield* Clock.Clock;\n const wake = yield* Queue.dropping<void>(1);\n const finished = yield* Queue.dropping<void>(1);\n const completed = yield* Deferred.make<void>();\n\n yield* Effect.addFinalizer(() =>\n Queue.shutdown(wake).pipe(Effect.andThen(Queue.shutdown(finished)), Effect.asVoid),\n );\n const buffer = new FrameBuffer<CapturedFrame>(maxFrames, maxBytes);\n let source: CaptureSource | undefined;\n let startPromise: Promise<void> | undefined;\n let startSettled = true;\n let target: Target | undefined;\n let leaseKey: string | undefined;\n\n let ended = false,\n subscribed = false,\n cleanupFinished = false;\n\n let reason = \"stopped\";\n let error: BrowserError | undefined;\n\n let received = 0,\n delivered = 0,\n rejected = 0,\n duplicates = 0,\n late = 0,\n lateRun = 0;\n\n let first: number | undefined, last: number | undefined;\n let document = 0;\n let initialUrl: string | null = null;\n let documentBoundariesTruncated = false;\n const documentBoundaries: Array<CaptureSummary[\"documentBoundaries\"][number]> = [];\n\n /** The page navigated and its screencast kept running: later frames belong to a new document. */\n const nextDocument = (url: string): void => {\n if (ended) return;\n document++;\n if (documentBoundaries.length >= MaxDocumentBoundaries) documentBoundariesTruncated = true;\n else\n documentBoundaries.push({\n document,\n observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n afterSequence: received === 0 ? null : received - 1,\n url: documentUrl(url),\n });\n };\n\n let geometry:\n | { width: number; height: number; viewportWidth: number; viewportHeight: number }\n | undefined;\n\n let nativeStop: CaptureSummary[\"nativeStop\"] = \"unconfirmed\";\n let lease: CaptureLease | undefined;\n\n const finish = (why: string, failure?: BrowserError) => {\n if (ended) return;\n ended = true;\n reason = why;\n error = failure;\n Queue.offerUnsafe(wake, undefined);\n Queue.offerUnsafe(finished, undefined);\n };\n\n const snapshot = (): CaptureSummary => {\n if (target === undefined) throw new Error(\"Capture target was not admitted\");\n\n return CaptureSummary.make({\n target,\n reason,\n received,\n delivered,\n discarded: buffer.dropped + rejected + late + duplicates,\n overflow: buffer.dropped,\n duplicates,\n late,\n rejected,\n peakBufferedFrames: buffer.highWaterFrames,\n peakBufferedBytes: buffer.highWaterBytes,\n bufferedFrames: buffer.size,\n bufferedBytes: buffer.bytes,\n sourceFirstMillis: first ?? null,\n sourceLastMillis: last ?? null,\n initialUrl,\n documentBoundaries: documentBoundaries.map((boundary) => ({ ...boundary })),\n documentBoundariesTruncated,\n nativeStop,\n upstreamDrops: \"unknown\",\n ...(error === undefined\n ? {}\n : {\n error: BrowserError.make({\n operation: error.operation,\n reason: { ...error.reason },\n outcome: error.outcome,\n }),\n }),\n });\n };\n\n const performStop = yield* Effect.cached(\n Effect.uninterruptibleMask((restore) =>\n Effect.gen(function* () {\n finish(\"stopped\");\n // Wait for an in-flight start before stop. If it will not settle, quarantine this page lease.\n if (startPromise !== undefined && !startSettled) {\n yield* restore(\n Effect.tryPromise({\n try: () => startPromise!,\n catch: () =>\n BrowserError.make({\n operation: \"capture-start\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n }).pipe(Effect.timeout(2000), Effect.exit),\n );\n }\n if (source !== undefined) {\n const stopped = yield* restore(\n Effect.tryPromise({\n try: () => source!.stop(),\n catch: () =>\n BrowserError.make({\n operation: \"capture-stop\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n }).pipe(Effect.timeout(3000)),\n ).pipe(Effect.exit);\n\n if (Exit.isSuccess(stopped) && startSettled) nativeStop = \"confirmed\";\n } else nativeStop = \"confirmed\";\n // No older attachment may clear a replacement. Unconfirmed native cleanup quarantines only this page.\n if (\n nativeStop === \"confirmed\" &&\n leaseKey !== undefined &&\n lease !== undefined &&\n parent.captureLeases.get(leaseKey) === lease\n ) {\n parent.captureLeases.delete(leaseKey);\n parent.captureReservedBytes = Math.max(\n 0,\n parent.captureReservedBytes - lease.reservedBytes,\n );\n }\n cleanupFinished = true;\n\n return snapshot();\n }),\n ),\n );\n\n // Publish completion only after the cached cleanup Exit has settled. Publishing\n // inside it lets the caller close Scope and interrupt the caching fiber first.\n const stopNative = performStop.pipe(Effect.tap(() => Deferred.succeed(completed, undefined)));\n\n const receive = (frame: NativeFrame): void => {\n if (ended) return;\n if (\n target === undefined ||\n parent.owner.state.generation !== target.generation ||\n parent.owner.state.phase !== \"open\"\n ) {\n finish(\n \"parent-unavailable\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Closed.make({}),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n const sequence = received++;\n\n try {\n const meta = parseMetadata(frame);\n\n if (!(frame.data instanceof Uint8Array)) {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({ path: \"bytes\" }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n if (frame.data.length > maxFrameBytes) {\n rejected++;\n finish(\n \"frame-limit\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Limit.make({\n dimension: \"frame-bytes\",\n maximum: maxFrameBytes,\n observed: frame.data.length,\n }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n if (last !== undefined && meta.timestamp < last) {\n // A newer frame is already accepted, so this one can no longer be presented in order.\n // It is discarded and counted rather than sorted in or given an invented time.\n late++;\n if (++lateRun > MaxConsecutiveLateFrames) {\n finish(\n \"timestamp-discontinuity\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Timestamp.make({}),\n outcome: \"undispatched\",\n }),\n );\n }\n\n return;\n }\n if (last === meta.timestamp) {\n duplicates++;\n\n return;\n }\n const dimensions = jpegGeometry(frame.data);\n\n if (frame.data[frame.data.length - 2] !== 255 || frame.data[frame.data.length - 1] !== 217) {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({ path: \"bytes\" }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n const maximumWidth = Math.min(16384, size?.width ?? 16384);\n const maximumHeight = Math.min(16384, size?.height ?? 16384);\n\n const excessive =\n dimensions.width > maximumWidth\n ? Reasons.Limit.make({\n dimension: \"width\",\n maximum: maximumWidth,\n observed: dimensions.width,\n })\n : dimensions.height > maximumHeight\n ? Reasons.Limit.make({\n dimension: \"height\",\n maximum: maximumHeight,\n observed: dimensions.height,\n })\n : dimensions.width * dimensions.height > 33_554_432\n ? Reasons.Limit.make({\n dimension: \"pixels\",\n maximum: 33_554_432,\n observed: dimensions.width * dimensions.height,\n })\n : undefined;\n\n if (excessive !== undefined) {\n rejected++;\n finish(\n \"frame-limit\",\n BrowserError.make({ operation: \"capture\", reason: excessive, outcome: \"undispatched\" }),\n );\n\n return;\n }\n if (\n geometry !== undefined &&\n (geometry.width !== dimensions.width ||\n geometry.height !== dimensions.height ||\n geometry.viewportWidth !== meta.viewportWidth ||\n geometry.viewportHeight !== meta.viewportHeight)\n ) {\n rejected++;\n finish(\n \"resized\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Resized.make({}),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n geometry ??= {\n ...dimensions,\n viewportWidth: meta.viewportWidth,\n viewportHeight: meta.viewportHeight,\n };\n first ??= meta.timestamp;\n last = meta.timestamp;\n lateRun = 0;\n buffer.offer({\n bytes: new Uint8Array(frame.data),\n mediaType: \"image/jpeg\",\n target,\n sequence,\n document,\n sourceTimeMillis: meta.timestamp,\n sourceClock: \"presentation-unix-millis\",\n receivedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n ...dimensions,\n viewportWidth: meta.viewportWidth,\n viewportHeight: meta.viewportHeight,\n });\n Queue.offerUnsafe(wake, undefined);\n } catch {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({}),\n outcome: \"undispatched\",\n }),\n );\n }\n };\n\n yield* parent.owner\n .guard(\n \"capture-start\",\n (ticket) =>\n Effect.gen(function* () {\n const resolved = yield* parent.resolve(ticket, options.target);\n\n // Frames and summaries share this identity with the generation guard. It must not\n // become writable through consumer-owned frame data.\n target = Object.freeze(resolved.target);\n source = resolved.source;\n leaseKey = resolved.key;\n if (parent.captureLeases.has(leaseKey)) {\n return yield* BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Busy.make({}),\n outcome: \"undispatched\",\n });\n }\n if (\n parent.captureLeases.size >= MaxParentCaptures ||\n parent.captureReservedBytes + maxBytes > MaxParentBufferedBytes\n ) {\n return yield* BrowserError.make({\n operation: \"capture\",\n reason:\n parent.captureLeases.size >= MaxParentCaptures\n ? Reasons.Limit.make({\n dimension: \"captures\",\n maximum: MaxParentCaptures,\n observed: parent.captureLeases.size + 1,\n })\n : Reasons.Limit.make({\n dimension: \"buffered-bytes\",\n maximum: MaxParentBufferedBytes,\n observed: parent.captureReservedBytes + maxBytes,\n }),\n outcome: \"undispatched\",\n });\n }\n lease = {\n reservedBytes: maxBytes,\n stop: stopNative.pipe(Effect.asVoid),\n invalidate: (why) =>\n finish(\n why,\n BrowserError.make({\n operation: \"capture\",\n reason:\n why === \"resized\" ? Reasons.Resized.make({}) : Reasons.TargetChanged.make({}),\n outcome: \"undispatched\",\n }),\n ),\n };\n parent.captureLeases.set(leaseKey, lease);\n parent.captureReservedBytes += maxBytes;\n // Installed before native acquisition. A cancelled capture cannot escape its caller's scope.\n yield* Effect.addFinalizer(() =>\n stopNative.pipe(\n Effect.andThen(\n Effect.sync(() => {\n rejected += buffer.size;\n buffer.clear();\n }),\n ),\n ),\n );\n startSettled = false;\n yield* Effect.tryPromise({\n try: () => {\n startPromise = source!.start({\n receive,\n quality,\n invalidate: (why) => lease?.invalidate(why),\n opened: (url) => {\n initialUrl = documentUrl(url);\n },\n ...(size === undefined ? {} : { size }),\n ...(options.lifetime === \"page\" ? { document: nextDocument } : {}),\n });\n void startPromise.then(\n () => {\n startSettled = true;\n if (\n cleanupFinished &&\n nativeStop === \"unconfirmed\" &&\n leaseKey !== undefined &&\n parent.captureLeases.get(leaseKey) === lease\n ) {\n // One late acquisition cleanup, not a callback-side worker. Keep the page quarantined.\n void source!.stop().catch(() => {});\n }\n },\n () => {\n startSettled = true;\n },\n );\n\n return startPromise;\n },\n catch: () =>\n BrowserError.make({\n operation: \"capture-start\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n });\n ticket.check();\n }),\n { charge: false },\n )\n .pipe(\n Effect.onError(() => (lease === undefined ? Effect.void : stopNative.pipe(Effect.asVoid))),\n Effect.onInterrupt(() =>\n lease === undefined ? Effect.void : stopNative.pipe(Effect.asVoid),\n ),\n );\n // One monitor, not a fiber per frame. The finalizer also calls stop directly if this monitor is interrupted.\n // The timeout and explicit-finish branches intentionally carry different\n // values before shared cleanup; preserve the proven first-completion semantics.\n // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off\n yield* Effect.raceFirst(\n Queue.take(finished).pipe(Effect.as(false)),\n Effect.sleep(duration).pipe(Effect.as(true)),\n ).pipe(\n Effect.tap((expired) => (expired ? Effect.sync(() => finish(\"duration-limit\")) : Effect.void)),\n Effect.andThen(stopNative),\n Effect.forkScoped,\n );\n\n const next: Effect.Effect<CapturedFrame, BrowserError | Cause.Done> = Effect.suspend(() => {\n const frame = buffer.take();\n\n if (frame !== undefined) {\n delivered++;\n\n return Effect.succeed(frame);\n }\n if (ended) return error === undefined ? Cause.done() : Effect.fail(error);\n\n return Queue.take(wake).pipe(Effect.andThen(next));\n });\n\n const frames = Stream.unwrap(\n Effect.suspend(() => {\n if (subscribed)\n return Effect.fail(\n BrowserError.make({\n operation: \"capture-consume\",\n reason: Reasons.Busy.make({}),\n outcome: \"undispatched\",\n }),\n );\n subscribed = true;\n\n return Effect.succeed(\n Stream.fromEffectRepeat(next).pipe(Stream.ensuring(stopNative.pipe(Effect.asVoid))),\n );\n }),\n );\n\n return {\n frames,\n snapshot: Effect.sync(() => {\n const { reason, nativeStop, ...metadata } = snapshot();\n\n return CaptureSnapshot.make({\n ...metadata,\n phase: cleanupFinished ? \"stopped\" : ended ? \"stopping\" : \"capturing\",\n observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n currentDocument: document,\n reason: ended ? reason : null,\n nativeStop: cleanupFinished ? nativeStop : null,\n });\n }),\n stop: stopNative,\n completed: Deferred.await(completed).pipe(Effect.map(snapshot)),\n } satisfies CaptureInterval;\n});\n","import { Effect, Schema, type Scope, Stream } from \"effect\";\n\nimport type { BrowserSession } from \"./Browser.ts\";\nimport { PageInfo } from \"./BrowserData.ts\";\nimport type {\n CapturedFrame,\n CaptureOptions,\n CaptureSnapshot,\n CaptureSummary,\n} from \"./CaptureData.ts\";\nimport { BrowserError, Reasons } from \"./Errors.ts\";\nimport { captureParent } from \"./internal/browser/Association.ts\";\nimport { startCapture } from \"./internal/capture/Capture.ts\";\n\nexport {\n CapturedFrame,\n CaptureOptions,\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n} from \"./CaptureData.ts\";\n\n/** Live stream/Effect capabilities intentionally have no data schema or serialization contract. */\nexport interface CaptureInterval {\n /** Single subscription. Ending or interrupting it stops this interval, not its browser. */\n readonly frames: Stream.Stream<CapturedFrame, BrowserError>;\n /**\n * A bounded copy of recorded metadata, available during capture and after cleanup. No native\n * work, budget charge or subscription. Drain frames, then read `completed` for final accounting.\n */\n readonly snapshot: Effect.Effect<CaptureSnapshot>;\n readonly stop: Effect.Effect<CaptureSummary>;\n readonly completed: Effect.Effect<CaptureSummary>;\n}\n\n/**\n * Capture one remote page independently of the session's selected page.\n *\n * Requires the exact live session returned by the host; copying a session object or decoding\n * a durable reference cannot copy its capture authority. The private owner is not frame data.\n * `CapturedFrame` and `CaptureOptions` are Schema values as well as structural types. Frame\n * decoding checks binary/metadata fields, not the complete JPEG bitstream or target authority,\n * and does not copy bytes. Options decoding preserves omissions; admission applies defaults.\n * `CaptureInterval` remains a live scoped capability, not a schema or JSON/Tool value.\n */\nexport const start = <E>(\n session: BrowserSession<E>,\n options: CaptureOptions = {},\n): Effect.Effect<CaptureInterval, BrowserError, Scope.Scope> =>\n Effect.suspend(() => {\n const parent = captureParent(session);\n\n if (parent === undefined)\n return Effect.fail(\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.UnregisteredSession.make({}),\n outcome: \"undispatched\",\n }),\n );\n if (options.target === undefined) return startCapture(parent, options);\n\n return Schema.decodeEffect(PageInfo)(options.target, { onExcessProperty: \"error\" }).pipe(\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n Effect.flatMap((target) => startCapture(parent, { ...options, target })),\n );\n });\n\n/**\n * A lazy, scoped frame stream. Each subscription acquires one interval and finalizes it when\n * consumption ends, fails or is interrupted; the browser remains owned by its caller.\n * Concurrent subscriptions on the same page are refused by the existing capture reservation.\n * Unconfirmed native cleanup retains the page reservation. Use start when the host needs\n * explicit stop acknowledgement, interval snapshots and the final capture summary.\n */\nexport const stream = <E>(\n session: BrowserSession<E>,\n options: CaptureOptions = {},\n): Stream.Stream<CapturedFrame, BrowserError> =>\n Stream.unwrap(start(session, options).pipe(Effect.map((interval) => interval.frames)));\n"],"mappings":"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"}
1
+ {"version":3,"file":"Capture.mjs","names":[],"sources":["../src/internal/capture/FrameBuffer.ts","../src/internal/capture/Capture.ts","../src/Capture.ts"],"sourcesContent":["import { CaptureMaxima } from \"./Options.ts\";\n\n/** Callback-side bounded retention. No Promise, Effect, blocked producer, or per-frame fiber. */\nexport class FrameBuffer<A extends { readonly bytes: Uint8Array }> {\n readonly maximumFrames: number;\n readonly maximumBytes: number;\n private readonly items: A[] = [];\n private retained = 0;\n dropped = 0;\n received = 0;\n highWaterBytes = 0;\n highWaterFrames = 0;\n\n constructor(maximumFrames: number, maximumBytes: number) {\n if (\n !Number.isSafeInteger(maximumFrames) ||\n maximumFrames < 1 ||\n maximumFrames > CaptureMaxima.frames ||\n !Number.isSafeInteger(maximumBytes) ||\n maximumBytes < 1 ||\n maximumBytes > 64 * 1024 * 1024\n ) {\n throw new RangeError(\"Invalid capture retention limits\");\n }\n this.maximumFrames = maximumFrames;\n this.maximumBytes = maximumBytes;\n }\n\n offer(frame: A): boolean {\n this.received++;\n if (frame.bytes.byteLength > this.maximumBytes) {\n this.dropped++;\n\n return false;\n }\n while (\n this.items.length >= this.maximumFrames ||\n this.retained + frame.bytes.byteLength > this.maximumBytes\n ) {\n const removed = this.items.shift();\n\n if (removed === undefined) break;\n this.retained -= removed.bytes.byteLength;\n this.dropped++;\n }\n this.items.push(frame);\n this.retained += frame.bytes.byteLength;\n this.highWaterBytes = Math.max(this.highWaterBytes, this.retained);\n this.highWaterFrames = Math.max(this.highWaterFrames, this.items.length);\n\n return true;\n }\n\n take(): A | undefined {\n const frame = this.items.shift();\n\n if (frame !== undefined) this.retained -= frame.bytes.byteLength;\n\n return frame;\n }\n\n clear(): void {\n this.items.length = 0;\n this.retained = 0;\n }\n\n get size(): number {\n return this.items.length;\n }\n get bytes(): number {\n return this.retained;\n }\n}\n","import { Cause, Clock, Deferred, Effect, Exit, Option, Queue, Schema, Stream } from \"effect\";\n\nimport type { Target } from \"../../BrowserData.ts\";\nimport type { CaptureInterval } from \"../../Capture.ts\";\nimport {\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n type CaptureOptions,\n type CapturedFrame,\n} from \"../../CaptureData.ts\";\nimport { BrowserError, Reasons } from \"../../Errors.ts\";\nimport { type CaptureLease, type CaptureParent } from \"../browser/Association.ts\";\nimport type { CaptureSource, NativeFrame } from \"../browser/Driver.ts\";\nimport { jpegGeometry } from \"../browser/Images.ts\";\nimport { FrameBuffer } from \"./FrameBuffer.ts\";\nimport { CaptureDefaults, CaptureLimits } from \"./Options.ts\";\n\nconst MaxParentCaptures = 4;\nconst MaxParentBufferedBytes = 64 * 1024 * 1024;\nconst MaxDocumentBoundaries = 64;\nconst MaxDocumentUrlLength = 8192;\n\n/** An over-long address is recorded as null rather than cut into one that was never shown. */\nconst documentUrl = (url: unknown): string | null =>\n typeof url === \"string\" && url.length <= MaxDocumentUrlLength ? url : null;\n\n/**\n * Chromium stamps a screencast frame on its UI thread, encodes it on an unsequenced thread\n * pool, and emits it when that encode completes. It admits a new frame while at most two are\n * unacknowledged (`kMaxScreencastFramesInFlight`), so up to three encode at once and two\n * frames stamped close together can complete in either order. Every frame stamped before an\n * accepted one was already in flight when that one was stamped, so at most two late frames\n * can arrive in a row. A longer run is source time that really went backwards.\n */\nconst MaxConsecutiveLateFrames = 2;\n\n// Synchronous Schema decoding at the callback boundary; no Effect/Fiber is allocated for each frame.\nconst Metadata = Schema.Struct({\n timestamp: Schema.Finite.check(Schema.isGreaterThan(0)),\n viewportWidth: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 16384 })),\n viewportHeight: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 16384 })),\n});\n\nconst decodeMetadata = Schema.decodeUnknownOption(Metadata);\n\n/** Private seam for deterministic callback/lifetime tests. The public start accepts a real live session. */\nexport const startCapture = Effect.fnUntraced(function* (\n parent: CaptureParent,\n options: CaptureOptions = {},\n) {\n const maxFrames = options.maxFrames ?? CaptureDefaults.maxFrames;\n const maxBytes = options.maxBufferedBytes ?? CaptureDefaults.maxBufferedBytes;\n const maxFrameBytes = options.maxFrameBytes ?? CaptureDefaults.maxFrameBytes;\n const duration = options.maxDurationMillis ?? CaptureDefaults.maxDurationMillis;\n const quality = options.quality ?? CaptureDefaults.quality;\n\n const size =\n options.size === undefined\n ? undefined\n : yield* Schema.decodeEffect(CaptureSize)(options.size, { onExcessProperty: \"error\" }).pipe(\n Effect.map(({ width, height }) => Object.freeze({ width, height })),\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n );\n\n yield* Schema.decodeEffect(CaptureLimits)({\n maxFrames,\n maxBufferedBytes: maxBytes,\n maxFrameBytes,\n maxDurationMillis: duration,\n quality,\n }).pipe(\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n );\n const clock = yield* Clock.Clock;\n const wake = yield* Queue.dropping<void>(1);\n const finished = yield* Queue.dropping<void>(1);\n const completed = yield* Deferred.make<void>();\n\n yield* Effect.addFinalizer(() =>\n Queue.shutdown(wake).pipe(Effect.andThen(Queue.shutdown(finished)), Effect.asVoid),\n );\n const buffer = new FrameBuffer<CapturedFrame>(maxFrames, maxBytes);\n let source: CaptureSource | undefined;\n let startPromise: Promise<void> | undefined;\n let startSettled = true;\n let target: Target | undefined;\n let leaseKey: string | undefined;\n\n let ended = false,\n subscribed = false,\n cleanupFinished = false;\n\n let reason = \"stopped\";\n let error: BrowserError | undefined;\n\n let received = 0,\n delivered = 0,\n rejected = 0,\n duplicates = 0,\n late = 0,\n lateRun = 0;\n\n let first: number | undefined, last: number | undefined;\n let document = 0;\n let initialUrl: string | null = null;\n let documentBoundariesTruncated = false;\n const documentBoundaries: Array<CaptureSummary[\"documentBoundaries\"][number]> = [];\n\n /** The page navigated and its screencast kept running: later frames belong to a new document. */\n const nextDocument = (url: string): void => {\n if (ended) return;\n document++;\n // The latest boundaries are kept: a live consumer needs the address of what it is showing now.\n if (documentBoundaries.length >= MaxDocumentBoundaries) {\n documentBoundaries.shift();\n documentBoundariesTruncated = true;\n }\n documentBoundaries.push({\n document,\n observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n afterSequence: received === 0 ? null : received - 1,\n url: documentUrl(url),\n });\n };\n\n let geometry:\n | { width: number; height: number; viewportWidth: number; viewportHeight: number }\n | undefined;\n\n let nativeStop: CaptureSummary[\"nativeStop\"] = \"unconfirmed\";\n let lease: CaptureLease | undefined;\n\n const finish = (why: string, failure?: BrowserError) => {\n if (ended) return;\n ended = true;\n reason = why;\n error = failure;\n Queue.offerUnsafe(wake, undefined);\n Queue.offerUnsafe(finished, undefined);\n };\n\n const snapshot = (): CaptureSummary => {\n if (target === undefined) throw new Error(\"Capture target was not admitted\");\n\n return CaptureSummary.make({\n target,\n reason,\n received,\n delivered,\n discarded: buffer.dropped + rejected + late + duplicates,\n overflow: buffer.dropped,\n duplicates,\n late,\n rejected,\n peakBufferedFrames: buffer.highWaterFrames,\n peakBufferedBytes: buffer.highWaterBytes,\n bufferedFrames: buffer.size,\n bufferedBytes: buffer.bytes,\n sourceFirstMillis: first ?? null,\n sourceLastMillis: last ?? null,\n initialUrl,\n documentBoundaries: documentBoundaries.map((boundary) => ({ ...boundary })),\n documentBoundariesTruncated,\n nativeStop,\n upstreamDrops: \"unknown\",\n ...(error === undefined\n ? {}\n : {\n error: BrowserError.make({\n operation: error.operation,\n reason: { ...error.reason },\n outcome: error.outcome,\n }),\n }),\n });\n };\n\n const performStop = yield* Effect.cached(\n Effect.uninterruptibleMask((restore) =>\n Effect.gen(function* () {\n finish(\"stopped\");\n // Wait for an in-flight start before stop. If it will not settle, quarantine this page lease.\n if (startPromise !== undefined && !startSettled) {\n const starting = startPromise;\n\n yield* restore(\n Effect.tryPromise({\n try: () => starting,\n catch: () =>\n BrowserError.make({\n operation: \"capture-start\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n }).pipe(Effect.timeout(2000), Effect.exit),\n );\n }\n if (source !== undefined) {\n const stopping = source;\n\n const stopped = yield* restore(\n Effect.tryPromise({\n try: () => stopping.stop(),\n catch: () =>\n BrowserError.make({\n operation: \"capture-stop\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n }).pipe(Effect.timeout(3000)),\n ).pipe(Effect.exit);\n\n if (Exit.isSuccess(stopped) && startSettled) nativeStop = \"confirmed\";\n } else nativeStop = \"confirmed\";\n // No older attachment may clear a replacement. Unconfirmed native cleanup quarantines only this page.\n if (\n nativeStop === \"confirmed\" &&\n leaseKey !== undefined &&\n lease !== undefined &&\n parent.captureLeases.get(leaseKey) === lease\n ) {\n parent.captureLeases.delete(leaseKey);\n parent.captureReservedBytes = Math.max(\n 0,\n parent.captureReservedBytes - lease.reservedBytes,\n );\n }\n cleanupFinished = true;\n\n return snapshot();\n }),\n ),\n );\n\n // Publish completion only after the cached cleanup Exit has settled. Publishing\n // inside it lets the caller close Scope and interrupt the caching fiber first.\n const stopNative = performStop.pipe(Effect.tap(() => Deferred.succeed(completed, undefined)));\n\n const receive = (frame: NativeFrame): void => {\n if (ended) return;\n if (\n target === undefined ||\n parent.owner.state.generation !== target.generation ||\n parent.owner.state.phase !== \"open\"\n ) {\n finish(\n \"parent-unavailable\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Closed.make({}),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n const sequence = received++;\n\n try {\n const decoded = decodeMetadata(frame);\n\n if (Option.isNone(decoded)) {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({}),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n const meta = decoded.value;\n\n if (!(frame.data instanceof Uint8Array)) {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({ path: \"bytes\" }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n if (frame.data.length > maxFrameBytes) {\n rejected++;\n finish(\n \"frame-limit\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Limit.make({\n dimension: \"frame-bytes\",\n maximum: maxFrameBytes,\n observed: frame.data.length,\n }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n if (last !== undefined && meta.timestamp < last) {\n // A newer frame is already accepted, so this one can no longer be presented in order.\n // It is discarded and counted rather than sorted in or given an invented time.\n late++;\n if (++lateRun > MaxConsecutiveLateFrames) {\n finish(\n \"timestamp-discontinuity\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Timestamp.make({}),\n outcome: \"undispatched\",\n }),\n );\n }\n\n return;\n }\n if (last === meta.timestamp) {\n duplicates++;\n\n return;\n }\n const dimensions = jpegGeometry(frame.data);\n\n if (frame.data[frame.data.length - 2] !== 255 || frame.data[frame.data.length - 1] !== 217) {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({ path: \"bytes\" }),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n const maximumWidth = Math.min(16384, size?.width ?? 16384);\n const maximumHeight = Math.min(16384, size?.height ?? 16384);\n\n const excessive =\n dimensions.width > maximumWidth\n ? Reasons.Limit.make({\n dimension: \"width\",\n maximum: maximumWidth,\n observed: dimensions.width,\n })\n : dimensions.height > maximumHeight\n ? Reasons.Limit.make({\n dimension: \"height\",\n maximum: maximumHeight,\n observed: dimensions.height,\n })\n : dimensions.width * dimensions.height > 33_554_432\n ? Reasons.Limit.make({\n dimension: \"pixels\",\n maximum: 33_554_432,\n observed: dimensions.width * dimensions.height,\n })\n : undefined;\n\n if (excessive !== undefined) {\n rejected++;\n finish(\n \"frame-limit\",\n BrowserError.make({ operation: \"capture\", reason: excessive, outcome: \"undispatched\" }),\n );\n\n return;\n }\n if (\n geometry !== undefined &&\n (geometry.width !== dimensions.width ||\n geometry.height !== dimensions.height ||\n geometry.viewportWidth !== meta.viewportWidth ||\n geometry.viewportHeight !== meta.viewportHeight)\n ) {\n rejected++;\n finish(\n \"resized\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Resized.make({}),\n outcome: \"undispatched\",\n }),\n );\n\n return;\n }\n geometry ??= {\n ...dimensions,\n viewportWidth: meta.viewportWidth,\n viewportHeight: meta.viewportHeight,\n };\n first ??= meta.timestamp;\n last = meta.timestamp;\n lateRun = 0;\n buffer.offer({\n bytes: new Uint8Array(frame.data),\n mediaType: \"image/jpeg\",\n target,\n sequence,\n document,\n sourceTimeMillis: meta.timestamp,\n sourceClock: \"presentation-unix-millis\",\n receivedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n ...dimensions,\n viewportWidth: meta.viewportWidth,\n viewportHeight: meta.viewportHeight,\n });\n Queue.offerUnsafe(wake, undefined);\n } catch {\n rejected++;\n finish(\n \"malformed-frame\",\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Malformed.make({}),\n outcome: \"undispatched\",\n }),\n );\n }\n };\n\n yield* parent.owner\n .guard(\n \"capture-start\",\n (ticket) =>\n Effect.gen(function* () {\n const resolved = yield* parent.resolve(ticket, options.target);\n\n // Frames and summaries share this identity with the generation guard. It must not\n // become writable through consumer-owned frame data.\n target = Object.freeze(resolved.target);\n const resolvedSource = resolved.source;\n\n source = resolvedSource;\n leaseKey = resolved.key;\n if (parent.captureLeases.has(leaseKey)) {\n return yield* BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Busy.make({}),\n outcome: \"undispatched\",\n });\n }\n if (\n parent.captureLeases.size >= MaxParentCaptures ||\n parent.captureReservedBytes + maxBytes > MaxParentBufferedBytes\n ) {\n return yield* BrowserError.make({\n operation: \"capture\",\n reason:\n parent.captureLeases.size >= MaxParentCaptures\n ? Reasons.Limit.make({\n dimension: \"captures\",\n maximum: MaxParentCaptures,\n observed: parent.captureLeases.size + 1,\n })\n : Reasons.Limit.make({\n dimension: \"buffered-bytes\",\n maximum: MaxParentBufferedBytes,\n observed: parent.captureReservedBytes + maxBytes,\n }),\n outcome: \"undispatched\",\n });\n }\n lease = {\n reservedBytes: maxBytes,\n stop: stopNative.pipe(Effect.asVoid),\n invalidate: (why) =>\n finish(\n why,\n BrowserError.make({\n operation: \"capture\",\n reason:\n why === \"resized\" ? Reasons.Resized.make({}) : Reasons.TargetChanged.make({}),\n outcome: \"undispatched\",\n }),\n ),\n };\n parent.captureLeases.set(leaseKey, lease);\n parent.captureReservedBytes += maxBytes;\n // Installed before native acquisition. A cancelled capture cannot escape its caller's scope.\n yield* Effect.addFinalizer(() =>\n stopNative.pipe(\n Effect.andThen(\n Effect.sync(() => {\n rejected += buffer.size;\n buffer.clear();\n }),\n ),\n ),\n );\n startSettled = false;\n yield* Effect.tryPromise({\n try: () => {\n startPromise = resolvedSource.start({\n receive,\n quality,\n invalidate: (why) => lease?.invalidate(why),\n opened: (url) => {\n initialUrl = documentUrl(url);\n },\n ...(size === undefined ? {} : { size }),\n ...(options.lifetime === \"page\" ? { document: nextDocument } : {}),\n });\n void startPromise.then(\n () => {\n startSettled = true;\n if (\n cleanupFinished &&\n nativeStop === \"unconfirmed\" &&\n leaseKey !== undefined &&\n parent.captureLeases.get(leaseKey) === lease\n ) {\n // One late acquisition cleanup, not a callback-side worker. Keep the page quarantined.\n void resolvedSource.stop().catch(() => {});\n }\n },\n () => {\n startSettled = true;\n },\n );\n\n return startPromise;\n },\n catch: () =>\n BrowserError.make({\n operation: \"capture-start\",\n reason: Reasons.Provider.make({}),\n outcome: \"unknown\",\n }),\n });\n ticket.check();\n }),\n { charge: false },\n )\n .pipe(\n Effect.onError(() => (lease === undefined ? Effect.void : stopNative.pipe(Effect.asVoid))),\n Effect.onInterrupt(() =>\n lease === undefined ? Effect.void : stopNative.pipe(Effect.asVoid),\n ),\n );\n // One monitor, not a fiber per frame. The finalizer also calls stop directly if this monitor is interrupted.\n // The timeout and explicit-finish branches intentionally carry different\n // values before shared cleanup; preserve the proven first-completion semantics.\n // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off\n yield* Effect.raceFirst(\n Queue.take(finished).pipe(Effect.as(false)),\n Effect.sleep(duration).pipe(Effect.as(true)),\n ).pipe(\n Effect.tap((expired) => (expired ? Effect.sync(() => finish(\"duration-limit\")) : Effect.void)),\n Effect.andThen(stopNative),\n Effect.forkScoped,\n );\n\n const next: Effect.Effect<CapturedFrame, BrowserError | Cause.Done> = Effect.suspend(() => {\n const frame = buffer.take();\n\n if (frame !== undefined) {\n delivered++;\n\n return Effect.succeed(frame);\n }\n if (ended) return error === undefined ? Cause.done() : Effect.fail(error);\n\n return Queue.take(wake).pipe(Effect.andThen(next));\n });\n\n const frames = Stream.unwrap(\n Effect.suspend(() => {\n if (subscribed)\n return Effect.fail(\n BrowserError.make({\n operation: \"capture-consume\",\n reason: Reasons.Busy.make({}),\n outcome: \"undispatched\",\n }),\n );\n subscribed = true;\n\n return Effect.succeed(\n Stream.fromEffectRepeat(next).pipe(Stream.ensuring(stopNative.pipe(Effect.asVoid))),\n );\n }),\n );\n\n return {\n frames,\n snapshot: Effect.sync(() => {\n const { reason, nativeStop, ...metadata } = snapshot();\n\n return CaptureSnapshot.make({\n ...metadata,\n phase: cleanupFinished ? \"stopped\" : ended ? \"stopping\" : \"capturing\",\n observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),\n currentDocument: document,\n reason: ended ? reason : null,\n nativeStop: cleanupFinished ? nativeStop : null,\n });\n }),\n stop: stopNative,\n completed: Deferred.await(completed).pipe(Effect.map(snapshot)),\n } satisfies CaptureInterval;\n});\n","import { Crypto, Effect, type PlatformError, Schema, type Scope, Stream } from \"effect\";\n\nimport type { BrowserSession } from \"./Browser.ts\";\nimport { PageInfo } from \"./BrowserData.ts\";\nimport type {\n CapturedFrame,\n CaptureOptions,\n CaptureSnapshot,\n CaptureSummary,\n} from \"./CaptureData.ts\";\nimport { BrowserError, Reasons } from \"./Errors.ts\";\nimport { captureParent } from \"./internal/browser/Association.ts\";\nimport { startCapture } from \"./internal/capture/Capture.ts\";\n\nexport {\n CapturedFrame,\n CaptureOptions,\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n} from \"./CaptureData.ts\";\n\n/** Live stream/Effect capabilities intentionally have no data schema or serialization contract. */\nexport interface CaptureInterval {\n /** Single subscription. Ending or interrupting it stops this interval, not its browser. */\n readonly frames: Stream.Stream<CapturedFrame, BrowserError>;\n /**\n * A bounded copy of recorded metadata, available during capture and after cleanup. No native\n * work, budget charge or subscription. Drain frames, then read `completed` for final accounting.\n */\n readonly snapshot: Effect.Effect<CaptureSnapshot>;\n readonly stop: Effect.Effect<CaptureSummary>;\n readonly completed: Effect.Effect<CaptureSummary>;\n}\n\n/**\n * Capture one remote page independently of the session's selected page.\n *\n * Requires the exact live session returned by the host; copying a session object or decoding\n * a durable reference cannot copy its capture authority. The private owner is not frame data.\n * `CapturedFrame` and `CaptureOptions` are Schema values as well as structural types. Frame\n * decoding checks binary/metadata fields, not the complete JPEG bitstream or target authority,\n * and does not copy bytes. Options decoding preserves omissions; admission applies defaults.\n * `CaptureInterval` remains a live scoped capability, not a schema or JSON/Tool value.\n */\nexport const start = <E>(\n session: BrowserSession<E>,\n options: CaptureOptions = {},\n): Effect.Effect<CaptureInterval, BrowserError, Scope.Scope> =>\n Effect.suspend(() => {\n const parent = captureParent(session);\n\n if (parent === undefined)\n return Effect.fail(\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.UnregisteredSession.make({}),\n outcome: \"undispatched\",\n }),\n );\n if (options.target === undefined) return startCapture(parent, options);\n\n return Schema.decodeEffect(PageInfo)(options.target, { onExcessProperty: \"error\" }).pipe(\n Effect.mapError(() =>\n BrowserError.make({\n operation: \"capture\",\n reason: Reasons.Configuration.make({}),\n outcome: \"undispatched\",\n }),\n ),\n Effect.flatMap((target) => startCapture(parent, { ...options, target })),\n );\n });\n\n/**\n * A lazy, scoped frame stream. Each subscription acquires one interval and finalizes it when\n * consumption ends, fails or is interrupted; the browser remains owned by its caller.\n * Concurrent subscriptions on the same page are refused by the existing capture reservation.\n * Unconfirmed native cleanup retains the page reservation. Use start when the host needs\n * explicit stop acknowledgement, interval snapshots and the final capture summary.\n */\nexport const stream = <E>(\n session: BrowserSession<E>,\n options: CaptureOptions = {},\n): Stream.Stream<CapturedFrame, BrowserError> =>\n Stream.unwrap(start(session, options).pipe(Effect.map((interval) => interval.frames)));\n\n/** A `multipart/x-mixed-replace` body and the content type that names its boundary. */\nexport interface MultipartBody<E, R> {\n readonly contentType: string;\n readonly body: Stream.Stream<Uint8Array, E, R>;\n}\n\n/**\n * Frames as motion JPEG for an `<img>`: one `multipart/x-mixed-replace` response, as WHATWG HTML\n * defines it for images. Each frame is followed at once by the next part's delimiter and headers,\n * because a browser shows a part only when it has read the headers of the one after it; without\n * that, a still page's last picture would never be shown. Parts carry no metadata, so nothing\n * but the pictures reaches a viewer. The boundary is drawn from `Crypto` for each response, so a\n * page cannot shape its pixels into it. Call it once per viewer.\n */\nexport const multipart = <E, R>(\n frames: Stream.Stream<CapturedFrame, E, R>,\n): Effect.Effect<MultipartBody<E, R>, PlatformError.PlatformError, Crypto.Crypto> =>\n Effect.map(\n Effect.flatMap(Crypto.Crypto, (crypto) => crypto.randomUUIDv4),\n (uuid) => {\n const boundary = `frame-${uuid.replaceAll(\"-\", \"\")}`;\n const encoder = new TextEncoder();\n const opening = encoder.encode(`--${boundary}\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n`);\n const next = encoder.encode(`\\r\\n--${boundary}\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n`);\n\n return {\n contentType: `multipart/x-mixed-replace; boundary=${boundary}`,\n body: Stream.make(opening).pipe(\n Stream.concat(frames.pipe(Stream.flatMap((frame) => Stream.make(frame.bytes, next)))),\n Stream.concat(Stream.make(encoder.encode(`--${boundary}--\\r\\n`))),\n ),\n };\n },\n );\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { PageInfo, Target } from "./BrowserData.mjs";
2
1
  import { BrowserError } from "./Errors.mjs";
2
+ import { PageInfo, Target } from "./BrowserData.mjs";
3
3
  import { Schema } from "effect";
4
4
  //#region src/internal/capture/Options.ts
5
5
  const Dimension = Schema.Int.check(Schema.isBetween({
@@ -10,6 +10,15 @@ const BufferedBytes = Schema.Int.check(Schema.isBetween({
10
10
  minimum: 1,
11
11
  maximum: 67108864
12
12
  }));
13
+ /**
14
+ * Upper bounds of one interval. Frames can be held for as long as a consumer delays them, so the
15
+ * frame bound is set by memory (the byte bound), not by a short hand-off; an interval can last
16
+ * as long as a session may.
17
+ */
18
+ const CaptureMaxima = Object.freeze({
19
+ frames: 1024,
20
+ durationMillis: 216e5
21
+ });
13
22
  /** Defaults shared by runtime admission and the optional public data schema. */
14
23
  const CaptureDefaults = Object.freeze({
15
24
  maxFrames: 4,
@@ -21,13 +30,13 @@ const CaptureDefaults = Object.freeze({
21
30
  const LimitFields = {
22
31
  maxFrames: Schema.Int.check(Schema.isBetween({
23
32
  minimum: 1,
24
- maximum: 64
33
+ maximum: CaptureMaxima.frames
25
34
  })),
26
35
  maxBufferedBytes: BufferedBytes,
27
36
  maxFrameBytes: BufferedBytes,
28
37
  maxDurationMillis: Schema.Int.check(Schema.isBetween({
29
38
  minimum: 1,
30
- maximum: 6e5
39
+ maximum: CaptureMaxima.durationMillis
31
40
  })),
32
41
  quality: Schema.Int.check(Schema.isBetween({
33
42
  minimum: 1,
@@ -73,9 +82,15 @@ const CaptureSize = Schema.Struct({
73
82
  const CaptureOptions = Schema.Struct({
74
83
  /** Pin to a page from `session.pages`; omission preserves selected-page behavior. */
75
84
  target: Schema.optionalKey(PageInfo),
85
+ /**
86
+ * Frames held until the consumer takes them (4 by default, at most 1024), oldest dropped first
87
+ * and counted as `overflow`. A consumer that delays its output leaves frames here, so this and
88
+ * `maxBufferedBytes` are the delay's memory bound.
89
+ */
76
90
  maxFrames: Schema.optionalKey(LimitFields.maxFrames),
77
91
  maxBufferedBytes: Schema.optionalKey(LimitFields.maxBufferedBytes),
78
92
  maxFrameBytes: Schema.optionalKey(LimitFields.maxFrameBytes),
93
+ /** 60 seconds by default, at most six hours: as long as a session may last. */
79
94
  maxDurationMillis: Schema.optionalKey(LimitFields.maxDurationMillis),
80
95
  quality: Schema.optionalKey(LimitFields.quality),
81
96
  /** Source fit only. Does not resize the viewport or impose an FPS cap. */
@@ -128,7 +143,10 @@ var CaptureSummary = class extends Schema.Class("BrowserCaptureSummary")({
128
143
  afterSequence: Schema.NullOr(Schema.Natural),
129
144
  url: DocumentUrl
130
145
  })).check(Schema.isMaxLength(64)),
131
- /** More navigations were observed than are recorded above. Frames still count them all. */
146
+ /**
147
+ * More navigations were observed than are recorded above, so the earliest were let go: the
148
+ * record keeps the latest 64. Frames still count them all.
149
+ */
132
150
  documentBoundariesTruncated: Schema.Boolean,
133
151
  nativeStop: Schema.Literals(["confirmed", "unconfirmed"]),
134
152
  /** Package accounting cannot measure frames omitted by Chromium, transport, or the provider. */
@@ -157,6 +175,6 @@ var CaptureSnapshot = class extends Schema.Class("BrowserCaptureSnapshot")({
157
175
  nativeStop: Schema.NullOr(NativeStop)
158
176
  }) {};
159
177
  //#endregion
160
- export { CapturedFrame as a, CaptureSummary as i, CaptureSize as n, CaptureDefaults as o, CaptureSnapshot as r, CaptureLimits as s, CaptureOptions as t };
178
+ export { CapturedFrame as a, CaptureMaxima as c, CaptureSummary as i, CaptureSize as n, CaptureDefaults as o, CaptureSnapshot as r, CaptureLimits as s, CaptureOptions as t };
161
179
 
162
- //# sourceMappingURL=CaptureData-AmqVP_Ba.mjs.map
180
+ //# sourceMappingURL=CaptureData-B5-mXlyq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CaptureData-B5-mXlyq.mjs","names":[],"sources":["../src/internal/capture/Options.ts","../src/CaptureData.ts"],"sourcesContent":["import { Schema } from \"effect\";\n\nexport const Dimension = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 16384 }));\nconst BufferedBytes = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 64 * 1024 * 1024 }));\n\n/**\n * Upper bounds of one interval. Frames can be held for as long as a consumer delays them, so the\n * frame bound is set by memory (the byte bound), not by a short hand-off; an interval can last\n * as long as a session may.\n */\nexport const CaptureMaxima = Object.freeze({ frames: 1024, durationMillis: 21_600_000 });\n\n/** Defaults shared by runtime admission and the optional public data schema. */\nexport const CaptureDefaults = Object.freeze({\n maxFrames: 4,\n maxBufferedBytes: 16 * 1024 * 1024,\n maxFrameBytes: 4 * 1024 * 1024,\n maxDurationMillis: 60000,\n quality: 80,\n});\n\nexport const LimitFields = {\n maxFrames: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: CaptureMaxima.frames })),\n maxBufferedBytes: BufferedBytes,\n maxFrameBytes: BufferedBytes,\n maxDurationMillis: Schema.Int.check(\n Schema.isBetween({ minimum: 1, maximum: CaptureMaxima.durationMillis }),\n ),\n quality: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })),\n};\n\nexport const FrameBudget = Schema.makeFilter(\n (value: { readonly maxFrameBytes?: number; readonly maxBufferedBytes?: number }) =>\n (value.maxFrameBytes ?? CaptureDefaults.maxFrameBytes) <=\n (value.maxBufferedBytes ?? CaptureDefaults.maxBufferedBytes),\n { message: \"A frame must fit within the resolved buffer budget\" },\n);\n\n/** Fully resolved admission values; callers supply the optional public CaptureOptions instead. */\nexport const CaptureLimits = Schema.Struct(LimitFields).check(FrameBudget);\n","import { Schema } from \"effect\";\n\nimport { PageInfo, Target } from \"./BrowserData.ts\";\nimport { BrowserError } from \"./Errors.ts\";\nimport { Dimension, FrameBudget, LimitFields } from \"./internal/capture/Options.ts\";\n\n/**\n * Binary frame data, not a live capability or a JSON/thread/Tool value. Decoding validates\n * fields; it does not copy bytes, decode JPEG pixels, prove target authority, or repair clocks.\n * Capture itself owns the byte copy, freezes its shared target identity, and verifies JPEG framing.\n */\nexport const CapturedFrame = Schema.Struct({\n bytes: Schema.Uint8Array.check(Schema.isMinLength(4), Schema.isMaxLength(64 * 1024 * 1024)),\n mediaType: Schema.Literal(\"image/jpeg\"),\n target: Target,\n sequence: Schema.Natural,\n /**\n * Which document of its page this frame was received during: 0 at the start, one more for each\n * main-frame navigation the interval observed. It is attribution by receipt order, not proof\n * of whose pixels these are: a frame received just after a navigation can still show the\n * document before it. Always 0 for an interval that lasts one document.\n */\n document: Schema.Natural,\n sourceTimeMillis: Schema.Finite.check(Schema.isGreaterThan(0)),\n sourceClock: Schema.Literal(\"presentation-unix-millis\"),\n receivedMonotonicNanos: Schema.BigInt,\n width: Dimension,\n height: Dimension,\n viewportWidth: Dimension,\n viewportHeight: Dimension,\n}).check(\n Schema.makeFilter((frame) => frame.width * frame.height <= 33_554_432, {\n message: \"Frame geometry exceeds the capture pixel bound\",\n }),\n);\n\nexport type CapturedFrame = typeof CapturedFrame.Type;\n\n/** Source-fit request in pixels. Actual JPEG bounds are checked before any frame is delivered. */\nexport const CaptureSize = Schema.Struct({ width: Dimension, height: Dimension });\n\nexport type CaptureSize = typeof CaptureSize.Type;\n\n/** Optional capture data. Defaults are applied at admission, not during schema decoding. */\nexport const CaptureOptions = Schema.Struct({\n /** Pin to a page from `session.pages`; omission preserves selected-page behavior. */\n target: Schema.optionalKey(PageInfo),\n /**\n * Frames held until the consumer takes them (4 by default, at most 1024), oldest dropped first\n * and counted as `overflow`. A consumer that delays its output leaves frames here, so this and\n * `maxBufferedBytes` are the delay's memory bound.\n */\n maxFrames: Schema.optionalKey(LimitFields.maxFrames),\n maxBufferedBytes: Schema.optionalKey(LimitFields.maxBufferedBytes),\n maxFrameBytes: Schema.optionalKey(LimitFields.maxFrameBytes),\n /** 60 seconds by default, at most six hours: as long as a session may last. */\n maxDurationMillis: Schema.optionalKey(LimitFields.maxDurationMillis),\n quality: Schema.optionalKey(LimitFields.quality),\n /** Source fit only. Does not resize the viewport or impose an FPS cap. */\n size: Schema.optionalKey(CaptureSize),\n /**\n * `document`, the default, ends the interval when its page navigates. `page` follows the page\n * across documents, so a capture started before a navigation covers the loading in between.\n * It applies to a page's main frame; a capture bound to a child frame still ends with it.\n */\n lifetime: Schema.optionalKey(Schema.Literals([\"document\", \"page\"])),\n}).check(FrameBudget);\n\nexport type CaptureOptions = typeof CaptureOptions.Type;\n\n/** An address as the browser reported it. One longer than this is recorded as null, not cut. */\nconst DocumentUrl = Schema.NullOr(Schema.String.check(Schema.isMaxLength(8192)));\n\nexport class CaptureSummary extends Schema.Class<CaptureSummary>(\"BrowserCaptureSummary\")({\n target: Target,\n reason: Schema.String.check(Schema.isMaxLength(64)),\n received: Schema.Natural,\n delivered: Schema.Natural,\n /** All frames this package omitted; the sum of the four disjoint components below. */\n discarded: Schema.Natural,\n /** Evicted by the frame-count or buffered-byte bound. */\n overflow: Schema.Natural,\n duplicates: Schema.Natural,\n /** Arrived after a newer frame was accepted; never reordered in. */\n late: Schema.Natural,\n /** Other refused frames, including malformed data, bounds and undelivered scope cleanup. */\n rejected: Schema.Natural,\n peakBufferedFrames: Schema.Natural,\n peakBufferedBytes: Schema.Natural,\n bufferedFrames: Schema.Natural,\n bufferedBytes: Schema.Natural,\n sourceFirstMillis: Schema.NullOr(Schema.Finite),\n sourceLastMillis: Schema.NullOr(Schema.Finite),\n /**\n * The captured frame's address when this interval began watching it: the URL of document 0.\n * It is read in the same turn the watch is installed, so no navigation can fall between them.\n */\n initialUrl: DocumentUrl,\n /**\n * Each main-frame navigation a `page` interval observed, in order, with the last frame\n * received before it and the address the new document committed. The native screencast is\n * never restarted for one, so a boundary is not a gap this package introduced; what Chromium\n * omitted while loading stays `upstreamDrops`. A boundary is the commit. When that navigation\n * started and when its document finished loading are the caller's to stamp, on this same\n * clock, around the operation that caused it. A title is page state, not part of a transition.\n */\n documentBoundaries: Schema.Array(\n Schema.Struct({\n document: Schema.Natural,\n observedMonotonicNanos: Schema.BigInt,\n afterSequence: Schema.NullOr(Schema.Natural),\n url: DocumentUrl,\n }),\n ).check(Schema.isMaxLength(64)),\n /**\n * More navigations were observed than are recorded above, so the earliest were let go: the\n * record keeps the latest 64. Frames still count them all.\n */\n documentBoundariesTruncated: Schema.Boolean,\n nativeStop: Schema.Literals([\"confirmed\", \"unconfirmed\"]),\n /** Package accounting cannot measure frames omitted by Chromium, transport, or the provider. */\n upstreamDrops: Schema.Literal(\"unknown\"),\n error: Schema.optionalKey(BrowserError),\n}) {}\n\nconst { reason: StopReason, nativeStop: NativeStop, ...SnapshotFields } = CaptureSummary.fields;\n\n/**\n * Host-side metadata at one instant, without reading or waking the page. The boundary prefix\n * and loss counters are the same facts as the final summary, independent of frame consumption.\n * `stopped` means native cleanup settled; buffered frames may still be drained afterwards.\n */\nexport class CaptureSnapshot extends Schema.Class<CaptureSnapshot>(\"BrowserCaptureSnapshot\")({\n ...SnapshotFields,\n phase: Schema.Literals([\"capturing\", \"stopping\", \"stopped\"]),\n observedMonotonicNanos: Schema.BigInt,\n /** Keeps counting when the bounded boundary prefix is truncated. */\n currentDocument: Schema.Natural,\n /** No reason is reported while the capture still accepts frames. */\n reason: Schema.NullOr(StopReason),\n /** Null until the native cleanup attempt settles; unconfirmed never means stopped remotely. */\n nativeStop: Schema.NullOr(NativeStop),\n}) {}\n"],"mappings":"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"}