@effect-agent/platform-cloudflare 0.1.0-beta.9 → 0.1.0-beta.91

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.
Files changed (100) hide show
  1. package/dist/Alarm.d.mts +188 -0
  2. package/dist/Alarm.mjs +451 -0
  3. package/dist/Alarm.mjs.map +1 -0
  4. package/dist/BrowserRestCapture.d.mts +35 -0
  5. package/dist/BrowserRestCapture.mjs +238 -0
  6. package/dist/BrowserRestCapture.mjs.map +1 -0
  7. package/dist/BrowserRestCrawl.d.mts +17 -0
  8. package/dist/BrowserRestCrawl.mjs +244 -0
  9. package/dist/BrowserRestCrawl.mjs.map +1 -0
  10. package/dist/CloudflareAiGateway.d.mts +64 -0
  11. package/dist/CloudflareAiGateway.mjs +70 -0
  12. package/dist/CloudflareAiGateway.mjs.map +1 -0
  13. package/dist/CloudflareBindings.d.mts +115 -0
  14. package/dist/CloudflareBindings.mjs +113 -0
  15. package/dist/CloudflareBindings.mjs.map +1 -0
  16. package/dist/CloudflareBrowser-DV6kya1H.mjs +495 -0
  17. package/dist/CloudflareBrowser-DV6kya1H.mjs.map +1 -0
  18. package/dist/CloudflareBrowser-Doe1M7R5.d.mts +80 -0
  19. package/dist/CloudflareBrowser.d.mts +2 -0
  20. package/dist/CloudflareBrowser.mjs +2 -0
  21. package/dist/CloudflareCodeMode.d.mts +44 -0
  22. package/dist/CloudflareCodeMode.mjs +616 -0
  23. package/dist/CloudflareCodeMode.mjs.map +1 -0
  24. package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
  25. package/dist/CloudflareConfig.d.mts +2 -0
  26. package/dist/CloudflareConfig.mjs +122 -0
  27. package/dist/CloudflareConfig.mjs.map +1 -0
  28. package/dist/CloudflareMemory.d.mts +142 -0
  29. package/dist/CloudflareMemory.mjs +202 -0
  30. package/dist/CloudflareMemory.mjs.map +1 -0
  31. package/dist/CloudflareScheduling.d.mts +52 -0
  32. package/dist/CloudflareScheduling.mjs +396 -0
  33. package/dist/CloudflareScheduling.mjs.map +1 -0
  34. package/dist/CloudflareSubscriptions.d.mts +87 -0
  35. package/dist/CloudflareSubscriptions.mjs +530 -0
  36. package/dist/CloudflareSubscriptions.mjs.map +1 -0
  37. package/dist/CloudflareThreadClient.d.mts +1496 -0
  38. package/dist/CloudflareThreadClient.mjs +382 -0
  39. package/dist/CloudflareThreadClient.mjs.map +1 -0
  40. package/dist/InteractiveBrowser-DUE_m12-.d.mts +161 -0
  41. package/dist/InteractiveBrowser-DupDtfw2.mjs +1169 -0
  42. package/dist/InteractiveBrowser-DupDtfw2.mjs.map +1 -0
  43. package/dist/InteractiveBrowser.d.mts +2 -0
  44. package/dist/InteractiveBrowser.mjs +2 -0
  45. package/dist/ProtectedBrowser.d.mts +123 -0
  46. package/dist/ProtectedBrowser.mjs +1135 -0
  47. package/dist/ProtectedBrowser.mjs.map +1 -0
  48. package/dist/ThreadObject-CIbmXZCY.d.mts +812 -0
  49. package/dist/ThreadObject-DvRG0dDz.mjs +835 -0
  50. package/dist/ThreadObject-DvRG0dDz.mjs.map +1 -0
  51. package/dist/ThreadObject.d.mts +2 -0
  52. package/dist/ThreadObject.mjs +3 -0
  53. package/dist/WakeScheduler.d.mts +23 -0
  54. package/dist/WakeScheduler.mjs +57 -0
  55. package/dist/WakeScheduler.mjs.map +1 -0
  56. package/dist/boundary-BguazVkh.mjs +42 -0
  57. package/dist/boundary-BguazVkh.mjs.map +1 -0
  58. package/dist/index.d.mts +13 -1548
  59. package/dist/index.mjs +13 -1636
  60. package/dist/prepared-admission-DU0_T-ev.mjs +73 -0
  61. package/dist/prepared-admission-DU0_T-ev.mjs.map +1 -0
  62. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  63. package/package.json +1 -53
  64. package/src/Alarm.ts +973 -0
  65. package/src/BrowserRestCapture.ts +477 -0
  66. package/src/BrowserRestCrawl.ts +540 -0
  67. package/src/CloudflareAiGateway.ts +170 -0
  68. package/src/CloudflareBindings.ts +199 -0
  69. package/src/CloudflareBrowser.ts +15 -0
  70. package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
  71. package/src/{config.ts → CloudflareConfig.ts} +9 -8
  72. package/src/CloudflareMemory.ts +420 -0
  73. package/src/CloudflareScheduling.ts +747 -0
  74. package/src/CloudflareSubscriptions.ts +1041 -0
  75. package/src/CloudflareThreadClient.ts +809 -0
  76. package/src/InteractiveBrowser.ts +2354 -0
  77. package/src/ProtectedBrowser.ts +9 -0
  78. package/src/ThreadObject.ts +1179 -0
  79. package/src/{wake-scheduler.ts → WakeScheduler.ts} +32 -26
  80. package/src/index.ts +12 -23
  81. package/src/internal/boundary.ts +55 -0
  82. package/src/internal/browser-quick-action.ts +857 -0
  83. package/src/internal/browser-session-lifecycle.ts +160 -0
  84. package/src/internal/layers.ts +751 -0
  85. package/src/internal/message-delivery.ts +152 -0
  86. package/src/internal/prepared-admission.ts +116 -0
  87. package/src/internal/progress-wait.ts +121 -0
  88. package/src/internal/transport.ts +44 -0
  89. package/src/protected-browser/binding.ts +313 -0
  90. package/src/protected-browser/host.ts +344 -0
  91. package/src/protected-browser/inspect-frame.ts +125 -0
  92. package/src/protected-browser/native.ts +445 -0
  93. package/src/protected-browser/policy.ts +821 -0
  94. package/dist/index.mjs.map +0 -1
  95. package/src/alarm.ts +0 -334
  96. package/src/bindings.ts +0 -145
  97. package/src/client.ts +0 -646
  98. package/src/conversation-object.ts +0 -768
  99. package/src/layers.ts +0 -376
  100. package/src/transport.ts +0 -38
