devflare 1.0.0-next.63 → 1.0.0-next.65

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.
@@ -1,5 +1,5 @@
1
1
  import { a as cyanBold, f as logLine, o as dim, r as createCliTheme } from "./ui-BUoZApvE.js";
2
- import { t as prepareBuildArtifacts } from "./build-artifacts-B5OsATal.js";
2
+ import { t as prepareBuildArtifacts } from "./build-artifacts-CXLdD1PE.js";
3
3
  //#region src/cli/commands/build.ts
4
4
  async function runBuildCommand(parsed, logger, options) {
5
5
  const theme = createCliTheme(parsed.options);
@@ -4,7 +4,7 @@ import { c as writeWranglerConfig, i as isolateViteBuildOutputPaths$1, o as reba
4
4
  import { f as logLine } from "./ui-BUoZApvE.js";
5
5
  import { n as getPackageVersion } from "./package-metadata-DjHBLB-z.js";
6
6
  import { t as resolvePackageSpecifier } from "./resolve-package-qizBfCIm.js";
7
- import { i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, n as resolveEffectiveViteProject } from "./vite-CzqppgNp.js";
7
+ import { i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, n as resolveEffectiveViteProject } from "./vite-BBiqD3k7.js";
8
8
  import { t as bundleWorkerEntry } from "./worker-bundler-dTTnYU3n.js";
9
9
  import { t as detectViteProject } from "./vite-utils-OVLMV605.js";
10
10
  import { t as getDependencies } from "./dependencies-C2oxFjU_.js";
@@ -1101,15 +1101,15 @@ async function runInit(parsed, logger, options) {
1101
1101
  return runInitCommand(parsed, logger, options);
1102
1102
  }
1103
1103
  async function runDev(parsed, logger, options) {
1104
- const { runDevCommand } = await import("./dev-o1NHvkSX.js");
1104
+ const { runDevCommand } = await import("./dev-C7Z23WNH.js");
1105
1105
  return runDevCommand(parsed, logger, options);
1106
1106
  }
1107
1107
  async function runBuild(parsed, logger, options) {
1108
- const { runBuildCommand } = await import("./build-Bipv3UYU.js");
1108
+ const { runBuildCommand } = await import("./build-BZ6lIG7n.js");
1109
1109
  return runBuildCommand(parsed, logger, options);
1110
1110
  }
1111
1111
  async function runDeploy(parsed, logger, options) {
1112
- const { runDeployCommand } = await import("./deploy-cU11igDG.js");
1112
+ const { runDeployCommand } = await import("./deploy-CujD7j-R.js");
1113
1113
  return runDeployCommand(parsed, logger, {
1114
1114
  ...options,
1115
1115
  requireExplicitDeployTarget: true
@@ -709,14 +709,67 @@ function tryParseWireError(data) {
709
709
  }
710
710
  //#endregion
711
711
  //#region src/bridge/v2/value-serialization.ts
712
+ /**
713
+ * Read every `Set-Cookie` header value as a SEPARATE string.
714
+ *
715
+ * `Headers.forEach`/`entries()`/`get()` fold multiple `Set-Cookie` headers into
716
+ * one comma-joined value (the Fetch spec's sort-and-combine), which corrupts
717
+ * cookies: the second cookie's attributes bleed into the first. Only the
718
+ * dedicated accessors keep them apart — the standard `getSetCookie()` (Bun,
719
+ * Node, and workerd on a compatibility date ≥ 2023-08-01) or workerd's legacy
720
+ * `getAll('set-cookie')` (present on older compat dates that predate
721
+ * `getSetCookie`).
722
+ *
723
+ * @param headers - The header set to read.
724
+ * @returns Each `Set-Cookie` value in insertion order, or `null` when the
725
+ * runtime exposes neither accessor — the caller then keeps the combined value
726
+ * rather than dropping the header.
727
+ */
728
+ function readSetCookieValues(headers) {
729
+ const accessors = headers;
730
+ if (typeof accessors.getSetCookie === "function") {
731
+ const values = accessors.getSetCookie();
732
+ return Array.isArray(values) ? values : null;
733
+ }
734
+ if (typeof accessors.getAll === "function") try {
735
+ const values = accessors.getAll("set-cookie");
736
+ return Array.isArray(values) ? values : null;
737
+ } catch {
738
+ return null;
739
+ }
740
+ return null;
741
+ }
742
+ /**
743
+ * Flatten a `Headers` set to `[name, value]` pairs WITHOUT collapsing multiple
744
+ * `Set-Cookie` headers into one, so a response setting several cookies survives
745
+ * the bridge round-trip byte-faithfully.
746
+ *
747
+ * Non-cookie headers are emitted via `forEach` as before. `Set-Cookie` is
748
+ * enumerated separately (see `readSetCookieValues`) and appended as its own pair
749
+ * per cookie, so the reconstructing `new Headers(pairs)` re-`append`s each one.
750
+ * When the runtime can split neither (no `getSetCookie`/`getAll`), the combined
751
+ * `forEach` value is kept verbatim — graceful degradation, never a dropped
752
+ * header.
753
+ *
754
+ * @param headers - The header set to flatten.
755
+ * @returns `[name, value]` pairs safe to round-trip; each `Set-Cookie` is a
756
+ * distinct pair.
757
+ */
758
+ function serializeHeaders(headers) {
759
+ const setCookies = readSetCookieValues(headers);
760
+ const pairs = [];
761
+ headers.forEach((value, key) => {
762
+ if (setCookies !== null && key.toLowerCase() === "set-cookie") return;
763
+ pairs.push([key, value]);
764
+ });
765
+ if (setCookies) for (const cookie of setCookies) pairs.push(["set-cookie", cookie]);
766
+ return pairs;
767
+ }
712
768
  /** Serialize a Request to a POJO */
713
769
  async function serializeRequest(request, options) {
714
770
  const streams = [];
715
771
  const threshold = options?.httpThreshold ?? 524288;
716
- const headers = [];
717
- request.headers.forEach((value, key) => {
718
- headers.push([key, value]);
719
- });
772
+ const headers = serializeHeaders(request.headers);
720
773
  let body = null;
721
774
  if (request.body) {
722
775
  const bytes = await request.arrayBuffer();
@@ -759,10 +812,7 @@ function deserializeRequest(serialized, getStream) {
759
812
  async function serializeResponse(response, options) {
760
813
  const streams = [];
761
814
  const threshold = options?.httpThreshold ?? 524288;
762
- const headers = [];
763
- response.headers.forEach((value, key) => {
764
- headers.push([key, value]);
765
- });
815
+ const headers = serializeHeaders(response.headers);
766
816
  let body = null;
767
817
  if (response.body) {
768
818
  const bytes = await response.arrayBuffer();
@@ -7,7 +7,7 @@ import { _ as yellowBold, d as green, f as logLine, g as yellow, o as dim, r as
7
7
  import { n as getPackageVersion } from "./package-metadata-DjHBLB-z.js";
8
8
  import { t as resolvePackageSpecifier } from "./resolve-package-qizBfCIm.js";
9
9
  import { t as asOptionalString } from "./command-utils-CSnoFdxM.js";
10
- import { a as createBuildManifest, i as compareManifests, n as applyDeploymentStrategy, o as formatDriftWarning, r as describeDeploymentStrategy, s as readBuildManifest, t as prepareBuildArtifacts } from "./build-artifacts-B5OsATal.js";
10
+ import { a as createBuildManifest, i as compareManifests, n as applyDeploymentStrategy, o as formatDriftWarning, r as describeDeploymentStrategy, s as readBuildManifest, t as prepareBuildArtifacts } from "./build-artifacts-CXLdD1PE.js";
11
11
  import { t as getDependencies } from "./dependencies-C2oxFjU_.js";
12
12
  import { n as preparePreviewScopedResourcesForDeploy } from "./preview-resources-D_aFwtZR.js";
13
13
  import { mkdir, open, readFile, rm, writeFile } from "node:fs/promises";
@@ -7,7 +7,7 @@ import { clearLocalSendEmailBindings, setLocalSendEmailBindings } from "../utils
7
7
  import { C as buildSendEmailConfig, D as buildVersionMetadataConfig, E as buildTailConsumersConfig, O as buildWorkerLoadersConfig, S as buildSecretsStoreConfig, T as buildStreamingTailConsumersConfig, _ as buildMtlsCertificatesConfig, b as buildQueueProducers, c as buildAiSearchInstancesConfig, d as buildArtifactsConfig, f as buildDispatchNamespacesConfig, g as buildMediaConfig, h as buildImagesConfig, k as buildWorkflowsConfig, l as buildAiSearchNamespacesConfig, m as buildHyperdrivesConfig, n as getRouteDirectoryCandidate, p as buildFlagshipConfig, s as resolveServiceBindings, t as discoverRoutes, u as buildAnalyticsEngineConfig, v as buildPipelinesConfig, w as buildStreamConfig, x as buildRateLimitsConfig, y as buildQueueConsumers } from "./routes-BMU7ga_l.js";
8
8
  import { r as transformDurableObject } from "./durable-object-DMtH0XYX.js";
9
9
  import { i as findFiles } from "./glob-CmQOvunB.js";
10
- import { d as DEFAULT_FETCH_ENTRY_FILES, f as DEFAULT_QUEUE_ENTRY_FILES, g as discoverDurableObjectFiles, h as resolveWorkerSurfacePaths, i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, m as hasWorkerSurfacePaths, n as resolveEffectiveViteProject, p as DEFAULT_SCHEDULED_ENTRY_FILES, u as DEFAULT_EMAIL_ENTRY_FILES } from "./vite-CzqppgNp.js";
10
+ import { d as DEFAULT_FETCH_ENTRY_FILES, f as DEFAULT_QUEUE_ENTRY_FILES, g as discoverDurableObjectFiles, h as resolveWorkerSurfacePaths, i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, m as hasWorkerSurfacePaths, n as resolveEffectiveViteProject, p as DEFAULT_SCHEDULED_ENTRY_FILES, u as DEFAULT_EMAIL_ENTRY_FILES } from "./vite-BBiqD3k7.js";
11
11
  import { a as createWorkerdBundlerDefaults, i as writeWorkerCompatibleBundle, n as ensureDebugShim, r as resolveWorkerCompatibleRolldownConfig, t as bundleWorkerEntry } from "./worker-bundler-dTTnYU3n.js";
12
12
  import { n as bundleWorkflowEntrypointScript, t as R2_PRESIGN_RUNTIME_JS } from "./r2-presign-runtime-zTOLS0Ea.js";
13
13
  import { n as buildLocalSecretWrappedBindingConfig } from "./local-secrets-6sMstHXw.js";
@@ -476,6 +476,44 @@ function serializeR2Objects(result) {
476
476
  }
477
477
  }
478
478
 
479
+ // Read each Set-Cookie value separately. entries()/forEach() combine multiple
480
+ // Set-Cookie headers into one comma-joined value (Fetch sort-and-combine), which
481
+ // corrupts cookies. getSetCookie() keeps them apart on a compat date
482
+ // >= 2023-08-01; older workerd predates it but still exposes getAll('set-cookie').
483
+ // MUST match readSetCookieValues in v2/value-serialization.ts.
484
+ function readSetCookieValues(headers) {
485
+ if (typeof headers.getSetCookie === 'function') {
486
+ const values = headers.getSetCookie()
487
+ return Array.isArray(values) ? values : null
488
+ }
489
+ if (typeof headers.getAll === 'function') {
490
+ try {
491
+ const values = headers.getAll('set-cookie')
492
+ return Array.isArray(values) ? values : null
493
+ } catch (error) {
494
+ return null
495
+ }
496
+ }
497
+ return null
498
+ }
499
+
500
+ // Flatten headers to [name, value] pairs without collapsing multiple Set-Cookie
501
+ // into one. Each cookie becomes its own pair so the client's new Headers(pairs)
502
+ // re-appends them individually. Falls back to the combined value when the
503
+ // runtime can split neither. MUST match serializeHeaders in v2/value-serialization.ts.
504
+ function serializeHeaders(headers) {
505
+ const setCookies = readSetCookieValues(headers)
506
+ const pairs = []
507
+ headers.forEach((value, key) => {
508
+ if (setCookies !== null && key.toLowerCase() === 'set-cookie') return
509
+ pairs.push([key, value])
510
+ })
511
+ if (setCookies) {
512
+ for (const cookie of setCookies) pairs.push(['set-cookie', cookie])
513
+ }
514
+ return pairs
515
+ }
516
+
479
517
  async function serializeResponse(response) {
480
518
  let body = null
481
519
  if (response.body) {
@@ -496,7 +534,7 @@ async function serializeResponse(response) {
496
534
  return {
497
535
  status: response.status,
498
536
  statusText: response.statusText,
499
- headers: [...response.headers.entries()],
537
+ headers: serializeHeaders(response.headers),
500
538
  body
501
539
  }
502
540
  }
@@ -2134,7 +2172,7 @@ function resolveR2PresignOrigin(serverConfig, miniflareHost, miniflarePort) {
2134
2172
  function buildMiniflareDevConfig(input) {
2135
2173
  const { config: loadedConfig, cwd, miniflarePort, miniflareHost = "127.0.0.1", persist, enableVite, debug, mainWorkerSurfacePaths, mainWorkerRoutes, mainWorkerScriptPath, bundledMainWorkerScriptPath, workflowEntrypointScript, browserShimPort, doResult, serviceBindingResolution, r2PresignSecret, logger } = input;
2136
2174
  const bindings = loadedConfig.bindings ?? {};
2137
- const persistPath = resolve(cwd, ".devflare/data");
2175
+ const persistPath = process.env.DEVFLARE_PERSIST_DIR ? resolve(cwd, process.env.DEVFLARE_PERSIST_DIR) : resolve(cwd, ".devflare/data");
2138
2176
  const appWorkerName = loadedConfig.name;
2139
2177
  const shouldRunMainWorker = !enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length));
2140
2178
  const queueProducers = buildQueueProducers(bindings);
@@ -1,4 +1,4 @@
1
- import { j as bridgeEnv, n as getContextOrNull } from "./context-B6S7cqQW.js";
1
+ import { j as bridgeEnv, n as getContextOrNull } from "./context-CQkLyah9.js";
2
2
  //#region src/env.ts
3
3
  let testContextEnv = null;
4
4
  let testContextDispose = null;
@@ -1,4 +1,4 @@
1
- import { D as createFetchEvent, b as createContextProxy, n as getContextOrNull, v as runWithEventContext } from "./context-B6S7cqQW.js";
1
+ import { D as createFetchEvent, b as createContextProxy, n as getContextOrNull, v as runWithEventContext } from "./context-CQkLyah9.js";
2
2
  import { AwsV4Signer } from "aws4fetch";
3
3
  //#region src/runtime/exports.ts
4
4
  /**
@@ -2,7 +2,7 @@ import { d as resolveConfigEnvVars } from "./preview-C3Cdr8an.js";
2
2
  import { i as resolveConfigPath, o as loadResolvedConfig, r as loadConfig, s as resolveResources, u as resolveConfigForEnvironment } from "./loader-E_UGyTrJ.js";
3
3
  import { o as normalizeDOBinding } from "./schema-normalization-Zgm4W6O6.js";
4
4
  import { c as writeWranglerConfig, i as isolateViteBuildOutputPaths, n as compileConfig, o as rebaseWranglerConfigPaths, r as compileToProgrammaticConfig, t as compileBuildConfig } from "./compiler-CgIFPfRA.js";
5
- import { _ as resolveFetchHandler, o as assertExplicit2ArgStyle } from "./runtime-sEr9b0ns.js";
5
+ import { _ as resolveFetchHandler, o as assertExplicit2ArgStyle } from "./runtime-Ckf-KKc7.js";
6
6
  import { s as resolveServiceBindings, t as discoverRoutes } from "./routes-BMU7ga_l.js";
7
7
  import { n as findDurableObjectClasses } from "./durable-object-DMtH0XYX.js";
8
8
  import { r as SUPPORTED_WORKER_EXTENSIONS } from "./worker-entrypoint-CQW77lG8.js";
@@ -4,5 +4,5 @@
4
4
  * module. All symbols are declared with `function`/`const` so they are
5
5
  * hoisted in both embedding sites.
6
6
  */
7
- export declare const GATEWAY_RUNTIME_JS = "\nconst RAW_EMAIL = 'EmailMessage::raw'\n\n// Inline body cap for proxied DO/service-fetch responses. Matches\n// HTTP_TRANSFER_THRESHOLD in wire.ts (512 KB). A larger body would exceed\n// workerd's ~1 MB WebSocket message limit once base64-encoded into the rpc.ok\n// frame, so it is rejected with a clear error rather than silently truncated.\nconst HTTP_TRANSFER_THRESHOLD = 512 * 1024\n\nfunction arrayBufferToBase64(buffer) {\n\tconst bytes = new Uint8Array(buffer)\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i])\n\treturn btoa(binary)\n}\n\nfunction base64ToArrayBuffer(base64) {\n\tconst binary = atob(base64)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\treturn bytes.buffer\n}\n\n// Binary WsData frame codec (10-byte header: kind:u8, id:u32 LE, seq:u32 LE,\n// flags:u8 + payload). MUST match wire.ts encodeBinaryFrame/decodeBinaryFrame so\n// the bridge client and this gateway agree on the WS-data wire format\n// (BinaryKind.WsData = 2, BinaryFlags.TEXT = 2).\nfunction encodeWsDataFrame(wid, flags, payload) {\n\tconst frame = new Uint8Array(10 + payload.byteLength)\n\tconst view = new DataView(frame.buffer)\n\tview.setUint8(0, 2)\n\tview.setUint32(1, wid, true)\n\tview.setUint32(5, 0, true)\n\tview.setUint8(9, flags)\n\tframe.set(payload, 10)\n\treturn frame\n}\n\nfunction decodeWsDataFrame(buffer) {\n\tconst bytes = new Uint8Array(buffer)\n\tif (bytes.byteLength < 10) return null\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n\treturn {\n\t\tkind: view.getUint8(0),\n\t\tid: view.getUint32(1, true),\n\t\tflags: view.getUint8(9),\n\t\tpayload: bytes.subarray(10)\n\t}\n}\n\nfunction serializeR2Object(obj) {\n\tif (!obj) return null\n\treturn {\n\t\t__type: 'R2Object',\n\t\tkey: obj.key,\n\t\tversion: obj.version,\n\t\tsize: obj.size,\n\t\tetag: obj.etag,\n\t\thttpEtag: obj.httpEtag,\n\t\tchecksums: obj.checksums,\n\t\tuploaded: obj.uploaded?.toISOString(),\n\t\thttpMetadata: obj.httpMetadata,\n\t\tcustomMetadata: obj.customMetadata,\n\t\trange: obj.range,\n\t\tstorageClass: obj.storageClass\n\t}\n}\n\nfunction serializeR2ObjectBody(obj, bodyData) {\n\tif (!obj) return null\n\treturn {\n\t\t__type: 'R2ObjectBody',\n\t\tkey: obj.key,\n\t\tversion: obj.version,\n\t\tsize: obj.size,\n\t\tetag: obj.etag,\n\t\thttpEtag: obj.httpEtag,\n\t\tchecksums: obj.checksums,\n\t\tuploaded: obj.uploaded?.toISOString(),\n\t\thttpMetadata: obj.httpMetadata,\n\t\tcustomMetadata: obj.customMetadata,\n\t\trange: obj.range,\n\t\tstorageClass: obj.storageClass,\n\t\tbodyData\n\t}\n}\n\nfunction serializeR2Objects(result) {\n\tif (!result) return null\n\treturn {\n\t\tobjects: result.objects.map(serializeR2Object),\n\t\ttruncated: result.truncated,\n\t\tcursor: result.cursor,\n\t\tdelimitedPrefixes: result.delimitedPrefixes\n\t}\n}\n\nasync function serializeResponse(response) {\n\tlet body = null\n\tif (response.body) {\n\t\tconst bytes = await response.arrayBuffer()\n\t\tif (bytes.byteLength > HTTP_TRANSFER_THRESHOLD) {\n\t\t\tthrow new Error(\n\t\t\t\t'[devflare][bridge] Response body (' + bytes.byteLength + ' bytes) exceeds the '\n\t\t\t\t+ HTTP_TRANSFER_THRESHOLD + '-byte inline limit. DO and service-binding fetch responses '\n\t\t\t\t+ 'are delivered inline over the bridge WebSocket and cannot be streamed locally; keep '\n\t\t\t\t+ 'proxied response bodies under the limit (large R2 objects are exempt \u2014 read them through '\n\t\t\t\t+ 'the R2 binding, which uses the HTTP transfer side-channel).'\n\t\t\t)\n\t\t}\n\t\tif (bytes.byteLength > 0) {\n\t\t\tbody = { type: 'bytes', data: arrayBufferToBase64(bytes) }\n\t\t}\n\t}\n\treturn {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: [...response.headers.entries()],\n\t\tbody\n\t}\n}\n\nfunction deserializeRequest(serializedReq) {\n\treturn new Request(serializedReq.url, {\n\t\tmethod: serializedReq.method,\n\t\theaders: serializedReq.headers,\n\t\tbody: serializedReq.body?.type === 'bytes'\n\t\t\t? base64ToArrayBuffer(serializedReq.body.data)\n\t\t\t: undefined,\n\t\tredirect: serializedReq.redirect\n\t})\n}\n\nfunction createEmailMessageRaw(raw) {\n\tif (typeof raw === 'string' || raw instanceof ReadableStream) {\n\t\treturn raw\n\t}\n\tif (raw instanceof ArrayBuffer || raw instanceof Uint8Array) {\n\t\treturn new Response(raw).body\n\t}\n\tthrow new Error('Unsupported EmailMessage raw payload')\n}\n\nfunction isDurableObjectNamespace(binding) {\n\treturn !!binding\n\t\t&& typeof binding.idFromName === 'function'\n\t\t&& typeof binding.idFromString === 'function'\n\t\t&& typeof binding.newUniqueId === 'function'\n}\n\n/**\n * Resolve a (possibly jurisdiction-scoped) DurableObjectNamespace.\n * Forwards the jurisdiction faithfully when the binding supports it\n * (real Cloudflare), and degrades to the plain binding otherwise\n * (miniflare/workerd local emulation, which does not pin jurisdictions).\n */\nfunction resolveDoNamespace(binding, jurisdiction) {\n\tif (jurisdiction && typeof binding.jurisdiction === 'function') {\n\t\treturn binding.jurisdiction(jurisdiction)\n\t}\n\treturn binding\n}\n\n// Dispatch a single Durable Object RPC method call to its stub.\n//\n// A DO that extends DurableObject (from cloudflare:workers) exposes its methods\n// as native RPC on the stub, so the method is invoked DIRECTLY as\n// stub[method](...args) \u2014 exactly as on real Cloudflare. This is the ONLY\n// correct path for a DO that ALSO defines its own fetch(): routing the call\n// through fetch() would hand devflare's internal _rpc probe to the user handler,\n// which may reject it (e.g. a websocket-only fetch() returning 426) and yield a\n// non-JSON body that then fails to parse (the reported crash).\n//\n// The fetch _rpc convention is kept only as a fallback for DOs that are not\n// RPC-enabled: the stub proxies every property as callable, so such a DO only\n// reveals itself when the native call throws \"does not support RPC\".\nasync function callDurableObjectRpc(stub, methodName, args) {\n\tif (typeof stub[methodName] === 'function') {\n\t\ttry {\n\t\t\treturn await stub[methodName](...args)\n\t\t} catch (error) {\n\t\t\tif (!isDurableObjectRpcUnsupported(error)) throw error\n\t\t}\n\t}\n\tconst response = await stub.fetch(new Request('http://do/_rpc', {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ method: methodName, params: args })\n\t}))\n\tconst result = await response.json()\n\tif (!result.ok) throw new Error(result.error?.message || 'RPC failed')\n\treturn result.result\n}\n\nfunction isDurableObjectRpcUnsupported(error) {\n\tconst message = error && error.message ? String(error.message) : String(error)\n\treturn message.indexOf('does not support RPC') !== -1\n}\n\n/**\n * Execute an RPC method against the gateway's bindings.\n *\n * Method format: \"binding.operation\". Operations must be namespaced by\n * binding kind (e.g. \"kv.get\", \"r2.head\", \"d1.stmt.first\", \"do.fetch\",\n * \"service.fetch\", \"queue.send\", \"email.send\", \"ai.run\"). Bare verbs and the legacy\n * \"stmt.*\" / \"stub.*\" sub-prefixes were removed in B3-final and now throw.\n * Method vocabulary must stay in sync with the canonical server in\n * src/bridge/server.ts.\n */\nasync function executeRpcMethod(method, params, env, _ctx) {\n\tconst parts = method.split('.')\n\tif (parts.length < 2) throw new Error('Invalid method format: ' + method)\n\n\tconst bindingName = parts[0]\n\tconst operation = parts.slice(1).join('.')\n\tconst binding = env[bindingName]\n\n\tif (!binding) throw new Error('Binding not found: ' + bindingName)\n\n\tconst isNamespaced =\n\t\toperation.indexOf('kv.') === 0 ||\n\t\toperation.indexOf('r2.') === 0 ||\n\t\toperation.indexOf('d1.') === 0 ||\n\t\toperation.indexOf('do.') === 0 ||\n\t\toperation.indexOf('service.') === 0 ||\n\t\toperation.indexOf('queue.') === 0 ||\n\t\toperation.indexOf('email.') === 0 ||\n\t\toperation.indexOf('ai.') === 0 ||\n\t\toperation.indexOf('workflow.') === 0 ||\n\t\toperation.indexOf('var.') === 0\n\tif (!isNamespaced) {\n\t\tthrow new Error(createUnsupportedBridgeOperationErrorMessage(bindingName, operation))\n\t}\n\n\t// KV\n\tif (operation === 'kv.get') return binding.get(params[0], params[1])\n\tif (operation === 'kv.put') return binding.put(params[0], params[1], params[2])\n\tif (operation === 'kv.delete') return binding.delete(params[0])\n\tif (operation === 'kv.list') return binding.list(params[0])\n\tif (operation === 'kv.getWithMetadata') return binding.getWithMetadata(params[0], params[1])\n\n\t// DO get (returns DOStub reference)\n\tif (operation === 'do.get') {\n\t\treturn { __type: 'DOStub', binding: bindingName, id: params[0] }\n\t}\n\n\t// R2\n\tif (operation === 'r2.head') return serializeR2Object(await binding.head(params[0]))\n\tif (operation === 'r2.get') {\n\t\tconst obj = await binding.get(params[0], params[1])\n\t\tif (!obj) return null\n\t\tconst body = await obj.arrayBuffer()\n\t\treturn serializeR2ObjectBody(obj, arrayBufferToBase64(body))\n\t}\n\tif (operation === 'r2.put') {\n\t\tlet value = params[1]\n\t\tif (value && typeof value === 'object') {\n\t\t\tif (value.__type === 'ArrayBuffer' || value.__type === 'Uint8Array') {\n\t\t\t\tvalue = base64ToArrayBuffer(value.data)\n\t\t\t}\n\t\t}\n\t\treturn serializeR2Object(await binding.put(params[0], value, params[2]))\n\t}\n\tif (operation === 'r2.delete') return binding.delete(params[0])\n\tif (operation === 'r2.list') return serializeR2Objects(await binding.list(params[0]))\n\n\t// D1\n\tif (operation === 'd1.exec') return binding.exec(params[0])\n\tif (operation === 'd1.batch') {\n\t\tconst statements = params[0].map((s) => binding.prepare(s.sql).bind(...(s.bindings || [])))\n\t\treturn binding.batch(statements)\n\t}\n\tif (operation.indexOf('d1.stmt.') === 0) {\n\t\tconst mode = operation.split('.')[2]\n\t\tconst [sql, ...rest] = params\n\t\tlet bindings = rest\n\t\tlet extraParam\n\t\tif (mode === 'first' || mode === 'raw') {\n\t\t\textraParam = rest[rest.length - 1]\n\t\t\tbindings = rest.slice(0, -1)\n\t\t}\n\t\tlet stmt = binding.prepare(sql)\n\t\tif (bindings.length > 0) stmt = stmt.bind(...bindings)\n\t\tif (mode === 'first') {\n\t\t\tif (typeof extraParam === 'string' && extraParam.length > 0) return stmt.first(extraParam)\n\t\t\treturn stmt.first()\n\t\t}\n\t\tif (mode === 'all') return stmt.all()\n\t\tif (mode === 'run') return stmt.run()\n\t\tif (mode === 'raw') return stmt.raw(extraParam)\n\t\tthrow new Error('Unknown stmt mode: ' + mode)\n\t}\n\n\t// Durable Objects\n\tif (operation === 'do.idFromName') {\n\t\tconst ns = resolveDoNamespace(binding, params[1])\n\t\tconst id = ns.idFromName(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.idFromString') {\n\t\tconst id = binding.idFromString(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.newUniqueId') {\n\t\tconst ns = resolveDoNamespace(binding, params[1])\n\t\tconst id = ns.newUniqueId(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.fetch') {\n\t\tconst [, serializedId, serializedReq] = params\n\t\tconst id = binding.idFromString(serializedId.hex)\n\t\tconst stub = binding.get(id)\n\t\tconst response = await stub.fetch(new Request(serializedReq.url, {\n\t\t\tmethod: serializedReq.method,\n\t\t\theaders: serializedReq.headers,\n\t\t\tbody: serializedReq.body?.type === 'bytes'\n\t\t\t\t? base64ToArrayBuffer(serializedReq.body.data)\n\t\t\t\t: undefined\n\t\t}))\n\t\treturn serializeResponse(response)\n\t}\n\tif (operation === 'do.rpc') {\n\t\tconst [, serializedId, methodName, args] = params\n\t\tconst id = binding.idFromString(serializedId.hex)\n\t\tconst stub = binding.get(id)\n\t\treturn callDurableObjectRpc(stub, methodName, Array.isArray(args) ? args : [])\n\t}\n\n\t// Service Bindings\n\tif (operation === 'service.fetch') {\n\t\tif (!binding || typeof binding.fetch !== 'function') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' does not support fetch()')\n\t\t}\n\t\tconst response = await binding.fetch(deserializeRequest(params[0]))\n\t\treturn serializeResponse(response)\n\t}\n\tif (operation === 'service.rpc') {\n\t\tconst methodName = params[0]\n\t\tif (typeof methodName !== 'string') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' RPC method name must be a string')\n\t\t}\n\t\tconst args = Array.isArray(params[1]) ? params[1] : []\n\t\tconst method = binding && binding[methodName]\n\t\tif (typeof method !== 'function') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' does not support ' + methodName + '()')\n\t\t}\n\t\treturn method.apply(binding, args)\n\t}\n\n\t// Queues\n\tif (operation === 'queue.send') return binding.send(params[0], params[1])\n\tif (operation === 'queue.sendBatch') return binding.sendBatch(params[0], params[1])\n\n\t// Send Email\n\tif (operation === 'email.send') {\n\t\tif (binding && typeof binding.send === 'function') {\n\t\t\tconst message = params[0]\n\t\t\tif (message && typeof message === 'object' && 'from' in message && 'to' in message && 'raw' in message) {\n\t\t\t\treturn binding.send({\n\t\t\t\t\tfrom: message.from,\n\t\t\t\t\tto: message.to,\n\t\t\t\t\t[RAW_EMAIL]: createEmailMessageRaw(message.raw)\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn binding.send(message)\n\t\t}\n\t\treturn { ok: true, simulated: true }\n\t}\n\n\t// Workflows\n\tif (operation === 'workflow.create') {\n\t\treturn serializeWorkflowInstance(await binding.create(params[0]))\n\t}\n\tif (operation === 'workflow.get') {\n\t\treturn serializeWorkflowInstance(await binding.get(params[0]))\n\t}\n\tif (operation === 'workflow.status') {\n\t\treturn (await binding.get(params[0])).status()\n\t}\n\tif (operation === 'workflow.pause') {\n\t\treturn (await binding.get(params[0])).pause()\n\t}\n\tif (operation === 'workflow.resume') {\n\t\treturn (await binding.get(params[0])).resume()\n\t}\n\tif (operation === 'workflow.terminate') {\n\t\treturn (await binding.get(params[0])).terminate()\n\t}\n\tif (operation === 'workflow.restart') {\n\t\treturn (await binding.get(params[0])).restart()\n\t}\n\tif (operation === 'workflow.sendEvent') {\n\t\treturn (await binding.get(params[0])).sendEvent(params[1])\n\t}\n\n\t// AI / generic run()\n\tif (operation === 'ai.run') {\n\t\tif (typeof binding.run !== 'function') {\n\t\t\tthrow new Error('Binding ' + bindingName + ' does not support run(): ' + method)\n\t\t}\n\t\treturn binding.run(params[0], params[1])\n\t}\n\n\tthrow new Error('Unknown operation: ' + method)\n}\n\nfunction createUnsupportedBridgeOperationErrorMessage(bindingName, operation) {\n\tconst base = \"[devflare][bridge] Unsupported bridge operation '\" + operation + \"' for binding '\" + bindingName + \"'.\"\n\tif (operation === 'fetch') {\n\t\treturn base + ' Devflare could not dispatch fetch() for this binding through the local bridge. '\n\t\t\t+ 'Expected Cloudflare API: env.' + bindingName + '.fetch(request). '\n\t\t\t+ 'If this came from SvelteKit platform.env, make sure the binding is declared as a service binding; '\n\t\t\t+ 'this is a Devflare local bridge issue when service bindings fall back to a bare fetch operation.'\n\t}\n\tif (operation === 'toString') {\n\t\treturn base + ' A platform.env value was coerced to a string through the bridge. '\n\t\t\t+ 'For SvelteKit local dev, declared vars should be plain string values and missing env names should read as undefined.'\n\t}\n\treturn base + ' Bare verbs and the legacy stmt.*/stub.* sub-prefixes are not supported; '\n\t\t+ 'use the namespaced form (e.g. kv.get, r2.put, d1.stmt.first, do.fetch, service.fetch).'\n}\n\nfunction serializeWorkflowInstance(instance) {\n\treturn {\n\t\t__type: 'WorkflowInstance',\n\t\tid: instance.id\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// WebSocket bridge (shared with src/bridge/server.ts in shape)\n// ---------------------------------------------------------------------------\n// NOTE: wsProxies is intentionally created per handleBridgeWebSocket call so\n// state never leaks across connections or across gateway-script regenerations.\n\nasync function handleBridgeRpcCall(msg, ws, env, ctx) {\n\ttry {\n\t\tconst result = await executeRpcMethod(msg.method, msg.params, env, ctx)\n\t\tws.send(JSON.stringify({ t: 'rpc.ok', id: msg.id, result }))\n\t} catch (error) {\n\t\tws.send(JSON.stringify({\n\t\t\tt: 'rpc.err',\n\t\t\tid: msg.id,\n\t\t\terror: {\n\t\t\t\tcode: error?.code || 'INTERNAL_ERROR',\n\t\t\t\tmessage: error?.message || String(error)\n\t\t\t}\n\t\t}))\n\t}\n}\n\nasync function handleBridgeWsOpen(msg, ws, env, wsProxies) {\n\ttry {\n\t\tconst binding = env[msg.target.binding]\n\t\tconst id = binding.idFromString(msg.target.id)\n\t\tconst stub = binding.get(id)\n\n\t\tconst headers = new Headers(msg.target.headers || [])\n\t\theaders.set('Upgrade', 'websocket')\n\n\t\tconst response = await stub.fetch(new Request(msg.target.url, { method: 'GET', headers }))\n\t\tconst doWs = response.webSocket\n\n\t\tif (!doWs) {\n\t\t\tws.send(JSON.stringify({\n\t\t\t\tt: 'rpc.err',\n\t\t\t\tid: 'ws_' + msg.wid,\n\t\t\t\terror: { code: 'WS_FAILED', message: 'No WebSocket returned' }\n\t\t\t}))\n\t\t\treturn\n\t\t}\n\n\t\tdoWs.accept()\n\t\twsProxies.set(msg.wid, { doWs })\n\n\t\tdoWs.addEventListener('message', (event) => {\n\t\t\tconst isText = typeof event.data === 'string'\n\t\t\tconst payload = isText\n\t\t\t\t? new TextEncoder().encode(event.data)\n\t\t\t\t: new Uint8Array(event.data)\n\t\t\tconst flags = isText ? 2 : 0\n\t\t\tws.send(encodeWsDataFrame(msg.wid, flags, payload))\n\t\t})\n\n\t\tdoWs.addEventListener('close', (event) => {\n\t\t\tws.send(JSON.stringify({ t: 'ws.close', wid: msg.wid, code: event.code, reason: event.reason }))\n\t\t\twsProxies.delete(msg.wid)\n\t\t})\n\n\t\tws.send(JSON.stringify({ t: 'ws.opened', wid: msg.wid }))\n\t} catch (error) {\n\t\tws.send(JSON.stringify({\n\t\t\tt: 'rpc.err',\n\t\t\tid: 'ws_' + msg.wid,\n\t\t\terror: { code: 'WS_FAILED', message: error.message }\n\t\t}))\n\t}\n}\n\nfunction handleBridgeWsClose(msg, wsProxies) {\n\tconst proxy = wsProxies.get(msg.wid)\n\tif (proxy) {\n\t\tproxy.doWs.close(msg.code, msg.reason)\n\t\twsProxies.delete(msg.wid)\n\t}\n}\n\n// Relay an inbound binary WsData frame (client -> DO). Mirrors server.ts\n// handleBinaryMessage: decode the frame, look up the proxy by ws id, and forward\n// the payload to the DO socket honoring the TEXT flag.\nfunction handleBridgeBinaryMessage(buffer, wsProxies) {\n\tconst frame = decodeWsDataFrame(buffer)\n\tif (!frame || frame.kind !== 2) return\n\tconst proxy = wsProxies.get(frame.id)\n\tif (!proxy) return\n\tif ((frame.flags & 2) !== 0) {\n\t\tproxy.doWs.send(new TextDecoder().decode(frame.payload))\n\t} else {\n\t\tproxy.doWs.send(frame.payload)\n\t}\n}\n\nasync function handleBridgeJsonMessage(data, ws, env, ctx, wsProxies) {\n\tconst msg = JSON.parse(data)\n\tswitch (msg.t) {\n\t\tcase 'hello':\n\t\t\t// v2 handshake \u2014 acknowledge with welcome echoing the negotiated\n\t\t\t// capability intersection. The gateway advertises only what it\n\t\t\t// implements end-to-end: 'ws-relay' (binary WsData relay to/from DO\n\t\t\t// sockets) and 'http-transfer' (the R2 transfer HTTP side-channel).\n\t\t\t// 'streams' is intentionally NOT advertised \u2014 large DO/service-fetch\n\t\t\t// responses are inlined, not streamed, in this gateway.\n\t\t\tws.send(JSON.stringify({\n\t\t\t\tt: 'welcome',\n\t\t\t\tprotocolVersion: 2,\n\t\t\t\tcapabilities: ['ws-relay', 'http-transfer']\n\t\t\t\t\t.filter((c) => Array.isArray(msg.capabilities) && msg.capabilities.includes(c))\n\t\t\t\t\t.sort()\n\t\t\t}))\n\t\t\tbreak\n\t\tcase 'rpc.call':\n\t\t\tawait handleBridgeRpcCall(msg, ws, env, ctx)\n\t\t\tbreak\n\t\tcase 'ws.open':\n\t\t\tawait handleBridgeWsOpen(msg, ws, env, wsProxies)\n\t\t\tbreak\n\t\tcase 'ws.close':\n\t\t\thandleBridgeWsClose(msg, wsProxies)\n\t\t\tbreak\n\t}\n}\n\n// Pass-through DO WebSocket upgrade (bridge client -> gateway -> DO).\n//\n// The bridge client's openDoWebSocket() opens a REAL loopback WebSocket to this\n// endpoint (/_devflare/do-ws?binding&id&u) instead of tunneling frames over the\n// bridge socket. We resolve the DO by id and RETURN the DO's 101 response\n// verbatim (webSocket included), so miniflare wires the inbound connection to\n// the DO's client socket. That genuine inbound connection is what makes workerd\n// dispatch the DO's hibernation handlers (webSocketMessage/webSocketClose) and\n// deliver ctx.getWebSockets() broadcasts. Pumping the DO socket in-process (the\n// legacy handleBridgeWsOpen path) never triggers hibernation, so two sockets to\n// one getByName(id) could not see each other's messages. This mirrors the\n// browser WS_ROUTES pass-through (handleDoWebSocket) and real Cloudflare.\n//\n// NOTE: the devflare/test gateway (src/test/simple-context-gateway-script.ts)\n// keeps a byte-equivalent copy of this handler because it is a separately-bundled\n// Worker template that cannot import this runtime \u2014 keep the two in sync.\nasync function handleBridgeDoWebSocket(request, env, url) {\n\ttry {\n\t\tconst bindingName = url.searchParams.get('binding')\n\t\tconst idHex = url.searchParams.get('id')\n\t\tconst targetUrl = url.searchParams.get('u') || url.toString()\n\n\t\tconst binding = env[bindingName]\n\t\tif (!binding || typeof binding.idFromString !== 'function') {\n\t\t\treturn new Response('Durable Object binding not found: ' + bindingName, { status: 500 })\n\t\t}\n\t\tif (!idHex) {\n\t\t\treturn new Response('Missing Durable Object id', { status: 400 })\n\t\t}\n\n\t\tconst stub = binding.get(binding.idFromString(idHex))\n\t\t// Forward the real upgrade request (its headers carry any auth/cookies the\n\t\t// caller passed to connect()) and pass the DO's 101 response straight back.\n\t\treturn stub.fetch(new Request(targetUrl, { method: 'GET', headers: request.headers }))\n\t} catch (error) {\n\t\treturn new Response('Error opening DO WebSocket: ' + (error?.message || String(error)), {\n\t\t\tstatus: 500\n\t\t})\n\t}\n}\n\nfunction handleBridgeWebSocket(request, env, ctx) {\n\tconst { 0: client, 1: server } = new WebSocketPair()\n\tserver.accept()\n\n\t// Per-connection state: recreated for every bridge client so reloads and\n\t// concurrent clients never share WS proxy entries.\n\tconst wsProxies = new Map()\n\n\tserver.addEventListener('message', async (event) => {\n\t\ttry {\n\t\t\tif (typeof event.data === 'string') {\n\t\t\t\tawait handleBridgeJsonMessage(event.data, server, env, ctx, wsProxies)\n\t\t\t} else {\n\t\t\t\thandleBridgeBinaryMessage(event.data, wsProxies)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('[Gateway] Error:', error)\n\t\t}\n\t})\n\n\tserver.addEventListener('close', () => {\n\t\tfor (const proxy of wsProxies.values()) {\n\t\t\t// Best-effort cleanup: the DO-side WS may already be closed or in an\n\t\t\t// invalid state; any throw here would abort sibling closes. Surface\n\t\t\t// the swallowed error when DEVFLARE_DEBUG_BRIDGE is enabled.\n\t\t\ttry { proxy.doWs.close() } catch (error) {\n\t\t\t\tif (globalThis.DEVFLARE_DEBUG_BRIDGE) {\n\t\t\t\t\tconsole.warn('[devflare:bridge] proxy.doWs.close() failed', error)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twsProxies.clear()\n\t})\n\n\treturn new Response(null, { status: 101, webSocket: client })\n}\n\n// ---------------------------------------------------------------------------\n// HTTP transfer for R2 bodies (shared with src/bridge/server.ts in shape)\n// ---------------------------------------------------------------------------\n\nasync function handleHttpTransfer(request, env, url) {\n\tconst transferIdEncoded = url.pathname.split('/').pop()\n\tconst transferId = decodeURIComponent(transferIdEncoded || '')\n\tconst [binding, ...keyParts] = transferId.split(':')\n\tconst key = keyParts.join(':')\n\tconst bucket = env[binding]\n\n\tif (!bucket) return new Response('Bucket not found: ' + binding, { status: 404 })\n\n\tif (request.method === 'PUT' || request.method === 'POST') {\n\t\tconst result = await bucket.put(key, request.body)\n\t\treturn new Response(JSON.stringify(serializeR2Object(result)), {\n\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t})\n\t}\n\n\tif (request.method === 'GET') {\n\t\tconst object = await bucket.get(key)\n\t\tif (!object) return new Response('Not found', { status: 404 })\n\t\treturn new Response(object.body, {\n\t\t\theaders: {\n\t\t\t\t'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',\n\t\t\t\t'Content-Length': String(object.size)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn new Response('Method not allowed', { status: 405 })\n}\n";
7
+ export declare const GATEWAY_RUNTIME_JS = "\nconst RAW_EMAIL = 'EmailMessage::raw'\n\n// Inline body cap for proxied DO/service-fetch responses. Matches\n// HTTP_TRANSFER_THRESHOLD in wire.ts (512 KB). A larger body would exceed\n// workerd's ~1 MB WebSocket message limit once base64-encoded into the rpc.ok\n// frame, so it is rejected with a clear error rather than silently truncated.\nconst HTTP_TRANSFER_THRESHOLD = 512 * 1024\n\nfunction arrayBufferToBase64(buffer) {\n\tconst bytes = new Uint8Array(buffer)\n\tlet binary = ''\n\tfor (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i])\n\treturn btoa(binary)\n}\n\nfunction base64ToArrayBuffer(base64) {\n\tconst binary = atob(base64)\n\tconst bytes = new Uint8Array(binary.length)\n\tfor (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\treturn bytes.buffer\n}\n\n// Binary WsData frame codec (10-byte header: kind:u8, id:u32 LE, seq:u32 LE,\n// flags:u8 + payload). MUST match wire.ts encodeBinaryFrame/decodeBinaryFrame so\n// the bridge client and this gateway agree on the WS-data wire format\n// (BinaryKind.WsData = 2, BinaryFlags.TEXT = 2).\nfunction encodeWsDataFrame(wid, flags, payload) {\n\tconst frame = new Uint8Array(10 + payload.byteLength)\n\tconst view = new DataView(frame.buffer)\n\tview.setUint8(0, 2)\n\tview.setUint32(1, wid, true)\n\tview.setUint32(5, 0, true)\n\tview.setUint8(9, flags)\n\tframe.set(payload, 10)\n\treturn frame\n}\n\nfunction decodeWsDataFrame(buffer) {\n\tconst bytes = new Uint8Array(buffer)\n\tif (bytes.byteLength < 10) return null\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n\treturn {\n\t\tkind: view.getUint8(0),\n\t\tid: view.getUint32(1, true),\n\t\tflags: view.getUint8(9),\n\t\tpayload: bytes.subarray(10)\n\t}\n}\n\nfunction serializeR2Object(obj) {\n\tif (!obj) return null\n\treturn {\n\t\t__type: 'R2Object',\n\t\tkey: obj.key,\n\t\tversion: obj.version,\n\t\tsize: obj.size,\n\t\tetag: obj.etag,\n\t\thttpEtag: obj.httpEtag,\n\t\tchecksums: obj.checksums,\n\t\tuploaded: obj.uploaded?.toISOString(),\n\t\thttpMetadata: obj.httpMetadata,\n\t\tcustomMetadata: obj.customMetadata,\n\t\trange: obj.range,\n\t\tstorageClass: obj.storageClass\n\t}\n}\n\nfunction serializeR2ObjectBody(obj, bodyData) {\n\tif (!obj) return null\n\treturn {\n\t\t__type: 'R2ObjectBody',\n\t\tkey: obj.key,\n\t\tversion: obj.version,\n\t\tsize: obj.size,\n\t\tetag: obj.etag,\n\t\thttpEtag: obj.httpEtag,\n\t\tchecksums: obj.checksums,\n\t\tuploaded: obj.uploaded?.toISOString(),\n\t\thttpMetadata: obj.httpMetadata,\n\t\tcustomMetadata: obj.customMetadata,\n\t\trange: obj.range,\n\t\tstorageClass: obj.storageClass,\n\t\tbodyData\n\t}\n}\n\nfunction serializeR2Objects(result) {\n\tif (!result) return null\n\treturn {\n\t\tobjects: result.objects.map(serializeR2Object),\n\t\ttruncated: result.truncated,\n\t\tcursor: result.cursor,\n\t\tdelimitedPrefixes: result.delimitedPrefixes\n\t}\n}\n\n// Read each Set-Cookie value separately. entries()/forEach() combine multiple\n// Set-Cookie headers into one comma-joined value (Fetch sort-and-combine), which\n// corrupts cookies. getSetCookie() keeps them apart on a compat date\n// >= 2023-08-01; older workerd predates it but still exposes getAll('set-cookie').\n// MUST match readSetCookieValues in v2/value-serialization.ts.\nfunction readSetCookieValues(headers) {\n\tif (typeof headers.getSetCookie === 'function') {\n\t\tconst values = headers.getSetCookie()\n\t\treturn Array.isArray(values) ? values : null\n\t}\n\tif (typeof headers.getAll === 'function') {\n\t\ttry {\n\t\t\tconst values = headers.getAll('set-cookie')\n\t\t\treturn Array.isArray(values) ? values : null\n\t\t} catch (error) {\n\t\t\treturn null\n\t\t}\n\t}\n\treturn null\n}\n\n// Flatten headers to [name, value] pairs without collapsing multiple Set-Cookie\n// into one. Each cookie becomes its own pair so the client's new Headers(pairs)\n// re-appends them individually. Falls back to the combined value when the\n// runtime can split neither. MUST match serializeHeaders in v2/value-serialization.ts.\nfunction serializeHeaders(headers) {\n\tconst setCookies = readSetCookieValues(headers)\n\tconst pairs = []\n\theaders.forEach((value, key) => {\n\t\tif (setCookies !== null && key.toLowerCase() === 'set-cookie') return\n\t\tpairs.push([key, value])\n\t})\n\tif (setCookies) {\n\t\tfor (const cookie of setCookies) pairs.push(['set-cookie', cookie])\n\t}\n\treturn pairs\n}\n\nasync function serializeResponse(response) {\n\tlet body = null\n\tif (response.body) {\n\t\tconst bytes = await response.arrayBuffer()\n\t\tif (bytes.byteLength > HTTP_TRANSFER_THRESHOLD) {\n\t\t\tthrow new Error(\n\t\t\t\t'[devflare][bridge] Response body (' + bytes.byteLength + ' bytes) exceeds the '\n\t\t\t\t+ HTTP_TRANSFER_THRESHOLD + '-byte inline limit. DO and service-binding fetch responses '\n\t\t\t\t+ 'are delivered inline over the bridge WebSocket and cannot be streamed locally; keep '\n\t\t\t\t+ 'proxied response bodies under the limit (large R2 objects are exempt \u2014 read them through '\n\t\t\t\t+ 'the R2 binding, which uses the HTTP transfer side-channel).'\n\t\t\t)\n\t\t}\n\t\tif (bytes.byteLength > 0) {\n\t\t\tbody = { type: 'bytes', data: arrayBufferToBase64(bytes) }\n\t\t}\n\t}\n\treturn {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: serializeHeaders(response.headers),\n\t\tbody\n\t}\n}\n\nfunction deserializeRequest(serializedReq) {\n\treturn new Request(serializedReq.url, {\n\t\tmethod: serializedReq.method,\n\t\theaders: serializedReq.headers,\n\t\tbody: serializedReq.body?.type === 'bytes'\n\t\t\t? base64ToArrayBuffer(serializedReq.body.data)\n\t\t\t: undefined,\n\t\tredirect: serializedReq.redirect\n\t})\n}\n\nfunction createEmailMessageRaw(raw) {\n\tif (typeof raw === 'string' || raw instanceof ReadableStream) {\n\t\treturn raw\n\t}\n\tif (raw instanceof ArrayBuffer || raw instanceof Uint8Array) {\n\t\treturn new Response(raw).body\n\t}\n\tthrow new Error('Unsupported EmailMessage raw payload')\n}\n\nfunction isDurableObjectNamespace(binding) {\n\treturn !!binding\n\t\t&& typeof binding.idFromName === 'function'\n\t\t&& typeof binding.idFromString === 'function'\n\t\t&& typeof binding.newUniqueId === 'function'\n}\n\n/**\n * Resolve a (possibly jurisdiction-scoped) DurableObjectNamespace.\n * Forwards the jurisdiction faithfully when the binding supports it\n * (real Cloudflare), and degrades to the plain binding otherwise\n * (miniflare/workerd local emulation, which does not pin jurisdictions).\n */\nfunction resolveDoNamespace(binding, jurisdiction) {\n\tif (jurisdiction && typeof binding.jurisdiction === 'function') {\n\t\treturn binding.jurisdiction(jurisdiction)\n\t}\n\treturn binding\n}\n\n// Dispatch a single Durable Object RPC method call to its stub.\n//\n// A DO that extends DurableObject (from cloudflare:workers) exposes its methods\n// as native RPC on the stub, so the method is invoked DIRECTLY as\n// stub[method](...args) \u2014 exactly as on real Cloudflare. This is the ONLY\n// correct path for a DO that ALSO defines its own fetch(): routing the call\n// through fetch() would hand devflare's internal _rpc probe to the user handler,\n// which may reject it (e.g. a websocket-only fetch() returning 426) and yield a\n// non-JSON body that then fails to parse (the reported crash).\n//\n// The fetch _rpc convention is kept only as a fallback for DOs that are not\n// RPC-enabled: the stub proxies every property as callable, so such a DO only\n// reveals itself when the native call throws \"does not support RPC\".\nasync function callDurableObjectRpc(stub, methodName, args) {\n\tif (typeof stub[methodName] === 'function') {\n\t\ttry {\n\t\t\treturn await stub[methodName](...args)\n\t\t} catch (error) {\n\t\t\tif (!isDurableObjectRpcUnsupported(error)) throw error\n\t\t}\n\t}\n\tconst response = await stub.fetch(new Request('http://do/_rpc', {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({ method: methodName, params: args })\n\t}))\n\tconst result = await response.json()\n\tif (!result.ok) throw new Error(result.error?.message || 'RPC failed')\n\treturn result.result\n}\n\nfunction isDurableObjectRpcUnsupported(error) {\n\tconst message = error && error.message ? String(error.message) : String(error)\n\treturn message.indexOf('does not support RPC') !== -1\n}\n\n/**\n * Execute an RPC method against the gateway's bindings.\n *\n * Method format: \"binding.operation\". Operations must be namespaced by\n * binding kind (e.g. \"kv.get\", \"r2.head\", \"d1.stmt.first\", \"do.fetch\",\n * \"service.fetch\", \"queue.send\", \"email.send\", \"ai.run\"). Bare verbs and the legacy\n * \"stmt.*\" / \"stub.*\" sub-prefixes were removed in B3-final and now throw.\n * Method vocabulary must stay in sync with the canonical server in\n * src/bridge/server.ts.\n */\nasync function executeRpcMethod(method, params, env, _ctx) {\n\tconst parts = method.split('.')\n\tif (parts.length < 2) throw new Error('Invalid method format: ' + method)\n\n\tconst bindingName = parts[0]\n\tconst operation = parts.slice(1).join('.')\n\tconst binding = env[bindingName]\n\n\tif (!binding) throw new Error('Binding not found: ' + bindingName)\n\n\tconst isNamespaced =\n\t\toperation.indexOf('kv.') === 0 ||\n\t\toperation.indexOf('r2.') === 0 ||\n\t\toperation.indexOf('d1.') === 0 ||\n\t\toperation.indexOf('do.') === 0 ||\n\t\toperation.indexOf('service.') === 0 ||\n\t\toperation.indexOf('queue.') === 0 ||\n\t\toperation.indexOf('email.') === 0 ||\n\t\toperation.indexOf('ai.') === 0 ||\n\t\toperation.indexOf('workflow.') === 0 ||\n\t\toperation.indexOf('var.') === 0\n\tif (!isNamespaced) {\n\t\tthrow new Error(createUnsupportedBridgeOperationErrorMessage(bindingName, operation))\n\t}\n\n\t// KV\n\tif (operation === 'kv.get') return binding.get(params[0], params[1])\n\tif (operation === 'kv.put') return binding.put(params[0], params[1], params[2])\n\tif (operation === 'kv.delete') return binding.delete(params[0])\n\tif (operation === 'kv.list') return binding.list(params[0])\n\tif (operation === 'kv.getWithMetadata') return binding.getWithMetadata(params[0], params[1])\n\n\t// DO get (returns DOStub reference)\n\tif (operation === 'do.get') {\n\t\treturn { __type: 'DOStub', binding: bindingName, id: params[0] }\n\t}\n\n\t// R2\n\tif (operation === 'r2.head') return serializeR2Object(await binding.head(params[0]))\n\tif (operation === 'r2.get') {\n\t\tconst obj = await binding.get(params[0], params[1])\n\t\tif (!obj) return null\n\t\tconst body = await obj.arrayBuffer()\n\t\treturn serializeR2ObjectBody(obj, arrayBufferToBase64(body))\n\t}\n\tif (operation === 'r2.put') {\n\t\tlet value = params[1]\n\t\tif (value && typeof value === 'object') {\n\t\t\tif (value.__type === 'ArrayBuffer' || value.__type === 'Uint8Array') {\n\t\t\t\tvalue = base64ToArrayBuffer(value.data)\n\t\t\t}\n\t\t}\n\t\treturn serializeR2Object(await binding.put(params[0], value, params[2]))\n\t}\n\tif (operation === 'r2.delete') return binding.delete(params[0])\n\tif (operation === 'r2.list') return serializeR2Objects(await binding.list(params[0]))\n\n\t// D1\n\tif (operation === 'd1.exec') return binding.exec(params[0])\n\tif (operation === 'd1.batch') {\n\t\tconst statements = params[0].map((s) => binding.prepare(s.sql).bind(...(s.bindings || [])))\n\t\treturn binding.batch(statements)\n\t}\n\tif (operation.indexOf('d1.stmt.') === 0) {\n\t\tconst mode = operation.split('.')[2]\n\t\tconst [sql, ...rest] = params\n\t\tlet bindings = rest\n\t\tlet extraParam\n\t\tif (mode === 'first' || mode === 'raw') {\n\t\t\textraParam = rest[rest.length - 1]\n\t\t\tbindings = rest.slice(0, -1)\n\t\t}\n\t\tlet stmt = binding.prepare(sql)\n\t\tif (bindings.length > 0) stmt = stmt.bind(...bindings)\n\t\tif (mode === 'first') {\n\t\t\tif (typeof extraParam === 'string' && extraParam.length > 0) return stmt.first(extraParam)\n\t\t\treturn stmt.first()\n\t\t}\n\t\tif (mode === 'all') return stmt.all()\n\t\tif (mode === 'run') return stmt.run()\n\t\tif (mode === 'raw') return stmt.raw(extraParam)\n\t\tthrow new Error('Unknown stmt mode: ' + mode)\n\t}\n\n\t// Durable Objects\n\tif (operation === 'do.idFromName') {\n\t\tconst ns = resolveDoNamespace(binding, params[1])\n\t\tconst id = ns.idFromName(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.idFromString') {\n\t\tconst id = binding.idFromString(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.newUniqueId') {\n\t\tconst ns = resolveDoNamespace(binding, params[1])\n\t\tconst id = ns.newUniqueId(params[0])\n\t\treturn { __type: 'DOId', hex: id.toString() }\n\t}\n\tif (operation === 'do.fetch') {\n\t\tconst [, serializedId, serializedReq] = params\n\t\tconst id = binding.idFromString(serializedId.hex)\n\t\tconst stub = binding.get(id)\n\t\tconst response = await stub.fetch(new Request(serializedReq.url, {\n\t\t\tmethod: serializedReq.method,\n\t\t\theaders: serializedReq.headers,\n\t\t\tbody: serializedReq.body?.type === 'bytes'\n\t\t\t\t? base64ToArrayBuffer(serializedReq.body.data)\n\t\t\t\t: undefined\n\t\t}))\n\t\treturn serializeResponse(response)\n\t}\n\tif (operation === 'do.rpc') {\n\t\tconst [, serializedId, methodName, args] = params\n\t\tconst id = binding.idFromString(serializedId.hex)\n\t\tconst stub = binding.get(id)\n\t\treturn callDurableObjectRpc(stub, methodName, Array.isArray(args) ? args : [])\n\t}\n\n\t// Service Bindings\n\tif (operation === 'service.fetch') {\n\t\tif (!binding || typeof binding.fetch !== 'function') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' does not support fetch()')\n\t\t}\n\t\tconst response = await binding.fetch(deserializeRequest(params[0]))\n\t\treturn serializeResponse(response)\n\t}\n\tif (operation === 'service.rpc') {\n\t\tconst methodName = params[0]\n\t\tif (typeof methodName !== 'string') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' RPC method name must be a string')\n\t\t}\n\t\tconst args = Array.isArray(params[1]) ? params[1] : []\n\t\tconst method = binding && binding[methodName]\n\t\tif (typeof method !== 'function') {\n\t\t\tthrow new Error('Service binding ' + bindingName + ' does not support ' + methodName + '()')\n\t\t}\n\t\treturn method.apply(binding, args)\n\t}\n\n\t// Queues\n\tif (operation === 'queue.send') return binding.send(params[0], params[1])\n\tif (operation === 'queue.sendBatch') return binding.sendBatch(params[0], params[1])\n\n\t// Send Email\n\tif (operation === 'email.send') {\n\t\tif (binding && typeof binding.send === 'function') {\n\t\t\tconst message = params[0]\n\t\t\tif (message && typeof message === 'object' && 'from' in message && 'to' in message && 'raw' in message) {\n\t\t\t\treturn binding.send({\n\t\t\t\t\tfrom: message.from,\n\t\t\t\t\tto: message.to,\n\t\t\t\t\t[RAW_EMAIL]: createEmailMessageRaw(message.raw)\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn binding.send(message)\n\t\t}\n\t\treturn { ok: true, simulated: true }\n\t}\n\n\t// Workflows\n\tif (operation === 'workflow.create') {\n\t\treturn serializeWorkflowInstance(await binding.create(params[0]))\n\t}\n\tif (operation === 'workflow.get') {\n\t\treturn serializeWorkflowInstance(await binding.get(params[0]))\n\t}\n\tif (operation === 'workflow.status') {\n\t\treturn (await binding.get(params[0])).status()\n\t}\n\tif (operation === 'workflow.pause') {\n\t\treturn (await binding.get(params[0])).pause()\n\t}\n\tif (operation === 'workflow.resume') {\n\t\treturn (await binding.get(params[0])).resume()\n\t}\n\tif (operation === 'workflow.terminate') {\n\t\treturn (await binding.get(params[0])).terminate()\n\t}\n\tif (operation === 'workflow.restart') {\n\t\treturn (await binding.get(params[0])).restart()\n\t}\n\tif (operation === 'workflow.sendEvent') {\n\t\treturn (await binding.get(params[0])).sendEvent(params[1])\n\t}\n\n\t// AI / generic run()\n\tif (operation === 'ai.run') {\n\t\tif (typeof binding.run !== 'function') {\n\t\t\tthrow new Error('Binding ' + bindingName + ' does not support run(): ' + method)\n\t\t}\n\t\treturn binding.run(params[0], params[1])\n\t}\n\n\tthrow new Error('Unknown operation: ' + method)\n}\n\nfunction createUnsupportedBridgeOperationErrorMessage(bindingName, operation) {\n\tconst base = \"[devflare][bridge] Unsupported bridge operation '\" + operation + \"' for binding '\" + bindingName + \"'.\"\n\tif (operation === 'fetch') {\n\t\treturn base + ' Devflare could not dispatch fetch() for this binding through the local bridge. '\n\t\t\t+ 'Expected Cloudflare API: env.' + bindingName + '.fetch(request). '\n\t\t\t+ 'If this came from SvelteKit platform.env, make sure the binding is declared as a service binding; '\n\t\t\t+ 'this is a Devflare local bridge issue when service bindings fall back to a bare fetch operation.'\n\t}\n\tif (operation === 'toString') {\n\t\treturn base + ' A platform.env value was coerced to a string through the bridge. '\n\t\t\t+ 'For SvelteKit local dev, declared vars should be plain string values and missing env names should read as undefined.'\n\t}\n\treturn base + ' Bare verbs and the legacy stmt.*/stub.* sub-prefixes are not supported; '\n\t\t+ 'use the namespaced form (e.g. kv.get, r2.put, d1.stmt.first, do.fetch, service.fetch).'\n}\n\nfunction serializeWorkflowInstance(instance) {\n\treturn {\n\t\t__type: 'WorkflowInstance',\n\t\tid: instance.id\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// WebSocket bridge (shared with src/bridge/server.ts in shape)\n// ---------------------------------------------------------------------------\n// NOTE: wsProxies is intentionally created per handleBridgeWebSocket call so\n// state never leaks across connections or across gateway-script regenerations.\n\nasync function handleBridgeRpcCall(msg, ws, env, ctx) {\n\ttry {\n\t\tconst result = await executeRpcMethod(msg.method, msg.params, env, ctx)\n\t\tws.send(JSON.stringify({ t: 'rpc.ok', id: msg.id, result }))\n\t} catch (error) {\n\t\tws.send(JSON.stringify({\n\t\t\tt: 'rpc.err',\n\t\t\tid: msg.id,\n\t\t\terror: {\n\t\t\t\tcode: error?.code || 'INTERNAL_ERROR',\n\t\t\t\tmessage: error?.message || String(error)\n\t\t\t}\n\t\t}))\n\t}\n}\n\nasync function handleBridgeWsOpen(msg, ws, env, wsProxies) {\n\ttry {\n\t\tconst binding = env[msg.target.binding]\n\t\tconst id = binding.idFromString(msg.target.id)\n\t\tconst stub = binding.get(id)\n\n\t\tconst headers = new Headers(msg.target.headers || [])\n\t\theaders.set('Upgrade', 'websocket')\n\n\t\tconst response = await stub.fetch(new Request(msg.target.url, { method: 'GET', headers }))\n\t\tconst doWs = response.webSocket\n\n\t\tif (!doWs) {\n\t\t\tws.send(JSON.stringify({\n\t\t\t\tt: 'rpc.err',\n\t\t\t\tid: 'ws_' + msg.wid,\n\t\t\t\terror: { code: 'WS_FAILED', message: 'No WebSocket returned' }\n\t\t\t}))\n\t\t\treturn\n\t\t}\n\n\t\tdoWs.accept()\n\t\twsProxies.set(msg.wid, { doWs })\n\n\t\tdoWs.addEventListener('message', (event) => {\n\t\t\tconst isText = typeof event.data === 'string'\n\t\t\tconst payload = isText\n\t\t\t\t? new TextEncoder().encode(event.data)\n\t\t\t\t: new Uint8Array(event.data)\n\t\t\tconst flags = isText ? 2 : 0\n\t\t\tws.send(encodeWsDataFrame(msg.wid, flags, payload))\n\t\t})\n\n\t\tdoWs.addEventListener('close', (event) => {\n\t\t\tws.send(JSON.stringify({ t: 'ws.close', wid: msg.wid, code: event.code, reason: event.reason }))\n\t\t\twsProxies.delete(msg.wid)\n\t\t})\n\n\t\tws.send(JSON.stringify({ t: 'ws.opened', wid: msg.wid }))\n\t} catch (error) {\n\t\tws.send(JSON.stringify({\n\t\t\tt: 'rpc.err',\n\t\t\tid: 'ws_' + msg.wid,\n\t\t\terror: { code: 'WS_FAILED', message: error.message }\n\t\t}))\n\t}\n}\n\nfunction handleBridgeWsClose(msg, wsProxies) {\n\tconst proxy = wsProxies.get(msg.wid)\n\tif (proxy) {\n\t\tproxy.doWs.close(msg.code, msg.reason)\n\t\twsProxies.delete(msg.wid)\n\t}\n}\n\n// Relay an inbound binary WsData frame (client -> DO). Mirrors server.ts\n// handleBinaryMessage: decode the frame, look up the proxy by ws id, and forward\n// the payload to the DO socket honoring the TEXT flag.\nfunction handleBridgeBinaryMessage(buffer, wsProxies) {\n\tconst frame = decodeWsDataFrame(buffer)\n\tif (!frame || frame.kind !== 2) return\n\tconst proxy = wsProxies.get(frame.id)\n\tif (!proxy) return\n\tif ((frame.flags & 2) !== 0) {\n\t\tproxy.doWs.send(new TextDecoder().decode(frame.payload))\n\t} else {\n\t\tproxy.doWs.send(frame.payload)\n\t}\n}\n\nasync function handleBridgeJsonMessage(data, ws, env, ctx, wsProxies) {\n\tconst msg = JSON.parse(data)\n\tswitch (msg.t) {\n\t\tcase 'hello':\n\t\t\t// v2 handshake \u2014 acknowledge with welcome echoing the negotiated\n\t\t\t// capability intersection. The gateway advertises only what it\n\t\t\t// implements end-to-end: 'ws-relay' (binary WsData relay to/from DO\n\t\t\t// sockets) and 'http-transfer' (the R2 transfer HTTP side-channel).\n\t\t\t// 'streams' is intentionally NOT advertised \u2014 large DO/service-fetch\n\t\t\t// responses are inlined, not streamed, in this gateway.\n\t\t\tws.send(JSON.stringify({\n\t\t\t\tt: 'welcome',\n\t\t\t\tprotocolVersion: 2,\n\t\t\t\tcapabilities: ['ws-relay', 'http-transfer']\n\t\t\t\t\t.filter((c) => Array.isArray(msg.capabilities) && msg.capabilities.includes(c))\n\t\t\t\t\t.sort()\n\t\t\t}))\n\t\t\tbreak\n\t\tcase 'rpc.call':\n\t\t\tawait handleBridgeRpcCall(msg, ws, env, ctx)\n\t\t\tbreak\n\t\tcase 'ws.open':\n\t\t\tawait handleBridgeWsOpen(msg, ws, env, wsProxies)\n\t\t\tbreak\n\t\tcase 'ws.close':\n\t\t\thandleBridgeWsClose(msg, wsProxies)\n\t\t\tbreak\n\t}\n}\n\n// Pass-through DO WebSocket upgrade (bridge client -> gateway -> DO).\n//\n// The bridge client's openDoWebSocket() opens a REAL loopback WebSocket to this\n// endpoint (/_devflare/do-ws?binding&id&u) instead of tunneling frames over the\n// bridge socket. We resolve the DO by id and RETURN the DO's 101 response\n// verbatim (webSocket included), so miniflare wires the inbound connection to\n// the DO's client socket. That genuine inbound connection is what makes workerd\n// dispatch the DO's hibernation handlers (webSocketMessage/webSocketClose) and\n// deliver ctx.getWebSockets() broadcasts. Pumping the DO socket in-process (the\n// legacy handleBridgeWsOpen path) never triggers hibernation, so two sockets to\n// one getByName(id) could not see each other's messages. This mirrors the\n// browser WS_ROUTES pass-through (handleDoWebSocket) and real Cloudflare.\n//\n// NOTE: the devflare/test gateway (src/test/simple-context-gateway-script.ts)\n// keeps a byte-equivalent copy of this handler because it is a separately-bundled\n// Worker template that cannot import this runtime \u2014 keep the two in sync.\nasync function handleBridgeDoWebSocket(request, env, url) {\n\ttry {\n\t\tconst bindingName = url.searchParams.get('binding')\n\t\tconst idHex = url.searchParams.get('id')\n\t\tconst targetUrl = url.searchParams.get('u') || url.toString()\n\n\t\tconst binding = env[bindingName]\n\t\tif (!binding || typeof binding.idFromString !== 'function') {\n\t\t\treturn new Response('Durable Object binding not found: ' + bindingName, { status: 500 })\n\t\t}\n\t\tif (!idHex) {\n\t\t\treturn new Response('Missing Durable Object id', { status: 400 })\n\t\t}\n\n\t\tconst stub = binding.get(binding.idFromString(idHex))\n\t\t// Forward the real upgrade request (its headers carry any auth/cookies the\n\t\t// caller passed to connect()) and pass the DO's 101 response straight back.\n\t\treturn stub.fetch(new Request(targetUrl, { method: 'GET', headers: request.headers }))\n\t} catch (error) {\n\t\treturn new Response('Error opening DO WebSocket: ' + (error?.message || String(error)), {\n\t\t\tstatus: 500\n\t\t})\n\t}\n}\n\nfunction handleBridgeWebSocket(request, env, ctx) {\n\tconst { 0: client, 1: server } = new WebSocketPair()\n\tserver.accept()\n\n\t// Per-connection state: recreated for every bridge client so reloads and\n\t// concurrent clients never share WS proxy entries.\n\tconst wsProxies = new Map()\n\n\tserver.addEventListener('message', async (event) => {\n\t\ttry {\n\t\t\tif (typeof event.data === 'string') {\n\t\t\t\tawait handleBridgeJsonMessage(event.data, server, env, ctx, wsProxies)\n\t\t\t} else {\n\t\t\t\thandleBridgeBinaryMessage(event.data, wsProxies)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('[Gateway] Error:', error)\n\t\t}\n\t})\n\n\tserver.addEventListener('close', () => {\n\t\tfor (const proxy of wsProxies.values()) {\n\t\t\t// Best-effort cleanup: the DO-side WS may already be closed or in an\n\t\t\t// invalid state; any throw here would abort sibling closes. Surface\n\t\t\t// the swallowed error when DEVFLARE_DEBUG_BRIDGE is enabled.\n\t\t\ttry { proxy.doWs.close() } catch (error) {\n\t\t\t\tif (globalThis.DEVFLARE_DEBUG_BRIDGE) {\n\t\t\t\t\tconsole.warn('[devflare:bridge] proxy.doWs.close() failed', error)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twsProxies.clear()\n\t})\n\n\treturn new Response(null, { status: 101, webSocket: client })\n}\n\n// ---------------------------------------------------------------------------\n// HTTP transfer for R2 bodies (shared with src/bridge/server.ts in shape)\n// ---------------------------------------------------------------------------\n\nasync function handleHttpTransfer(request, env, url) {\n\tconst transferIdEncoded = url.pathname.split('/').pop()\n\tconst transferId = decodeURIComponent(transferIdEncoded || '')\n\tconst [binding, ...keyParts] = transferId.split(':')\n\tconst key = keyParts.join(':')\n\tconst bucket = env[binding]\n\n\tif (!bucket) return new Response('Bucket not found: ' + binding, { status: 404 })\n\n\tif (request.method === 'PUT' || request.method === 'POST') {\n\t\tconst result = await bucket.put(key, request.body)\n\t\treturn new Response(JSON.stringify(serializeR2Object(result)), {\n\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t})\n\t}\n\n\tif (request.method === 'GET') {\n\t\tconst object = await bucket.get(key)\n\t\tif (!object) return new Response('Not found', { status: 404 })\n\t\treturn new Response(object.body, {\n\t\t\theaders: {\n\t\t\t\t'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',\n\t\t\t\t'Content-Length': String(object.size)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn new Response('Method not allowed', { status: 405 })\n}\n";
8
8
  //# sourceMappingURL=gateway-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-runtime.d.ts","sourceRoot":"","sources":["../../src/bridge/gateway-runtime.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,6xwBAupB9B,CAAA"}
1
+ {"version":3,"file":"gateway-runtime.d.ts","sourceRoot":"","sources":["../../src/bridge/gateway-runtime.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,6wzBA6rB9B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../../src/bridge/v2/serialization.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,wFAAwF;AACxF,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhF,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;CACxC;AAED,MAAM,WAAW,6BAA6B;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,GACX;IAAE,UAAU,EAAE,4BAA4B,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAkBhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,4BAA4B,EACxC,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAQT;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,GACX;IAAE,UAAU,EAAE,6BAA6B,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAiBjF;AAED,wCAAwC;AACxC,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,6BAA6B,EACzC,KAAK,EAAE,gBAAgB,GACrB,QAAQ,CAOV"}
1
+ {"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../../src/bridge/v2/serialization.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,wFAAwF;AACxF,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhF,MAAM,WAAW,4BAA4B;IAC5C,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;CACxC;AAED,MAAM,WAAW,6BAA6B;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,GACX;IAAE,UAAU,EAAE,4BAA4B,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAkBhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,UAAU,EAAE,4BAA4B,EACxC,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAQT;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,GACX;IAAE,UAAU,EAAE,6BAA6B,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAsBjF;AAED,wCAAwC;AACxC,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,6BAA6B,EACzC,KAAK,EAAE,gBAAgB,GACrB,QAAQ,CAOV"}
@@ -38,6 +38,40 @@ export interface SerializedDOId {
38
38
  __type: typeof DO_ID_TYPE;
39
39
  hex: string;
40
40
  }
41
+ /**
42
+ * Read every `Set-Cookie` header value as a SEPARATE string.
43
+ *
44
+ * `Headers.forEach`/`entries()`/`get()` fold multiple `Set-Cookie` headers into
45
+ * one comma-joined value (the Fetch spec's sort-and-combine), which corrupts
46
+ * cookies: the second cookie's attributes bleed into the first. Only the
47
+ * dedicated accessors keep them apart — the standard `getSetCookie()` (Bun,
48
+ * Node, and workerd on a compatibility date ≥ 2023-08-01) or workerd's legacy
49
+ * `getAll('set-cookie')` (present on older compat dates that predate
50
+ * `getSetCookie`).
51
+ *
52
+ * @param headers - The header set to read.
53
+ * @returns Each `Set-Cookie` value in insertion order, or `null` when the
54
+ * runtime exposes neither accessor — the caller then keeps the combined value
55
+ * rather than dropping the header.
56
+ */
57
+ export declare function readSetCookieValues(headers: Headers): string[] | null;
58
+ /**
59
+ * Flatten a `Headers` set to `[name, value]` pairs WITHOUT collapsing multiple
60
+ * `Set-Cookie` headers into one, so a response setting several cookies survives
61
+ * the bridge round-trip byte-faithfully.
62
+ *
63
+ * Non-cookie headers are emitted via `forEach` as before. `Set-Cookie` is
64
+ * enumerated separately (see `readSetCookieValues`) and appended as its own pair
65
+ * per cookie, so the reconstructing `new Headers(pairs)` re-`append`s each one.
66
+ * When the runtime can split neither (no `getSetCookie`/`getAll`), the combined
67
+ * `forEach` value is kept verbatim — graceful degradation, never a dropped
68
+ * header.
69
+ *
70
+ * @param headers - The header set to flatten.
71
+ * @returns `[name, value]` pairs safe to round-trip; each `Set-Cookie` is a
72
+ * distinct pair.
73
+ */
74
+ export declare function serializeHeaders(headers: Headers): [string, string][];
41
75
  /** Serialize a Request to a POJO */
42
76
  export declare function serializeRequest(request: Request, options?: {
43
77
  httpThreshold?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"value-serialization.d.ts","sourceRoot":"","sources":["../../../src/bridge/v2/value-serialization.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"value-serialization.d.ts","sourceRoot":"","sources":["../../../src/bridge/v2/value-serialization.ts"],"names":[],"mappings":"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"}
package/dist/browser.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { n as defineConfig, t as ref } from "./_chunks/ref-CYJNAAe_.js";
2
2
  import { t as workerName } from "./_chunks/workerName-CFJsLZA-.js";
3
3
  import { n as getDurableObjectOptions, t as durableObject } from "./_chunks/decorators-QmV57ixr.js";
4
- import { F as BridgeClient, I as getClient, M as createEnvProxy, N as initEnv, P as setBindingHints } from "./_chunks/context-B6S7cqQW.js";
5
- import { i as vars, r as env } from "./_chunks/env-UqevBSTd.js";
4
+ import { F as BridgeClient, I as getClient, M as createEnvProxy, N as initEnv, P as setBindingHints } from "./_chunks/context-CQkLyah9.js";
5
+ import { i as vars, r as env } from "./_chunks/env-_QKteEYJ.js";
6
6
  //#region src/browser.ts
7
7
  function createUnsupportedApiError(name) {
8
8
  return /* @__PURE__ */ new Error(`${name} is not available in browser environments; import from devflare/test or devflare/runtime in a worker/Node context instead.`);
package/dist/cli/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as runCli, t as parseArgs } from "../_chunks/cli-DsmlUqPd.js";
1
+ import { n as runCli, t as parseArgs } from "../_chunks/cli-z3FJ-9cO.js";
2
2
  export { parseArgs, runCli };
@@ -1 +1 @@
1
- {"version":3,"file":"miniflare-dev-config.d.ts","sourceRoot":"","sources":["../../src/dev-server/miniflare-dev-config.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAG9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAI/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AAC9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAgClE,OAAO,EAAE,KAAK,kBAAkB,EAAyB,MAAM,wBAAwB,CAAA;AAGvF,KAAK,wBAAwB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,4BAA4B;IAC5C,MAAM,EAAE,cAAc,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,sBAAsB,EAAE,kBAAkB,CAAA;IAC1C,gBAAgB,EAAE,oBAAoB,GAAG,IAAI,CAAA;IAC7C,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,wBAAwB,EAAE,MAAM,CAAA;IAChC,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,wBAAwB,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC1D;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,CAAC,EAAE,eAAe,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,YAAY,EAAE,cAAc,CAAC,QAAQ,CAAC,EACtC,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACnB,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B,GAAG,GAAG,CAwQhF"}
1
+ {"version":3,"file":"miniflare-dev-config.d.ts","sourceRoot":"","sources":["../../src/dev-server/miniflare-dev-config.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAG9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAI/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AAC9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAgClE,OAAO,EAAE,KAAK,kBAAkB,EAAyB,MAAM,wBAAwB,CAAA;AAGvF,KAAK,wBAAwB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,4BAA4B;IAC5C,MAAM,EAAE,cAAc,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,sBAAsB,EAAE,kBAAkB,CAAA;IAC1C,gBAAgB,EAAE,oBAAoB,GAAG,IAAI,CAAA;IAC7C,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,wBAAwB,EAAE,MAAM,CAAA;IAChC,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,wBAAwB,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC1D;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,CAAC,EAAE,eAAe,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,YAAY,EAAE,cAAc,CAAC,QAAQ,CAAC,EACtC,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,GACnB,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B,GAAG,GAAG,CA+QhF"}
package/dist/index.js CHANGED
@@ -4,6 +4,6 @@ import { a as ConfigResourceResolutionError, d as configSchema, n as ConfigValid
4
4
  import { n as compileConfig, s as stringifyConfig } from "./_chunks/compiler-CgIFPfRA.js";
5
5
  import { t as workerName } from "./_chunks/workerName-CFJsLZA-.js";
6
6
  import { n as getDurableObjectOptions, t as durableObject } from "./_chunks/decorators-QmV57ixr.js";
7
- import { n as runCli, t as parseArgs } from "./_chunks/cli-DsmlUqPd.js";
8
- import { i as vars, r as env } from "./_chunks/env-UqevBSTd.js";
7
+ import { n as runCli, t as parseArgs } from "./_chunks/cli-z3FJ-9cO.js";
8
+ import { i as vars, r as env } from "./_chunks/env-_QKteEYJ.js";
9
9
  export { ConfigNotFoundError, ConfigResourceResolutionError, ConfigValidationError, compileConfig, configSchema, defineConfig as default, defineConfig, durableObject, env, getDurableObjectOptions, loadConfig, loadResolvedConfig, parseArgs, preview, ref, runCli, stringifyConfig, vars, workerName };
@@ -1,5 +1,5 @@
1
1
  import { n as getDurableObjectOptions, t as durableObject } from "../_chunks/decorators-QmV57ixr.js";
2
- import { A as createTailEvent, C as createDurableObjectWebSocketCloseEvent, D as createFetchEvent, E as createEmailEvent, O as createQueueEvent, S as createDurableObjectFetchEvent, T as createDurableObjectWebSocketMessageEvent, _ as runWithContext, a as getDurableObjectFetchEvent, b as createContextProxy, c as getDurableObjectWebSocketMessageEvent, d as getEventContextOrNull, f as getFetchEvent, g as hasContext, h as getTailEvent, i as getDurableObjectEvent, k as createScheduledEvent, l as getEmailEvent, m as getScheduledEvent, n as getContextOrNull, o as getDurableObjectWebSocketCloseEvent, p as getQueueEvent, r as getDurableObjectAlarmEvent, s as getDurableObjectWebSocketErrorEvent, t as getContext, u as getEventContext, v as runWithEventContext, w as createDurableObjectWebSocketErrorEvent, x as createDurableObjectAlarmEvent, y as ContextAccessError } from "../_chunks/context-B6S7cqQW.js";
2
+ import { A as createTailEvent, C as createDurableObjectWebSocketCloseEvent, D as createFetchEvent, E as createEmailEvent, O as createQueueEvent, S as createDurableObjectFetchEvent, T as createDurableObjectWebSocketMessageEvent, _ as runWithContext, a as getDurableObjectFetchEvent, b as createContextProxy, c as getDurableObjectWebSocketMessageEvent, d as getEventContextOrNull, f as getFetchEvent, g as hasContext, h as getTailEvent, i as getDurableObjectEvent, k as createScheduledEvent, l as getEmailEvent, m as getScheduledEvent, n as getContextOrNull, o as getDurableObjectWebSocketCloseEvent, p as getQueueEvent, r as getDurableObjectAlarmEvent, s as getDurableObjectWebSocketErrorEvent, t as getContext, u as getEventContext, v as runWithEventContext, w as createDurableObjectWebSocketErrorEvent, x as createDurableObjectAlarmEvent, y as ContextAccessError } from "../_chunks/context-CQkLyah9.js";
3
3
  import { clearLocalSendEmailBindings, setLocalSendEmailBindings } from "../utils/send-email.js";
4
- import { C as vars, S as locals, _ as resolveFetchHandler, a as matchFetchRoute, b as env, c as assertExplicitScheduledHandlerStyle, d as defineQueueHandler, f as defineScheduledHandler, g as markWorkerStyle, h as markResolveStyle, i as invokeRouteModules, l as createResolveFetch, m as invokeFetchModule, n as presignR2Put, o as assertExplicit2ArgStyle, p as invokeFetchHandler, r as createRouteResolve, s as assertExplicitQueueHandlerStyle, t as presignR2Get, u as defineFetchHandler, v as sequence, x as event, y as ctx } from "../_chunks/runtime-sEr9b0ns.js";
4
+ import { C as vars, S as locals, _ as resolveFetchHandler, a as matchFetchRoute, b as env, c as assertExplicitScheduledHandlerStyle, d as defineQueueHandler, f as defineScheduledHandler, g as markWorkerStyle, h as markResolveStyle, i as invokeRouteModules, l as createResolveFetch, m as invokeFetchModule, n as presignR2Put, o as assertExplicit2ArgStyle, p as invokeFetchHandler, r as createRouteResolve, s as assertExplicitQueueHandlerStyle, t as presignR2Get, u as defineFetchHandler, v as sequence, x as event, y as ctx } from "../_chunks/runtime-Ckf-KKc7.js";
5
5
  export { ContextAccessError, assertExplicit2ArgStyle, assertExplicitQueueHandlerStyle, assertExplicitScheduledHandlerStyle, clearLocalSendEmailBindings, createContextProxy, createDurableObjectAlarmEvent, createDurableObjectFetchEvent, createDurableObjectWebSocketCloseEvent, createDurableObjectWebSocketErrorEvent, createDurableObjectWebSocketMessageEvent, createEmailEvent, createFetchEvent, createQueueEvent, createResolveFetch, createRouteResolve, createScheduledEvent, createTailEvent, ctx, defineFetchHandler, defineQueueHandler, defineScheduledHandler, durableObject, env, event, getContext, getContextOrNull, getDurableObjectAlarmEvent, getDurableObjectEvent, getDurableObjectFetchEvent, getDurableObjectOptions, getDurableObjectWebSocketCloseEvent, getDurableObjectWebSocketErrorEvent, getDurableObjectWebSocketMessageEvent, getEmailEvent, getEventContext, getEventContextOrNull, getFetchEvent, getQueueEvent, getScheduledEvent, getTailEvent, hasContext, invokeFetchHandler, invokeFetchModule, invokeRouteModules, locals, markResolveStyle, markWorkerStyle, matchFetchRoute, presignR2Get, presignR2Put, resolveFetchHandler, runWithContext, runWithEventContext, sequence, setLocalSendEmailBindings, vars };
@@ -1,6 +1,6 @@
1
1
  import { r as loadConfig } from "../_chunks/loader-E_UGyTrJ.js";
2
2
  import { l as normalizeHyperdriveBinding } from "../_chunks/schema-normalization-Zgm4W6O6.js";
3
- import { D as createFetchEvent, I as getClient, M as createEnvProxy, P as setBindingHints, v as runWithEventContext } from "../_chunks/context-B6S7cqQW.js";
3
+ import { D as createFetchEvent, I as getClient, M as createEnvProxy, P as setBindingHints, v as runWithEventContext } from "../_chunks/context-CQkLyah9.js";
4
4
  import { createLocalSendEmailBinding } from "../utils/send-email.js";
5
5
  import { i as extractBindingHints, n as createLocalWorkerLoaderBinding, t as createLocalHyperdrive } from "../_chunks/local-hyperdrive-CJ90j46O.js";
6
6
  import { t as buildLocalSecretNodeBindings } from "../_chunks/local-secrets-6sMstHXw.js";
@@ -4,10 +4,10 @@ import { _ as normalizeSecretsStoreBinding, f as normalizeMediaBinding, h as nor
4
4
  import { t as applyLocalDevVarsToConfig } from "../_chunks/local-dev-vars-CTSa-wvF.js";
5
5
  import { g as isAuthenticated, p as getApiToken } from "../_chunks/api-TzdliH-6.js";
6
6
  import { d as getPrimaryAccount, n as getEffectiveAccountId } from "../_chunks/preferences-BKp_7XJx.js";
7
- import { A as createTailEvent, D as createFetchEvent, E as createEmailEvent, F as BridgeClient, M as createEnvProxy, O as createQueueEvent, P as setBindingHints, _ as runWithContext, k as createScheduledEvent, v as runWithEventContext, x as createDurableObjectAlarmEvent } from "../_chunks/context-B6S7cqQW.js";
7
+ import { A as createTailEvent, D as createFetchEvent, E as createEmailEvent, F as BridgeClient, M as createEnvProxy, O as createQueueEvent, P as setBindingHints, _ as runWithContext, k as createScheduledEvent, v as runWithEventContext, x as createDurableObjectAlarmEvent } from "../_chunks/context-CQkLyah9.js";
8
8
  import { createLocalSendEmailBinding, wrapEnvSendEmailBindings } from "../utils/send-email.js";
9
- import { n as __setTestContext, r as env, t as __clearTestContext } from "../_chunks/env-UqevBSTd.js";
10
- import { _ as resolveFetchHandler, a as matchFetchRoute, m as invokeFetchModule, r as createRouteResolve } from "../_chunks/runtime-sEr9b0ns.js";
9
+ import { n as __setTestContext, r as env, t as __clearTestContext } from "../_chunks/env-_QKteEYJ.js";
10
+ import { _ as resolveFetchHandler, a as matchFetchRoute, m as invokeFetchModule, r as createRouteResolve } from "../_chunks/runtime-Ckf-KKc7.js";
11
11
  import { i as extractBindingHints, n as createLocalWorkerLoaderBinding, r as disposeLocalWorkerLoaderBindings, t as createLocalHyperdrive } from "../_chunks/local-hyperdrive-CJ90j46O.js";
12
12
  import { E as buildTailConsumersConfig, T as buildStreamingTailConsumersConfig, a as hasServiceBindings, i as hasCrossWorkerDOs, m as buildHyperdrivesConfig, o as resolveDOBindings, r as clearBundleCache, s as resolveServiceBindings, t as discoverRoutes, u as buildAnalyticsEngineConfig } from "../_chunks/routes-BMU7ga_l.js";
13
13
  import { i as findFiles, t as DEFAULT_DO_PATTERN } from "../_chunks/glob-CmQOvunB.js";
@@ -1,2 +1,2 @@
1
- import { a as devflarePlugin, c as getDevflareConfigs, i as writeGeneratedViteConfig, n as resolveEffectiveViteProject, o as getPluginContext, r as resolveViteUserConfig, s as getCloudflareConfig, t as hasInlineViteConfig } from "../_chunks/vite-CzqppgNp.js";
1
+ import { a as devflarePlugin, c as getDevflareConfigs, i as writeGeneratedViteConfig, n as resolveEffectiveViteProject, o as getPluginContext, r as resolveViteUserConfig, s as getCloudflareConfig, t as hasInlineViteConfig } from "../_chunks/vite-BBiqD3k7.js";
2
2
  export { devflarePlugin as default, devflarePlugin, getCloudflareConfig, getDevflareConfigs, getPluginContext, hasInlineViteConfig, resolveEffectiveViteProject, resolveViteUserConfig, writeGeneratedViteConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devflare",
3
- "version": "1.0.0-next.63",
3
+ "version": "1.0.0-next.65",
4
4
  "description": "Devflare is a developer-first toolkit for Cloudflare Workers that sits on top of Miniflare and Wrangler-compatible config",
5
5
  "repository": {
6
6
  "type": "git",
@@ -84,7 +84,7 @@
84
84
  "test:unit": "bun test tests/unit",
85
85
  "test:coverage": "bun test tests/unit --coverage",
86
86
  "test:integration:control": "bun test tests/integration/cli tests/integration/examples tests/integration/package-entry tests/integration/vite",
87
- "test:integration:bridge": "bun test tests/integration/bridge/bridge-proxy.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/case18-do.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/durable-object.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-rpc-native.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-hibernation.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-hibernation-test-context.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-app-route.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/miniflare.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/r2-transfer.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/r2-presign.test.ts --parallel=1 --max-concurrency=1",
87
+ "test:integration:bridge": "bun test tests/integration/bridge/bridge-proxy.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/case18-do.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/durable-object.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-rpc-native.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-hibernation.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-hibernation-test-context.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/do-websocket-app-route.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/miniflare.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/r2-transfer.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/r2-presign.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/set-cookie-bridge.test.ts --parallel=1 --max-concurrency=1",
88
88
  "test:integration:test-context": "bun test tests/integration/test-context --parallel=1 --max-concurrency=1",
89
89
  "test:integration:dev-server": "bun test tests/integration/dev-server --parallel=1 --max-concurrency=1",
90
90
  "test:integration:shims": "bun test tests/integration/shims --parallel=1 --max-concurrency=1",