import { PluginOption, CSSOptions, DepOptimizationOptions, ESBuildOptions, LogLevel, ResolveOptions, ServerOptions, BuildOptions, Rollup, Rolldown } from 'vite'; /** * τjs [ taujs ] Orchestration System * (c) 2024-present Aoede Ltd * Author: John Smith * * Licensed under the MIT License - attribution appreciated. * Part of the τjs [ taujs ] system for declarative, build-time orchestration of microfrontend applications, * including CSR, SSR, streaming, and middleware composition. */ /** * RFC 0005 (VS2): the PUBLIC, allowlisted Vite customisation surface for `taujs.config.ts`. * * These types describe exactly the Vite fields τjs supports through its declared channels - no * more. `Partial` would autocomplete every Vite property while the merge silently * dropped the protected ones (a lie in the editor); `TaujsViteConfig` instead admits only the * matrix-supported fields (RFC 0005 Amended contract §4). The runtime honours them symmetrically: * the merge engine (`utils/ViteMergeEngine.ts`) applies `config.vite` to the shared dev server and * to every app build, and `config.alias` layers into both sides' alias resolution * (`utils/ViteAlias.ts`). * * NOTE: these live OUTSIDE `core/` on purpose. `core/config/types.ts` is deliberately Vite-free * (e.g. `CoreAppConfig.plugins` is `readonly unknown[]`, re-decorated to `PluginOption[]` in * `Config.ts`); the Vite-typed surface belongs alongside `Config.ts`/`Build.ts`, which already * import from `vite`. */ /** * RFC 0005 Amended contract §6 - the day-one dev `optimizeDeps` subset (maintainer ruling * 2026-07-14). `include`/`exclude` force or withhold pre-bundling; `esbuildOptions` accommodates * dependency transforms, loaders, and esbuild plugins. Every OTHER optimiser field is deliberately * unadmitted, NOT forgotten - `entries`/`noDiscovery` (τjs owns shared-dev entry discovery), * `force` (an operational cache-bust, not durable config), `disabled` (deprecated), and the * experimental remainder each stay withheld until a concrete ecosystem case earns them. `optimizeDeps` * is development-only: nothing from it reaches client or SSR builds. */ type TaujsOptimizeDeps = Pick; /** * INTERNAL shape for `TaujsViteConfig['server']` - not exported from the package. Reference it as * `TaujsViteConfig['server']` if you need to name it. * * An ALLOWLIST of one, matching every other surface in this file, rather than "`ServerOptions` * minus what we own". The exclusion form was tried and rejected: it admitted options τjs cannot * honestly honour in middleware mode, and it would silently widen the public surface every time * Vite adds a field. * * Deliberately withheld, each for a reason: * * - `ws` - Vite documents `ws: false` as disabling the WebSocket connection, which would break the * HMR facility τjs owns through `server.ws` (Vite 8's canonical surface; the deprecated * `server.hmr` stays protected as legacy input). Admitting a field that disables an invariant we * claim is a contradiction, not a customisation. * - `host`, `port`, `strictPort`, `https`, `open` - these configure Vite's own HTTP listener, and * in middleware mode Vite has no listener. Fastify owns it. They would be inert, which is worse * than absent: the editor would suggest them and nothing would happen. * - `proxy` - overlaps caller-route ownership, which is a separate, unresolved design question. * It cannot be admitted before that is settled. * * More can be admitted later, one at a time, each with evidence that it works in middleware mode. * That is the same bar `optimizeDeps` was held to. * * Development-only: nothing under `server` reaches a build, silently, exactly like `optimizeDeps`. */ type TaujsViteServer = Pick; /** * RFC 0005 Amended contract §4 (support matrix) - the allowlisted Vite override object. Only the * matrix-admitted fields appear; the protected invariants (`root`, `base`, `publicDir`, * `configFile`, `appType`, `server.middlewareMode`, `server.hmr`, * `build.outDir`/`emptyOutDir`/`ssr`/`ssrManifest`/`format`/`target`/`manifest`, * `build.rollupOptions.input`, `resolve.alias`) are ABSENT from the type, so the editor refuses them * up front rather than the merge dropping them later. Aliases have their own declarative home * (top-level `alias`), so `resolve` here is the alias-free `ResolveOptions`. * * Under `server`, ONLY `allowedHosts` is admitted. It applies to the shared development server, and * is silently absent from every build (like `optimizeDeps`) rather than reported there as a * rejected override. */ type TaujsViteConfig = { /** Appended to the framework plugin list (append + dedupe by name; §5). */ plugins?: PluginOption[]; /** Shallow-merged with framework defines. */ define?: Record; /** Per-engine deep merge; only `preprocessorOptions` is admitted from `CSSOptions`. */ css?: { preprocessorOptions?: CSSOptions['preprocessorOptions']; }; /** Dev-only (§6); never reaches build configs. */ optimizeDeps?: TaujsOptimizeDeps; /** Override. */ esbuild?: ESBuildOptions | false; /** Override. */ logLevel?: LogLevel; /** `resolve` subset - `alias` is intentionally excluded (use top-level `alias`). */ resolve?: ResolveOptions; /** * Dev-server subset - `middlewareMode` and `hmr` are framework-owned and excluded. Declare * `allowedHosts` here to run development behind a proxy that presents a non-localhost `Host`. */ server?: TaujsViteServer; /** Build-tuning subset - the framework owns everything else under `build`. */ build?: { sourcemap?: BuildOptions['sourcemap']; minify?: BuildOptions['minify']; terserOptions?: BuildOptions['terserOptions']; rollupOptions?: { external?: Rollup.ExternalOption; output?: { /** * @deprecated MIGRATION SURFACE ONLY, and FUNCTION FORM ONLY. * * Vite 8/Rolldown does NOT support the Rollup OBJECT form (`{ vendor: ['react'] }`), and * Rolldown documents `manualChunks` as deprecated in favour of `output.codeSplitting`. * τjs deliberately does NOT translate the object form: exact Rollup object semantics * involve module resolution and dependency capture, so an approximation would risk * producing different bundles. * * Declaring the object form is REJECTED at the configuration boundary with a migration * error. Prefer `build.rolldownOptions.output.codeSplitting`. * * The type is derived from the bundler's own option shape, so it tracks exactly what the * installed bundler accepts. */ manualChunks?: Rollup.OutputOptions['manualChunks']; }; }; /** * The CANONICAL Vite 8 chunking surface, replacing the deprecated `manualChunks`. * Declaring BOTH this and `build.rollupOptions.output.manualChunks` is rejected: Rolldown * would otherwise silently ignore `manualChunks`, so τjs fails with an error naming both * paths rather than choosing one. */ rolldownOptions?: { output?: { codeSplitting?: Rolldown.OutputOptions['codeSplitting']; }; }; }; }; /** * RFC 0005 Amended contract §1 - the discriminated serve/build context handed to the function form * of `vite`. Dev invokes the callback ONCE with the `serve` arm (no `appId`/`entryPoint` - per-app * dev invocation would pretend an isolation the shared dev server deliberately does not have); build * invokes it per app with the `build` arm. `appId`/`entryPoint` are typed `never` on the serve arm so * a callback cannot read them without narrowing to `command === 'build'` first. */ type TaujsViteContext = { command: 'serve'; mode: string; isSSRBuild: false; appId?: never; entryPoint?: never; clientRoot: string; } | { command: 'build'; mode: string; isSSRBuild: boolean; appId: string; entryPoint: string; clientRoot: string; }; /** * RFC 0005 Amended contract §4 - the `config.vite` field type: a static `TaujsViteConfig` or a * function of the serve/build context. The function form must accept the WHOLE `TaujsViteContext`; * a callback typed for the build arm alone is not a valid `TaujsViteOverride` (it could not honestly * run for the shared dev server). */ type TaujsViteOverride = TaujsViteConfig | ((ctx: TaujsViteContext) => TaujsViteConfig); declare const DEBUG_CATEGORIES: readonly ["auth", "routes", "errors", "vite", "network", "ssr"]; type DebugCategory = (typeof DEBUG_CATEGORIES)[number]; type DebugConfig = boolean | DebugCategory[] | ({ all?: boolean; } & Partial>); interface BaseLogger { debug?(meta?: unknown, message?: string): void; info?(meta?: unknown, message?: string): void; warn?(meta?: unknown, message?: string): void; error?(meta?: unknown, message?: string): void; child?(context: Record): BaseLogger; } interface Logs extends BaseLogger { debug(meta?: unknown, message?: string): void; debug(category: DebugCategory, meta?: unknown, message?: string): void; info(meta?: unknown, message?: string): void; warn(meta?: unknown, message?: string): void; error(meta?: unknown, message?: string): void; child(context: Record): Logs; isDebugEnabled(category: DebugCategory): boolean; } interface EpisodeRecorder { requestStart(e: { requestId: string; url: string; method: string; }): void; /** * RFC 0018 (Substrate): `kind` is required, with no "absent means page" default - a persisted * discriminant must not acquire meaning implicitly. A host route has no appId and no render * strategy, so both become optional; `method` is added optional and carries the request's own * `request.method`, never a route's declared method (RFC 0018 Limits). */ routeMatched(e: { requestId: string; path: string; method?: string; appId?: string; render?: 'ssr' | 'streaming'; kind: 'page' | 'host'; }): void; dataFetch(e: { requestId: string; ms: number; ok: boolean; }): void; /** * RFC 0007 (R5): fired exactly ONCE per declared `attr.deferred` key per request. `ms` measures * from registry creation to settlement or `aborted` classification. No payload, params, URL * values, error message or stack - the graph supplies the declared key -> service relation. */ deferredData(e: { requestId: string; key: string; ms: number; outcome: 'complete' | 'failed' | 'aborted'; }): void; serviceCall(e: { requestId: string; service: string; method: string; ms: number; ok: boolean; }): void; streamPhase(e: { requestId: string; phase: 'head' | 'shellReady' | 'allReady'; }): void; /** * RFC 0018 (Host terminal contract): discriminated by `kind`. The page arm is unchanged; the host * arm carries no `mode` at all - a route that never renders has no render mode to invent. */ sent(e: { requestId: string; status: number; mode: 'ssr' | 'streaming' | 'fallthrough'; } | { requestId: string; status: number; kind: 'host'; }): void; aborted(e: { requestId: string; phase?: string; }): void; /** * RFC 0018 (Host terminal contract): `status` is the HTTP status the client received, or is * about to receive - never a domain classification, which stays `error.kind`. `error` is optional * so a host outcome with no error object still records a status; the assembler substitutes a * redacted placeholder when it is absent. */ failed(e: { requestId: string; status: number; error?: { kind: string; message: string; }; }): void; clientHydration(e: { requestId: string; ok: boolean; ms?: number; error?: string; }): void; } type JsonPrimitive = string | number | boolean | null; type JsonValue = JsonPrimitive | JsonValue[] | { [k: string]: JsonValue; }; type JsonObject = { [k: string]: JsonValue; }; type NarrowSchema = { parse: (u: unknown) => T; } | ((u: unknown) => T); type BaseServiceContext = { signal?: AbortSignal; deadlineMs?: number; requestId?: string; logger?: Logs; user?: { id: string; roles: string[]; } | null; recorder?: EpisodeRecorder; }; type UntypedRegistryCaller = (serviceName: string, methodName: string, args?: JsonObject) => Promise; type RuntimeServiceContext = BaseServiceContext & { call?: UntypedRegistryCaller; }; interface ServiceContext extends BaseServiceContext { } /** * Params and result are JSON object types. Type params with a type alias or an inline object * type; an interface without an index signature is not accepted. */ type ServiceMethod

