devflare 1.0.0-next.36 → 1.0.0-next.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LLM.md CHANGED
@@ -324,21 +324,21 @@ Every native binding or platform lane in the binding docs is listed here with it
324
324
  - **Services** — `ref()` service bindings, typed worker-to-worker env contracts, local multi-worker runtime, and tests that call the same service binding the app uses. ([link](/docs/bindings/services))
325
325
  - **AI** — Native config, generated types, deploy support, and AI Gateway method coverage are present. Real inference, model behavior, billing, and most meaningful tests remain Cloudflare remote behavior. ([link](/docs/bindings/ai))
326
326
  - **Vectorize** — Native config, generated types, preview-aware resource naming, and remote-mode tests are supported. Real index semantics and similarity results require Cloudflare. ([link](/docs/bindings/vectorize))
327
- - **Hyperdrive** — Config, name resolution, local connection strings, and Miniflare-backed Hyperdrive bindings support ordinary app queries without Cloudflare. Hosted pooling, placement, credentials, and production routing remain Cloudflare behavior. ([link](/docs/bindings/hyperdrive))
328
- - **Browser Rendering** — Native config, generated typing, route examples, and bridge-backed dev-server support through the local browser-rendering shim. Cloudflare still owns hosted session limits, live/HITL behavior, recordings, and billing. ([link](/docs/bindings/browser-rendering))
329
- - **Analytics Engine** — Dataset bindings are configured in Devflare, and app code can be thin-tested around `writeDataPoint()`. Production ingestion and analytics behavior remain hosted. ([link](/docs/bindings/analytics-engine))
327
+ - **Hyperdrive** — Config, name resolution, local connection strings, and Miniflare-backed Hyperdrive bindings support ordinary app queries through the populated connection fields. The raw socket `connect()` is the one named gap — the local shim throws there, and Miniflare establishes real Hyperdrive sockets only inside a `createTestContext()` run. Hosted pooling, placement, credentials, and production routing remain Cloudflare behavior. ([link](/docs/bindings/hyperdrive))
328
+ - **Browser Rendering** — Native config, generated typing, route examples, and bridge-backed dev-server support through the local browser-rendering shim. Live view, human-in-the-loop, recordings, and external CDP stay hosted, so the local contract is intentionally narrower than the deployed product. ([link](/docs/bindings/browser-rendering))
329
+ - **Analytics Engine** — Dataset bindings are configured in Devflare and compiled for deploy. Local delivery is not yet wired into the dev/test Miniflare config; Miniflare ships a native Analytics Engine plugin whose `writeDataPoint()` is a write-shape-only no-op (it records nothing), so it is not an inherent remote boundary — production ingestion and query behavior remain hosted. ([link](/docs/bindings/analytics-engine))
330
330
  - **Send Email** — Outbound email bindings have native config, local support, and test access through the env binding. Inbound email handlers are a separate Worker surface. ([link](/docs/bindings/send-email))
331
331
  - **Rate Limiting** — Native fixed-window config, Miniflare-backed local behavior, generated typing, and pure mocks support deterministic application-level rate-limit tests. ([link](/docs/bindings/rate-limiting))
332
332
  - **Version Metadata** — Native config, deterministic local metadata, and test helpers support version-aware responses and diagnostics without requiring Cloudflare state. ([link](/docs/bindings/version-metadata))
333
- - **Worker Loaders** — Devflare wires Worker Loader bindings through Miniflare and pure test stubs, so local apps can load explicit Worker payloads without Cloudflare. Upload, discovery, and hosted lifecycle stay on the platform. ([link](/docs/bindings/worker-loaders))
333
+ - **Worker Loaders** — Devflare wires Worker Loader `fetch` entrypoints through Miniflare and pure test stubs, so local apps can load explicit Worker payloads without Cloudflare. The named gap is `getDurableObjectClass()`: the local shim throws because that opaque facet-spawning reference only exists inside the runtime. Upload, discovery, and hosted lifecycle stay on the platform. ([link](/docs/bindings/worker-loaders))
334
334
  - **Secrets Store** — Native config, Miniflare wiring, and explicit local fixtures cover app code that reads Secrets Store values. Devflare still does not read, provision, or sync account secret values. ([link](/docs/bindings/secrets-store))
335
335
  - **AI Search** — Native instance and namespace config plus deterministic fixtures can test application flow. Crawling, indexing, ranking, and hosted model behavior stay in Cloudflare. ([link](/docs/bindings/ai-search))
