fetchWorker: communicate with a worker over a fetch-like interface!

Example:

Create valimport { fetchWorker } from "https://esm.town/v/postpostscript/fetchWorker"; using worker = await fetchWorker({ url: "https://esm.town/v/postpostscript/fetchWorkerExample", handler: "handler", }); const res = await worker.fetch("/", { method: "POST", body: JSON.stringify({ test: 1, }), headers: { "Content-Type": "application/json", }, }); console.log(await res.json()); // { // method: "POST", // url: "https://7ae81ab0-04cf-485a-ae09-054c4d3be6b3.val.town/", // text: { test: 1 }, // headers: { "content-type": "application/json" } // }

Full Example

Options:

  • url (string, required): URL that the worker will fetch the handler from
  • handler (string, defaults to default): name of export that will execute the request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { readerFromStreamReader } from "https://deno.land/std@0.223.0/io/mod.ts";
import { httpClient } from "https://esm.town/v/pomdtr/http_client";
import { disposable } from "https://esm.town/v/postpostscript/disposable";
export const workerURL = new URL(import.meta.url);
workerURL.searchParams.set("worker", "1");
export async function fetchWorker(opts: {
url: string;
handler?: string;
}) {
const worker = disposable(
new Worker(workerURL.toString(), {
type: "module",
}),
(worker) => worker.terminate(),
);
// wait for worker setup to finish
const { resolve, promise } = Promise.withResolvers();
worker.onmessage = resolve;
worker.postMessage({
code: "setup",
...opts,
});
await promise;
const resolvers: {
[id: string]: (value: Response) => void;
} = {};
worker.onmessage = (e) => {
const {
id,
response: {
status,
headers,
body,
},
} = e.data;
const response = new Response(body, {
status,
headers,
});
resolvers[id](response);
};
return {
fetch: async (input: URL | RequestInfo, init?: RequestInit) => {
const req = new Request(input, init);
const body: ArrayBuffer | null = req.body
? await req.blob().then(blob => blob.arrayBuffer())
: null;
const id = crypto.randomUUID();
const { resolve, promise } = Promise.withResolvers<Response>();
resolvers[id] = resolve;
worker.postMessage({
code: "request",
id,
request: {
url: req.url,
body,
init: {
method: req.method,
headers: Object.fromEntries(req.headers.entries()),
},
},
});
return promise;
},
instance: worker,
terminate: () => worker.terminate(),
[Symbol.dispose]: () => worker.terminate(),
};
}
export function setupWorker() {
let handler: (req: Request) => Promise<Response>;
self.onmessage = async (e) => {
if (e.data.code === "setup") {
const module = await import(e.data.url);
handler = module[e.data.handler ?? "default"];
self.postMessage({
code: "ready",
});
}
if (e.data.code === "request") {
const {
id,
1
2
3
4
5
6
7
8
9
10
11
12
13
import { httpClient } from "https://esm.town/v/pomdtr/http_client";
export default httpClient((req) => {
return new Response("Hello World!");
}, {
bookmarks: [
{
"label": "Dummy Request",
"request": new Request("https://dummyjson.com/products"),
},
],
path: "/",
});
1
Next