devflare 1.0.0-next.60 → 1.0.0-next.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/_chunks/{build-Dp8k2SWa.js → build-Bipv3UYU.js} +1 -1
  2. package/dist/_chunks/{build-artifacts-DLEYfdVL.js → build-artifacts-B5OsATal.js} +1 -1
  3. package/dist/_chunks/{cli-BlVetiEh.js → cli-sOZfXGua.js} +3 -3
  4. package/dist/_chunks/{context-CX50Y2Kb.js → context-B6S7cqQW.js} +272 -68
  5. package/dist/_chunks/{deploy-Blrdr3Gv.js → deploy-cU11igDG.js} +1 -1
  6. package/dist/_chunks/{dev-DRP6LR2H.js → dev-9XMiBRSG.js} +84 -9
  7. package/dist/_chunks/{env-S0_nMVz1.js → env-UqevBSTd.js} +1 -1
  8. package/dist/_chunks/{runtime-RekfD_2X.js → runtime-sEr9b0ns.js} +1 -1
  9. package/dist/_chunks/{vite-CtvgOi1c.js → vite-CzqppgNp.js} +1 -1
  10. package/dist/bridge/client.d.ts +47 -4
  11. package/dist/bridge/client.d.ts.map +1 -1
  12. package/dist/bridge/do-rpc-dispatch.d.ts +23 -0
  13. package/dist/bridge/do-rpc-dispatch.d.ts.map +1 -0
  14. package/dist/bridge/do-websocket-connect.d.ts +33 -0
  15. package/dist/bridge/do-websocket-connect.d.ts.map +1 -0
  16. package/dist/bridge/gateway-runtime.d.ts +1 -1
  17. package/dist/bridge/gateway-runtime.d.ts.map +1 -1
  18. package/dist/bridge/proxy.d.ts.map +1 -1
  19. package/dist/bridge/server.d.ts.map +1 -1
  20. package/dist/bridge/websocket-constructor.d.ts +38 -0
  21. package/dist/bridge/websocket-constructor.d.ts.map +1 -0
  22. package/dist/browser.js +2 -2
  23. package/dist/cli/index.js +1 -1
  24. package/dist/dev-server/gateway-script.d.ts.map +1 -1
  25. package/dist/index.js +2 -2
  26. package/dist/runtime/index.js +2 -2
  27. package/dist/sveltekit/index.js +1 -1
  28. package/dist/test/index.js +36 -3
  29. package/dist/test/simple-context-gateway-script.d.ts.map +1 -1
  30. package/dist/vite/index.js +1 -1
  31. package/package.json +2 -2
@@ -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-DLEYfdVL.js";
2
+ import { t as prepareBuildArtifacts } from "./build-artifacts-B5OsATal.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-CtvgOi1c.js";
7
+ import { i as writeGeneratedViteConfig, l as prepareComposedWorkerEntrypoint, n as resolveEffectiveViteProject } from "./vite-CzqppgNp.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-DRP6LR2H.js");
1104
+ const { runDevCommand } = await import("./dev-9XMiBRSG.js");
1105
1105
  return runDevCommand(parsed, logger, options);
1106
1106
  }
1107
1107
  async function runBuild(parsed, logger, options) {
1108
- const { runBuildCommand } = await import("./build-Dp8k2SWa.js");
1108
+ const { runBuildCommand } = await import("./build-Bipv3UYU.js");
1109
1109
  return runBuildCommand(parsed, logger, options);
1110
1110
  }