336
336
  - **mTLS Certificates** — Native config and Fetcher-shaped local fixtures are supported. Real client-certificate presentation and certificate lifecycle remain Wrangler and Cloudflare remote behavior. ([link](/docs/bindings/mtls-certificates))
337
337
  - **Dispatch Namespaces** — Native dispatch namespace bindings and tenant Fetcher fixtures are supported. Devflare does not upload tenant Workers or emulate the Workers for Platforms control plane. ([link](/docs/bindings/dispatch-namespaces))
338
338
  - **Workflows** — Native config, Miniflare workflow bindings, deterministic mocks, and real WorkflowEntrypoint examples cover the local app loop. Production lifecycle, durability, retries, and scheduling remain Cloudflare-owned. ([link](/docs/bindings/workflows))
339
339
  - **Pipelines** — Native config and local send-recording tests are supported for producer code. Pipeline creation, batching, transformations, sinks, and delivery are Cloudflare-managed. ([link](/docs/bindings/pipelines))
340
- - **Images** — Native singleton config, Miniflare image bindings, persisted local state, and deterministic pure mocks cover Worker image transform flows. Hosted storage, variants, delivery rules, billing, and final transform fidelity remain remote. ([link](/docs/bindings/images))
341
- - **Media Transformations** — Native config, Miniflare media bindings, and deterministic pure mocks cover Worker media transform chains locally. Real codecs, output fidelity, duration handling, cache behavior, and billing remain hosted Cloudflare behavior. ([link](/docs/bindings/media-transformations))
340
+ - **Images** — Native singleton config, Miniflare image bindings, and a low-fidelity deterministic pure mock cover Worker `info()`/transform chain shapes. The named gap is the hosted storage API (`hosted.image()` / `.upload()` / `.list()`), which throws locally. Hosted storage, variants, delivery rules, billing, and final transform fidelity remain remote. ([link](/docs/bindings/images))
341
+ - **Media Transformations** — Native config, Miniflare media bindings, and a deterministic passthrough pure mock cover Worker media transform chain shapes locally. Real codecs, output fidelity, duration handling, cache behavior, and billing remain hosted Cloudflare behavior, so the local contract is narrower than the deployed product. ([link](/docs/bindings/media-transformations))
342
342
  - **Artifacts** — Native config and in-memory repo or token fixtures are supported for app flow. Durable storage, Git-over-HTTPS remotes, namespace creation, and permissions are Cloudflare-owned. ([link](/docs/bindings/artifacts))
343
343
  - **Containers** — Native top-level container config has full local support through Docker or Podman: Devflare can build Dockerfile paths offline-first, run prebuilt image tags, and interact with launched instances. Deployed rollout, registry availability, SSH, scaling, and hosted platform behavior remain Cloudflare-owned. ([link](/docs/bindings/containers))
344
344
 
