@effect-agent/platform-cloudflare 0.1.0-beta.39 → 0.1.0-beta.40
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 → bindings-BrVEOkfe.d.mts} +4 -2
- package/dist/browser-quick-action.d.mts +18 -1
- package/dist/browser-quick-action.mjs +14 -1
- package/dist/browser-quick-action.mjs.map +1 -1
- package/dist/browser-rest-capture.d.mts +17 -2
- package/dist/browser-rest-capture.mjs +12 -1
- package/dist/browser-rest-capture.mjs.map +1 -1
- package/dist/browser-rest-crawl.mjs +2 -2
- package/dist/browser-rest-crawl.mjs.map +1 -1
- package/dist/index.d.mts +81 -58
- package/dist/index.mjs +14 -4
- package/dist/index.mjs.map +1 -1
- package/dist/interactive-browser.d.mts +17 -1
- package/dist/interactive-browser.mjs +18 -6
- package/dist/interactive-browser.mjs.map +1 -1
- package/dist/{prepared-admission-D5hQ-ofV.mjs → prepared-admission-BKp_Upw2.mjs} +16 -4
- package/dist/prepared-admission-BKp_Upw2.mjs.map +1 -0
- package/dist/scheduling.d.mts +1 -1
- package/dist/scheduling.mjs +1 -1
- package/dist/subscriptions.d.mts +1 -1
- package/dist/subscriptions.mjs +2 -2
- package/dist/subscriptions.mjs.map +1 -1
- package/package.json +10 -10
- package/src/bindings.ts +5 -1
- package/src/browser-quick-action.ts +31 -0
- package/src/browser-rest-capture.ts +27 -0
- package/src/client.ts +21 -7
- package/src/code-mode-executor.ts +19 -0
- package/src/index.ts +1 -0
- package/src/interactive-browser.ts +29 -1
- package/dist/prepared-admission-D5hQ-ofV.mjs.map +0 -1
|
@@ -57,7 +57,9 @@ declare const ThreadObjectNamespace_base: Context.ServiceClass<ThreadObjectNames
|
|
|
57
57
|
* globally unique, so the mapping is total and deterministic and no directory service exists.
|
|
58
58
|
*/
|
|
59
59
|
declare class ThreadObjectNamespace extends ThreadObjectNamespace_base {
|
|
60
|
-
static layer(namespace: DurableObjectNamespace<ThreadObjectRpc
|
|
60
|
+
static layer(namespace: DurableObjectNamespace<ThreadObjectRpc>, options?: {
|
|
61
|
+
readonly rpcTracing?: string;
|
|
62
|
+
}): Layer.Layer<ThreadObjectNamespace>;
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
65
|
* Narrow one `env` member to a `DurableObjectNamespace`. `env` is an untyped platform value,
|
|
@@ -97,4 +99,4 @@ declare const ThreadObjectIdentity_base: Context.ServiceClass<ThreadObjectIdenti
|
|
|
97
99
|
declare class ThreadObjectIdentity extends ThreadObjectIdentity_base {}
|
|
98
100
|
//#endregion
|
|
99
101
|
export { ThreadObjectRpc as a, ThreadObjectNamespace as i, DurableObjectContext as n, threadNamespaceFromEnv as o, ThreadObjectIdentity as r, threadNamespaceLayer as s, CloudflareBindingError as t };
|
|
100
|
-
//# sourceMappingURL=bindings-
|
|
102
|
+
//# sourceMappingURL=bindings-BrVEOkfe.d.mts.map
|
|
@@ -65,8 +65,25 @@ declare class BrowserQuickActionWorkersAi extends BrowserQuickActionWorkersAi_ba
|
|
|
65
65
|
declare const browserQuickActionCaptureLayer: () => Layer.Layer<PageCapture, never, BrowserQuickActionBrowserBinding>;
|
|
66
66
|
/** Structured Quick Actions require host-owned browser and Workers AI authority. */
|
|
67
67
|
declare const browserQuickActionWorkersAiCaptureLayer: () => Layer.Layer<PageCapture, never, BrowserQuickActionBrowserBinding | BrowserQuickActionWorkersAi>;
|
|
68
|
+
/** Host-owned Quick Action binding and optional, separately billed extraction authority. */
|
|
69
|
+
interface CloudflareBrowserOptions extends BrowserQuickActionCaptureOptions {
|
|
70
|
+
readonly workersAi?: BrowserQuickActionWorkersAiPolicy;
|
|
71
|
+
}
|
|
72
|
+
/** Compose WebCapture handlers with the Cloudflare Quick Action adapter. */
|
|
73
|
+
declare const CloudflareBrowser: {
|
|
74
|
+
/**
|
|
75
|
+
* Supply a WebCapture definition and the resolved Worker browser binding.
|
|
76
|
+
* Supports capture, scrape, and extraction definitions without importing capabilities.
|
|
77
|
+
* Only PageCapture is provided; other handler requirements and errors stay visible.
|
|
78
|
+
* Extraction fails closed unless workersAi explicitly authorizes and accounts for it.
|
|
79
|
+
* Capture limits, typed failures, tracing, and scoped response cleanup are unchanged.
|
|
80
|
+
*/
|
|
81
|
+
layer: <A, E, R>(definition: {
|
|
82
|
+
readonly handlers: Layer.Layer<A, E, R>;
|
|
83
|
+
}, options: CloudflareBrowserOptions) => Layer.Layer<A, E, Exclude<R, PageCapture>>;
|
|
84
|
+
};
|
|
68
85
|
/** Native Browser Run screenshot adapter. It retains PNG bytes only for the caller's result. */
|
|
69
86
|
declare const browserQuickActionScreenshotLayer: () => Layer.Layer<PageScreenshot, never, BrowserQuickActionBrowserBinding>;
|
|
70
87
|
//#endregion
|
|
71
|
-
export { BrowserQuickActionBrowserBinding, BrowserQuickActionCaptureOptions, BrowserQuickActionClient, BrowserQuickActionRpcError, BrowserQuickActionWorkersAi, BrowserQuickActionWorkersAiPolicy, BrowserQuickActionWorkersAiPolicyError, browserQuickActionCaptureLayer, browserQuickActionImplementation, browserQuickActionScreenshotLayer, browserQuickActionWorkersAiCaptureLayer };
|
|
88
|
+
export { BrowserQuickActionBrowserBinding, BrowserQuickActionCaptureOptions, BrowserQuickActionClient, BrowserQuickActionRpcError, BrowserQuickActionWorkersAi, BrowserQuickActionWorkersAiPolicy, BrowserQuickActionWorkersAiPolicyError, CloudflareBrowser, CloudflareBrowserOptions, browserQuickActionCaptureLayer, browserQuickActionImplementation, browserQuickActionScreenshotLayer, browserQuickActionWorkersAiCaptureLayer };
|
|
72
89
|
//# sourceMappingURL=browser-quick-action.d.mts.map
|
|
@@ -313,6 +313,19 @@ const browserQuickActionWorkersAiCaptureLayer = () => Layer.effect(PageCapture,
|
|
|
313
313
|
const workersAi = yield* BrowserQuickActionWorkersAi;
|
|
314
314
|
return PageCapture.of({ capture: makeCapture(browser, workersAi) });
|
|
315
315
|
}));
|
|
316
|
+
/** Compose WebCapture handlers with the Cloudflare Quick Action adapter. */
|
|
317
|
+
const CloudflareBrowser = {
|
|
318
|
+
/**
|
|
319
|
+
* Supply a WebCapture definition and the resolved Worker browser binding.
|
|
320
|
+
* Supports capture, scrape, and extraction definitions without importing capabilities.
|
|
321
|
+
* Only PageCapture is provided; other handler requirements and errors stay visible.
|
|
322
|
+
* Extraction fails closed unless workersAi explicitly authorizes and accounts for it.
|
|
323
|
+
* Capture limits, typed failures, tracing, and scoped response cleanup are unchanged.
|
|
324
|
+
*/
|
|
325
|
+
layer: (definition, options) => {
|
|
326
|
+
const capture = options.workersAi === void 0 ? browserQuickActionCaptureLayer() : browserQuickActionWorkersAiCaptureLayer().pipe(Layer.provide(BrowserQuickActionWorkersAi.layer(options.workersAi)));
|
|
327
|
+
return definition.handlers.pipe(Layer.provide(capture.pipe(Layer.provide(BrowserQuickActionBrowserBinding.layer(options)))));
|
|
328
|
+
} };
|
|
316
329
|
const screenshotOptions = (request) => {
|
|
317
330
|
const options = {};
|
|
318
331
|
if (request.navigation?.waitUntil !== void 0 || request.navigation?.timeoutMillis !== void 0) options.gotoOptions = {
|
|
@@ -434,6 +447,6 @@ const makeScreenshot = (browser) => Effect.fn("BrowserQuickActionScreenshot.capt
|
|
|
434
447
|
/** Native Browser Run screenshot adapter. It retains PNG bytes only for the caller's result. */
|
|
435
448
|
const browserQuickActionScreenshotLayer = () => Layer.effect(PageScreenshot, Effect.map(BrowserQuickActionBrowserBinding, (browser) => PageScreenshot.of({ capture: makeScreenshot(browser) })));
|
|
436
449
|
//#endregion
|
|
437
|
-
export { BrowserQuickActionBrowserBinding, BrowserQuickActionRpcError, BrowserQuickActionWorkersAi, BrowserQuickActionWorkersAiPolicyError, browserQuickActionCaptureLayer, browserQuickActionImplementation, browserQuickActionScreenshotLayer, browserQuickActionWorkersAiCaptureLayer };
|
|
450
|
+
export { BrowserQuickActionBrowserBinding, BrowserQuickActionRpcError, BrowserQuickActionWorkersAi, BrowserQuickActionWorkersAiPolicyError, CloudflareBrowser, browserQuickActionCaptureLayer, browserQuickActionImplementation, browserQuickActionScreenshotLayer, browserQuickActionWorkersAiCaptureLayer };
|
|
438
451
|
|
|
439
452
|
//# sourceMappingURL=browser-quick-action.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-quick-action.mjs","names":[],"sources":["../src/browser-quick-action.ts"],"sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\n\nimport {\n PageCapture,\n PageCaptureInferenceUse,\n PageCaptureInferencePolicyError,\n PageCaptureNavigationError,\n PageCaptureOutputLimitError,\n PageCaptureProtocolError,\n PageCaptureRateLimitedError,\n PageCaptureResourceUse,\n PageCaptureResult,\n PageCaptureUnsupportedError,\n PageScreenshot,\n PageScreenshotOutputLimitError,\n PageScreenshotResult,\n type PageScreenshotCapture,\n type PageScreenshotError,\n type PageScreenshotRequest,\n PageContentCaptured,\n PageLinksCaptured,\n PageMarkdownCaptured,\n PageScrapeCaptured,\n PageStructuredCaptured,\n SandboxImplementation,\n type PageCaptureAction,\n type PageCaptureCapture,\n type PageCaptureError,\n type PageCaptureOutput,\n type PageCaptureRequest,\n} from \"@effect-agent/sandbox\";\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\n\n/**\n * The Cloudflare Browser Run Quick Action `PageCapture` adapter (capability\n * spec §9.2). Each capture is one stateless `quickAction()` RPC on the\n * Wrangler `browser` binding: the platform renders the target in a managed\n * headless browser and returns one bounded output; the adapter holds no\n * session and no state between passes. The binding requires a Worker\n * compatibility date of `2026-03-24` or later, and local `wrangler dev` needs\n * remote mode (`\"remote\": true` on the binding) because `quickAction` has no\n * local implementation.\n *\n * Rendered output is untrusted, attacker-influenced content; this adapter\n * only bounds and types it. Deployment class `E` only: no durability claim.\n */\nexport const browserQuickActionImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-quick-action\",\n});\n\n/**\n * Effect-native client captured by the binding service. Its option types come\n * directly from the pinned Workers declarations rather than a local copy.\n */\nexport interface BrowserQuickActionClient {\n readonly screenshot: (\n options: BrowserRunScreenshotOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly content: (\n options: BrowserRunContentOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly markdown: (\n options: BrowserRunMarkdownOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly links: (\n options: BrowserRunLinksOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly scrape: (\n options: BrowserRunScrapeOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly json: (\n options: BrowserRunJsonOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n}\n\n/** A native binding RPC rejected before it returned an HTTP response. */\nexport class BrowserQuickActionRpcError extends Schema.TaggedError<BrowserQuickActionRpcError>()(\n \"BrowserQuickActionRpcError\",\n {\n action: Schema.Literals([\"screenshot\", \"content\", \"markdown\", \"links\", \"scrape\", \"json\"]),\n cause: Schema.Defect(),\n },\n) {}\n\nexport interface BrowserQuickActionCaptureOptions {\n /** The resolved Wrangler `browser` binding (DEPLOY-014: supplied, never ambient). */\n readonly browser: BrowserRun;\n}\n\n/** Host-owned browser binding authority, supplied explicitly at the composition root. */\nexport class BrowserQuickActionBrowserBinding extends Context.Service<\n BrowserQuickActionBrowserBinding,\n BrowserQuickActionClient\n>()(\"@effect-agent/platform-cloudflare/BrowserQuickActionBrowserBinding\") {\n static layer(\n options: BrowserQuickActionCaptureOptions,\n ): Layer.Layer<BrowserQuickActionBrowserBinding> {\n const browser = options.browser;\n const invoke = Effect.fn(\"BrowserQuickActionBrowserBinding.invoke\")(function* (\n action: \"screenshot\" | \"content\" | \"markdown\" | \"links\" | \"scrape\" | \"json\",\n evaluate: () => Promise<Response>,\n ): Effect.fn.Return<Response, BrowserQuickActionRpcError> {\n return yield* Effect.tryPromise({\n try: evaluate,\n catch: (cause) => BrowserQuickActionRpcError.make({ action, cause }),\n });\n });\n return Layer.succeed(BrowserQuickActionBrowserBinding)({\n screenshot: (request) =>\n invoke(\"screenshot\", () => browser.quickAction(\"screenshot\", request)),\n content: (request) => invoke(\"content\", () => browser.quickAction(\"content\", request)),\n markdown: (request) => invoke(\"markdown\", () => browser.quickAction(\"markdown\", request)),\n links: (request) => invoke(\"links\", () => browser.quickAction(\"links\", request)),\n scrape: (request) => invoke(\"scrape\", () => browser.quickAction(\"scrape\", request)),\n json: (request) => invoke(\"json\", () => browser.quickAction(\"json\", request)),\n });\n }\n}\n\n/** Host-owned authorization and accounting for one Workers AI extraction. */\nexport interface BrowserQuickActionWorkersAiPolicy {\n readonly authorizeAndAccount: (\n request: PageCaptureRequest,\n ) => Effect.Effect<void, BrowserQuickActionWorkersAiPolicyError>;\n}\n\n/** Host-only diagnostic for a denied or unaccounted Workers AI extraction. */\nexport class BrowserQuickActionWorkersAiPolicyError extends Schema.TaggedError<BrowserQuickActionWorkersAiPolicyError>()(\n \"BrowserQuickActionWorkersAiPolicyError\",\n {\n reason: Schema.Literals([\"authorization\", \"accounting\"]),\n message: Schema.String.check(Schema.isMaxLength(8_000)),\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\n/** Explicit host-owned authority and accounting for separately billed Workers AI extraction. */\nexport class BrowserQuickActionWorkersAi extends Context.Service<\n BrowserQuickActionWorkersAi,\n BrowserQuickActionWorkersAiPolicy\n>()(\"@effect-agent/platform-cloudflare/BrowserQuickActionWorkersAi\") {\n static layer(\n policy: BrowserQuickActionWorkersAiPolicy,\n ): Layer.Layer<BrowserQuickActionWorkersAi> {\n return Layer.succeed(BrowserQuickActionWorkersAi)(policy);\n }\n}\n\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\nconst QuickActionSuccessEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Json,\n});\n\nconst QuickActionErrorEnvelope = Schema.Struct({\n success: Schema.Literal(false),\n errors: Schema.Array(\n Schema.Struct({\n message: Schema.String,\n code: Schema.optionalKey(Schema.Number),\n detail: Schema.optionalKey(Schema.String),\n path: Schema.optionalKey(Schema.String),\n }),\n ),\n rawAiResponse: Schema.optionalKey(Schema.String),\n});\n\nconst QuickActionEnvelope = Schema.Union([QuickActionSuccessEnvelope, QuickActionErrorEnvelope]);\nconst decodeEnvelope = Schema.decodeUnknownOption(Schema.fromJsonString(QuickActionEnvelope));\n\n/** Project the schema-validated request onto Cloudflare's native common options. */\nconst quickActionCommonOptions = (request: PageCaptureRequest): BrowserRunCommonOptions => {\n const options: BrowserRunBaseOptions = {};\n const navigation = request.navigation;\n if (navigation !== undefined) {\n const goto: NonNullable<BrowserRunBaseOptions[\"gotoOptions\"]> = {};\n if (navigation.waitUntil !== undefined) goto.waitUntil = navigation.waitUntil;\n if (navigation.timeoutMillis !== undefined) goto.timeout = navigation.timeoutMillis;\n if (Object.keys(goto).length > 0) options.gotoOptions = goto;\n if (navigation.waitForSelector !== undefined) {\n options.waitForSelector = {\n selector: navigation.waitForSelector.selector,\n ...(navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: navigation.waitForSelector.timeoutMillis }),\n };\n }\n }\n if (request.viewport !== undefined) {\n options.viewport = { width: request.viewport.width, height: request.viewport.height };\n }\n if (request.resourcePolicy !== undefined) {\n if (request.resourcePolicy.rejectResourceTypes !== undefined) {\n options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy.allowRequestPatterns !== undefined) {\n options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n }\n return request.target._tag === \"PageUrlTarget\"\n ? { ...options, url: request.target.url }\n : { ...options, html: request.target.html };\n};\n\n/** Dispatch through Cloudflare's native action-specific overloads. */\nconst executeQuickAction = (\n browser: BrowserQuickActionClient,\n request: PageCaptureRequest,\n): Effect.Effect<Response, BrowserQuickActionRpcError> => {\n const options = quickActionCommonOptions(request);\n switch (request.action._tag) {\n case \"CapturePageContent\": {\n return browser.content(options);\n }\n case \"CapturePageMarkdown\": {\n return browser.markdown(options);\n }\n case \"CapturePageLinks\": {\n return browser.links({\n ...options,\n ...(request.action.visibleLinksOnly === undefined\n ? {}\n : { visibleLinksOnly: request.action.visibleLinksOnly }),\n });\n }\n case \"CapturePageScrape\": {\n return browser.scrape({\n ...options,\n elements: request.action.selectors.map((selector) => ({ selector })),\n });\n }\n case \"CapturePageStructured\": {\n return browser.json({\n ...options,\n response_format: {\n type: \"json_schema\",\n json_schema: request.action.responseFormat,\n },\n ...(request.action.prompt === undefined ? {} : { prompt: request.action.prompt }),\n });\n }\n }\n};\n\nconst protocolError = (message: string, cause?: unknown): PageCaptureProtocolError =>\n PageCaptureProtocolError.make({\n implementation: browserQuickActionImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst navigationError = (message: string, cause?: unknown): PageCaptureNavigationError =>\n PageCaptureNavigationError.make({\n implementation: browserQuickActionImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\n/** Preserve bounded remote diagnostics for the host without exposing their text to a model. */\nconst privateResponseCause = (bodyText: string): Error | undefined =>\n bodyText.length === 0 ? undefined : new Error(boundedDiagnostic(bodyText));\n\nconst releaseResponseReader = (\n reader: ReadableStreamDefaultReader<Uint8Array>,\n): Effect.Effect<void> =>\n Effect.tryPromise({\n try: () => reader.cancel(),\n catch: (cause) => protocolError(\"Canceling the Quick Action response failed\", cause),\n }).pipe(\n Effect.catch((error) => Effect.logWarning(error.message)),\n Effect.ensuring(\n Effect.try({\n try: () => reader.releaseLock(),\n catch: (cause) => protocolError(\"Releasing the Quick Action response failed\", cause),\n }).pipe(Effect.catch((error) => Effect.logWarning(error.message))),\n ),\n );\n\nconst readBoundedResponse = Effect.fn(\"BrowserQuickActionCapture.readResponse\")(function* (\n response: Response,\n request: PageCaptureRequest,\n) {\n const body = response.body;\n if (body === null) return \"\";\n\n const reader = yield* Effect.acquireRelease(\n Effect.try({\n try: () => body.getReader(),\n catch: (cause) => protocolError(\"Opening the Quick Action response failed\", cause),\n }),\n releaseResponseReader,\n );\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n let observedBytes = 0;\n let bodyText = \"\";\n\n while (true) {\n const chunk = yield* Effect.tryPromise({\n try: () => reader.read(),\n catch: (cause) => protocolError(\"Reading the Quick Action response failed\", cause),\n });\n if (chunk.done) break;\n\n observedBytes += chunk.value.byteLength;\n if (observedBytes > request.limits.maxOutputBytes) {\n return yield* PageCaptureOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed: observedBytes,\n });\n }\n\n bodyText += yield* Effect.try({\n try: () => decoder.decode(chunk.value, { stream: true }),\n catch: (cause) => protocolError(\"Decoding the Quick Action response failed\", cause),\n });\n }\n\n return (\n bodyText +\n (yield* Effect.try({\n try: () => decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Quick Action response failed\", cause),\n }))\n );\n}, Effect.scoped);\n\n/**\n * Retry-After arrives in whole seconds; a non-integer form (an HTTP date) is\n * dropped rather than guessed at.\n */\nconst retryAfterMillis = (response: Response): number | undefined => {\n const header = response.headers.get(\"Retry-After\");\n if (header === null) return undefined;\n const seconds = Number(header);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst browserMillis = (response: Response): number | undefined => {\n const header = response.headers.get(\"X-Browser-Ms-Used\");\n if (header === null) return undefined;\n const millis = Number(header);\n return Number.isSafeInteger(millis) && millis >= 0 ? millis : undefined;\n};\n\n/** Only trusted response metadata chooses transport framing; page text never does. */\nconst isJsonResponse = (response: Response): boolean => {\n const contentType = response.headers.get(\"Content-Type\");\n if (contentType === null) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst parseOutput = (\n action: PageCaptureAction,\n bodyText: string,\n response: Response,\n): PageCaptureOutput | PageCaptureNavigationError | PageCaptureProtocolError => {\n if (!isJsonResponse(response)) {\n return protocolError(\n \"The Quick Action success response was not a JSON response envelope\",\n privateResponseCause(bodyText),\n );\n }\n const envelope = decodeEnvelope(bodyText);\n if (Option.isNone(envelope)) {\n return protocolError(\n \"The JSON Quick Action response did not carry a valid response envelope\",\n privateResponseCause(bodyText),\n );\n }\n if (!envelope.value.success) {\n return navigationError(\n \"The Quick Action reported a navigation failure\",\n privateResponseCause(bodyText),\n );\n }\n switch (action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\": {\n if (typeof envelope.value.result !== \"string\") {\n return protocolError(\"The Quick Action envelope carried a non-text result\");\n }\n return action._tag === \"CapturePageContent\"\n ? PageContentCaptured.make({ html: envelope.value.result })\n : PageMarkdownCaptured.make({ markdown: envelope.value.result });\n }\n case \"CapturePageLinks\": {\n const decoded = Schema.decodeUnknownOption(PageLinksCaptured)({\n _tag: \"PageLinksCaptured\",\n links: envelope.value.result,\n });\n if (Option.isNone(decoded)) {\n return protocolError(\"The links Quick Action did not return a bounded array of valid URLs\");\n }\n return decoded.value;\n }\n case \"CapturePageScrape\": {\n const decoded = Schema.decodeUnknownOption(PageScrapeCaptured)({\n _tag: \"PageScrapeCaptured\",\n groups: envelope.value.result,\n });\n if (Option.isNone(decoded)) {\n return protocolError(\n \"The scrape Quick Action did not return bounded grouped element records\",\n );\n }\n return decoded.value;\n }\n case \"CapturePageStructured\": {\n return PageStructuredCaptured.make({ value: envelope.value.result });\n }\n }\n};\n\nconst isQuotaMessage = (text: string): boolean => /time limit|daily|quota/i.test(text);\n\nconst makeCapture = (\n browser: BrowserQuickActionClient,\n workersAi?: BrowserQuickActionWorkersAiPolicy,\n): PageCaptureCapture =>\n Effect.fn(\"BrowserQuickActionCapture.capture\")(function* (\n request: PageCaptureRequest,\n ): Effect.fn.Return<PageCaptureResult, PageCaptureError> {\n if (request.engine !== \"chromium\") {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"engine\",\n message:\n \"The browser binding's quickAction() exposes no engine selector; kitesurf requires the REST or CDP surface\",\n });\n }\n\n const usesWorkersAi = request.action._tag === \"CapturePageStructured\";\n if (usesWorkersAi) {\n if (workersAi === undefined) {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"action\",\n message:\n \"Structured capture invokes separately billed Workers AI and requires an explicit authorization and accounting policy\",\n });\n }\n yield* workersAi.authorizeAndAccount(request).pipe(\n Effect.mapError((cause) =>\n PageCaptureInferencePolicyError.make({\n implementation: browserQuickActionImplementation,\n provider: \"cloudflare-workers-ai\",\n reason: cause.reason,\n message:\n cause.reason === \"authorization\"\n ? \"Workers AI extraction was not authorized\"\n : \"Workers AI extraction could not be accounted for\",\n cause,\n }),\n ),\n );\n }\n\n const response = yield* executeQuickAction(browser, request).pipe(\n Effect.mapError((error) =>\n protocolError(\"The browser binding rejected the Quick Action\", error.cause),\n ),\n );\n const bodyText = yield* readBoundedResponse(response, request);\n if (response.status === 429) {\n const retryAfter = retryAfterMillis(response);\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n const cause = privateResponseCause(bodyText);\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserQuickActionImplementation,\n reason,\n ...(retryAfter === undefined ? {} : { retryAfterMillis: retryAfter }),\n ...(cause === undefined ? {} : { cause }),\n message:\n reason === \"quota\"\n ? \"The Quick Action exceeded its browser quota\"\n : \"The Quick Action was rate limited\",\n });\n }\n if (!response.ok) {\n const message = `The Quick Action answered HTTP ${response.status}`;\n const cause = privateResponseCause(bodyText);\n if (response.status >= 500) {\n return yield* protocolError(message, cause);\n }\n return yield* navigationError(message, cause);\n }\n const output = parseOutput(request.action, bodyText, response);\n if (\n output._tag === \"PageCaptureNavigationError\" ||\n output._tag === \"PageCaptureProtocolError\"\n ) {\n return yield* output;\n }\n const millis = browserMillis(response);\n return PageCaptureResult.make({\n implementation: browserQuickActionImplementation,\n output,\n resourceUse: PageCaptureResourceUse.make({\n ...(millis === undefined ? {} : { browserMillis: millis }),\n ...(usesWorkersAi\n ? {\n inference: PageCaptureInferenceUse.make({\n provider: \"cloudflare-workers-ai\",\n modelCalls: 1,\n }),\n }\n : {}),\n }),\n });\n });\n\n/**\n * Ordinary Quick Actions require host-owned browser binding authority. Workers\n * AI stays unavailable unless the host deliberately selects its separate Layer.\n */\nexport const browserQuickActionCaptureLayer = (): Layer.Layer<\n PageCapture,\n never,\n BrowserQuickActionBrowserBinding\n> =>\n Layer.effect(\n PageCapture,\n Effect.map(BrowserQuickActionBrowserBinding, (browser) =>\n PageCapture.of({ capture: makeCapture(browser) }),\n ),\n );\n\n/** Structured Quick Actions require host-owned browser and Workers AI authority. */\nexport const browserQuickActionWorkersAiCaptureLayer = (): Layer.Layer<\n PageCapture,\n never,\n BrowserQuickActionBrowserBinding | BrowserQuickActionWorkersAi\n> =>\n Layer.effect(\n PageCapture,\n Effect.gen(function* () {\n const browser = yield* BrowserQuickActionBrowserBinding;\n const workersAi = yield* BrowserQuickActionWorkersAi;\n return PageCapture.of({ capture: makeCapture(browser, workersAi) });\n }),\n );\n\nconst screenshotOptions = (request: PageScreenshotRequest): BrowserRunScreenshotOptions => {\n const options: BrowserRunBaseOptions = {};\n if (\n request.navigation?.waitUntil !== undefined ||\n request.navigation?.timeoutMillis !== undefined\n ) {\n options.gotoOptions = {\n ...(request.navigation.waitUntil === undefined\n ? {}\n : { waitUntil: request.navigation.waitUntil }),\n ...(request.navigation.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.timeoutMillis }),\n };\n }\n if (request.navigation?.waitForSelector !== undefined) {\n options.waitForSelector = {\n selector: request.navigation.waitForSelector.selector,\n ...(request.navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.waitForSelector.timeoutMillis }),\n };\n }\n if (request.viewport !== undefined) {\n options.viewport = { width: request.viewport.width, height: request.viewport.height };\n }\n if (request.resourcePolicy?.rejectResourceTypes !== undefined) {\n options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy?.allowRequestPatterns !== undefined) {\n options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n return {\n ...options,\n ...(request.target._tag === \"PageUrlTarget\"\n ? { url: request.target.url }\n : { html: request.target.html }),\n screenshotOptions: { type: \"png\", encoding: \"binary\", fullPage: request.fullPage },\n };\n};\n\nconst cancelBody = (body: ReadableStream<Uint8Array>): Effect.Effect<void> =>\n Effect.tryPromise({\n try: () => body.cancel(),\n catch: () => undefined,\n }).pipe(Effect.catch(() => Effect.void));\n\nconst releaseScreenshotReader = (\n reader: ReadableStreamDefaultReader<Uint8Array>,\n): Effect.Effect<void> =>\n Effect.tryPromise({ try: () => reader.cancel(), catch: () => undefined }).pipe(\n Effect.catch(() => Effect.logWarning(\"Canceling the screenshot response failed\")),\n Effect.ensuring(\n Effect.try({\n try: () => reader.releaseLock(),\n catch: () => undefined,\n }).pipe(Effect.catch(() => Effect.logWarning(\"Releasing the screenshot response failed\"))),\n ),\n );\n\nconst pngResponse = (response: Response): boolean =>\n response.headers.get(\"Content-Type\")?.split(\";\", 1)[0]?.trim().toLowerCase() === \"image/png\";\n\nconst declaredLength = (response: Response): number | undefined => {\n const raw = response.headers.get(\"Content-Length\");\n if (raw === null || !/^(0|[1-9][0-9]*)$/.test(raw)) return undefined;\n const length = Number(raw);\n return Number.isSafeInteger(length) ? length : undefined;\n};\n\nconst readScreenshot = Effect.fn(\"BrowserQuickActionScreenshot.read\")(function* (\n response: Response,\n request: PageScreenshotRequest,\n) {\n const body = response.body;\n if (body === null) {\n return yield* protocolError(\"The screenshot response had no body\");\n }\n if (!pngResponse(response)) {\n yield* cancelBody(body);\n return yield* protocolError(\"The screenshot response was not image/png\");\n }\n const length = declaredLength(response);\n if (length !== undefined && length > request.limits.maxOutputBytes) {\n yield* cancelBody(body);\n return yield* PageScreenshotOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed: length,\n });\n }\n const reader = yield* Effect.acquireRelease(\n Effect.try({\n try: () => body.getReader(),\n catch: (cause) => protocolError(\"Opening the screenshot response failed\", cause),\n }),\n releaseScreenshotReader,\n );\n const chunks: Array<Uint8Array> = [];\n let observed = 0;\n while (true) {\n const next = yield* Effect.tryPromise({\n try: () => reader.read(),\n catch: (cause) => protocolError(\"Reading the screenshot response failed\", cause),\n });\n if (next.done) break;\n observed += next.value.byteLength;\n if (observed > request.limits.maxOutputBytes) {\n return yield* PageScreenshotOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed,\n });\n }\n chunks.push(next.value);\n }\n const bytes = new Uint8Array(observed);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n}, Effect.scoped);\n\nconst makeScreenshot = (browser: BrowserQuickActionClient): PageScreenshotCapture =>\n Effect.fn(\"BrowserQuickActionScreenshot.capture\")(function* (\n request: PageScreenshotRequest,\n ): Effect.fn.Return<PageScreenshotResult, PageScreenshotError> {\n if (request.engine !== \"chromium\") {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"engine\",\n message: \"The browser binding's screenshot action exposes no engine selector\",\n });\n }\n const response = yield* browser\n .screenshot(screenshotOptions(request))\n .pipe(\n Effect.mapError((error) =>\n protocolError(\"The browser binding rejected the screenshot\", error.cause),\n ),\n );\n if (response.status === 429) {\n const body = response.body;\n if (body !== null) yield* cancelBody(body);\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserQuickActionImplementation,\n reason: \"rate\",\n ...(retryAfterMillis(response) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response) }),\n message: \"The screenshot Quick Action was rate limited\",\n });\n }\n if (!response.ok) {\n const body = response.body;\n if (body !== null) yield* cancelBody(body);\n const message = `The screenshot Quick Action answered HTTP ${response.status}`;\n return yield* response.status >= 500 ? protocolError(message) : navigationError(message);\n }\n const bytes = yield* readScreenshot(response, request);\n return PageScreenshotResult.make({\n implementation: browserQuickActionImplementation,\n mediaType: \"image/png\",\n bytes,\n });\n });\n\n/** Native Browser Run screenshot adapter. It retains PNG bytes only for the caller's result. */\nexport const browserQuickActionScreenshotLayer = (): Layer.Layer<\n PageScreenshot,\n never,\n BrowserQuickActionBrowserBinding\n> =>\n Layer.effect(\n PageScreenshot,\n Effect.map(BrowserQuickActionBrowserBinding, (browser) =>\n PageScreenshot.of({ capture: makeScreenshot(browser) }),\n ),\n );\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser-quick-action.mjs","names":[],"sources":["../src/browser-quick-action.ts"],"sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\n\nimport {\n PageCapture,\n PageCaptureInferenceUse,\n PageCaptureInferencePolicyError,\n PageCaptureNavigationError,\n PageCaptureOutputLimitError,\n PageCaptureProtocolError,\n PageCaptureRateLimitedError,\n PageCaptureResourceUse,\n PageCaptureResult,\n PageCaptureUnsupportedError,\n PageScreenshot,\n PageScreenshotOutputLimitError,\n PageScreenshotResult,\n type PageScreenshotCapture,\n type PageScreenshotError,\n type PageScreenshotRequest,\n PageContentCaptured,\n PageLinksCaptured,\n PageMarkdownCaptured,\n PageScrapeCaptured,\n PageStructuredCaptured,\n SandboxImplementation,\n type PageCaptureAction,\n type PageCaptureCapture,\n type PageCaptureError,\n type PageCaptureOutput,\n type PageCaptureRequest,\n} from \"@effect-agent/sandbox\";\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\n\n/**\n * The Cloudflare Browser Run Quick Action `PageCapture` adapter (capability\n * spec §9.2). Each capture is one stateless `quickAction()` RPC on the\n * Wrangler `browser` binding: the platform renders the target in a managed\n * headless browser and returns one bounded output; the adapter holds no\n * session and no state between passes. The binding requires a Worker\n * compatibility date of `2026-03-24` or later, and local `wrangler dev` needs\n * remote mode (`\"remote\": true` on the binding) because `quickAction` has no\n * local implementation.\n *\n * Rendered output is untrusted, attacker-influenced content; this adapter\n * only bounds and types it. Deployment class `E` only: no durability claim.\n */\nexport const browserQuickActionImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-quick-action\",\n});\n\n/**\n * Effect-native client captured by the binding service. Its option types come\n * directly from the pinned Workers declarations rather than a local copy.\n */\nexport interface BrowserQuickActionClient {\n readonly screenshot: (\n options: BrowserRunScreenshotOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly content: (\n options: BrowserRunContentOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly markdown: (\n options: BrowserRunMarkdownOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly links: (\n options: BrowserRunLinksOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly scrape: (\n options: BrowserRunScrapeOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n readonly json: (\n options: BrowserRunJsonOptions,\n ) => Effect.Effect<Response, BrowserQuickActionRpcError>;\n}\n\n/** A native binding RPC rejected before it returned an HTTP response. */\nexport class BrowserQuickActionRpcError extends Schema.TaggedError<BrowserQuickActionRpcError>()(\n \"BrowserQuickActionRpcError\",\n {\n action: Schema.Literals([\"screenshot\", \"content\", \"markdown\", \"links\", \"scrape\", \"json\"]),\n cause: Schema.Defect(),\n },\n) {}\n\nexport interface BrowserQuickActionCaptureOptions {\n /** The resolved Wrangler `browser` binding (DEPLOY-014: supplied, never ambient). */\n readonly browser: BrowserRun;\n}\n\n/** Host-owned browser binding authority, supplied explicitly at the composition root. */\nexport class BrowserQuickActionBrowserBinding extends Context.Service<\n BrowserQuickActionBrowserBinding,\n BrowserQuickActionClient\n>()(\"@effect-agent/platform-cloudflare/BrowserQuickActionBrowserBinding\") {\n static layer(\n options: BrowserQuickActionCaptureOptions,\n ): Layer.Layer<BrowserQuickActionBrowserBinding> {\n const browser = options.browser;\n const invoke = Effect.fn(\"BrowserQuickActionBrowserBinding.invoke\")(function* (\n action: \"screenshot\" | \"content\" | \"markdown\" | \"links\" | \"scrape\" | \"json\",\n evaluate: () => Promise<Response>,\n ): Effect.fn.Return<Response, BrowserQuickActionRpcError> {\n return yield* Effect.tryPromise({\n try: evaluate,\n catch: (cause) => BrowserQuickActionRpcError.make({ action, cause }),\n });\n });\n return Layer.succeed(BrowserQuickActionBrowserBinding)({\n screenshot: (request) =>\n invoke(\"screenshot\", () => browser.quickAction(\"screenshot\", request)),\n content: (request) => invoke(\"content\", () => browser.quickAction(\"content\", request)),\n markdown: (request) => invoke(\"markdown\", () => browser.quickAction(\"markdown\", request)),\n links: (request) => invoke(\"links\", () => browser.quickAction(\"links\", request)),\n scrape: (request) => invoke(\"scrape\", () => browser.quickAction(\"scrape\", request)),\n json: (request) => invoke(\"json\", () => browser.quickAction(\"json\", request)),\n });\n }\n}\n\n/** Host-owned authorization and accounting for one Workers AI extraction. */\nexport interface BrowserQuickActionWorkersAiPolicy {\n readonly authorizeAndAccount: (\n request: PageCaptureRequest,\n ) => Effect.Effect<void, BrowserQuickActionWorkersAiPolicyError>;\n}\n\n/** Host-only diagnostic for a denied or unaccounted Workers AI extraction. */\nexport class BrowserQuickActionWorkersAiPolicyError extends Schema.TaggedError<BrowserQuickActionWorkersAiPolicyError>()(\n \"BrowserQuickActionWorkersAiPolicyError\",\n {\n reason: Schema.Literals([\"authorization\", \"accounting\"]),\n message: Schema.String.check(Schema.isMaxLength(8_000)),\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\n/** Explicit host-owned authority and accounting for separately billed Workers AI extraction. */\nexport class BrowserQuickActionWorkersAi extends Context.Service<\n BrowserQuickActionWorkersAi,\n BrowserQuickActionWorkersAiPolicy\n>()(\"@effect-agent/platform-cloudflare/BrowserQuickActionWorkersAi\") {\n static layer(\n policy: BrowserQuickActionWorkersAiPolicy,\n ): Layer.Layer<BrowserQuickActionWorkersAi> {\n return Layer.succeed(BrowserQuickActionWorkersAi)(policy);\n }\n}\n\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\nconst QuickActionSuccessEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Json,\n});\n\nconst QuickActionErrorEnvelope = Schema.Struct({\n success: Schema.Literal(false),\n errors: Schema.Array(\n Schema.Struct({\n message: Schema.String,\n code: Schema.optionalKey(Schema.Number),\n detail: Schema.optionalKey(Schema.String),\n path: Schema.optionalKey(Schema.String),\n }),\n ),\n rawAiResponse: Schema.optionalKey(Schema.String),\n});\n\nconst QuickActionEnvelope = Schema.Union([QuickActionSuccessEnvelope, QuickActionErrorEnvelope]);\nconst decodeEnvelope = Schema.decodeUnknownOption(Schema.fromJsonString(QuickActionEnvelope));\n\n/** Project the schema-validated request onto Cloudflare's native common options. */\nconst quickActionCommonOptions = (request: PageCaptureRequest): BrowserRunCommonOptions => {\n const options: BrowserRunBaseOptions = {};\n const navigation = request.navigation;\n if (navigation !== undefined) {\n const goto: NonNullable<BrowserRunBaseOptions[\"gotoOptions\"]> = {};\n if (navigation.waitUntil !== undefined) goto.waitUntil = navigation.waitUntil;\n if (navigation.timeoutMillis !== undefined) goto.timeout = navigation.timeoutMillis;\n if (Object.keys(goto).length > 0) options.gotoOptions = goto;\n if (navigation.waitForSelector !== undefined) {\n options.waitForSelector = {\n selector: navigation.waitForSelector.selector,\n ...(navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: navigation.waitForSelector.timeoutMillis }),\n };\n }\n }\n if (request.viewport !== undefined) {\n options.viewport = { width: request.viewport.width, height: request.viewport.height };\n }\n if (request.resourcePolicy !== undefined) {\n if (request.resourcePolicy.rejectResourceTypes !== undefined) {\n options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy.allowRequestPatterns !== undefined) {\n options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n }\n return request.target._tag === \"PageUrlTarget\"\n ? { ...options, url: request.target.url }\n : { ...options, html: request.target.html };\n};\n\n/** Dispatch through Cloudflare's native action-specific overloads. */\nconst executeQuickAction = (\n browser: BrowserQuickActionClient,\n request: PageCaptureRequest,\n): Effect.Effect<Response, BrowserQuickActionRpcError> => {\n const options = quickActionCommonOptions(request);\n switch (request.action._tag) {\n case \"CapturePageContent\": {\n return browser.content(options);\n }\n case \"CapturePageMarkdown\": {\n return browser.markdown(options);\n }\n case \"CapturePageLinks\": {\n return browser.links({\n ...options,\n ...(request.action.visibleLinksOnly === undefined\n ? {}\n : { visibleLinksOnly: request.action.visibleLinksOnly }),\n });\n }\n case \"CapturePageScrape\": {\n return browser.scrape({\n ...options,\n elements: request.action.selectors.map((selector) => ({ selector })),\n });\n }\n case \"CapturePageStructured\": {\n return browser.json({\n ...options,\n response_format: {\n type: \"json_schema\",\n json_schema: request.action.responseFormat,\n },\n ...(request.action.prompt === undefined ? {} : { prompt: request.action.prompt }),\n });\n }\n }\n};\n\nconst protocolError = (message: string, cause?: unknown): PageCaptureProtocolError =>\n PageCaptureProtocolError.make({\n implementation: browserQuickActionImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst navigationError = (message: string, cause?: unknown): PageCaptureNavigationError =>\n PageCaptureNavigationError.make({\n implementation: browserQuickActionImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\n/** Preserve bounded remote diagnostics for the host without exposing their text to a model. */\nconst privateResponseCause = (bodyText: string): Error | undefined =>\n bodyText.length === 0 ? undefined : new Error(boundedDiagnostic(bodyText));\n\nconst releaseResponseReader = (\n reader: ReadableStreamDefaultReader<Uint8Array>,\n): Effect.Effect<void> =>\n Effect.tryPromise({\n try: () => reader.cancel(),\n catch: (cause) => protocolError(\"Canceling the Quick Action response failed\", cause),\n }).pipe(\n Effect.catch((error) => Effect.logWarning(error.message)),\n Effect.ensuring(\n Effect.try({\n try: () => reader.releaseLock(),\n catch: (cause) => protocolError(\"Releasing the Quick Action response failed\", cause),\n }).pipe(Effect.catch((error) => Effect.logWarning(error.message))),\n ),\n );\n\nconst readBoundedResponse = Effect.fn(\"BrowserQuickActionCapture.readResponse\")(function* (\n response: Response,\n request: PageCaptureRequest,\n) {\n const body = response.body;\n if (body === null) return \"\";\n\n const reader = yield* Effect.acquireRelease(\n Effect.try({\n try: () => body.getReader(),\n catch: (cause) => protocolError(\"Opening the Quick Action response failed\", cause),\n }),\n releaseResponseReader,\n );\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n let observedBytes = 0;\n let bodyText = \"\";\n\n while (true) {\n const chunk = yield* Effect.tryPromise({\n try: () => reader.read(),\n catch: (cause) => protocolError(\"Reading the Quick Action response failed\", cause),\n });\n if (chunk.done) break;\n\n observedBytes += chunk.value.byteLength;\n if (observedBytes > request.limits.maxOutputBytes) {\n return yield* PageCaptureOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed: observedBytes,\n });\n }\n\n bodyText += yield* Effect.try({\n try: () => decoder.decode(chunk.value, { stream: true }),\n catch: (cause) => protocolError(\"Decoding the Quick Action response failed\", cause),\n });\n }\n\n return (\n bodyText +\n (yield* Effect.try({\n try: () => decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Quick Action response failed\", cause),\n }))\n );\n}, Effect.scoped);\n\n/**\n * Retry-After arrives in whole seconds; a non-integer form (an HTTP date) is\n * dropped rather than guessed at.\n */\nconst retryAfterMillis = (response: Response): number | undefined => {\n const header = response.headers.get(\"Retry-After\");\n if (header === null) return undefined;\n const seconds = Number(header);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst browserMillis = (response: Response): number | undefined => {\n const header = response.headers.get(\"X-Browser-Ms-Used\");\n if (header === null) return undefined;\n const millis = Number(header);\n return Number.isSafeInteger(millis) && millis >= 0 ? millis : undefined;\n};\n\n/** Only trusted response metadata chooses transport framing; page text never does. */\nconst isJsonResponse = (response: Response): boolean => {\n const contentType = response.headers.get(\"Content-Type\");\n if (contentType === null) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst parseOutput = (\n action: PageCaptureAction,\n bodyText: string,\n response: Response,\n): PageCaptureOutput | PageCaptureNavigationError | PageCaptureProtocolError => {\n if (!isJsonResponse(response)) {\n return protocolError(\n \"The Quick Action success response was not a JSON response envelope\",\n privateResponseCause(bodyText),\n );\n }\n const envelope = decodeEnvelope(bodyText);\n if (Option.isNone(envelope)) {\n return protocolError(\n \"The JSON Quick Action response did not carry a valid response envelope\",\n privateResponseCause(bodyText),\n );\n }\n if (!envelope.value.success) {\n return navigationError(\n \"The Quick Action reported a navigation failure\",\n privateResponseCause(bodyText),\n );\n }\n switch (action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\": {\n if (typeof envelope.value.result !== \"string\") {\n return protocolError(\"The Quick Action envelope carried a non-text result\");\n }\n return action._tag === \"CapturePageContent\"\n ? PageContentCaptured.make({ html: envelope.value.result })\n : PageMarkdownCaptured.make({ markdown: envelope.value.result });\n }\n case \"CapturePageLinks\": {\n const decoded = Schema.decodeUnknownOption(PageLinksCaptured)({\n _tag: \"PageLinksCaptured\",\n links: envelope.value.result,\n });\n if (Option.isNone(decoded)) {\n return protocolError(\"The links Quick Action did not return a bounded array of valid URLs\");\n }\n return decoded.value;\n }\n case \"CapturePageScrape\": {\n const decoded = Schema.decodeUnknownOption(PageScrapeCaptured)({\n _tag: \"PageScrapeCaptured\",\n groups: envelope.value.result,\n });\n if (Option.isNone(decoded)) {\n return protocolError(\n \"The scrape Quick Action did not return bounded grouped element records\",\n );\n }\n return decoded.value;\n }\n case \"CapturePageStructured\": {\n return PageStructuredCaptured.make({ value: envelope.value.result });\n }\n }\n};\n\nconst isQuotaMessage = (text: string): boolean => /time limit|daily|quota/i.test(text);\n\nconst makeCapture = (\n browser: BrowserQuickActionClient,\n workersAi?: BrowserQuickActionWorkersAiPolicy,\n): PageCaptureCapture =>\n Effect.fn(\"BrowserQuickActionCapture.capture\")(function* (\n request: PageCaptureRequest,\n ): Effect.fn.Return<PageCaptureResult, PageCaptureError> {\n if (request.engine !== \"chromium\") {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"engine\",\n message:\n \"The browser binding's quickAction() exposes no engine selector; kitesurf requires the REST or CDP surface\",\n });\n }\n\n const usesWorkersAi = request.action._tag === \"CapturePageStructured\";\n if (usesWorkersAi) {\n if (workersAi === undefined) {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"action\",\n message:\n \"Structured capture invokes separately billed Workers AI and requires an explicit authorization and accounting policy\",\n });\n }\n yield* workersAi.authorizeAndAccount(request).pipe(\n Effect.mapError((cause) =>\n PageCaptureInferencePolicyError.make({\n implementation: browserQuickActionImplementation,\n provider: \"cloudflare-workers-ai\",\n reason: cause.reason,\n message:\n cause.reason === \"authorization\"\n ? \"Workers AI extraction was not authorized\"\n : \"Workers AI extraction could not be accounted for\",\n cause,\n }),\n ),\n );\n }\n\n const response = yield* executeQuickAction(browser, request).pipe(\n Effect.mapError((error) =>\n protocolError(\"The browser binding rejected the Quick Action\", error.cause),\n ),\n );\n const bodyText = yield* readBoundedResponse(response, request);\n if (response.status === 429) {\n const retryAfter = retryAfterMillis(response);\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n const cause = privateResponseCause(bodyText);\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserQuickActionImplementation,\n reason,\n ...(retryAfter === undefined ? {} : { retryAfterMillis: retryAfter }),\n ...(cause === undefined ? {} : { cause }),\n message:\n reason === \"quota\"\n ? \"The Quick Action exceeded its browser quota\"\n : \"The Quick Action was rate limited\",\n });\n }\n if (!response.ok) {\n const message = `The Quick Action answered HTTP ${response.status}`;\n const cause = privateResponseCause(bodyText);\n if (response.status >= 500) {\n return yield* protocolError(message, cause);\n }\n return yield* navigationError(message, cause);\n }\n const output = parseOutput(request.action, bodyText, response);\n if (\n output._tag === \"PageCaptureNavigationError\" ||\n output._tag === \"PageCaptureProtocolError\"\n ) {\n return yield* output;\n }\n const millis = browserMillis(response);\n return PageCaptureResult.make({\n implementation: browserQuickActionImplementation,\n output,\n resourceUse: PageCaptureResourceUse.make({\n ...(millis === undefined ? {} : { browserMillis: millis }),\n ...(usesWorkersAi\n ? {\n inference: PageCaptureInferenceUse.make({\n provider: \"cloudflare-workers-ai\",\n modelCalls: 1,\n }),\n }\n : {}),\n }),\n });\n });\n\n/**\n * Ordinary Quick Actions require host-owned browser binding authority. Workers\n * AI stays unavailable unless the host deliberately selects its separate Layer.\n */\nexport const browserQuickActionCaptureLayer = (): Layer.Layer<\n PageCapture,\n never,\n BrowserQuickActionBrowserBinding\n> =>\n Layer.effect(\n PageCapture,\n Effect.map(BrowserQuickActionBrowserBinding, (browser) =>\n PageCapture.of({ capture: makeCapture(browser) }),\n ),\n );\n\n/** Structured Quick Actions require host-owned browser and Workers AI authority. */\nexport const browserQuickActionWorkersAiCaptureLayer = (): Layer.Layer<\n PageCapture,\n never,\n BrowserQuickActionBrowserBinding | BrowserQuickActionWorkersAi\n> =>\n Layer.effect(\n PageCapture,\n Effect.gen(function* () {\n const browser = yield* BrowserQuickActionBrowserBinding;\n const workersAi = yield* BrowserQuickActionWorkersAi;\n return PageCapture.of({ capture: makeCapture(browser, workersAi) });\n }),\n );\n\n/** Host-owned Quick Action binding and optional, separately billed extraction authority. */\nexport interface CloudflareBrowserOptions extends BrowserQuickActionCaptureOptions {\n readonly workersAi?: BrowserQuickActionWorkersAiPolicy;\n}\n\n/** Compose WebCapture handlers with the Cloudflare Quick Action adapter. */\nexport const CloudflareBrowser = {\n /**\n * Supply a WebCapture definition and the resolved Worker browser binding.\n * Supports capture, scrape, and extraction definitions without importing capabilities.\n * Only PageCapture is provided; other handler requirements and errors stay visible.\n * Extraction fails closed unless workersAi explicitly authorizes and accounts for it.\n * Capture limits, typed failures, tracing, and scoped response cleanup are unchanged.\n */\n layer: <A, E, R>(\n definition: { readonly handlers: Layer.Layer<A, E, R> },\n options: CloudflareBrowserOptions,\n ): Layer.Layer<A, E, Exclude<R, PageCapture>> => {\n const capture =\n options.workersAi === undefined\n ? browserQuickActionCaptureLayer()\n : browserQuickActionWorkersAiCaptureLayer().pipe(\n Layer.provide(BrowserQuickActionWorkersAi.layer(options.workersAi)),\n );\n\n return definition.handlers.pipe(\n Layer.provide(capture.pipe(Layer.provide(BrowserQuickActionBrowserBinding.layer(options)))),\n );\n },\n};\n\nconst screenshotOptions = (request: PageScreenshotRequest): BrowserRunScreenshotOptions => {\n const options: BrowserRunBaseOptions = {};\n if (\n request.navigation?.waitUntil !== undefined ||\n request.navigation?.timeoutMillis !== undefined\n ) {\n options.gotoOptions = {\n ...(request.navigation.waitUntil === undefined\n ? {}\n : { waitUntil: request.navigation.waitUntil }),\n ...(request.navigation.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.timeoutMillis }),\n };\n }\n if (request.navigation?.waitForSelector !== undefined) {\n options.waitForSelector = {\n selector: request.navigation.waitForSelector.selector,\n ...(request.navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.waitForSelector.timeoutMillis }),\n };\n }\n if (request.viewport !== undefined) {\n options.viewport = { width: request.viewport.width, height: request.viewport.height };\n }\n if (request.resourcePolicy?.rejectResourceTypes !== undefined) {\n options.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy?.allowRequestPatterns !== undefined) {\n options.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n return {\n ...options,\n ...(request.target._tag === \"PageUrlTarget\"\n ? { url: request.target.url }\n : { html: request.target.html }),\n screenshotOptions: { type: \"png\", encoding: \"binary\", fullPage: request.fullPage },\n };\n};\n\nconst cancelBody = (body: ReadableStream<Uint8Array>): Effect.Effect<void> =>\n Effect.tryPromise({\n try: () => body.cancel(),\n catch: () => undefined,\n }).pipe(Effect.catch(() => Effect.void));\n\nconst releaseScreenshotReader = (\n reader: ReadableStreamDefaultReader<Uint8Array>,\n): Effect.Effect<void> =>\n Effect.tryPromise({ try: () => reader.cancel(), catch: () => undefined }).pipe(\n Effect.catch(() => Effect.logWarning(\"Canceling the screenshot response failed\")),\n Effect.ensuring(\n Effect.try({\n try: () => reader.releaseLock(),\n catch: () => undefined,\n }).pipe(Effect.catch(() => Effect.logWarning(\"Releasing the screenshot response failed\"))),\n ),\n );\n\nconst pngResponse = (response: Response): boolean =>\n response.headers.get(\"Content-Type\")?.split(\";\", 1)[0]?.trim().toLowerCase() === \"image/png\";\n\nconst declaredLength = (response: Response): number | undefined => {\n const raw = response.headers.get(\"Content-Length\");\n if (raw === null || !/^(0|[1-9][0-9]*)$/.test(raw)) return undefined;\n const length = Number(raw);\n return Number.isSafeInteger(length) ? length : undefined;\n};\n\nconst readScreenshot = Effect.fn(\"BrowserQuickActionScreenshot.read\")(function* (\n response: Response,\n request: PageScreenshotRequest,\n) {\n const body = response.body;\n if (body === null) {\n return yield* protocolError(\"The screenshot response had no body\");\n }\n if (!pngResponse(response)) {\n yield* cancelBody(body);\n return yield* protocolError(\"The screenshot response was not image/png\");\n }\n const length = declaredLength(response);\n if (length !== undefined && length > request.limits.maxOutputBytes) {\n yield* cancelBody(body);\n return yield* PageScreenshotOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed: length,\n });\n }\n const reader = yield* Effect.acquireRelease(\n Effect.try({\n try: () => body.getReader(),\n catch: (cause) => protocolError(\"Opening the screenshot response failed\", cause),\n }),\n releaseScreenshotReader,\n );\n const chunks: Array<Uint8Array> = [];\n let observed = 0;\n while (true) {\n const next = yield* Effect.tryPromise({\n try: () => reader.read(),\n catch: (cause) => protocolError(\"Reading the screenshot response failed\", cause),\n });\n if (next.done) break;\n observed += next.value.byteLength;\n if (observed > request.limits.maxOutputBytes) {\n return yield* PageScreenshotOutputLimitError.make({\n implementation: browserQuickActionImplementation,\n limit: request.limits.maxOutputBytes,\n observed,\n });\n }\n chunks.push(next.value);\n }\n const bytes = new Uint8Array(observed);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n}, Effect.scoped);\n\nconst makeScreenshot = (browser: BrowserQuickActionClient): PageScreenshotCapture =>\n Effect.fn(\"BrowserQuickActionScreenshot.capture\")(function* (\n request: PageScreenshotRequest,\n ): Effect.fn.Return<PageScreenshotResult, PageScreenshotError> {\n if (request.engine !== \"chromium\") {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserQuickActionImplementation,\n feature: \"engine\",\n message: \"The browser binding's screenshot action exposes no engine selector\",\n });\n }\n const response = yield* browser\n .screenshot(screenshotOptions(request))\n .pipe(\n Effect.mapError((error) =>\n protocolError(\"The browser binding rejected the screenshot\", error.cause),\n ),\n );\n if (response.status === 429) {\n const body = response.body;\n if (body !== null) yield* cancelBody(body);\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserQuickActionImplementation,\n reason: \"rate\",\n ...(retryAfterMillis(response) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response) }),\n message: \"The screenshot Quick Action was rate limited\",\n });\n }\n if (!response.ok) {\n const body = response.body;\n if (body !== null) yield* cancelBody(body);\n const message = `The screenshot Quick Action answered HTTP ${response.status}`;\n return yield* response.status >= 500 ? protocolError(message) : navigationError(message);\n }\n const bytes = yield* readScreenshot(response, request);\n return PageScreenshotResult.make({\n implementation: browserQuickActionImplementation,\n mediaType: \"image/png\",\n bytes,\n });\n });\n\n/** Native Browser Run screenshot adapter. It retains PNG bytes only for the caller's result. */\nexport const browserQuickActionScreenshotLayer = (): Layer.Layer<\n PageScreenshot,\n never,\n BrowserQuickActionBrowserBinding\n> =>\n Layer.effect(\n PageScreenshot,\n Effect.map(BrowserQuickActionBrowserBinding, (browser) =>\n PageScreenshot.of({ capture: makeScreenshot(browser) }),\n ),\n );\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BrowserQuickActionWorkersAi } from "./browser-quick-action.mjs";
|
|
1
|
+
import { BrowserQuickActionWorkersAi, BrowserQuickActionWorkersAiPolicy } from "./browser-quick-action.mjs";
|
|
2
2
|
import { Layer, Redacted } from "effect";
|
|
3
3
|
import { PageCapture, SandboxImplementation } from "@effect-agent/sandbox";
|
|
4
4
|
import { HttpClient } from "effect/unstable/http";
|
|
@@ -14,6 +14,21 @@ interface BrowserRestCaptureOptions {
|
|
|
14
14
|
declare const browserRestCaptureLayer: (options: BrowserRestCaptureOptions) => Layer.Layer<PageCapture, never, HttpClient.HttpClient>;
|
|
15
15
|
/** REST structured extraction additionally requires explicit Workers AI authorization/accounting. */
|
|
16
16
|
declare const browserRestWorkersAiCaptureLayer: (options: BrowserRestCaptureOptions) => Layer.Layer<PageCapture, never, HttpClient.HttpClient | BrowserQuickActionWorkersAi>;
|
|
17
|
+
/** Explicit REST credentials and optional authorization for separately billed extraction. */
|
|
18
|
+
interface CloudflareBrowserRestOptions extends BrowserRestCaptureOptions {
|
|
19
|
+
readonly workersAi?: BrowserQuickActionWorkersAiPolicy;
|
|
20
|
+
}
|
|
21
|
+
/** Node-safe WebCapture handler assembly; the application supplies its HttpClient. */
|
|
22
|
+
declare const CloudflareBrowserRest: {
|
|
23
|
+
/**
|
|
24
|
+
* Supports capture, scrape, and extraction definitions. Only PageCapture is supplied;
|
|
25
|
+
* handler errors and other services remain visible. Extraction fails closed without
|
|
26
|
+
* workersAi. Existing host policies, response limits, and scoped cleanup are unchanged.
|
|
27
|
+
*/
|
|
28
|
+
layer: <A, E, R>(definition: {
|
|
29
|
+
readonly handlers: Layer.Layer<A, E, R>;
|
|
30
|
+
}, options: CloudflareBrowserRestOptions) => Layer.Layer<A, E, Exclude<R, PageCapture> | HttpClient.HttpClient>;
|
|
31
|
+
};
|
|
17
32
|
//#endregion
|
|
18
|
-
export { BrowserRestCaptureOptions, browserRestCaptureImplementation, browserRestCaptureLayer, browserRestWorkersAiCaptureLayer };
|
|
33
|
+
export { BrowserRestCaptureOptions, CloudflareBrowserRest, CloudflareBrowserRestOptions, browserRestCaptureImplementation, browserRestCaptureLayer, browserRestWorkersAiCaptureLayer };
|
|
19
34
|
//# sourceMappingURL=browser-rest-capture.d.mts.map
|
|
@@ -220,7 +220,18 @@ const browserRestWorkersAiCaptureLayer = (options) => Layer.effect(PageCapture,
|
|
|
220
220
|
const workersAi = yield* BrowserQuickActionWorkersAi;
|
|
221
221
|
return PageCapture.of({ capture: makeCapture(client, options, workersAi) });
|
|
222
222
|
}));
|
|
223
|
+
/** Node-safe WebCapture handler assembly; the application supplies its HttpClient. */
|
|
224
|
+
const CloudflareBrowserRest = {
|
|
225
|
+
/**
|
|
226
|
+
* Supports capture, scrape, and extraction definitions. Only PageCapture is supplied;
|
|
227
|
+
* handler errors and other services remain visible. Extraction fails closed without
|
|
228
|
+
* workersAi. Existing host policies, response limits, and scoped cleanup are unchanged.
|
|
229
|
+
*/
|
|
230
|
+
layer: (definition, options) => {
|
|
231
|
+
const capture = options.workersAi === void 0 ? browserRestCaptureLayer(options) : browserRestWorkersAiCaptureLayer(options).pipe(Layer.provide(BrowserQuickActionWorkersAi.layer(options.workersAi)));
|
|
232
|
+
return definition.handlers.pipe(Layer.provide(capture));
|
|
233
|
+
} };
|
|
223
234
|
//#endregion
|
|
224
|
-
export { browserRestCaptureImplementation, browserRestCaptureLayer, browserRestWorkersAiCaptureLayer };
|
|
235
|
+
export { CloudflareBrowserRest, browserRestCaptureImplementation, browserRestCaptureLayer, browserRestWorkersAiCaptureLayer };
|
|
225
236
|
|
|
226
237
|
//# sourceMappingURL=browser-rest-capture.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-rest-capture.mjs","names":[],"sources":["../src/browser-rest-capture.ts"],"sourcesContent":["import {\n PageCapture,\n PageCaptureInferencePolicyError,\n PageCaptureInferenceUse,\n PageCaptureNavigationError,\n PageCaptureOutputLimitError,\n PageCaptureProtocolError,\n PageCaptureRateLimitedError,\n PageCaptureResourceUse,\n PageCaptureResult,\n PageContentCaptured,\n PageLinksCaptured,\n PageMarkdownCaptured,\n PageScrapeCaptured,\n PageStructuredCaptured,\n PageCaptureUnsupportedError,\n SandboxImplementation,\n type PageCaptureAction,\n type PageCaptureCapture,\n type PageCaptureError,\n type PageCaptureOutput,\n type PageCaptureRequest,\n} from \"@effect-agent/sandbox\";\nimport { Effect, Layer, Option, Redacted, Schema, Stream } from \"effect\";\nimport { HttpClient, HttpClientRequest, type HttpClientError } from \"effect/unstable/http\";\n\nimport {\n BrowserQuickActionWorkersAi,\n type BrowserQuickActionWorkersAiPolicy,\n} from \"./browser-quick-action.ts\";\n\n/** Node-safe REST PageCapture implementation; it never imports Worker runtime modules. */\nexport const browserRestCaptureImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-rest\",\n});\n\nconst API_ORIGIN = \"https://api.cloudflare.com\";\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\n/** Explicit construction inputs; credentials are only projected into the fixed-origin Authorization header. */\nexport interface BrowserRestCaptureOptions {\n readonly accountId: string;\n readonly apiToken: Redacted.Redacted<string>;\n}\n\nconst RestSuccessEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Json,\n errors: Schema.optionalKey(Schema.Array(Schema.Unknown)),\n meta: Schema.optionalKey(Schema.Unknown),\n});\nconst RestErrorEnvelope = Schema.Struct({\n success: Schema.Literal(false),\n errors: Schema.Array(\n Schema.Struct({\n message: Schema.String,\n code: Schema.optionalKey(Schema.Number),\n }),\n ),\n result: Schema.optionalKey(Schema.Json),\n meta: Schema.optionalKey(Schema.Unknown),\n});\nconst RestEnvelope = Schema.Union([RestSuccessEnvelope, RestErrorEnvelope]);\nconst decodeEnvelope = Schema.decodeUnknownOption(Schema.fromJsonString(RestEnvelope));\n\nconst protocolError = (message: string, cause?: unknown): PageCaptureProtocolError =>\n PageCaptureProtocolError.make({\n implementation: browserRestCaptureImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst navigationError = (message: string, cause?: unknown): PageCaptureNavigationError =>\n PageCaptureNavigationError.make({\n implementation: browserRestCaptureImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst privateResponseCause = (\n bodyText: string,\n apiToken: Redacted.Redacted<string>,\n): Error | undefined => {\n if (bodyText.length === 0) return undefined;\n const token = Redacted.value(apiToken);\n const diagnostic = boundedDiagnostic(bodyText);\n return new Error(token.length === 0 ? diagnostic : diagnostic.replaceAll(token, \"[REDACTED]\"));\n};\n\nconst requestBody = (request: PageCaptureRequest): Record<string, unknown> => {\n const body: Record<string, unknown> =\n request.target._tag === \"PageUrlTarget\"\n ? { url: request.target.url }\n : { html: request.target.html };\n if (request.navigation !== undefined) {\n const goto: Record<string, unknown> = {};\n if (request.navigation.waitUntil !== undefined) goto.waitUntil = request.navigation.waitUntil;\n if (request.navigation.timeoutMillis !== undefined)\n goto.timeout = request.navigation.timeoutMillis;\n if (Object.keys(goto).length > 0) body.gotoOptions = goto;\n if (request.navigation.waitForSelector !== undefined) {\n body.waitForSelector = {\n selector: request.navigation.waitForSelector.selector,\n ...(request.navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.waitForSelector.timeoutMillis }),\n };\n }\n }\n if (request.viewport !== undefined) body.viewport = request.viewport;\n if (request.resourcePolicy?.rejectResourceTypes !== undefined) {\n body.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy?.allowRequestPatterns !== undefined) {\n body.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n return body;\n};\n\nconst actionName = (\n action: PageCaptureAction,\n): \"content\" | \"markdown\" | \"links\" | \"scrape\" | \"json\" => {\n switch (action._tag) {\n case \"CapturePageContent\":\n return \"content\";\n case \"CapturePageMarkdown\":\n return \"markdown\";\n case \"CapturePageLinks\":\n return \"links\";\n case \"CapturePageScrape\":\n return \"scrape\";\n case \"CapturePageStructured\":\n return \"json\";\n }\n};\n\nconst actionBody = (request: PageCaptureRequest): Record<string, unknown> => {\n const body = requestBody(request);\n switch (request.action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\":\n return body;\n case \"CapturePageLinks\":\n return {\n ...body,\n ...(request.action.visibleLinksOnly === undefined\n ? {}\n : { visibleLinksOnly: request.action.visibleLinksOnly }),\n };\n case \"CapturePageScrape\":\n return {\n ...body,\n elements: request.action.selectors.map((selector) => ({ selector })),\n };\n case \"CapturePageStructured\":\n return {\n ...body,\n response_format: { type: \"json_schema\", json_schema: request.action.responseFormat },\n ...(request.action.prompt === undefined ? {} : { prompt: request.action.prompt }),\n };\n }\n};\n\nconst retryAfterMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const seconds = Number(headers[\"retry-after\"]);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst browserMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const millis = Number(headers[\"x-browser-ms-used\"]);\n return Number.isSafeInteger(millis) && millis >= 0 ? millis : undefined;\n};\n\n/** Response framing is provider metadata, never content controlled by the rendered page. */\nconst isJsonResponse = (headers: Readonly<Record<string, string | undefined>>): boolean => {\n const contentType = headers[\"content-type\"];\n if (contentType === undefined) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst readBoundedResponse = Effect.fn(\"BrowserRestCapture.readResponse\")(function* (\n response: { readonly stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError> },\n request: PageCaptureRequest,\n): Effect.fn.Return<string, PageCaptureError> {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n const chunks = yield* Stream.runFoldEffect<\n Uint8Array,\n HttpClientError.HttpClientError,\n never,\n { readonly observed: number; readonly text: string },\n PageCaptureOutputLimitError | PageCaptureProtocolError,\n never\n >(\n response.stream,\n () => ({ observed: 0, text: \"\" }),\n (state, chunk) => {\n const observed = state.observed + chunk.byteLength;\n if (observed > request.limits.maxOutputBytes) {\n return Effect.fail(\n PageCaptureOutputLimitError.make({\n implementation: browserRestCaptureImplementation,\n limit: request.limits.maxOutputBytes,\n observed,\n }),\n );\n }\n return Effect.try({\n try: () => ({ observed, text: state.text + decoder.decode(chunk, { stream: true }) }),\n catch: (cause) => protocolError(\"Decoding the Browser Run response failed\", cause),\n });\n },\n ).pipe(\n Effect.mapError((error) =>\n Schema.is(PageCaptureOutputLimitError)(error) || Schema.is(PageCaptureProtocolError)(error)\n ? error\n : protocolError(\"Reading the Browser Run response failed\", error),\n ),\n );\n return yield* Effect.try({\n try: () => chunks.text + decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Browser Run response failed\", cause),\n });\n});\n\nconst parseOutput = (\n action: PageCaptureAction,\n bodyText: string,\n apiToken: Redacted.Redacted<string>,\n): PageCaptureOutput | PageCaptureNavigationError | PageCaptureProtocolError => {\n const envelope = decodeEnvelope(bodyText);\n if (Option.isNone(envelope)) {\n return protocolError(\n \"The Browser Run response did not carry a valid response envelope\",\n privateResponseCause(bodyText, apiToken),\n );\n }\n if (!envelope.value.success) {\n return navigationError(\n \"Browser Run reported a navigation failure\",\n privateResponseCause(bodyText, apiToken),\n );\n }\n switch (action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\":\n if (typeof envelope.value.result !== \"string\") {\n return protocolError(\"The Browser Run response envelope carried a non-text result\");\n }\n return action._tag === \"CapturePageContent\"\n ? PageContentCaptured.make({ html: envelope.value.result })\n : PageMarkdownCaptured.make({ markdown: envelope.value.result });\n case \"CapturePageLinks\": {\n const decoded = Schema.decodeUnknownOption(PageLinksCaptured)({\n _tag: \"PageLinksCaptured\",\n links: envelope.value.result,\n });\n return Option.isSome(decoded)\n ? decoded.value\n : protocolError(\"Browser Run links did not return a bounded array of valid URLs\");\n }\n case \"CapturePageScrape\": {\n const decoded = Schema.decodeUnknownOption(PageScrapeCaptured)({\n _tag: \"PageScrapeCaptured\",\n groups: envelope.value.result,\n });\n return Option.isSome(decoded)\n ? decoded.value\n : protocolError(\"Browser Run scrape did not return bounded grouped element records\");\n }\n case \"CapturePageStructured\":\n return PageStructuredCaptured.make({ value: envelope.value.result });\n }\n};\n\nconst isQuotaMessage = (text: string): boolean => /time limit|daily|quota/i.test(text);\n\nconst makeCapture = (\n client: HttpClient.HttpClient,\n options: BrowserRestCaptureOptions,\n workersAi?: BrowserQuickActionWorkersAiPolicy,\n): PageCaptureCapture =>\n Effect.fn(\"BrowserRestCapture.capture\")(function* (\n request: PageCaptureRequest,\n ): Effect.fn.Return<PageCaptureResult, PageCaptureError> {\n const usesWorkersAi = request.action._tag === \"CapturePageStructured\";\n if (usesWorkersAi) {\n if (workersAi === undefined) {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserRestCaptureImplementation,\n feature: \"action\",\n message:\n \"Structured capture invokes separately billed Workers AI and requires an explicit authorization and accounting policy\",\n });\n }\n yield* workersAi.authorizeAndAccount(request).pipe(\n Effect.mapError((cause) =>\n PageCaptureInferencePolicyError.make({\n implementation: browserRestCaptureImplementation,\n provider: \"cloudflare-workers-ai\",\n reason: cause.reason,\n message:\n cause.reason === \"authorization\"\n ? \"Workers AI extraction was not authorized\"\n : \"Workers AI extraction could not be accounted for\",\n cause,\n }),\n ),\n );\n }\n const action = actionName(request.action);\n const path = `${API_ORIGIN}/client/v4/accounts/${encodeURIComponent(options.accountId)}/browser-rendering/${action}`;\n const requestWithBody = yield* HttpClientRequest.post(path).pipe(\n request.engine === \"kitesurf\"\n ? HttpClientRequest.setUrlParam(\"browser\", \"kitesurf\")\n : (value) => value,\n HttpClientRequest.bearerToken(options.apiToken),\n HttpClientRequest.acceptJson,\n HttpClientRequest.bodyJson(actionBody(request)),\n Effect.mapError((cause) => protocolError(\"Encoding the Browser Run request failed\", cause)),\n );\n const response = yield* client\n .execute(requestWithBody)\n .pipe(\n Effect.mapError((cause) => protocolError(\"Calling the Browser Run REST API failed\", cause)),\n );\n const bodyText = yield* readBoundedResponse(response, request);\n if (response.status === 429) {\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserRestCaptureImplementation,\n reason,\n ...(retryAfterMillis(response.headers) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response.headers) }),\n ...(privateResponseCause(bodyText, options.apiToken) === undefined\n ? {}\n : { cause: privateResponseCause(bodyText, options.apiToken) }),\n message:\n reason === \"quota\"\n ? \"Browser Run exceeded its browser quota\"\n : \"Browser Run was rate limited\",\n });\n }\n if (response.status < 200 || response.status >= 300) {\n const error =\n response.status >= 500\n ? protocolError(\n `Browser Run answered HTTP ${String(response.status)}`,\n privateResponseCause(bodyText, options.apiToken),\n )\n : navigationError(\n `Browser Run answered HTTP ${String(response.status)}`,\n privateResponseCause(bodyText, options.apiToken),\n );\n return yield* error;\n }\n if (!isJsonResponse(response.headers)) {\n return yield* protocolError(\n \"The Browser Run success response was not a JSON response envelope\",\n privateResponseCause(bodyText, options.apiToken),\n );\n }\n const output = parseOutput(request.action, bodyText, options.apiToken);\n if (\n output._tag === \"PageCaptureNavigationError\" ||\n output._tag === \"PageCaptureProtocolError\"\n ) {\n return yield* output;\n }\n return PageCaptureResult.make({\n implementation: browserRestCaptureImplementation,\n output,\n resourceUse: PageCaptureResourceUse.make({\n ...(browserMillis(response.headers) === undefined\n ? {}\n : { browserMillis: browserMillis(response.headers) }),\n ...(usesWorkersAi\n ? {\n inference: PageCaptureInferenceUse.make({\n provider: \"cloudflare-workers-ai\",\n modelCalls: 1,\n }),\n }\n : {}),\n }),\n });\n });\n\n/** REST PageCapture layer for Node and other non-Worker composition roots. */\nexport const browserRestCaptureLayer = (\n options: BrowserRestCaptureOptions,\n): Layer.Layer<PageCapture, never, HttpClient.HttpClient> =>\n Layer.effect(\n PageCapture,\n Effect.map(HttpClient.HttpClient, (client) =>\n PageCapture.of({ capture: makeCapture(client, options) }),\n ),\n );\n\n/** REST structured extraction additionally requires explicit Workers AI authorization/accounting. */\nexport const browserRestWorkersAiCaptureLayer = (\n options: BrowserRestCaptureOptions,\n): Layer.Layer<PageCapture, never, HttpClient.HttpClient | BrowserQuickActionWorkersAi> =>\n Layer.effect(\n PageCapture,\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const workersAi = yield* BrowserQuickActionWorkersAi;\n return PageCapture.of({ capture: makeCapture(client, options, workersAi) });\n }),\n );\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser-rest-capture.mjs","names":[],"sources":["../src/browser-rest-capture.ts"],"sourcesContent":["import {\n PageCapture,\n PageCaptureInferencePolicyError,\n PageCaptureInferenceUse,\n PageCaptureNavigationError,\n PageCaptureOutputLimitError,\n PageCaptureProtocolError,\n PageCaptureRateLimitedError,\n PageCaptureResourceUse,\n PageCaptureResult,\n PageContentCaptured,\n PageLinksCaptured,\n PageMarkdownCaptured,\n PageScrapeCaptured,\n PageStructuredCaptured,\n PageCaptureUnsupportedError,\n SandboxImplementation,\n type PageCaptureAction,\n type PageCaptureCapture,\n type PageCaptureError,\n type PageCaptureOutput,\n type PageCaptureRequest,\n} from \"@effect-agent/sandbox\";\nimport { Effect, Layer, Option, Redacted, Schema, Stream } from \"effect\";\nimport { HttpClient, HttpClientRequest, type HttpClientError } from \"effect/unstable/http\";\n\nimport {\n BrowserQuickActionWorkersAi,\n type BrowserQuickActionWorkersAiPolicy,\n} from \"./browser-quick-action.ts\";\n\n/** Node-safe REST PageCapture implementation; it never imports Worker runtime modules. */\nexport const browserRestCaptureImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-rest\",\n});\n\nconst API_ORIGIN = \"https://api.cloudflare.com\";\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\n/** Explicit construction inputs; credentials are only projected into the fixed-origin Authorization header. */\nexport interface BrowserRestCaptureOptions {\n readonly accountId: string;\n readonly apiToken: Redacted.Redacted<string>;\n}\n\nconst RestSuccessEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Json,\n errors: Schema.optionalKey(Schema.Array(Schema.Unknown)),\n meta: Schema.optionalKey(Schema.Unknown),\n});\nconst RestErrorEnvelope = Schema.Struct({\n success: Schema.Literal(false),\n errors: Schema.Array(\n Schema.Struct({\n message: Schema.String,\n code: Schema.optionalKey(Schema.Number),\n }),\n ),\n result: Schema.optionalKey(Schema.Json),\n meta: Schema.optionalKey(Schema.Unknown),\n});\nconst RestEnvelope = Schema.Union([RestSuccessEnvelope, RestErrorEnvelope]);\nconst decodeEnvelope = Schema.decodeUnknownOption(Schema.fromJsonString(RestEnvelope));\n\nconst protocolError = (message: string, cause?: unknown): PageCaptureProtocolError =>\n PageCaptureProtocolError.make({\n implementation: browserRestCaptureImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst navigationError = (message: string, cause?: unknown): PageCaptureNavigationError =>\n PageCaptureNavigationError.make({\n implementation: browserRestCaptureImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst privateResponseCause = (\n bodyText: string,\n apiToken: Redacted.Redacted<string>,\n): Error | undefined => {\n if (bodyText.length === 0) return undefined;\n const token = Redacted.value(apiToken);\n const diagnostic = boundedDiagnostic(bodyText);\n return new Error(token.length === 0 ? diagnostic : diagnostic.replaceAll(token, \"[REDACTED]\"));\n};\n\nconst requestBody = (request: PageCaptureRequest): Record<string, unknown> => {\n const body: Record<string, unknown> =\n request.target._tag === \"PageUrlTarget\"\n ? { url: request.target.url }\n : { html: request.target.html };\n if (request.navigation !== undefined) {\n const goto: Record<string, unknown> = {};\n if (request.navigation.waitUntil !== undefined) goto.waitUntil = request.navigation.waitUntil;\n if (request.navigation.timeoutMillis !== undefined)\n goto.timeout = request.navigation.timeoutMillis;\n if (Object.keys(goto).length > 0) body.gotoOptions = goto;\n if (request.navigation.waitForSelector !== undefined) {\n body.waitForSelector = {\n selector: request.navigation.waitForSelector.selector,\n ...(request.navigation.waitForSelector.timeoutMillis === undefined\n ? {}\n : { timeout: request.navigation.waitForSelector.timeoutMillis }),\n };\n }\n }\n if (request.viewport !== undefined) body.viewport = request.viewport;\n if (request.resourcePolicy?.rejectResourceTypes !== undefined) {\n body.rejectResourceTypes = [...request.resourcePolicy.rejectResourceTypes];\n }\n if (request.resourcePolicy?.allowRequestPatterns !== undefined) {\n body.allowRequestPattern = [...request.resourcePolicy.allowRequestPatterns];\n }\n return body;\n};\n\nconst actionName = (\n action: PageCaptureAction,\n): \"content\" | \"markdown\" | \"links\" | \"scrape\" | \"json\" => {\n switch (action._tag) {\n case \"CapturePageContent\":\n return \"content\";\n case \"CapturePageMarkdown\":\n return \"markdown\";\n case \"CapturePageLinks\":\n return \"links\";\n case \"CapturePageScrape\":\n return \"scrape\";\n case \"CapturePageStructured\":\n return \"json\";\n }\n};\n\nconst actionBody = (request: PageCaptureRequest): Record<string, unknown> => {\n const body = requestBody(request);\n switch (request.action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\":\n return body;\n case \"CapturePageLinks\":\n return {\n ...body,\n ...(request.action.visibleLinksOnly === undefined\n ? {}\n : { visibleLinksOnly: request.action.visibleLinksOnly }),\n };\n case \"CapturePageScrape\":\n return {\n ...body,\n elements: request.action.selectors.map((selector) => ({ selector })),\n };\n case \"CapturePageStructured\":\n return {\n ...body,\n response_format: { type: \"json_schema\", json_schema: request.action.responseFormat },\n ...(request.action.prompt === undefined ? {} : { prompt: request.action.prompt }),\n };\n }\n};\n\nconst retryAfterMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const seconds = Number(headers[\"retry-after\"]);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst browserMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const millis = Number(headers[\"x-browser-ms-used\"]);\n return Number.isSafeInteger(millis) && millis >= 0 ? millis : undefined;\n};\n\n/** Response framing is provider metadata, never content controlled by the rendered page. */\nconst isJsonResponse = (headers: Readonly<Record<string, string | undefined>>): boolean => {\n const contentType = headers[\"content-type\"];\n if (contentType === undefined) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst readBoundedResponse = Effect.fn(\"BrowserRestCapture.readResponse\")(function* (\n response: { readonly stream: Stream.Stream<Uint8Array, HttpClientError.HttpClientError> },\n request: PageCaptureRequest,\n): Effect.fn.Return<string, PageCaptureError> {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n const chunks = yield* Stream.runFoldEffect<\n Uint8Array,\n HttpClientError.HttpClientError,\n never,\n { readonly observed: number; readonly text: string },\n PageCaptureOutputLimitError | PageCaptureProtocolError,\n never\n >(\n response.stream,\n () => ({ observed: 0, text: \"\" }),\n (state, chunk) => {\n const observed = state.observed + chunk.byteLength;\n if (observed > request.limits.maxOutputBytes) {\n return Effect.fail(\n PageCaptureOutputLimitError.make({\n implementation: browserRestCaptureImplementation,\n limit: request.limits.maxOutputBytes,\n observed,\n }),\n );\n }\n return Effect.try({\n try: () => ({ observed, text: state.text + decoder.decode(chunk, { stream: true }) }),\n catch: (cause) => protocolError(\"Decoding the Browser Run response failed\", cause),\n });\n },\n ).pipe(\n Effect.mapError((error) =>\n Schema.is(PageCaptureOutputLimitError)(error) || Schema.is(PageCaptureProtocolError)(error)\n ? error\n : protocolError(\"Reading the Browser Run response failed\", error),\n ),\n );\n return yield* Effect.try({\n try: () => chunks.text + decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Browser Run response failed\", cause),\n });\n});\n\nconst parseOutput = (\n action: PageCaptureAction,\n bodyText: string,\n apiToken: Redacted.Redacted<string>,\n): PageCaptureOutput | PageCaptureNavigationError | PageCaptureProtocolError => {\n const envelope = decodeEnvelope(bodyText);\n if (Option.isNone(envelope)) {\n return protocolError(\n \"The Browser Run response did not carry a valid response envelope\",\n privateResponseCause(bodyText, apiToken),\n );\n }\n if (!envelope.value.success) {\n return navigationError(\n \"Browser Run reported a navigation failure\",\n privateResponseCause(bodyText, apiToken),\n );\n }\n switch (action._tag) {\n case \"CapturePageContent\":\n case \"CapturePageMarkdown\":\n if (typeof envelope.value.result !== \"string\") {\n return protocolError(\"The Browser Run response envelope carried a non-text result\");\n }\n return action._tag === \"CapturePageContent\"\n ? PageContentCaptured.make({ html: envelope.value.result })\n : PageMarkdownCaptured.make({ markdown: envelope.value.result });\n case \"CapturePageLinks\": {\n const decoded = Schema.decodeUnknownOption(PageLinksCaptured)({\n _tag: \"PageLinksCaptured\",\n links: envelope.value.result,\n });\n return Option.isSome(decoded)\n ? decoded.value\n : protocolError(\"Browser Run links did not return a bounded array of valid URLs\");\n }\n case \"CapturePageScrape\": {\n const decoded = Schema.decodeUnknownOption(PageScrapeCaptured)({\n _tag: \"PageScrapeCaptured\",\n groups: envelope.value.result,\n });\n return Option.isSome(decoded)\n ? decoded.value\n : protocolError(\"Browser Run scrape did not return bounded grouped element records\");\n }\n case \"CapturePageStructured\":\n return PageStructuredCaptured.make({ value: envelope.value.result });\n }\n};\n\nconst isQuotaMessage = (text: string): boolean => /time limit|daily|quota/i.test(text);\n\nconst makeCapture = (\n client: HttpClient.HttpClient,\n options: BrowserRestCaptureOptions,\n workersAi?: BrowserQuickActionWorkersAiPolicy,\n): PageCaptureCapture =>\n Effect.fn(\"BrowserRestCapture.capture\")(function* (\n request: PageCaptureRequest,\n ): Effect.fn.Return<PageCaptureResult, PageCaptureError> {\n const usesWorkersAi = request.action._tag === \"CapturePageStructured\";\n if (usesWorkersAi) {\n if (workersAi === undefined) {\n return yield* PageCaptureUnsupportedError.make({\n implementation: browserRestCaptureImplementation,\n feature: \"action\",\n message:\n \"Structured capture invokes separately billed Workers AI and requires an explicit authorization and accounting policy\",\n });\n }\n yield* workersAi.authorizeAndAccount(request).pipe(\n Effect.mapError((cause) =>\n PageCaptureInferencePolicyError.make({\n implementation: browserRestCaptureImplementation,\n provider: \"cloudflare-workers-ai\",\n reason: cause.reason,\n message:\n cause.reason === \"authorization\"\n ? \"Workers AI extraction was not authorized\"\n : \"Workers AI extraction could not be accounted for\",\n cause,\n }),\n ),\n );\n }\n const action = actionName(request.action);\n const path = `${API_ORIGIN}/client/v4/accounts/${encodeURIComponent(options.accountId)}/browser-rendering/${action}`;\n const requestWithBody = yield* HttpClientRequest.post(path).pipe(\n request.engine === \"kitesurf\"\n ? HttpClientRequest.setUrlParam(\"browser\", \"kitesurf\")\n : (value) => value,\n HttpClientRequest.bearerToken(options.apiToken),\n HttpClientRequest.acceptJson,\n HttpClientRequest.bodyJson(actionBody(request)),\n Effect.mapError((cause) => protocolError(\"Encoding the Browser Run request failed\", cause)),\n );\n const response = yield* client\n .execute(requestWithBody)\n .pipe(\n Effect.mapError((cause) => protocolError(\"Calling the Browser Run REST API failed\", cause)),\n );\n const bodyText = yield* readBoundedResponse(response, request);\n if (response.status === 429) {\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n return yield* PageCaptureRateLimitedError.make({\n implementation: browserRestCaptureImplementation,\n reason,\n ...(retryAfterMillis(response.headers) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response.headers) }),\n ...(privateResponseCause(bodyText, options.apiToken) === undefined\n ? {}\n : { cause: privateResponseCause(bodyText, options.apiToken) }),\n message:\n reason === \"quota\"\n ? \"Browser Run exceeded its browser quota\"\n : \"Browser Run was rate limited\",\n });\n }\n if (response.status < 200 || response.status >= 300) {\n const error =\n response.status >= 500\n ? protocolError(\n `Browser Run answered HTTP ${String(response.status)}`,\n privateResponseCause(bodyText, options.apiToken),\n )\n : navigationError(\n `Browser Run answered HTTP ${String(response.status)}`,\n privateResponseCause(bodyText, options.apiToken),\n );\n return yield* error;\n }\n if (!isJsonResponse(response.headers)) {\n return yield* protocolError(\n \"The Browser Run success response was not a JSON response envelope\",\n privateResponseCause(bodyText, options.apiToken),\n );\n }\n const output = parseOutput(request.action, bodyText, options.apiToken);\n if (\n output._tag === \"PageCaptureNavigationError\" ||\n output._tag === \"PageCaptureProtocolError\"\n ) {\n return yield* output;\n }\n return PageCaptureResult.make({\n implementation: browserRestCaptureImplementation,\n output,\n resourceUse: PageCaptureResourceUse.make({\n ...(browserMillis(response.headers) === undefined\n ? {}\n : { browserMillis: browserMillis(response.headers) }),\n ...(usesWorkersAi\n ? {\n inference: PageCaptureInferenceUse.make({\n provider: \"cloudflare-workers-ai\",\n modelCalls: 1,\n }),\n }\n : {}),\n }),\n });\n });\n\n/** REST PageCapture layer for Node and other non-Worker composition roots. */\nexport const browserRestCaptureLayer = (\n options: BrowserRestCaptureOptions,\n): Layer.Layer<PageCapture, never, HttpClient.HttpClient> =>\n Layer.effect(\n PageCapture,\n Effect.map(HttpClient.HttpClient, (client) =>\n PageCapture.of({ capture: makeCapture(client, options) }),\n ),\n );\n\n/** REST structured extraction additionally requires explicit Workers AI authorization/accounting. */\nexport const browserRestWorkersAiCaptureLayer = (\n options: BrowserRestCaptureOptions,\n): Layer.Layer<PageCapture, never, HttpClient.HttpClient | BrowserQuickActionWorkersAi> =>\n Layer.effect(\n PageCapture,\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const workersAi = yield* BrowserQuickActionWorkersAi;\n return PageCapture.of({ capture: makeCapture(client, options, workersAi) });\n }),\n );\n\n/** Explicit REST credentials and optional authorization for separately billed extraction. */\nexport interface CloudflareBrowserRestOptions extends BrowserRestCaptureOptions {\n readonly workersAi?: BrowserQuickActionWorkersAiPolicy;\n}\n\n/** Node-safe WebCapture handler assembly; the application supplies its HttpClient. */\nexport const CloudflareBrowserRest = {\n /**\n * Supports capture, scrape, and extraction definitions. Only PageCapture is supplied;\n * handler errors and other services remain visible. Extraction fails closed without\n * workersAi. Existing host policies, response limits, and scoped cleanup are unchanged.\n */\n layer: <A, E, R>(\n definition: { readonly handlers: Layer.Layer<A, E, R> },\n options: CloudflareBrowserRestOptions,\n ): Layer.Layer<A, E, Exclude<R, PageCapture> | HttpClient.HttpClient> => {\n const capture =\n options.workersAi === undefined\n ? browserRestCaptureLayer(options)\n : browserRestWorkersAiCaptureLayer(options).pipe(\n Layer.provide(BrowserQuickActionWorkersAi.layer(options.workersAi)),\n );\n\n return definition.handlers.pipe(Layer.provide(capture));\n },\n};\n"],"mappings":"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"}
|
|
@@ -10,8 +10,8 @@ const browserRestCrawlImplementation = SandboxImplementation.make({
|
|
|
10
10
|
const API_ORIGIN = "https://api.cloudflare.com";
|
|
11
11
|
const POLL_INTERVAL = Duration.seconds(1);
|
|
12
12
|
const CANCEL_TIMEOUT = Duration.seconds(30);
|
|
13
|
-
const MAX_CONTROL_RESPONSE_BYTES =
|
|
14
|
-
const MAX_RESULTS_RESPONSE_BYTES =
|
|
13
|
+
const MAX_CONTROL_RESPONSE_BYTES = 65536;
|
|
14
|
+
const MAX_RESULTS_RESPONSE_BYTES = 12582912;
|
|
15
15
|
const MAX_DIAGNOSTIC_LENGTH = 8e3;
|
|
16
16
|
const MAX_CURSOR_LENGTH = 1024;
|
|
17
17
|
const MAX_RECORDS_PER_RESPONSE = 1e4;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-rest-crawl.mjs","names":[],"sources":["../src/browser-rest-crawl.ts"],"sourcesContent":["import {\n PageCrawl,\n PageCrawlLimitError,\n PageCrawlProtocolError,\n PageCrawlRateLimitedError,\n PageCrawlRecord,\n PageCrawlTerminalError,\n SandboxImplementation,\n type PageCrawlCrawl,\n type PageCrawlError,\n type PageCrawlRequest,\n} from \"@effect-agent/sandbox\";\nimport {\n Duration,\n Effect,\n Layer,\n Option,\n Redacted,\n Ref,\n Schedule,\n Schema,\n Scope,\n Stream,\n} from \"effect\";\nimport {\n HttpClient,\n HttpClientRequest,\n type HttpClientError,\n type HttpClientResponse,\n} from \"effect/unstable/http\";\n\n/** Node-safe REST crawl implementation. It never exposes or persists provider job identity. */\nexport const browserRestCrawlImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-rest-crawl\",\n});\n\nconst API_ORIGIN = \"https://api.cloudflare.com\";\nconst POLL_INTERVAL = Duration.seconds(1);\nconst CANCEL_TIMEOUT = Duration.seconds(30);\nconst MAX_CONTROL_RESPONSE_BYTES = 64 * 1024;\nconst MAX_RESULTS_RESPONSE_BYTES = 12 * 1024 * 1024;\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst MAX_CURSOR_LENGTH = 1_024;\nconst MAX_RECORDS_PER_RESPONSE = 10_000;\nconst BoundedJobId = Schema.NonEmptyString.check(Schema.isMaxLength(256));\nconst BoundedCursorString = Schema.NonEmptyString.check(Schema.isMaxLength(MAX_CURSOR_LENGTH));\n// The generated API response Schema says string; the current product example returns a number.\nconst ProviderCursor = Schema.Union([BoundedCursorString, Schema.Natural]);\nconst ProviderJobStatus = Schema.Literals([\n \"running\",\n \"completed\",\n \"errored\",\n \"cancelled_by_user\",\n \"cancelled_due_to_timeout\",\n \"cancelled_due_to_limits\",\n]);\nconst ProviderResult = Schema.Struct({\n id: BoundedJobId,\n status: ProviderJobStatus,\n browserSecondsUsed: Schema.optionalKey(Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))),\n total: Schema.optionalKey(Schema.Natural),\n finished: Schema.optionalKey(Schema.Natural),\n skipped: Schema.optionalKey(Schema.Natural),\n records: Schema.Array(PageCrawlRecord).check(Schema.isMaxLength(MAX_RECORDS_PER_RESPONSE)),\n cursor: Schema.optionalKey(ProviderCursor),\n});\nconst CreateEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: BoundedJobId,\n});\nconst ResultEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: ProviderResult,\n});\nconst DeleteEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Struct({\n job_id: BoundedJobId,\n message: Schema.String.check(Schema.isMaxLength(MAX_DIAGNOSTIC_LENGTH)),\n }),\n});\n\n/** Explicit credentials. The token is projected only into fixed-origin Authorization headers. */\nexport interface BrowserRestCrawlOptions {\n readonly accountId: string;\n readonly apiToken: Redacted.Redacted<string>;\n}\n\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\nconst privateCause = (value: unknown, apiToken: Redacted.Redacted<string>): Error | undefined => {\n const raw = boundedDiagnostic(String(value));\n if (raw.length === 0) return undefined;\n const token = Redacted.value(apiToken);\n return new Error(token.length === 0 ? raw : raw.replaceAll(token, \"[REDACTED]\"));\n};\n\nconst protocolError = (message: string, cause?: unknown): PageCrawlProtocolError =>\n PageCrawlProtocolError.make({\n implementation: browserRestCrawlImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst limitError = (\n request: PageCrawlRequest,\n limit: PageCrawlLimitError[\"limit\"],\n observed: number,\n): PageCrawlLimitError => {\n const maximum =\n limit === \"pages\"\n ? request.limits.maxPages\n : limit === \"page-bytes\"\n ? request.limits.maxPageBytes\n : limit === \"total-bytes\"\n ? request.limits.maxTotalBytes\n : request.limits.deadlineMillis;\n return PageCrawlLimitError.make({\n implementation: browserRestCrawlImplementation,\n limit,\n maximum,\n observed,\n message: `The crawl exceeded its ${limit} limit`,\n });\n};\n\nconst retryAfterMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const seconds = Number(headers[\"retry-after\"]);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst isJsonResponse = (headers: Readonly<Record<string, string | undefined>>): boolean => {\n const contentType = headers[\"content-type\"];\n if (contentType === undefined) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst isQuotaMessage = (bodyText: string): boolean => /time limit|daily|quota/i.test(bodyText);\n\nconst endpoint = (options: BrowserRestCrawlOptions, jobId?: string): string =>\n `${API_ORIGIN}/client/v4/accounts/${encodeURIComponent(options.accountId)}/browser-rendering/crawl${\n jobId === undefined ? \"\" : `/${encodeURIComponent(jobId)}`\n }`;\n\nconst readBoundedResponse = Effect.fn(\"BrowserRestCrawl.readBoundedResponse\")(function* (\n response: HttpClientResponse.HttpClientResponse,\n maximum: number,\n): Effect.fn.Return<string, PageCrawlProtocolError> {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n const state = yield* Stream.runFoldEffect<\n Uint8Array,\n HttpClientError.HttpClientError,\n never,\n { readonly observed: number; readonly text: string },\n PageCrawlProtocolError,\n never\n >(\n response.stream,\n () => ({ observed: 0, text: \"\" }),\n (current, chunk) => {\n const observed = current.observed + chunk.byteLength;\n if (observed > maximum) {\n return Effect.fail(\n protocolError(\n `The Browser Run crawl response exceeded ${String(maximum)} transport bytes`,\n ),\n );\n }\n return Effect.try({\n try: () => ({ observed, text: current.text + decoder.decode(chunk, { stream: true }) }),\n catch: (cause) => protocolError(\"Decoding the Browser Run crawl response failed\", cause),\n });\n },\n ).pipe(\n Effect.mapError((cause) =>\n Schema.is(PageCrawlProtocolError)(cause)\n ? cause\n : protocolError(\"Reading the Browser Run crawl response failed\", cause),\n ),\n );\n return yield* Effect.try({\n try: () => state.text + decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Browser Run crawl response failed\", cause),\n });\n});\n\nconst deadlineFailure = Effect.fn(\"BrowserRestCrawl.deadlineFailure\")(function* (\n request: PageCrawlRequest,\n startedAt: number,\n) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n return yield* limitError(request, \"deadline\", Math.max(0, now - startedAt));\n});\n\nconst withinDeadline = Effect.fn(\"BrowserRestCrawl.withinDeadline\")(function* <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n request: PageCrawlRequest,\n startedAt: number,\n): Effect.fn.Return<A, E | PageCrawlLimitError, R> {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const elapsed = Math.max(0, now - startedAt);\n const remaining = request.limits.deadlineMillis - elapsed;\n if (remaining <= 0) return yield* limitError(request, \"deadline\", elapsed);\n return yield* effect.pipe(\n Effect.timeoutOrElse({\n duration: Duration.millis(remaining),\n orElse: () => deadlineFailure(request, startedAt),\n }),\n );\n});\n\nconst checkDeadline = Effect.fn(\"BrowserRestCrawl.checkDeadline\")(function* (\n request: PageCrawlRequest,\n startedAt: number,\n) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const elapsed = Math.max(0, now - startedAt);\n if (elapsed >= request.limits.deadlineMillis) {\n return yield* limitError(request, \"deadline\", elapsed);\n }\n});\n\nconst normalizedCursor = (cursor: typeof ProviderCursor.Type): string => String(cursor);\n\ninterface PaginationState {\n readonly cursor: Option.Option<string>;\n readonly seen: ReadonlyArray<string>;\n}\n\ninterface RecordLimitsState {\n readonly pages: number;\n readonly totalBytes: number;\n}\n\nconst sameHost = (expectedHost: string, url: string): boolean => new URL(url).host === expectedHost;\n\nconst makeCrawl =\n (client: HttpClient.HttpClient, options: BrowserRestCrawlOptions): PageCrawlCrawl =>\n (input) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const startedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const startHost = new URL(input.startUrl).host;\n\n const executeJson = Effect.fn(\"BrowserRestCrawl.executeJson\")(function* <\n S extends Schema.Top,\n >(request: HttpClientRequest.HttpClientRequest, schema: S, maximum: number) {\n const authorized = HttpClientRequest.bearerToken(request, options.apiToken).pipe(\n HttpClientRequest.acceptJson,\n );\n const response = yield* client\n .execute(authorized)\n .pipe(\n Effect.mapError((cause) =>\n protocolError(\n \"Calling the Browser Run crawl REST API failed\",\n privateCause(cause, options.apiToken),\n ),\n ),\n );\n const bodyText = yield* readBoundedResponse(response, maximum);\n if (response.status === 429) {\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n return yield* PageCrawlRateLimitedError.make({\n implementation: browserRestCrawlImplementation,\n reason,\n ...(retryAfterMillis(response.headers) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response.headers) }),\n message:\n reason === \"quota\"\n ? \"Browser Run exceeded its browser quota\"\n : \"Browser Run crawl was rate limited\",\n ...(privateCause(bodyText, options.apiToken) === undefined\n ? {}\n : { cause: privateCause(bodyText, options.apiToken) }),\n });\n }\n if (response.status < 200 || response.status >= 300) {\n return yield* protocolError(\n `Browser Run crawl answered HTTP ${String(response.status)}`,\n privateCause(bodyText, options.apiToken),\n );\n }\n if (!isJsonResponse(response.headers)) {\n return yield* protocolError(\n \"The Browser Run crawl success response was not JSON\",\n privateCause(bodyText, options.apiToken),\n );\n }\n return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(bodyText).pipe(\n Effect.mapError((cause) =>\n protocolError(\n \"Browser Run returned a malformed crawl response\",\n privateCause(cause, options.apiToken),\n ),\n ),\n );\n });\n\n const createJob = Effect.gen(function* () {\n const requestWithBody = yield* HttpClientRequest.post(endpoint(options)).pipe(\n HttpClientRequest.bodyJson({\n url: input.startUrl,\n crawlPurposes: [...input.purposes],\n limit: input.limits.maxPages,\n depth: input.limits.maxDepth,\n formats: [\"markdown\"],\n render: true,\n options: {\n includeExternalLinks: false,\n includeSubdomains: false,\n },\n }),\n Effect.mapError((cause) =>\n protocolError(\"Encoding the Browser Run crawl request failed\", cause),\n ),\n );\n const created = yield* withinDeadline(\n executeJson(requestWithBody, CreateEnvelope, MAX_CONTROL_RESPONSE_BYTES),\n input,\n startedAt,\n );\n return created.result;\n });\n\n const job = yield* Effect.uninterruptibleMask((restore) =>\n restore(createJob).pipe(\n Effect.flatMap((id) =>\n Effect.gen(function* () {\n const state = yield* Ref.make<\"running\" | \"terminal\">(\"running\");\n const scope = yield* Effect.scope;\n\n const cancelIfRunning = Ref.modify(state, (current) =>\n current === \"running\"\n ? [true, \"terminal\" as const]\n : [false, \"terminal\" as const],\n ).pipe(\n Effect.flatMap((shouldCancel) => {\n if (!shouldCancel) return Effect.void;\n const cancellation = executeJson(\n HttpClientRequest.delete(endpoint(options, id)),\n DeleteEnvelope,\n MAX_CONTROL_RESPONSE_BYTES,\n ).pipe(\n Effect.flatMap((deleted) =>\n deleted.result.job_id === id\n ? Effect.void\n : protocolError(\"Browser Run cancelled a different crawl job\"),\n ),\n );\n return cancellation.pipe(\n Effect.timeoutOrElse({\n duration: CANCEL_TIMEOUT,\n orElse: () =>\n protocolError(\"Cancelling the Browser Run crawl exceeded 30 seconds\"),\n }),\n );\n }),\n );\n\n yield* Scope.addFinalizer(\n scope,\n cancelIfRunning.pipe(\n Effect.catchCause(() =>\n Effect.logWarning(\"Browser Run crawl cancellation failed\"),\n ),\n ),\n );\n return { id, state } as const;\n }),\n ),\n ),\n );\n\n const fetchResult = Effect.fn(\"BrowserRestCrawl.fetchResult\")(function* (\n cursor: Option.Option<string>,\n statusOnly: boolean,\n ) {\n let request = HttpClientRequest.get(endpoint(options, job.id));\n if (statusOnly) request = HttpClientRequest.setUrlParam(request, \"limit\", \"1\");\n if (Option.isSome(cursor)) {\n request = HttpClientRequest.setUrlParam(request, \"cursor\", cursor.value);\n }\n // `limit=1` still permits one complete record, so poll GETs use the bounded result cap.\n const envelope = yield* withinDeadline(\n executeJson(request, ResultEnvelope, MAX_RESULTS_RESPONSE_BYTES),\n input,\n startedAt,\n );\n if (envelope.result.id !== job.id) {\n return yield* protocolError(\"Browser Run returned a different crawl job identity\");\n }\n return envelope.result;\n });\n\n const terminal = yield* Effect.uninterruptibleMask((restore) =>\n restore(\n withinDeadline(\n Effect.repeat(fetchResult(Option.none(), true), {\n schedule: Schedule.spaced(POLL_INTERVAL),\n until: (result) => result.status !== \"running\",\n }),\n input,\n startedAt,\n ),\n ).pipe(Effect.tap(() => Ref.set(job.state, \"terminal\"))),\n );\n if (terminal.status === \"running\") {\n return yield* protocolError(\"Browser Run polling stopped before a terminal status\");\n }\n if (terminal.status !== \"completed\") {\n return yield* PageCrawlTerminalError.make({\n implementation: browserRestCrawlImplementation,\n status: terminal.status,\n message: `Browser Run crawl ended with status ${terminal.status}`,\n });\n }\n\n const pages = Stream.paginate<PaginationState, PageCrawlRecord, PageCrawlError>(\n { cursor: Option.none(), seen: [] },\n (state) =>\n Effect.gen(function* () {\n const result = yield* fetchResult(state.cursor, false);\n if (result.status !== \"completed\") {\n return yield* protocolError(\n \"Browser Run changed crawl status during result pagination\",\n );\n }\n if (result.cursor === undefined) {\n return [result.records, Option.none()] as const;\n }\n const cursor = normalizedCursor(result.cursor);\n if (state.seen.includes(cursor)) {\n return yield* protocolError(\"Browser Run repeated a crawl result cursor\");\n }\n if (state.seen.length >= input.limits.maxPages) {\n return yield* protocolError(\"Browser Run returned too many crawl result cursors\");\n }\n return [\n result.records,\n Option.some({ cursor: Option.some(cursor), seen: [...state.seen, cursor] }),\n ] as const;\n }),\n );\n\n return pages.pipe(\n Stream.rechunk(1),\n Stream.mapAccumEffect(\n (): RecordLimitsState => ({ pages: 0, totalBytes: 0 }),\n (state, record) =>\n Effect.gen(function* () {\n yield* checkDeadline(input, startedAt);\n const pages = state.pages + 1;\n if (pages > input.limits.maxPages) {\n return yield* limitError(input, \"pages\", pages);\n }\n if (!sameHost(startHost, record.url)) {\n return yield* protocolError(\"Browser Run returned an off-host crawl record\");\n }\n if (record.metadata !== undefined && !sameHost(startHost, record.metadata.url)) {\n return yield* protocolError(\"Browser Run returned off-host crawl metadata\");\n }\n const pageBytes =\n record.markdown === undefined\n ? 0\n : new TextEncoder().encode(record.markdown).byteLength;\n if (pageBytes > input.limits.maxPageBytes) {\n return yield* limitError(input, \"page-bytes\", pageBytes);\n }\n const totalBytes = state.totalBytes + pageBytes;\n if (totalBytes > input.limits.maxTotalBytes) {\n return yield* limitError(input, \"total-bytes\", totalBytes);\n }\n return [{ pages, totalBytes }, [record]] as const;\n }),\n ),\n );\n }),\n );\n\n/** Cloudflare REST PageCrawl Layer for Node and other non-Worker composition roots. */\nexport const browserRestCrawlLayer = (\n options: BrowserRestCrawlOptions,\n): Layer.Layer<PageCrawl, never, HttpClient.HttpClient> =>\n Layer.effect(\n PageCrawl,\n Effect.map(HttpClient.HttpClient, (client) =>\n PageCrawl.of({ crawl: makeCrawl(client, options) }),\n ),\n );\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser-rest-crawl.mjs","names":[],"sources":["../src/browser-rest-crawl.ts"],"sourcesContent":["import {\n PageCrawl,\n PageCrawlLimitError,\n PageCrawlProtocolError,\n PageCrawlRateLimitedError,\n PageCrawlRecord,\n PageCrawlTerminalError,\n SandboxImplementation,\n type PageCrawlCrawl,\n type PageCrawlError,\n type PageCrawlRequest,\n} from \"@effect-agent/sandbox\";\nimport {\n Duration,\n Effect,\n Layer,\n Option,\n Redacted,\n Ref,\n Schedule,\n Schema,\n Scope,\n Stream,\n} from \"effect\";\nimport {\n HttpClient,\n HttpClientRequest,\n type HttpClientError,\n type HttpClientResponse,\n} from \"effect/unstable/http\";\n\n/** Node-safe REST crawl implementation. It never exposes or persists provider job identity. */\nexport const browserRestCrawlImplementation = SandboxImplementation.make({\n isolation: \"isolated\",\n identity: \"cloudflare-browser-rest-crawl\",\n});\n\nconst API_ORIGIN = \"https://api.cloudflare.com\";\nconst POLL_INTERVAL = Duration.seconds(1);\nconst CANCEL_TIMEOUT = Duration.seconds(30);\nconst MAX_CONTROL_RESPONSE_BYTES = 64 * 1024;\nconst MAX_RESULTS_RESPONSE_BYTES = 12 * 1024 * 1024;\nconst MAX_DIAGNOSTIC_LENGTH = 8_000;\nconst MAX_CURSOR_LENGTH = 1_024;\nconst MAX_RECORDS_PER_RESPONSE = 10_000;\nconst BoundedJobId = Schema.NonEmptyString.check(Schema.isMaxLength(256));\nconst BoundedCursorString = Schema.NonEmptyString.check(Schema.isMaxLength(MAX_CURSOR_LENGTH));\n// The generated API response Schema says string; the current product example returns a number.\nconst ProviderCursor = Schema.Union([BoundedCursorString, Schema.Natural]);\nconst ProviderJobStatus = Schema.Literals([\n \"running\",\n \"completed\",\n \"errored\",\n \"cancelled_by_user\",\n \"cancelled_due_to_timeout\",\n \"cancelled_due_to_limits\",\n]);\nconst ProviderResult = Schema.Struct({\n id: BoundedJobId,\n status: ProviderJobStatus,\n browserSecondsUsed: Schema.optionalKey(Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))),\n total: Schema.optionalKey(Schema.Natural),\n finished: Schema.optionalKey(Schema.Natural),\n skipped: Schema.optionalKey(Schema.Natural),\n records: Schema.Array(PageCrawlRecord).check(Schema.isMaxLength(MAX_RECORDS_PER_RESPONSE)),\n cursor: Schema.optionalKey(ProviderCursor),\n});\nconst CreateEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: BoundedJobId,\n});\nconst ResultEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: ProviderResult,\n});\nconst DeleteEnvelope = Schema.Struct({\n success: Schema.Literal(true),\n result: Schema.Struct({\n job_id: BoundedJobId,\n message: Schema.String.check(Schema.isMaxLength(MAX_DIAGNOSTIC_LENGTH)),\n }),\n});\n\n/** Explicit credentials. The token is projected only into fixed-origin Authorization headers. */\nexport interface BrowserRestCrawlOptions {\n readonly accountId: string;\n readonly apiToken: Redacted.Redacted<string>;\n}\n\nconst boundedDiagnostic = (message: string): string => message.slice(0, MAX_DIAGNOSTIC_LENGTH);\n\nconst privateCause = (value: unknown, apiToken: Redacted.Redacted<string>): Error | undefined => {\n const raw = boundedDiagnostic(String(value));\n if (raw.length === 0) return undefined;\n const token = Redacted.value(apiToken);\n return new Error(token.length === 0 ? raw : raw.replaceAll(token, \"[REDACTED]\"));\n};\n\nconst protocolError = (message: string, cause?: unknown): PageCrawlProtocolError =>\n PageCrawlProtocolError.make({\n implementation: browserRestCrawlImplementation,\n message: boundedDiagnostic(message),\n ...(cause === undefined ? {} : { cause }),\n });\n\nconst limitError = (\n request: PageCrawlRequest,\n limit: PageCrawlLimitError[\"limit\"],\n observed: number,\n): PageCrawlLimitError => {\n const maximum =\n limit === \"pages\"\n ? request.limits.maxPages\n : limit === \"page-bytes\"\n ? request.limits.maxPageBytes\n : limit === \"total-bytes\"\n ? request.limits.maxTotalBytes\n : request.limits.deadlineMillis;\n return PageCrawlLimitError.make({\n implementation: browserRestCrawlImplementation,\n limit,\n maximum,\n observed,\n message: `The crawl exceeded its ${limit} limit`,\n });\n};\n\nconst retryAfterMillis = (\n headers: Readonly<Record<string, string | undefined>>,\n): number | undefined => {\n const seconds = Number(headers[\"retry-after\"]);\n if (!Number.isSafeInteger(seconds) || seconds < 0) return undefined;\n const millis = seconds * 1_000;\n return Number.isSafeInteger(millis) ? millis : undefined;\n};\n\nconst isJsonResponse = (headers: Readonly<Record<string, string | undefined>>): boolean => {\n const contentType = headers[\"content-type\"];\n if (contentType === undefined) return false;\n const mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n return mediaType === \"application/json\" || mediaType?.endsWith(\"+json\") === true;\n};\n\nconst isQuotaMessage = (bodyText: string): boolean => /time limit|daily|quota/i.test(bodyText);\n\nconst endpoint = (options: BrowserRestCrawlOptions, jobId?: string): string =>\n `${API_ORIGIN}/client/v4/accounts/${encodeURIComponent(options.accountId)}/browser-rendering/crawl${\n jobId === undefined ? \"\" : `/${encodeURIComponent(jobId)}`\n }`;\n\nconst readBoundedResponse = Effect.fn(\"BrowserRestCrawl.readBoundedResponse\")(function* (\n response: HttpClientResponse.HttpClientResponse,\n maximum: number,\n): Effect.fn.Return<string, PageCrawlProtocolError> {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: false });\n const state = yield* Stream.runFoldEffect<\n Uint8Array,\n HttpClientError.HttpClientError,\n never,\n { readonly observed: number; readonly text: string },\n PageCrawlProtocolError,\n never\n >(\n response.stream,\n () => ({ observed: 0, text: \"\" }),\n (current, chunk) => {\n const observed = current.observed + chunk.byteLength;\n if (observed > maximum) {\n return Effect.fail(\n protocolError(\n `The Browser Run crawl response exceeded ${String(maximum)} transport bytes`,\n ),\n );\n }\n return Effect.try({\n try: () => ({ observed, text: current.text + decoder.decode(chunk, { stream: true }) }),\n catch: (cause) => protocolError(\"Decoding the Browser Run crawl response failed\", cause),\n });\n },\n ).pipe(\n Effect.mapError((cause) =>\n Schema.is(PageCrawlProtocolError)(cause)\n ? cause\n : protocolError(\"Reading the Browser Run crawl response failed\", cause),\n ),\n );\n return yield* Effect.try({\n try: () => state.text + decoder.decode(),\n catch: (cause) => protocolError(\"Decoding the Browser Run crawl response failed\", cause),\n });\n});\n\nconst deadlineFailure = Effect.fn(\"BrowserRestCrawl.deadlineFailure\")(function* (\n request: PageCrawlRequest,\n startedAt: number,\n) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n return yield* limitError(request, \"deadline\", Math.max(0, now - startedAt));\n});\n\nconst withinDeadline = Effect.fn(\"BrowserRestCrawl.withinDeadline\")(function* <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n request: PageCrawlRequest,\n startedAt: number,\n): Effect.fn.Return<A, E | PageCrawlLimitError, R> {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const elapsed = Math.max(0, now - startedAt);\n const remaining = request.limits.deadlineMillis - elapsed;\n if (remaining <= 0) return yield* limitError(request, \"deadline\", elapsed);\n return yield* effect.pipe(\n Effect.timeoutOrElse({\n duration: Duration.millis(remaining),\n orElse: () => deadlineFailure(request, startedAt),\n }),\n );\n});\n\nconst checkDeadline = Effect.fn(\"BrowserRestCrawl.checkDeadline\")(function* (\n request: PageCrawlRequest,\n startedAt: number,\n) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const elapsed = Math.max(0, now - startedAt);\n if (elapsed >= request.limits.deadlineMillis) {\n return yield* limitError(request, \"deadline\", elapsed);\n }\n});\n\nconst normalizedCursor = (cursor: typeof ProviderCursor.Type): string => String(cursor);\n\ninterface PaginationState {\n readonly cursor: Option.Option<string>;\n readonly seen: ReadonlyArray<string>;\n}\n\ninterface RecordLimitsState {\n readonly pages: number;\n readonly totalBytes: number;\n}\n\nconst sameHost = (expectedHost: string, url: string): boolean => new URL(url).host === expectedHost;\n\nconst makeCrawl =\n (client: HttpClient.HttpClient, options: BrowserRestCrawlOptions): PageCrawlCrawl =>\n (input) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const startedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const startHost = new URL(input.startUrl).host;\n\n const executeJson = Effect.fn(\"BrowserRestCrawl.executeJson\")(function* <\n S extends Schema.Top,\n >(request: HttpClientRequest.HttpClientRequest, schema: S, maximum: number) {\n const authorized = HttpClientRequest.bearerToken(request, options.apiToken).pipe(\n HttpClientRequest.acceptJson,\n );\n const response = yield* client\n .execute(authorized)\n .pipe(\n Effect.mapError((cause) =>\n protocolError(\n \"Calling the Browser Run crawl REST API failed\",\n privateCause(cause, options.apiToken),\n ),\n ),\n );\n const bodyText = yield* readBoundedResponse(response, maximum);\n if (response.status === 429) {\n const reason = isQuotaMessage(bodyText) ? \"quota\" : \"rate\";\n return yield* PageCrawlRateLimitedError.make({\n implementation: browserRestCrawlImplementation,\n reason,\n ...(retryAfterMillis(response.headers) === undefined\n ? {}\n : { retryAfterMillis: retryAfterMillis(response.headers) }),\n message:\n reason === \"quota\"\n ? \"Browser Run exceeded its browser quota\"\n : \"Browser Run crawl was rate limited\",\n ...(privateCause(bodyText, options.apiToken) === undefined\n ? {}\n : { cause: privateCause(bodyText, options.apiToken) }),\n });\n }\n if (response.status < 200 || response.status >= 300) {\n return yield* protocolError(\n `Browser Run crawl answered HTTP ${String(response.status)}`,\n privateCause(bodyText, options.apiToken),\n );\n }\n if (!isJsonResponse(response.headers)) {\n return yield* protocolError(\n \"The Browser Run crawl success response was not JSON\",\n privateCause(bodyText, options.apiToken),\n );\n }\n return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(bodyText).pipe(\n Effect.mapError((cause) =>\n protocolError(\n \"Browser Run returned a malformed crawl response\",\n privateCause(cause, options.apiToken),\n ),\n ),\n );\n });\n\n const createJob = Effect.gen(function* () {\n const requestWithBody = yield* HttpClientRequest.post(endpoint(options)).pipe(\n HttpClientRequest.bodyJson({\n url: input.startUrl,\n crawlPurposes: [...input.purposes],\n limit: input.limits.maxPages,\n depth: input.limits.maxDepth,\n formats: [\"markdown\"],\n render: true,\n options: {\n includeExternalLinks: false,\n includeSubdomains: false,\n },\n }),\n Effect.mapError((cause) =>\n protocolError(\"Encoding the Browser Run crawl request failed\", cause),\n ),\n );\n const created = yield* withinDeadline(\n executeJson(requestWithBody, CreateEnvelope, MAX_CONTROL_RESPONSE_BYTES),\n input,\n startedAt,\n );\n return created.result;\n });\n\n const job = yield* Effect.uninterruptibleMask((restore) =>\n restore(createJob).pipe(\n Effect.flatMap((id) =>\n Effect.gen(function* () {\n const state = yield* Ref.make<\"running\" | \"terminal\">(\"running\");\n const scope = yield* Effect.scope;\n\n const cancelIfRunning = Ref.modify(state, (current) =>\n current === \"running\"\n ? [true, \"terminal\" as const]\n : [false, \"terminal\" as const],\n ).pipe(\n Effect.flatMap((shouldCancel) => {\n if (!shouldCancel) return Effect.void;\n const cancellation = executeJson(\n HttpClientRequest.delete(endpoint(options, id)),\n DeleteEnvelope,\n MAX_CONTROL_RESPONSE_BYTES,\n ).pipe(\n Effect.flatMap((deleted) =>\n deleted.result.job_id === id\n ? Effect.void\n : protocolError(\"Browser Run cancelled a different crawl job\"),\n ),\n );\n return cancellation.pipe(\n Effect.timeoutOrElse({\n duration: CANCEL_TIMEOUT,\n orElse: () =>\n protocolError(\"Cancelling the Browser Run crawl exceeded 30 seconds\"),\n }),\n );\n }),\n );\n\n yield* Scope.addFinalizer(\n scope,\n cancelIfRunning.pipe(\n Effect.catchCause(() =>\n Effect.logWarning(\"Browser Run crawl cancellation failed\"),\n ),\n ),\n );\n return { id, state } as const;\n }),\n ),\n ),\n );\n\n const fetchResult = Effect.fn(\"BrowserRestCrawl.fetchResult\")(function* (\n cursor: Option.Option<string>,\n statusOnly: boolean,\n ) {\n let request = HttpClientRequest.get(endpoint(options, job.id));\n if (statusOnly) request = HttpClientRequest.setUrlParam(request, \"limit\", \"1\");\n if (Option.isSome(cursor)) {\n request = HttpClientRequest.setUrlParam(request, \"cursor\", cursor.value);\n }\n // `limit=1` still permits one complete record, so poll GETs use the bounded result cap.\n const envelope = yield* withinDeadline(\n executeJson(request, ResultEnvelope, MAX_RESULTS_RESPONSE_BYTES),\n input,\n startedAt,\n );\n if (envelope.result.id !== job.id) {\n return yield* protocolError(\"Browser Run returned a different crawl job identity\");\n }\n return envelope.result;\n });\n\n const terminal = yield* Effect.uninterruptibleMask((restore) =>\n restore(\n withinDeadline(\n Effect.repeat(fetchResult(Option.none(), true), {\n schedule: Schedule.spaced(POLL_INTERVAL),\n until: (result) => result.status !== \"running\",\n }),\n input,\n startedAt,\n ),\n ).pipe(Effect.tap(() => Ref.set(job.state, \"terminal\"))),\n );\n if (terminal.status === \"running\") {\n return yield* protocolError(\"Browser Run polling stopped before a terminal status\");\n }\n if (terminal.status !== \"completed\") {\n return yield* PageCrawlTerminalError.make({\n implementation: browserRestCrawlImplementation,\n status: terminal.status,\n message: `Browser Run crawl ended with status ${terminal.status}`,\n });\n }\n\n const pages = Stream.paginate<PaginationState, PageCrawlRecord, PageCrawlError>(\n { cursor: Option.none(), seen: [] },\n (state) =>\n Effect.gen(function* () {\n const result = yield* fetchResult(state.cursor, false);\n if (result.status !== \"completed\") {\n return yield* protocolError(\n \"Browser Run changed crawl status during result pagination\",\n );\n }\n if (result.cursor === undefined) {\n return [result.records, Option.none()] as const;\n }\n const cursor = normalizedCursor(result.cursor);\n if (state.seen.includes(cursor)) {\n return yield* protocolError(\"Browser Run repeated a crawl result cursor\");\n }\n if (state.seen.length >= input.limits.maxPages) {\n return yield* protocolError(\"Browser Run returned too many crawl result cursors\");\n }\n return [\n result.records,\n Option.some({ cursor: Option.some(cursor), seen: [...state.seen, cursor] }),\n ] as const;\n }),\n );\n\n return pages.pipe(\n Stream.rechunk(1),\n Stream.mapAccumEffect(\n (): RecordLimitsState => ({ pages: 0, totalBytes: 0 }),\n (state, record) =>\n Effect.gen(function* () {\n yield* checkDeadline(input, startedAt);\n const pages = state.pages + 1;\n if (pages > input.limits.maxPages) {\n return yield* limitError(input, \"pages\", pages);\n }\n if (!sameHost(startHost, record.url)) {\n return yield* protocolError(\"Browser Run returned an off-host crawl record\");\n }\n if (record.metadata !== undefined && !sameHost(startHost, record.metadata.url)) {\n return yield* protocolError(\"Browser Run returned off-host crawl metadata\");\n }\n const pageBytes =\n record.markdown === undefined\n ? 0\n : new TextEncoder().encode(record.markdown).byteLength;\n if (pageBytes > input.limits.maxPageBytes) {\n return yield* limitError(input, \"page-bytes\", pageBytes);\n }\n const totalBytes = state.totalBytes + pageBytes;\n if (totalBytes > input.limits.maxTotalBytes) {\n return yield* limitError(input, \"total-bytes\", totalBytes);\n }\n return [{ pages, totalBytes }, [record]] as const;\n }),\n ),\n );\n }),\n );\n\n/** Cloudflare REST PageCrawl Layer for Node and other non-Worker composition roots. */\nexport const browserRestCrawlLayer = (\n options: BrowserRestCrawlOptions,\n): Layer.Layer<PageCrawl, never, HttpClient.HttpClient> =>\n Layer.effect(\n PageCrawl,\n Effect.map(HttpClient.HttpClient, (client) =>\n PageCrawl.of({ crawl: makeCrawl(client, options) }),\n ),\n );\n"],"mappings":"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"}
|