Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/dependabot-update-15633.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"miniflare": patch
"wrangler": patch
---

Update dependencies of "miniflare", "wrangler"

The following dependency versions have been updated:

| Dependency | From | To |
| ------------------------- | ------------- | ------------- |
| @cloudflare/workers-types | ^5.20260911.1 | ^5.20260915.1 |
Comment thread
NuroDev marked this conversation as resolved.
| workerd | 1.20260911.1 | 1.20260915.1 |
7 changes: 7 additions & 0 deletions .changeset/tidy-event-listener-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Preserve service-worker middleware error propagation with spec-compliant event dispatch

Wrangler's synthetic service-worker events now propagate listener exceptions to middleware without changing the behavior of user-created `EventTarget` instances.
15 changes: 2 additions & 13 deletions packages/miniflare/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4347,12 +4347,7 @@ test("Miniflare: CF-Connecting-IP is injected", async ({ expect }) => {
useDispose(mf);

const ip = await mf.dispatchFetch("http://example.com/");
// Tracked in https://github.com/cloudflare/workerd/issues/3310
if (!isWindows) {
expect(await ip.text()).toEqual("127.0.0.1");
} else {
expect(await ip.text()).toEqual("");
}
expect(await ip.text()).toEqual("127.0.0.1");
});

test("Miniflare: CF-Connecting-IP is injected (ipv6)", async ({ expect }) => {
Expand All @@ -4377,13 +4372,7 @@ test("Miniflare: CF-Connecting-IP is injected (ipv6)", async ({ expect }) => {
useDispose(mf);

const ip = await mf.dispatchFetch("http://example.com/");

// Tracked in https://github.com/cloudflare/workerd/issues/3310
if (!isWindows) {
expect(await ip.text()).toEqual("::1");
} else {
expect(await ip.text()).toEqual("");
}
expect(await ip.text()).toEqual("::1");
});

test("Miniflare: CF-Connecting-IP is preserved when present", async ({
Expand Down
11 changes: 8 additions & 3 deletions packages/miniflare/test/plugins/browser/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1060,9 +1060,14 @@ export default {
});
useDispose(mf);

const { resultValue } = (await mf
.dispatchFetch("https://localhost")
.then((r) => r.json())) as any;
const response = await mf.dispatchFetch("https://localhost");
const responseText = await response.text();
if (!response.ok) {
throw new Error(responseText);
}
const { resultValue } = JSON.parse(responseText) as {
resultValue: number;
};

expect(resultValue).toBe(2);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/workers-utils/src/default-compat-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ import type { CompatDate } from "./compatibility-date";
* @see https://github.com/cloudflare/workerd/blob/main/src/workerd/io/BUILD.bazel
* @see https://github.com/cloudflare/workerd/blob/main/src/workerd/io/compatibility-date.c%2B%2B
*/
export const DEFAULT_COMPAT_DATE: CompatDate = "2026-09-11";
export const DEFAULT_COMPAT_DATE: CompatDate = "2026-09-15";
4 changes: 4 additions & 0 deletions packages/wrangler/src/__tests__/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,15 @@ describe("middleware", () => {
addEventListener("scheduled", (event) => {
throw new Error("Error in scheduled worker");
});
addEventListener("scheduled", (event) => {
throw new Error("Unexpected second scheduled listener");
});
`;

fs.writeFileSync("index.js", scriptContent);

const worker = await startWorker({
compatibilityDate: "2026-09-15",
entrypoint: "index.js",
dev: {
server: { hostname: "127.0.0.1", port: 0 },
Expand Down
70 changes: 52 additions & 18 deletions packages/wrangler/templates/middleware/loader-sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,48 @@ import {

export { __facade_register__, __facade_registerInternal__ };

// Miniflare 2's `EventTarget` follows the spec and doesn't allow exceptions to
// be caught by `dispatchEvent`. Instead it has a custom `ThrowingEventTarget`
// class that rethrows errors from event listeners in `dispatchEvent`.
// We'd like errors to be propagated to the top-level `addEventListener`, so
// we'd like to use `ThrowingEventTarget`. Unfortunately, `ThrowingEventTarget`
// isn't exposed on the global scope, but `WorkerGlobalScope` (which extends
// `ThrowingEventTarget`) is. Therefore, we get at it in this nasty way.
let __FACADE_EVENT_TARGET__: EventTarget;
if ((globalThis as any).MINIFLARE) {
__FACADE_EVENT_TARGET__ = new (Object.getPrototypeOf(WorkerGlobalScope))();
} else {
__FACADE_EVENT_TARGET__ = new EventTarget();
const __FACADE_EVENT_TARGET__ = new EventTarget();
const __facade_eventListenerErrors__ = new WeakMap<Event, unknown>();
const __facade_eventListenerWrappers__ = new WeakMap<
EventListenerOrEventListenerObject,
EventListener
>();
Comment thread
NuroDev marked this conversation as resolved.

// Facade events model runtime event delivery, so listener exceptions must
// propagate even when EventTarget uses spec-compliant report-and-continue semantics.
function __facade_getEventListenerWrapper__(
listener: EventListenerOrEventListenerObject
): EventListener {
const existingWrapper = __facade_eventListenerWrappers__.get(listener);
if (existingWrapper !== undefined) {
return existingWrapper;
}

const wrapper: EventListener = function (this: EventTarget, event) {
try {
if (typeof listener === "function") {
listener.call(this, event);
} else {
listener.handleEvent(event);
}
} catch (error) {
__facade_eventListenerErrors__.set(event, error);
event.stopImmediatePropagation();
}
};
__facade_eventListenerWrappers__.set(listener, wrapper);
return wrapper;
}

function __facade_dispatchEvent__(event: Event): boolean {
const result = __FACADE_EVENT_TARGET__.dispatchEvent(event);
if (!__facade_eventListenerErrors__.has(event)) {
return result;
}

const error = __facade_eventListenerErrors__.get(event);
__facade_eventListenerErrors__.delete(event);
throw error;
}

function __facade_isSpecialEvent__(
Expand All @@ -37,7 +67,9 @@ globalThis.addEventListener = function (type, listener, options) {
if (__facade_isSpecialEvent__(type)) {
__FACADE_EVENT_TARGET__.addEventListener(
type,
listener as EventListenerOrEventListenerObject,
__facade_getEventListenerWrapper__(
listener as EventListenerOrEventListenerObject
),
Comment thread
NuroDev marked this conversation as resolved.
options
);
} else {
Expand All @@ -48,7 +80,9 @@ globalThis.removeEventListener = function (type, listener, options) {
if (__facade_isSpecialEvent__(type)) {
__FACADE_EVENT_TARGET__.removeEventListener(
type,
listener as EventListenerOrEventListenerObject,
__facade_getEventListenerWrapper__(
listener as EventListenerOrEventListenerObject
),
options
);
} else {
Expand All @@ -57,7 +91,7 @@ globalThis.removeEventListener = function (type, listener, options) {
};
globalThis.dispatchEvent = function (event) {
if (__facade_isSpecialEvent__(event.type)) {
return __FACADE_EVENT_TARGET__.dispatchEvent(event);
return __facade_dispatchEvent__(event);
} else {
return __facade__originalDispatchEvent__(event);
}
Expand Down Expand Up @@ -184,7 +218,7 @@ __facade__originalAddEventListener__("fetch", (event) => {
noRetry() {},
});

__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
__facade_dispatchEvent__(facadeEvent);
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
}
};
Expand All @@ -195,7 +229,7 @@ __facade__originalAddEventListener__("fetch", (event) => {
passThroughOnException: ctx.passThroughOnException,
});

__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
__facade_dispatchEvent__(facadeEvent);
facadeEvent[__facade_dispatched__] = true;
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));

Expand Down Expand Up @@ -224,6 +258,6 @@ __facade__originalAddEventListener__("scheduled", (event) => {
noRetry: event.noRetry.bind(event),
});

__FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
__facade_dispatchEvent__(facadeEvent);
event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
});
Loading
Loading