@@ -10058,11 +10058,11 @@ export async function fetch(): Promise<Response> {
10058
10058
 
10059
10059
  #### Local and Remote Support
10060
10060
 
10061
- Devflare can run useful Hyperdrive application behavior locally for ordinary development and tests. Cloudflare still owns production limits, quotas, billing, and deployed account behavior.
10061
+ Devflare has a real lane for Hyperdrive, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
10062
10062
 
10063
- Full local support when Devflare has a local database connection string for the binding. Start locally with `createTestContext()` or `createOfflineEnv()` with `localConnectionString`; that lane should cover the normal Hyperdrive application flow without requiring a Cloudflare connection.
10063
+ Full local support when Devflare has a local database connection string for the binding. Use the documented local lane only for the behavior Devflare explicitly models, and keep the narrower boundary visible in code review.
10064
10064
 
10065
- Use Cloudflare when the assertion depends on deployed limits, account state, lifecycle behavior, billing, or other production-only Hyperdrive details.
10065
+ Use Cloudflare when the app depends on real preview isolation or actual postgres query behavior. This is the lane for full Hyperdrive product fidelity, remote state, lifecycle behavior, and platform-specific limits.
10066
10066
 
10067
10067
  #### When this binding fits best
10068
10068
 
@@ -10416,11 +10416,11 @@ export async function fetch(): Promise<Response> {
10416
10416
 
10417
10417
  #### Local and Remote Support
10418
10418
 
10419
- Devflare can run useful Browser Rendering application behavior locally for ordinary development and tests. Cloudflare still owns production limits, quotas, billing, and deployed account behavior.
10419
+ Devflare has a real lane for Browser Rendering, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
10420
10420
 
10421
10421
  Local support is the Devflare browser-rendering shim: the dev server starts a loopback-only browser bridge and binding worker that browser libraries can call during local development. Treat it as a practical local/dev path, then use Cloudflare for hosted Browser Rendering limits, session behavior, and product fidelity.
10422
10422
 
10423
- Use Cloudflare when the assertion depends on deployed limits, account state, lifecycle behavior, billing, or other production-only Browser Rendering details.
10423
+ Use Cloudflare when a real browser workflow is mission-critical or too heavy for ordinary test runs. This is the lane for full Browser Rendering product fidelity, remote state, lifecycle behavior, and platform-specific limits.
10424
10424
 
10425
10425
  #### When this binding fits best
10426
10426
 
@@ -10765,9 +10765,9 @@ export async function fetch(): Promise<Response> {
10765
10765
 
10766
10766
  #### Local and Remote Support
10767
10767
 
10768
- Devflare supports the config, generated env shape, docs, and local application-flow work, but full fidelity requires Cloudflare remote infrastructure. Use local shims or fixtures for code your app owns, then connect to Cloudflare when the product behavior is the assertion.
10768
+ Devflare has a real lane for Analytics Engine, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
10769
10769
 
10770
- Supported, but usually tested through integration or thin mocks. Keep local coverage focused on deterministic application flow through fixtures, mocks, shims, or Miniflare-backed wiring instead of pretending to reproduce Cloudflare-hosted product behavior.
10770
+ Supported, but usually tested through integration or thin mocks. Use the documented local lane only for the behavior Devflare explicitly models, and keep the narrower boundary visible in code review.
10771
10771
 
10772
10772
  Use Cloudflare when analytics delivery itself is a release-critical guarantee. This is the lane for full Analytics Engine product fidelity, remote state, lifecycle behavior, and platform-specific limits.
10773
10773
 
@@ -12152,11 +12152,11 @@ export async function fetch(request: Request): Promise<Response> {
12152
12152
 
12153
12153
  #### Local and Remote Support
12154
12154
 
12155
- Devflare can run useful Worker Loaders application behavior locally for ordinary development and tests. Cloudflare still owns production limits, quotas, billing, and deployed account behavior.
12155
+ Devflare has a real lane for Worker Loaders, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
12156
12156
 
12157
- Full local support through Miniflare Worker Loader bindings and explicit pure-test Worker stubs. Start locally with `createTestContext()` with explicit Worker payloads or a pure stub; that lane should cover the normal Worker Loaders application flow without requiring a Cloudflare connection.
12157
+ Full local support through Miniflare Worker Loader bindings and explicit pure-test Worker stubs. Use the documented local lane only for the behavior Devflare explicitly models, and keep the narrower boundary visible in code review.
12158
12158
 
12159
- Use Cloudflare when the assertion depends on deployed limits, account state, lifecycle behavior, billing, or other production-only Worker Loaders details.
12159
+ Use Cloudflare when the assertion depends on cloudflare-hosted product behavior rather than the app calling the binding correctly. This is the lane for full Worker Loaders product fidelity, remote state, lifecycle behavior, and platform-specific limits.
12160
12160
 
12161
12161
  #### When this binding fits best
12162
12162
 
@@ -14546,11 +14546,11 @@ export async function fetch(request: Request): Promise<Response> {
14546
14546
 
14547
14547
  #### Local and Remote Support
14548
14548
 
14549
- Devflare can run useful Images application behavior locally for ordinary development and tests. Cloudflare still owns production limits, quotas, billing, and deployed account behavior.
14549
+ Devflare has a real lane for Images, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
14550
14550
 
14551
- Full local support through Miniflare image bindings, persisted local state, and deterministic pure mocks. Start locally with `createTestContext()` or `createOfflineEnv()`; that lane should cover the normal Images application flow without requiring a Cloudflare connection.
14551
+ Full local support through Miniflare image bindings, persisted local state, and deterministic pure mocks. Use the documented local lane only for the behavior Devflare explicitly models, and keep the narrower boundary visible in code review.
14552
14552
 
14553
- Use Cloudflare when the assertion depends on deployed limits, account state, lifecycle behavior, billing, or other production-only Images details.
14553
+ Use Cloudflare when the assertion depends on cloudflare-hosted product behavior rather than the app calling the binding correctly. This is the lane for full Images product fidelity, remote state, lifecycle behavior, and platform-specific limits.
14554
14554
 
14555
14555
  #### When this binding fits best
14556
14556
 
@@ -14878,11 +14878,11 @@ export async function fetch(request: Request): Promise<Response> {
14878
14878
 
14879
14879
  #### Local and Remote Support
14880
14880
 
14881
- Devflare can run useful Media Transformations application behavior locally for ordinary development and tests. Cloudflare still owns production limits, quotas, billing, and deployed account behavior.
14881
+ Devflare has a real lane for Media Transformations, but the local contract is intentionally narrower than Cloudflare's hosted product. The docs call out the supported local path and the remote boundary separately.
14882
14882
 
14883
- Full local support through Miniflare media bindings and deterministic pure mocks for transform chains. Start locally with `createTestContext()` or `createOfflineEnv()` with media fixtures; that lane should cover the normal Media Transformations application flow without requiring a Cloudflare connection.
14883
+ Full local support through Miniflare media bindings and deterministic pure mocks for transform chains. Use the documented local lane only for the behavior Devflare explicitly models, and keep the narrower boundary visible in code review.
14884
14884
 
14885
- Use Cloudflare when the assertion depends on deployed limits, account state, lifecycle behavior, billing, or other production-only Media Transformations details.
14885
+ Use Cloudflare when the assertion depends on cloudflare-hosted product behavior rather than the app calling the binding correctly. This is the lane for full Media Transformations product fidelity, remote state, lifecycle behavior, and platform-specific limits.
14886
14886
 
14887
14887
  #### When this binding fits best
14888
14888
 
@@ -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\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\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 > 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 data = isText ? event.data : arrayBufferToBase64(event.data)\n\t\t\tws.send(JSON.stringify({ t: 'ws.data', wid: msg.wid, data, isText }))\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\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. Capabilities advertised by the gateway\n\t\t\t// are kept in sync with src/bridge/client.ts (BRIDGE_CLIENT_CAPABILITIES).\n\t\t\tws.send(JSON.stringify({\n\t\t\t\tt: 'welcome',\n\t\t\t\tprotocolVersion: 2,\n\t\t\t\tcapabilities: ['streams', '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}\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/**\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";
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,mpkBAghB9B,CAAA"}
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"}
package/dist/cli/index.js CHANGED
@@ -1315,7 +1315,7 @@ async function runInit(parsed, logger, options) {
1315
1315
  return runInitCommand(parsed, logger, options);
1316
1316
  }
1317
1317
  async function runDev(parsed, logger, options) {
1318
- const { runDevCommand } = await import("../dev-mkhjdwh7.js");
1318
+ const { runDevCommand } = await import("../dev-xz2qxw0m.js");
1319
1319
  return runDevCommand(parsed, logger, options);
1320
1320
  }
1321
1321
  async function runBuild(parsed, logger, options) {
@@ -665,6 +665,12 @@ function buildLocalBindingShimServiceConfig(config) {
665
665
  var GATEWAY_RUNTIME_JS = `
666
666
  const RAW_EMAIL = 'EmailMessage::raw'
667
667
 
668
+ // Inline body cap for proxied DO/service-fetch responses. Matches
669
+ // HTTP_TRANSFER_THRESHOLD in wire.ts (512 KB). A larger body would exceed
670
+ // workerd's ~1 MB WebSocket message limit once base64-encoded into the rpc.ok
671
+ // frame, so it is rejected with a clear error rather than silently truncated.
672
+ const HTTP_TRANSFER_THRESHOLD = 512 * 1024
673
+
668
674
  function arrayBufferToBase64(buffer) {
669
675
  const bytes = new Uint8Array(buffer)
670
676
  let binary = ''
@@ -679,6 +685,33 @@ function base64ToArrayBuffer(base64) {
679
685
  return bytes.buffer
680
686
  }
681
687
 
688
+ // Binary WsData frame codec (10-byte header: kind:u8, id:u32 LE, seq:u32 LE,
689
+ // flags:u8 + payload). MUST match wire.ts encodeBinaryFrame/decodeBinaryFrame so
690
+ // the bridge client and this gateway agree on the WS-data wire format
691
+ // (BinaryKind.WsData = 2, BinaryFlags.TEXT = 2).
692
+ function encodeWsDataFrame(wid, flags, payload) {
693
+ const frame = new Uint8Array(10 + payload.byteLength)
694
+ const view = new DataView(frame.buffer)
695
+ view.setUint8(0, 2)
696
+ view.setUint32(1, wid, true)
697
+ view.setUint32(5, 0, true)
698
+ view.setUint8(9, flags)
699
+ frame.set(payload, 10)
700
+ return frame
701
+ }
702
+
703
+ function decodeWsDataFrame(buffer) {
704
+ const bytes = new Uint8Array(buffer)
705
+ if (bytes.byteLength < 10) return null
706
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
707
+ return {
708
+ kind: view.getUint8(0),
709
+ id: view.getUint32(1, true),
710
+ flags: view.getUint8(9),
711
+ payload: bytes.subarray(10)
712
+ }
713
+ }
714
+
682
715
  function serializeR2Object(obj) {
683
716
  if (!obj) return null
684
717
  return {
@@ -730,6 +763,15 @@ async function serializeResponse(response) {
730
763
  let body = null
731
764
  if (response.body) {
732
765
  const bytes = await response.arrayBuffer()
766
+ if (bytes.byteLength > HTTP_TRANSFER_THRESHOLD) {
767
+ throw new Error(
768
+ '[devflare][bridge] Response body (' + bytes.byteLength + ' bytes) exceeds the '
769
+ + HTTP_TRANSFER_THRESHOLD + '-byte inline limit. DO and service-binding fetch responses '
770
+ + 'are delivered inline over the bridge WebSocket and cannot be streamed locally; keep '
771
+ + 'proxied response bodies under the limit (large R2 objects are exempt — read them through '
772
+ + 'the R2 binding, which uses the HTTP transfer side-channel).'
773
+ )
774
+ }
733
775
  if (bytes.byteLength > 0) {
734
776
  body = { type: 'bytes', data: arrayBufferToBase64(bytes) }
735
777
  }
@@ -1068,8 +1110,11 @@ async function handleBridgeWsOpen(msg, ws, env, wsProxies) {
1068
1110
 
1069
1111
  doWs.addEventListener('message', (event) => {
1070
1112
  const isText = typeof event.data === 'string'
1071
- const data = isText ? event.data : arrayBufferToBase64(event.data)
1072
- ws.send(JSON.stringify({ t: 'ws.data', wid: msg.wid, data, isText }))
1113
+ const payload = isText
1114
+ ? new TextEncoder().encode(event.data)
1115
+ : new Uint8Array(event.data)
1116
+ const flags = isText ? 2 : 0
1117
+ ws.send(encodeWsDataFrame(msg.wid, flags, payload))
1073
1118
  })
1074
1119
 
1075
1120
  doWs.addEventListener('close', (event) => {
@@ -1095,17 +1140,35 @@ function handleBridgeWsClose(msg, wsProxies) {
1095
1140
  }
1096
1141
  }
1097
1142
 
1143
+ // Relay an inbound binary WsData frame (client -> DO). Mirrors server.ts
1144
+ // handleBinaryMessage: decode the frame, look up the proxy by ws id, and forward
1145
+ // the payload to the DO socket honoring the TEXT flag.
1146
+ function handleBridgeBinaryMessage(buffer, wsProxies) {
1147
+ const frame = decodeWsDataFrame(buffer)
1148
+ if (!frame || frame.kind !== 2) return
1149
+ const proxy = wsProxies.get(frame.id)
1150
+ if (!proxy) return
1151
+ if ((frame.flags & 2) !== 0) {
1152
+ proxy.doWs.send(new TextDecoder().decode(frame.payload))
1153
+ } else {
1154
+ proxy.doWs.send(frame.payload)
1155
+ }
1156
+ }
1157
+
1098
1158
  async function handleBridgeJsonMessage(data, ws, env, ctx, wsProxies) {
1099
1159
  const msg = JSON.parse(data)
1100
1160
  switch (msg.t) {
1101
1161
  case 'hello':
1102
1162
  // v2 handshake — acknowledge with welcome echoing the negotiated
1103
- // capability intersection. Capabilities advertised by the gateway
1104
- // are kept in sync with src/bridge/client.ts (BRIDGE_CLIENT_CAPABILITIES).
1163
+ // capability intersection. The gateway advertises only what it
1164
+ // implements end-to-end: 'ws-relay' (binary WsData relay to/from DO
1165
+ // sockets) and 'http-transfer' (the R2 transfer HTTP side-channel).
1166
+ // 'streams' is intentionally NOT advertised — large DO/service-fetch
1167
+ // responses are inlined, not streamed, in this gateway.
1105
1168
  ws.send(JSON.stringify({
1106
1169
  t: 'welcome',
1107
1170
  protocolVersion: 2,
1108
- capabilities: ['streams', 'ws-relay', 'http-transfer']
1171
+ capabilities: ['ws-relay', 'http-transfer']
1109
1172
  .filter((c) => Array.isArray(msg.capabilities) && msg.capabilities.includes(c))
1110
1173
  .sort()
1111
1174
  }))
@@ -1134,6 +1197,8 @@ function handleBridgeWebSocket(request, env, ctx) {
1134
1197
  try {
1135
1198
  if (typeof event.data === 'string') {
1136
1199
  await handleBridgeJsonMessage(event.data, server, env, ctx, wsProxies)
1200
+ } else {
1201
+ handleBridgeBinaryMessage(event.data, wsProxies)
1137
1202
  }
1138
1203
  } catch (error) {
1139
1204
  console.error('[Gateway] Error:', error)
@@ -55,10 +55,26 @@ export declare function getOfflineSupportMatrix(): Record<string, OfflineSupport
55
55
  export declare function describeOfflineSupport(service: string): OfflineSupportEntry;
56
56
  /**
57
57
  * Builds a deterministic, pure-test env object from Devflare config.
58
+ *
59
+ * Covers the bindings that have a pure-offline simulator or fixture (rate
60
+ * limits, version metadata, hyperdrive, worker loaders, mTLS, dispatch
61
+ * namespaces, workflows, pipelines, images, media, artifacts, secrets store,
62
+ * AI Search). It does **not** create the core storage/wiring bindings
63
+ * (`kv`, `d1`, `r2`, `queues`, `durableObjects`, `services`) — those require a
64
+ * real Miniflare runtime, which only `createTestContext()` provides. When such a
65
+ * binding is present in config it is reported in the returned `missingFixtures`
66
+ * (its `env` entry is left unset rather than silently faked); use
67
+ * `createTestContext()` or a `createMock*()` helper for those bindings.
58
68
  */
59
69
  export declare function createOfflineBindings(config: OfflineConfig, fixtures?: OfflineBindingFixtures, options?: OfflineBindingOptions): OfflineBindingsResult;
60
70
  /**
61
71
  * Convenience wrapper for callers that only need the derived env object.
72
+ *
73
+ * Note: the core storage/wiring bindings (`kv`, `d1`, `r2`, `queues`,
74
+ * `durableObjects`, `services`) are **not** created here and will be
75
+ * `undefined` in the returned env — they need a real Miniflare runtime. Use
76
+ * `createTestContext()` or a `createMock*()` helper for them, or call
77
+ * `createOfflineBindings()` to inspect `missingFixtures` for the exact list.
62
78
  */
63
79
  export declare function createOfflineEnv(config: OfflineConfig, fixtures?: OfflineBindingFixtures, options?: OfflineBindingOptions): Record<string, unknown>;
64
80
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"offline-bindings.d.ts","sourceRoot":"","sources":["../../src/test/offline-bindings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"offline-bindings.d.ts","sourceRoot":"","sources":["../../src/test/offline-bindings.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devflare",
3
- "version": "1.0.0-next.36",
3
+ "version": "1.0.0-next.38",
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",
@@ -83,7 +83,7 @@
83
83
  "test:unit": "bun test tests/unit",
84
84
  "test:coverage": "bun test tests/unit --coverage",
85
85
  "test:integration:control": "bun test tests/integration/cli tests/integration/examples tests/integration/package-entry tests/integration/vite",
86
- "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/miniflare.test.ts --parallel=1 --max-concurrency=1 && bun test tests/integration/bridge/r2-transfer.test.ts --parallel=1 --max-concurrency=1",
86
+ "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",
87
87
  "test:integration:test-context": "bun test tests/integration/test-context --parallel=1 --max-concurrency=1",
88
88
  "test:integration:dev-server": "bun test tests/integration/dev-server --parallel=1 --max-concurrency=1",
89
89
  "test:watch": "bun test --watch",