@@ -0,0 +1,113 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Context, Effect, Layer, Predicate, Schema } from "effect";
3
+ import "effect-agent/identifiers";
4
+ import "effect-agent/records";
5
+ import { RpcTargets } from "effect-cf";
6
+ //#region src/CloudflareBindings.ts
7
+ var CloudflareBindings_exports = /* @__PURE__ */ __exportAll({
8
+ CloudflareBindingError: () => CloudflareBindingError,
9
+ DurableObjectContext: () => DurableObjectContext,
10
+ ThreadObjectIdentity: () => ThreadObjectIdentity,
11
+ ThreadObjectNamespace: () => ThreadObjectNamespace,
12
+ ThreadObjectPlacement: () => ThreadObjectPlacement,
13
+ callThreadObject: () => callThreadObject,
14
+ threadNamespaceFromEnv: () => threadNamespaceFromEnv,
15
+ threadNamespaceLayer: () => threadNamespaceLayer
16
+ });
17
+ /**
18
+ * Cloudflare platform bindings as Effect services (DEPLOY-010: "Cloudflare platform bindings
19
+ * are supplied as Effect services/Layers"). Application code never reads `env` or touches a
20
+ * `DurableObjectState` directly — the Thread Object class constructs these Layers once
21
+ * per incarnation and everything downstream consumes the services.
22
+ */
23
+ /** A Cloudflare platform binding was missing or carried the wrong shape (DEPLOY-003/010). */
24
+ var CloudflareBindingError = class extends Schema.TaggedError()("CloudflareBindingError", {
25
+ binding: Schema.String,
26
+ message: Schema.String
27
+ }) {};
28
+ /**
29
+ * Deterministic logical Thread placement. The native adapter uses `idFromName(threadId)`;
30
+ * a shared application owner can bind that logical identity into its RPC adapter instead.
31
+ * Lookup performs no I/O and grants no authority. Calls reuse a native target within
32
+ * one invocation; incoming requests and durable retries acquire their own targets.
33
+ */
34
+ var ThreadObjectNamespace = class ThreadObjectNamespace extends Context.Service()("@effect-agent/platform-cloudflare/ThreadObjectNamespace") {
35
+ static layer(namespace, options = {}) {
36
+ return Layer.succeed(ThreadObjectNamespace)({
37
+ get: (threadId) => namespace.get(namespace.idFromName(threadId)),
38
+ ...options.rpcTracing === void 0 ? {} : { rpcTracing: options.rpcTracing }
39
+ });
40
+ }
41
+ };
42
+ /** Invoke a placed Thread using the current invocation's native RPC channel. */
43
+ const callThreadObject = Effect.fn("callThreadObject")(function* (threadId, invoke, onError) {
44
+ const namespace = yield* ThreadObjectNamespace;
45
+ const target = yield* RpcTargets.get(namespace, threadId, () => namespace.get(threadId)).pipe(Effect.mapError(onError));
46
+ return yield* Effect.tryPromise({
47
+ try: () => invoke(target),
48
+ catch: onError
49
+ }).pipe(Effect.tapCause(() => RpcTargets.invalidate(target)));
50
+ });
51
+ /**
52
+ * Narrow one `env` member to a `DurableObjectNamespace`. `env` is an untyped platform value,
53
+ * and a namespace binding is a host object no Schema can decode, so this is the documented
54
+ * narrowest-boundary check (structural probe for the namespace surface the transport uses);
55
+ * a missing or misshaped binding fails typed before any Layer is built.
56
+ */
57
+ const threadNamespaceFromEnv = Effect.fn("threadNamespaceFromEnv")(function* (env, binding) {
58
+ if (!Predicate.isObjectKeyword(env)) return yield* CloudflareBindingError.make({
59
+ binding,
60
+ message: "The Worker environment is not an object; no bindings are available."
61
+ });
62
+ const candidate = yield* Effect.try({
63
+ try: () => {
64
+ const value = Reflect.get(env, binding);
65
+ if (!Predicate.isObjectKeyword(value)) return void 0;
66
+ const idFromName = Reflect.get(value, "idFromName");
67
+ const get = Reflect.get(value, "get");
68
+ return typeof idFromName === "function" && typeof get === "function" ? value : void 0;
69
+ },
70
+ catch: () => CloudflareBindingError.make({
71
+ binding,
72
+ message: `env.${binding} could not be inspected as a DurableObjectNamespace binding.`
73
+ })
74
+ });
75
+ if (candidate !== void 0) return candidate;
76
+ return yield* CloudflareBindingError.make({
77
+ binding,
78
+ message: `env.${binding} is not a DurableObjectNamespace binding; declare the Thread Object class under this binding in the Worker configuration.`
79
+ });
80
+ });
81
+ /**
82
+ * Build the namespace from Worker `env` (fails typed). Enable `rpcTracing` only when the
83
+ * receiver also opts into the effect-cf native RPC trace-context contract.
84
+ */
85
+ const threadNamespaceLayer = (env, binding, options = {}) => Layer.effect(ThreadObjectNamespace)(Effect.map(threadNamespaceFromEnv(env, binding), (namespace) => ({
86
+ get: (threadId) => namespace.get(namespace.idFromName(threadId)),
87
+ ...options.rpcTracing === true ? { rpcTracing: binding } : {}
88
+ })));
89
+ /**
90
+ * The live Durable Object execution context of THIS incarnation. Only Layer construction and
91
+ * the alarm service consume it; important state never lives on it (`ctx.storage` is truth,
92
+ * everything in memory is a cache — deployment spec §11).
93
+ */
94
+ var DurableObjectContext = class DurableObjectContext extends Context.Service()("@effect-agent/platform-cloudflare/DurableObjectContext") {
95
+ static layer(ctx, env) {
96
+ return Layer.succeed(DurableObjectContext)({
97
+ ctx,
98
+ env
99
+ });
100
+ }
101
+ };
102
+ /**
103
+ * The Thread identity this Object serializes and the producer identity its Attempts
104
+ * write with (`{producerPrefix}:{threadId}`, plan §1.4). Derived once per incarnation
105
+ * from `ctx.id.name` — the Object identity rule guarantees the name IS the Thread ID.
106
+ */
107
+ var ThreadObjectIdentity = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadObjectIdentity") {};
108
+ /** Logical Threads whose canonical stores and admission ledger live in this physical Object. */
109
+ var ThreadObjectPlacement = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadObjectPlacement") {};
110
+ //#endregion
111
+ export { CloudflareBindingError, DurableObjectContext, ThreadObjectIdentity, ThreadObjectNamespace, ThreadObjectPlacement, callThreadObject, CloudflareBindings_exports as t, threadNamespaceFromEnv, threadNamespaceLayer };
112
+
113
+ //# sourceMappingURL=CloudflareBindings.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareBindings.mjs","names":[],"sources":["../src/CloudflareBindings.ts"],"sourcesContent":["import { Context, Effect, Layer, Predicate, Schema } from \"effect\";\nimport { type ThreadId } from \"effect-agent/identifiers\";\nimport { type ProducerId } from \"effect-agent/records\";\nimport { RpcTargets } from \"effect-cf\";\n\n/**\n * Cloudflare platform bindings as Effect services (DEPLOY-010: \"Cloudflare platform bindings\n * are supplied as Effect services/Layers\"). Application code never reads `env` or touches a\n * `DurableObjectState` directly — the Thread Object class constructs these Layers once\n * per incarnation and everything downstream consumes the services.\n */\n\n/** A Cloudflare platform binding was missing or carried the wrong shape (DEPLOY-003/010). */\nexport class CloudflareBindingError extends Schema.TaggedError<CloudflareBindingError>()(\n \"CloudflareBindingError\",\n {\n binding: Schema.String,\n message: Schema.String,\n },\n) {}\n\n/**\n * The RPC surface one Thread Durable Object exposes to Workers and to sibling\n * Thread Objects. `ThreadObject.make` implements it; the Worker-side client\n * and the cross-Object transport call it through `DurableObjectNamespace` stubs. Every\n * `encoded` value is a Schema-encoded envelope (`client.ts` wire schemas for host entry\n * points, `@effect-agent/storage-cloudflare` port envelopes for `portCall`), so the RPC\n * boundary carries only structured-cloneable JSON. The optional trailing trace context is\n * transient native RPC metadata, stripped by an opted-in effect-cf receiver before decoding\n * the host envelope. It never enters durable state.\n */\nexport interface ThreadObjectRpc extends Rpc.DurableObjectBranded {\n /** Admission-limits gate + `DurableAgentRuntime.submit`; answers a `SubmitResponse`. */\n submitEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Wake-hinted, poll-guaranteed settlement wait; answers an `AwaitSettlementResponse`. */\n submissionStatusEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n awaitSettlementEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Event-driven durable progress wait; answers a `ProgressObserved` host response. */\n awaitProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Best-effort cancellation for one in-flight progress wait. */\n cancelProgressEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** One bounded page of canonical records; answers an `ObservePageResponse`. */\n observePage(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Durable abort intent; answers an `AbortResponse`. */\n abortEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Durable approval decision (plan §2.6); answers a `ResolveApprovalResponse`. */\n resolveApprovalEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Authorized DUR-017 Unknown-Outcome resolution; answers a `ResolveUnknownResponse`. */\n resolveUnknownEncoded(encoded: unknown, traceContext?: unknown): Promise<unknown>;\n /** Owner-side cross-Object port endpoint (WP2 envelopes, executed on LOCAL facets). */\n portCall(encoded: unknown): Promise<unknown>;\n /** Droppable liveness hint from another Object: arms an immediate alarm. */\n wake(): Promise<void>;\n}\n\n/** A logical Thread endpoint may be bound to an application Object without forging its brand. */\nexport type ThreadObjectClient = Omit<ThreadObjectRpc, keyof Rpc.DurableObjectBranded>;\n\n/**\n * Deterministic logical Thread placement. The native adapter uses `idFromName(threadId)`;\n * a shared application owner can bind that logical identity into its RPC adapter instead.\n * Lookup performs no I/O and grants no authority. Calls reuse a native target within\n * one invocation; incoming requests and durable retries acquire their own targets.\n */\nexport class ThreadObjectNamespace extends Context.Service<\n ThreadObjectNamespace,\n {\n readonly get: (threadId: ThreadId) => ThreadObjectClient;\n /** Stable binding name for opted-in native RPC tracing; absent by default. */\n readonly rpcTracing?: string;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadObjectNamespace\") {\n static layer(\n namespace: DurableObjectNamespace<ThreadObjectRpc>,\n options: { readonly rpcTracing?: string } = {},\n ): Layer.Layer<ThreadObjectNamespace> {\n return Layer.succeed(ThreadObjectNamespace)({\n get: (threadId) => namespace.get(namespace.idFromName(threadId)),\n ...(options.rpcTracing === undefined ? {} : { rpcTracing: options.rpcTracing }),\n });\n }\n}\n\n/** Invoke a placed Thread using the current invocation's native RPC channel. */\nexport const callThreadObject = Effect.fn(\"callThreadObject\")(function* <A, E>(\n threadId: ThreadId,\n invoke: (target: ThreadObjectClient) => Promise<A>,\n onError: (cause: unknown) => E,\n): Effect.fn.Return<A, E, ThreadObjectNamespace> {\n const namespace = yield* ThreadObjectNamespace;\n\n const target = yield* RpcTargets.get(namespace, threadId, () => namespace.get(threadId)).pipe(\n Effect.mapError(onError),\n );\n\n return yield* Effect.tryPromise({ try: () => invoke(target), catch: onError }).pipe(\n Effect.tapCause(() => RpcTargets.invalidate(target)),\n );\n});\n\n/**\n * Narrow one `env` member to a `DurableObjectNamespace`. `env` is an untyped platform value,\n * and a namespace binding is a host object no Schema can decode, so this is the documented\n * narrowest-boundary check (structural probe for the namespace surface the transport uses);\n * a missing or misshaped binding fails typed before any Layer is built.\n */\nexport const threadNamespaceFromEnv = Effect.fn(\"threadNamespaceFromEnv\")(function* (\n env: unknown,\n binding: string,\n): Effect.fn.Return<DurableObjectNamespace<ThreadObjectRpc>, CloudflareBindingError> {\n if (!Predicate.isObjectKeyword(env)) {\n return yield* CloudflareBindingError.make({\n binding,\n message: \"The Worker environment is not an object; no bindings are available.\",\n });\n }\n\n const candidate = yield* Effect.try({\n try: () => {\n const value: unknown = Reflect.get(env, binding);\n\n if (!Predicate.isObjectKeyword(value)) return undefined;\n const idFromName: unknown = Reflect.get(value, \"idFromName\");\n const get: unknown = Reflect.get(value, \"get\");\n\n return typeof idFromName === \"function\" && typeof get === \"function\" ? value : undefined;\n },\n catch: () =>\n CloudflareBindingError.make({\n binding,\n message: `env.${binding} could not be inspected as a DurableObjectNamespace binding.`,\n }),\n });\n\n if (candidate !== undefined) {\n // The structural probe above is the entire runtime contract this package relies on;\n // the assertion records that `idFromName`/`get` name a DurableObjectNamespace.\n return candidate as unknown as DurableObjectNamespace<ThreadObjectRpc>;\n }\n\n return yield* CloudflareBindingError.make({\n binding,\n message:\n `env.${binding} is not a DurableObjectNamespace binding; declare the Thread ` +\n \"Object class under this binding in the Worker configuration.\",\n });\n});\n\n/**\n * Build the namespace from Worker `env` (fails typed). Enable `rpcTracing` only when the\n * receiver also opts into the effect-cf native RPC trace-context contract.\n */\nexport const threadNamespaceLayer = (\n env: unknown,\n binding: string,\n options: { readonly rpcTracing?: boolean } = {},\n): Layer.Layer<ThreadObjectNamespace, CloudflareBindingError> =>\n Layer.effect(ThreadObjectNamespace)(\n Effect.map(threadNamespaceFromEnv(env, binding), (namespace) => ({\n get: (threadId) => namespace.get(namespace.idFromName(threadId)),\n ...(options.rpcTracing === true ? { rpcTracing: binding } : {}),\n })),\n );\n\n/**\n * The live Durable Object execution context of THIS incarnation. Only Layer construction and\n * the alarm service consume it; important state never lives on it (`ctx.storage` is truth,\n * everything in memory is a cache — deployment spec §11).\n */\nexport class DurableObjectContext extends Context.Service<\n DurableObjectContext,\n {\n readonly ctx: DurableObjectState;\n readonly env: unknown;\n }\n>()(\"@effect-agent/platform-cloudflare/DurableObjectContext\") {\n static layer(ctx: DurableObjectState, env: unknown): Layer.Layer<DurableObjectContext> {\n return Layer.succeed(DurableObjectContext)({ ctx, env });\n }\n}\n\n/**\n * The Thread identity this Object serializes and the producer identity its Attempts\n * write with (`{producerPrefix}:{threadId}`, plan §1.4). Derived once per incarnation\n * from `ctx.id.name` — the Object identity rule guarantees the name IS the Thread ID.\n */\nexport class ThreadObjectIdentity extends Context.Service<\n ThreadObjectIdentity,\n {\n readonly threadId: ThreadId;\n readonly producerId: ProducerId;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadObjectIdentity\") {}\n\n/** Logical Threads whose canonical stores and admission ledger live in this physical Object. */\nexport class ThreadObjectPlacement extends Context.Service<\n ThreadObjectPlacement,\n { readonly ownsThread: (threadId: ThreadId) => boolean }\n>()(\"@effect-agent/platform-cloudflare/ThreadObjectPlacement\") {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAaA,IAAa,yBAAb,cAA4C,OAAO,YAAoC,CAAC,CACtF,0BACA;CACE,SAAS,OAAO;CAChB,SAAS,OAAO;AAClB,CACF,CAAC,CAAC,CAAC;;;;;;;AA6CH,IAAa,wBAAb,MAAa,8BAA8B,QAAQ,QAOjD,CAAC,CAAC,yDAAyD,CAAC,CAAC;CAC7D,OAAO,MACL,WACA,UAA4C,CAAC,GACT;EACpC,OAAO,MAAM,QAAQ,qBAAqB,CAAC,CAAC;GAC1C,MAAM,aAAa,UAAU,IAAI,UAAU,WAAW,QAAQ,CAAC;GAC/D,GAAI,QAAQ,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,QAAQ,WAAW;EAC/E,CAAC;CACH;AACF;;AAGA,MAAa,mBAAmB,OAAO,GAAG,kBAAkB,CAAC,CAAC,WAC5D,UACA,QACA,SAC+C;CAC/C,MAAM,YAAY,OAAO;CAEzB,MAAM,SAAS,OAAO,WAAW,IAAI,WAAW,gBAAgB,UAAU,IAAI,QAAQ,CAAC,CAAC,CAAC,KACvF,OAAO,SAAS,OAAO,CACzB;CAEA,OAAO,OAAO,OAAO,WAAW;EAAE,WAAW,OAAO,MAAM;EAAG,OAAO;CAAQ,CAAC,CAAC,CAAC,KAC7E,OAAO,eAAe,WAAW,WAAW,MAAM,CAAC,CACrD;AACF,CAAC;;;;;;;AAQD,MAAa,yBAAyB,OAAO,GAAG,wBAAwB,CAAC,CAAC,WACxE,KACA,SACmF;CACnF,IAAI,CAAC,UAAU,gBAAgB,GAAG,GAChC,OAAO,OAAO,uBAAuB,KAAK;EACxC;EACA,SAAS;CACX,CAAC;CAGH,MAAM,YAAY,OAAO,OAAO,IAAI;EAClC,WAAW;GACT,MAAM,QAAiB,QAAQ,IAAI,KAAK,OAAO;GAE/C,IAAI,CAAC,UAAU,gBAAgB,KAAK,GAAG,OAAO,KAAA;GAC9C,MAAM,aAAsB,QAAQ,IAAI,OAAO,YAAY;GAC3D,MAAM,MAAe,QAAQ,IAAI,OAAO,KAAK;GAE7C,OAAO,OAAO,eAAe,cAAc,OAAO,QAAQ,aAAa,QAAQ,KAAA;EACjF;EACA,aACE,uBAAuB,KAAK;GAC1B;GACA,SAAS,OAAO,QAAQ;EAC1B,CAAC;CACL,CAAC;CAED,IAAI,cAAc,KAAA,GAGhB,OAAO;CAGT,OAAO,OAAO,uBAAuB,KAAK;EACxC;EACA,SACE,OAAO,QAAQ;CAEnB,CAAC;AACH,CAAC;;;;;AAMD,MAAa,wBACX,KACA,SACA,UAA6C,CAAC,MAE9C,MAAM,OAAO,qBAAqB,CAAC,CACjC,OAAO,IAAI,uBAAuB,KAAK,OAAO,IAAI,eAAe;CAC/D,MAAM,aAAa,UAAU,IAAI,UAAU,WAAW,QAAQ,CAAC;CAC/D,GAAI,QAAQ,eAAe,OAAO,EAAE,YAAY,QAAQ,IAAI,CAAC;AAC/D,EAAE,CACJ;;;;;;AAOF,IAAa,uBAAb,MAAa,6BAA6B,QAAQ,QAMhD,CAAC,CAAC,wDAAwD,CAAC,CAAC;CAC5D,OAAO,MAAM,KAAyB,KAAiD;EACrF,OAAO,MAAM,QAAQ,oBAAoB,CAAC,CAAC;GAAE;GAAK;EAAI,CAAC;CACzD;AACF;;;;;;AAOA,IAAa,uBAAb,cAA0C,QAAQ,QAMhD,CAAC,CAAC,wDAAwD,CAAC,CAAC,CAAC;;AAG/D,IAAa,wBAAb,cAA2C,QAAQ,QAGjD,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC"}
@@ -0,0 +1,495 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Context, Effect, Layer, Option, Schema } from "effect";
3
+ import { PageCapture, PageCaptureInferencePolicyError, PageCaptureInferenceUse, PageCaptureNavigationError, PageCaptureOutputLimitError, PageCaptureProtocolError, PageCaptureRateLimitedError, PageCaptureResourceUse, PageCaptureResult, PageCaptureUnsupportedError, PageContentCaptured, PageLinksCaptured, PageMarkdownCaptured, PageScrapeCaptured, PageStructuredCaptured } from "effect-agent/page-capture";
4
+ import { PageScreenshot, PageScreenshotOutputLimitError, PageScreenshotResult } from "effect-agent/page-screenshot";
5
+ import { SandboxImplementation } from "effect-agent/sandbox";
6
+ //#region src/internal/browser-quick-action.ts
7
+ /**
8
+ * The Cloudflare Browser Run Quick Action `PageCapture` adapter (capability
9
+ * spec §9.2). Each capture is one stateless `quickAction()` RPC on the
10
+ * Wrangler `browser` binding: the platform renders the target in a managed
11
+ * headless browser and returns one bounded output; the adapter holds no
12
+ * session and no state between passes. The binding requires a Worker
13
+ * compatibility date of `2026-03-24` or later, and local `wrangler dev` needs
14
+ * remote mode (`"remote": true` on the binding) because `quickAction` has no
15
+ * local implementation.
16
+ *
17
+ * Rendered output is untrusted, attacker-influenced content; this adapter
18
+ * only bounds and types it. Deployment class `E` only: no durability claim.
19
+ */
20
+ const browserQuickActionImplementation = SandboxImplementation.make({
21
+ isolation: "isolated",
22
+ identity: "cloudflare-browser-quick-action"
23
+ });
24
+ /** A native binding RPC rejected before it returned an HTTP response. */
25
+ var BrowserQuickActionRpcError = class extends Schema.TaggedError()("BrowserQuickActionRpcError", {
26
+ action: Schema.Literals([
27
+ "screenshot",
28
+ "content",
29
+ "markdown",
30
+ "links",
31
+ "scrape",
32
+ "json"
33
+ ]),
34
+ cause: Schema.Defect()
35
+ }) {};
36
+ /** Host-owned browser binding authority, supplied explicitly at the composition root. */
37
+ var BrowserQuickActionBrowserBinding = class BrowserQuickActionBrowserBinding extends Context.Service()("@effect-agent/platform-cloudflare/BrowserQuickActionBrowserBinding") {
38
+ static layer(options) {
39
+ const browser = options.browser;
40
+ const invoke = Effect.fn("BrowserQuickActionBrowserBinding.invoke")(function* (action, evaluate) {
41
+ return yield* Effect.tryPromise({
42
+ try: evaluate,
43
+ catch: (cause) => BrowserQuickActionRpcError.make({
44
+ action,
45
+ cause
46
+ })
47
+ });
48
+ });
49
+ return Layer.succeed(BrowserQuickActionBrowserBinding)({
50
+ screenshot: (request) => invoke("screenshot", () => browser.quickAction("screenshot", request)),
51
+ content: (request) => invoke("content", () => browser.quickAction("content", request)),
52
+ markdown: (request) => invoke("markdown", () => browser.quickAction("markdown", request)),
53
+ links: (request) => invoke("links", () => browser.quickAction("links", request)),
54
+ scrape: (request) => invoke("scrape", () => browser.quickAction("scrape", request)),
55
+ json: (request) => invoke("json", () => browser.quickAction("json", request))
56
+ });
57
+ }
58
+ };
59
+ /** Host-only diagnostic for a denied or unaccounted Workers AI extraction. */
60
+ var BrowserQuickActionWorkersAiPolicyError = class extends Schema.TaggedError()("BrowserQuickActionWorkersAiPolicyError", {
61
+ reason: Schema.Literals(["authorization", "accounting"]),
62
+ message: Schema.String.check(Schema.isMaxLength(8e3)),
63
+ cause: Schema.optionalKey(Schema.Defect())
64
+ }) {};
65
+ /** Explicit host-owned authority and accounting for separately billed Workers AI extraction. */
66
+ var BrowserQuickActionWorkersAi = class BrowserQuickActionWorkersAi extends Context.Service()("@effect-agent/platform-cloudflare/BrowserQuickActionWorkersAi") {
67
+ static layer(policy) {
68
+ return Layer.succeed(BrowserQuickActionWorkersAi)(policy);
69
+ }
70
+ };
71
+ const MAX_DIAGNOSTIC_LENGTH = 8e3;
72
+ const boundedDiagnostic = (message) => message.slice(0, MAX_DIAGNOSTIC_LENGTH);
73
+ const QuickActionSuccessEnvelope = Schema.Struct({
74
+ success: Schema.Literal(true),
75
+ result: Schema.Json
76
+ });
77
+ const QuickActionErrorEnvelope = Schema.Struct({
78
+ success: Schema.Literal(false),
79
+ errors: Schema.Array(Schema.Struct({
80
+ message: Schema.String,
81
+ code: Schema.optionalKey(Schema.Number),
82
+ detail: Schema.optionalKey(Schema.String),
83
+ path: Schema.optionalKey(Schema.String)
84
+ })),
85
+ rawAiResponse: Schema.optionalKey(Schema.String)
86
+ });
87
+ const QuickActionEnvelope = Schema.Union([QuickActionSuccessEnvelope, QuickActionErrorEnvelope]);
88
+ const decodeEnvelope = Schema.decodeUnknownOption(Schema.fromJsonString(QuickActionEnvelope));
89
+ /** Recognize only the provider's exact timeout grammar; never forward arbitrary error text. */
90
+ const navigationFailureMessage = (bodyText, response) => {
91
+ const fallback = `The Browser Run Quick Action API answered HTTP ${response.status}; the destination status is unknown`;
92
+ if (!isJsonResponse(response)) return fallback;
93
+ const envelope = decodeEnvelope(bodyText);
94
+ if (Option.isNone(envelope) || envelope.value.success) return fallback;
95
+ for (const error of envelope.value.errors) {
96
+ if (error.code !== 6002) continue;
97
+ const timeout = /^Navigation timeout of ([1-9][0-9]{0,5}) ms exceeded$/.exec(error.detail ?? "");
98
+ if (timeout !== null) return `Browser Run navigation timed out after ${timeout[1]} ms (Quick Action API HTTP ${response.status}); the destination status is unknown`;
99
+ }
100
+ return fallback;
101
+ };
102
+ /** Project the schema-validated request onto Cloudflare's native common options. */
103
+ const quickActionCommonOptions = (request) => {
104
+ const options = {};
105
+ const navigation = request.navigation;
106
+ if (navigation !== void 0) {
107
+ const goto = {};
108
+ if (navigation.waitUntil !== void 0) goto.waitUntil = navigation.waitUntil;
109
+ if (navigation.timeoutMillis !== void 0) goto.timeout = navigation.timeoutMillis;
110
+ if (Object.keys(goto).length > 0) options.gotoOptions = goto;
111
+ if (navigation.waitForSelector !== void 0) options.waitForSelector = {
112
+ selector: navigation.waitForSelector.selector,
113
+ ...navigation.waitForSelector.timeoutMillis === void 0 ? {} : { timeout: navigation.waitForSelector.timeoutMillis }
114
+ };
115
+ }
116
+ if (request.viewport !== void 0) options.viewport = {
117
+ width: request.viewport.width,
118
+ height: request.viewport.height
119
+ };
120
+ if (request.resourcePolicy !== void 0) {
121
+ if (request.resourcePolicy.rejectResourceTypes !== void 0) options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];
122
+ if (request.resourcePolicy.allowRequestPatterns !== void 0) options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];
123
+ }
124
+ return request.target._tag === "PageUrlTarget" ? {
125
+ ...options,
126
+ url: request.target.url
127
+ } : {
128
+ ...options,
129
+ html: request.target.html
130
+ };
131
+ };
132
+ /** Dispatch through Cloudflare's native action-specific overloads. */
133
+ const executeQuickAction = (browser, request) => {
134
+ const options = quickActionCommonOptions(request);
135
+ switch (request.action._tag) {
136
+ case "CapturePageContent": return browser.content(options);
137
+ case "CapturePageMarkdown": return browser.markdown(options);
138
+ case "CapturePageLinks": return browser.links({
139
+ ...options,
140
+ ...request.action.visibleLinksOnly === void 0 ? {} : { visibleLinksOnly: request.action.visibleLinksOnly }
141
+ });
142
+ case "CapturePageScrape": return browser.scrape({
143
+ ...options,
144
+ elements: request.action.selectors.map((selector) => ({ selector }))
145
+ });
146
+ case "CapturePageStructured": return browser.json({
147
+ ...options,
148
+ response_format: {
149
+ type: "json_schema",
150
+ json_schema: request.action.responseFormat
151
+ },
152
+ ...request.action.prompt === void 0 ? {} : { prompt: request.action.prompt }
153
+ });
154
+ }
155
+ };
156
+ const protocolError = (message, cause) => PageCaptureProtocolError.make({
157
+ implementation: browserQuickActionImplementation,
158
+ message: boundedDiagnostic(message),
159
+ ...cause === void 0 ? {} : { cause }
160
+ });
161
+ const navigationError = (message, cause) => PageCaptureNavigationError.make({
162
+ implementation: browserQuickActionImplementation,
163
+ message: boundedDiagnostic(message),
164
+ ...cause === void 0 ? {} : { cause }
165
+ });
166
+ /** Preserve bounded remote diagnostics for the host without exposing their text to a model. */
167
+ const privateResponseCause = (bodyText, response) => new Error(boundedDiagnostic(bodyText), { cause: {
168
+ provider: "cloudflare-browser-run",
169
+ httpStatus: response.status,
170
+ httpStatusSource: "browser-api",
171
+ headers: Object.fromEntries([
172
+ "content-type",
173
+ "cf-ray",
174
+ "x-request-id",
175
+ "retry-after",
176
+ "x-browser-ms-used"
177
+ ].flatMap((name) => {
178
+ const value = response.headers.get(name);
179
+ return value === null ? [] : [[name, boundedDiagnostic(value)]];
180
+ })),
181
+ bodyCharacters: bodyText.length,
182
+ bodyTruncated: bodyText.length > MAX_DIAGNOSTIC_LENGTH
183
+ } });
184
+ /** Foreign cancellation must not keep a response Scope open indefinitely. */
185
+ const cancelResponse = (cancel, warning) => Effect.tryPromise({
186
+ try: cancel,
187
+ catch: () => void 0
188
+ }).pipe(Effect.interruptible, Effect.timeoutOrElse({
189
+ duration: "1 second",
190
+ orElse: () => Effect.fail(void 0)
191
+ }), Effect.catch(() => Effect.logWarning(warning)));
192
+ const releaseResponseReader = (reader) => cancelResponse(() => reader.cancel(), "Canceling the Quick Action response failed").pipe(Effect.ensuring(Effect.try({
193
+ try: () => reader.releaseLock(),
194
+ catch: (cause) => protocolError("Releasing the Quick Action response failed", cause)
195
+ }).pipe(Effect.catch((error) => Effect.logWarning(error.message)))));
196
+ const readBoundedResponse = Effect.fn("BrowserQuickActionCapture.readResponse")(function* (response, request) {
197
+ const body = response.body;
198
+ if (body === null) return "";
199
+ const reader = yield* Effect.acquireRelease(Effect.try({
200
+ try: () => body.getReader(),
201
+ catch: (cause) => protocolError("Opening the Quick Action response failed", cause)
202
+ }), releaseResponseReader);
203
+ const decoder = new TextDecoder("utf-8", {
204
+ fatal: true,
205
+ ignoreBOM: false
206
+ });
207
+ let observedBytes = 0;
208
+ let bodyText = "";
209
+ while (true) {
210
+ const chunk = yield* Effect.tryPromise({
211
+ try: () => reader.read(),
212
+ catch: (cause) => protocolError("Reading the Quick Action response failed", cause)
213
+ });
214
+ if (chunk.done) break;
215
+ observedBytes += chunk.value.byteLength;
216
+ if (observedBytes > request.limits.maxOutputBytes) return yield* PageCaptureOutputLimitError.make({
217
+ implementation: browserQuickActionImplementation,
218
+ limit: request.limits.maxOutputBytes,
219
+ observed: observedBytes
220
+ });
221
+ bodyText += yield* Effect.try({
222
+ try: () => decoder.decode(chunk.value, { stream: true }),
223
+ catch: (cause) => protocolError("Decoding the Quick Action response failed", cause)
224
+ });
225
+ }
226
+ return bodyText + (yield* Effect.try({
227
+ try: () => decoder.decode(),
228
+ catch: (cause) => protocolError("Decoding the Quick Action response failed", cause)
229
+ }));
230
+ }, Effect.scoped);
231
+ /**
232
+ * Retry-After arrives in whole seconds; a non-integer form (an HTTP date) is
233
+ * dropped rather than guessed at.
234
+ */
235
+ const retryAfterMillis = (response) => {
236
+ const header = response.headers.get("Retry-After");
237
+ if (header === null) return void 0;
238
+ const seconds = Number(header);
239
+ if (!Number.isSafeInteger(seconds) || seconds < 0) return void 0;
240
+ const millis = seconds * 1e3;
241
+ return Number.isSafeInteger(millis) ? millis : void 0;
242
+ };
243
+ const browserMillis = (response) => {
244
+ const header = response.headers.get("X-Browser-Ms-Used");
245
+ if (header === null) return void 0;
246
+ const millis = Number(header);
247
+ return Number.isSafeInteger(millis) && millis >= 0 ? millis : void 0;
248
+ };
249
+ /** Only trusted response metadata chooses transport framing; page text never does. */
250
+ const isJsonResponse = (response) => {
251
+ const contentType = response.headers.get("Content-Type");
252
+ if (contentType === null) return false;
253
+ const mediaType = contentType.split(";", 1)[0]?.trim().toLowerCase();
254
+ return mediaType === "application/json" || mediaType?.endsWith("+json") === true;
255
+ };
256
+ const parseOutput = (action, bodyText, response) => {
257
+ if (!isJsonResponse(response)) return protocolError("The Quick Action success response was not a JSON response envelope", privateResponseCause(bodyText, response));
258
+ const envelope = decodeEnvelope(bodyText);
259
+ if (Option.isNone(envelope)) return protocolError("The JSON Quick Action response did not carry a valid response envelope", privateResponseCause(bodyText, response));
260
+ if (!envelope.value.success) return navigationError(navigationFailureMessage(bodyText, response), privateResponseCause(bodyText, response));
261
+ switch (action._tag) {
262
+ case "CapturePageContent":
263
+ case "CapturePageMarkdown":
264
+ if (typeof envelope.value.result !== "string") return protocolError("The Quick Action envelope carried a non-text result");
265
+ return action._tag === "CapturePageContent" ? PageContentCaptured.make({ html: envelope.value.result }) : PageMarkdownCaptured.make({ markdown: envelope.value.result });
266
+ case "CapturePageLinks": {
267
+ const decoded = Schema.decodeUnknownOption(PageLinksCaptured)({
268
+ _tag: "PageLinksCaptured",
269
+ links: envelope.value.result
270
+ });
271
+ if (Option.isNone(decoded)) return protocolError("The links Quick Action did not return a bounded array of valid URLs");
272
+ return decoded.value;
273
+ }
274
+ case "CapturePageScrape": {
275
+ const decoded = Schema.decodeUnknownOption(PageScrapeCaptured)({
276
+ _tag: "PageScrapeCaptured",
277
+ groups: envelope.value.result
278
+ });
279
+ if (Option.isNone(decoded)) return protocolError("The scrape Quick Action did not return bounded grouped element records");
280
+ return decoded.value;
281
+ }
282
+ case "CapturePageStructured": return PageStructuredCaptured.make({ value: envelope.value.result });
283
+ }
284
+ };
285
+ const isQuotaMessage = (text) => /time limit|daily|quota/i.test(text);
286
+ const makeCapture = (browser, workersAi) => Effect.fn("BrowserQuickActionCapture.capture")(function* (request) {
287
+ if (request.engine !== "chromium") return yield* PageCaptureUnsupportedError.make({
288
+ implementation: browserQuickActionImplementation,
289
+ feature: "engine",
290
+ message: "The browser binding's quickAction() exposes no engine selector; kitesurf requires the REST or CDP surface"
291
+ });
292
+ const usesWorkersAi = request.action._tag === "CapturePageStructured";
293
+ if (usesWorkersAi) {
294
+ if (workersAi === void 0) return yield* PageCaptureUnsupportedError.make({
295
+ implementation: browserQuickActionImplementation,
296
+ feature: "action",
297
+ message: "Structured capture invokes separately billed Workers AI and requires an explicit authorization and accounting policy"
298
+ });
299
+ yield* workersAi.authorizeAndAccount(request).pipe(Effect.mapError((cause) => PageCaptureInferencePolicyError.make({
300
+ implementation: browserQuickActionImplementation,
301
+ provider: "cloudflare-workers-ai",
302
+ reason: cause.reason,
303
+ message: cause.reason === "authorization" ? "Workers AI extraction was not authorized" : "Workers AI extraction could not be accounted for",
304
+ cause
305
+ })));
306
+ }
307
+ const response = yield* executeQuickAction(browser, request).pipe(Effect.mapError((error) => protocolError("The browser binding rejected the Quick Action", error.cause)));
308
+ const bodyText = yield* readBoundedResponse(response, request);
309
+ if (response.status === 429) {
310
+ const retryAfter = retryAfterMillis(response);
311
+ const reason = isQuotaMessage(bodyText) ? "quota" : "rate";
312
+ const cause = privateResponseCause(bodyText, response);
313
+ return yield* PageCaptureRateLimitedError.make({
314
+ implementation: browserQuickActionImplementation,
315
+ reason,
316
+ ...retryAfter === void 0 ? {} : { retryAfterMillis: retryAfter },
317
+ ...cause === void 0 ? {} : { cause },
318
+ message: reason === "quota" ? "The Quick Action exceeded its browser quota" : "The Quick Action was rate limited"
319
+ });
320
+ }
321
+ if (!response.ok) {
322
+ const message = navigationFailureMessage(bodyText, response);
323
+ const cause = privateResponseCause(bodyText, response);
324
+ if (response.status >= 500) return yield* protocolError(message, cause);
325
+ return yield* navigationError(message, cause);
326
+ }
327
+ const output = parseOutput(request.action, bodyText, response);
328
+ if (output._tag === "PageCaptureNavigationError" || output._tag === "PageCaptureProtocolError") return yield* output;
329
+ const millis = browserMillis(response);
330
+ return PageCaptureResult.make({
331
+ implementation: browserQuickActionImplementation,
332
+ output,
333
+ resourceUse: PageCaptureResourceUse.make({
334
+ ...millis === void 0 ? {} : { browserMillis: millis },
335
+ ...usesWorkersAi ? { inference: PageCaptureInferenceUse.make({
336
+ provider: "cloudflare-workers-ai",
337
+ modelCalls: 1
338
+ }) } : {}
339
+ })
340
+ });
341
+ });
342
+ /**
343
+ * Ordinary Quick Actions require host-owned browser binding authority. Workers
344
+ * AI stays unavailable unless the host deliberately selects its separate Layer.
345
+ */
346
+ const browserQuickActionCaptureLayer = () => Layer.effect(PageCapture, Effect.map(BrowserQuickActionBrowserBinding, (browser) => PageCapture.of({ capture: makeCapture(browser) })));
347
+ /** Structured Quick Actions require host-owned browser and Workers AI authority. */
348
+ const browserQuickActionWorkersAiCaptureLayer = () => Layer.effect(PageCapture, Effect.gen(function* () {
349
+ const browser = yield* BrowserQuickActionBrowserBinding;
350
+ const workersAi = yield* BrowserQuickActionWorkersAi;
351
+ return PageCapture.of({ capture: makeCapture(browser, workersAi) });
352
+ }));
353
+ /** Compose WebCapture handlers with the Cloudflare Quick Action adapter. */
354
+ const CloudflareBrowser = {
355
+ /**
356
+ * Supply a WebCapture definition and the resolved Worker browser binding.
357
+ * Supports capture, scrape, and extraction definitions without importing capabilities.
358
+ * Only PageCapture is provided; other handler requirements and errors stay visible.
359
+ * Extraction fails closed unless workersAi explicitly authorizes and accounts for it.
360
+ * Capture limits, typed failures, tracing, and scoped response cleanup are unchanged.
361
+ */
362
+ layer: (definition, options) => {
363
+ const capture = options.workersAi === void 0 ? browserQuickActionCaptureLayer() : browserQuickActionWorkersAiCaptureLayer().pipe(Layer.provide(BrowserQuickActionWorkersAi.layer(options.workersAi)));
364
+ return definition.handlers.pipe(Layer.provide(capture.pipe(Layer.provide(BrowserQuickActionBrowserBinding.layer(options)))));
365
+ } };
366
+ const screenshotOptions = (request) => {
367
+ const options = {};
368
+ if (request.navigation?.waitUntil !== void 0 || request.navigation?.timeoutMillis !== void 0) options.gotoOptions = {
369
+ ...request.navigation.waitUntil === void 0 ? {} : { waitUntil: request.navigation.waitUntil },
370
+ ...request.navigation.timeoutMillis === void 0 ? {} : { timeout: request.navigation.timeoutMillis }
371
+ };
372
+ if (request.navigation?.waitForSelector !== void 0) options.waitForSelector = {
373
+ selector: request.navigation.waitForSelector.selector,
374
+ ...request.navigation.waitForSelector.timeoutMillis === void 0 ? {} : { timeout: request.navigation.waitForSelector.timeoutMillis }
375
+ };
376
+ if (request.viewport !== void 0) options.viewport = {
377
+ width: request.viewport.width,
378
+ height: request.viewport.height
379
+ };
380
+ if (request.resourcePolicy?.rejectResourceTypes !== void 0) options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];
381
+ if (request.resourcePolicy?.allowRequestPatterns !== void 0) options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];
382
+ return {
383
+ ...options,
384
+ ...request.target._tag === "PageUrlTarget" ? { url: request.target.url } : { html: request.target.html },
385
+ screenshotOptions: {
386
+ type: "png",
387
+ encoding: "binary",
388
+ fullPage: request.fullPage
389
+ }
390
+ };
391
+ };
392
+ const cancelBody = (body) => cancelResponse(() => body.cancel(), "Canceling the screenshot response failed");
393
+ const releaseScreenshotReader = (reader) => cancelResponse(() => reader.cancel(), "Canceling the screenshot response failed").pipe(Effect.ensuring(Effect.try({
394
+ try: () => reader.releaseLock(),
395
+ catch: () => void 0
396
+ }).pipe(Effect.catch(() => Effect.logWarning("Releasing the screenshot response failed")))));
397
+ const pngResponse = (response) => response.headers.get("Content-Type")?.split(";", 1)[0]?.trim().toLowerCase() === "image/png";
398
+ const declaredLength = (response) => {
399
+ const raw = response.headers.get("Content-Length");
400
+ if (raw === null || !/^(0|[1-9][0-9]*)$/.test(raw)) return void 0;
401
+ const length = Number(raw);
402
+ return Number.isSafeInteger(length) ? length : void 0;
403
+ };
404
+ const readScreenshot = Effect.fn("BrowserQuickActionScreenshot.read")(function* (response, request) {
405
+ const body = response.body;
406
+ if (body === null) return yield* protocolError("The screenshot response had no body");
407
+ if (!pngResponse(response)) {
408
+ yield* cancelBody(body);
409
+ return yield* protocolError("The screenshot response was not image/png");
410
+ }
411
+ const length = declaredLength(response);
412
+ if (length !== void 0 && length > request.limits.maxOutputBytes) {
413
+ yield* cancelBody(body);
414
+ return yield* PageScreenshotOutputLimitError.make({
415
+ implementation: browserQuickActionImplementation,
416
+ limit: request.limits.maxOutputBytes,
417
+ observed: length
418
+ });
419
+ }
420
+ const reader = yield* Effect.acquireRelease(Effect.try({
421
+ try: () => body.getReader(),
422
+ catch: (cause) => protocolError("Opening the screenshot response failed", cause)
423
+ }), releaseScreenshotReader);
424
+ const chunks = [];
425
+ let observed = 0;
426
+ while (true) {
427
+ const next = yield* Effect.tryPromise({
428
+ try: () => reader.read(),
429
+ catch: (cause) => protocolError("Reading the screenshot response failed", cause)
430
+ });
431
+ if (next.done) break;
432
+ observed += next.value.byteLength;
433
+ if (observed > request.limits.maxOutputBytes) return yield* PageScreenshotOutputLimitError.make({
434
+ implementation: browserQuickActionImplementation,
435
+ limit: request.limits.maxOutputBytes,
436
+ observed
437
+ });
438
+ chunks.push(next.value);
439
+ }
440
+ const bytes = new Uint8Array(observed);
441
+ let offset = 0;
442
+ for (const chunk of chunks) {
443
+ bytes.set(chunk, offset);
444
+ offset += chunk.byteLength;
445
+ }
446
+ return bytes;
447
+ }, Effect.scoped);
448
+ const makeScreenshot = (browser) => Effect.fn("BrowserQuickActionScreenshot.capture")(function* (request) {
449
+ if (request.engine !== "chromium") return yield* PageCaptureUnsupportedError.make({
450
+ implementation: browserQuickActionImplementation,
451
+ feature: "engine",
452
+ message: "The browser binding's screenshot action exposes no engine selector"
453
+ });
454
+ const response = yield* browser.screenshot(screenshotOptions(request)).pipe(Effect.mapError((error) => protocolError("The browser binding rejected the screenshot", error.cause)));
455
+ if (response.status === 429) {
456
+ const body = response.body;
457
+ if (body !== null) yield* cancelBody(body);
458
+ return yield* PageCaptureRateLimitedError.make({
459
+ implementation: browserQuickActionImplementation,
460
+ reason: "rate",
461
+ ...retryAfterMillis(response) === void 0 ? {} : { retryAfterMillis: retryAfterMillis(response) },
462
+ message: "The screenshot Quick Action was rate limited"
463
+ });
464
+ }
465
+ if (!response.ok) {
466
+ const body = response.body;
467
+ if (body !== null) yield* cancelBody(body);
468
+ const message = `The screenshot Quick Action answered HTTP ${response.status}`;
469
+ return yield* response.status >= 500 ? protocolError(message) : navigationError(message);
470
+ }
471
+ const bytes = yield* readScreenshot(response, request);
472
+ return PageScreenshotResult.make({
473
+ implementation: browserQuickActionImplementation,
474
+ mediaType: "image/png",
475
+ bytes
476
+ });
477
+ });
478
+ /** Native Browser Run screenshot adapter. It retains PNG bytes only for the caller's result. */
479
+ const browserQuickActionScreenshotLayer = () => Layer.effect(PageScreenshot, Effect.map(BrowserQuickActionBrowserBinding, (browser) => PageScreenshot.of({ capture: makeScreenshot(browser) })));
480
+ //#endregion
481
+ //#region src/CloudflareBrowser.ts
482
+ var CloudflareBrowser_exports = /* @__PURE__ */ __exportAll({
483
+ BrowserQuickActionBrowserBinding: () => BrowserQuickActionBrowserBinding,
484
+ BrowserQuickActionRpcError: () => BrowserQuickActionRpcError,
485
+ BrowserQuickActionWorkersAi: () => BrowserQuickActionWorkersAi,
486
+ BrowserQuickActionWorkersAiPolicyError: () => BrowserQuickActionWorkersAiPolicyError,
487
+ CloudflareBrowser: () => CloudflareBrowser,
488
+ browserQuickActionCaptureLayer: () => browserQuickActionCaptureLayer,
489
+ browserQuickActionScreenshotLayer: () => browserQuickActionScreenshotLayer,
490
+ browserQuickActionWorkersAiCaptureLayer: () => browserQuickActionWorkersAiCaptureLayer
491
+ });
492
+ //#endregion
493
+ export { BrowserQuickActionWorkersAiPolicyError as a, browserQuickActionScreenshotLayer as c, BrowserQuickActionWorkersAi as i, browserQuickActionWorkersAiCaptureLayer as l, BrowserQuickActionBrowserBinding as n, CloudflareBrowser as o, BrowserQuickActionRpcError as r, browserQuickActionCaptureLayer as s, CloudflareBrowser_exports as t };
494
+
495
+ //# sourceMappingURL=CloudflareBrowser-DV6kya1H.mjs.map