UNPKG

miniflare

Version:

Fun, full-featured, fully-local simulator for Cloudflare Workers

175 lines (172 loc) • 6.4 kB
// src/workers/core/dev-registry-proxy.worker.ts import { WorkerEntrypoint } from "cloudflare:workers"; // src/workers/core/dev-registry-proxy-shared.worker.ts import { DurableObject } from "cloudflare:workers"; var registry = /* @__PURE__ */ new Map(); function setRegistry(data) { registry = new Map(Object.entries(data)); } function resolveTarget(service) { let entry = registry.get(service); if (!(!entry || !("debugPortAddress" in entry))) return entry; } function hasRegistryEntry(service) { return registry.has(service); } function workerNotFoundMessage(service) { return hasRegistryEntry(service) ? `Worker "${service}" is not compatible with this version of the dev server. Please update all Worker instances to the same version.` : `Worker "${service}" not found. Make sure it is running locally.`; } function connectToActor(debugPort, scriptName, className, actorId) { let target = resolveTarget(scriptName); return !target || !target.debugPortAddress ? null : debugPort.connect(target.debugPortAddress).getActor(target.userWorkerService, className, actorId); } function createProxyDurableObjectClass({ scriptName, className }) { return class extends DurableObject { _cachedFetcher; _cachedDebugPortAddress; // Lazily resolve and cache. Invalidates when debugPortAddress changes. _resolve() { let target = resolveTarget(scriptName); if (this._cachedFetcher && target?.debugPortAddress === this._cachedDebugPortAddress) return this._cachedFetcher; this._cachedFetcher = void 0, this._cachedDebugPortAddress = void 0; let fetcher = connectToActor( this.env.DEV_REGISTRY_DEBUG_PORT, scriptName, className, this.ctx.id.toString() ); return fetcher && target && (this._cachedFetcher = fetcher, this._cachedDebugPortAddress = target.debugPortAddress), fetcher; } constructor(ctx, env) { return super(ctx, env), new Proxy(this, { get(target, prop) { if (Reflect.has(target, prop)) return Reflect.get(target, prop); let fetcher = target._resolve(); return fetcher ? Reflect.get(fetcher, prop) : () => { throw new Error(workerNotFoundMessage(scriptName)); }; } }); } fetch(request) { let fetcher = this._resolve(); return fetcher ? fetcher.fetch(request) : Promise.resolve( new Response(workerNotFoundMessage(scriptName), { status: 503 }) ); } }; } var SERIALIZED_DATE = "___serialized_date___", SERIALIZED_BIGINT = "___serialized_bigint___"; function tailEventsReplacer(_, value) { return value instanceof Date ? { [SERIALIZED_DATE]: value.toISOString() } : typeof value == "bigint" ? { [SERIALIZED_BIGINT]: value.toString() } : value; } function tailEventsReviver(_, value) { if (value && typeof value == "object") { if (SERIALIZED_DATE in value) return new Date(value[SERIALIZED_DATE]); if (SERIALIZED_BIGINT in value) return BigInt(value[SERIALIZED_BIGINT]); } return value; } // src/workers/core/dev-registry-proxy.worker.ts var HANDLER_RESERVED_KEYS = /* @__PURE__ */ new Set([ "alarm", "connect", "self", "tail", "tailStream", "test", "trace", "webSocketClose", "webSocketError", "webSocketMessage" ]); function resolve(props, env) { let { service, entrypoint, userProps } = props, target = resolveTarget(service); if (!target || !target.debugPortAddress) return null; let serviceName = entrypoint === null || entrypoint === "default" ? target.defaultEntrypointService : target.userWorkerService; return env.DEV_REGISTRY_DEBUG_PORT.connect(target.debugPortAddress).getEntrypoint(serviceName, entrypoint ?? void 0, userProps); } var ExternalServiceProxy = class extends WorkerEntrypoint { _fetcher = null; _entryFetcher = null; constructor(ctx, env) { super(ctx, env), this._fetcher = resolve(ctx.props, env); let target = resolveTarget(ctx.props.service); if (target && target.debugPortAddress) { let client = env.DEV_REGISTRY_DEBUG_PORT.connect( target.debugPortAddress ); this._entryFetcher = client.getEntrypoint("core:entry"); } return new Proxy(this, { get(target2, prop) { if (Reflect.has(target2, prop)) return Reflect.get(target2, prop); if (!(typeof prop == "string" && HANDLER_RESERVED_KEYS.has(prop))) { if (!target2._fetcher) throw new Error(workerNotFoundMessage(ctx.props.service)); return Reflect.get(target2._fetcher, prop); } } }); } fetch(request) { return this._fetcher ? this._fetcher.fetch(request) : new Response(workerNotFoundMessage(this.ctx.props.service), { status: 503 }); } async scheduled(controller) { if (!this._entryFetcher) throw new Error(workerNotFoundMessage(this.ctx.props.service)); let params = new URLSearchParams(); controller.cron && params.set("cron", controller.cron), controller.scheduledTime && params.set("time", String(controller.scheduledTime)); let response = await this._entryFetcher.fetch( new Request(`http://localhost/cdn-cgi/handler/scheduled?${params}`, { headers: { "MF-Route-Override": this.ctx.props.service } }) ); if (!response.ok) { let body = await response.text(); throw new Error( `Scheduled handler returned HTTP ${response.status}: ${body}` ); } } // Forward tail events to the remote worker via RPC. // Events with rpcMethod==="tail" are filtered out to prevent infinite // recursion (the remote tail() call would itself produce a tail event). tail(events) { if (!this._fetcher) return; let filtered = events.filter( (e) => e.event?.rpcMethod !== "tail" ); if (filtered.length !== 0) try { let serializedEvents = JSON.parse( JSON.stringify(filtered, tailEventsReplacer), tailEventsReviver ); return this._fetcher.tail(serializedEvents); } catch (e) { console.warn( `[dev-registry] Failed to forward tail events to "${this.ctx.props.service}": ${e instanceof Error ? e.message : String(e)}` ); } } }; export { ExternalServiceProxy, createProxyDurableObjectClass, setRegistry }; //# sourceMappingURL=dev-registry-proxy.worker.js.map