summaryrefslogtreecommitdiff
path: root/node_modules/wrangler/templates/middleware/loader-sw.ts
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/wrangler/templates/middleware/loader-sw.ts')
-rw-r--r--node_modules/wrangler/templates/middleware/loader-sw.ts228
1 files changed, 228 insertions, 0 deletions
diff --git a/node_modules/wrangler/templates/middleware/loader-sw.ts b/node_modules/wrangler/templates/middleware/loader-sw.ts
new file mode 100644
index 0000000..9e465f9
--- /dev/null
+++ b/node_modules/wrangler/templates/middleware/loader-sw.ts
@@ -0,0 +1,228 @@
+import {
+ Awaitable,
+ Dispatcher,
+ IncomingRequest,
+ Middleware,
+ __facade_invoke__,
+ __facade_register__,
+ __facade_registerInternal__,
+} from "./common";
+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();
+}
+
+function __facade_isSpecialEvent__(
+ type: string
+): type is "fetch" | "scheduled" {
+ return type === "fetch" || type === "scheduled";
+}
+const __facade__originalAddEventListener__ = globalThis.addEventListener;
+const __facade__originalRemoveEventListener__ = globalThis.removeEventListener;
+const __facade__originalDispatchEvent__ = globalThis.dispatchEvent;
+
+globalThis.addEventListener = function (type, listener, options) {
+ if (__facade_isSpecialEvent__(type)) {
+ __FACADE_EVENT_TARGET__.addEventListener(
+ type,
+ listener as EventListenerOrEventListenerObject,
+ options
+ );
+ } else {
+ __facade__originalAddEventListener__(type, listener, options);
+ }
+};
+globalThis.removeEventListener = function (type, listener, options) {
+ if (__facade_isSpecialEvent__(type)) {
+ __FACADE_EVENT_TARGET__.removeEventListener(
+ type,
+ listener as EventListenerOrEventListenerObject,
+ options
+ );
+ } else {
+ __facade__originalRemoveEventListener__(type, listener, options);
+ }
+};
+globalThis.dispatchEvent = function (event) {
+ if (__facade_isSpecialEvent__(event.type)) {
+ return __FACADE_EVENT_TARGET__.dispatchEvent(event);
+ } else {
+ return __facade__originalDispatchEvent__(event);
+ }
+};
+
+declare global {
+ var addMiddleware: typeof __facade_register__;
+ var addMiddlewareInternal: typeof __facade_registerInternal__;
+}
+globalThis.addMiddleware = __facade_register__;
+globalThis.addMiddlewareInternal = __facade_registerInternal__;
+
+const __facade_waitUntil__ = Symbol("__facade_waitUntil__");
+const __facade_response__ = Symbol("__facade_response__");
+const __facade_dispatched__ = Symbol("__facade_dispatched__");
+
+class __Facade_ExtendableEvent__ extends Event {
+ [__facade_waitUntil__]: Awaitable<unknown>[] = [];
+
+ waitUntil(promise: Awaitable<any>) {
+ if (!(this instanceof __Facade_ExtendableEvent__)) {
+ throw new TypeError("Illegal invocation");
+ }
+ this[__facade_waitUntil__].push(promise);
+ }
+}
+
+interface FetchEventInit extends EventInit {
+ request: Request;
+ passThroughOnException: FetchEvent["passThroughOnException"];
+}
+
+class __Facade_FetchEvent__ extends __Facade_ExtendableEvent__ {
+ #request: Request;
+ #passThroughOnException: FetchEvent["passThroughOnException"];
+ [__facade_response__]?: Awaitable<Response>;
+ [__facade_dispatched__] = false;
+
+ constructor(type: "fetch", init: FetchEventInit) {
+ super(type);
+ this.#request = init.request;
+ this.#passThroughOnException = init.passThroughOnException;
+ }
+
+ get request() {
+ return this.#request;
+ }
+
+ respondWith(response: Awaitable<Response>) {
+ if (!(this instanceof __Facade_FetchEvent__)) {
+ throw new TypeError("Illegal invocation");
+ }
+ if (this[__facade_response__] !== undefined) {
+ throw new DOMException(
+ "FetchEvent.respondWith() has already been called; it can only be called once.",
+ "InvalidStateError"
+ );
+ }
+ if (this[__facade_dispatched__]) {
+ throw new DOMException(
+ "Too late to call FetchEvent.respondWith(). It must be called synchronously in the event handler.",
+ "InvalidStateError"
+ );
+ }
+ this.stopImmediatePropagation();
+ this[__facade_response__] = response;
+ }
+
+ passThroughOnException() {
+ if (!(this instanceof __Facade_FetchEvent__)) {
+ throw new TypeError("Illegal invocation");
+ }
+ // Need to call native method immediately in case uncaught error thrown
+ this.#passThroughOnException();
+ }
+}
+
+interface ScheduledEventInit extends EventInit {
+ scheduledTime: number;
+ cron: string;
+ noRetry: ScheduledEvent["noRetry"];
+}
+
+class __Facade_ScheduledEvent__ extends __Facade_ExtendableEvent__ {
+ #scheduledTime: number;
+ #cron: string;
+ #noRetry: ScheduledEvent["noRetry"];
+
+ constructor(type: "scheduled", init: ScheduledEventInit) {
+ super(type);
+ this.#scheduledTime = init.scheduledTime;
+ this.#cron = init.cron;
+ this.#noRetry = init.noRetry;
+ }
+
+ get scheduledTime() {
+ return this.#scheduledTime;
+ }
+
+ get cron() {
+ return this.#cron;
+ }
+
+ noRetry() {
+ if (!(this instanceof __Facade_ScheduledEvent__)) {
+ throw new TypeError("Illegal invocation");
+ }
+ // Need to call native method immediately in case uncaught error thrown
+ this.#noRetry();
+ }
+}
+
+__facade__originalAddEventListener__("fetch", (event) => {
+ const ctx: ExecutionContext = {
+ waitUntil: event.waitUntil.bind(event),
+ passThroughOnException: event.passThroughOnException.bind(event),
+ };
+
+ const __facade_sw_dispatch__: Dispatcher = function (type, init) {
+ if (type === "scheduled") {
+ const facadeEvent = new __Facade_ScheduledEvent__("scheduled", {
+ scheduledTime: Date.now(),
+ cron: init.cron ?? "",
+ noRetry() {},
+ });
+
+ __FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
+ event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
+ }
+ };
+
+ const __facade_sw_fetch__: Middleware = function (request, _env, ctx) {
+ const facadeEvent = new __Facade_FetchEvent__("fetch", {
+ request,
+ passThroughOnException: ctx.passThroughOnException,
+ });
+
+ __FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
+ facadeEvent[__facade_dispatched__] = true;
+ event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
+
+ const response = facadeEvent[__facade_response__];
+ if (response === undefined) {
+ throw new Error("No response!"); // TODO: proper error message
+ }
+ return response;
+ };
+
+ event.respondWith(
+ __facade_invoke__(
+ event.request as IncomingRequest,
+ globalThis,
+ ctx,
+ __facade_sw_dispatch__,
+ __facade_sw_fetch__
+ )
+ );
+});
+
+__facade__originalAddEventListener__("scheduled", (event) => {
+ const facadeEvent = new __Facade_ScheduledEvent__("scheduled", {
+ scheduledTime: event.scheduledTime,
+ cron: event.cron,
+ noRetry: event.noRetry.bind(event),
+ });
+
+ __FACADE_EVENT_TARGET__.dispatchEvent(facadeEvent);
+ event.waitUntil(Promise.all(facadeEvent[__facade_waitUntil__]));
+});