@effect-agent/platform-cloudflare 0.1.0-beta.83 → 0.1.0-beta.85
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/Alarm.mjs +1 -1
- package/dist/Alarm.mjs.map +1 -1
- package/dist/BrowserRestCrawl.mjs +1 -1
- package/dist/BrowserRestCrawl.mjs.map +1 -1
- package/dist/CloudflareBindings.d.mts +6 -3
- package/dist/CloudflareBindings.mjs +14 -2
- package/dist/CloudflareBindings.mjs.map +1 -1
- package/dist/CloudflareMemory.d.mts +1 -1
- package/dist/CloudflareMemory.mjs +16 -9
- package/dist/CloudflareMemory.mjs.map +1 -1
- package/dist/CloudflareScheduling.mjs +15 -8
- package/dist/CloudflareScheduling.mjs.map +1 -1
- package/dist/CloudflareSubscriptions.mjs +12 -6
- package/dist/CloudflareSubscriptions.mjs.map +1 -1
- package/dist/CloudflareThreadClient.d.mts +256 -46
- package/dist/CloudflareThreadClient.mjs +11 -15
- package/dist/CloudflareThreadClient.mjs.map +1 -1
- package/dist/InteractiveBrowser.mjs +15 -15
- package/dist/InteractiveBrowser.mjs.map +1 -1
- package/dist/ProtectedBrowser.mjs +12 -12
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-rVo6_oel.d.mts → ThreadObject-DZMkwIBh.d.mts} +29 -33
- package/dist/{ThreadObject-BpwkEc5E.mjs → ThreadObject-gyx8Qq03.mjs} +8 -9
- package/dist/ThreadObject-gyx8Qq03.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/WakeScheduler.mjs +7 -10
- package/dist/WakeScheduler.mjs.map +1 -1
- package/dist/{browser-session-lifecycle-CUbVfEgP.mjs → browser-session-lifecycle-CmbQABHC.mjs} +5 -5
- package/dist/browser-session-lifecycle-CmbQABHC.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +6 -6
- package/src/BrowserRestCrawl.ts +1 -1
- package/src/CloudflareBindings.ts +20 -1
- package/src/CloudflareMemory.ts +25 -11
- package/src/CloudflareScheduling.ts +23 -9
- package/src/CloudflareSubscriptions.ts +18 -18
- package/src/CloudflareThreadClient.ts +14 -12
- package/src/InteractiveBrowser.ts +19 -22
- package/src/WakeScheduler.ts +12 -6
- package/src/internal/browser-session-lifecycle.ts +8 -8
- package/src/internal/message-delivery.ts +5 -6
- package/src/internal/transport.ts +8 -6
- package/src/protected-browser/binding.ts +1 -1
- package/src/protected-browser/policy.ts +29 -31
- package/dist/ThreadObject-BpwkEc5E.mjs.map +0 -1
- package/dist/browser-session-lifecycle-CUbVfEgP.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session-lifecycle-CmbQABHC.mjs","names":[],"sources":["../src/internal/browser-session-lifecycle.ts"],"sourcesContent":["import { Context, Effect, Layer, Option, Redacted, Schema, Stream } from \"effect\";\nimport { FetchHttpClient, HttpClient, HttpClientRequest } from \"effect/unstable/http\";\n\nexport class BrowserRunCleanupError extends Schema.TaggedError<BrowserRunCleanupError>()(\n \"BrowserRunCleanupError\",\n {\n reason: Schema.Literals([\n \"configuration\",\n \"authorization\",\n \"rate-limited\",\n \"provider\",\n \"malformed\",\n \"timeout\",\n \"pending\",\n ]),\n status: Schema.optionalKey(Schema.Int),\n },\n) {}\n\nexport interface BrowserRunLifecycleOptions {\n readonly accountId: string;\n readonly apiToken: Redacted.Redacted<string>;\n}\n\nconst Identity = Schema.String.check(Schema.isUUID());\nconst Account = Schema.String.check(Schema.isPattern(/^[a-f0-9]{32}$/));\nconst Closed = Schema.Struct({ status: Schema.Literals([\"closing\", \"closed\"]) });\nconst Metadata = Schema.Struct({ sessionId: Identity, endTime: Schema.optionalKey(Schema.Finite) });\n// This exact provider response is accepted only on a fixed-origin, authenticated,\n// exact-session request. An arbitrary 404 or a listing omission is never absence.\nconst Absent = Schema.Struct({ error: Schema.Literal(\"Session not found\") });\n\nexport class BrowserRunSessionLifecycle extends Context.Service<\n BrowserRunSessionLifecycle,\n {\n readonly close: (\n sessionId: Redacted.Redacted<string>,\n ) => Effect.Effect<void, BrowserRunCleanupError>;\n }\n>()(\"@effect-agent/platform-cloudflare/BrowserRunSessionLifecycle\") {\n static layer(options: BrowserRunLifecycleOptions) {\n return Layer.effect(\n this,\n Effect.gen(function* () {\n if (\n !Schema.is(Account)(options.accountId) ||\n Redacted.value(options.apiToken).length === 0\n ) {\n return yield* new BrowserRunCleanupError({ reason: \"configuration\" });\n }\n const client = yield* HttpClient.HttpClient;\n\n const request = Effect.fn(\"BrowserRunSessionLifecycle.request\")(function* (\n method: \"GET\" | \"DELETE\",\n sessionId: string,\n ) {\n const path = method === \"DELETE\" ? \"browser\" : \"session\";\n\n const response = yield* client\n .execute(\n HttpClientRequest.make(method)(\n `https://api.cloudflare.com/client/v4/accounts/${options.accountId}/browser-rendering/devtools/${path}/${sessionId}`,\n ).pipe(HttpClientRequest.bearerToken(options.apiToken)),\n )\n .pipe(\n // workerd supports manual/follow, not error. Reject every redirect below.\n Effect.provideService(FetchHttpClient.RequestInit, { redirect: \"manual\" }),\n Effect.mapError(() => new BrowserRunCleanupError({ reason: \"provider\" })),\n );\n\n if (response.status === 401 || response.status === 403)\n return yield* new BrowserRunCleanupError({\n reason: \"authorization\",\n status: response.status,\n });\n if (response.status === 429)\n return yield* new BrowserRunCleanupError({\n reason: \"rate-limited\",\n status: response.status,\n });\n if (response.status !== 200 && response.status !== 404)\n return yield* new BrowserRunCleanupError({\n reason: \"provider\",\n status: response.status,\n });\n if (response.headers[\"content-type\"]?.split(\";\", 1)[0]?.trim() !== \"application/json\")\n return yield* new BrowserRunCleanupError({ reason: \"malformed\" });\n\n const bytes = yield* Stream.runFoldEffect(\n response.stream,\n () => new Uint8Array(),\n (body, chunk) => {\n if (body.byteLength + chunk.byteLength > 16_384)\n return Effect.fail(new BrowserRunCleanupError({ reason: \"malformed\" }));\n const combined = new Uint8Array(body.byteLength + chunk.byteLength);\n\n combined.set(body);\n combined.set(chunk, body.byteLength);\n\n return Effect.succeed(combined);\n },\n ).pipe(Effect.mapError(() => new BrowserRunCleanupError({ reason: \"malformed\" })));\n\n const body = yield* Effect.try({\n try: () => new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false }).decode(bytes),\n catch: () => new BrowserRunCleanupError({ reason: \"malformed\" }),\n });\n\n if (response.status === 404) {\n const absent = Schema.decodeOption(Schema.fromJsonString(Absent))(body, {\n onExcessProperty: \"error\",\n });\n\n if (Option.isSome(absent)) return true;\n\n return yield* new BrowserRunCleanupError({ reason: \"malformed\" });\n }\n if (method === \"DELETE\") {\n const result = yield* Schema.decodeEffect(Schema.fromJsonString(Closed))(body).pipe(\n Effect.mapError(() => new BrowserRunCleanupError({ reason: \"malformed\" })),\n );\n\n return result.status === \"closed\";\n }\n\n const result = yield* Schema.decodeEffect(Schema.fromJsonString(Metadata))(body).pipe(\n Effect.mapError(() => new BrowserRunCleanupError({ reason: \"malformed\" })),\n );\n\n if (result.sessionId !== sessionId)\n return yield* new BrowserRunCleanupError({ reason: \"malformed\" });\n\n return result.endTime !== undefined && result.endTime > 0;\n });\n\n const close = Effect.fn(\"BrowserRunSessionLifecycle.close\")(\n function* (sessionId: Redacted.Redacted<string>) {\n const id = yield* Schema.decodeEffect(Identity)(Redacted.value(sessionId)).pipe(\n Effect.mapError(() => new BrowserRunCleanupError({ reason: \"configuration\" })),\n );\n\n if (yield* request(\"DELETE\", id)) return;\n for (let read = 0; read < 2; read++) {\n if (yield* request(\"GET\", id)) return;\n }\n\n return yield* new BrowserRunCleanupError({ reason: \"pending\" });\n },\n Effect.timeoutOrElse({\n duration: \"10 seconds\",\n orElse: () => Effect.fail(new BrowserRunCleanupError({ reason: \"timeout\" })),\n }),\n Effect.withTracerEnabled(false),\n );\n\n return { close };\n }),\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,6 @@ import { t as CloudflareMemory_d_exports } from "./CloudflareMemory.mjs";
|
|
|
8
8
|
import { t as CloudflareScheduling_d_exports } from "./CloudflareScheduling.mjs";
|
|
9
9
|
import { t as CloudflareSubscriptions_d_exports } from "./CloudflareSubscriptions.mjs";
|
|
10
10
|
import { t as CloudflareThreadClient_d_exports } from "./CloudflareThreadClient.mjs";
|
|
11
|
-
import { f as ThreadObject_d_exports } from "./ThreadObject-
|
|
11
|
+
import { f as ThreadObject_d_exports } from "./ThreadObject-DZMkwIBh.mjs";
|
|
12
12
|
import { t as WakeScheduler_d_exports } from "./WakeScheduler.mjs";
|
|
13
13
|
export { Alarm_d_exports as Alarm, CloudflareAiGateway_d_exports as CloudflareAiGateway, CloudflareBindings_d_exports as CloudflareBindings, CloudflareBrowser_d_exports as CloudflareBrowser, CloudflareCodeMode_d_exports as CloudflareCodeMode, CloudflareConfig_d_exports as CloudflareConfig, CloudflareMemory_d_exports as CloudflareMemory, CloudflareScheduling_d_exports as CloudflareScheduling, CloudflareSubscriptions_d_exports as CloudflareSubscriptions, CloudflareThreadClient_d_exports as CloudflareThreadClient, ThreadObject_d_exports as ThreadObject, WakeScheduler_d_exports as WakeScheduler };
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,6 @@ import { t as CloudflareThreadClient_exports } from "./CloudflareThreadClient.mj
|
|
|
8
8
|
import { t as CloudflareScheduling_exports } from "./CloudflareScheduling.mjs";
|
|
9
9
|
import { t as CloudflareSubscriptions_exports } from "./CloudflareSubscriptions.mjs";
|
|
10
10
|
import { t as WakeScheduler_exports } from "./WakeScheduler.mjs";
|
|
11
|
-
import { l as ThreadObject_exports } from "./ThreadObject-
|
|
11
|
+
import { l as ThreadObject_exports } from "./ThreadObject-gyx8Qq03.mjs";
|
|
12
12
|
import { t as CloudflareAiGateway_exports } from "./CloudflareAiGateway.mjs";
|
|
13
13
|
export { Alarm_exports as Alarm, CloudflareAiGateway_exports as CloudflareAiGateway, CloudflareBindings_exports as CloudflareBindings, CloudflareBrowser_exports as CloudflareBrowser, CloudflareCodeMode_exports as CloudflareCodeMode, CloudflareConfig_exports as CloudflareConfig, CloudflareMemory_exports as CloudflareMemory, CloudflareScheduling_exports as CloudflareScheduling, CloudflareSubscriptions_exports as CloudflareSubscriptions, CloudflareThreadClient_exports as CloudflareThreadClient, ThreadObject_exports as ThreadObject, WakeScheduler_exports as WakeScheduler };
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@effect-agent/platform-cloudflare","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@effect-agent/platform-cloudflare","version":"0.1.0-beta.85","dependencies":{"@effect-agent/core":"0.1.0-beta.85","@effect-agent/engine":"0.1.0-beta.85","@effect-agent/sandbox":"0.1.0-beta.85","@effect-agent/storage-cloudflare":"0.1.0-beta.85","@effect-agent/thread":"0.1.0-beta.85","@effect/platform-browser":"4.0.0-rc.112","@effect/sql-sqlite-do":"4.0.0-rc.112"},"devDependencies":{"@cloudflare/puppeteer":"1.1.0","@cloudflare/vitest-pool-workers":"0.21.3","@cloudflare/workers-types":"5.20260825.1","@effect-agent/capabilities":"0.1.0-beta.85","@effect-agent/testing":"0.1.0-beta.85","@effect/platform-node":"4.0.0-rc.112","@effect/sql-d1":"4.0.0-rc.112","@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","effect-cf":"0.41.0","esbuild":"0.28.1","miniflare":"5.20260811.1-alpha","typescript":"7.0.2","vite-plus":"0.3.0","vitest":"4.1.11"},"peerDependencies":{"@cloudflare/puppeteer":"^1.1.0","effect":"^4.0.0-rc.112","effect-cf":"^0.41.0"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./Alarm":{"types":"./dist/Alarm.d.mts","default":"./dist/Alarm.mjs"},"./BrowserRestCapture":{"types":"./dist/BrowserRestCapture.d.mts","default":"./dist/BrowserRestCapture.mjs"},"./BrowserRestCrawl":{"types":"./dist/BrowserRestCrawl.d.mts","default":"./dist/BrowserRestCrawl.mjs"},"./CloudflareBindings":{"types":"./dist/CloudflareBindings.d.mts","default":"./dist/CloudflareBindings.mjs"},"./CloudflareBrowser":{"types":"./dist/CloudflareBrowser.d.mts","default":"./dist/CloudflareBrowser.mjs"},"./CloudflareCodeMode":{"types":"./dist/CloudflareCodeMode.d.mts","default":"./dist/CloudflareCodeMode.mjs"},"./CloudflareConfig":{"types":"./dist/CloudflareConfig.d.mts","default":"./dist/CloudflareConfig.mjs"},"./CloudflareMemory":{"types":"./dist/CloudflareMemory.d.mts","default":"./dist/CloudflareMemory.mjs"},"./CloudflareScheduling":{"types":"./dist/CloudflareScheduling.d.mts","default":"./dist/CloudflareScheduling.mjs"},"./CloudflareSubscriptions":{"types":"./dist/CloudflareSubscriptions.d.mts","default":"./dist/CloudflareSubscriptions.mjs"},"./CloudflareThreadClient":{"types":"./dist/CloudflareThreadClient.d.mts","default":"./dist/CloudflareThreadClient.mjs"},"./InteractiveBrowser":{"types":"./dist/InteractiveBrowser.d.mts","default":"./dist/InteractiveBrowser.mjs"},"./ProtectedBrowser":{"types":"./dist/ProtectedBrowser.d.mts","default":"./dist/ProtectedBrowser.mjs"},"./ThreadObject":{"types":"./dist/ThreadObject.d.mts","default":"./dist/ThreadObject.mjs"},"./WakeScheduler":{"types":"./dist/WakeScheduler.d.mts","default":"./dist/WakeScheduler.mjs"},"./CloudflareAiGateway":{"types":"./dist/CloudflareAiGateway.d.mts","default":"./dist/CloudflareAiGateway.mjs"}},"description":"Cloudflare Layer assembly for Effect Agent: Durable Objects and Browser Run adapters.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/platform-cloudflare"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json"},"peerDependenciesMeta":{"@cloudflare/puppeteer":{"optional":true}}}
|
package/src/Alarm.ts
CHANGED
|
@@ -545,12 +545,12 @@ export class ThreadMaintenance extends Context.Service<
|
|
|
545
545
|
|
|
546
546
|
// A broken disposable index still needs a retry alarm and must not prevent startup.
|
|
547
547
|
const projectionDeadline = projection.pendingDeadline.pipe(
|
|
548
|
-
Effect.
|
|
549
|
-
Cause.hasInterrupts(cause)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
548
|
+
Effect.catchCauseIf(
|
|
549
|
+
(cause) => !Cause.hasInterrupts(cause),
|
|
550
|
+
(cause) =>
|
|
551
|
+
Effect.logError("Thread projection deadline unavailable", cause).pipe(
|
|
552
|
+
Effect.as(Option.some(0)),
|
|
553
|
+
),
|
|
554
554
|
),
|
|
555
555
|
);
|
|
556
556
|
|
package/src/BrowserRestCrawl.ts
CHANGED
|
@@ -319,7 +319,7 @@ const makeCrawl =
|
|
|
319
319
|
);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
return yield* Schema.
|
|
322
|
+
return yield* Schema.decodeEffect(Schema.fromJsonString(schema))(bodyText).pipe(
|
|
323
323
|
Effect.mapError((cause) =>
|
|
324
324
|
protocolError(
|
|
325
325
|
"Browser Run returned a malformed crawl response",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ThreadId } from "@effect-agent/core/Identifiers";
|
|
2
2
|
import { type ProducerId } from "@effect-agent/thread/Records";
|
|
3
3
|
import { Context, Effect, Layer, Predicate, Schema } from "effect";
|
|
4
|
+
import { RpcTargets } from "effect-cf";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Cloudflare platform bindings as Effect services (DEPLOY-010: "Cloudflare platform bindings
|
|
@@ -58,7 +59,8 @@ export type ThreadObjectClient = Omit<ThreadObjectRpc, keyof Rpc.DurableObjectBr
|
|
|
58
59
|
/**
|
|
59
60
|
* Deterministic logical Thread placement. The native adapter uses `idFromName(threadId)`;
|
|
60
61
|
* a shared application owner can bind that logical identity into its RPC adapter instead.
|
|
61
|
-
* Lookup performs no I/O and grants no authority.
|
|
62
|
+
* Lookup performs no I/O and grants no authority. Calls reuse a native target within
|
|
63
|
+
* one invocation; incoming requests and durable retries acquire their own targets.
|
|
62
64
|
*/
|
|
63
65
|
export class ThreadObjectNamespace extends Context.Service<
|
|
64
66
|
ThreadObjectNamespace,
|
|
@@ -79,6 +81,23 @@ export class ThreadObjectNamespace extends Context.Service<
|
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
/** Invoke a placed Thread using the current invocation's native RPC channel. */
|
|
85
|
+
export const callThreadObject = Effect.fn("callThreadObject")(function* <A, E>(
|
|
86
|
+
threadId: ThreadId,
|
|
87
|
+
invoke: (target: ThreadObjectClient) => Promise<A>,
|
|
88
|
+
onError: (cause: unknown) => E,
|
|
89
|
+
): Effect.fn.Return<A, E, ThreadObjectNamespace> {
|
|
90
|
+
const namespace = yield* ThreadObjectNamespace;
|
|
91
|
+
|
|
92
|
+
const target = yield* RpcTargets.get(namespace, threadId, () => namespace.get(threadId)).pipe(
|
|
93
|
+
Effect.mapError(onError),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return yield* Effect.tryPromise({ try: () => invoke(target), catch: onError }).pipe(
|
|
97
|
+
Effect.tapCause(() => RpcTargets.invalidate(target)),
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
82
101
|
/**
|
|
83
102
|
* Narrow one `env` member to a `DurableObjectNamespace`. `env` is an untyped platform value,
|
|
84
103
|
* and a namespace binding is a host object no Schema can decode, so this is the documented
|
package/src/CloudflareMemory.ts
CHANGED
|
@@ -40,6 +40,7 @@ import { Clock, Context, Effect, Layer, Schema } from "effect";
|
|
|
40
40
|
import {
|
|
41
41
|
DurableObject as EffectCfDurableObject,
|
|
42
42
|
DurableObjectState,
|
|
43
|
+
RpcTargets,
|
|
43
44
|
type WorkerEnvironment,
|
|
44
45
|
} from "effect-cf";
|
|
45
46
|
|
|
@@ -70,7 +71,7 @@ const makeMemoryClient = Effect.fn("CloudflareMemoryClient.make")(function* <
|
|
|
70
71
|
principal: Principal,
|
|
71
72
|
rpcLimits: MemoryRpcLimits = defaultMemoryRpcLimits,
|
|
72
73
|
) {
|
|
73
|
-
const validated = yield* Schema.
|
|
74
|
+
const validated = yield* Schema.decodeEffect(MemoryRpcLimits)(rpcLimits).pipe(
|
|
74
75
|
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
75
76
|
);
|
|
76
77
|
|
|
@@ -78,23 +79,36 @@ const makeMemoryClient = Effect.fn("CloudflareMemoryClient.make")(function* <
|
|
|
78
79
|
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
79
80
|
);
|
|
80
81
|
|
|
81
|
-
principal = yield* Schema.
|
|
82
|
+
principal = yield* Schema.decodeEffect(Principal)(principal).pipe(
|
|
82
83
|
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
83
84
|
);
|
|
84
85
|
const { namespace } = yield* MemoryObjectNamespace;
|
|
85
86
|
|
|
86
87
|
const call = Effect.fn("CloudflareMemoryClient.call")(function* (request: MemoryOwnerRequest) {
|
|
87
|
-
const decoded = yield* Schema.
|
|
88
|
+
const decoded = yield* Schema.decodeEffect(MemoryOwnerRequest)(request).pipe(
|
|
88
89
|
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
const encoded = yield* encodeMemoryWire(MemoryOwnerRequest, decoded, validated.maxRequestBytes);
|
|
92
93
|
|
|
94
|
+
const unavailable = (cause: unknown) => {
|
|
95
|
+
const error = MemoryRpcError.make({ reason: "unavailable" });
|
|
96
|
+
|
|
97
|
+
error.cause = cause;
|
|
98
|
+
|
|
99
|
+
return error;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const address = memoryObjectName(bound.namespace);
|
|
103
|
+
|
|
104
|
+
const target = yield* RpcTargets.get(namespace, address, () =>
|
|
105
|
+
namespace.get(namespace.idFromName(address)),
|
|
106
|
+
).pipe(Effect.mapError(unavailable));
|
|
107
|
+
|
|
93
108
|
const raw = yield* Effect.tryPromise({
|
|
94
|
-
try: () =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
109
|
+
try: () => target.memory(encoded),
|
|
110
|
+
catch: unavailable,
|
|
111
|
+
}).pipe(Effect.tapCause(() => RpcTargets.invalidate(target)));
|
|
98
112
|
|
|
99
113
|
const response = yield* decodeMemoryWire(MemoryOwnerResponse, raw, validated.maxResponseBytes);
|
|
100
114
|
|
|
@@ -120,7 +134,7 @@ const makeMemoryClient = Effect.fn("CloudflareMemoryClient.make")(function* <
|
|
|
120
134
|
lookup: MemoryLookup,
|
|
121
135
|
limits: MemoryRecallLimits,
|
|
122
136
|
) {
|
|
123
|
-
limits = yield* Schema.
|
|
137
|
+
limits = yield* Schema.decodeEffect(MemoryRecallLimits)(limits).pipe(
|
|
124
138
|
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
125
139
|
);
|
|
126
140
|
const timeoutMillis = Math.min(validated.timeoutMillis, limits.timeoutMillis);
|
|
@@ -381,9 +395,9 @@ const makeMemoryObject = <E>(
|
|
|
381
395
|
const state = yield* DurableObjectState.DurableObjectState;
|
|
382
396
|
const scope = yield* Effect.scope;
|
|
383
397
|
|
|
384
|
-
yield* Schema.
|
|
385
|
-
|
|
386
|
-
)
|
|
398
|
+
yield* Schema.decodeEffect(MemoryRpcLimits)(options.rpcLimits ?? defaultMemoryRpcLimits).pipe(
|
|
399
|
+
Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })),
|
|
400
|
+
);
|
|
387
401
|
|
|
388
402
|
return yield* state.blockConcurrencyWhile(Layer.buildWithScope(application, scope));
|
|
389
403
|
}),
|
|
@@ -40,6 +40,7 @@ import { Clock, Context, DateTime, Effect, Layer, Schema } from "effect";
|
|
|
40
40
|
import {
|
|
41
41
|
DurableObject as EffectCfDurableObject,
|
|
42
42
|
DurableObjectAlarm,
|
|
43
|
+
RpcTargets,
|
|
43
44
|
DurableObjectState as EffectCfDurableObjectState,
|
|
44
45
|
type WorkerEnvironment,
|
|
45
46
|
} from "effect-cf";
|
|
@@ -198,14 +199,27 @@ export class CloudflareSchedulingClient {
|
|
|
198
199
|
),
|
|
199
200
|
);
|
|
200
201
|
|
|
202
|
+
const unavailable = (cause: unknown) => {
|
|
203
|
+
const error = ScheduleStorageError.make({
|
|
204
|
+
operation: "call Schedule Owner",
|
|
205
|
+
reason: "unavailable",
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
error.cause = cause;
|
|
209
|
+
|
|
210
|
+
return error;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const address = scheduleOwnerKey(owner);
|
|
214
|
+
|
|
215
|
+
const target = yield* RpcTargets.get(namespace, address, () =>
|
|
216
|
+
namespace.get(namespace.idFromName(address)),
|
|
217
|
+
).pipe(Effect.mapError(unavailable));
|
|
218
|
+
|
|
201
219
|
const raw = yield* Effect.tryPromise({
|
|
202
|
-
try: () =>
|
|
203
|
-
catch:
|
|
204
|
-
|
|
205
|
-
operation: "call Schedule Owner",
|
|
206
|
-
reason: "unavailable",
|
|
207
|
-
}),
|
|
208
|
-
});
|
|
220
|
+
try: () => target.schedule(encoded),
|
|
221
|
+
catch: unavailable,
|
|
222
|
+
}).pipe(Effect.tapCause(() => RpcTargets.invalidate(target)));
|
|
209
223
|
|
|
210
224
|
const response = yield* decodeScheduleOwnerResponse(raw).pipe(
|
|
211
225
|
Effect.mapError(() =>
|
|
@@ -387,7 +401,7 @@ const decodeOwnerName = Effect.fn("decodeScheduleOwnerName")(function* (
|
|
|
387
401
|
});
|
|
388
402
|
}
|
|
389
403
|
|
|
390
|
-
const tuple = yield* Schema.
|
|
404
|
+
const tuple = yield* Schema.decodeEffect(
|
|
391
405
|
Schema.fromJsonString(Schema.Tuple([Schema.String, Schema.String])),
|
|
392
406
|
)(name).pipe(
|
|
393
407
|
Effect.mapError(() =>
|
|
@@ -395,7 +409,7 @@ const decodeOwnerName = Effect.fn("decodeScheduleOwnerName")(function* (
|
|
|
395
409
|
),
|
|
396
410
|
);
|
|
397
411
|
|
|
398
|
-
return yield* Schema.
|
|
412
|
+
return yield* Schema.decodeEffect(ScheduleOwner)({
|
|
399
413
|
tenantId: tuple[0],
|
|
400
414
|
ownerId: tuple[1],
|
|
401
415
|
}).pipe(
|
|
@@ -140,7 +140,7 @@ export const makeSubscriptionPartitionAlarmHandler = Effect.fn(
|
|
|
140
140
|
message: "Ancillary alarm tag mismatch",
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
const payload = yield* Schema.
|
|
143
|
+
const payload = yield* Schema.decodeEffect(options.payload)(event.payload).pipe(
|
|
144
144
|
Effect.mapError(() =>
|
|
145
145
|
SubscriptionAlarmProtocolError.make({ message: "Invalid ancillary alarm payload" }),
|
|
146
146
|
),
|
|
@@ -149,10 +149,10 @@ export const makeSubscriptionPartitionAlarmHandler = Effect.fn(
|
|
|
149
149
|
yield* options.handle({ ...event, payload });
|
|
150
150
|
}).pipe(
|
|
151
151
|
Effect.scoped,
|
|
152
|
-
Effect.
|
|
153
|
-
|
|
154
|
-
SubscriptionAlarmExtensionError.make({ code: "timeout" }),
|
|
155
|
-
),
|
|
152
|
+
Effect.timeoutOrElse({
|
|
153
|
+
duration: options.timeoutMillis,
|
|
154
|
+
orElse: () => SubscriptionAlarmExtensionError.make({ code: "timeout" }),
|
|
155
|
+
}),
|
|
156
156
|
Effect.provideContext(services),
|
|
157
157
|
),
|
|
158
158
|
};
|
|
@@ -629,7 +629,7 @@ const decodePartitionName = Effect.fn("decodeSubscriptionPartitionName")(functio
|
|
|
629
629
|
});
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
-
const tuple = yield* Schema.
|
|
632
|
+
const tuple = yield* Schema.decodeEffect(
|
|
633
633
|
Schema.fromJsonString(Schema.Tuple([Schema.String, Schema.String])),
|
|
634
634
|
)(name).pipe(
|
|
635
635
|
Effect.mapError(() =>
|
|
@@ -639,7 +639,7 @@ const decodePartitionName = Effect.fn("decodeSubscriptionPartitionName")(functio
|
|
|
639
639
|
),
|
|
640
640
|
);
|
|
641
641
|
|
|
642
|
-
return yield* Schema.
|
|
642
|
+
return yield* Schema.decodeEffect(SourcePartition)({
|
|
643
643
|
tenantId: tuple[0],
|
|
644
644
|
address: tuple[1],
|
|
645
645
|
}).pipe(
|
|
@@ -860,10 +860,10 @@ const alarmHandler = (limits: SubscriptionLimits) =>
|
|
|
860
860
|
|
|
861
861
|
return yield* handler.handle(event).pipe(
|
|
862
862
|
Effect.scoped,
|
|
863
|
-
Effect.
|
|
864
|
-
|
|
865
|
-
SubscriptionAlarmExtensionError.make({ code: "timeout" }),
|
|
866
|
-
),
|
|
863
|
+
Effect.timeoutOrElse({
|
|
864
|
+
duration: MAX_ANCILLARY_ALARM_MILLIS,
|
|
865
|
+
orElse: () => SubscriptionAlarmExtensionError.make({ code: "timeout" }),
|
|
866
|
+
}),
|
|
867
867
|
);
|
|
868
868
|
}
|
|
869
869
|
if (event.id !== SUBSCRIPTION_ALARM_ID) {
|
|
@@ -884,13 +884,13 @@ const alarmHandler = (limits: SubscriptionLimits) =>
|
|
|
884
884
|
yield* alarmControl.prearm((yield* Clock.currentTimeMillis) + limits.retryMillis);
|
|
885
885
|
|
|
886
886
|
const pass = yield* driver.runDue.pipe(
|
|
887
|
-
Effect.
|
|
888
|
-
Cause.hasInterrupts(cause)
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
887
|
+
Effect.catchCauseIf(
|
|
888
|
+
(cause) => !Cause.hasInterrupts(cause),
|
|
889
|
+
(cause) =>
|
|
890
|
+
Clock.currentTimeMillis.pipe(
|
|
891
|
+
Effect.flatMap((time) => alarmControl.prearm(time + limits.retryMillis)),
|
|
892
|
+
Effect.andThen(Effect.failCause(cause)),
|
|
893
|
+
),
|
|
894
894
|
),
|
|
895
895
|
);
|
|
896
896
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentInputError } from "@effect-agent/core/AgentError";
|
|
2
2
|
import { AgentId, type ThreadId } from "@effect-agent/core/Identifiers";
|
|
3
|
-
import {
|
|
3
|
+
import { InputMessage } from "@effect-agent/core/Messaging";
|
|
4
4
|
import { DigestError } from "@effect-agent/thread/Digest";
|
|
5
5
|
import {
|
|
6
6
|
Receipt,
|
|
@@ -48,7 +48,11 @@ import { Context, Crypto, Duration, Effect, Layer, Schema } from "effect";
|
|
|
48
48
|
import { RpcTracing } from "effect-cf";
|
|
49
49
|
|
|
50
50
|
import { DurableAlarmError } from "./Alarm.ts";
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
callThreadObject,
|
|
53
|
+
ThreadObjectNamespace,
|
|
54
|
+
type ThreadObjectRpc,
|
|
55
|
+
} from "./CloudflareBindings.ts";
|
|
52
56
|
import { AdmissionLimitExceeded } from "./CloudflareConfig.ts";
|
|
53
57
|
import { cloudflareFailureSignals, safeCauseMessage } from "./internal/boundary.ts";
|
|
54
58
|
|
|
@@ -117,7 +121,7 @@ export class SubmitRequest extends Schema.Class<SubmitRequest>(
|
|
|
117
121
|
admissionGroup: Schema.optionalKey(AdmissionGroup),
|
|
118
122
|
admissionFence: Schema.optionalKey(AdmissionFence),
|
|
119
123
|
workerAdmission: Schema.optionalKey(WorkerAdmission),
|
|
120
|
-
messageAdmission: Schema.optionalKey(
|
|
124
|
+
messageAdmission: Schema.optionalKey(InputMessage),
|
|
121
125
|
definitions: DefinitionDigests,
|
|
122
126
|
inputPayload: PersistedJson,
|
|
123
127
|
}) {}
|
|
@@ -445,7 +449,8 @@ export class CloudflareThreadClient extends Context.Service<
|
|
|
445
449
|
ThreadObjectNamespace | Crypto.Crypto
|
|
446
450
|
> = Layer.effect(CloudflareThreadClient)(
|
|
447
451
|
Effect.gen(function* () {
|
|
448
|
-
const
|
|
452
|
+
const namespace = yield* ThreadObjectNamespace;
|
|
453
|
+
const { rpcTracing } = namespace;
|
|
449
454
|
const crypto = yield* Crypto.Crypto;
|
|
450
455
|
|
|
451
456
|
const call = Effect.fn(
|
|
@@ -458,13 +463,10 @@ export class CloudflareThreadClient extends Context.Service<
|
|
|
458
463
|
const traceArgs =
|
|
459
464
|
rpcTracing === undefined ? [] : yield* RpcTracing.withRpcTraceContext([]);
|
|
460
465
|
|
|
461
|
-
const raw = yield*
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
return stub[hostRpcMethods[operation]](encoded, ...traceArgs);
|
|
466
|
-
},
|
|
467
|
-
catch: (cause) =>
|
|
466
|
+
const raw = yield* callThreadObject(
|
|
467
|
+
threadId,
|
|
468
|
+
(stub) => stub[hostRpcMethods[operation]](encoded, ...traceArgs),
|
|
469
|
+
(cause) =>
|
|
468
470
|
ThreadClientError.make({
|
|
469
471
|
threadId,
|
|
470
472
|
message: boundHostDiagnostic(
|
|
@@ -476,7 +478,7 @@ export class CloudflareThreadClient extends Context.Service<
|
|
|
476
478
|
cause,
|
|
477
479
|
...cloudflareFailureSignals(cause),
|
|
478
480
|
}),
|
|
479
|
-
|
|
481
|
+
).pipe(Effect.provideService(ThreadObjectNamespace, namespace));
|
|
480
482
|
|
|
481
483
|
return yield* decodeHostResponse(raw).pipe(
|
|
482
484
|
Effect.mapError((error): HostProtocolError =>
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
Duration,
|
|
37
37
|
Effect,
|
|
38
38
|
Layer,
|
|
39
|
-
Option,
|
|
40
39
|
Redacted,
|
|
41
40
|
Ref,
|
|
42
41
|
Schema,
|
|
@@ -231,7 +230,7 @@ export class BrowserRunViewport extends Schema.Class<BrowserRunViewport>("Browse
|
|
|
231
230
|
) {}
|
|
232
231
|
|
|
233
232
|
const decodeViewport = (input: BrowserRunViewport) =>
|
|
234
|
-
Schema.
|
|
233
|
+
Schema.decodeEffect(BrowserRunViewport)(input, { onExcessProperty: "error" }).pipe(
|
|
235
234
|
Effect.mapError(() => policyError("The browser viewport is malformed")),
|
|
236
235
|
// Copy only presentation fields, including for Schema class instances.
|
|
237
236
|
Effect.map((viewport) => ({
|
|
@@ -1043,7 +1042,7 @@ const makeProductionPage = (page: Page): BrowserRunInteractivePage => {
|
|
|
1043
1042
|
),
|
|
1044
1043
|
);
|
|
1045
1044
|
|
|
1046
|
-
if (observation._tag === "Text") Schema.
|
|
1045
|
+
if (observation._tag === "Text") Schema.decodeSync(PageObservation)(observation.text);
|
|
1047
1046
|
|
|
1048
1047
|
return observation;
|
|
1049
1048
|
},
|
|
@@ -1177,7 +1176,7 @@ const snapshotPolicy = Effect.fn("BrowserRunInteractive.snapshotPolicy")(functio
|
|
|
1177
1176
|
InteractiveBrowserPolicySnapshot,
|
|
1178
1177
|
InteractiveBrowserPolicyDeniedError | InteractiveBrowserUnsupportedError
|
|
1179
1178
|
> {
|
|
1180
|
-
const decoded = yield* Schema.
|
|
1179
|
+
const decoded = yield* Schema.decodeEffect(InteractiveBrowserPolicy)(input).pipe(
|
|
1181
1180
|
Effect.mapError(() => policyError("The interactive browser policy is malformed")),
|
|
1182
1181
|
);
|
|
1183
1182
|
|
|
@@ -1651,15 +1650,13 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
1651
1650
|
}),
|
|
1652
1651
|
)
|
|
1653
1652
|
.pipe(
|
|
1654
|
-
Effect.flatMap(
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
}),
|
|
1662
|
-
),
|
|
1653
|
+
Effect.flatMap(
|
|
1654
|
+
Effect.fromOption(() =>
|
|
1655
|
+
InteractiveBrowserBusyError.make({
|
|
1656
|
+
implementation: browserRunInteractiveImplementation,
|
|
1657
|
+
message: "The browser handle already has an operation in flight",
|
|
1658
|
+
}),
|
|
1659
|
+
),
|
|
1663
1660
|
),
|
|
1664
1661
|
);
|
|
1665
1662
|
|
|
@@ -1714,7 +1711,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
1714
1711
|
});
|
|
1715
1712
|
}
|
|
1716
1713
|
|
|
1717
|
-
return yield* Schema.
|
|
1714
|
+
return yield* Schema.decodeEffect(BrowserTextResult)({
|
|
1718
1715
|
text: observation.text,
|
|
1719
1716
|
}).pipe(
|
|
1720
1717
|
Effect.mapError((cause) =>
|
|
@@ -1748,7 +1745,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
1748
1745
|
}),
|
|
1749
1746
|
),
|
|
1750
1747
|
screenshot: (request) =>
|
|
1751
|
-
Schema.
|
|
1748
|
+
Schema.decodeEffect(BrowserScreenshotRequest)(request).pipe(
|
|
1752
1749
|
Effect.mapError(() => policyError("The browser screenshot request is malformed")),
|
|
1753
1750
|
Effect.flatMap((decoded) =>
|
|
1754
1751
|
run(
|
|
@@ -1771,7 +1768,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
1771
1768
|
});
|
|
1772
1769
|
}
|
|
1773
1770
|
|
|
1774
|
-
return yield* Schema.
|
|
1771
|
+
return yield* Schema.decodeEffect(PageScreenshotResult)({
|
|
1775
1772
|
implementation: browserRunInteractiveImplementation,
|
|
1776
1773
|
mediaType: "image/png",
|
|
1777
1774
|
bytes: new Uint8Array(bytes),
|
|
@@ -1786,7 +1783,7 @@ const makeHandle = Effect.fn("BrowserRunInteractive.makeHandle")(function* (
|
|
|
1786
1783
|
),
|
|
1787
1784
|
),
|
|
1788
1785
|
scroll: (request) =>
|
|
1789
|
-
Schema.
|
|
1786
|
+
Schema.decodeEffect(BrowserScrollRequest)(request).pipe(
|
|
1790
1787
|
Effect.mapError(() => policyError("The browser scroll request is malformed")),
|
|
1791
1788
|
Effect.flatMap((decoded) =>
|
|
1792
1789
|
run(
|
|
@@ -2209,7 +2206,7 @@ const makeHostService = (
|
|
|
2209
2206
|
),
|
|
2210
2207
|
),
|
|
2211
2208
|
getLiveView: (request) =>
|
|
2212
|
-
Schema.
|
|
2209
|
+
Schema.decodeEffect(BrowserRunLiveViewRequest)(request).pipe(
|
|
2213
2210
|
Effect.mapError(() => policyError("The Live View request is malformed")),
|
|
2214
2211
|
Effect.flatMap((decoded) =>
|
|
2215
2212
|
runtime.run(
|
|
@@ -2222,7 +2219,7 @@ const makeHostService = (
|
|
|
2222
2219
|
"Cloudflare returned a malformed Live View response",
|
|
2223
2220
|
).pipe(
|
|
2224
2221
|
Effect.flatMap((observation) =>
|
|
2225
|
-
Schema.
|
|
2222
|
+
Schema.decodeEffect(BrowserRunLiveViewResult)({
|
|
2226
2223
|
devtoolsFrontendUrl: Redacted.make(observation.devtoolsFrontendUrl),
|
|
2227
2224
|
}).pipe(
|
|
2228
2225
|
Effect.mapError(() =>
|
|
@@ -2236,7 +2233,7 @@ const makeHostService = (
|
|
|
2236
2233
|
),
|
|
2237
2234
|
),
|
|
2238
2235
|
handoff: (request) =>
|
|
2239
|
-
Schema.
|
|
2236
|
+
Schema.decodeEffect(BrowserRunHandoffRequest)(request).pipe(
|
|
2240
2237
|
Effect.mapError(() => policyError("The browser handoff request is malformed")),
|
|
2241
2238
|
Effect.flatMap((decoded) =>
|
|
2242
2239
|
runtime.run(
|
|
@@ -2249,7 +2246,7 @@ const makeHostService = (
|
|
|
2249
2246
|
"Cloudflare returned a malformed browser handoff response",
|
|
2250
2247
|
).pipe(
|
|
2251
2248
|
Effect.flatMap((observation) =>
|
|
2252
|
-
Schema.
|
|
2249
|
+
Schema.decodeEffect(BrowserRunHandoffResult)({
|
|
2253
2250
|
handoffId: Redacted.make(observation.handoffId),
|
|
2254
2251
|
}).pipe(
|
|
2255
2252
|
Effect.mapError(() =>
|
|
@@ -2272,7 +2269,7 @@ const makeHostService = (
|
|
|
2272
2269
|
"Cloudflare returned a malformed browser handoff state",
|
|
2273
2270
|
).pipe(
|
|
2274
2271
|
Effect.flatMap((observation) =>
|
|
2275
|
-
Schema.
|
|
2272
|
+
Schema.decodeEffect(BrowserRunHandoffState)({
|
|
2276
2273
|
active: observation.active,
|
|
2277
2274
|
...(observation.handoffId === undefined
|
|
2278
2275
|
? {}
|
package/src/WakeScheduler.ts
CHANGED
|
@@ -3,7 +3,11 @@ import { makeWakeSubscriptionHub, WakeScheduler } from "@effect-agent/thread/Wak
|
|
|
3
3
|
import { Effect, Layer, PubSub, Schema, Stream } from "effect";
|
|
4
4
|
|
|
5
5
|
import { DurableAlarmService } from "./Alarm.ts";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
callThreadObject,
|
|
8
|
+
ThreadObjectPlacement,
|
|
9
|
+
ThreadObjectNamespace,
|
|
10
|
+
} from "./CloudflareBindings.ts";
|
|
7
11
|
import { safeCauseMessage } from "./internal/boundary.ts";
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -41,7 +45,7 @@ export const cloudflareWakeSchedulerLayer: Layer.Layer<
|
|
|
41
45
|
Effect.gen(function* () {
|
|
42
46
|
const alarm = yield* DurableAlarmService;
|
|
43
47
|
const placement = yield* ThreadObjectPlacement;
|
|
44
|
-
const
|
|
48
|
+
const namespace = yield* ThreadObjectNamespace;
|
|
45
49
|
const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);
|
|
46
50
|
const progress = yield* makeWakeSubscriptionHub;
|
|
47
51
|
|
|
@@ -60,15 +64,17 @@ export const cloudflareWakeSchedulerLayer: Layer.Layer<
|
|
|
60
64
|
);
|
|
61
65
|
|
|
62
66
|
const notifyRemote = (threadId: ThreadId) =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
callThreadObject(
|
|
68
|
+
threadId,
|
|
69
|
+
(target) => target.wake(),
|
|
70
|
+
(cause) =>
|
|
66
71
|
RemoteWakeDropped.make({
|
|
67
72
|
threadId,
|
|
68
73
|
message: safeCauseMessage(cause, "The remote wake failed without a diagnostic"),
|
|
69
74
|
cause,
|
|
70
75
|
}),
|
|
71
|
-
|
|
76
|
+
).pipe(
|
|
77
|
+
Effect.provideService(ThreadObjectNamespace, namespace),
|
|
72
78
|
Effect.catch((error) =>
|
|
73
79
|
Effect.logWarning(`CloudflareWakeScheduler: remote wake of ${threadId} dropped`, error),
|
|
74
80
|
),
|
|
@@ -107,7 +107,7 @@ export class BrowserRunSessionLifecycle extends Context.Service<
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
if (response.status === 404) {
|
|
110
|
-
const absent = Schema.
|
|
110
|
+
const absent = Schema.decodeOption(Schema.fromJsonString(Absent))(body, {
|
|
111
111
|
onExcessProperty: "error",
|
|
112
112
|
});
|
|
113
113
|
|
|
@@ -116,16 +116,16 @@ export class BrowserRunSessionLifecycle extends Context.Service<
|
|
|
116
116
|
return yield* new BrowserRunCleanupError({ reason: "malformed" });
|
|
117
117
|
}
|
|
118
118
|
if (method === "DELETE") {
|
|
119
|
-
const result = yield* Schema.
|
|
120
|
-
|
|
121
|
-
)
|
|
119
|
+
const result = yield* Schema.decodeEffect(Schema.fromJsonString(Closed))(body).pipe(
|
|
120
|
+
Effect.mapError(() => new BrowserRunCleanupError({ reason: "malformed" })),
|
|
121
|
+
);
|
|
122
122
|
|
|
123
123
|
return result.status === "closed";
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const result = yield* Schema.
|
|
127
|
-
|
|
128
|
-
)
|
|
126
|
+
const result = yield* Schema.decodeEffect(Schema.fromJsonString(Metadata))(body).pipe(
|
|
127
|
+
Effect.mapError(() => new BrowserRunCleanupError({ reason: "malformed" })),
|
|
128
|
+
);
|
|
129
129
|
|
|
130
130
|
if (result.sessionId !== sessionId)
|
|
131
131
|
return yield* new BrowserRunCleanupError({ reason: "malformed" });
|
|
@@ -135,7 +135,7 @@ export class BrowserRunSessionLifecycle extends Context.Service<
|
|
|
135
135
|
|
|
136
136
|
const close = Effect.fn("BrowserRunSessionLifecycle.close")(
|
|
137
137
|
function* (sessionId: Redacted.Redacted<string>) {
|
|
138
|
-
const id = yield* Schema.
|
|
138
|
+
const id = yield* Schema.decodeEffect(Identity)(Redacted.value(sessionId)).pipe(
|
|
139
139
|
Effect.mapError(() => new BrowserRunCleanupError({ reason: "configuration" })),
|
|
140
140
|
);
|
|
141
141
|
|