/** * `createWorkerEntry` — the shared worker `fetch` plumbing every Astrale domain * worker (and the cloudflare adapter's codegen) needs, so it lives in ONE place * instead of being copy-pasted per worker: * * • resolve the serving URL (== `iss`), canonicalize it (so the value matches * what `createRemoteServer` signs with and the kernel pins), and cache the * built app per distinct URL; * • optional subrequest routing: a `globalThis.fetch` override (installed ONLY * when `selfBinding` and/or `routeSubrequest` is configured) redirects * certain outbound fetches through a caller-supplied binding — `selfBinding` * for same-host fetches (a Worker can't fetch its own hostname), and the * vendor-neutral `routeSubrequest` for any caller policy (e.g. instance * hostnames a same-zone Worker→Worker fetch would 522 on). The SDK names no * backend or topology; the caller owns the predicate + the fetcher; * • optional SPA hook (e.g. `/ui/*` served from an `ASSETS` binding). * * The worker's OWN JWKS (`/.well-known/jwks.json`) is served as a normal * route by `createRemoteServer`; the verifier resolves a self-issued credential * from the in-memory key (see `auth/verify.ts`), so no self-fetch shim is needed. * * The worker file is then just schema + methods + a `build(url, env)` callback. */ import type { ExecutionContext } from 'hono'; import type { RemoteServerConfig } from './config.js'; export type Fetcher = { fetch(request: Request): Response | Promise; }; export type WorkerApp = { fetch(request: Request, env?: unknown, executionCtx?: ExecutionContext): Response | Promise; }; /** A built app cached by its serving URL: `origin` for same-origin matching, * `app` for in-process self-dispatch. */ type CachedApp = { origin: string; app: WorkerApp; }; /** * Choose where an OUTBOUND subrequest to `u` is routed — or `null` to let the * real `fetch` handle it. Order matters and same-origin wins FIRST: a call to * this worker's OWN serving origin is a self-dispatch, not an edge fetch — and * the caller's `routeSubrequest` policy may itself match our own host (e.g. an * `isInstanceHost` predicate matches every `*.svc.astrale.ai`, ours included), * so it must never get first look at a same-origin call. * * • same-origin + SELF binding present → the SELF fetcher: a fresh same-script * invocation. The normal Cloudflare path — a Worker can't fetch its own * hostname over the edge, so it re-enters itself through the binding. * • same-origin + NO SELF binding → the cached app, dispatched IN-PROCESS. A * Workers-for-Platforms dispatch-namespace tenant can't service-bind to * itself (its script name is platform-renamed), so it has no SELF binding; an * in-process dispatch reaches the same script with no edge hop and no binding. * `App` and the SELF `Fetcher` share the `{ fetch(request) }` shape, so the * caller drives both identically. * • else, the caller's `routeSubrequest` policy matched → its fetcher. * • else → `null`: passthrough to the real network fetch. * * Pure (no closure over instance state) so the routing decision is unit-testable * without standing up a real app. */ export declare function selectSubrequestTarget(u: URL, ctx: { self: Fetcher | null; apps: Iterable; routeEnv: TDeps | null; routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined; }): { fetch(request: Request): Response | Promise; } | null; export interface WorkerEntryConfig { /** * Build the `createRemoteServer` config for the resolved serving `url`. Called * once per distinct URL (the resulting app is cached), with the same `env` the * request carries — so it can read additional bindings (e.g. a base domain). */ build: (url: string, env: TDeps) => RemoteServerConfig; /** * Resolve the raw serving URL from `env` (+ the per-request origin, for workers * that fall back to the request host). Defaults to the `WORKER_URL` env var. * The result is always canonicalized before use. * * The `requestOrigin` honors an `X-Forwarded-Proto: https` upgrade (see * `clientOrigin`), so a dev worker behind a TLS-terminating proxy (cloudflared * tunnel, reverse proxy) resolves its public `https://` origin, not the raw * `http://` one workerd sees. */ resolveUrl?: (env: TDeps, requestOrigin: string) => string; /** Optional: the `SELF` service binding used to route same-host subrequests. */ selfBinding?: (env: TDeps) => Fetcher | null | undefined; /** * Optional, vendor-neutral: route an OUTBOUND subrequest through a * caller-supplied fetcher instead of the network — for hosts a Worker can't * reach directly over the edge (e.g. a platform router on the SAME zone: * Cloudflare 522s a same-zone Worker→Worker public `fetch`). Return a `Fetcher` * to route the request through, or null/undefined to fall through to the normal * fetch. The SDK names no backend or topology — the CALLER owns BOTH the * predicate (which hosts) and the fetcher (the binding). This generalizes * `selfBinding` (same-origin → SELF) to any caller policy; both are honored. */ routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined; /** * Optional: handle a request before it reaches the kernel app — e.g. serve a * SPA under `/ui/*` or a same-origin `/api/*` endpoint the view calls. Return * a `Response` to short-circuit, or `undefined` to fall through to the domain * dispatch. */ before?: (env: TDeps, url: URL, request: Request) => Response | undefined | Promise; /** * Optional: transform the request on the fall-through path, just before it * reaches the kernel app (e.g. rewrite the hostname for wildcard-subdomain * routing). Not applied when `before` short-circuits. */ rewriteRequest?: (env: TDeps, request: Request) => Request; } export interface AppWorkerEntryConfig extends Omit, 'build'> { buildApp: (url: string, env: TDeps) => WorkerApp; } export interface WorkerEntry { fetch(request: Request, env: TDeps, executionCtx?: ExecutionContext): Response | Promise; } /** * The request origin as the CLIENT reached it — i.e. the origin a fallback * serving URL (and therefore the `iss`) may be derived from. Behind a * TLS-terminating proxy (a cloudflared tunnel in front of `wrangler dev`, any * reverse proxy) the worker sees plain HTTP, so `request.url` says `http://…` * while the public URL is `https://…`; the proxy advertises the original * scheme via `X-Forwarded-Proto`. Honoring it is restricted to the http→https * UPGRADE of the SAME host (never a downgrade, never a host change), so a * spoofed header can at worst derive an `iss` the kernel's JWKS check then * fails — it can never make this worker speak for another origin. */ export declare function clientOrigin(url: URL, request: Request): string; /** * Build a `before` hook that serves a static-asset `binding` (e.g. a Workers * Assets binding) mounted under `base` (default `/ui`) — the runtime half of an * adapter env's client-asset config. Returns `undefined` for non-matching paths * (and when no binding is present) so the request falls through to domain dispatch. * * Asset URLs are rooted at `base` (a client bundler sets `base: '/'`); * this hook strips that prefix before delegating to the binding, so * `/x.js` resolves from the binding's root. When `devProxy` yields a URL * (local dev), requests are proxied there instead — the seam for a bundler's * HMR dev server. Whether unknown sub-paths fall back to `index.html` is the * binding's own concern (e.g. wrangler's `not_found_handling`), not baked in * here. * * Lives here, type-checked and testable, instead of being emitted as a string * by every adapter's worker codegen. */ export declare function assets(opts: { base?: string; binding: (env: TDeps) => Fetcher | null | undefined; devProxy?: (env: TDeps) => string | null | undefined; }): (env: TDeps, url: URL, request: Request) => Response | Promise | undefined; export declare function createWorkerEntry(config: WorkerEntryConfig): WorkerEntry; export declare function createAppWorkerEntry(config: AppWorkerEntryConfig): WorkerEntry; export {}; //# sourceMappingURL=worker-entry.d.ts.map