1111
1111
  async function runDeploy(parsed, logger, options) {
1112
- const { runDeployCommand } = await import("./deploy-Blrdr3Gv.js");
1112
+ const { runDeployCommand } = await import("./deploy-cU11igDG.js");
1113
1113
  return runDeployCommand(parsed, logger, {
1114
1114
  ...options,
1115
1115
  requireExplicitDeployTarget: true
@@ -1,5 +1,234 @@
1
1
  import { wrapEnvSendEmailBindings } from "../utils/send-email.js";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
+ //#region src/bridge/v2/wire.ts
4
+ /** Binary frame kinds */
5
+ const BinaryKind = {
6
+ StreamChunk: 1,
7
+ WsData: 2
8
+ };
9
+ /** Binary frame flags */
10
+ const BinaryFlags = {
11
+ FIN: 1,
12
+ TEXT: 2
13
+ };
14
+ /** Encode a binary frame */
15
+ function encodeBinaryFrame(kind, id, seq, flags, payload) {
16
+ const frame = new Uint8Array(10 + payload.byteLength);
17
+ const view = new DataView(frame.buffer);
18
+ view.setUint8(0, kind);
19
+ view.setUint32(1, id, true);
20
+ view.setUint32(5, seq, true);
21
+ view.setUint8(9, flags);
22
+ frame.set(payload, 10);
23
+ return frame;
24
+ }
25
+ /** Parse a JSON message from string */
26
+ function parseJsonMsg(data) {
27
+ const msg = JSON.parse(data);
28
+ if (typeof msg !== "object" || msg === null || !("t" in msg)) throw new Error("Invalid message: missing type field");
29
+ return msg;
30
+ }
31
+ /** Stringify a JSON message */
32
+ function stringifyJsonMsg(msg) {
33
+ return JSON.stringify(msg);
34
+ }
35
+ let streamIdCounter = 0;
36
+ let wsIdCounter = 0;
37
+ /** Generate unique stream ID */
38
+ function nextStreamId() {
39
+ return ++streamIdCounter;
40
+ }
41
+ /** Generate unique WebSocket proxy ID */
42
+ function nextWsId() {
43
+ return ++wsIdCounter;
44
+ }
45
+ /** Default chunk size for streaming (256 KB) */
46
+ const DEFAULT_CHUNK_SIZE = 256 * 1024;
47
+ /** Threshold for switching to HTTP transfer (512 KB — workerd ~1MB WS message limit). */
48
+ const HTTP_TRANSFER_THRESHOLD = 512 * 1024;
49
+ //#endregion
50
+ //#region src/bridge/websocket-constructor.ts
51
+ let wsPackageConstructorPromise = null;
52
+ /**
53
+ * Lazily import the `ws` package's WebSocket constructor (memoized). Used as the
54
+ * Node fallback and whenever custom upgrade headers are required — unlike the
55
+ * platform global, the `ws` constructor accepts an options object with headers.
56
+ *
57
+ * @returns The `ws` package WebSocket constructor.
58
+ * @throws When the `ws` package does not export a usable WebSocket implementation.
59
+ */
60
+ async function importWsPackageConstructor() {
61
+ if (!wsPackageConstructorPromise) wsPackageConstructorPromise = (async () => {
62
+ const wsModule = await new Function("specifier", [
63
+ "return ",
64
+ "import",
65
+ "(specifier)"
66
+ ].join(""))("ws");
67
+ const defaultExport = wsModule.default;
68
+ const wsConstructor = wsModule.WebSocket ?? (typeof defaultExport === "object" && defaultExport !== null ? defaultExport.WebSocket : void 0) ?? defaultExport;
69
+ if (typeof wsConstructor !== "function") throw new Error("Could not load a WebSocket client implementation from the ws package");
70
+ return wsConstructor;
71
+ })();
72
+ return wsPackageConstructorPromise;
73
+ }
74
+ /**
75
+ * Return the runtime's global WebSocket constructor, or null when the runtime
76
+ * has none (older Node without a global WebSocket).
77
+ *
78
+ * @param runtimeWebSocket - The candidate global (defaults to `globalThis.WebSocket`).
79
+ * @returns The global constructor, or null.
80
+ */
81
+ function getRuntimeWebSocketConstructor(runtimeWebSocket = globalThis.WebSocket) {
82
+ if (typeof runtimeWebSocket === "function") return runtimeWebSocket;
83
+ return null;
84
+ }
85
+ /**
86
+ * Resolve a WebSocket constructor for the bridge socket: prefer the runtime
87
+ * global, fall back to the `ws` package.
88
+ *
89
+ * @param runtimeWebSocket - The candidate global (defaults to `globalThis.WebSocket`).
90
+ * @returns A usable WebSocket constructor.
91
+ */
92
+ async function resolveBridgeWebSocketConstructor(runtimeWebSocket = globalThis.WebSocket) {
93
+ const runtimeConstructor = getRuntimeWebSocketConstructor(runtimeWebSocket);
94
+ if (runtimeConstructor) return runtimeConstructor;
95
+ return importWsPackageConstructor();
96
+ }
97
+ /**
98
+ * Resolve the WebSocket constructor for a Durable Object pass-through
99
+ * connection. Custom upgrade headers require the `ws` package (whose constructor
100
+ * accepts an options object); the global WebSocket (undici/Bun) cannot set
101
+ * request headers, so it is used only when there are no headers to forward.
102
+ *
103
+ * @param hasHeaders - Whether application headers must ride the upgrade request.
104
+ * @returns A WebSocket constructor suitable for the connection.
105
+ */
106
+ async function resolveDoWebSocketConstructor(hasHeaders) {
107
+ if (hasHeaders) return importWsPackageConstructor();
108
+ return resolveBridgeWebSocketConstructor();
109
+ }
110
+ //#endregion
111
+ //#region src/bridge/do-websocket-connect.ts
112
+ /**
113
+ * Hop-by-hop / WebSocket-control request headers that the WebSocket client sets
114
+ * itself. They must never be copied from the user's `connect(url, { headers })`
115
+ * onto the outgoing upgrade — a duplicate `Upgrade`/`Sec-WebSocket-*` breaks the
116
+ * handshake (the `ws` package rejects it). Auth/cookie headers are preserved.
117
+ */
118
+ const WS_CONTROL_HEADERS = new Set([
119
+ "upgrade",
120
+ "connection",
121
+ "host",
122
+ "sec-websocket-key",
123
+ "sec-websocket-version",
124
+ "sec-websocket-extensions",
125
+ "sec-websocket-protocol"
126
+ ]);
127
+ /**
128
+ * Strip WebSocket-control headers from user-supplied upgrade headers, leaving
129
+ * only the application headers (auth, cookies) worth forwarding to the DO.
130
+ *
131
+ * @param headers - Header tuples from `connect(url, { headers })`, or undefined.
132
+ * @returns A plain object of the forwardable headers (may be empty).
133
+ */
134
+ function forwardableUpgradeHeaders(headers) {
135
+ const out = {};
136
+ for (const [key, value] of headers ?? []) if (!WS_CONTROL_HEADERS.has(key.toLowerCase())) out[key] = value;
137
+ return out;
138
+ }
139
+ /**
140
+ * Normalize an inbound WebSocket payload to the `Uint8Array | string` contract
141
+ * the DO Socket facade expects: text frames stay strings, binary frames become
142
+ * a `Uint8Array` view regardless of whether the runtime delivered an
143
+ * `ArrayBuffer`, a `Buffer`, or another typed-array/DataView.
144
+ *
145
+ * @param data - The raw `MessageEvent.data` from the DO pass-through socket.
146
+ * @returns The payload as a string (text) or `Uint8Array` (binary).
147
+ */
148
+ function normalizeIncomingWsData(data) {
149
+ if (typeof data === "string") return data;
150
+ if (data instanceof Uint8Array) return data;
151
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
152
+ if (ArrayBuffer.isView(data)) {
153
+ const view = data;
154
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
155
+ }
156
+ return String(data);
157
+ }
158
+ /**
159
+ * Open a real pass-through WebSocket to a Durable Object through the gateway's
160
+ * `/_devflare/do-ws` endpoint. Unlike the legacy relay, this is a standalone
161
+ * connection with its own lifecycle, so it is unaffected by the bridge client's
162
+ * `disconnect()`/reconnect bookkeeping.
163
+ *
164
+ * @param bridgeUrl - The bridge/gateway base URL (e.g. `ws://127.0.0.1:8787`).
165
+ * @param binding - DO namespace binding name (e.g. `DOC_ROOM`).
166
+ * @param idHex - Already-resolved Durable Object id, hex form.
167
+ * @param targetUrl - The DO fetch URL the caller passed to `stub.connect(url)`.
168
+ * @param headers - Optional upgrade headers (auth/cookies) forwarded to the DO.
169
+ * @returns A proxy surface (send/close/onMessage/onClose) over the socket.
170
+ * @throws When the pass-through socket fails to open (bad binding/id, or the DO
171
+ * rejected the upgrade).
172
+ */
173
+ async function openDurableObjectWebSocket(bridgeUrl, binding, idHex, targetUrl, headers) {
174
+ const wsUrl = new URL(bridgeUrl);
175
+ wsUrl.pathname = "/_devflare/do-ws";
176
+ wsUrl.searchParams.set("binding", binding);
177
+ wsUrl.searchParams.set("id", idHex);
178
+ wsUrl.searchParams.set("u", targetUrl);
179
+ const forwardHeaders = forwardableUpgradeHeaders(headers);
180
+ const hasHeaders = Object.keys(forwardHeaders).length > 0;
181
+ const WebSocketCtor = await resolveDoWebSocketConstructor(hasHeaders);
182
+ const socket = hasHeaders ? new WebSocketCtor(wsUrl.toString(), { headers: forwardHeaders }) : new WebSocketCtor(wsUrl.toString());
183
+ socket.binaryType = "arraybuffer";
184
+ let opened = false;
185
+ let messageHandler = null;
186
+ let closeHandler = null;
187
+ const pendingMessages = [];
188
+ let pendingClose = null;
189
+ socket.onmessage = (event) => {
190
+ const data = normalizeIncomingWsData(event.data);
191
+ if (messageHandler) messageHandler(data);
192
+ else pendingMessages.push(data);
193
+ };
194
+ await new Promise((resolve, reject) => {
195
+ socket.onopen = () => {
196
+ opened = true;
197
+ resolve();
198
+ };
199
+ socket.onerror = () => {
200
+ if (!opened) reject(/* @__PURE__ */ new Error(`DO WebSocket connection failed: ${binding}/${idHex}`));
201
+ };
202
+ socket.onclose = (event) => {
203
+ if (!opened) {
204
+ reject(/* @__PURE__ */ new Error(`DO WebSocket closed before open: ${binding}/${idHex}`));
205
+ return;
206
+ }
207
+ if (closeHandler) closeHandler(event?.code, event?.reason);
208
+ else pendingClose = {
209
+ code: event?.code,
210
+ reason: event?.reason
211
+ };
212
+ };
213
+ });
214
+ return {
215
+ wid: nextWsId(),
216
+ send: (data) => socket.send(data),
217
+ close: (code, reason) => socket.close(code, reason),
218
+ onMessage: (handler) => {
219
+ messageHandler = handler;
220
+ if (pendingMessages.length > 0) for (const data of pendingMessages.splice(0)) handler(data);
221
+ },
222
+ onClose: (handler) => {
223
+ closeHandler = handler;
224
+ if (pendingClose) {
225
+ handler(pendingClose.code, pendingClose.reason);
226
+ pendingClose = null;
227
+ }
228
+ }
229
+ };
230
+ }
231
+ //#endregion
3
232
  //#region src/bridge/log.ts
4
233
  const isDebugEnabled = () => {
5
234
  try {
@@ -479,53 +708,6 @@ function tryParseWireError(data) {
479
708
  return parsed;
480
709
  }
481
710
  //#endregion
482
- //#region src/bridge/v2/wire.ts
483
- /** Binary frame kinds */
484
- const BinaryKind = {
485
- StreamChunk: 1,
486
- WsData: 2
487
- };
488
- /** Binary frame flags */
489
- const BinaryFlags = {
490
- FIN: 1,
491
- TEXT: 2
492
- };
493
- /** Encode a binary frame */
494
- function encodeBinaryFrame(kind, id, seq, flags, payload) {
495
- const frame = new Uint8Array(10 + payload.byteLength);
496
- const view = new DataView(frame.buffer);
497
- view.setUint8(0, kind);
498
- view.setUint32(1, id, true);
499
- view.setUint32(5, seq, true);
500
- view.setUint8(9, flags);
501
- frame.set(payload, 10);
502
- return frame;
503
- }
504
- /** Parse a JSON message from string */
505
- function parseJsonMsg(data) {
506
- const msg = JSON.parse(data);
507
- if (typeof msg !== "object" || msg === null || !("t" in msg)) throw new Error("Invalid message: missing type field");
508
- return msg;
509
- }
510
- /** Stringify a JSON message */
511
- function stringifyJsonMsg(msg) {
512
- return JSON.stringify(msg);
513
- }
514
- let streamIdCounter = 0;
515
- let wsIdCounter = 0;
516
- /** Generate unique stream ID */
517
- function nextStreamId() {
518
- return ++streamIdCounter;
519
- }
520
- /** Generate unique WebSocket proxy ID */
521
- function nextWsId() {
522
- return ++wsIdCounter;
523
- }
524
- /** Default chunk size for streaming (256 KB) */
525
- const DEFAULT_CHUNK_SIZE = 256 * 1024;
526
- /** Threshold for switching to HTTP transfer (512 KB — workerd ~1MB WS message limit). */
527
- const HTTP_TRANSFER_THRESHOLD = 512 * 1024;
528
- //#endregion
529
711
  //#region src/bridge/v2/value-serialization.ts
530
712
  /** Serialize a Request to a POJO */
531
713
  async function serializeRequest(request, options) {
@@ -859,25 +1041,6 @@ const BRIDGE_CLIENT_CAPABILITIES = [
859
1041
  "ws-relay",
860
1042
  "http-transfer"
861
1043
  ];
862
- let wsPackageConstructorPromise = null;
863
- async function importWsPackageConstructor() {
864
- if (!wsPackageConstructorPromise) wsPackageConstructorPromise = (async () => {
865
- const wsModule = await new Function("specifier", [
866
- "return ",
867
- "import",
868
- "(specifier)"
869
- ].join(""))("ws");
870
- const defaultExport = wsModule.default;
871
- const wsConstructor = wsModule.WebSocket ?? (typeof defaultExport === "object" && defaultExport !== null ? defaultExport.WebSocket : void 0) ?? defaultExport;
872
- if (typeof wsConstructor !== "function") throw new Error("Could not load a WebSocket client implementation from the ws package");
873
- return wsConstructor;
874
- })();
875
- return wsPackageConstructorPromise;
876
- }
877
- function getRuntimeWebSocketConstructor(runtimeWebSocket = globalThis.WebSocket) {
878
- if (typeof runtimeWebSocket === "function") return runtimeWebSocket;
879
- return null;
880
- }
881
1044
  var BridgeClient = class {
882
1045
  ws = null;
883
1046
  codec = null;
@@ -1072,7 +1235,18 @@ var BridgeClient = class {
1072
1235
  if (current.size === 0) this.eventListeners.delete(topic);
1073
1236
  };
1074
1237
  }
1075
- /** Create a proxied WebSocket to a Durable Object */
1238
+ /**
1239
+ * Create a proxied WebSocket to a Durable Object via the in-process bridge
1240
+ * relay (`ws.open` control frame → gateway `stub.fetch()` pump → `WsData`
1241
+ * frames).
1242
+ *
1243
+ * DEPRECATED for DO connect(): this relay pumps the DO's WebSocket in-process,
1244
+ * which does NOT trigger workerd's hibernation dispatch — so a DO that uses
1245
+ * `ctx.acceptWebSocket()` never sees `webSocketMessage`/`webSocketClose` and
1246
+ * cross-socket `ctx.getWebSockets()` broadcasts never cross. `connect()` now
1247
+ * uses `openDoWebSocket()` (a real pass-through) instead. This method is
1248
+ * retained for the `ws-relay` wire capability and backward compatibility.
1249
+ */
1076
1250
  async createWsProxy(binding, id, url, headers) {
1077
1251
  await this.ensureConnected();
1078
1252
  const wid = nextWsId();
@@ -1131,6 +1305,36 @@ var BridgeClient = class {
1131
1305
  }
1132
1306
  };
1133
1307
  }
1308
+ /**
1309
+ * Open a REAL WebSocket to the gateway's DO pass-through endpoint
1310
+ * (`/_devflare/do-ws`), which forwards the upgrade to the target Durable
1311
+ * Object and passes through its `101` response.
1312
+ *
1313
+ * This is the ONLY way a bridged DO WebSocket triggers the runtime's
1314
+ * hibernation dispatch under miniflare: the gateway returns the DO's client
1315
+ * socket for a genuine inbound connection, so workerd pumps it and invokes
1316
+ * `webSocketMessage`/`webSocketClose` and delivers `ctx.getWebSockets()`
1317
+ * broadcasts. The legacy in-process relay (`createWsProxy` + gateway
1318
+ * `stub.fetch()` pump) never fires hibernation, so two sockets to the same
1319
+ * `getByName(id)` could not see each other's messages. Here they share ONE DO
1320
+ * instance and broadcast across each other, exactly as on real Cloudflare.
1321
+ *
1322
+ * Unlike `createWsProxy`, this does not multiplex over the bridge socket: it
1323
+ * is a standalone connection (see `openDurableObjectWebSocket`) with its own
1324
+ * lifecycle, so it is unaffected by the bridge client's disconnect/reconnect
1325
+ * bookkeeping. It also does not require the bridge socket to be connected.
1326
+ *
1327
+ * @param binding - DO namespace binding name (e.g. `DOC_ROOM`).
1328
+ * @param idHex - Already-resolved Durable Object id, hex form.
1329
+ * @param targetUrl - The DO fetch URL the caller passed to `stub.connect(url)`.
1330
+ * @param headers - Optional upgrade headers (auth/cookies) forwarded to the DO.
1331
+ * @returns A minimal proxy surface (send/close/onMessage/onClose) over the socket.
1332
+ * @throws When the pass-through socket fails to open (bad binding/id, or the
1333
+ * DO rejected the upgrade).
1334
+ */
1335
+ async openDoWebSocket(binding, idHex, targetUrl, headers) {
1336
+ return openDurableObjectWebSocket(this.url, binding, idHex, targetUrl, headers);
1337
+ }
1134
1338
  /** Create a readable stream that pulls from the bridge */
1135
1339
  createReadableStream(sid) {
1136
1340
  return new ReadableStream({
@@ -1574,7 +1778,7 @@ function createDOStubProxy(client, bindingName, idInfo, proxyOptions = {}) {
1574
1778
  if (options?.headers) new Headers(options.headers).forEach((value, key) => {
1575
1779
  headersList.push([key, value]);
1576
1780
  });
1577
- const wsProxy = await client.createWsProxy(bindingName, id.hex, url, headersList);
1781
+ const wsProxy = await client.openDoWebSocket(bindingName, id.hex, url, headersList);
1578
1782
  let readController = null;
1579
1783
  const readable = new ReadableStream({
1580
1784
  start(controller) {
@@ -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-DLEYfdVL.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";
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-CtvgOi1c.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";
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";
@@ -542,6 +542,42 @@ function resolveDoNamespace(binding, jurisdiction) {
542
542
  return binding
543
543
  }
544
544
 
545
+ // Dispatch a single Durable Object RPC method call to its stub.
546
+ //
547
+ // A DO that extends DurableObject (from cloudflare:workers) exposes its methods
548
+ // as native RPC on the stub, so the method is invoked DIRECTLY as
549
+ // stub[method](...args) — exactly as on real Cloudflare. This is the ONLY
550
+ // correct path for a DO that ALSO defines its own fetch(): routing the call
551
+ // through fetch() would hand devflare's internal _rpc probe to the user handler,
552
+ // which may reject it (e.g. a websocket-only fetch() returning 426) and yield a
553
+ // non-JSON body that then fails to parse (the reported crash).
554
+ //
555
+ // The fetch _rpc convention is kept only as a fallback for DOs that are not
556
+ // RPC-enabled: the stub proxies every property as callable, so such a DO only
557
+ // reveals itself when the native call throws "does not support RPC".
558
+ async function callDurableObjectRpc(stub, methodName, args) {
559
+ if (typeof stub[methodName] === 'function') {
560
+ try {
561
+ return await stub[methodName](...args)
562
+ } catch (error) {
563
+ if (!isDurableObjectRpcUnsupported(error)) throw error
564
+ }
565
+ }
566
+ const response = await stub.fetch(new Request('http://do/_rpc', {
567
+ method: 'POST',
568
+ headers: { 'Content-Type': 'application/json' },
569
+ body: JSON.stringify({ method: methodName, params: args })
570
+ }))
571
+ const result = await response.json()
572
+ if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
573
+ return result.result
574
+ }
575
+
576
+ function isDurableObjectRpcUnsupported(error) {
577
+ const message = error && error.message ? String(error.message) : String(error)
578
+ return message.indexOf('does not support RPC') !== -1
579
+ }
580
+
545
581
  /**
546
582
  * Execute an RPC method against the gateway's bindings.
547
583
  *
@@ -668,14 +704,7 @@ async function executeRpcMethod(method, params, env, _ctx) {
668
704
  const [, serializedId, methodName, args] = params
669
705
  const id = binding.idFromString(serializedId.hex)
670
706
  const stub = binding.get(id)
671
- const response = await stub.fetch(new Request('http://do/_rpc', {
672
- method: 'POST',
673
- headers: { 'Content-Type': 'application/json' },
674
- body: JSON.stringify({ method: methodName, params: args })
675
- }))
676
- const result = await response.json()
677
- if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
678
- return result.result
707
+ return callDurableObjectRpc(stub, methodName, Array.isArray(args) ? args : [])
679
708
  }
680
709
 
681
710
  // Service Bindings
@@ -902,6 +931,47 @@ async function handleBridgeJsonMessage(data, ws, env, ctx, wsProxies) {
902
931
  }
903
932
  }
904
933
 
934
+ // Pass-through DO WebSocket upgrade (bridge client -> gateway -> DO).
935
+ //
936
+ // The bridge client's openDoWebSocket() opens a REAL loopback WebSocket to this
937
+ // endpoint (/_devflare/do-ws?binding&id&u) instead of tunneling frames over the
938
+ // bridge socket. We resolve the DO by id and RETURN the DO's 101 response
939
+ // verbatim (webSocket included), so miniflare wires the inbound connection to
940
+ // the DO's client socket. That genuine inbound connection is what makes workerd
941
+ // dispatch the DO's hibernation handlers (webSocketMessage/webSocketClose) and
942
+ // deliver ctx.getWebSockets() broadcasts. Pumping the DO socket in-process (the
943
+ // legacy handleBridgeWsOpen path) never triggers hibernation, so two sockets to
944
+ // one getByName(id) could not see each other's messages. This mirrors the
945
+ // browser WS_ROUTES pass-through (handleDoWebSocket) and real Cloudflare.
946
+ //
947
+ // NOTE: the devflare/test gateway (src/test/simple-context-gateway-script.ts)
948
+ // keeps a byte-equivalent copy of this handler because it is a separately-bundled
949
+ // Worker template that cannot import this runtime — keep the two in sync.
950
+ async function handleBridgeDoWebSocket(request, env, url) {
951
+ try {
952
+ const bindingName = url.searchParams.get('binding')
953
+ const idHex = url.searchParams.get('id')
954
+ const targetUrl = url.searchParams.get('u') || url.toString()
955
+
956
+ const binding = env[bindingName]
957
+ if (!binding || typeof binding.idFromString !== 'function') {
958
+ return new Response('Durable Object binding not found: ' + bindingName, { status: 500 })
959
+ }
960
+ if (!idHex) {
961
+ return new Response('Missing Durable Object id', { status: 400 })
962
+ }
963
+
964
+ const stub = binding.get(binding.idFromString(idHex))
965
+ // Forward the real upgrade request (its headers carry any auth/cookies the
966
+ // caller passed to connect()) and pass the DO's 101 response straight back.
967
+ return stub.fetch(new Request(targetUrl, { method: 'GET', headers: request.headers }))
968
+ } catch (error) {
969
+ return new Response('Error opening DO WebSocket: ' + (error?.message || String(error)), {
970
+ status: 500
971
+ })
972
+ }
973
+ }
974
+
905
975
  function handleBridgeWebSocket(request, env, ctx) {
906
976
  const { 0: client, 1: server } = new WebSocketPair()
907
977
  server.accept()
@@ -1627,6 +1697,11 @@ export default {
1627
1697
  const isWebSocket = request.headers.get('Upgrade') === 'websocket'
1628
1698
 
1629
1699
  if (isWebSocket) {
1700
+ // Bridge DO connect(): a real pass-through upgrade to a Durable Object
1701
+ // (hibernation-correct), distinct from the bridge RPC/relay socket.
1702
+ if (url.pathname === '/_devflare/do-ws') {
1703
+ return handleBridgeDoWebSocket(request, env, url)
1704
+ }
1630
1705
  const matchedRoute = matchWsRoute(url.pathname)
1631
1706
  if (matchedRoute) {
1632
1707
  return handleDoWebSocket(request, env, url, matchedRoute)
@@ -1,4 +1,4 @@
1
- import { j as bridgeEnv, n as getContextOrNull } from "./context-CX50Y2Kb.js";
1
+ import { j as bridgeEnv, n as getContextOrNull } from "./context-B6S7cqQW.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-CX50Y2Kb.js";
1
+ import { D as createFetchEvent, b as createContextProxy, n as getContextOrNull, v as runWithEventContext } from "./context-B6S7cqQW.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-RekfD_2X.js";
5
+ import { _ as resolveFetchHandler, o as assertExplicit2ArgStyle } from "./runtime-sEr9b0ns.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";
@@ -1,5 +1,4 @@
1
- type WebSocketConstructor = new (url: string) => WebSocket;
2
- export declare function resolveBridgeWebSocketConstructor(runtimeWebSocket?: unknown): Promise<WebSocketConstructor>;
1
+ export { resolveBridgeWebSocketConstructor } from './websocket-constructor.js';
3
2
  export interface BridgeClientOptions {
4
3
  /** Bridge WebSocket URL (default: ws://localhost:8787) */
5
4
  url?: string;
@@ -85,7 +84,18 @@ export declare class BridgeClient {
85
84
  * surface is provided so consumers can register ahead of that work.
86
85
  */
87
86
  on(topic: string, cb: (data: unknown) => void): () => void;
88
- /** Create a proxied WebSocket to a Durable Object */
87
+ /**
88
+ * Create a proxied WebSocket to a Durable Object via the in-process bridge
89
+ * relay (`ws.open` control frame → gateway `stub.fetch()` pump → `WsData`
90
+ * frames).
91
+ *
92
+ * DEPRECATED for DO connect(): this relay pumps the DO's WebSocket in-process,
93
+ * which does NOT trigger workerd's hibernation dispatch — so a DO that uses
94
+ * `ctx.acceptWebSocket()` never sees `webSocketMessage`/`webSocketClose` and
95
+ * cross-socket `ctx.getWebSockets()` broadcasts never cross. `connect()` now
96
+ * uses `openDoWebSocket()` (a real pass-through) instead. This method is
97
+ * retained for the `ws-relay` wire capability and backward compatibility.
98
+ */
89
99
  createWsProxy(binding: string, id: string, url: string, headers?: [string, string][]): Promise<{
90
100
  wid: number;
91
101
  send: (data: Uint8Array | string) => void;
@@ -93,6 +103,40 @@ export declare class BridgeClient {
93
103
  onMessage: (handler: (data: Uint8Array | string) => void) => void;
94
104
  onClose: (handler: (code?: number, reason?: string) => void) => void;
95
105
  }>;
106
+ /**
107
+ * Open a REAL WebSocket to the gateway's DO pass-through endpoint
108
+ * (`/_devflare/do-ws`), which forwards the upgrade to the target Durable
109
+ * Object and passes through its `101` response.
110
+ *
111
+ * This is the ONLY way a bridged DO WebSocket triggers the runtime's
112
+ * hibernation dispatch under miniflare: the gateway returns the DO's client
113
+ * socket for a genuine inbound connection, so workerd pumps it and invokes
114
+ * `webSocketMessage`/`webSocketClose` and delivers `ctx.getWebSockets()`
115
+ * broadcasts. The legacy in-process relay (`createWsProxy` + gateway
116
+ * `stub.fetch()` pump) never fires hibernation, so two sockets to the same
117
+ * `getByName(id)` could not see each other's messages. Here they share ONE DO
118
+ * instance and broadcast across each other, exactly as on real Cloudflare.
119
+ *
120
+ * Unlike `createWsProxy`, this does not multiplex over the bridge socket: it
121
+ * is a standalone connection (see `openDurableObjectWebSocket`) with its own
122
+ * lifecycle, so it is unaffected by the bridge client's disconnect/reconnect
123
+ * bookkeeping. It also does not require the bridge socket to be connected.
124
+ *
125
+ * @param binding - DO namespace binding name (e.g. `DOC_ROOM`).
126
+ * @param idHex - Already-resolved Durable Object id, hex form.
127
+ * @param targetUrl - The DO fetch URL the caller passed to `stub.connect(url)`.
128
+ * @param headers - Optional upgrade headers (auth/cookies) forwarded to the DO.
129
+ * @returns A minimal proxy surface (send/close/onMessage/onClose) over the socket.
130
+ * @throws When the pass-through socket fails to open (bad binding/id, or the
131
+ * DO rejected the upgrade).
132
+ */
133
+ openDoWebSocket(binding: string, idHex: string, targetUrl: string, headers?: [string, string][]): Promise<{
134
+ wid: number;
135
+ send: (data: Uint8Array | string) => void;
136
+ close: (code?: number, reason?: string) => void;
137
+ onMessage: (handler: (data: Uint8Array | string) => void) => void;
138
+ onClose: (handler: (code?: number, reason?: string) => void) => void;
139
+ }>;
96
140
  /** Create a readable stream that pulls from the bridge */
97
141
  createReadableStream(sid: number): ReadableStream<Uint8Array>;
98
142
  private handleJsonMessage;
@@ -120,5 +164,4 @@ export declare class BridgeClient {
120
164
  export declare function getClient(options?: BridgeClientOptions): BridgeClient;
121
165
  /** Reset the default client (for testing) */
122
166
  export declare function resetClient(): void;
123
- export {};
124
167
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bridge/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bridge/client.ts"],"names":[],"mappings":"AA6CA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAA;AAkC3E,MAAM,WAAW,mBAAmB;IACnC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,4BAA4B;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACjC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA;CACtC;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,EAAE,+BAA+B,CAAC,UAAU,CAAC,CAAA;IACvD,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,0DAA0D;IAC1D,WAAW,EAAE;QACZ,OAAO,EAAE,MAAM,IAAI,CAAA;QACnB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KAC9B,GAAG,IAAI,CAAA;IACR,kEAAkE;IAClE,KAAK,EAAE,OAAO,CAAA;IACd,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,CAAA;IAC9C,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CAC9B;AAMD,qBAAa,YAAY;IACxB,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,GAAG,CAAQ;IACnB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,cAAc,CAAQ;IAE9B,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,cAAc,CAAkD;IAExE,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,WAAW,CAAQ;IAE3B,YAAY,OAAO,GAAE,mBAAwB,EAK5C;IAED,4BAA4B;IAC5B,MAAM,IAAI,MAAM,CAEf;IAED,6CAA6C;IAC7C,UAAU,IAAI,MAAM,CAGnB;IAMD,4BAA4B;IACtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAgB7B;YAEa,iCAAiC;IAK/C,OAAO,CAAC,cAAc;IA8DtB,iEAAiE;IACjE,UAAU,IAAI,IAAI,CAgBjB;IAED,6BAA6B;IAC7B,KAAK,IAAI,IAAI,CAEZ;IAED,yBAAyB;IACzB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,CAAC,gBAAgB;IAmBxB,gFAAgF;IAChF,OAAO,CAAC,cAAc;IAwCtB,yBAAyB;IACnB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,SAAS,SAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CA0CjF;IAMD;;;;;;;;;;OAUG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAazD;IAMD;;;;;;;;;;;OAWG;IACG,aAAa,CAClB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAC1B,OAAO,CAAC;QACV,GAAG,EAAE,MAAM,CAAA;QACX,IAAI,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,CAAA;QACzC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;QACjE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;KACpE,CAAC,CAqDD;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,eAAe,CACpB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAC1B,OAAO,CAAC;QACV,GAAG,EAAE,MAAM,CAAA;QACX,IAAI,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,CAAA;QACzC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;QACjE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;KACpE,CAAC,CAED;IAMD,0DAA0D;IAC1D,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAqE5D;IASD,OAAO,CAAC,iBAAiB;IA6BzB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,gBAAgB;YAQV,UAAU;IAmCxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;YAWR,eAAe;IAM7B,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,UAAU;CAOlB;AAQD,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAKrE;AAED,6CAA6C;AAC7C,wBAAgB,WAAW,IAAI,IAAI,CAGlC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Dispatch a single Durable Object RPC method call to its stub.
3
+ *
4
+ * A DO that `extends DurableObject` (from `cloudflare:workers`) exposes its
5
+ * methods as native RPC on the stub, so the method is invoked DIRECTLY as
6
+ * `stub[method](...args)` — exactly as on real Cloudflare. This is the ONLY
7
+ * correct path for a DO that ALSO defines its own `fetch()`: routing the call
8
+ * through `fetch()` would hand devflare's internal `_rpc` probe to the user
9
+ * handler, which may reject it (e.g. a websocket-only `fetch()` returning 426),
10
+ * yielding a non-JSON body that then fails to parse.
11
+ *
12
+ * The fetch `_rpc` convention is kept only as a fallback for DOs that are not
13
+ * RPC-enabled (see `callDurableObjectRpcViaFetch`).
14
+ *
15
+ * @param stub - The resolved Durable Object stub to dispatch against.
16
+ * @param methodName - The RPC method name requested by the client proxy.
17
+ * @param args - The already-deserialized positional arguments for the method.
18
+ * @returns The method's return value (or the `_rpc` fallback's result).
19
+ * @throws Re-throws any genuine error from the method; only a workerd
20
+ * "does not support RPC" signal is swallowed to trigger the fetch fallback.
21
+ */
22
+ export declare function callDurableObjectRpc(stub: DurableObjectStub, methodName: string, args: unknown[]): Promise<unknown>;
23
+ //# sourceMappingURL=do-rpc-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"do-rpc-dispatch.d.ts","sourceRoot":"","sources":["../../src/bridge/do-rpc-dispatch.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,oBAAoB,CACzC,IAAI,EAAE,iBAAiB,EACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EAAE,GACb,OAAO,CAAC,OAAO,CAAC,CAelB"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The minimal proxy surface over a DO pass-through socket, matching the shape
3
+ * consumed by the DO Socket facade in `proxy.ts` `connect()`.
4
+ */
5
+ export interface DoWebSocketProxy {
6
+ /** Synthetic id for parity with the legacy relay proxy (not on the wire). */
7
+ wid: number;
8
+ /** Send a frame to the DO (binary for bytes, text for strings). */
9
+ send: (data: Uint8Array | string) => void;
10
+ /** Close the socket. */
11
+ close: (code?: number, reason?: string) => void;
12
+ /** Attach the inbound-frame handler; buffered frames flush immediately. */
13
+ onMessage: (handler: (data: Uint8Array | string) => void) => void;
14
+ /** Attach the close handler; a buffered close fires immediately. */
15
+ onClose: (handler: (code?: number, reason?: string) => void) => void;
16
+ }
17
+ /**
18
+ * Open a real pass-through WebSocket to a Durable Object through the gateway's
19
+ * `/_devflare/do-ws` endpoint. Unlike the legacy relay, this is a standalone
20
+ * connection with its own lifecycle, so it is unaffected by the bridge client's
21
+ * `disconnect()`/reconnect bookkeeping.
22
+ *
23
+ * @param bridgeUrl - The bridge/gateway base URL (e.g. `ws://127.0.0.1:8787`).
24
+ * @param binding - DO namespace binding name (e.g. `DOC_ROOM`).
25
+ * @param idHex - Already-resolved Durable Object id, hex form.
26
+ * @param targetUrl - The DO fetch URL the caller passed to `stub.connect(url)`.
27
+ * @param headers - Optional upgrade headers (auth/cookies) forwarded to the DO.
28
+ * @returns A proxy surface (send/close/onMessage/onClose) over the socket.
29
+ * @throws When the pass-through socket fails to open (bad binding/id, or the DO
30
+ * rejected the upgrade).
31
+ */
32
+ export declare function openDurableObjectWebSocket(bridgeUrl: string, binding: string, idHex: string, targetUrl: string, headers?: [string, string][]): Promise<DoWebSocketProxy>;
33
+ //# sourceMappingURL=do-websocket-connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"do-websocket-connect.d.ts","sourceRoot":"","sources":["../../src/bridge/do-websocket-connect.ts"],"names":[],"mappings":"AAqEA;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAA;IACX,mEAAmE;IACnE,IAAI,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,CAAA;IACzC,wBAAwB;IACxB,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,2EAA2E;IAC3E,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;IACjE,oEAAoE;IACpE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAA;CACpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,0BAA0B,CAC/C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAyE3B"}
@@ -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/**\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\tconst response = await stub.fetch(new Request('http://do/_rpc', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ method: methodName, params: args })\n\t\t}))\n\t\tconst result = await response.json()\n\t\tif (!result.ok) throw new Error(result.error?.message || 'RPC failed')\n\t\treturn result.result\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\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\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";
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,+1pBAilB9B,CAAA"}
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 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/bridge/proxy.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,UAAU,CAAA;AAavD,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,qCAAqC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC9C;AAgoBD,mDAAmD;AACnD,MAAM,MAAM,WAAW,GACpB,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,OAAO,GACP,IAAI,GACJ,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,KAAK,CAAA;AAER,MAAM,WAAW,YAAY;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;CAC1B;AAKD;;GAEG;AACH,wBAAgB,cAAc,CAC7B,OAAO,GAAE,eAAe,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAO,GACtD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoFzB;AAwFD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAO5C,CAAA;AAEF;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAIzD"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/bridge/proxy.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,UAAU,CAAA;AAavD,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,qCAAqC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;CAC9C;AAooBD,mDAAmD;AACnD,MAAM,MAAM,WAAW,GACpB,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,OAAO,GACP,IAAI,GACJ,SAAS,GACT,WAAW,GACX,UAAU,GACV,QAAQ,GACR,KAAK,CAAA;AAER,MAAM,WAAW,YAAY;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;CAC1B;AAKD;;GAEG;AACH,wBAAgB,cAAc,CAC7B,OAAO,GAAE,eAAe,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAO,GACtD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoFzB;AAwFD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAO5C,CAAA;AAEF;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,eAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAIzD"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"AAuCA,MAAM,WAAW,UAAU;IAE1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB;;IAiBM,KAAK,UAAU,OAAO,OAAO,UAAU,OAAO,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;;;AA+LzF,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EAAE,EACjB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,gBAAgB,GACnB,OAAO,CAAC,OAAO,CAAC,CA+KlB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,UAAU;IAE1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB;;IAiBM,KAAK,UAAU,OAAO,OAAO,UAAU,OAAO,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;;;AA+LzF,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EAAE,EACjB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,gBAAgB,GACnB,OAAO,CAAC,OAAO,CAAC,CA+KlB"}
@@ -0,0 +1,38 @@
1
+ /** A WebSocket constructor callable as `new Ctor(url)`. */
2
+ export type WebSocketConstructor = new (url: string) => WebSocket;
3
+ /**
4
+ * Lazily import the `ws` package's WebSocket constructor (memoized). Used as the
5
+ * Node fallback and whenever custom upgrade headers are required — unlike the
6
+ * platform global, the `ws` constructor accepts an options object with headers.
7
+ *
8
+ * @returns The `ws` package WebSocket constructor.
9
+ * @throws When the `ws` package does not export a usable WebSocket implementation.
10
+ */
11
+ export declare function importWsPackageConstructor(): Promise<WebSocketConstructor>;
12
+ /**
13
+ * Return the runtime's global WebSocket constructor, or null when the runtime
14
+ * has none (older Node without a global WebSocket).
15
+ *
16
+ * @param runtimeWebSocket - The candidate global (defaults to `globalThis.WebSocket`).
17
+ * @returns The global constructor, or null.
18
+ */
19
+ export declare function getRuntimeWebSocketConstructor(runtimeWebSocket?: unknown): WebSocketConstructor | null;
20
+ /**
21
+ * Resolve a WebSocket constructor for the bridge socket: prefer the runtime
22
+ * global, fall back to the `ws` package.
23
+ *
24
+ * @param runtimeWebSocket - The candidate global (defaults to `globalThis.WebSocket`).
25
+ * @returns A usable WebSocket constructor.
26
+ */
27
+ export declare function resolveBridgeWebSocketConstructor(runtimeWebSocket?: unknown): Promise<WebSocketConstructor>;
28
+ /**
29
+ * Resolve the WebSocket constructor for a Durable Object pass-through
30
+ * connection. Custom upgrade headers require the `ws` package (whose constructor
31
+ * accepts an options object); the global WebSocket (undici/Bun) cannot set
32
+ * request headers, so it is used only when there are no headers to forward.
33
+ *
34
+ * @param hasHeaders - Whether application headers must ride the upgrade request.
35
+ * @returns A WebSocket constructor suitable for the connection.
36
+ */
37
+ export declare function resolveDoWebSocketConstructor(hasHeaders: boolean): Promise<WebSocketConstructor>;
38
+ //# sourceMappingURL=websocket-constructor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-constructor.d.ts","sourceRoot":"","sources":["../../src/bridge/websocket-constructor.ts"],"names":[],"mappings":"AAUA,2DAA2D;AAC3D,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,EAAE,MAAM,KAAK,SAAS,CAAA;AAIjE;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,oBAAoB,CAAC,CA4BhF;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC7C,gBAAgB,GAAE,OAA8B,GAC9C,oBAAoB,GAAG,IAAI,CAM7B;AAED;;;;;;GAMG;AACH,wBAAsB,iCAAiC,CACtD,gBAAgB,GAAE,OAA8B,GAC9C,OAAO,CAAC,oBAAoB,CAAC,CAK/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,6BAA6B,CAClD,UAAU,EAAE,OAAO,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAG/B"}
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-CX50Y2Kb.js";
5
- import { i as vars, r as env } from "./_chunks/env-S0_nMVz1.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";
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-BlVetiEh.js";
1
+ import { n as runCli, t as parseArgs } from "../_chunks/cli-sOZfXGua.js";
2
2
  export { parseArgs, runCli };
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-script.d.ts","sourceRoot":"","sources":["../../src/dev-server/gateway-script.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC/B,QAAQ,GAAE,aAAa,EAAO,EAC9B,KAAK,UAAQ,EACb,qBAAqB,GAAE,MAAM,GAAG,IAAW,GACzC,MAAM,CAuQR"}
1
+ {"version":3,"file":"gateway-script.d.ts","sourceRoot":"","sources":["../../src/dev-server/gateway-script.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC/B,QAAQ,GAAE,aAAa,EAAO,EAC9B,KAAK,UAAQ,EACb,qBAAqB,GAAE,MAAM,GAAG,IAAW,GACzC,MAAM,CA4QR"}
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-BlVetiEh.js";
8
- import { i as vars, r as env } from "./_chunks/env-S0_nMVz1.js";
7
+ import { n as runCli, t as parseArgs } from "./_chunks/cli-sOZfXGua.js";
8
+ import { i as vars, r as env } from "./_chunks/env-UqevBSTd.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-CX50Y2Kb.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";
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-RekfD_2X.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";
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-CX50Y2Kb.js";
3
+ import { D as createFetchEvent, I as getClient, M as createEnvProxy, P as setBindingHints, v as runWithEventContext } from "../_chunks/context-B6S7cqQW.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-CX50Y2Kb.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";
8
8
  import { createLocalSendEmailBinding, wrapEnvSendEmailBindings } from "../utils/send-email.js";
9
- import { n as __setTestContext, r as env, t as __clearTestContext } from "../_chunks/env-S0_nMVz1.js";
10
- import { _ as resolveFetchHandler, a as matchFetchRoute, m as invokeFetchModule, r as createRouteResolve } from "../_chunks/runtime-RekfD_2X.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";
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";
@@ -1617,6 +1617,39 @@ function __encodeTransport(value) {
1617
1617
  export default {
1618
1618
  async fetch(request, env) {
1619
1619
  if (request.headers.get('Upgrade') === 'websocket') {
1620
+ const wsUrl = new URL(request.url)
1621
+ // Bridge DO connect(): a real pass-through upgrade to a Durable Object.
1622
+ // The client's openDoWebSocket() opens a real loopback WS here rather
1623
+ // than tunneling frames over the RPC socket; we forward the upgrade to
1624
+ // the DO and pass its 101 response straight back so miniflare wires the
1625
+ // inbound connection to the DO's client socket. That genuine inbound
1626
+ // connection is what makes the runtime dispatch the DO's hibernation
1627
+ // handlers (webSocketMessage/webSocketClose) and deliver
1628
+ // ctx.getWebSockets() broadcasts — an in-process pump never does.
1629
+ //
1630
+ // This is a byte-equivalent copy of handleBridgeDoWebSocket in
1631
+ // src/bridge/gateway-runtime.ts (this gateway is a separately-bundled
1632
+ // Worker template that cannot import that runtime) — keep the two in sync.
1633
+ if (wsUrl.pathname === '/_devflare/do-ws') {
1634
+ try {
1635
+ const bindingName = wsUrl.searchParams.get('binding')
1636
+ const idHex = wsUrl.searchParams.get('id')
1637
+ const targetUrl = wsUrl.searchParams.get('u') || request.url
1638
+ const doBinding = env[bindingName]
1639
+ if (!doBinding || typeof doBinding.idFromString !== 'function') {
1640
+ return new Response('Durable Object binding not found: ' + bindingName, { status: 500 })
1641
+ }
1642
+ if (!idHex) {
1643
+ return new Response('Missing Durable Object id', { status: 400 })
1644
+ }
1645
+ const stub = doBinding.get(doBinding.idFromString(idHex))
1646
+ return stub.fetch(new Request(targetUrl, { method: 'GET', headers: request.headers }))
1647
+ } catch (error) {
1648
+ return new Response('Error opening DO WebSocket: ' + (error?.message || String(error)), {
1649
+ status: 500
1650
+ })
1651
+ }
1652
+ }
1620
1653
  const { 0: client, 1: server } = new WebSocketPair()
1621
1654
  server.accept()
1622
1655
  server.addEventListener('message', async (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"simple-context-gateway-script.d.ts","sourceRoot":"","sources":["../../src/test/simple-context-gateway-script.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,GAAE,MAAM,EAAO,GAClC,MAAM,CA0NR"}
1
+ {"version":3,"file":"simple-context-gateway-script.d.ts","sourceRoot":"","sources":["../../src/test/simple-context-gateway-script.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,GAAE,MAAM,EAAO,GAClC,MAAM,CA2PR"}
@@ -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-CtvgOi1c.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-CzqppgNp.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.60",
3
+ "version": "1.0.0-next.62",
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/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/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",
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",