import { Redis } from "ioredis"; import { createPinboard } from "../server.js"; import { pinboardEnv } from "../env.js"; import { IoredisKV } from "../node/redis.js"; import { MemoryRedis } from "../memory-redis.js"; import { logger } from "../logger.js"; export namespace PinboardDO { export type Stub = { fetch(request: Request): Promise }; export type Namespace = { idFromName(name: string): unknown; get(id: unknown): Stub; }; export type Env = { PINBOARD: Namespace } & pinboardEnv.Source; } /** * Durable Object hosting one pinboard node: the actor context owns the Redis * socket and the license/durability timers, which per-request worker contexts * cannot (workerd cancels cross-request I/O). */ export class PinboardDO { private readonly pinboard: createPinboard.Instance; constructor(_state: unknown, env: PinboardDO.Env) { const { redisUrl, options } = pinboardEnv(env); const redis = redisUrl === "memory:" ? new MemoryRedis() : new IoredisKV(new Redis(redisUrl)); this.pinboard = createPinboard({ ...options, redis, onDisplaced: () => { logger.error( { operation: "license" }, "displaced by a higher-epoch fleet; refusing to serve", ); }, }); this.pinboard.start(); } fetch(request: Request): Promise { return this.pinboard.fetch(request, { address: request.headers.get("cf-connecting-ip") ?? undefined, }); } } /** Workerd entry: every request rides to the singleton pinboard actor. */ export default { fetch: (request: Request, env: PinboardDO.Env): Promise => env.PINBOARD.get(env.PINBOARD.idFromName("pinboard")).fetch(request), };