@effect-agent/platform-cloudflare 0.1.0-beta.37 → 0.1.0-beta.39
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/bindings-C00ZfjsX.d.mts +100 -0
- package/dist/index.d.mts +191 -206
- package/dist/index.mjs +157 -163
- package/dist/index.mjs.map +1 -1
- package/dist/interactive-browser.d.mts +23 -3
- package/dist/interactive-browser.mjs +110 -35
- package/dist/interactive-browser.mjs.map +1 -1
- package/dist/{scheduling-B-OFqoS9.mjs → prepared-admission-D5hQ-ofV.mjs} +121 -432
- package/dist/prepared-admission-D5hQ-ofV.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/scheduling.d.mts +47 -2
- package/dist/scheduling.mjs +339 -1
- package/dist/scheduling.mjs.map +1 -0
- package/dist/subscriptions.d.mts +52 -0
- package/dist/subscriptions.mjs +346 -0
- package/dist/subscriptions.mjs.map +1 -0
- package/package.json +18 -11
- package/src/alarm.ts +264 -265
- package/src/bindings.ts +30 -30
- package/src/browser-session-lifecycle.ts +142 -0
- package/src/client.ts +90 -98
- package/src/config.ts +7 -7
- package/src/index.ts +9 -11
- package/src/interactive-browser.ts +81 -70
- package/src/layers.ts +178 -257
- package/src/prepared-admission.ts +90 -0
- package/src/scheduling.ts +19 -55
- package/src/subscriptions.ts +661 -0
- package/src/{conversation-object.ts → thread-object.ts} +167 -136
- package/src/transport.ts +15 -15
- package/src/wake-scheduler.ts +17 -22
- package/dist/scheduling-B-OFqoS9.mjs.map +0 -1
- package/dist/scheduling-BJs_kHTx.d.mts +0 -142
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Redacted, Schema, Scope } from "effect";
|
|
2
2
|
import { BrowserHandle, InteractiveBrowser, InteractiveBrowserError, InteractiveBrowserPolicy, InteractiveBrowserPolicyDeniedError, SandboxImplementation } from "@effect-agent/sandbox";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
//#region src/browser-session-lifecycle.d.ts
|
|
5
|
+
declare const BrowserRunCleanupError_base: Schema.Class<BrowserRunCleanupError, Schema.TaggedStruct<"BrowserRunCleanupError", {
|
|
6
|
+
readonly reason: Schema.Literals<readonly ["configuration", "authorization", "rate-limited", "provider", "malformed", "timeout", "pending"]>;
|
|
7
|
+
readonly status: Schema.optionalKey<Schema.Int>;
|
|
8
|
+
}>, import("effect/Cause").YieldableError>;
|
|
9
|
+
declare class BrowserRunCleanupError extends BrowserRunCleanupError_base {}
|
|
10
|
+
interface BrowserRunLifecycleOptions {
|
|
11
|
+
readonly accountId: string;
|
|
12
|
+
readonly apiToken: Redacted.Redacted<string>;
|
|
13
|
+
}
|
|
14
|
+
declare const BrowserRunSessionLifecycle_base: Context.ServiceClass<BrowserRunSessionLifecycle, "@effect-agent/platform-cloudflare/BrowserRunSessionLifecycle", {
|
|
15
|
+
readonly close: (sessionId: Redacted.Redacted<string>) => Effect.Effect<void, BrowserRunCleanupError>;
|
|
16
|
+
}>;
|
|
17
|
+
declare class BrowserRunSessionLifecycle extends BrowserRunSessionLifecycle_base {
|
|
18
|
+
static layer(options: BrowserRunLifecycleOptions): Layer.Layer<BrowserRunSessionLifecycle, BrowserRunCleanupError, HttpClient.HttpClient>;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
3
21
|
//#region src/interactive-browser.d.ts
|
|
4
22
|
declare const browserRunInteractiveImplementation: SandboxImplementation;
|
|
5
23
|
declare const BrowserRunViewport_base: Schema.Class<BrowserRunViewport, Schema.Struct<{
|
|
@@ -83,14 +101,15 @@ interface BrowserRunInteractiveBrowser {
|
|
|
83
101
|
}
|
|
84
102
|
declare const BrowserRunInteractiveBinding_base: Context.ServiceClass<BrowserRunInteractiveBinding, "@effect-agent/platform-cloudflare/BrowserRunInteractiveBinding", {
|
|
85
103
|
readonly launch: (keepAliveMillis: number) => Promise<BrowserRunInteractiveBrowser>;
|
|
86
|
-
|
|
104
|
+
/** Success proves whole-browser termination or exact-session absence. */
|
|
105
|
+
readonly closeSession: (sessionId: Redacted.Redacted<string>) => Effect.Effect<void, InteractiveBrowserError>;
|
|
87
106
|
}>;
|
|
88
107
|
/** Host-supplied Browser Run binding projected into one fakeable launch operation. */
|
|
89
108
|
declare class BrowserRunInteractiveBinding extends BrowserRunInteractiveBinding_base {
|
|
90
109
|
static layer(options: {
|
|
91
110
|
readonly browser: BrowserRun;
|
|
92
111
|
readonly viewport?: BrowserRunViewport;
|
|
93
|
-
}): Layer.Layer<BrowserRunInteractiveBinding, InteractiveBrowserPolicyDeniedError>;
|
|
112
|
+
}): Layer.Layer<BrowserRunInteractiveBinding, InteractiveBrowserPolicyDeniedError, BrowserRunSessionLifecycle>;
|
|
94
113
|
}
|
|
95
114
|
interface BrowserRunInteractiveSession {
|
|
96
115
|
readonly handle: BrowserHandle;
|
|
@@ -108,6 +127,7 @@ interface BrowserRunInteractiveSession {
|
|
|
108
127
|
readonly close: Effect.Effect<void, InteractiveBrowserError>;
|
|
109
128
|
}
|
|
110
129
|
declare const BrowserRunInteractiveHost_base: Context.ServiceClass<BrowserRunInteractiveHost, "@effect-agent/platform-cloudflare/BrowserRunInteractiveHost", {
|
|
130
|
+
readonly cleanupSemantics?: "confirmed-terminal";
|
|
111
131
|
readonly open: (policy: InteractiveBrowserPolicy) => Effect.Effect<BrowserRunInteractiveSession, InteractiveBrowserError, Scope.Scope>;
|
|
112
132
|
readonly closeSession: (sessionId: Redacted.Redacted<string>) => Effect.Effect<void, InteractiveBrowserError>;
|
|
113
133
|
}>;
|
|
@@ -120,5 +140,5 @@ declare const browserRunInteractiveHostLayer: () => Layer.Layer<BrowserRunIntera
|
|
|
120
140
|
/** Worker-only generic adapter; Cloudflare identity and controls remain host-only. */
|
|
121
141
|
declare const browserRunInteractiveLayer: () => Layer.Layer<InteractiveBrowser, never, BrowserRunInteractiveBinding>;
|
|
122
142
|
//#endregion
|
|
123
|
-
export { BrowserRunCloudflareCommand, BrowserRunHandoffRequest, BrowserRunHandoffResult, BrowserRunHandoffState, BrowserRunInteractiveBinding, BrowserRunInteractiveBrowser, BrowserRunInteractiveCdpSession, BrowserRunInteractiveContext, BrowserRunInteractiveHost, BrowserRunInteractivePage, BrowserRunInteractiveRequest, BrowserRunInteractiveRequestListener, BrowserRunInteractiveSession, BrowserRunLiveViewRequest, BrowserRunLiveViewResult, BrowserRunViewport, browserRunInteractiveHostLayer, browserRunInteractiveImplementation, browserRunInteractiveLayer, isBrowserRunUndispatchedActionError };
|
|
143
|
+
export { BrowserRunCleanupError, BrowserRunCloudflareCommand, BrowserRunHandoffRequest, BrowserRunHandoffResult, BrowserRunHandoffState, BrowserRunInteractiveBinding, BrowserRunInteractiveBrowser, BrowserRunInteractiveCdpSession, BrowserRunInteractiveContext, BrowserRunInteractiveHost, BrowserRunInteractivePage, BrowserRunInteractiveRequest, BrowserRunInteractiveRequestListener, BrowserRunInteractiveSession, BrowserRunLiveViewRequest, BrowserRunLiveViewResult, BrowserRunSessionLifecycle, BrowserRunViewport, browserRunInteractiveHostLayer, browserRunInteractiveImplementation, browserRunInteractiveLayer, isBrowserRunUndispatchedActionError };
|
|
124
144
|
//# sourceMappingURL=interactive-browser.d.mts.map
|
|
@@ -1,6 +1,86 @@
|
|
|
1
|
-
import { Context, Duration, Effect, Layer, Option, Redacted, Ref, Schema, Semaphore } from "effect";
|
|
1
|
+
import { Clock, Context, Duration, Effect, Layer, Option, Redacted, Ref, Schema, Semaphore, Stream } from "effect";
|
|
2
2
|
import { BrowserActionResult, BrowserNavigationResult, BrowserScreenshotRequest, BrowserScrollRequest, BrowserTextResult, InteractiveBrowser, InteractiveBrowserActionError, InteractiveBrowserBusyError, InteractiveBrowserCapacityError, InteractiveBrowserExpiredError, InteractiveBrowserLimitError, InteractiveBrowserPolicy, InteractiveBrowserPolicyDeniedError, InteractiveBrowserProtocolError, InteractiveBrowserTargetUrl, InteractiveBrowserUnsupportedError, PageScreenshotResult, SandboxImplementation } from "@effect-agent/sandbox";
|
|
3
3
|
import puppeteer from "@cloudflare/puppeteer";
|
|
4
|
+
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
5
|
+
//#region src/browser-session-lifecycle.ts
|
|
6
|
+
var BrowserRunCleanupError = class extends Schema.TaggedError()("BrowserRunCleanupError", {
|
|
7
|
+
reason: Schema.Literals([
|
|
8
|
+
"configuration",
|
|
9
|
+
"authorization",
|
|
10
|
+
"rate-limited",
|
|
11
|
+
"provider",
|
|
12
|
+
"malformed",
|
|
13
|
+
"timeout",
|
|
14
|
+
"pending"
|
|
15
|
+
]),
|
|
16
|
+
status: Schema.optionalKey(Schema.Int)
|
|
17
|
+
}) {};
|
|
18
|
+
const Identity = Schema.String.check(Schema.isUUID());
|
|
19
|
+
const Account = Schema.String.check(Schema.isPattern(/^[a-f0-9]{32}$/));
|
|
20
|
+
const Closed = Schema.Struct({ status: Schema.Literals(["closing", "closed"]) });
|
|
21
|
+
const Metadata = Schema.Struct({
|
|
22
|
+
sessionId: Identity,
|
|
23
|
+
endTime: Schema.optionalKey(Schema.Finite)
|
|
24
|
+
});
|
|
25
|
+
const Absent = Schema.Struct({ error: Schema.Literal("Session not found") });
|
|
26
|
+
var BrowserRunSessionLifecycle = class extends Context.Service()("@effect-agent/platform-cloudflare/BrowserRunSessionLifecycle") {
|
|
27
|
+
static layer(options) {
|
|
28
|
+
return Layer.effect(this, Effect.gen(function* () {
|
|
29
|
+
if (!Schema.is(Account)(options.accountId) || Redacted.value(options.apiToken).length === 0) return yield* new BrowserRunCleanupError({ reason: "configuration" });
|
|
30
|
+
const client = yield* HttpClient.HttpClient;
|
|
31
|
+
const request = Effect.fn("BrowserRunSessionLifecycle.request")(function* (method, sessionId) {
|
|
32
|
+
const path = method === "DELETE" ? "browser" : "session";
|
|
33
|
+
const response = yield* client.execute(HttpClientRequest.make(method)(`https://api.cloudflare.com/client/v4/accounts/${options.accountId}/browser-rendering/devtools/${path}/${sessionId}`).pipe(HttpClientRequest.bearerToken(options.apiToken))).pipe(Effect.provideService(FetchHttpClient.RequestInit, { redirect: "manual" }), Effect.mapError(() => new BrowserRunCleanupError({ reason: "provider" })));
|
|
34
|
+
if (response.status === 401 || response.status === 403) return yield* new BrowserRunCleanupError({
|
|
35
|
+
reason: "authorization",
|
|
36
|
+
status: response.status
|
|
37
|
+
});
|
|
38
|
+
if (response.status === 429) return yield* new BrowserRunCleanupError({
|
|
39
|
+
reason: "rate-limited",
|
|
40
|
+
status: response.status
|
|
41
|
+
});
|
|
42
|
+
if (response.status !== 200 && response.status !== 404) return yield* new BrowserRunCleanupError({
|
|
43
|
+
reason: "provider",
|
|
44
|
+
status: response.status
|
|
45
|
+
});
|
|
46
|
+
if (response.headers["content-type"]?.split(";", 1)[0]?.trim() !== "application/json") return yield* new BrowserRunCleanupError({ reason: "malformed" });
|
|
47
|
+
const bytes = yield* Stream.runFoldEffect(response.stream, () => /* @__PURE__ */ new Uint8Array(), (body, chunk) => {
|
|
48
|
+
if (body.byteLength + chunk.byteLength > 16384) return Effect.fail(new BrowserRunCleanupError({ reason: "malformed" }));
|
|
49
|
+
const combined = new Uint8Array(body.byteLength + chunk.byteLength);
|
|
50
|
+
combined.set(body);
|
|
51
|
+
combined.set(chunk, body.byteLength);
|
|
52
|
+
return Effect.succeed(combined);
|
|
53
|
+
}).pipe(Effect.mapError(() => new BrowserRunCleanupError({ reason: "malformed" })));
|
|
54
|
+
const body = yield* Effect.try({
|
|
55
|
+
try: () => new TextDecoder("utf-8", {
|
|
56
|
+
fatal: true,
|
|
57
|
+
ignoreBOM: false
|
|
58
|
+
}).decode(bytes),
|
|
59
|
+
catch: () => new BrowserRunCleanupError({ reason: "malformed" })
|
|
60
|
+
});
|
|
61
|
+
if (response.status === 404) {
|
|
62
|
+
const absent = Schema.decodeUnknownOption(Schema.fromJsonString(Absent))(body, { onExcessProperty: "error" });
|
|
63
|
+
if (Option.isSome(absent)) return true;
|
|
64
|
+
return yield* new BrowserRunCleanupError({ reason: "malformed" });
|
|
65
|
+
}
|
|
66
|
+
if (method === "DELETE") return (yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Closed))(body).pipe(Effect.mapError(() => new BrowserRunCleanupError({ reason: "malformed" })))).status === "closed";
|
|
67
|
+
const result = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Metadata))(body).pipe(Effect.mapError(() => new BrowserRunCleanupError({ reason: "malformed" })));
|
|
68
|
+
if (result.sessionId !== sessionId) return yield* new BrowserRunCleanupError({ reason: "malformed" });
|
|
69
|
+
return result.endTime !== void 0 && result.endTime > 0;
|
|
70
|
+
});
|
|
71
|
+
return { close: Effect.fn("BrowserRunSessionLifecycle.close")(function* (sessionId) {
|
|
72
|
+
const id = yield* Schema.decodeUnknownEffect(Identity)(Redacted.value(sessionId)).pipe(Effect.mapError(() => new BrowserRunCleanupError({ reason: "configuration" })));
|
|
73
|
+
if (yield* request("DELETE", id)) return;
|
|
74
|
+
for (let read = 0; read < 2; read++) if (yield* request("GET", id)) return;
|
|
75
|
+
return yield* new BrowserRunCleanupError({ reason: "pending" });
|
|
76
|
+
}, Effect.timeoutOrElse({
|
|
77
|
+
duration: "10 seconds",
|
|
78
|
+
orElse: () => Effect.fail(new BrowserRunCleanupError({ reason: "timeout" }))
|
|
79
|
+
}), Effect.withTracerEnabled(false)) };
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
//#endregion
|
|
4
84
|
//#region src/interactive-browser.ts
|
|
5
85
|
const browserRunInteractiveImplementation = SandboxImplementation.make({
|
|
6
86
|
isolation: "isolated",
|
|
@@ -15,7 +95,6 @@ const MAX_LIVE_VIEW_EXPIRY_MILLIS = 60 * 6e4;
|
|
|
15
95
|
const MAX_HANDOFF_TIMEOUT_MILLIS = 30 * 6e4;
|
|
16
96
|
const MAX_HOST_TEXT_LENGTH = 8 * 1024;
|
|
17
97
|
const CLEANUP_STEP_TIMEOUT_MILLIS = 1e4;
|
|
18
|
-
const CLOSE_SESSION_TIMEOUT_MILLIS = 1e4;
|
|
19
98
|
const ACTION_NETWORK_QUIET_MILLIS = 200;
|
|
20
99
|
const ACTION_NETWORK_SETTLE_MILLIS = 2e3;
|
|
21
100
|
const ACTION_POST_STATE_MILLIS = 250;
|
|
@@ -149,13 +228,14 @@ var BrowserRunHandoffState = class extends Schema.Class("BrowserRunHandoffState"
|
|
|
149
228
|
var BrowserRunInteractiveBinding = class BrowserRunInteractiveBinding extends Context.Service()("@effect-agent/platform-cloudflare/BrowserRunInteractiveBinding") {
|
|
150
229
|
static layer(options) {
|
|
151
230
|
return Layer.effect(BrowserRunInteractiveBinding)(Effect.gen(function* () {
|
|
231
|
+
const lifecycle = yield* BrowserRunSessionLifecycle;
|
|
152
232
|
const viewport = options.viewport === void 0 ? void 0 : yield* decodeViewport(options.viewport);
|
|
153
233
|
return {
|
|
154
234
|
launch: async (keepAliveMillis) => makeProductionBrowser(await puppeteer.launch(options.browser, {
|
|
155
235
|
keep_alive: keepAliveMillis,
|
|
156
236
|
...viewport === void 0 ? {} : { defaultViewport: { ...viewport } }
|
|
157
237
|
})),
|
|
158
|
-
|
|
238
|
+
closeSession: (sessionId) => lifecycle.close(sessionId).pipe(Effect.mapError((cause) => actionError("close", cause)))
|
|
159
239
|
};
|
|
160
240
|
}));
|
|
161
241
|
}
|
|
@@ -919,6 +999,24 @@ const cdpCommand = (page, state, command, parameters, output, malformedMessage)
|
|
|
919
999
|
return yield* Schema.decodeUnknownEffect(output)(raw).pipe(Effect.mapError(() => protocolError(malformedMessage)));
|
|
920
1000
|
}));
|
|
921
1001
|
const makeHostService = (binding) => {
|
|
1002
|
+
const closeSession = binding.closeSession;
|
|
1003
|
+
const terminate = Effect.fn("BrowserRunInteractiveHost.terminate")(function* (sessionId, entries) {
|
|
1004
|
+
const deadline = (yield* Clock.currentTimeMillis) + 1e4;
|
|
1005
|
+
yield* closeSession(sessionId).pipe(Effect.interruptible, Effect.timeoutOrElse({
|
|
1006
|
+
duration: "10 seconds",
|
|
1007
|
+
orElse: () => Effect.fail(actionError("close"))
|
|
1008
|
+
}));
|
|
1009
|
+
const remaining = deadline - (yield* Clock.currentTimeMillis);
|
|
1010
|
+
if (remaining <= 0) return yield* Effect.logWarning("Local browser teardown skipped after confirmed termination deadline");
|
|
1011
|
+
yield* runTeardown(entries).pipe(Effect.flatMap((failures) => Effect.forEach(failures, (failure) => Effect.logWarning(failure.warning), { discard: true })), Effect.interruptible, Effect.timeout(`${remaining} millis`), Effect.catchCause(() => Effect.logWarning("Local browser teardown incomplete after confirmed termination")));
|
|
1012
|
+
});
|
|
1013
|
+
const closeAcquired = Effect.fn("BrowserRunInteractiveHost.closeAcquired")(function* (browser) {
|
|
1014
|
+
const sessionId = yield* Effect.try({
|
|
1015
|
+
try: browser.sessionId,
|
|
1016
|
+
catch: () => actionError("close")
|
|
1017
|
+
}).pipe(Effect.flatMap(Schema.decodeUnknownEffect(BrowserRunSessionId)), Effect.mapError(() => actionError("close")), Effect.onError(() => closeWithWarning(browser.close, "Closing an unidentified browser failed")));
|
|
1018
|
+
yield* terminate(Redacted.make(sessionId), [closeEntry(browser.close, "Closing the local browser connection failed")]);
|
|
1019
|
+
});
|
|
922
1020
|
const open = Effect.fn("BrowserRunInteractiveHost.open")(function* (policy) {
|
|
923
1021
|
const fixedPolicy = yield* snapshotPolicy(policy);
|
|
924
1022
|
const startedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
|
|
@@ -929,21 +1027,18 @@ const makeHostService = (binding) => {
|
|
|
929
1027
|
violation: { value: void 0 },
|
|
930
1028
|
pendingRequests: /* @__PURE__ */ new Set()
|
|
931
1029
|
};
|
|
932
|
-
const lifecycle = {
|
|
933
|
-
managedTeardownInstalled: false,
|
|
934
|
-
explicitCloseInvoked: false
|
|
935
|
-
};
|
|
1030
|
+
const lifecycle = { managedTeardownInstalled: false };
|
|
936
1031
|
const closers = [];
|
|
937
1032
|
const releaseBeforeManaged = (entry) => Effect.suspend(() => lifecycle.managedTeardownInstalled ? Effect.void : entry.close.pipe(Effect.catchCause(() => Effect.logWarning(entry.warning))));
|
|
938
1033
|
const browser = yield* Effect.acquireRelease(withinDeadline(Effect.tryPromise({
|
|
939
|
-
try: (signal) => closeLateAcquisition(signal, () => binding.launch(keepAliveMillis(fixedPolicy)), (acquired) =>
|
|
1034
|
+
try: (signal) => closeLateAcquisition(signal, () => binding.launch(keepAliveMillis(fixedPolicy)), (acquired) => Effect.runPromise(closeAcquired(acquired))),
|
|
940
1035
|
catch: (cause) => isCapacityRefusal(cause) ? InteractiveBrowserCapacityError.make({
|
|
941
1036
|
implementation: browserRunInteractiveImplementation,
|
|
942
1037
|
message: "Browser Run has no capacity for a new browser session"
|
|
943
1038
|
}) : protocolError("Launching the Browser Run session failed", cause)
|
|
944
1039
|
}), fixedPolicy, startedAt), (acquired) => {
|
|
945
1040
|
state.disconnected.value = true;
|
|
946
|
-
return
|
|
1041
|
+
return lifecycle.managedTeardownInstalled ? Effect.void : closeAcquired(acquired).pipe(Effect.catch(() => Effect.logWarning("Whole-browser cleanup remains unconfirmed")));
|
|
947
1042
|
}, { interruptible: true });
|
|
948
1043
|
closers.push(closeEntry(browser.close, "Closing the interactive browser failed"));
|
|
949
1044
|
const disconnected = () => {
|
|
@@ -993,19 +1088,18 @@ const makeHostService = (binding) => {
|
|
|
993
1088
|
const setupFailure = stateFailure(state);
|
|
994
1089
|
if (setupFailure !== void 0) return yield* setupFailure;
|
|
995
1090
|
const teardown = yield* Effect.uninterruptible(Effect.gen(function* () {
|
|
996
|
-
const cached = yield* Effect.cached(
|
|
1091
|
+
const cached = yield* Effect.cached(terminate(Redacted.make(sessionIdValue), closers));
|
|
997
1092
|
lifecycle.managedTeardownInstalled = true;
|
|
998
1093
|
yield* Effect.addFinalizer(() => Effect.uninterruptible(Effect.sync(() => {
|
|
999
1094
|
state.closed.value = true;
|
|
1000
1095
|
state.disconnected.value = true;
|
|
1001
|
-
}).pipe(Effect.andThen(cached), Effect.
|
|
1096
|
+
}).pipe(Effect.andThen(cached), Effect.catch(() => Effect.logWarning("Whole-browser cleanup remains unconfirmed")))));
|
|
1002
1097
|
return cached;
|
|
1003
1098
|
}));
|
|
1004
1099
|
const close = Effect.uninterruptible(Effect.sync(() => {
|
|
1005
|
-
lifecycle.explicitCloseInvoked = true;
|
|
1006
1100
|
state.closed.value = true;
|
|
1007
1101
|
state.disconnected.value = true;
|
|
1008
|
-
}).pipe(Effect.andThen(teardown)
|
|
1102
|
+
}).pipe(Effect.andThen(teardown)));
|
|
1009
1103
|
const runtime = yield* makeHandle(page, fixedPolicy, startedAt, state, close);
|
|
1010
1104
|
const currentPagePreflight = decodeActionResult(page, fixedPolicy).pipe(Effect.asVoid);
|
|
1011
1105
|
const requestFitsSession = (requestedMillis) => remainingMillis(fixedPolicy, startedAt).pipe(Effect.flatMap((remaining) => remaining > 0 && requestedMillis <= remaining ? Effect.void : Effect.fail(policyError("The host browser request exceeds the remaining session time"))));
|
|
@@ -1038,29 +1132,10 @@ const makeHostService = (binding) => {
|
|
|
1038
1132
|
close
|
|
1039
1133
|
};
|
|
1040
1134
|
});
|
|
1041
|
-
const closeSession = Effect.fn("BrowserRunInteractiveHost.closeSession")(function* (sessionId) {
|
|
1042
|
-
const decoded = yield* Schema.decodeUnknownEffect(Schema.Redacted(BrowserRunSessionId))(sessionId).pipe(Effect.mapError(() => policyError("The Browser Run cleanup session identity is malformed")));
|
|
1043
|
-
return yield* Effect.scoped(Effect.gen(function* () {
|
|
1044
|
-
const closeAttempted = { value: false };
|
|
1045
|
-
const browser = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
1046
|
-
try: (signal) => closeLateAcquisition(signal, () => binding.connect(Redacted.value(decoded)), (acquired) => acquired.close()),
|
|
1047
|
-
catch: (cause) => actionError("close", cause)
|
|
1048
|
-
}), (acquired) => closeAttempted.value ? Effect.void : closeWithWarning(acquired.close, "Closing the leaked Browser Run session failed"), { interruptible: true });
|
|
1049
|
-
return yield* Effect.tryPromise({
|
|
1050
|
-
try: () => {
|
|
1051
|
-
closeAttempted.value = true;
|
|
1052
|
-
return browser.close();
|
|
1053
|
-
},
|
|
1054
|
-
catch: (cause) => actionError("close", cause)
|
|
1055
|
-
});
|
|
1056
|
-
})).pipe(Effect.timeoutOrElse({
|
|
1057
|
-
duration: Duration.millis(CLOSE_SESSION_TIMEOUT_MILLIS),
|
|
1058
|
-
orElse: () => Effect.fail(actionError("close"))
|
|
1059
|
-
}));
|
|
1060
|
-
});
|
|
1061
1135
|
return BrowserRunInteractiveHost.of({
|
|
1062
1136
|
open,
|
|
1063
|
-
closeSession
|
|
1137
|
+
closeSession,
|
|
1138
|
+
cleanupSemantics: "confirmed-terminal"
|
|
1064
1139
|
});
|
|
1065
1140
|
};
|
|
1066
1141
|
/** Cloudflare host controls and private session identity for one scoped Browser Run pass. */
|
|
@@ -1074,6 +1149,6 @@ const browserRunInteractiveLayer = () => Layer.effect(InteractiveBrowser, Effect
|
|
|
1074
1149
|
return InteractiveBrowser.of({ open: (policy) => host.open(policy).pipe(Effect.map((session) => session.handle)) });
|
|
1075
1150
|
}));
|
|
1076
1151
|
//#endregion
|
|
1077
|
-
export { BrowserRunHandoffRequest, BrowserRunHandoffResult, BrowserRunHandoffState, BrowserRunInteractiveBinding, BrowserRunInteractiveHost, BrowserRunLiveViewRequest, BrowserRunLiveViewResult, BrowserRunViewport, browserRunInteractiveHostLayer, browserRunInteractiveImplementation, browserRunInteractiveLayer, isBrowserRunUndispatchedActionError };
|
|
1152
|
+
export { BrowserRunCleanupError, BrowserRunHandoffRequest, BrowserRunHandoffResult, BrowserRunHandoffState, BrowserRunInteractiveBinding, BrowserRunInteractiveHost, BrowserRunLiveViewRequest, BrowserRunLiveViewResult, BrowserRunSessionLifecycle, BrowserRunViewport, browserRunInteractiveHostLayer, browserRunInteractiveImplementation, browserRunInteractiveLayer, isBrowserRunUndispatchedActionError };
|
|
1078
1153
|
|
|
1079
1154
|
//# sourceMappingURL=interactive-browser.mjs.map
|