@effect-agent/platform-cloudflare 0.1.0-beta.91 → 0.1.0-beta.93
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 +46 -46
- package/dist/{InteractiveBrowser-DupDtfw2.mjs → InteractiveBrowser-BCS9g0tn.mjs} +117 -106
- package/dist/InteractiveBrowser-BCS9g0tn.mjs.map +1 -0
- package/dist/{InteractiveBrowser-DUE_m12-.d.mts → InteractiveBrowser-BR16UPbF.d.mts} +12 -4
- package/dist/InteractiveBrowser.d.mts +2 -2
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/ProtectedBrowser.d.mts +1 -1
- package/dist/ProtectedBrowser.mjs +4 -4
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-CIbmXZCY.d.mts → ThreadObject-BVMSQgqx.d.mts} +28 -28
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/package.json +1 -1
- package/src/InteractiveBrowser.ts +341 -277
- package/src/protected-browser/host.ts +3 -3
- package/src/protected-browser/policy.ts +6 -6
- package/dist/InteractiveBrowser-DupDtfw2.mjs.map +0 -1
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
Ref,
|
|
18
18
|
Schema,
|
|
19
19
|
Semaphore,
|
|
20
|
-
|
|
20
|
+
Scope,
|
|
21
21
|
} from "effect";
|
|
22
22
|
import {
|
|
23
23
|
BrowserActionResult,
|
|
@@ -348,11 +348,12 @@ export interface BrowserRunInteractiveBrowser {
|
|
|
348
348
|
readonly offDisconnected: (listener: () => void) => void;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
/** Host-supplied Browser Run
|
|
351
|
+
/** Host-supplied Browser Run allocation and connection transport. */
|
|
352
352
|
export class BrowserRunInteractiveBinding extends Context.Service<
|
|
353
353
|
BrowserRunInteractiveBinding,
|
|
354
354
|
{
|
|
355
|
-
readonly
|
|
355
|
+
readonly acquire: (keepAliveMillis: number) => Promise<unknown>;
|
|
356
|
+
readonly connect: (sessionId: string) => Promise<BrowserRunInteractiveBrowser>;
|
|
356
357
|
/** Success proves whole-browser termination or exact-session absence. */
|
|
357
358
|
readonly closeSession: (
|
|
358
359
|
sessionId: Redacted.Redacted<string>,
|
|
@@ -375,13 +376,10 @@ export class BrowserRunInteractiveBinding extends Context.Service<
|
|
|
375
376
|
options.viewport === undefined ? undefined : yield* decodeViewport(options.viewport);
|
|
376
377
|
|
|
377
378
|
return {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
...(viewport === undefined ? {} : { defaultViewport: { ...viewport } }),
|
|
383
|
-
}),
|
|
384
|
-
),
|
|
379
|
+
acquire: async (keepAliveMillis: number) =>
|
|
380
|
+
(await puppeteer.acquire(options.browser, { keep_alive: keepAliveMillis })).sessionId,
|
|
381
|
+
connect: async (sessionId: string) =>
|
|
382
|
+
makeProductionBrowser(await puppeteer.connect(options.browser, sessionId), viewport),
|
|
385
383
|
closeSession: (sessionId: Redacted.Redacted<string>) =>
|
|
386
384
|
lifecycle
|
|
387
385
|
.close(sessionId)
|
|
@@ -414,11 +412,21 @@ export interface BrowserRunInteractiveSession {
|
|
|
414
412
|
readonly close: Effect.Effect<void, InteractiveBrowserError>;
|
|
415
413
|
}
|
|
416
414
|
|
|
415
|
+
/** Persist the private cleanup identity before connecting or creating a page. */
|
|
416
|
+
export interface BrowserRunInteractiveAcquisition {
|
|
417
|
+
readonly sessionId: Redacted.Redacted<string>;
|
|
418
|
+
/** Connect at most once, within the acquisition's original scope and elapsed deadline. */
|
|
419
|
+
readonly connect: Effect.Effect<BrowserRunInteractiveSession, InteractiveBrowserError>;
|
|
420
|
+
}
|
|
421
|
+
|
|
417
422
|
/** Cloudflare host authority kept separate from the provider-neutral browser handle. */
|
|
418
423
|
export class BrowserRunInteractiveHost extends Context.Service<
|
|
419
424
|
BrowserRunInteractiveHost,
|
|
420
425
|
{
|
|
421
426
|
readonly cleanupSemantics?: "confirmed-terminal";
|
|
427
|
+
readonly acquire: (
|
|
428
|
+
policy: InteractiveBrowserPolicy,
|
|
429
|
+
) => Effect.Effect<BrowserRunInteractiveAcquisition, InteractiveBrowserError, Scope.Scope>;
|
|
422
430
|
readonly open: (
|
|
423
431
|
policy: InteractiveBrowserPolicy,
|
|
424
432
|
) => Effect.Effect<BrowserRunInteractiveSession, InteractiveBrowserError, Scope.Scope>;
|
|
@@ -1097,13 +1105,25 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
|
|
|
1097
1105
|
};
|
|
1098
1106
|
};
|
|
1099
1107
|
|
|
1100
|
-
const makeProductionContext = (
|
|
1101
|
-
|
|
1108
|
+
const makeProductionContext = (
|
|
1109
|
+
context: BrowserContext,
|
|
1110
|
+
viewport?: BrowserRunViewport,
|
|
1111
|
+
): BrowserRunInteractiveContext => ({
|
|
1112
|
+
newPage: async () => {
|
|
1113
|
+
const page = await context.newPage();
|
|
1114
|
+
|
|
1115
|
+
if (viewport !== undefined) await page.setViewport(viewport);
|
|
1116
|
+
|
|
1117
|
+
return makeProductionPage(page);
|
|
1118
|
+
},
|
|
1102
1119
|
close: () => context.close(),
|
|
1103
1120
|
});
|
|
1104
1121
|
|
|
1105
|
-
const makeProductionBrowser = (
|
|
1106
|
-
|
|
1122
|
+
const makeProductionBrowser = (
|
|
1123
|
+
browser: Browser,
|
|
1124
|
+
viewport?: BrowserRunViewport,
|
|
1125
|
+
): BrowserRunInteractiveBrowser => ({
|
|
1126
|
+
createContext: async () => makeProductionContext(await browser.createBrowserContext(), viewport),
|
|
1107
1127
|
close: () => browser.close(),
|
|
1108
1128
|
sessionId: () => browser.sessionId(),
|
|
1109
1129
|
isConnected: () => browser.isConnected(),
|
|
@@ -1176,9 +1196,9 @@ const snapshotPolicy = Effect.fn("BrowserRunInteractive.snapshotPolicy")(functio
|
|
|
1176
1196
|
InteractiveBrowserPolicySnapshot,
|
|
1177
1197
|
InteractiveBrowserPolicyDeniedError | InteractiveBrowserUnsupportedError
|
|
1178
1198
|
> {
|
|
1179
|
-
const decoded = yield* Schema.decodeEffect(InteractiveBrowserPolicy
|
|
1180
|
-
|
|
1181
|
-
);
|
|
1199
|
+
const decoded = yield* Schema.decodeEffect(InteractiveBrowserPolicy, {
|
|
1200
|
+
onExcessProperty: "error",
|
|
1201
|
+
})(input).pipe(Effect.mapError(() => policyError("The interactive browser policy is malformed")));
|
|
1182
1202
|
|
|
1183
1203
|
if (decoded.network._tag === "PublicWeb") {
|
|
1184
1204
|
return yield* InteractiveBrowserUnsupportedError.make({
|
|
@@ -1941,9 +1961,10 @@ const makeHostService = (
|
|
|
1941
1961
|
]);
|
|
1942
1962
|
});
|
|
1943
1963
|
|
|
1944
|
-
const
|
|
1964
|
+
const acquire = Effect.fn("BrowserRunInteractiveHost.acquire")(function* (
|
|
1945
1965
|
policy: InteractiveBrowserPolicy,
|
|
1946
|
-
): Effect.fn.Return<
|
|
1966
|
+
): Effect.fn.Return<BrowserRunInteractiveAcquisition, InteractiveBrowserError, Scope.Scope> {
|
|
1967
|
+
const scope = yield* Scope.Scope;
|
|
1947
1968
|
const fixedPolicy = yield* snapshotPolicy(policy);
|
|
1948
1969
|
const startedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
|
|
1949
1970
|
// Late SDK replies outlive the opening fiber; cleanup retains this pass's clock.
|
|
@@ -1970,14 +1991,19 @@ const makeHostService = (
|
|
|
1970
1991
|
: entry.close.pipe(Effect.catchCause(() => Effect.logWarning(entry.warning))),
|
|
1971
1992
|
);
|
|
1972
1993
|
|
|
1973
|
-
const
|
|
1994
|
+
const sessionIdValue = yield* Effect.acquireRelease(
|
|
1974
1995
|
withinDeadline(
|
|
1975
1996
|
Effect.tryPromise({
|
|
1976
1997
|
try: (signal) =>
|
|
1977
1998
|
closeLateAcquisition(
|
|
1978
1999
|
signal,
|
|
1979
|
-
() => binding.
|
|
1980
|
-
(
|
|
2000
|
+
() => binding.acquire(keepAliveMillis(fixedPolicy)),
|
|
2001
|
+
(id) =>
|
|
2002
|
+
runCleanup(
|
|
2003
|
+
Schema.decodeUnknownEffect(BrowserRunSessionId)(id).pipe(
|
|
2004
|
+
Effect.flatMap((value) => terminate(Redacted.make(value), [])),
|
|
2005
|
+
),
|
|
2006
|
+
),
|
|
1981
2007
|
),
|
|
1982
2008
|
catch: (cause) =>
|
|
1983
2009
|
isCapacityRefusal(cause)
|
|
@@ -1985,313 +2011,351 @@ const makeHostService = (
|
|
|
1985
2011
|
implementation: browserRunInteractiveImplementation,
|
|
1986
2012
|
message: "Browser Run has no capacity for a new browser session",
|
|
1987
2013
|
})
|
|
1988
|
-
: protocolError("
|
|
1989
|
-
})
|
|
2014
|
+
: protocolError("Acquiring the Browser Run session failed", cause),
|
|
2015
|
+
}).pipe(
|
|
2016
|
+
Effect.flatMap(Schema.decodeUnknownEffect(BrowserRunSessionId)),
|
|
2017
|
+
Effect.catchTag("SchemaError", () =>
|
|
2018
|
+
protocolError("The Browser Run session identity was malformed"),
|
|
2019
|
+
),
|
|
2020
|
+
),
|
|
1990
2021
|
fixedPolicy,
|
|
1991
2022
|
startedAt,
|
|
1992
2023
|
),
|
|
1993
|
-
(
|
|
1994
|
-
|
|
2024
|
+
(id) =>
|
|
2025
|
+
Effect.suspend(() => {
|
|
2026
|
+
state.closed.value = true;
|
|
2027
|
+
state.disconnected.value = true;
|
|
1995
2028
|
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2029
|
+
return lifecycle.managedTeardownInstalled
|
|
2030
|
+
? Effect.void
|
|
2031
|
+
: terminate(Redacted.make(id), []).pipe(
|
|
2032
|
+
Effect.catch(() => Effect.logWarning("Whole-browser cleanup remains unconfirmed")),
|
|
2033
|
+
);
|
|
2034
|
+
}),
|
|
2002
2035
|
{ interruptible: true },
|
|
2003
2036
|
);
|
|
2004
2037
|
|
|
2005
|
-
|
|
2038
|
+
const connect = yield* Effect.cached(
|
|
2039
|
+
Effect.gen(function* (): Effect.fn.Return<
|
|
2040
|
+
BrowserRunInteractiveSession,
|
|
2041
|
+
InteractiveBrowserError,
|
|
2042
|
+
Scope.Scope
|
|
2043
|
+
> {
|
|
2044
|
+
const browser = yield* Effect.acquireRelease(
|
|
2045
|
+
withinDeadline(
|
|
2046
|
+
Effect.tryPromise({
|
|
2047
|
+
try: (signal) =>
|
|
2048
|
+
closeLateAcquisition(
|
|
2049
|
+
signal,
|
|
2050
|
+
() => binding.connect(sessionIdValue),
|
|
2051
|
+
(acquired) => runCleanup(closeAcquired(acquired)),
|
|
2052
|
+
),
|
|
2053
|
+
catch: (cause) =>
|
|
2054
|
+
protocolError("Connecting to the Browser Run session failed", cause),
|
|
2055
|
+
}),
|
|
2056
|
+
fixedPolicy,
|
|
2057
|
+
startedAt,
|
|
2058
|
+
),
|
|
2059
|
+
(acquired) =>
|
|
2060
|
+
releaseBeforeManaged(
|
|
2061
|
+
closeEntry(acquired.close, "Closing the local browser connection failed"),
|
|
2062
|
+
),
|
|
2063
|
+
{ interruptible: true },
|
|
2064
|
+
);
|
|
2006
2065
|
|
|
2007
|
-
|
|
2008
|
-
state.disconnected.value = true;
|
|
2009
|
-
};
|
|
2066
|
+
closers.push(closeEntry(browser.close, "Closing the interactive browser failed"));
|
|
2010
2067
|
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2068
|
+
const disconnected = () => {
|
|
2069
|
+
state.disconnected.value = true;
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
yield* Effect.acquireRelease(
|
|
2073
|
+
Effect.try({
|
|
2074
|
+
try: () => browser.onDisconnected(disconnected),
|
|
2075
|
+
catch: (cause) =>
|
|
2076
|
+
protocolError("Installing the browser disconnect listener failed", cause),
|
|
2077
|
+
}),
|
|
2078
|
+
() =>
|
|
2079
|
+
releaseBeforeManaged(
|
|
2080
|
+
syncCloseEntry(
|
|
2081
|
+
() => browser.offDisconnected(disconnected),
|
|
2082
|
+
"Removing the browser disconnect listener failed",
|
|
2083
|
+
),
|
|
2084
|
+
),
|
|
2085
|
+
);
|
|
2086
|
+
closers.push(
|
|
2018
2087
|
syncCloseEntry(
|
|
2019
2088
|
() => browser.offDisconnected(disconnected),
|
|
2020
2089
|
"Removing the browser disconnect listener failed",
|
|
2021
2090
|
),
|
|
2022
|
-
)
|
|
2023
|
-
);
|
|
2024
|
-
closers.push(
|
|
2025
|
-
syncCloseEntry(
|
|
2026
|
-
() => browser.offDisconnected(disconnected),
|
|
2027
|
-
"Removing the browser disconnect listener failed",
|
|
2028
|
-
),
|
|
2029
|
-
);
|
|
2030
|
-
|
|
2031
|
-
const connected = yield* Effect.try({
|
|
2032
|
-
try: browser.isConnected,
|
|
2033
|
-
catch: (cause) => protocolError("Reading the Browser Run connection state failed", cause),
|
|
2034
|
-
});
|
|
2035
|
-
|
|
2036
|
-
if (!connected) {
|
|
2037
|
-
state.disconnected.value = true;
|
|
2038
|
-
|
|
2039
|
-
return yield* expiredError();
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
const sessionIdValue = yield* Effect.try({
|
|
2043
|
-
try: browser.sessionId,
|
|
2044
|
-
catch: (cause) => protocolError("Reading the Browser Run session identity failed", cause),
|
|
2045
|
-
}).pipe(
|
|
2046
|
-
Effect.flatMap((value) =>
|
|
2047
|
-
Schema.decodeUnknownEffect(BrowserRunSessionId)(value).pipe(
|
|
2048
|
-
Effect.mapError(() => protocolError("The Browser Run session identity was malformed")),
|
|
2049
|
-
),
|
|
2050
|
-
),
|
|
2051
|
-
);
|
|
2091
|
+
);
|
|
2052
2092
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
closeLateAcquisition(signal, browser.createContext, (acquired) => acquired.close()),
|
|
2058
|
-
catch: (cause) =>
|
|
2059
|
-
state.disconnected.value || isRemoteClosure(cause)
|
|
2060
|
-
? expiredError()
|
|
2061
|
-
: protocolError("Creating the browser context failed", cause),
|
|
2062
|
-
}),
|
|
2063
|
-
fixedPolicy,
|
|
2064
|
-
startedAt,
|
|
2065
|
-
),
|
|
2066
|
-
(acquired) =>
|
|
2067
|
-
releaseBeforeManaged(
|
|
2068
|
-
closeEntry(acquired.close, "Closing the interactive browser context failed"),
|
|
2069
|
-
),
|
|
2070
|
-
{ interruptible: true },
|
|
2071
|
-
);
|
|
2093
|
+
const connected = yield* Effect.try({
|
|
2094
|
+
try: browser.isConnected,
|
|
2095
|
+
catch: (cause) => protocolError("Reading the Browser Run connection state failed", cause),
|
|
2096
|
+
});
|
|
2072
2097
|
|
|
2073
|
-
|
|
2098
|
+
if (!connected) {
|
|
2099
|
+
state.disconnected.value = true;
|
|
2100
|
+
|
|
2101
|
+
return yield* expiredError();
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
const context = yield* Effect.acquireRelease(
|
|
2105
|
+
withinDeadline(
|
|
2106
|
+
Effect.tryPromise({
|
|
2107
|
+
try: (signal) =>
|
|
2108
|
+
closeLateAcquisition(signal, browser.createContext, (acquired) => acquired.close()),
|
|
2109
|
+
catch: (cause) =>
|
|
2110
|
+
state.disconnected.value || isRemoteClosure(cause)
|
|
2111
|
+
? expiredError()
|
|
2112
|
+
: protocolError("Creating the browser context failed", cause),
|
|
2113
|
+
}),
|
|
2114
|
+
fixedPolicy,
|
|
2115
|
+
startedAt,
|
|
2116
|
+
),
|
|
2117
|
+
(acquired) =>
|
|
2118
|
+
releaseBeforeManaged(
|
|
2119
|
+
closeEntry(acquired.close, "Closing the interactive browser context failed"),
|
|
2120
|
+
),
|
|
2121
|
+
{ interruptible: true },
|
|
2122
|
+
);
|
|
2074
2123
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
Effect.
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2124
|
+
closers.push(closeEntry(context.close, "Closing the interactive browser context failed"));
|
|
2125
|
+
|
|
2126
|
+
const page = yield* Effect.acquireRelease(
|
|
2127
|
+
withinDeadline(
|
|
2128
|
+
Effect.tryPromise({
|
|
2129
|
+
try: (signal) =>
|
|
2130
|
+
closeLateAcquisition(signal, context.newPage, (acquired) => acquired.close()),
|
|
2131
|
+
catch: (cause) =>
|
|
2132
|
+
state.disconnected.value || isRemoteClosure(cause)
|
|
2133
|
+
? expiredError()
|
|
2134
|
+
: protocolError("Creating the browser page failed", cause),
|
|
2135
|
+
}),
|
|
2136
|
+
fixedPolicy,
|
|
2137
|
+
startedAt,
|
|
2138
|
+
),
|
|
2139
|
+
(acquired) =>
|
|
2140
|
+
releaseBeforeManaged(
|
|
2141
|
+
closeEntry(acquired.close, "Closing the interactive browser page failed"),
|
|
2142
|
+
),
|
|
2143
|
+
{ interruptible: true },
|
|
2144
|
+
);
|
|
2094
2145
|
|
|
2095
|
-
|
|
2146
|
+
closers.push(closeEntry(page.close, "Closing the interactive browser page failed"));
|
|
2096
2147
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2148
|
+
yield* withinDeadline(
|
|
2149
|
+
Effect.tryPromise({
|
|
2150
|
+
try: () => page.setBypassServiceWorker(true),
|
|
2151
|
+
catch: (cause) => protocolError("Bypassing browser service workers failed", cause),
|
|
2152
|
+
}),
|
|
2153
|
+
fixedPolicy,
|
|
2154
|
+
startedAt,
|
|
2155
|
+
);
|
|
2105
2156
|
|
|
2106
|
-
|
|
2157
|
+
const requestListener = makeRequestListener(fixedPolicy, state);
|
|
2107
2158
|
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2159
|
+
yield* Effect.acquireRelease(
|
|
2160
|
+
Effect.try({
|
|
2161
|
+
try: () => page.onRequest(requestListener),
|
|
2162
|
+
catch: (cause) =>
|
|
2163
|
+
protocolError("Installing the browser request listener failed", cause),
|
|
2164
|
+
}),
|
|
2165
|
+
() =>
|
|
2166
|
+
releaseBeforeManaged(
|
|
2167
|
+
syncCloseEntry(
|
|
2168
|
+
() => page.offRequest(requestListener),
|
|
2169
|
+
"Removing the browser request policy failed",
|
|
2170
|
+
),
|
|
2171
|
+
),
|
|
2172
|
+
);
|
|
2173
|
+
closers.push(
|
|
2115
2174
|
syncCloseEntry(
|
|
2116
2175
|
() => page.offRequest(requestListener),
|
|
2117
2176
|
"Removing the browser request policy failed",
|
|
2118
2177
|
),
|
|
2119
|
-
)
|
|
2120
|
-
);
|
|
2121
|
-
closers.push(
|
|
2122
|
-
syncCloseEntry(
|
|
2123
|
-
() => page.offRequest(requestListener),
|
|
2124
|
-
"Removing the browser request policy failed",
|
|
2125
|
-
),
|
|
2126
|
-
);
|
|
2178
|
+
);
|
|
2127
2179
|
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2180
|
+
yield* withinDeadline(
|
|
2181
|
+
Effect.tryPromise({
|
|
2182
|
+
try: () => page.setRequestInterception(true),
|
|
2183
|
+
catch: (cause) => protocolError("Installing the browser request policy failed", cause),
|
|
2184
|
+
}),
|
|
2185
|
+
fixedPolicy,
|
|
2186
|
+
startedAt,
|
|
2187
|
+
);
|
|
2136
2188
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2189
|
+
yield* withinDeadline(awaitPendingRequests(state), fixedPolicy, startedAt);
|
|
2190
|
+
const setupFailure = stateFailure(state);
|
|
2139
2191
|
|
|
2140
|
-
|
|
2192
|
+
if (setupFailure !== undefined) return yield* setupFailure;
|
|
2141
2193
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2194
|
+
const teardown = yield* Effect.uninterruptible(
|
|
2195
|
+
Effect.gen(function* () {
|
|
2196
|
+
const cached = yield* Effect.cached(terminate(Redacted.make(sessionIdValue), closers));
|
|
2197
|
+
|
|
2198
|
+
lifecycle.managedTeardownInstalled = true;
|
|
2199
|
+
yield* Effect.addFinalizer(() =>
|
|
2200
|
+
Effect.uninterruptible(
|
|
2201
|
+
Effect.sync(() => {
|
|
2202
|
+
state.closed.value = true;
|
|
2203
|
+
state.disconnected.value = true;
|
|
2204
|
+
}).pipe(
|
|
2205
|
+
Effect.andThen(cached),
|
|
2206
|
+
Effect.catch(() =>
|
|
2207
|
+
Effect.logWarning("Whole-browser cleanup remains unconfirmed"),
|
|
2208
|
+
),
|
|
2209
|
+
),
|
|
2210
|
+
),
|
|
2211
|
+
);
|
|
2212
|
+
|
|
2213
|
+
return cached;
|
|
2214
|
+
}),
|
|
2157
2215
|
);
|
|
2158
2216
|
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2217
|
+
const close: Effect.Effect<void, InteractiveBrowserError> = Effect.uninterruptible(
|
|
2218
|
+
Effect.sync(() => {
|
|
2219
|
+
state.closed.value = true;
|
|
2220
|
+
state.disconnected.value = true;
|
|
2221
|
+
}).pipe(Effect.andThen(teardown)),
|
|
2222
|
+
);
|
|
2162
2223
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
state.closed.value = true;
|
|
2166
|
-
state.disconnected.value = true;
|
|
2167
|
-
}).pipe(Effect.andThen(teardown)),
|
|
2168
|
-
);
|
|
2224
|
+
const runtime = yield* makeHandle(page, fixedPolicy, startedAt, state, close);
|
|
2225
|
+
const currentPagePreflight = decodeActionResult(page, fixedPolicy).pipe(Effect.asVoid);
|
|
2169
2226
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2227
|
+
const requestFitsSession = (requestedMillis: number): Effect.Effect<void, BrowserFailure> =>
|
|
2228
|
+
remainingMillis(fixedPolicy, startedAt).pipe(
|
|
2229
|
+
Effect.flatMap((remaining) =>
|
|
2230
|
+
remaining > 0 && requestedMillis <= remaining
|
|
2231
|
+
? Effect.void
|
|
2232
|
+
: Effect.fail(
|
|
2233
|
+
policyError("The host browser request exceeds the remaining session time"),
|
|
2234
|
+
),
|
|
2235
|
+
),
|
|
2236
|
+
);
|
|
2172
2237
|
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2238
|
+
return {
|
|
2239
|
+
handle: runtime.handle,
|
|
2240
|
+
sessionId: Redacted.make(sessionIdValue),
|
|
2241
|
+
resizeViewport: (viewport) =>
|
|
2242
|
+
decodeViewport(viewport).pipe(
|
|
2243
|
+
Effect.flatMap((decoded) =>
|
|
2244
|
+
runtime.run(
|
|
2245
|
+
Effect.tryPromise({
|
|
2246
|
+
try: () => page.setViewport(decoded),
|
|
2247
|
+
catch: (cause) => {
|
|
2248
|
+
if (state.disconnected.value || isRemoteClosure(cause)) {
|
|
2249
|
+
state.disconnected.value = true;
|
|
2250
|
+
|
|
2251
|
+
return expiredError();
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
return protocolError("Resizing the browser viewport failed", cause);
|
|
2255
|
+
},
|
|
2256
|
+
}),
|
|
2257
|
+
currentPagePreflight,
|
|
2258
|
+
false,
|
|
2259
|
+
),
|
|
2180
2260
|
),
|
|
2181
|
-
),
|
|
2182
|
-
);
|
|
2183
|
-
|
|
2184
|
-
return {
|
|
2185
|
-
handle: runtime.handle,
|
|
2186
|
-
sessionId: Redacted.make(sessionIdValue),
|
|
2187
|
-
resizeViewport: (viewport) =>
|
|
2188
|
-
decodeViewport(viewport).pipe(
|
|
2189
|
-
Effect.flatMap((decoded) =>
|
|
2190
|
-
runtime.run(
|
|
2191
|
-
Effect.tryPromise({
|
|
2192
|
-
try: () => page.setViewport(decoded),
|
|
2193
|
-
catch: (cause) => {
|
|
2194
|
-
if (state.disconnected.value || isRemoteClosure(cause)) {
|
|
2195
|
-
state.disconnected.value = true;
|
|
2196
|
-
|
|
2197
|
-
return expiredError();
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
return protocolError("Resizing the browser viewport failed", cause);
|
|
2201
|
-
},
|
|
2202
|
-
}),
|
|
2203
|
-
currentPagePreflight,
|
|
2204
|
-
false,
|
|
2205
2261
|
),
|
|
2206
|
-
)
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2262
|
+
getLiveView: (request) =>
|
|
2263
|
+
Schema.decodeEffect(BrowserRunLiveViewRequest)(request).pipe(
|
|
2264
|
+
Effect.mapError(() => policyError("The Live View request is malformed")),
|
|
2265
|
+
Effect.flatMap((decoded) =>
|
|
2266
|
+
runtime.run(
|
|
2267
|
+
cdpCommand(
|
|
2268
|
+
page,
|
|
2269
|
+
state,
|
|
2270
|
+
"Cloudflare.getLiveView",
|
|
2271
|
+
{ mode: decoded.mode, expiresInMs: decoded.expiresInMs },
|
|
2272
|
+
LiveViewObservation,
|
|
2273
|
+
"Cloudflare returned a malformed Live View response",
|
|
2274
|
+
).pipe(
|
|
2275
|
+
Effect.flatMap((observation) =>
|
|
2276
|
+
Schema.decodeEffect(BrowserRunLiveViewResult)({
|
|
2277
|
+
devtoolsFrontendUrl: Redacted.make(observation.devtoolsFrontendUrl),
|
|
2278
|
+
}).pipe(
|
|
2279
|
+
Effect.mapError(() =>
|
|
2280
|
+
protocolError("Cloudflare returned a malformed Live View response"),
|
|
2281
|
+
),
|
|
2282
|
+
),
|
|
2227
2283
|
),
|
|
2228
2284
|
),
|
|
2285
|
+
currentPagePreflight.pipe(
|
|
2286
|
+
Effect.andThen(requestFitsSession(decoded.expiresInMs)),
|
|
2287
|
+
),
|
|
2229
2288
|
),
|
|
2230
2289
|
),
|
|
2231
|
-
currentPagePreflight.pipe(Effect.andThen(requestFitsSession(decoded.expiresInMs))),
|
|
2232
2290
|
),
|
|
2233
|
-
)
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2291
|
+
handoff: (request) =>
|
|
2292
|
+
Schema.decodeEffect(BrowserRunHandoffRequest)(request).pipe(
|
|
2293
|
+
Effect.mapError(() => policyError("The browser handoff request is malformed")),
|
|
2294
|
+
Effect.flatMap((decoded) =>
|
|
2295
|
+
runtime.run(
|
|
2296
|
+
cdpCommand(
|
|
2297
|
+
page,
|
|
2298
|
+
state,
|
|
2299
|
+
"Cloudflare.handoff",
|
|
2300
|
+
{ instructions: decoded.instructions, timeout: decoded.timeout },
|
|
2301
|
+
HandoffObservation,
|
|
2302
|
+
"Cloudflare returned a malformed browser handoff response",
|
|
2303
|
+
).pipe(
|
|
2304
|
+
Effect.flatMap((observation) =>
|
|
2305
|
+
Schema.decodeEffect(BrowserRunHandoffResult)({
|
|
2306
|
+
handoffId: Redacted.make(observation.handoffId),
|
|
2307
|
+
}).pipe(
|
|
2308
|
+
Effect.mapError(() =>
|
|
2309
|
+
protocolError("Cloudflare returned a malformed browser handoff response"),
|
|
2310
|
+
),
|
|
2311
|
+
),
|
|
2254
2312
|
),
|
|
2255
2313
|
),
|
|
2314
|
+
currentPagePreflight.pipe(Effect.andThen(requestFitsSession(decoded.timeout))),
|
|
2256
2315
|
),
|
|
2257
2316
|
),
|
|
2258
|
-
currentPagePreflight.pipe(Effect.andThen(requestFitsSession(decoded.timeout))),
|
|
2259
2317
|
),
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2318
|
+
getHandoffState: runtime.run(
|
|
2319
|
+
cdpCommand(
|
|
2320
|
+
page,
|
|
2321
|
+
state,
|
|
2322
|
+
"Cloudflare.getHandoffState",
|
|
2323
|
+
{},
|
|
2324
|
+
HandoffStateObservation,
|
|
2325
|
+
"Cloudflare returned a malformed browser handoff state",
|
|
2326
|
+
).pipe(
|
|
2327
|
+
Effect.flatMap((observation) =>
|
|
2328
|
+
Schema.decodeEffect(BrowserRunHandoffState)({
|
|
2329
|
+
active: observation.active,
|
|
2330
|
+
...(observation.handoffId === undefined
|
|
2331
|
+
? {}
|
|
2332
|
+
: { handoffId: Redacted.make(observation.handoffId) }),
|
|
2333
|
+
...(observation.durationMs === undefined
|
|
2334
|
+
? {}
|
|
2335
|
+
: { durationMs: observation.durationMs }),
|
|
2336
|
+
}).pipe(
|
|
2337
|
+
Effect.mapError(() =>
|
|
2338
|
+
protocolError("Cloudflare returned a malformed browser handoff state"),
|
|
2339
|
+
),
|
|
2340
|
+
),
|
|
2283
2341
|
),
|
|
2284
2342
|
),
|
|
2343
|
+
currentPagePreflight,
|
|
2285
2344
|
),
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
),
|
|
2289
|
-
|
|
2345
|
+
close,
|
|
2346
|
+
};
|
|
2347
|
+
}).pipe(Effect.provideService(Scope.Scope, scope)),
|
|
2348
|
+
);
|
|
2349
|
+
|
|
2350
|
+
return {
|
|
2351
|
+
sessionId: Redacted.make(sessionIdValue),
|
|
2352
|
+
connect: Effect.suspend(() => (state.closed.value ? expiredError() : connect)),
|
|
2290
2353
|
};
|
|
2291
2354
|
});
|
|
2292
2355
|
|
|
2293
2356
|
return BrowserRunInteractiveHost.of({
|
|
2294
|
-
|
|
2357
|
+
acquire,
|
|
2358
|
+
open: (policy) => acquire(policy).pipe(Effect.flatMap((acquired) => acquired.connect)),
|
|
2295
2359
|
closeSession,
|
|
2296
2360
|
cleanupSemantics: "confirmed-terminal",
|
|
2297
2361
|
});
|