Skip to content

Instantly share code, notes, and snippets.

@jacob-ebey
Created June 20, 2024 21:27
Show Gist options
  • Save jacob-ebey/ac2bf225c46b2b8fbb49ed4a1350c0b5 to your computer and use it in GitHub Desktop.
Save jacob-ebey/ac2bf225c46b2b8fbb49ed4a1350c0b5 to your computer and use it in GitHub Desktop.
Workerd Durable Object Vite Proxy POC
import { DurableObject } from "cloudflare:workers";
class TestDO extends DurableObject {
exp: string;
constructor(ctx: DurableObjectState, env: any) {
super(ctx, env);
this.exp = "exp";
}
sayHello() {
return "Hello, " + this.exp + "!";
}
}
class TestDO2 extends DurableObject {
exp: string;
constructor(ctx: DurableObjectState, env: any) {
super(ctx, env);
this.exp = "exp2";
}
sayHello() {
return "Hello, " + this.exp + "!";
}
}
let ClassToGet = TestDO;
async function getClass() {
return ClassToGet;
}
let InstanceClass: any = await getClass();
export class ViteDO extends DurableObject {
base!: string;
_instance: DurableObject;
constructor(ctx: DurableObjectState, env: any) {
super(ctx, env);
const proxyKeys = new Set(
Object.getOwnPropertyNames(InstanceClass.prototype)
);
proxyKeys.delete("constructor");
proxyKeys.delete("fetch");
proxyKeys.delete("_viteLogic");
InstanceClass.prototype._viteLogic = this._viteLogic;
this._instance = Reflect.construct(InstanceClass, [ctx, env]) as ViteDO;
// Trick Workerd's RPC runtime into thinking this class has the available
// methods of the DurableObject we are proxying, and always proxy the latest
// instance version.
// https://github.com/cloudflare/workerd/blob/e791667ae24c2d275b89ab0845614de9dbef9d7f/src/workerd/api/worker-rpc.c%2B%2B#L1185
return new Proxy(this._instance, {
get: (target, prop, receiver) => {
if (proxyKeys.has(prop as string)) {
const self = this;
return async function () {
const instance = await self.getInstance(self.ctx, self.env);
return ((instance as any)[prop as string] as Function).apply(
instance,
arguments
);
};
}
return Reflect.get(target, prop, receiver);
},
}) as ViteDO;
}
async getInstance(ctx: DurableObjectState, env: any) {
const Class = await getClass();
if (!this._instance || Class !== InstanceClass) {
InstanceClass = Class;
this._instance = Reflect.construct(InstanceClass, [
ctx,
env,
]) as DurableObject;
}
return this._instance;
}
async fetch(request: Request) {
const instance = await this.getInstance(this.ctx, this.env);
const url = new URL(request.url);
if (url.pathname === "/__viteInitLogic") {
return new Response("OK");
}
if (!instance.fetch) {
return new Response("Durable Object does not implement fetch.");
}
return instance.fetch(request);
}
async _viteLogic() {
ClassToGet = TestDO2;
return "Hello, vite!";
}
}
export default {
async fetch(request, env) {
const stub = env.VITE.get(env.VITE.idFromName("test"));
const res = await stub.sayHello();
await stub._viteLogic();
return new Response(res);
},
} satisfies ExportedHandler<Env>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment