effect-browserbase 0.1.0-beta.104 → 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.
- package/README.md +106 -307
- package/dist/Account.d.mts +17 -19
- package/dist/Account.mjs +4 -9
- package/dist/Account.mjs.map +1 -1
- package/dist/{Acquisition-WBafHwXH.mjs → Acquisition-CAsTTXp4.mjs} +25 -70
- package/dist/Acquisition-CAsTTXp4.mjs.map +1 -0
- package/dist/Agents-5HpTp9YE.d.mts +155 -0
- package/dist/Agents.d.mts +3 -156
- package/dist/Agents.mjs +4 -20
- package/dist/Agents.mjs.map +1 -1
- package/dist/Allocation.d.mts +9 -11
- package/dist/Allocation.mjs +2 -4
- package/dist/Allocation.mjs.map +1 -1
- package/dist/Browser-DpSajNai.mjs +270 -0
- package/dist/Browser-DpSajNai.mjs.map +1 -0
- package/dist/Browser.d.mts +26 -143
- package/dist/Browser.mjs +3 -223
- package/dist/BrowserBinding.d.mts +7 -34
- package/dist/BrowserBinding.mjs +30 -1
- package/dist/BrowserBinding.mjs.map +1 -0
- package/dist/Certificates-D4NQmyeA.d.mts +34 -0
- package/dist/Certificates.d.mts +3 -35
- package/dist/Certificates.mjs +2 -7
- package/dist/Certificates.mjs.map +1 -1
- package/dist/{Cleanup-G2YpTCuU.d.mts → Cleanup-DMTDBir8.d.mts} +5 -6
- package/dist/Cleanup.d.mts +3 -2
- package/dist/Cleanup.mjs +1 -6
- package/dist/Cleanup.mjs.map +1 -1
- package/dist/{Client-D1_xYM9o.mjs → Client-4RMKIpL2.mjs} +12 -12
- package/dist/Client-4RMKIpL2.mjs.map +1 -0
- package/dist/{Client-Ca0zSg1n.d.mts → Client-B_f6bBLT.d.mts} +4 -5
- package/dist/Client.d.mts +3 -2
- package/dist/Client.mjs +1 -1
- package/dist/Connection-sxyxtwlk.mjs +56 -0
- package/dist/Connection-sxyxtwlk.mjs.map +1 -0
- package/dist/ContextCoordination.d.mts +5 -6
- package/dist/ContextCoordination.mjs +2 -9
- package/dist/ContextCoordination.mjs.map +1 -1
- package/dist/{ContextWriter-BrQmoXRg.mjs → ContextWriter-oKWFLHuP.mjs} +8 -12
- package/dist/ContextWriter-oKWFLHuP.mjs.map +1 -0
- package/dist/Contexts-DqdL7cyX.d.mts +31 -0
- package/dist/Contexts.d.mts +3 -32
- package/dist/Contexts.mjs +3 -9
- package/dist/Contexts.mjs.map +1 -1
- package/dist/Downloads-CdhiCO5X.d.mts +43 -0
- package/dist/Downloads.d.mts +3 -44
- package/dist/Downloads.mjs +3 -8
- package/dist/Downloads.mjs.map +1 -1
- package/dist/Errors.d.mts +3 -26
- package/dist/Errors.mjs +1 -137
- package/dist/Errors.mjs.map +1 -1
- package/dist/Extensions-DBXuVM1f.d.mts +43 -0
- package/dist/{Extensions-ClZbYb1H.mjs → Extensions-Dg3jHOf0.mjs} +3 -9
- package/dist/Extensions-Dg3jHOf0.mjs.map +1 -0
- package/dist/Extensions.d.mts +3 -44
- package/dist/Extensions.mjs +1 -1
- package/dist/Functions-CsKMMUuZ.d.mts +226 -0
- package/dist/Functions.d.mts +3 -227
- package/dist/Functions.mjs +4 -18
- package/dist/Functions.mjs.map +1 -1
- package/dist/{Launch-BHcAEq05.mjs → Launch-cAdE2rrZ.mjs} +16 -12
- package/dist/Launch-cAdE2rrZ.mjs.map +1 -0
- package/dist/Launch.d.mts +127 -2
- package/dist/Launch.mjs +5 -13
- package/dist/Launch.mjs.map +1 -1
- package/dist/PageFetch-CAMqb5m8.d.mts +39 -0
- package/dist/PageFetch.d.mts +3 -40
- package/dist/PageFetch.mjs +2 -8
- package/dist/PageFetch.mjs.map +1 -1
- package/dist/Projects-tm5bTDH0.d.mts +38 -0
- package/dist/Projects.d.mts +3 -39
- package/dist/Projects.mjs +2 -8
- package/dist/Projects.mjs.map +1 -1
- package/dist/Recordings-bVI33sTq.d.mts +27 -0
- package/dist/Recordings.d.mts +3 -28
- package/dist/Recordings.mjs +3 -5
- package/dist/Recordings.mjs.map +1 -1
- package/dist/{References-URDAtapt.d.mts → References-DOH3Wu44.d.mts} +5 -6
- package/dist/References.d.mts +3 -2
- package/dist/References.mjs +10 -18
- package/dist/References.mjs.map +1 -1
- package/dist/Replays-Bo-9xeLz.d.mts +27 -0
- package/dist/Replays.d.mts +3 -28
- package/dist/Replays.mjs +3 -5
- package/dist/Replays.mjs.map +1 -1
- package/dist/Search-1g6TUem7.d.mts +37 -0
- package/dist/Search.d.mts +3 -38
- package/dist/Search.mjs +2 -9
- package/dist/Search.mjs.map +1 -1
- package/dist/{SessionData-DRKcKCqw.d.mts → SessionData-5xYZDshJ.d.mts} +5 -6
- package/dist/SessionData.d.mts +3 -2
- package/dist/SessionData.mjs +1 -8
- package/dist/SessionData.mjs.map +1 -1
- package/dist/{Sessions-B8E4kMxo.d.mts → Sessions-BpZgjteE.d.mts} +8 -8
- package/dist/{Sessions-CtbseWEh.mjs → Sessions-suOLSym9.mjs} +34 -18
- package/dist/Sessions-suOLSym9.mjs.map +1 -0
- package/dist/Sessions.d.mts +4 -3
- package/dist/Sessions.mjs +1 -1
- package/dist/Testing.d.mts +119 -0
- package/dist/Testing.mjs +206 -0
- package/dist/Testing.mjs.map +1 -0
- package/dist/{Transfers-Czv5IZzJ.d.mts → Transfers-GVqzrDWl.d.mts} +22 -9
- package/dist/Transfers.d.mts +3 -2
- package/dist/Transfers.mjs +12 -28
- package/dist/Transfers.mjs.map +1 -1
- package/dist/Uploads-C2neBEpj.d.mts +28 -0
- package/dist/Uploads.d.mts +3 -29
- package/dist/Uploads.mjs +3 -5
- package/dist/Uploads.mjs.map +1 -1
- package/dist/Webhooks-BoeRAxzG.d.mts +59 -0
- package/dist/Webhooks.d.mts +3 -60
- package/dist/Webhooks.mjs +2 -11
- package/dist/Webhooks.mjs.map +1 -1
- package/dist/{WriterFacts-osvYAsIx.d.mts → WriterFacts-BBJRDZYY.d.mts} +8 -15
- package/dist/index.d.mts +27 -31
- package/dist/index.mjs +26 -31
- package/package.json +7 -28
- package/dist/Acquisition-WBafHwXH.mjs.map +0 -1
- package/dist/Association-CEps4hYP.mjs +0 -10
- package/dist/Association-CEps4hYP.mjs.map +0 -1
- package/dist/Bootstrap-BXuzZojd.mjs +0 -194
- package/dist/Bootstrap-BXuzZojd.mjs.map +0 -1
- package/dist/Bootstrap-m7lHgj0D.d.mts +0 -177
- package/dist/Bootstrap.d.mts +0 -2
- package/dist/Bootstrap.mjs +0 -2
- package/dist/Browser.mjs.map +0 -1
- package/dist/BrowserBinding-DlJTGYZL.mjs +0 -2508
- package/dist/BrowserBinding-DlJTGYZL.mjs.map +0 -1
- package/dist/BrowserData-LpcAuAub.d.mts +0 -369
- package/dist/BrowserData.d.mts +0 -2
- package/dist/BrowserData.mjs +0 -442
- package/dist/BrowserData.mjs.map +0 -1
- package/dist/Capture-Bc0z9IAi.mjs +0 -571
- package/dist/Capture-Bc0z9IAi.mjs.map +0 -1
- package/dist/Capture-BvWI-l1A.d.mts +0 -168
- package/dist/Capture.d.mts +0 -2
- package/dist/Capture.mjs +0 -2
- package/dist/Client-D1_xYM9o.mjs.map +0 -1
- package/dist/ContextWriter-BrQmoXRg.mjs.map +0 -1
- package/dist/Extensions-ClZbYb1H.mjs.map +0 -1
- package/dist/Images-DgscEfSf.mjs +0 -127
- package/dist/Images-DgscEfSf.mjs.map +0 -1
- package/dist/Launch-BHcAEq05.mjs.map +0 -1
- package/dist/Launch-C8CfaYxb.d.mts +0 -132
- package/dist/LocalBrowser-BLancJ3R.d.mts +0 -79
- package/dist/LocalBrowser-DsQLWNL1.mjs +0 -439
- package/dist/LocalBrowser-DsQLWNL1.mjs.map +0 -1
- package/dist/LocalBrowser.d.mts +0 -2
- package/dist/LocalBrowser.mjs +0 -2
- package/dist/PageControl.d.mts +0 -16
- package/dist/PageControl.mjs +0 -36
- package/dist/PageControl.mjs.map +0 -1
- package/dist/PageControlAssociation-DabEg5AR.mjs +0 -10
- package/dist/PageControlAssociation-DabEg5AR.mjs.map +0 -1
- package/dist/Session-D4nT9hOO.mjs +0 -1379
- package/dist/Session-D4nT9hOO.mjs.map +0 -1
- package/dist/Sessions-CtbseWEh.mjs.map +0 -1
- package/dist/rolldown-runtime-D7D4PA-g.mjs +0 -13
|
@@ -1,571 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { BrowserError } from "./Errors.mjs";
|
|
3
|
-
import { PageInfo, Target } from "./BrowserData.mjs";
|
|
4
|
-
import { t as jpegGeometry } from "./Images-DgscEfSf.mjs";
|
|
5
|
-
import { n as captureParent } from "./Association-CEps4hYP.mjs";
|
|
6
|
-
import { Cause, Clock, Deferred, Effect, Exit, Queue, Schema, Stream } from "effect";
|
|
7
|
-
//#region src/internal/capture/CaptureTypes.ts
|
|
8
|
-
const Dimension = Schema.Int.check(Schema.isBetween({
|
|
9
|
-
minimum: 1,
|
|
10
|
-
maximum: 16384
|
|
11
|
-
}));
|
|
12
|
-
const BufferedBytes = Schema.Int.check(Schema.isBetween({
|
|
13
|
-
minimum: 1,
|
|
14
|
-
maximum: 67108864
|
|
15
|
-
}));
|
|
16
|
-
/** Defaults shared by admission and the optional public data schema. */
|
|
17
|
-
const CaptureDefaults = Object.freeze({
|
|
18
|
-
maxFrames: 4,
|
|
19
|
-
maxBufferedBytes: 16777216,
|
|
20
|
-
maxFrameBytes: 4194304,
|
|
21
|
-
maxDurationMillis: 6e4,
|
|
22
|
-
quality: 80
|
|
23
|
-
});
|
|
24
|
-
const LimitFields = {
|
|
25
|
-
maxFrames: Schema.Int.check(Schema.isBetween({
|
|
26
|
-
minimum: 1,
|
|
27
|
-
maximum: 64
|
|
28
|
-
})),
|
|
29
|
-
maxBufferedBytes: BufferedBytes,
|
|
30
|
-
maxFrameBytes: BufferedBytes,
|
|
31
|
-
maxDurationMillis: Schema.Int.check(Schema.isBetween({
|
|
32
|
-
minimum: 1,
|
|
33
|
-
maximum: 6e5
|
|
34
|
-
})),
|
|
35
|
-
quality: Schema.Int.check(Schema.isBetween({
|
|
36
|
-
minimum: 1,
|
|
37
|
-
maximum: 100
|
|
38
|
-
}))
|
|
39
|
-
};
|
|
40
|
-
const FrameBudget = Schema.makeFilter((value) => (value.maxFrameBytes ?? CaptureDefaults.maxFrameBytes) <= (value.maxBufferedBytes ?? CaptureDefaults.maxBufferedBytes), { message: "A frame must fit within the resolved buffer budget" });
|
|
41
|
-
/** Private resolved admission schema. It is not an additional public options API. */
|
|
42
|
-
const CaptureLimits = Schema.Struct(LimitFields).check(FrameBudget);
|
|
43
|
-
/**
|
|
44
|
-
* Binary frame data, not a live capability or a JSON/thread/Tool value. Decoding validates
|
|
45
|
-
* fields; it does not copy bytes, decode JPEG pixels, prove target authority, or repair clocks.
|
|
46
|
-
* Capture itself owns the byte copy, freezes its shared target identity, and verifies JPEG framing.
|
|
47
|
-
*/
|
|
48
|
-
const CapturedFrame = Schema.Struct({
|
|
49
|
-
bytes: Schema.Uint8Array.check(Schema.isMinLength(4), Schema.isMaxLength(67108864)),
|
|
50
|
-
mediaType: Schema.Literal("image/jpeg"),
|
|
51
|
-
target: Target,
|
|
52
|
-
sequence: Schema.Natural,
|
|
53
|
-
/**
|
|
54
|
-
* Which document of its page this frame was received during: 0 at the start, one more for each
|
|
55
|
-
* main-frame navigation the interval observed. It is attribution by receipt order, not proof
|
|
56
|
-
* of whose pixels these are: a frame received just after a navigation can still show the
|
|
57
|
-
* document before it. Always 0 for an interval that lasts one document.
|
|
58
|
-
*/
|
|
59
|
-
document: Schema.Natural,
|
|
60
|
-
sourceTimeMillis: Schema.Finite.check(Schema.isGreaterThan(0)),
|
|
61
|
-
sourceClock: Schema.Literal("presentation-unix-millis"),
|
|
62
|
-
receivedMonotonicNanos: Schema.BigInt,
|
|
63
|
-
width: Dimension,
|
|
64
|
-
height: Dimension,
|
|
65
|
-
viewportWidth: Dimension,
|
|
66
|
-
viewportHeight: Dimension
|
|
67
|
-
}).check(Schema.makeFilter((frame) => frame.width * frame.height <= 33554432, { message: "Frame geometry exceeds the capture pixel bound" }));
|
|
68
|
-
/** Source-fit request in pixels. Actual JPEG bounds are checked before any frame is delivered. */
|
|
69
|
-
const CaptureSize = Schema.Struct({
|
|
70
|
-
width: Dimension,
|
|
71
|
-
height: Dimension
|
|
72
|
-
});
|
|
73
|
-
/** Optional capture data. Defaults are applied at admission, not during schema decoding. */
|
|
74
|
-
const CaptureOptions = Schema.Struct({
|
|
75
|
-
/** Pin to a page from `session.pages`; omission preserves selected-page behavior. */
|
|
76
|
-
target: Schema.optionalKey(PageInfo),
|
|
77
|
-
maxFrames: Schema.optionalKey(LimitFields.maxFrames),
|
|
78
|
-
maxBufferedBytes: Schema.optionalKey(LimitFields.maxBufferedBytes),
|
|
79
|
-
maxFrameBytes: Schema.optionalKey(LimitFields.maxFrameBytes),
|
|
80
|
-
maxDurationMillis: Schema.optionalKey(LimitFields.maxDurationMillis),
|
|
81
|
-
quality: Schema.optionalKey(LimitFields.quality),
|
|
82
|
-
/** Source fit only. Does not resize the viewport or impose an FPS cap. */
|
|
83
|
-
size: Schema.optionalKey(CaptureSize),
|
|
84
|
-
/**
|
|
85
|
-
* `document`, the default, ends the interval when its page navigates. `page` follows the page
|
|
86
|
-
* across documents, so a capture started before a navigation covers the loading in between.
|
|
87
|
-
* It applies to a page's main frame; a capture bound to a child frame still ends with it.
|
|
88
|
-
*/
|
|
89
|
-
lifetime: Schema.optionalKey(Schema.Literals(["document", "page"]))
|
|
90
|
-
}).check(FrameBudget);
|
|
91
|
-
/** An address as the browser reported it. One longer than this is recorded as null, not cut. */
|
|
92
|
-
const DocumentUrl = Schema.NullOr(Schema.String.check(Schema.isMaxLength(8192)));
|
|
93
|
-
var CaptureSummary = class extends Schema.Class("BrowserbaseCaptureSummary")({
|
|
94
|
-
target: Target,
|
|
95
|
-
reason: Schema.String.check(Schema.isMaxLength(64)),
|
|
96
|
-
received: Schema.Natural,
|
|
97
|
-
delivered: Schema.Natural,
|
|
98
|
-
dropped: Schema.Natural,
|
|
99
|
-
duplicates: Schema.Natural,
|
|
100
|
-
/** Arrived after a newer frame was accepted; counted within `dropped`, never reordered in. */
|
|
101
|
-
late: Schema.Natural,
|
|
102
|
-
peakBufferedFrames: Schema.Natural,
|
|
103
|
-
peakBufferedBytes: Schema.Natural,
|
|
104
|
-
bufferedFrames: Schema.Natural,
|
|
105
|
-
bufferedBytes: Schema.Natural,
|
|
106
|
-
sourceFirstMillis: Schema.NullOr(Schema.Finite),
|
|
107
|
-
sourceLastMillis: Schema.NullOr(Schema.Finite),
|
|
108
|
-
/**
|
|
109
|
-
* The captured frame's address when this interval began watching it: the URL of document 0.
|
|
110
|
-
* It is read in the same turn the watch is installed, so no navigation can fall between them.
|
|
111
|
-
*/
|
|
112
|
-
initialUrl: DocumentUrl,
|
|
113
|
-
/**
|
|
114
|
-
* Each main-frame navigation a `page` interval observed, in order, with the last frame
|
|
115
|
-
* received before it and the address the new document committed. The native screencast is
|
|
116
|
-
* never restarted for one, so a boundary is not a gap this package introduced; what Chromium
|
|
117
|
-
* omitted while loading stays `upstreamDrops`. A boundary is the commit. When that navigation
|
|
118
|
-
* started and when its document finished loading are the caller's to stamp, on this same
|
|
119
|
-
* clock, around the operation that caused it. A title is page state, not part of a transition.
|
|
120
|
-
*/
|
|
121
|
-
documentBoundaries: Schema.Array(Schema.Struct({
|
|
122
|
-
document: Schema.Natural,
|
|
123
|
-
observedMonotonicNanos: Schema.BigInt,
|
|
124
|
-
afterSequence: Schema.NullOr(Schema.Natural),
|
|
125
|
-
url: DocumentUrl
|
|
126
|
-
})).check(Schema.isMaxLength(64)),
|
|
127
|
-
/** More navigations were observed than are recorded above. Frames still count them all. */
|
|
128
|
-
documentBoundariesTruncated: Schema.Boolean,
|
|
129
|
-
nativeStop: Schema.Literals(["confirmed", "unconfirmed"]),
|
|
130
|
-
/** Package accounting cannot measure frames omitted by Chromium, transport, or the provider. */
|
|
131
|
-
upstreamDrops: Schema.Literal("unknown"),
|
|
132
|
-
error: Schema.optionalKey(BrowserError)
|
|
133
|
-
}) {};
|
|
134
|
-
const { reason: StopReason, nativeStop: NativeStop, ...SnapshotFields } = CaptureSummary.fields;
|
|
135
|
-
/**
|
|
136
|
-
* Host-side metadata at one instant, without reading or waking the page. The boundary prefix
|
|
137
|
-
* and loss counters are the same facts as the final summary, independent of frame consumption.
|
|
138
|
-
* `stopped` means native cleanup settled; buffered frames may still be drained afterwards.
|
|
139
|
-
*/
|
|
140
|
-
var CaptureSnapshot = class extends Schema.Class("BrowserbaseCaptureSnapshot")({
|
|
141
|
-
...SnapshotFields,
|
|
142
|
-
phase: Schema.Literals([
|
|
143
|
-
"capturing",
|
|
144
|
-
"stopping",
|
|
145
|
-
"stopped"
|
|
146
|
-
]),
|
|
147
|
-
observedMonotonicNanos: Schema.BigInt,
|
|
148
|
-
/** Keeps counting when the bounded boundary prefix is truncated. */
|
|
149
|
-
currentDocument: Schema.Natural,
|
|
150
|
-
/** No reason is reported while the capture still accepts frames. */
|
|
151
|
-
reason: Schema.NullOr(StopReason),
|
|
152
|
-
/** Null until the native cleanup attempt settles; unconfirmed never means stopped remotely. */
|
|
153
|
-
nativeStop: Schema.NullOr(NativeStop)
|
|
154
|
-
}) {};
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/internal/capture/FrameBuffer.ts
|
|
157
|
-
/** Callback-side bounded retention. No Promise, Effect, blocked producer, or per-frame fiber. */
|
|
158
|
-
var FrameBuffer = class {
|
|
159
|
-
maximumFrames;
|
|
160
|
-
maximumBytes;
|
|
161
|
-
items = [];
|
|
162
|
-
retained = 0;
|
|
163
|
-
dropped = 0;
|
|
164
|
-
received = 0;
|
|
165
|
-
highWaterBytes = 0;
|
|
166
|
-
highWaterFrames = 0;
|
|
167
|
-
constructor(maximumFrames, maximumBytes) {
|
|
168
|
-
if (!Number.isSafeInteger(maximumFrames) || maximumFrames < 1 || maximumFrames > 64 || !Number.isSafeInteger(maximumBytes) || maximumBytes < 1 || maximumBytes > 67108864) throw new RangeError("Invalid capture retention limits");
|
|
169
|
-
this.maximumFrames = maximumFrames;
|
|
170
|
-
this.maximumBytes = maximumBytes;
|
|
171
|
-
}
|
|
172
|
-
offer(frame) {
|
|
173
|
-
this.received++;
|
|
174
|
-
if (frame.bytes.byteLength > this.maximumBytes) {
|
|
175
|
-
this.dropped++;
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
while (this.items.length >= this.maximumFrames || this.retained + frame.bytes.byteLength > this.maximumBytes) {
|
|
179
|
-
const removed = this.items.shift();
|
|
180
|
-
if (removed === void 0) break;
|
|
181
|
-
this.retained -= removed.bytes.byteLength;
|
|
182
|
-
this.dropped++;
|
|
183
|
-
}
|
|
184
|
-
this.items.push(frame);
|
|
185
|
-
this.retained += frame.bytes.byteLength;
|
|
186
|
-
this.highWaterBytes = Math.max(this.highWaterBytes, this.retained);
|
|
187
|
-
this.highWaterFrames = Math.max(this.highWaterFrames, this.items.length);
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
take() {
|
|
191
|
-
const frame = this.items.shift();
|
|
192
|
-
if (frame !== void 0) this.retained -= frame.bytes.byteLength;
|
|
193
|
-
return frame;
|
|
194
|
-
}
|
|
195
|
-
clear() {
|
|
196
|
-
this.items.length = 0;
|
|
197
|
-
this.retained = 0;
|
|
198
|
-
}
|
|
199
|
-
get size() {
|
|
200
|
-
return this.items.length;
|
|
201
|
-
}
|
|
202
|
-
get bytes() {
|
|
203
|
-
return this.retained;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
//#endregion
|
|
207
|
-
//#region src/internal/capture/Capture.ts
|
|
208
|
-
const MaxParentCaptures = 4;
|
|
209
|
-
const MaxParentBufferedBytes = 67108864;
|
|
210
|
-
const MaxDocumentBoundaries = 64;
|
|
211
|
-
const MaxDocumentUrlLength = 8192;
|
|
212
|
-
/** An over-long address is recorded as null rather than cut into one that was never shown. */
|
|
213
|
-
const documentUrl = (url) => typeof url === "string" && url.length <= MaxDocumentUrlLength ? url : null;
|
|
214
|
-
/**
|
|
215
|
-
* Chromium stamps a screencast frame on its UI thread, encodes it on an unsequenced thread
|
|
216
|
-
* pool, and emits it when that encode completes. It admits a new frame while at most two are
|
|
217
|
-
* unacknowledged (`kMaxScreencastFramesInFlight`), so up to three encode at once and two
|
|
218
|
-
* frames stamped close together can complete in either order. Every frame stamped before an
|
|
219
|
-
* accepted one was already in flight when that one was stamped, so at most two late frames
|
|
220
|
-
* can arrive in a row. A longer run is source time that really went backwards.
|
|
221
|
-
*/
|
|
222
|
-
const MaxConsecutiveLateFrames = 2;
|
|
223
|
-
const Metadata = Schema.Struct({
|
|
224
|
-
timestamp: Schema.Finite.check(Schema.isGreaterThan(0)),
|
|
225
|
-
viewportWidth: Schema.Int.check(Schema.isBetween({
|
|
226
|
-
minimum: 1,
|
|
227
|
-
maximum: 16384
|
|
228
|
-
})),
|
|
229
|
-
viewportHeight: Schema.Int.check(Schema.isBetween({
|
|
230
|
-
minimum: 1,
|
|
231
|
-
maximum: 16384
|
|
232
|
-
}))
|
|
233
|
-
});
|
|
234
|
-
const parseMetadata = Schema.decodeUnknownSync(Metadata);
|
|
235
|
-
/** Private seam for deterministic callback/lifetime tests. The public start accepts a real live session. */
|
|
236
|
-
const startCapture = Effect.fnUntraced(function* (parent, options = {}) {
|
|
237
|
-
const maxFrames = options.maxFrames ?? CaptureDefaults.maxFrames;
|
|
238
|
-
const maxBytes = options.maxBufferedBytes ?? CaptureDefaults.maxBufferedBytes;
|
|
239
|
-
const maxFrameBytes = options.maxFrameBytes ?? CaptureDefaults.maxFrameBytes;
|
|
240
|
-
const duration = options.maxDurationMillis ?? CaptureDefaults.maxDurationMillis;
|
|
241
|
-
const quality = options.quality ?? CaptureDefaults.quality;
|
|
242
|
-
const size = options.size === void 0 ? void 0 : yield* Schema.decodeEffect(CaptureSize)(options.size, { onExcessProperty: "error" }).pipe(Effect.map(({ width, height }) => Object.freeze({
|
|
243
|
-
width,
|
|
244
|
-
height
|
|
245
|
-
})), Effect.mapError(() => BrowserError.make({
|
|
246
|
-
operation: "capture",
|
|
247
|
-
reason: "configuration",
|
|
248
|
-
outcome: "undispatched"
|
|
249
|
-
})));
|
|
250
|
-
yield* Schema.decodeEffect(CaptureLimits)({
|
|
251
|
-
maxFrames,
|
|
252
|
-
maxBufferedBytes: maxBytes,
|
|
253
|
-
maxFrameBytes,
|
|
254
|
-
maxDurationMillis: duration,
|
|
255
|
-
quality
|
|
256
|
-
}).pipe(Effect.mapError(() => BrowserError.make({
|
|
257
|
-
operation: "capture",
|
|
258
|
-
reason: "configuration",
|
|
259
|
-
outcome: "undispatched"
|
|
260
|
-
})));
|
|
261
|
-
const clock = yield* Clock.Clock;
|
|
262
|
-
const wake = yield* Queue.dropping(1);
|
|
263
|
-
const finished = yield* Queue.dropping(1);
|
|
264
|
-
const completed = yield* Deferred.make();
|
|
265
|
-
yield* Effect.addFinalizer(() => Queue.shutdown(wake).pipe(Effect.andThen(Queue.shutdown(finished)), Effect.asVoid));
|
|
266
|
-
const buffer = new FrameBuffer(maxFrames, maxBytes);
|
|
267
|
-
let source;
|
|
268
|
-
let startPromise;
|
|
269
|
-
let startSettled = true;
|
|
270
|
-
let target;
|
|
271
|
-
let leaseKey;
|
|
272
|
-
let ended = false, subscribed = false, cleanupFinished = false;
|
|
273
|
-
let reason = "stopped";
|
|
274
|
-
let error;
|
|
275
|
-
let received = 0, delivered = 0, rejected = 0, duplicates = 0, late = 0, lateRun = 0;
|
|
276
|
-
let first, last;
|
|
277
|
-
let document = 0;
|
|
278
|
-
let initialUrl = null;
|
|
279
|
-
let documentBoundariesTruncated = false;
|
|
280
|
-
const documentBoundaries = [];
|
|
281
|
-
/** The page navigated and its screencast kept running: later frames belong to a new document. */
|
|
282
|
-
const nextDocument = (url) => {
|
|
283
|
-
if (ended) return;
|
|
284
|
-
document++;
|
|
285
|
-
if (documentBoundaries.length >= MaxDocumentBoundaries) documentBoundariesTruncated = true;
|
|
286
|
-
else documentBoundaries.push({
|
|
287
|
-
document,
|
|
288
|
-
observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),
|
|
289
|
-
afterSequence: received === 0 ? null : received - 1,
|
|
290
|
-
url: documentUrl(url)
|
|
291
|
-
});
|
|
292
|
-
};
|
|
293
|
-
let geometry;
|
|
294
|
-
let nativeStop = "unconfirmed";
|
|
295
|
-
let lease;
|
|
296
|
-
const finish = (why, failure) => {
|
|
297
|
-
if (ended) return;
|
|
298
|
-
ended = true;
|
|
299
|
-
reason = why;
|
|
300
|
-
error = failure;
|
|
301
|
-
Queue.offerUnsafe(wake, void 0);
|
|
302
|
-
Queue.offerUnsafe(finished, void 0);
|
|
303
|
-
};
|
|
304
|
-
const snapshot = () => {
|
|
305
|
-
if (target === void 0) throw new Error("Capture target was not admitted");
|
|
306
|
-
return CaptureSummary.make({
|
|
307
|
-
target,
|
|
308
|
-
reason,
|
|
309
|
-
received,
|
|
310
|
-
delivered,
|
|
311
|
-
dropped: buffer.dropped + rejected,
|
|
312
|
-
duplicates,
|
|
313
|
-
late,
|
|
314
|
-
peakBufferedFrames: buffer.highWaterFrames,
|
|
315
|
-
peakBufferedBytes: buffer.highWaterBytes,
|
|
316
|
-
bufferedFrames: buffer.size,
|
|
317
|
-
bufferedBytes: buffer.bytes,
|
|
318
|
-
sourceFirstMillis: first ?? null,
|
|
319
|
-
sourceLastMillis: last ?? null,
|
|
320
|
-
initialUrl,
|
|
321
|
-
documentBoundaries: documentBoundaries.map((boundary) => ({ ...boundary })),
|
|
322
|
-
documentBoundariesTruncated,
|
|
323
|
-
nativeStop,
|
|
324
|
-
upstreamDrops: "unknown",
|
|
325
|
-
...error === void 0 ? {} : { error: BrowserError.make({
|
|
326
|
-
operation: error.operation,
|
|
327
|
-
reason: error.reason,
|
|
328
|
-
...error.outcome === void 0 ? {} : { outcome: error.outcome },
|
|
329
|
-
...error.status === void 0 ? {} : { status: error.status },
|
|
330
|
-
...error.retryAfterMillis === void 0 ? {} : { retryAfterMillis: error.retryAfterMillis }
|
|
331
|
-
}) }
|
|
332
|
-
});
|
|
333
|
-
};
|
|
334
|
-
const stopNative = (yield* Effect.cached(Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
335
|
-
finish("stopped");
|
|
336
|
-
if (startPromise !== void 0 && !startSettled) yield* restore(Effect.tryPromise({
|
|
337
|
-
try: () => startPromise,
|
|
338
|
-
catch: () => BrowserError.make({
|
|
339
|
-
operation: "capture-start",
|
|
340
|
-
reason: "provider"
|
|
341
|
-
})
|
|
342
|
-
}).pipe(Effect.timeout(2e3), Effect.exit));
|
|
343
|
-
if (source !== void 0) {
|
|
344
|
-
const stopped = yield* restore(Effect.tryPromise({
|
|
345
|
-
try: () => source.stop(),
|
|
346
|
-
catch: () => BrowserError.make({
|
|
347
|
-
operation: "capture-stop",
|
|
348
|
-
reason: "provider"
|
|
349
|
-
})
|
|
350
|
-
}).pipe(Effect.timeout(3e3))).pipe(Effect.exit);
|
|
351
|
-
if (Exit.isSuccess(stopped) && startSettled) nativeStop = "confirmed";
|
|
352
|
-
} else nativeStop = "confirmed";
|
|
353
|
-
if (nativeStop === "confirmed" && leaseKey !== void 0 && lease !== void 0 && parent.captureLeases.get(leaseKey) === lease) {
|
|
354
|
-
parent.captureLeases.delete(leaseKey);
|
|
355
|
-
parent.captureReservedBytes = Math.max(0, parent.captureReservedBytes - lease.reservedBytes);
|
|
356
|
-
}
|
|
357
|
-
cleanupFinished = true;
|
|
358
|
-
return snapshot();
|
|
359
|
-
})))).pipe(Effect.tap(() => Deferred.succeed(completed, void 0)));
|
|
360
|
-
const receive = (frame) => {
|
|
361
|
-
if (ended) return;
|
|
362
|
-
if (target === void 0 || parent.owner.state.generation !== target.generation || parent.owner.state.phase !== "open") {
|
|
363
|
-
finish("parent-unavailable", BrowserError.make({
|
|
364
|
-
operation: "capture",
|
|
365
|
-
reason: "closed"
|
|
366
|
-
}));
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
const sequence = received++;
|
|
370
|
-
try {
|
|
371
|
-
const meta = parseMetadata(frame);
|
|
372
|
-
if (!(frame.data instanceof Uint8Array) || frame.data.length > maxFrameBytes) {
|
|
373
|
-
rejected++;
|
|
374
|
-
finish("frame-limit", BrowserError.make({
|
|
375
|
-
operation: "capture",
|
|
376
|
-
reason: "limit"
|
|
377
|
-
}));
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
if (last !== void 0 && meta.timestamp < last) {
|
|
381
|
-
rejected++;
|
|
382
|
-
late++;
|
|
383
|
-
if (++lateRun > MaxConsecutiveLateFrames) finish("timestamp-discontinuity", BrowserError.make({
|
|
384
|
-
operation: "capture",
|
|
385
|
-
reason: "timestamp"
|
|
386
|
-
}));
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (last === meta.timestamp) {
|
|
390
|
-
duplicates++;
|
|
391
|
-
rejected++;
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
const dimensions = jpegGeometry(frame.data);
|
|
395
|
-
if (dimensions.width > 16384 || dimensions.height > 16384 || size !== void 0 && (dimensions.width > size.width || dimensions.height > size.height) || dimensions.width * dimensions.height > 33554432 || frame.data[frame.data.length - 2] !== 255 || frame.data[frame.data.length - 1] !== 217) {
|
|
396
|
-
rejected++;
|
|
397
|
-
finish("frame-limit", BrowserError.make({
|
|
398
|
-
operation: "capture",
|
|
399
|
-
reason: "limit"
|
|
400
|
-
}));
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (geometry !== void 0 && (geometry.width !== dimensions.width || geometry.height !== dimensions.height || geometry.viewportWidth !== meta.viewportWidth || geometry.viewportHeight !== meta.viewportHeight)) {
|
|
404
|
-
rejected++;
|
|
405
|
-
finish("resized", BrowserError.make({
|
|
406
|
-
operation: "capture",
|
|
407
|
-
reason: "resized"
|
|
408
|
-
}));
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
geometry ??= {
|
|
412
|
-
...dimensions,
|
|
413
|
-
viewportWidth: meta.viewportWidth,
|
|
414
|
-
viewportHeight: meta.viewportHeight
|
|
415
|
-
};
|
|
416
|
-
first ??= meta.timestamp;
|
|
417
|
-
last = meta.timestamp;
|
|
418
|
-
lateRun = 0;
|
|
419
|
-
buffer.offer({
|
|
420
|
-
bytes: new Uint8Array(frame.data),
|
|
421
|
-
mediaType: "image/jpeg",
|
|
422
|
-
target,
|
|
423
|
-
sequence,
|
|
424
|
-
document,
|
|
425
|
-
sourceTimeMillis: meta.timestamp,
|
|
426
|
-
sourceClock: "presentation-unix-millis",
|
|
427
|
-
receivedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),
|
|
428
|
-
...dimensions,
|
|
429
|
-
viewportWidth: meta.viewportWidth,
|
|
430
|
-
viewportHeight: meta.viewportHeight
|
|
431
|
-
});
|
|
432
|
-
Queue.offerUnsafe(wake, void 0);
|
|
433
|
-
} catch {
|
|
434
|
-
rejected++;
|
|
435
|
-
finish("malformed-frame", BrowserError.make({
|
|
436
|
-
operation: "capture",
|
|
437
|
-
reason: "malformed"
|
|
438
|
-
}));
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
yield* parent.owner.guard("capture-start", (ticket) => Effect.gen(function* () {
|
|
442
|
-
const resolved = yield* parent.resolve(ticket, options.target);
|
|
443
|
-
target = Object.freeze(resolved.target);
|
|
444
|
-
source = resolved.source;
|
|
445
|
-
leaseKey = resolved.key;
|
|
446
|
-
if (parent.captureLeases.has(leaseKey)) return yield* BrowserError.make({
|
|
447
|
-
operation: "capture",
|
|
448
|
-
reason: "busy",
|
|
449
|
-
outcome: "undispatched"
|
|
450
|
-
});
|
|
451
|
-
if (parent.captureLeases.size >= MaxParentCaptures || parent.captureReservedBytes + maxBytes > MaxParentBufferedBytes) return yield* BrowserError.make({
|
|
452
|
-
operation: "capture",
|
|
453
|
-
reason: "limit",
|
|
454
|
-
outcome: "undispatched"
|
|
455
|
-
});
|
|
456
|
-
lease = {
|
|
457
|
-
reservedBytes: maxBytes,
|
|
458
|
-
stop: stopNative.pipe(Effect.asVoid),
|
|
459
|
-
invalidate: (why) => finish(why, BrowserError.make({
|
|
460
|
-
operation: "capture",
|
|
461
|
-
reason: why === "resized" ? "resized" : "target-changed"
|
|
462
|
-
}))
|
|
463
|
-
};
|
|
464
|
-
parent.captureLeases.set(leaseKey, lease);
|
|
465
|
-
parent.captureReservedBytes += maxBytes;
|
|
466
|
-
yield* Effect.addFinalizer(() => stopNative.pipe(Effect.andThen(Effect.sync(() => {
|
|
467
|
-
rejected += buffer.size;
|
|
468
|
-
buffer.clear();
|
|
469
|
-
}))));
|
|
470
|
-
startSettled = false;
|
|
471
|
-
yield* Effect.tryPromise({
|
|
472
|
-
try: () => {
|
|
473
|
-
startPromise = source.start({
|
|
474
|
-
receive,
|
|
475
|
-
quality,
|
|
476
|
-
invalidate: (why) => lease?.invalidate(why),
|
|
477
|
-
opened: (url) => {
|
|
478
|
-
initialUrl = documentUrl(url);
|
|
479
|
-
},
|
|
480
|
-
...size === void 0 ? {} : { size },
|
|
481
|
-
...options.lifetime === "page" ? { document: nextDocument } : {}
|
|
482
|
-
});
|
|
483
|
-
startPromise.then(() => {
|
|
484
|
-
startSettled = true;
|
|
485
|
-
if (cleanupFinished && nativeStop === "unconfirmed" && leaseKey !== void 0 && parent.captureLeases.get(leaseKey) === lease) source.stop().catch(() => {});
|
|
486
|
-
}, () => {
|
|
487
|
-
startSettled = true;
|
|
488
|
-
});
|
|
489
|
-
return startPromise;
|
|
490
|
-
},
|
|
491
|
-
catch: () => BrowserError.make({
|
|
492
|
-
operation: "capture-start",
|
|
493
|
-
reason: "provider"
|
|
494
|
-
})
|
|
495
|
-
});
|
|
496
|
-
ticket.check();
|
|
497
|
-
}), { charge: false }).pipe(Effect.onError(() => lease === void 0 ? Effect.void : stopNative.pipe(Effect.asVoid)), Effect.onInterrupt(() => lease === void 0 ? Effect.void : stopNative.pipe(Effect.asVoid)));
|
|
498
|
-
yield* Effect.raceFirst(Queue.take(finished).pipe(Effect.as(false)), Effect.sleep(duration).pipe(Effect.as(true))).pipe(Effect.tap((expired) => expired ? Effect.sync(() => finish("duration-limit")) : Effect.void), Effect.andThen(stopNative), Effect.forkScoped);
|
|
499
|
-
const next = Effect.suspend(() => {
|
|
500
|
-
const frame = buffer.take();
|
|
501
|
-
if (frame !== void 0) {
|
|
502
|
-
delivered++;
|
|
503
|
-
return Effect.succeed(frame);
|
|
504
|
-
}
|
|
505
|
-
if (ended) return error === void 0 ? Cause.done() : Effect.fail(error);
|
|
506
|
-
return Queue.take(wake).pipe(Effect.andThen(next));
|
|
507
|
-
});
|
|
508
|
-
return {
|
|
509
|
-
frames: Stream.unwrap(Effect.suspend(() => {
|
|
510
|
-
if (subscribed) return Effect.fail(BrowserError.make({
|
|
511
|
-
operation: "capture-consume",
|
|
512
|
-
reason: "busy"
|
|
513
|
-
}));
|
|
514
|
-
subscribed = true;
|
|
515
|
-
return Effect.succeed(Stream.fromEffectRepeat(next).pipe(Stream.ensuring(stopNative.pipe(Effect.asVoid))));
|
|
516
|
-
})),
|
|
517
|
-
snapshot: Effect.sync(() => {
|
|
518
|
-
const { reason, nativeStop, ...metadata } = snapshot();
|
|
519
|
-
return CaptureSnapshot.make({
|
|
520
|
-
...metadata,
|
|
521
|
-
phase: cleanupFinished ? "stopped" : ended ? "stopping" : "capturing",
|
|
522
|
-
observedMonotonicNanos: clock.monotonicTimeNanosUnsafe(),
|
|
523
|
-
currentDocument: document,
|
|
524
|
-
reason: ended ? reason : null,
|
|
525
|
-
nativeStop: cleanupFinished ? nativeStop : null
|
|
526
|
-
});
|
|
527
|
-
}),
|
|
528
|
-
stop: stopNative,
|
|
529
|
-
completed: Deferred.await(completed).pipe(Effect.map(snapshot))
|
|
530
|
-
};
|
|
531
|
-
});
|
|
532
|
-
//#endregion
|
|
533
|
-
//#region src/Capture.ts
|
|
534
|
-
var Capture_exports = /* @__PURE__ */ __exportAll({
|
|
535
|
-
CaptureOptions: () => CaptureOptions,
|
|
536
|
-
CaptureSize: () => CaptureSize,
|
|
537
|
-
CaptureSnapshot: () => CaptureSnapshot,
|
|
538
|
-
CaptureSummary: () => CaptureSummary,
|
|
539
|
-
CapturedFrame: () => CapturedFrame,
|
|
540
|
-
start: () => start
|
|
541
|
-
});
|
|
542
|
-
/**
|
|
543
|
-
* Capture one remote page independently of the session's selected page.
|
|
544
|
-
*
|
|
545
|
-
* Requires the exact live session returned by the host; copying a session object or decoding
|
|
546
|
-
* a durable reference cannot copy its capture authority. The private owner is not frame data.
|
|
547
|
-
* `CapturedFrame` and `CaptureOptions` are Schema values as well as structural types. Frame
|
|
548
|
-
* decoding checks binary/metadata fields, not the complete JPEG bitstream or target authority,
|
|
549
|
-
* and does not copy bytes. Options decoding preserves omissions; admission applies defaults.
|
|
550
|
-
* `CaptureInterval` remains a live scoped capability, not a schema or JSON/Tool value.
|
|
551
|
-
*/
|
|
552
|
-
const start = (session, options = {}) => Effect.suspend(() => {
|
|
553
|
-
const parent = captureParent(session);
|
|
554
|
-
if (parent === void 0) return Effect.fail(BrowserError.make({
|
|
555
|
-
operation: "capture",
|
|
556
|
-
reason: "closed"
|
|
557
|
-
}));
|
|
558
|
-
if (options.target === void 0) return startCapture(parent, options);
|
|
559
|
-
return Schema.decodeEffect(PageInfo)(options.target, { onExcessProperty: "error" }).pipe(Effect.mapError(() => BrowserError.make({
|
|
560
|
-
operation: "capture",
|
|
561
|
-
reason: "configuration",
|
|
562
|
-
outcome: "undispatched"
|
|
563
|
-
})), Effect.flatMap((target) => startCapture(parent, {
|
|
564
|
-
...options,
|
|
565
|
-
target
|
|
566
|
-
})));
|
|
567
|
-
});
|
|
568
|
-
//#endregion
|
|
569
|
-
export { CaptureSnapshot as a, CaptureSize as i, start as n, CaptureSummary as o, CaptureOptions as r, CapturedFrame as s, Capture_exports as t };
|
|
570
|
-
|
|
571
|
-
//# sourceMappingURL=Capture-Bc0z9IAi.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Capture-Bc0z9IAi.mjs","names":[],"sources":["../src/internal/capture/CaptureTypes.ts","../src/internal/capture/FrameBuffer.ts","../src/internal/capture/Capture.ts","../src/Capture.ts"],"sourcesContent":["import { Schema, type Effect, type Stream } from \"effect\";\n\nimport { PageInfo, Target } from \"../../BrowserData.ts\";\nimport { BrowserError } from \"../../Errors.ts\";\nconst 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/** Defaults shared by 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\nconst LimitFields = {\n maxFrames: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 64 })),\n maxBufferedBytes: BufferedBytes,\n maxFrameBytes: BufferedBytes,\n maxDurationMillis: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 600000 })),\n quality: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })),\n};\n\nconst 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/** Private resolved admission schema. It is not an additional public options API. */\nexport const CaptureLimits = Schema.Struct(LimitFields).check(FrameBudget);\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 maxFrames: Schema.optionalKey(LimitFields.maxFrames),\n maxBufferedBytes: Schema.optionalKey(LimitFields.maxBufferedBytes),\n maxFrameBytes: Schema.optionalKey(LimitFields.maxFrameBytes),\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>(\"BrowserbaseCaptureSummary\")({\n target: Target,\n reason: Schema.String.check(Schema.isMaxLength(64)),\n received: Schema.Natural,\n delivered: Schema.Natural,\n dropped: Schema.Natural,\n duplicates: Schema.Natural,\n /** Arrived after a newer frame was accepted; counted within `dropped`, never reordered in. */\n late: 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 /** More navigations were observed than are recorded above. Frames still count them all. */\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>(\"BrowserbaseCaptureSnapshot\")({\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\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","/** 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 { BrowserError } from \"../../Errors.ts\";\nimport { type CaptureLease, type CaptureParent } from \"../browser/Association.ts\";\nimport type { CaptureSource, NativeFrame } from \"../browser/Driver.ts\";\nimport {\n CaptureDefaults,\n CaptureLimits,\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n type CaptureInterval,\n type CaptureOptions,\n type CapturedFrame,\n} from \"./CaptureTypes.ts\";\nimport { FrameBuffer } from \"./FrameBuffer.ts\";\nimport { jpegGeometry } from \"./Images.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: \"configuration\",\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: \"configuration\",\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 dropped: buffer.dropped + rejected,\n duplicates,\n late,\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 ...(error.outcome === undefined ? {} : { outcome: error.outcome }),\n ...(error.status === undefined ? {} : { status: error.status }),\n ...(error.retryAfterMillis === undefined\n ? {}\n : { retryAfterMillis: error.retryAfterMillis }),\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: () => BrowserError.make({ operation: \"capture-start\", reason: \"provider\" }),\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: () => BrowserError.make({ operation: \"capture-stop\", reason: \"provider\" }),\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(\"parent-unavailable\", BrowserError.make({ operation: \"capture\", reason: \"closed\" }));\n\n return;\n }\n const sequence = received++;\n\n try {\n const meta = parseMetadata(frame);\n\n if (!(frame.data instanceof Uint8Array) || frame.data.length > maxFrameBytes) {\n rejected++;\n finish(\"frame-limit\", BrowserError.make({ operation: \"capture\", reason: \"limit\" }));\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 rejected++;\n late++;\n if (++lateRun > MaxConsecutiveLateFrames) {\n finish(\n \"timestamp-discontinuity\",\n BrowserError.make({ operation: \"capture\", reason: \"timestamp\" }),\n );\n }\n\n return;\n }\n if (last === meta.timestamp) {\n duplicates++;\n rejected++;\n\n return;\n }\n const dimensions = jpegGeometry(frame.data);\n\n if (\n dimensions.width > 16384 ||\n dimensions.height > 16384 ||\n (size !== undefined &&\n (dimensions.width > size.width || dimensions.height > size.height)) ||\n dimensions.width * dimensions.height > 33_554_432 ||\n frame.data[frame.data.length - 2] !== 255 ||\n frame.data[frame.data.length - 1] !== 217\n ) {\n rejected++;\n finish(\"frame-limit\", BrowserError.make({ operation: \"capture\", reason: \"limit\" }));\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(\"resized\", BrowserError.make({ operation: \"capture\", reason: \"resized\" }));\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(\"malformed-frame\", BrowserError.make({ operation: \"capture\", reason: \"malformed\" }));\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: \"busy\",\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: \"limit\",\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: why === \"resized\" ? \"resized\" : \"target-changed\",\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: () => BrowserError.make({ operation: \"capture-start\", reason: \"provider\" }),\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(BrowserError.make({ operation: \"capture-consume\", reason: \"busy\" }));\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 } from \"effect\";\n\nimport type { BrowserSession } from \"./Browser.ts\";\nimport { PageInfo } from \"./BrowserData.ts\";\nimport { BrowserError } from \"./Errors.ts\";\nimport { captureParent } from \"./internal/browser/Association.ts\";\nimport { startCapture } from \"./internal/capture/Capture.ts\";\nimport type { CaptureInterval, CaptureOptions } from \"./internal/capture/CaptureTypes.ts\";\n\nexport {\n CapturedFrame,\n CaptureOptions,\n CaptureSize,\n CaptureSnapshot,\n CaptureSummary,\n} from \"./internal/capture/CaptureTypes.ts\";\n\nexport type { CaptureInterval } from \"./internal/capture/CaptureTypes.ts\";\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(BrowserError.make({ operation: \"capture\", reason: \"closed\" }));\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: \"configuration\",\n outcome: \"undispatched\",\n }),\n ),\n Effect.flatMap((target) => startCapture(parent, { ...options, target })),\n );\n });\n"],"mappings":"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"}
|