{
  "version": 3,
  "sources": ["../../../../src/workers/core/outbound.worker.ts", "../../../../src/workers/core/constants.ts"],
  "sourcesContent": ["// General-purpose outbound interceptor for user Workers.\n//\n// This Worker sits at the top of every user Worker's `globalOutbound` chain and\n// applies a small pipeline to outbound `fetch()`es before they continue down to\n// the previous outbound (the user's `outboundService`, or the internet):\n//\n//   1. `stripCfConnectingIp` \u2014 removes the `CF-Connecting-IP` header and sets\n//      `CF-Worker`, matching production edge behaviour. Toggled by the public\n//      `stripCfConnectingIp` Miniflare option.\n//   2. `cf.image` \u2014 when a request carries `cf.image` transform options\n//      (`fetch(url, { cf: { image: { ... } } })`), the origin image is fetched\n//      and transformed locally via Sharp, mirroring the production\n//      \"transform via Workers\" feature. This is always on.\n\nimport { LogLevel, SharedHeaders } from \"miniflare:shared\";\nimport { CoreBindings, CoreHeaders } from \"./constants\";\nimport type { RequestInitCfPropertiesImage } from \"@cloudflare/workers-types/experimental\";\n\ninterface Env {\n\tCF_WORKER_ZONE: string;\n\tSTRIP_CF_CONNECTING_IP: boolean;\n\t[CoreBindings.SERVICE_LOOPBACK]: Fetcher;\n}\n\nconst RESIZING_VIA_TOKEN = \"image-resizing\";\n\nlet warnedLowFidelity = false;\n\nexport default {\n\tasync fetch(request, env: Env) {\n\t\tconst image = (\n\t\t\trequest.cf as { image?: RequestInitCfPropertiesImage } | undefined\n\t\t)?.image;\n\n\t\tconst hasImageOptions =\n\t\t\timage !== undefined &&\n\t\t\timage !== null &&\n\t\t\ttypeof image === \"object\" &&\n\t\t\tObject.keys(image).length > 0;\n\n\t\tif (!hasImageOptions) {\n\t\t\tconst headers = strippedHeaders(request, env);\n\t\t\treturn headers ? fetch(request, { headers }) : fetch(request);\n\t\t}\n\n\t\treturn resizeImage(request, env, image);\n\t},\n} satisfies ExportedHandler<Env>;\n\nfunction strippedHeaders(request: Request, env: Env): Headers | undefined {\n\tif (!env.STRIP_CF_CONNECTING_IP) {\n\t\treturn undefined;\n\t}\n\tconst headers = new Headers(request.headers);\n\theaders.delete(\"CF-Connecting-IP\");\n\theaders.set(\"CF-Worker\", env.CF_WORKER_ZONE);\n\treturn headers;\n}\n\nasync function resizeImage(\n\trequest: Request,\n\tenv: Env,\n\timage: RequestInitCfPropertiesImage\n): Promise<Response> {\n\tconst headers = strippedHeaders(request, env) ?? new Headers(request.headers);\n\tconst via = headers.get(\"via\");\n\theaders.set(\n\t\t\"via\",\n\t\tvia ? `${via}, ${RESIZING_VIA_TOKEN}` : RESIZING_VIA_TOKEN\n\t);\n\n\tconst originResponse = await fetch(request, { headers });\n\n\tif (!originResponse.ok) {\n\t\treturn originResponse;\n\t}\n\n\tconst source = await originResponse.arrayBuffer();\n\n\tconst form = new FormData();\n\tform.append(\"image\", new Blob([source]));\n\tform.append(\"options\", JSON.stringify(image));\n\n\tconst transformRequest = new Request(\"http://localhost/cf-image\", {\n\t\tmethod: \"POST\",\n\t\tbody: form,\n\t});\n\ttransformRequest.headers.set(\n\t\tCoreHeaders.CUSTOM_FETCH_SERVICE,\n\t\tCoreBindings.IMAGES_FETCH_SERVICE\n\t);\n\n\tconst transformed =\n\t\tawait env[CoreBindings.SERVICE_LOOPBACK].fetch(transformRequest);\n\n\t// If Sharp can't transform the image return the origin image\n\tif (!transformed.ok) {\n\t\tconst headers = new Headers(originResponse.headers);\n\t\theaders.delete(\"content-encoding\");\n\t\theaders.delete(\"content-length\");\n\t\treturn new Response(source, {\n\t\t\tstatus: originResponse.status,\n\t\t\theaders,\n\t\t});\n\t}\n\n\tawait warnLowFidelityOnce(env);\n\treturn transformed;\n}\n\nasync function warnLowFidelityOnce(env: Env): Promise<void> {\n\tif (warnedLowFidelity) {\n\t\treturn;\n\t}\n\twarnedLowFidelity = true;\n\tawait env[CoreBindings.SERVICE_LOOPBACK].fetch(\"http://localhost/core/log\", {\n\t\tmethod: \"POST\",\n\t\theaders: { [SharedHeaders.LOG_LEVEL]: LogLevel.WARN.toString() },\n\t\tbody: \"Local cf.image transforms are a low-fidelity mock; only resize, rotate and format conversion are applied. Deploy to preview the full transformation.\",\n\t});\n}\n", "/**\n * Reserved `/cdn-cgi/` paths for internal Miniflare endpoints.\n * These paths are reserved by Cloudflare's network and won't conflict with user routes.\n */\nexport const CorePaths = {\n\t/** Magic proxy used by getPlatformProxy */\n\tPLATFORM_PROXY: \"/cdn-cgi/platform-proxy\",\n\t/** Trigger scheduled event handlers */\n\tSCHEDULED: \"/cdn-cgi/handler/scheduled\",\n\t/** Trigger email event handlers */\n\tEMAIL: \"/cdn-cgi/handler/email\",\n\t/** Handler path prefix for validation */\n\tHANDLER_PREFIX: \"/cdn-cgi/handler/\",\n\t/** Live reload WebSocket endpoint */\n\tLIVE_RELOAD: \"/cdn-cgi/mf/reload\",\n\t/** Local explorer UI and API */\n\tEXPLORER: \"/cdn-cgi/explorer\",\n\t/** Legacy way to trigger scheduled event handlers */\n\tLEGACY_SCHEDULED: \"/cdn-cgi/mf/scheduled\",\n\t/** Stream video serving endpoint */\n\tSTREAM_VIDEO: \"/cdn-cgi/mf/stream\",\n\t/** Local image delivery endpoint for serving hosted images */\n\tIMAGE_DELIVERY: \"/cdn-cgi/mf/imagedelivery\",\n\t/** Public R2 bucket object serving endpoint */\n\tR2_PUBLIC: \"/cdn-cgi/local/r2/public\",\n} as const;\n\nexport const CoreHeaders = {\n\tCUSTOM_FETCH_SERVICE: \"MF-Custom-Fetch-Service\",\n\tCUSTOM_NODE_SERVICE: \"MF-Custom-Node-Service\",\n\tORIGINAL_URL: \"MF-Original-URL\",\n\t/**\n\t * Stores the original hostname when using the `upstream` option.\n\t * When requests are proxied to an upstream, the `Host` header is rewritten\n\t * to match the upstream. This header preserves the original hostname\n\t * so Workers can access it if needed.\n\t */\n\tORIGINAL_HOSTNAME: \"MF-Original-Hostname\",\n\tPROXY_SHARED_SECRET: \"MF-Proxy-Shared-Secret\",\n\tDISABLE_PRETTY_ERROR: \"MF-Disable-Pretty-Error\",\n\tERROR_STACK: \"MF-Experimental-Error-Stack\",\n\tROUTE_OVERRIDE: \"MF-Route-Override\",\n\tCF_BLOB: \"MF-CF-Blob\",\n\t/** Used by the Vite plugin to pass through the original `sec-fetch-mode` header */\n\tSEC_FETCH_MODE: \"MF-Sec-Fetch-Mode\",\n\n\t// API Proxy\n\tOP_SECRET: \"MF-Op-Secret\",\n\tOP: \"MF-Op\",\n\tOP_TARGET: \"MF-Op-Target\",\n\tOP_KEY: \"MF-Op-Key\",\n\tOP_SYNC: \"MF-Op-Sync\",\n\tOP_STRINGIFIED_SIZE: \"MF-Op-Stringified-Size\",\n\tOP_RESULT_TYPE: \"MF-Op-Result-Type\",\n\tOP_ORIGINAL_URL: \"MF-Op-Original-URL\",\n} as const;\n\nexport const CoreBindings = {\n\tSERVICE_LOOPBACK: \"MINIFLARE_LOOPBACK\",\n\tSERVICE_USER_ROUTE_PREFIX: \"MINIFLARE_USER_ROUTE_\",\n\tSERVICE_USER_FALLBACK: \"MINIFLARE_USER_FALLBACK\",\n\tTEXT_CUSTOM_SERVICE: \"MINIFLARE_CUSTOM_SERVICE\",\n\t// Backs the Images binding (`env.IMAGES`) \u2014 see imagesLocalFetcher.\n\tIMAGES_BINDING_SERVICE: \"MINIFLARE_IMAGES_BINDING_SERVICE\",\n\t// Backs `fetch(url, { cf: { image } })` transforms \u2014 see cfImageLocalFetcher.\n\tIMAGES_FETCH_SERVICE: \"MINIFLARE_IMAGES_FETCH_SERVICE\",\n\tTEXT_UPSTREAM_URL: \"MINIFLARE_UPSTREAM_URL\",\n\tJSON_CF_BLOB: \"CF_BLOB\",\n\tJSON_ROUTES: \"MINIFLARE_ROUTES\",\n\tJSON_LOG_LEVEL: \"MINIFLARE_LOG_LEVEL\",\n\tDATA_LIVE_RELOAD_SCRIPT: \"MINIFLARE_LIVE_RELOAD_SCRIPT\",\n\tDURABLE_OBJECT_NAMESPACE_PROXY: \"MINIFLARE_PROXY\",\n\tDATA_PROXY_SECRET: \"MINIFLARE_PROXY_SECRET\",\n\tDATA_PROXY_SHARED_SECRET: \"MINIFLARE_PROXY_SHARED_SECRET\",\n\tTRIGGER_HANDLERS: \"TRIGGER_HANDLERS\",\n\tLOG_REQUESTS: \"LOG_REQUESTS\",\n\tSTRIP_DISABLE_PRETTY_ERROR: \"STRIP_DISABLE_PRETTY_ERROR\",\n\tSERVICE_LOCAL_EXPLORER: \"MINIFLARE_LOCAL_EXPLORER\",\n\tEXPLORER_DISK: \"MINIFLARE_EXPLORER_DISK\",\n\tJSON_LOCAL_EXPLORER_BINDING_MAP: \"LOCAL_EXPLORER_BINDING_MAP\",\n\tJSON_LOCAL_EXPLORER_WORKER_NAMES: \"LOCAL_EXPLORER_WORKER_NAMES\",\n\tJSON_EXPLORER_WORKER_OPTS: \"MINIFLARE_EXPLORER_WORKER_OPTS\",\n\tSERVICE_CACHE: \"MINIFLARE_CACHE\",\n\tSERVICE_DEV_REGISTRY_PROXY: \"MINIFLARE_DEV_REGISTRY_PROXY\",\n\tJSON_TELEMETRY_CONFIG: \"MINIFLARE_TELEMETRY_CONFIG\",\n\tDEV_REGISTRY_DEBUG_PORT: \"DEV_REGISTRY_DEBUG_PORT\",\n\tSERVICE_STREAM: \"MINIFLARE_STREAM\",\n\tSERVICE_IMAGES_DELIVERY: \"MINIFLARE_IMAGES_DELIVERY\",\n\tSERVICE_R2_PUBLIC: \"MINIFLARE_R2_PUBLIC\",\n} as const;\n\nexport const ProxyOps = {\n\t// Get the target or a property of the target\n\tGET: \"GET\",\n\t// Get the descriptor for a property of the target\n\tGET_OWN_DESCRIPTOR: \"GET_OWN_DESCRIPTOR\",\n\t// Get the target's own property names\n\tGET_OWN_KEYS: \"GET_OWN_KEYS\",\n\t// Call a method on the target\n\tCALL: \"CALL\",\n\t// Remove the strong reference to the target on the \"heap\", allowing it to be\n\t// garbage collected\n\tFREE: \"FREE\",\n} as const;\nexport const ProxyAddresses = {\n\tGLOBAL: 0, // globalThis\n\tENV: 1, // env\n\tUSER_START: 2,\n} as const;\n\n// ### Proxy Special Cases\n// The proxy supports serialising `Request`/`Response`s for the Cache API. It\n// doesn't support serialising `WebSocket`s though. Rather than attempting this,\n// we call `Fetcher#fetch()` using `dispatchFetch()` directly, using the passed\n// request. This gives us WebSocket support, and is much more efficient, since\n// there's no need to serialise the `Request`/`Response`: we just pass\n// everything to `dispatchFetch()` and return what that gives us.\nexport function isFetcherFetch(targetName: string, key: string) {\n\t// `DurableObject` and `WorkerRpc` are the internal names of `DurableObjectStub`:\n\t// https://github.com/cloudflare/workerd/blob/62b9ceee4c94d2b238692397dc4f604fef84f474/src/workerd/api/actor.h#L86\n\t// https://github.com/cloudflare/workerd/blob/62b9ceee4c94d2b238692397dc4f604fef84f474/src/workerd/api/worker-rpc.h#L30\n\treturn (\n\t\t(targetName === \"Fetcher\" ||\n\t\t\ttargetName === \"DurableObject\" ||\n\t\t\ttargetName === \"WorkerRpc\") &&\n\t\tkey === \"fetch\"\n\t);\n}\n// `R2Object#writeHttpMetadata()` is one of the few functions that mutates its\n// arguments. This would be proxied correctly if the argument were a native\n// target proxy itself, but `Headers` can be constructed in Node. Instead, we\n// respond with the updated headers in the proxy server, then copy them to the\n// original argument on the client.\nexport function isR2ObjectWriteHttpMetadata(targetName: string, key: string) {\n\t// `HeadResult` and `GetResult` are the internal names of `R2Object` and `R2ObjectBody` respectively:\n\t// https://github.com/cloudflare/workerd/blob/ae612f0563d864c82adbfa4c2e5ed78b547aa0a1/src/workerd/api/r2-bucket.h#L210\n\t// https://github.com/cloudflare/workerd/blob/ae612f0563d864c82adbfa4c2e5ed78b547aa0a1/src/workerd/api/r2-bucket.h#L263-L264\n\treturn (\n\t\t(targetName === \"HeadResult\" || targetName === \"GetResult\") &&\n\t\tkey === \"writeHttpMetadata\"\n\t);\n}\n\n/**\n * See #createMediaProxy() comment for why this is special\n */\nexport function isImagesInput(targetName: string, key: string) {\n\treturn targetName === \"ImagesBindingImpl\" && key === \"input\";\n}\n\n// Durable Object stub RPC calls should always be async to avoid blocking the\n// Node.js event loop. The internal names are \"DurableObject\" and \"WorkerRpc\".\n// https://github.com/cloudflare/workerd/blob/62b9ceee/src/workerd/api/actor.h#L86\n// https://github.com/cloudflare/workerd/blob/62b9ceee/src/workerd/api/worker-rpc.h#L30\nexport function isDurableObjectStub(targetName: string) {\n\treturn targetName === \"DurableObject\" || targetName === \"WorkerRpc\";\n}\n"],
  "mappings": "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",
  "names": ["headers"]
}
