Skip to content

Commit

Permalink
requestMethod router example
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiancook committed Dec 3, 2023
1 parent 2b18fc5 commit 535a5e6
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 170 deletions.
1 change: 0 additions & 1 deletion src/events/schedule/dispatch-scheduled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ export async function dispatchScheduledDurableEvents(options: BackgroundSchedule
undefined;
// TODO detect if this event tries to dispatch again
try {
let isMainThread = true;
if (event.serviceWorkerId) {
if (isMainThread) {
const { dispatchServiceWorkerEvent } = await import("../../worker/service-worker/execute");
Expand Down
21 changes: 13 additions & 8 deletions src/fetch/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {DurableEventData, DurableRequest, DurableRequestData} from "../data";
import {on} from "../events";
import {isLike, ok} from "../is";
import {FetchRespondWith} from "./dispatch";
import {isMainThread} from "node:worker_threads";

export const FETCH = "fetch" as const;
type ScheduleFetchEventType = typeof FETCH;
Expand Down Expand Up @@ -40,11 +41,15 @@ function isFetchEvent(event: unknown): event is FetchEvent {
)
}

export const removeFetchScheduledFunction = on(FETCH, async (event) => {
ok(isFetchEvent(event));
event.respondWith(
fetch(
event.request
)
);
});
export let removeFetchScheduledFunction = () => {};

if (isMainThread) {
removeFetchScheduledFunction = on(FETCH, async (event) => {
ok(isFetchEvent(event));
event.respondWith(
fetch(
event.request
)
);
});
}
13 changes: 4 additions & 9 deletions src/tests/worker/service-worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ export {};
const pathname = fileURLToPath(import.meta.url);
const worker = join(dirname(pathname), "./worker.js");

async function waitForServiceWorker(registration: DurableServiceWorkerRegistration) {
if (registration.active) {
return registration.active;
}
await new Promise(resolve => setTimeout(resolve, 500));
await registration.update();
return waitForServiceWorker(registration);
}



{
Expand Down Expand Up @@ -95,4 +88,6 @@ async function waitForServiceWorker(registration: DurableServiceWorkerRegistrati
console.log(response.status, routes);
ok(response.ok);

}
}

await import("./routes.test");
91 changes: 91 additions & 0 deletions src/tests/worker/service-worker/routes.example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {FetchEvent} from "../../../fetch";
import {isRouteMatchCondition, RouterRule} from "../../../worker/service-worker/router";
import {DurableServiceWorkerScope} from "../../../worker/service-worker/types";
import {URLPattern} from "urlpattern-polyfill";

export interface URLPatternInit {
baseURL?: string;
username?: string;
password?: string;
protocol?: string;
hostname?: string;
port?: string;
pathname?: string;
search?: string;
hash?: string;
}

declare var self: DurableServiceWorkerScope;

export type RouterRequestMethodLower = "get" | "put" | "post" | "delete" | "options" | "patch"

export interface OnRequestFn {
(request: Request, event: FetchEvent): Response | undefined | void | Promise<Response | undefined | void>
}

export interface AddRouteAndHandlerFn {
(pathnameOrInit: string | URLPatternInit, onRequest: OnRequestFn): void
}

export const requestMethod = makeRequestMethod();

function makeRequestMethod(): Record<RouterRequestMethodLower, AddRouteAndHandlerFn> {
return {
get: makeAddRequestMethodRouteAndHandler("get"),
patch: makeAddRequestMethodRouteAndHandler("patch"),
put: makeAddRequestMethodRouteAndHandler("put"),
post: makeAddRequestMethodRouteAndHandler("post"),
delete: makeAddRequestMethodRouteAndHandler("delete"),
options: makeAddRequestMethodRouteAndHandler("options")
}
}

function makeAddRequestMethodRouteAndHandler(requestMethod: RouterRequestMethodLower): AddRouteAndHandlerFn {
return function addRequestRouteAndHandler(
pathnameOrInit: string | URLPatternInit,
onRequest: OnRequestFn) {
return addRequestMethodRouteAndHandler(
requestMethod,
pathnameOrInit,
onRequest
)
}
}

function addRequestMethodRouteAndHandler(
requestMethod: string,
pathnameOrInit: string | URLPatternInit,
onRequest: OnRequestFn
) {
const urlPattern = new URLPattern(
typeof pathnameOrInit === "string" ? {
pathname: pathnameOrInit
} : pathnameOrInit
);
const rule: RouterRule = {
condition: [
{
requestMethod
},
{
urlPattern
}
],
source: "fetch-event"
}
self.addEventListener("install", event => {
event.addRoutes(rule)
});
self.addEventListener("fetch", event => {
if (isRouteMatchCondition(self.registration, rule, event.request)) {
event.waitUntil(intercept());
}

async function intercept() {
const response = await onRequest(event.request, event);
if (response) {
event.respondWith(response);
}
}
});
}
53 changes: 53 additions & 0 deletions src/tests/worker/service-worker/routes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {serviceWorker} from "../../../worker";
import {createRouter} from "../../../worker/service-worker/router";
import {ok} from "../../../is";
import {fileURLToPath} from "node:url";
import {dirname, join} from "node:path";
import {v4} from "uuid";
import {waitForServiceWorker} from "./wait";

export {};

const pathname = fileURLToPath(import.meta.url);
const worker = join(dirname(pathname), "./routes.worker.js");

{
const registration = await serviceWorker.register(worker);

const fetch = await createRouter([
registration
]);

// wait for activated before starting to route
// note we can create the router earlier than installing, though register needs to be done ahead of time
await waitForServiceWorker(registration);

{
const response = await fetch("/test");

console.log(response.status);
ok(response.ok);

console.log(await response.text());
}

{
const body = `SOMETHING RANDOM: ${v4()}`

const response = await fetch("/test", {
method: "PUT",
body
});

console.log(response.status);
ok(response.ok);

const text = await response.text();

console.log(text);
ok(text === body, "Expected returned body to match");
}



}
13 changes: 13 additions & 0 deletions src/tests/worker/service-worker/routes.worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {requestMethod} from "./routes.example";

requestMethod.get("/test", () => {
console.log("In get handler");
return new Response("Hello from test get handler");
})

requestMethod.put("/test", (request) => {
console.log("In put handler");
return new Response(request.body, {
headers: request.headers
});
})
18 changes: 18 additions & 0 deletions src/tests/worker/service-worker/wait.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {DurableServiceWorkerRegistration} from "../../../worker";
import {dispatchEvent} from "../../../events";

export async function waitForServiceWorker(registration: DurableServiceWorkerRegistration) {
if (registration.active) {
return registration.active;
}
// Send a push event to the worker to ensure it is activated
await dispatchEvent({
type: "push",
serviceWorkerId: registration.durable.serviceWorkerId,
schedule: {
immediate: true
}
});
await registration.update();
return waitForServiceWorker(registration);
}
1 change: 1 addition & 0 deletions src/tests/worker/service-worker/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ self.addEventListener("activate", event => {
})

self.addEventListener("fetch", event => {
console.log("In fetch handler!");
event.respondWith(onFetchEvent(event));
});

Expand Down
6 changes: 5 additions & 1 deletion src/worker/service-worker/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ServiceWorkerWorkerData} from "./worker";
import {createRespondWith, DurableFetchEventData, isDurableFetchEventData} from "../../fetch";
import {dispatchEvent} from "../../events";
import {ok} from "../../is";
import {dispatchScheduledDurableEvents} from "../../events/schedule/dispatch-scheduled";

export interface FetchResponseMessage {
type: "fetch:response";
Expand All @@ -18,8 +19,11 @@ export interface FetchResponseMessage {
export async function dispatchWorkerEvent(event: DurableEventData, context: ServiceWorkerWorkerData) {
if (isDurableFetchEventData(event)) {
return dispatchWorkerFetchEvent(event, context);
} else {
return dispatchScheduledDurableEvents({
event
})
}
return dispatchEvent(event);
}

export async function dispatchWorkerFetchEvent(event: DurableFetchEventData, context: ServiceWorkerWorkerData) {
Expand Down
7 changes: 6 additions & 1 deletion src/worker/service-worker/execute-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ export function createServiceWorkerFetch(registration: DurableServiceWorkerRegis
if (input instanceof Request) {
request = input
} else if (init?.body) {
request = new Request(input, init);
request = new Request(
typeof input === "string" ?
new URL(input, getOrigin()).toString() :
input,
init
);
} else {
request = {
url: new URL(input, getOrigin()).toString(),
Expand Down
Loading

0 comments on commit 535a5e6

Please sign in to comment.