= (params: P, ctx: Ctx) => Promise; type RuntimeServiceMethod

= (params: P, ctx: RuntimeServiceContext) => Promise; type ServiceDefinition = Readonly>>; type ServiceRegistry = Readonly>; type ServiceMethodParams = M extends (params: infer P, ctx: any) => Promise ? P : never; type ServiceMethodResult = Awaited Promise ? R : never>; type RegistryCallerArgs = undefined extends ServiceMethodParams ? [serviceName: S, methodName: M, args?: ServiceMethodParams] : [serviceName: S, methodName: M, args: ServiceMethodParams]; type RegistryCaller = (...args: RegistryCallerArgs) => Promise>; type TypedServiceContext = ServiceContext & { call?: RegistryCaller; }; declare function withDeadline(signal: AbortSignal | undefined, ms?: number): AbortSignal | undefined; type ServiceDescriptor = { serviceName: string; serviceMethod: string; args?: JsonObject; }; type ServiceSpecEntry = ServiceMethod | { handler: ServiceMethod; params?: NarrowSchema; result?: NarrowSchema; }; type ServiceSpec = Record; type ExtractServiceMethod = T extends { handler: infer H; } ? H : T; type NormalizeServiceMethod = M extends (params: infer P extends JsonObject, ctx: any) => Promise ? RuntimeServiceMethod : ServiceParamsTypeError; type ServiceParamsMessage = 'params must be a JSON object type; an interface without an index signature is not accepted, so use a type alias or an inline object type'; type ServiceParamsTypeError = { readonly __taujsServiceTypeError: ServiceParamsMessage; }; type NormalizedServiceSpec = { [K in keyof T]: NormalizeServiceMethod>; }; type ServiceSchemaKind = 'parse' | 'function'; type ServiceSchemaMetadata = Readonly<{ declared: boolean; kind?: ServiceSchemaKind; }>; type ServiceMethodMetadata = Readonly<{ params: ServiceSchemaMetadata; result: ServiceSchemaMetadata; }>; type ServiceParamsOf = M extends (...args: infer A) => any ? (A extends [] ? JsonObject : A[0]) : JsonObject; type ValidateServiceSpec = { [K in keyof T]: ServiceParamsOf> extends JsonObject ? T[K] : { readonly __taujsServiceTypeError: ServiceParamsMessage; }; }; /** * Params and result types must be JSON object types (a type alias or inline object type; an * interface without an index signature is not accepted - see {@link ServiceMethod}). */ declare function defineService(spec: T & ValidateServiceSpec): NormalizedServiceSpec; declare const getServiceMethodMetadata: (fn: unknown) => ServiceMethodMetadata | undefined; declare const defineServiceRegistry: (registry: R) => R; declare function callServiceMethod(registry: ServiceRegistry, serviceName: string, methodName: string, params: JsonObject | undefined, ctx: BaseServiceContext): Promise; /** * RFC 0016 (Phase A): the two evidence names τjs can honestly derive from what it already * proves at boot - never an authentication OUTCOME, never a coverage statement about runtime * reachability. Framework-derived and environment-independent (the truth tables live on * `isEvidencePresent` below). */ type RoutePolicyEvidenceName = 'taujs.auth-wired' | 'taujs.csp-configured'; /** * RFC 0016 (Phase A): every field is DETERMINATE (match or no-match, nothing else) and * EXACT-MATCH only - no wildcard grammar, no transitive service/method reachability (deferred * by the revision-5 shrink). Fields are conjunctive; `{}` is the explicit catch-all that owns * every route no other rule claims. */ type RoutePolicySelector = { /** Exact match against the route's declared `appId`. */ appId?: string; /** Exact match against the graph's declared path string - no wildcard grammar. */ path?: string; render?: 'ssr' | 'streaming'; hydrate?: boolean; /** `true` when the route's `data.kind !== 'none'`. */ hasData?: boolean; /** `true` when the route declares `attr.head.data`. */ hasHead?: boolean; /** `true` when the route declares at least one `attr.deferred` entry. */ hasDeferred?: boolean; }; /** * RFC 0016 (Phase A): one ordered rule. `id` is required, unique and stable * (`^[a-z][a-z0-9-]{0,63}$`). `require` empty or omitted is valid and EXPLICITLY owns a public * route - it is not the same as no rule matching at all (`policy.route_unmatched`). */ type RoutePolicyRule = { id: string; match: RoutePolicySelector; require?: RoutePolicyEvidenceName[]; }; /** RFC 0016 (Phase A): the top-level declared shape - ordered, first-match rules. */ type RoutePolicy = { rules: RoutePolicyRule[]; }; type RequestContext = { /** Canonical request-correlation identity: always `String(req.id)` (SC-09). */ requestId: string; logger: L; headers?: Record; /** * Fastify's request target as received: the path plus any query string (`/products?sort=price`), * never an origin and never a parsed `URL`. Loaders read query state from here and parse what * they need themselves. */ url: string; /** Dev-only episode recorder (already safety-wrapped); absent in production. */ recorder?: EpisodeRecorder; }; type RouteParams = Partial>; type RouteCSPConfig = { disabled?: boolean; mode?: 'merge' | 'replace'; directives?: unknown | ((args: { url: string; params: RouteParams; headers: Record; req?: unknown; }) => unknown); generateCSP?: (directives: unknown, nonce: string, req?: unknown) => string; reportOnly?: boolean; }; type BaseMiddleware = { auth?: { redirect?: string; roles?: string[]; strategy?: string; }; csp?: RouteCSPConfig | false; }; type DataResult = Record | ServiceDescriptor; type RequestServiceContext = ServiceContext & RequestContext & { call?: RegistryCaller; headers: Record; }; type DataHandler = (params: Params, ctx: (RequestServiceContext & { call: RegistryCaller; }) & { [key: string]: unknown; }) => Promise; declare const SERVICE_RESULT: unique symbol; /** * RFC 0004 (H1): a `DataHandler` produced by `serviceData()`, carrying the selected service * method's eventual (post-dispatch) result as a PHANTOM type brand. The callable's declared * return stays the honest service DESCRIPTOR - the runtime value really is the descriptor, and * the server dispatches it - while the brand tells the type system what that dispatch resolves * to, so `HeadDataOf` and `RouteDataOf` (brand arm completed 2026-07-30) can infer the real * payload instead of the descriptor shape. */ type ServiceDataHandler = DataHandler & { readonly [SERVICE_RESULT]: Result; }; /** * RFC 0004 (H1): per-route dynamic head data, resolved BEFORE the renderer starts on BOTH * strategies and delivered to the renderer as `opts.headData` (never serialised into * `__INITIAL_DATA__` - ruling 1). `attr.meta` remains the static layer (ruling 5). */ type HeadAttributes = { /** Head data loader - same shape as `attr.data` (plain object or `ServiceDescriptor`, incl. `serviceData()` sugar). */ data: DataHandler; /** * Head loader deadline in ms - POSITIVE FINITE only, validated at boot (default 3000). On * expiry with the request still live, the render proceeds with `headData: undefined` plus an * advisory log (RFC 0004 Policy ii). There is deliberately no wait-forever sentinel: the head * blocks the shell, so its deadline stays bounded. */ timeoutMs?: number; /** * Opt-in recoverability for ORDINARY loader rejection: `true` degrades a rejection like a * deadline expiry (undefined + advisory) instead of failing the request. Default `false` - * real application defects stay visible on the existing error path. */ optional?: boolean; }; /** * RFC 0007 (R1): the flat, STREAMING-ONLY record of route-owned deferred loaders. Values are the * existing `DataHandler` shape (`serviceData()` sugar included) - no new helper, no dependencies * between entries, and no optionality: `deferred` describes timing, not whether a value may be * missing. Keys are stable route-local identifiers matching `^[A-Za-z][A-Za-z0-9_]*$`, validated at * boot beside the other extract-routes checks. */ type DeferredDataAttributes = Readonly>>; type RouteAttributes = { render: 'ssr'; hydrate?: boolean; meta?: Record; middleware?: Middleware; data?: DataHandler; head?: HeadAttributes; } | { render: 'streaming'; hydrate?: boolean; meta: Record; middleware?: Middleware; data?: DataHandler; deferred?: DeferredDataAttributes; head?: HeadAttributes; }; type Route = { attr?: RouteAttributes; path: string; appId?: string; }; type AppId = C['apps'][number]['appId']; type AppOf> = Extract; type RoutesOfApp> = NonNullable['routes']> extends readonly any[] ? NonNullable['routes']>[number] : never; /** * What a route WITHOUT `attr.data` resolves to: `fetchInitialData` returns `{}` for such routes * (DataRoutes.ts), so property access honestly yields `undefined` - and unlike `unknown`, this * unions cleanly into an app-wide RouteData instead of absorbing it (review ruling, 2026-07-30). */ type EmptyRouteData = Record; /** * The type a route's `attr.data` resolves to - the "future RouteDataOf work" the SERVICE_RESULT * brand comment promised, completed 2026-07-30 for the scaffolder type chain. Arms follow * `HeadDataOf`, with a ruled no-data arm: * - `serviceData()` sugar: the SELECTED METHOD's resolved result, read from the phantom brand - * never the descriptor the handler honestly returns at runtime; * - closure handler: its resolved return type (descriptor returns collapse to * `Record` - the dispatch result is untyped for hand-built descriptors); * - no `attr.data`: `EmptyRouteData` - the honest type of the `{}` the server supplies, so an * app-wide union stays usable (`data.field` reads `T | undefined`) rather than collapsing to * `unknown`; * - a declared `data` of unrecognisable shape stays `unknown`. * Pinned by `core/config/test/RouteContext.test-d.ts` and `test/PublicRouteContext.test-d.ts`. */ type RouteDataOf = R extends { attr?: { data?: infer D; }; } ? D extends { readonly [SERVICE_RESULT]: infer Res; } ? Res : D extends (...args: any) => infer Ret ? DescriptorMemberToRecord> : unknown : EmptyRouteData; /** * RFC 0004 (H1): the type `headContent` receives as `headData` for a route. Three arms, pinned * by `test/HeadDataOf.test-d.ts` (a signed hard gate): * - `serviceData()` sugar: the SELECTED METHOD's resolved result, read from the phantom brand - * never the descriptor, never `Record`; * - closure handler: its resolved return type (descriptor returns collapse to * `Record` - the dispatch result is untyped for hand-built descriptors); * - no `attr.head`: `undefined`. */ type HeadDataOf = R extends { attr?: infer A; } ? A extends { head: { data: infer H; }; } ? H extends { readonly [SERVICE_RESULT]: infer Res; } ? Res : H extends (...args: any) => infer Ret ? DescriptorMemberToRecord> : unknown : undefined : undefined; /** * RFC 0007: the type a renderer's deferred accessor receives for a route, following `HeadDataOf`'s * three arms exactly: * - `serviceData()` sugar: the SELECTED METHOD's resolved result, read from the phantom brand; * - closure handler: its resolved return type (descriptor returns collapse to * `Record` - the dispatch result is untyped for hand-built descriptors); * - no `attr.deferred`: `undefined`. * * Note the inferred type describes the loader's DECLARED result. What arrives is that value's JSON * snapshot (failure semantics item 2) - the same caveat that already applies to `attr.data` * crossing `__INITIAL_DATA__`. */ type DeferredDataOf = R extends { attr?: infer A; } ? A extends { deferred: infer D; } ? { [K in keyof D]: D[K] extends { readonly [SERVICE_RESULT]: infer Res; } ? Res : D[K] extends (...args: any) => infer Ret ? DescriptorMemberToRecord> : unknown; } : undefined : undefined; /** * Distributes over a closure handler's return union: a descriptor MEMBER resolves (via service * dispatch) to an untyped record, so it contributes `Record` to the union - it * must never be EXCLUDED, which would falsely narrow a mixed `{ title } | ServiceDescriptor` * return to `{ title }` alone (gate-recheck finding: the dispatched branch may resolve to any * record). Pure-object returns stay precise; pure-descriptor returns collapse to the record. */ type DescriptorMemberToRecord = V extends ServiceDescriptor ? Record : V; type RoutePathOf = R extends { path: infer P; } ? P : never; /** * Ruled 2026-07-29 (followup: RouteContext type/runtime drift): the public context type mirrors * the runtime value HandleRender constructs - `{ appId, path, attr, params }` - EXACTLY. `data` * was never supplied at runtime (route data reaches the renderer's store, not the context) and is * deliberately absent; `params` is the matched route parameters, typed as the same broad * `RouteParams` every data handler receives. Pinned by `test/RouteContext.test-d.ts` and by the * HandleRender runtime assertions on both render strategies. */ type SingleRouteContext, R extends RoutesOfApp> = R extends any ? { appId: A; path: RoutePathOf; attr: R extends { attr: infer Attr; } ? Attr : R extends { attr?: infer Attr; } ? Attr | undefined : undefined; params: RouteParams; } : never; type RouteContext = { [A in AppId]: SingleRouteContext>; }[AppId]; type SingleRouteDataEntry, R extends RoutesOfApp> = R extends any ? { path: RoutePathOf; data: RouteDataOf; } : never; type RouteDataEntry = { [A in AppId]: SingleRouteDataEntry>; }[AppId]; type RouteData = Extract, { path: Path; }>['data']; type CoreSecurityConfig = { csp?: { directives?: unknown; generateCSP?: (directives: unknown, nonce: string, req?: unknown) => string; reporting?: { endpoint: string; onViolation?: (report: unknown, req: unknown) => void; reportOnly?: boolean; }; }; }; type AppRoute = Omit, 'appId'> & { attr?: RouteAttributes; }; type CoreAppConfig = { appId: string; entryPoint: string; plugins?: readonly unknown[]; renderer?: unknown; routes?: readonly AppRoute[]; }; type CoreIntrospectionConfig = { /** Relaxes ONLY the overlay remote-address check; shouts in the boot summary when enabled. */ allowNonLoopback?: boolean; /** * Extends ONLY the overlay Host admission with exact DNS hostnames (post-freeze ruling * 2026-08-08) - for development behind a reverse proxy that presents a non-localhost `Host`. * Declare the hostname as seen AT the τjs hop: a rewriting proxy substitutes its own * upstream name for the browser-facing one. Exact matches only - no wildcards, IP literals * or boolean escape; localhost forms and IP literals stay admitted intrinsically. Behind a * rewriting proxy, browser-facing host validation is the PROXY's job - τjs reads no * forwarding headers. Independent of `allowNonLoopback` (a cross-machine proxy needs both); * neither implies the other. Shouts in the boot summary when non-empty. */ allowedHosts?: string[]; redaction?: { /** Extends the default denylist (password, token, secret, ssn, auth, cookie, session, key). */ denyKeys?: string[]; replaceDefaultDenyKeys?: boolean; }; }; type CoreTaujsConfig = { apps: readonly CoreAppConfig[]; security?: CoreSecurityConfig; introspection?: CoreIntrospectionConfig; server?: { host?: string; port?: number; hmrPort?: number; /** * RFC 0012: where Fastify RECEIVES the τjs installation - the single scope prefix under * which every declared route (all apps), τjs static and the development `/__taujs/*` * surface register. Installation-level; `entryPoint` remains the per-app layout * namespace and is never a substitute for this. Canonical form only (`''` or * `/segment(/segment)*`, no trailing slash) - non-canonical values are rejected at * config validation, never silently normalised. Default `''` (root - today's behaviour * byte-for-byte). */ mountPrefix?: string; /** * RFC 0012: what τjs EMITS in front of every URL it generates (asset, preload and CSS * links, the bootstrap module URL, the dev beacon) and the value the Vite `base` * derives from. Defaults to `mountPrefix`; the two differ exactly when a proxy STRIPS * the public prefix (declare `mountPrefix: ''` with the public prefix here). Explicit * `''` alongside a non-empty `mountPrefix` is rejected as unsupported (no measured * topology). Same canonical form as `mountPrefix`. */ publicBasePath?: string; /** * RFC 0013: how the development HMR WebSocket is carried. Installation-level, because * choosing the transport is an installation decision of the same kind as the RFC 0012 * coordinates above. * * - `'fixed-port'` (DEFAULT) - the dedicated `hmrPort` listener, today's behaviour * byte-for-byte. * - `'attached'` - the HMR socket rides the application's own HTTP server, so it flows * wherever that channel flows. This is what makes development work where a second * fixed port cannot be reached: a supervisor that virtualises worker binds, a firewall, * or a single-channel proxy. * - `'mediated'` (RFC 0014) - for a caller-supplied host (mode B): the caller offers τjs * first refusal on each upgrade through the returned `dev.hmr.tryHandleUpgrade`, so HMR * rides whatever channel the caller's own listener is on, without τjs touching the * caller's root. * * OFF BY DEFAULT and never inferred: τjs does not detect its host or sniff the * environment, so a non-default transport is REQUESTED explicitly, exactly as development * mode itself is. `'attached'` requires τjs to own the Fastify host (mode A); `'mediated'` * requires the opposite, a caller-supplied host (mode B); each is rejected on the * ownership it does not fit, at configuration time, rather than mutating listeners it does * not own. `hmrPort`, `HMR_PORT` and `--hmr-port` remain accepted so an existing * configuration can switch transport without being rewritten, but they do not select or * alter the attached or mediated channel. No effect in production, where no HMR facility * exists. * * Carrying an attached channel through a proxy is a HOST deployment matter (prefix * preservation, a real upstream, watcher scope) and requires a trusted development * network - see the τjs documentation; τjs adds no proxy machinery of its own. */ hmrTransport?: 'fixed-port' | 'attached' | 'mediated'; }; alias?: Record; /** * RFC 0016 (Phase A): opt-in, fail-closed boot invariant - ordered first-match rules * declaring which routes are supposed to require which framework-derived evidence, and * which are explicitly public (an empty or omitted `require`). When absent: no canonical * request graph is built and no evaluation, policy logging or request-time work runs - * configuration validation is the entire cost. When declared, τjs builds the canonical graph (development and * production alike), evaluates every route, logs every finding, then refuses to boot on ANY * finding - in both environments, with no escape switch. Validated at `createServer` * function entry, before any host state exists. */ routePolicy?: RoutePolicy; }; export { type AppRoute as A, type DebugConfig as B, type CoreTaujsConfig as C, type DeferredDataAttributes as D, type EmptyRouteData as E, type BaseLogger as F, type HeadAttributes as H, type JsonObject as J, type RouteParams as R, type ServiceRegistry as S, type TaujsViteOverride as T, type ServiceMethodParams as a, type ServiceDataHandler as b, type CoreAppConfig as c, type CoreSecurityConfig as d, type RouteContext as e, type RouteData as f, type DeferredDataOf as g, type HeadDataOf as h, type JsonPrimitive as i, type JsonValue as j, type RegistryCaller as k, type RoutePolicy as l, type RoutePolicyEvidenceName as m, type RoutePolicyRule as n, type RoutePolicySelector as o, type ServiceContext as p, type ServiceMethodMetadata as q, type TaujsOptimizeDeps as r, type TaujsViteConfig as s, type TaujsViteContext as t, type TypedServiceContext as u, callServiceMethod as v, defineService as w, defineServiceRegistry as x, getServiceMethodMetadata as y, withDeadline as z };