@effect-agent/platform-cloudflare 0.1.0-beta.106 → 0.1.0-beta.108
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/dist/CloudflareThreadClient.d.mts +47 -47
- package/dist/{InteractiveBrowser-BR16UPbF.d.mts → InteractiveBrowser-CKp6pqmw.d.mts} +4 -2
- package/dist/{InteractiveBrowser-BCS9g0tn.mjs → InteractiveBrowser-l6J4eP6T.mjs} +286 -39
- package/dist/InteractiveBrowser-l6J4eP6T.mjs.map +1 -0
- package/dist/InteractiveBrowser.d.mts +1 -1
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/ProtectedBrowser.d.mts +3 -3
- package/dist/ProtectedBrowser.mjs +160 -135
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-CLBMWEPn.d.mts → ThreadObject-D_vvj7Qx.d.mts} +31 -31
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/package.json +1 -1
- package/src/InteractiveBrowser.ts +242 -48
- package/src/internal/browser-binding.ts +82 -0
- package/src/internal/browser-failure.ts +92 -0
- package/src/internal/browser-file-selection.ts +126 -0
- package/src/internal/browser-session-lifecycle.ts +38 -5
- package/src/protected-browser/binding.ts +170 -129
- package/src/protected-browser/host.ts +52 -19
- package/src/protected-browser/native.ts +45 -17
- package/src/protected-browser/policy.ts +75 -39
- package/dist/InteractiveBrowser-BCS9g0tn.mjs.map +0 -1
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { type Browser, type Page, type CDPSession } from "@cloudflare/puppeteer";
|
|
3
|
+
import { Cause, Context, Crypto, Effect, Layer, Redacted, Schema, type Scope } from "effect";
|
|
4
4
|
import { type InteractiveBrowserPolicy } from "effect-agent/interactive-browser";
|
|
5
5
|
import { ProtectedBrowserError } from "effect-agent/protected-browser";
|
|
6
6
|
|
|
7
|
+
import { BrowserRunBinding } from "../internal/browser-binding.ts";
|
|
8
|
+
import {
|
|
9
|
+
browserFailure,
|
|
10
|
+
BrowserRunFailure,
|
|
11
|
+
reportBrowserCause,
|
|
12
|
+
reportedBrowserError,
|
|
13
|
+
} from "../internal/browser-failure.ts";
|
|
7
14
|
import { BrowserRunSessionLifecycle } from "../internal/browser-session-lifecycle.ts";
|
|
8
15
|
import { makeProtectedNativeTransport, ProtectedNativeSession } from "./native.ts";
|
|
9
16
|
import {
|
|
@@ -50,6 +57,7 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
50
57
|
Layer.effect(BrowserRunProtectedBinding)(
|
|
51
58
|
Effect.gen(function* () {
|
|
52
59
|
const lifecycle = yield* BrowserRunSessionLifecycle;
|
|
60
|
+
const binding = yield* BrowserRunBinding;
|
|
53
61
|
const crypto = yield* Crypto.Crypto;
|
|
54
62
|
|
|
55
63
|
const open = Effect.fn("BrowserRunProtectedTransport.open")(function* (
|
|
@@ -73,8 +81,8 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
73
81
|
let driver: ProtectedBrowserTransport | undefined;
|
|
74
82
|
let invalid = false;
|
|
75
83
|
// SDK acquisition may finish after interruption. Its late-reply callback must await cleanup,
|
|
76
|
-
//
|
|
77
|
-
const runCleanup = Effect.runPromiseWith(
|
|
84
|
+
// retaining this pass's clock and private reporting scope.
|
|
85
|
+
const runCleanup = Effect.runPromiseWith(yield* Effect.context<never>());
|
|
78
86
|
|
|
79
87
|
const terminate = Effect.gen(function* () {
|
|
80
88
|
invalid = true;
|
|
@@ -83,11 +91,19 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
83
91
|
|
|
84
92
|
const cleanup = yield* lifecycle.close(sessionId).pipe(
|
|
85
93
|
Effect.as("confirmed" as const),
|
|
86
|
-
Effect.catchCause(() =>
|
|
94
|
+
Effect.catchCause((cause) =>
|
|
95
|
+
reportBrowserCause("protected.close", cause).pipe(Effect.as("unconfirmed" as const)),
|
|
96
|
+
),
|
|
87
97
|
Effect.interruptible,
|
|
88
98
|
Effect.timeoutOrElse({
|
|
89
99
|
duration: "10 seconds",
|
|
90
|
-
orElse: () =>
|
|
100
|
+
orElse: () =>
|
|
101
|
+
reportBrowserCause(
|
|
102
|
+
"protected.close",
|
|
103
|
+
Cause.fail(
|
|
104
|
+
new BrowserRunFailure({ operation: "protected.close", reason: "timeout" }),
|
|
105
|
+
),
|
|
106
|
+
).pipe(Effect.as("unconfirmed" as const)),
|
|
91
107
|
}),
|
|
92
108
|
);
|
|
93
109
|
|
|
@@ -96,9 +112,21 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
96
112
|
|
|
97
113
|
if (connected !== undefined)
|
|
98
114
|
yield* Effect.promise(() => connected.disconnect()).pipe(
|
|
99
|
-
Effect.catchCause(() =>
|
|
115
|
+
Effect.catchCause((cause) => reportBrowserCause("protected.disconnect", cause)),
|
|
100
116
|
Effect.interruptible,
|
|
101
|
-
Effect.timeoutOrElse({
|
|
117
|
+
Effect.timeoutOrElse({
|
|
118
|
+
duration: "1 second",
|
|
119
|
+
orElse: () =>
|
|
120
|
+
reportBrowserCause(
|
|
121
|
+
"protected.disconnect",
|
|
122
|
+
Cause.fail(
|
|
123
|
+
new BrowserRunFailure({
|
|
124
|
+
operation: "protected.disconnect",
|
|
125
|
+
reason: "timeout",
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
),
|
|
129
|
+
}),
|
|
102
130
|
);
|
|
103
131
|
|
|
104
132
|
return cleanup;
|
|
@@ -106,144 +134,138 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
106
134
|
|
|
107
135
|
const close = yield* Effect.cached(terminate);
|
|
108
136
|
|
|
109
|
-
yield* Effect.addFinalizer(() =>
|
|
110
|
-
|
|
111
|
-
? Effect.void
|
|
112
|
-
: close.pipe(
|
|
113
|
-
Effect.flatMap((state) =>
|
|
114
|
-
state === "confirmed"
|
|
115
|
-
? Effect.void
|
|
116
|
-
: Effect.logWarning("Protected browser exact-session closure unconfirmed"),
|
|
117
|
-
),
|
|
118
|
-
),
|
|
119
|
-
);
|
|
137
|
+
yield* Effect.addFinalizer(() => (detached ? Effect.void : close));
|
|
138
|
+
let stage = identity === undefined ? "protected.acquire" : "protected.connect";
|
|
120
139
|
|
|
121
140
|
const acquired = yield* Effect.tryPromise({
|
|
122
141
|
try: async (signal) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
try {
|
|
143
|
+
sessionId =
|
|
144
|
+
identity === undefined
|
|
145
|
+
? Redacted.make(
|
|
146
|
+
await binding.acquire(
|
|
147
|
+
Math.min(600_000, Math.max(10_000, policy.maxElapsedMillis)),
|
|
148
|
+
"protected.acquire",
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
: identity.sessionId;
|
|
152
|
+
if (signal.aborted || invalid) {
|
|
153
|
+
await runCleanup(terminate);
|
|
154
|
+
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
155
|
+
}
|
|
156
|
+
// Resume only a host-persisted exact page. Never open a replacement page or context.
|
|
157
|
+
stage = "protected.connect";
|
|
158
|
+
browser = await binding.connect(Redacted.value(sessionId), stage);
|
|
159
|
+
if (signal.aborted || invalid) {
|
|
160
|
+
await runCleanup(terminate);
|
|
161
|
+
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
162
|
+
}
|
|
163
|
+
let page: Page;
|
|
133
164
|
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
if (identity === undefined) {
|
|
166
|
+
stage = "protected.context";
|
|
167
|
+
const context = await browser.createBrowserContext();
|
|
136
168
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const acquired =
|
|
142
|
-
identity === undefined
|
|
143
|
-
? await puppeteer.acquire(binding, {
|
|
144
|
-
recording: false,
|
|
145
|
-
// Provider inactivity timeout, independent of the finite total pass deadline.
|
|
146
|
-
keep_alive: Math.min(600_000, Math.max(10_000, policy.maxElapsedMillis)),
|
|
147
|
-
})
|
|
148
|
-
: { sessionId: Redacted.value(identity.sessionId) };
|
|
149
|
-
|
|
150
|
-
sessionId = Redacted.make(
|
|
151
|
-
Schema.decodeSync(Schema.String.check(Schema.isUUID()))(acquired.sessionId),
|
|
152
|
-
);
|
|
153
|
-
if ((identity === undefined && !recordingDisabled) || signal.aborted || invalid) {
|
|
154
|
-
await runCleanup(terminate);
|
|
155
|
-
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
156
|
-
}
|
|
157
|
-
// Resume only a host-persisted exact page. Never open a replacement page or context.
|
|
158
|
-
browser = await puppeteer.connect(options.browser, acquired.sessionId);
|
|
159
|
-
if (signal.aborted || invalid) {
|
|
160
|
-
await runCleanup(terminate);
|
|
161
|
-
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
162
|
-
}
|
|
163
|
-
let page: Page;
|
|
169
|
+
stage = "protected.page";
|
|
170
|
+
page = await context.newPage();
|
|
171
|
+
} else {
|
|
172
|
+
stage = "protected.resume";
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
const context = browser
|
|
175
|
+
.browserContexts()
|
|
176
|
+
.find((candidate) => candidate.id === Redacted.value(identity.contextId));
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
.find((candidate) => candidate.id === Redacted.value(identity.contextId));
|
|
178
|
+
if (context === undefined)
|
|
179
|
+
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
180
|
+
const pages = await context.pages();
|
|
181
|
+
let found: Page | undefined;
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let found: Page | undefined;
|
|
183
|
+
for (const candidate of pages) {
|
|
184
|
+
const client = await candidate.createCDPSession();
|
|
185
|
+
const info = await client.send("Target.getTargetInfo");
|
|
178
186
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
await client.detach();
|
|
188
|
+
if (info.targetInfo.targetId === Redacted.value(identity.targetId))
|
|
189
|
+
found = candidate;
|
|
190
|
+
}
|
|
191
|
+
if (found === undefined || pages.length !== 1)
|
|
192
|
+
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
193
|
+
page = found;
|
|
186
194
|
}
|
|
187
|
-
|
|
195
|
+
stage = "protected.control";
|
|
196
|
+
control = await page.createCDPSession();
|
|
197
|
+
const info = await control.send("Target.getTargetInfo");
|
|
198
|
+
|
|
199
|
+
stage = "protected.identity";
|
|
200
|
+
providerIdentity = Schema.decodeSync(ProtectedProviderIdentity)({
|
|
201
|
+
sessionId,
|
|
202
|
+
contextId: Redacted.make(page.browserContext().id ?? ""),
|
|
203
|
+
targetId: Redacted.make(info.targetInfo.targetId),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
stage = "protected.interception";
|
|
207
|
+
await page.setBypassServiceWorker(true);
|
|
208
|
+
await page.setRequestInterception(true);
|
|
209
|
+
page.on("request", (request) => {
|
|
210
|
+
let allowed = policy.network._tag === "Unrestricted";
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const url = new URL(request.url());
|
|
214
|
+
|
|
215
|
+
allowed ||=
|
|
216
|
+
policy.network._tag === "ExactHosts" &&
|
|
217
|
+
url.protocol === "https:" &&
|
|
218
|
+
!url.username &&
|
|
219
|
+
!url.password &&
|
|
220
|
+
policy.network.allowedHosts.includes(url.host);
|
|
221
|
+
} catch {
|
|
222
|
+
/* Refuse malformed destinations. */
|
|
223
|
+
}
|
|
224
|
+
void (
|
|
225
|
+
allowed && !invalid ? request.continue() : request.abort("blockedbyclient")
|
|
226
|
+
).catch(async (cause) => {
|
|
227
|
+
invalid = true;
|
|
228
|
+
driver?.invalidate();
|
|
229
|
+
await runCleanup(reportBrowserCause("protected.interception", Cause.fail(cause)));
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
if (signal.aborted || invalid) {
|
|
233
|
+
await runCleanup(terminate);
|
|
188
234
|
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
189
|
-
page = found;
|
|
190
|
-
}
|
|
191
|
-
control = await page.createCDPSession();
|
|
192
|
-
const info = await control.send("Target.getTargetInfo");
|
|
193
|
-
|
|
194
|
-
providerIdentity = Schema.decodeSync(ProtectedProviderIdentity)({
|
|
195
|
-
sessionId,
|
|
196
|
-
contextId: Redacted.make(page.browserContext().id ?? ""),
|
|
197
|
-
targetId: Redacted.make(info.targetInfo.targetId),
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
await page.setBypassServiceWorker(true);
|
|
201
|
-
await page.setRequestInterception(true);
|
|
202
|
-
page.on("request", (request) => {
|
|
203
|
-
let allowed = policy.network._tag === "Unrestricted";
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
const url = new URL(request.url());
|
|
207
|
-
|
|
208
|
-
allowed ||=
|
|
209
|
-
policy.network._tag === "ExactHosts" &&
|
|
210
|
-
url.protocol === "https:" &&
|
|
211
|
-
!url.username &&
|
|
212
|
-
!url.password &&
|
|
213
|
-
policy.network.allowedHosts.includes(url.host);
|
|
214
|
-
} catch {
|
|
215
|
-
/* Refuse malformed destinations. */
|
|
216
235
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
|
|
237
|
+
return ProtectedNativeSession.of({
|
|
238
|
+
browser,
|
|
239
|
+
page,
|
|
240
|
+
close,
|
|
241
|
+
release: Effect.suspend(() => (detached ? Effect.void : close.pipe(Effect.asVoid))),
|
|
222
242
|
});
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
throw
|
|
243
|
+
} catch (cause) {
|
|
244
|
+
if (signal.aborted && !(cause instanceof ProtectedTransportError))
|
|
245
|
+
await runCleanup(reportBrowserCause(stage, Cause.fail(cause)));
|
|
246
|
+
throw cause;
|
|
227
247
|
}
|
|
228
|
-
|
|
229
|
-
return ProtectedNativeSession.of({
|
|
230
|
-
browser,
|
|
231
|
-
page,
|
|
232
|
-
close,
|
|
233
|
-
release: Effect.suspend(() => (detached ? Effect.void : close.pipe(Effect.asVoid))),
|
|
234
|
-
});
|
|
235
248
|
},
|
|
236
|
-
catch:
|
|
249
|
+
catch: (cause) => browserFailure(stage, cause),
|
|
237
250
|
}).pipe(
|
|
238
251
|
Effect.timeoutOrElse({
|
|
239
252
|
duration: Math.min(policy.maxElapsedMillis, 30_000),
|
|
240
253
|
orElse: () =>
|
|
241
|
-
Effect.fail(new
|
|
254
|
+
Effect.fail(new BrowserRunFailure({ operation: stage, reason: "timeout" })),
|
|
242
255
|
}),
|
|
243
256
|
Effect.catch((error) =>
|
|
244
|
-
|
|
257
|
+
reportBrowserCause(stage, Cause.fail(error)).pipe(
|
|
258
|
+
Effect.andThen(close),
|
|
245
259
|
Effect.flatMap((cleanup) =>
|
|
246
|
-
Effect.fail(
|
|
260
|
+
Effect.fail(
|
|
261
|
+
reportedBrowserError(
|
|
262
|
+
new ProtectedBrowserError({
|
|
263
|
+
...failure(),
|
|
264
|
+
reason: error.reason === "timeout" ? "timeout" : "provider",
|
|
265
|
+
cleanup,
|
|
266
|
+
}),
|
|
267
|
+
),
|
|
268
|
+
),
|
|
247
269
|
),
|
|
248
270
|
),
|
|
249
271
|
),
|
|
@@ -271,12 +293,20 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
271
293
|
|
|
272
294
|
return await Reflect.apply(send, control, [method, parameters]);
|
|
273
295
|
},
|
|
274
|
-
catch:
|
|
296
|
+
catch: (cause) => browserFailure("protected.command", cause),
|
|
275
297
|
}).pipe(
|
|
276
298
|
Effect.timeoutOrElse({
|
|
277
299
|
duration: "10 seconds",
|
|
278
|
-
orElse: () =>
|
|
300
|
+
orElse: () =>
|
|
301
|
+
Effect.fail(
|
|
302
|
+
new BrowserRunFailure({ operation: "protected.command", reason: "timeout" }),
|
|
303
|
+
),
|
|
279
304
|
}),
|
|
305
|
+
Effect.catch((error) =>
|
|
306
|
+
reportBrowserCause("protected.command", Cause.fail(error)).pipe(
|
|
307
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure()))),
|
|
308
|
+
),
|
|
309
|
+
),
|
|
280
310
|
),
|
|
281
311
|
detach: Effect.tryPromise({
|
|
282
312
|
try: async () => {
|
|
@@ -286,16 +316,27 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
286
316
|
detached = true;
|
|
287
317
|
driver?.invalidate();
|
|
288
318
|
},
|
|
289
|
-
catch:
|
|
319
|
+
catch: (cause) => browserFailure("protected.detach", cause),
|
|
290
320
|
}).pipe(
|
|
291
|
-
Effect.timeoutOrElse({
|
|
321
|
+
Effect.timeoutOrElse({
|
|
322
|
+
duration: "10 seconds",
|
|
323
|
+
orElse: () =>
|
|
324
|
+
Effect.fail(
|
|
325
|
+
new BrowserRunFailure({ operation: "protected.detach", reason: "timeout" }),
|
|
326
|
+
),
|
|
327
|
+
}),
|
|
328
|
+
Effect.catch((error) =>
|
|
329
|
+
reportBrowserCause("protected.detach", Cause.fail(error)).pipe(
|
|
330
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure()))),
|
|
331
|
+
),
|
|
332
|
+
),
|
|
292
333
|
),
|
|
293
334
|
};
|
|
294
335
|
}, Effect.withTracerEnabled(false));
|
|
295
336
|
|
|
296
337
|
return { open };
|
|
297
338
|
}),
|
|
298
|
-
);
|
|
339
|
+
).pipe(Layer.provide(BrowserRunBinding.layer(options.browser)));
|
|
299
340
|
|
|
300
341
|
/** One binding implementation serves scoped tools and host-managed protected handoffs. */
|
|
301
342
|
export const browserRunProtectedBindingLayer = (options: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Cause,
|
|
2
3
|
Clock,
|
|
3
4
|
Context,
|
|
4
5
|
Crypto,
|
|
@@ -25,6 +26,11 @@ import {
|
|
|
25
26
|
BrowserRunLiveViewRequest,
|
|
26
27
|
BrowserRunLiveViewResult,
|
|
27
28
|
} from "../InteractiveBrowser.ts";
|
|
29
|
+
import {
|
|
30
|
+
inheritBrowserReport,
|
|
31
|
+
reportBrowserCause,
|
|
32
|
+
reportedBrowserError,
|
|
33
|
+
} from "../internal/browser-failure.ts";
|
|
28
34
|
import { BrowserRunSessionLifecycle } from "../internal/browser-session-lifecycle.ts";
|
|
29
35
|
import { BrowserRunProtectedBinding, ProtectedProviderIdentity } from "./binding.ts";
|
|
30
36
|
import { BrowserRunProtectedTransport, makeProtectedBrowserPolicy } from "./policy.ts";
|
|
@@ -184,7 +190,11 @@ export const browserRunProtectedHostLayer = () =>
|
|
|
184
190
|
const raw = yield* provider.command("Cloudflare.getHandoffState", {});
|
|
185
191
|
|
|
186
192
|
const result = yield* Schema.decodeUnknownEffect(HandoffState)(raw).pipe(
|
|
187
|
-
Effect.
|
|
193
|
+
Effect.catch((error) =>
|
|
194
|
+
reportBrowserCause("protected.host.decode", Cause.fail(error)).pipe(
|
|
195
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure("provider")))),
|
|
196
|
+
),
|
|
197
|
+
),
|
|
188
198
|
);
|
|
189
199
|
|
|
190
200
|
if (
|
|
@@ -243,20 +253,25 @@ export const browserRunProtectedHostLayer = () =>
|
|
|
243
253
|
})
|
|
244
254
|
.pipe(
|
|
245
255
|
Effect.flatMap(Schema.decodeUnknownEffect(Handoff)),
|
|
246
|
-
Effect.
|
|
247
|
-
|
|
248
|
-
Effect.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
Effect.catchCause((cause) =>
|
|
257
|
+
Cause.hasInterruptsOnly(cause)
|
|
258
|
+
? Effect.interrupt
|
|
259
|
+
: reportBrowserCause("protected.handoff", cause).pipe(
|
|
260
|
+
Effect.andThen(state.handle.close),
|
|
261
|
+
Effect.flatMap((cleanup) =>
|
|
262
|
+
Effect.fail(
|
|
263
|
+
reportedBrowserError(
|
|
264
|
+
new ProtectedBrowserError({
|
|
265
|
+
reason: "outcome-unknown",
|
|
266
|
+
dispatch: "possibly-dispatched",
|
|
267
|
+
milestone: suspended.milestone,
|
|
268
|
+
observation: "closed",
|
|
269
|
+
cleanup,
|
|
270
|
+
}),
|
|
271
|
+
),
|
|
272
|
+
),
|
|
273
|
+
),
|
|
257
274
|
),
|
|
258
|
-
),
|
|
259
|
-
),
|
|
260
275
|
),
|
|
261
276
|
Effect.onInterrupt(() => state.handle.close),
|
|
262
277
|
);
|
|
@@ -287,12 +302,22 @@ export const browserRunProtectedHostLayer = () =>
|
|
|
287
302
|
});
|
|
288
303
|
|
|
289
304
|
const result = yield* Schema.decodeUnknownEffect(LiveView)(raw).pipe(
|
|
290
|
-
Effect.
|
|
305
|
+
Effect.catch((error) =>
|
|
306
|
+
reportBrowserCause("protected.host.decode", Cause.fail(error)).pipe(
|
|
307
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure("provider")))),
|
|
308
|
+
),
|
|
309
|
+
),
|
|
291
310
|
);
|
|
292
311
|
|
|
293
312
|
return yield* Schema.decodeEffect(BrowserRunLiveViewResult)({
|
|
294
313
|
devtoolsFrontendUrl: Redacted.make(result.devtoolsFrontendUrl),
|
|
295
|
-
}).pipe(
|
|
314
|
+
}).pipe(
|
|
315
|
+
Effect.catch((error) =>
|
|
316
|
+
reportBrowserCause("protected.host.decode", Cause.fail(error)).pipe(
|
|
317
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure("provider")))),
|
|
318
|
+
),
|
|
319
|
+
),
|
|
320
|
+
);
|
|
296
321
|
}),
|
|
297
322
|
),
|
|
298
323
|
getHandoffState: control(getHandoffState),
|
|
@@ -301,11 +326,15 @@ export const browserRunProtectedHostLayer = () =>
|
|
|
301
326
|
if ((yield* getHandoffState).active) return yield* failure("busy");
|
|
302
327
|
|
|
303
328
|
const context = yield* provider.driver.context.pipe(
|
|
304
|
-
Effect.mapError((error) => failure(error.reason)),
|
|
329
|
+
Effect.mapError((error) => inheritBrowserReport(error, failure(error.reason))),
|
|
305
330
|
);
|
|
306
331
|
|
|
307
332
|
return yield* Schema.decodeEffect(CredentialOrigin)(context.topOrigin).pipe(
|
|
308
|
-
Effect.
|
|
333
|
+
Effect.catch((error) =>
|
|
334
|
+
reportBrowserCause("protected.host.decode", Cause.fail(error)).pipe(
|
|
335
|
+
Effect.andThen(Effect.fail(reportedBrowserError(failure("provider")))),
|
|
336
|
+
),
|
|
337
|
+
),
|
|
309
338
|
);
|
|
310
339
|
}),
|
|
311
340
|
),
|
|
@@ -337,7 +366,11 @@ export const browserRunProtectedHostLayer = () =>
|
|
|
337
366
|
closeSession: (id: Redacted.Redacted<string>) =>
|
|
338
367
|
lifecycle.close(id).pipe(
|
|
339
368
|
Effect.as("confirmed" as const),
|
|
340
|
-
Effect.catchCause(() =>
|
|
369
|
+
Effect.catchCause((cause) =>
|
|
370
|
+
reportBrowserCause("protected.closeSession", cause).pipe(
|
|
371
|
+
Effect.as("unconfirmed" as const),
|
|
372
|
+
),
|
|
373
|
+
),
|
|
341
374
|
),
|
|
342
375
|
};
|
|
343
376
|
}),
|