@cleverbrush/server 4.4.1 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/CacheTag.ts","../src/Subscription.ts","../src/Endpoint.ts","../src/route.ts","../src/contract.ts"],"sourcesContent":["import {\n type ObjectSchemaBuilder,\n object,\n type PropertyDescriptor,\n type SchemaBuilder,\n SYMBOL_HAS_PROPERTIES,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * An accessor that can extract a property value from a structured\n * request root. Wraps a {@link PropertyDescriptor}'s `getValue`\n * closure so the middleware layer does not need to know about schemas.\n */\nexport interface CacheTagPropertyAccessor {\n getValue(root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }): { value?: unknown; success: boolean };\n}\n\n/**\n * A serialisable cache-tag definition stored on endpoint metadata.\n *\n * `properties` maps human-readable key names (used as label segments\n * in the final cache key) to accessors that resolve the actual value\n * from call-time request data.\n */\nexport interface CacheTagDefinition {\n readonly name: string;\n readonly properties: Readonly<Record<string, CacheTagPropertyAccessor>>;\n}\n\n// ---------------------------------------------------------------------------\n// Synthetic schema construction\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a synthetic `object({ params, body, query, headers })` schema\n * from the endpoint's schema definitions and returns its\n * `PropertyDescriptorTree` so callers can write type-safe selectors like:\n *\n * ```ts\n * endpoint.cacheTag('todo', p => ({\n * id: p.query.id,\n * fromBodyId: p.body.id\n * }))\n * ```\n *\n * Only non-null schemas are included in the synthetic schema.\n */\nexport function createCacheTagTree(schemas: {\n paramsSchema?: SchemaBuilder<any, any, any, any, any> | null;\n bodySchema?: SchemaBuilder<any, any, any, any, any> | null;\n querySchema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null;\n headerSchema?: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n}): any {\n const props: Record<string, SchemaBuilder<any, any, any, any, any>> = {};\n\n if (schemas.paramsSchema) {\n const ps = schemas.paramsSchema;\n // ParseStringSchemaBuilder wraps an ObjectSchemaBuilder;\n // extract it so PropertyDescriptorTree can recurse into params.\n if (\n typeof (ps as any).introspect === 'function' &&\n (ps as any).introspect().objectSchema\n ) {\n props.params = (ps as any).introspect().objectSchema;\n } else if ((ps as any)[SYMBOL_HAS_PROPERTIES] === true) {\n props.params = ps;\n }\n }\n\n if (schemas.bodySchema) {\n const bs = schemas.bodySchema;\n if (\n (bs as any)[SYMBOL_HAS_PROPERTIES] === true ||\n typeof (bs as any).introspect === 'function'\n ) {\n props.body = bs;\n }\n }\n\n if (schemas.querySchema) {\n props.query = schemas.querySchema;\n }\n\n if (schemas.headerSchema) {\n props.headers = schemas.headerSchema;\n }\n\n const syn = object(props as any);\n return (object as any).getPropertiesFor(syn);\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a value returned from a cache-tag selector is a valid\n * property descriptor.\n */\nfunction isPropertyDescriptor(value: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (typeof value !== 'object') return false;\n return (\n typeof (value as any)[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR] === 'object' &&\n (value as any)[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR] !== null\n );\n}\n\n/**\n * Serialises the result of a cache-tag selector callback into a\n * {@link CacheTagDefinition} that can be stored on endpoint metadata\n * and forwarded to the client middleware.\n *\n * Each value in `descriptors` must be a {@link PropertyDescriptor} —\n * its `getValue` closure is wrapped in a {@link CacheTagPropertyAccessor}.\n *\n * @throws If any value is not a valid property descriptor.\n */\nexport function serializeTag(\n name: string,\n descriptors: Record<string, unknown>\n): CacheTagDefinition {\n const properties: Record<string, CacheTagPropertyAccessor> = {};\n\n for (const [key, value] of Object.entries(descriptors)) {\n if (!isPropertyDescriptor(value)) {\n throw new Error(\n `Cache tag \"${name}\": property \"${key}\" is not a valid ` +\n `PropertyDescriptor. Make sure you select a leaf property ` +\n `from the tree (e.g. p.query.id, not p.query).`\n );\n }\n\n const inner = (value as PropertyDescriptor<any, any, any, any>)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ];\n\n properties[key] = {\n getValue: (root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }) => inner.getValue(root as any)\n };\n }\n\n return { name, properties };\n}\n\n// ---------------------------------------------------------------------------\n// Key computation (client-side)\n// ---------------------------------------------------------------------------\n\n/**\n * Computes a deterministic cache key from a tag definition and live\n * request data.\n *\n * - Simple tags (no properties) produce just the tag name.\n * - Tags with properties produce `name:key1=val1,key2=val2` where\n * keys are sorted alphabetically for determinism.\n *\n * Properties whose `getValue` returns `success: false` are skipped\n * (their value is not included in the key).\n */\nexport function computeCacheKey(\n tag: CacheTagDefinition,\n root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }\n): string {\n const entries = Object.entries(tag.properties);\n\n if (entries.length === 0) {\n return tag.name;\n }\n\n const parts: string[] = [];\n for (const [key, accessor] of entries.sort(([a], [b]) =>\n a.localeCompare(b)\n )) {\n const result = accessor.getValue(root);\n if (result.success && result.value !== undefined) {\n parts.push(`${key}=${String(result.value)}`);\n }\n }\n\n if (parts.length === 0) {\n return tag.name;\n }\n\n return `${tag.name}:${parts.join(',')}`;\n}\n","import type {\n InferType,\n ObjectSchemaBuilder,\n ParseStringSchemaBuilder,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport type { RequestContext } from './RequestContext.js';\nimport type { Middleware } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Simplify — flattens intersection types for clean IDE tooltips\n// ---------------------------------------------------------------------------\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// ---------------------------------------------------------------------------\n// tracked() — resumable event wrapper\n// ---------------------------------------------------------------------------\n\nconst TRACKED_SYMBOL = Symbol.for('cleverbrush.tracked');\n\n/**\n * A server-sent event wrapped with a unique ID for resumable subscriptions.\n *\n * When a client reconnects, it can send the last received event ID so the\n * server resumes from that point instead of replaying everything.\n *\n * @see {@link tracked}\n */\nexport interface TrackedEvent<T = unknown> {\n /** @internal Brand marker. */\n readonly [TRACKED_SYMBOL]: true;\n /** Unique event identifier for resume tracking. */\n readonly id: string;\n /** The actual event payload. */\n readonly data: T;\n}\n\n/**\n * Wrap a server-sent event with a unique ID for resumable subscriptions.\n *\n * When a handler yields a `TrackedEvent`, the server sends the event with its\n * ID. If the client disconnects and reconnects with the last received ID,\n * the handler can skip already-delivered events.\n *\n * @param id - A unique, monotonically increasing identifier for this event.\n * @param data - The event payload to send to the client.\n * @returns A {@link TrackedEvent} wrapper.\n *\n * @example\n * ```ts\n * async function* handler({ incoming }) {\n * yield tracked('evt-1', { message: 'Hello' });\n * yield tracked('evt-2', { message: 'World' });\n * }\n * ```\n */\nexport function tracked<T>(id: string, data: T): TrackedEvent<T> {\n return { [TRACKED_SYMBOL]: true, id, data };\n}\n\n/**\n * Check whether a value is a {@link TrackedEvent}.\n * @internal\n */\nexport function isTrackedEvent(value: unknown): value is TrackedEvent {\n return (\n value !== null &&\n typeof value === 'object' &&\n TRACKED_SYMBOL in value &&\n (value as any)[TRACKED_SYMBOL] === true\n );\n}\n\n// ---------------------------------------------------------------------------\n// SubscriptionContext — the typed argument for subscription handlers\n// ---------------------------------------------------------------------------\n\ntype HasKeys<T> = keyof T extends never ? false : true;\n\ntype SubscriptionContextParts<\n TParams,\n TQuery,\n THeaders,\n TPrincipal,\n TIncoming\n> = {\n context: RequestContext;\n signal: AbortSignal;\n} & (HasKeys<TParams> extends true ? { params: TParams } : {}) &\n (HasKeys<TQuery> extends true ? { query: TQuery } : {}) &\n (HasKeys<THeaders> extends true ? { headers: THeaders } : {}) &\n (TPrincipal extends undefined ? {} : { principal: TPrincipal }) &\n ([TIncoming] extends [undefined]\n ? {}\n : {\n incoming: AsyncIterable<\n TIncoming extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TIncoming>\n : TIncoming\n >;\n });\n\n/**\n * The fully-typed argument object passed to subscription handlers.\n *\n * The shape is inferred from the `SubscriptionBuilder` chain — only the keys\n * actually configured (query, headers, params, principal, incoming) are present.\n * Always includes `context` ({@link RequestContext}) and `signal` ({@link AbortSignal}).\n */\nexport type SubscriptionContext<E> =\n E extends SubscriptionBuilder<\n infer TParams,\n infer TQuery,\n infer THeaders,\n any,\n infer TPrincipal,\n any,\n infer TIncoming,\n any\n >\n ? Simplify<\n SubscriptionContextParts<\n TParams,\n TQuery,\n THeaders,\n TPrincipal,\n TIncoming\n >\n >\n : never;\n\n// ---------------------------------------------------------------------------\n// InferServices — maps { name: SchemaBuilder } to { name: InferType<Schema> }\n// ---------------------------------------------------------------------------\n\ntype InferServices<T> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : never;\n};\n\n/**\n * Extracts the injected service schemas map from a `SubscriptionBuilder`.\n */\nexport type SubscriptionServiceSchemas<E> =\n E extends SubscriptionBuilder<\n any,\n any,\n any,\n infer TServices,\n any,\n any,\n any,\n any\n >\n ? TServices\n : {};\n\n// ---------------------------------------------------------------------------\n// InferOutgoing — extracts the outgoing event type\n// ---------------------------------------------------------------------------\n\ntype OutgoingType<E> =\n E extends SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TOutgoing\n >\n ? TOutgoing extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TOutgoing> | TrackedEvent<InferType<TOutgoing>>\n : TOutgoing | TrackedEvent<TOutgoing>\n : any;\n\n// ---------------------------------------------------------------------------\n// SubscriptionHandler — the async generator function type\n// ---------------------------------------------------------------------------\n\n/**\n * The handler function type inferred from a `SubscriptionBuilder`.\n *\n * Must be an async generator that yields outgoing events. When the endpoint\n * has injected services, the handler receives a second `services` argument.\n *\n * @example\n * ```ts\n * const handler: SubscriptionHandler<typeof chatEndpoint> =\n * async function* ({ incoming, principal }) {\n * yield tracked('welcome', { text: 'Hello!' });\n * for await (const msg of incoming) {\n * yield { text: `${principal.name}: ${msg.text}` };\n * }\n * };\n * ```\n */\nexport type SubscriptionHandler<E> =\n HasKeys<SubscriptionServiceSchemas<E>> extends true\n ? (\n arg: SubscriptionContext<E>,\n services: Simplify<InferServices<SubscriptionServiceSchemas<E>>>\n ) => AsyncGenerator<OutgoingType<E>>\n : (arg: SubscriptionContext<E>) => AsyncGenerator<OutgoingType<E>>;\n\n// ---------------------------------------------------------------------------\n// SubscriptionMetadata — runtime introspection snapshot\n// ---------------------------------------------------------------------------\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/**\n * Snapshot of all configuration set on a `SubscriptionBuilder`.\n * Used by the server for WebSocket upgrade handling and by documentation\n * generators for AsyncAPI / OpenAPI spec output.\n */\nexport interface SubscriptionMetadata {\n /** Always `'subscription'`. Distinguishes from HTTP endpoint metadata. */\n readonly protocol: 'subscription';\n readonly basePath: string;\n readonly pathTemplate: RoutePath;\n readonly incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly authRoles: readonly string[] | null;\n readonly summary: string | null;\n readonly description: string | null;\n readonly tags: readonly string[];\n readonly operationId: string | null;\n readonly deprecated: boolean;\n readonly externalDocs: { url: string; description?: string } | null;\n}\n\n// ---------------------------------------------------------------------------\n// SubscriptionBuilder — immutable builder for WebSocket subscription endpoints\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable, fluent builder for WebSocket subscription endpoint definitions.\n *\n * All methods return a new builder instance — the original is never mutated.\n * Use `endpoint.subscription()` to obtain the first builder in the chain.\n *\n * @example\n * ```ts\n * const ChatRoom = endpoint\n * .subscription('/ws/chat')\n * .incoming(object({ text: string() }))\n * .outgoing(object({ user: string(), text: string(), timestamp: number() }))\n * .authorize(PrincipalSchema)\n * .summary('Real-time chat room');\n * ```\n */\nexport class SubscriptionBuilder<\n TParams = {},\n TQuery = {},\n THeaders = {},\n TServices = {},\n TPrincipal = undefined,\n TRoles extends string = string,\n TIncoming = undefined,\n TOutgoing = undefined\n> {\n readonly #basePath: string;\n readonly #pathTemplate: RoutePath;\n readonly #incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly #authRoles: readonly string[] | null;\n readonly #summary: string | null;\n readonly #description: string | null;\n readonly #tags: readonly string[];\n readonly #operationId: string | null;\n readonly #deprecated: boolean;\n readonly #externalDocs: { url: string; description?: string } | null;\n\n constructor(\n basePath: string,\n pathTemplate: RoutePath = '/',\n incomingSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n outgoingSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null = null,\n authRoles: readonly string[] | null = null,\n summary: string | null = null,\n description: string | null = null,\n tags: readonly string[] = [],\n operationId: string | null = null,\n deprecated: boolean = false,\n externalDocs: { url: string; description?: string } | null = null\n ) {\n this.#basePath = basePath;\n this.#pathTemplate = pathTemplate;\n this.#incomingSchema = incomingSchema;\n this.#outgoingSchema = outgoingSchema;\n this.#querySchema = querySchema;\n this.#headerSchema = headerSchema;\n this.#serviceSchemas = serviceSchemas;\n this.#authRoles = authRoles;\n this.#summary = summary;\n this.#description = description;\n this.#tags = tags;\n this.#operationId = operationId;\n this.#deprecated = deprecated;\n this.#externalDocs = externalDocs;\n }\n\n // -- Helper to clone with one field changed ---------------------------------\n\n #clone(\n overrides: Partial<{\n basePath: string;\n pathTemplate: RoutePath;\n incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n authRoles: readonly string[] | null;\n summary: string | null;\n description: string | null;\n tags: readonly string[];\n operationId: string | null;\n deprecated: boolean;\n externalDocs: { url: string; description?: string } | null;\n }>\n ): SubscriptionBuilder<any, any, any, any, any, any, any, any> {\n return new SubscriptionBuilder(\n overrides.basePath ?? this.#basePath,\n overrides.pathTemplate ?? this.#pathTemplate,\n overrides.incomingSchema !== undefined\n ? overrides.incomingSchema\n : this.#incomingSchema,\n overrides.outgoingSchema !== undefined\n ? overrides.outgoingSchema\n : this.#outgoingSchema,\n overrides.querySchema !== undefined\n ? overrides.querySchema\n : this.#querySchema,\n overrides.headerSchema !== undefined\n ? overrides.headerSchema\n : this.#headerSchema,\n overrides.serviceSchemas !== undefined\n ? overrides.serviceSchemas\n : this.#serviceSchemas,\n overrides.authRoles !== undefined\n ? overrides.authRoles\n : this.#authRoles,\n overrides.summary !== undefined ? overrides.summary : this.#summary,\n overrides.description !== undefined\n ? overrides.description\n : this.#description,\n overrides.tags ?? this.#tags,\n overrides.operationId !== undefined\n ? overrides.operationId\n : this.#operationId,\n overrides.deprecated ?? this.#deprecated,\n overrides.externalDocs !== undefined\n ? overrides.externalDocs\n : this.#externalDocs\n );\n }\n\n /**\n * Define the schema for client→server messages.\n * Messages that do not match this schema are rejected with an error frame.\n */\n incoming<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TSchema,\n TOutgoing\n > {\n return this.#clone({ incomingSchema: schema }) as any;\n }\n\n /**\n * Define the schema for server→client events.\n * All yielded values are validated against this schema before sending.\n */\n outgoing<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TSchema\n > {\n return this.#clone({ outgoingSchema: schema }) as any;\n }\n\n /** Define the query string schema for the WebSocket upgrade URL. */\n query<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n InferType<TSchema>,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ querySchema: schema }) as any;\n }\n\n /** Define expected headers on the WebSocket upgrade request. */\n headers<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n InferType<TSchema>,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ headerSchema: schema }) as any;\n }\n\n /** Declare DI services to be resolved per-connection and passed as the second handler argument. */\n inject<\n TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>\n >(\n schemas: TSchemas\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TSchemas,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ serviceSchemas: schemas }) as any;\n }\n\n /**\n * Mark this subscription as requiring authorization.\n *\n * Overloads:\n * - `authorize(principalSchema, ...roles)` — typed principal, optional role requirements\n * - `authorize(...roles)` — untyped principal (`unknown`), optional role requirements\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n InferType<TSchema>,\n TRoles,\n TIncoming,\n TOutgoing\n >;\n authorize(\n ...roles: TRoles[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n unknown,\n TRoles,\n TIncoming,\n TOutgoing\n >;\n authorize(\n ...args: unknown[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n any,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n const merged = this.#authRoles ? [...this.#authRoles, ...roles] : roles;\n return this.#clone({ authRoles: merged }) as any;\n }\n\n /**\n * Mark this subscription as public — no authentication required.\n *\n * Calling `.public()` sets `authRoles` to `null`, overriding any\n * previously set authorization requirements.\n */\n public(): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ authRoles: null }) as any;\n }\n\n /** Short, human-readable summary for documentation. */\n summary(\n text: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ summary: text }) as any;\n }\n\n /** Longer description for documentation. Supports Markdown. */\n description(\n text: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ description: text }) as any;\n }\n\n /** Documentation tags for grouping. */\n tags(\n ...tags: string[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ tags }) as any;\n }\n\n /** A unique, stable identifier for documentation. */\n operationId(\n id: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ operationId: id }) as any;\n }\n\n /** Mark this subscription as deprecated in documentation. */\n deprecated(): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ deprecated: true }) as any;\n }\n\n /** Link external documentation. */\n externalDocs(\n url: string,\n description?: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ externalDocs: { url, description } }) as any;\n }\n\n /** Return an immutable snapshot of this builder's configuration as {@link SubscriptionMetadata}. */\n introspect(): SubscriptionMetadata {\n return {\n protocol: 'subscription',\n basePath: this.#basePath,\n pathTemplate: this.#pathTemplate,\n incomingSchema: this.#incomingSchema,\n outgoingSchema: this.#outgoingSchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema,\n serviceSchemas: this.#serviceSchemas,\n authRoles: this.#authRoles,\n summary: this.#summary,\n description: this.#description,\n tags: this.#tags,\n operationId: this.#operationId,\n deprecated: this.#deprecated,\n externalDocs: this.#externalDocs\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Subscription handler mapping types\n// ---------------------------------------------------------------------------\n\ntype AnySubscription = SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\n\n/**\n * A single handler entry for a subscription in a handler map.\n *\n * Either a bare handler function or an object with a `handler` function\n * and optional per-subscription `middlewares`.\n */\nexport type SubscriptionHandlerEntry<E> =\n | SubscriptionHandler<E>\n | { handler: SubscriptionHandler<E>; middlewares?: Middleware[] };\n\n/**\n * Create a subscription endpoint builder.\n * @internal — used by the endpoint factory.\n */\nexport function createSubscription<TParams>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n): SubscriptionBuilder<TParams extends undefined ? {} : TParams>;\n\nexport function createSubscription(\n basePath: string,\n pathTemplate?: RoutePath\n): SubscriptionBuilder<any> {\n return new SubscriptionBuilder(basePath, pathTemplate ?? '/');\n}\n\n/**\n * Runtime check: is this endpoint builder a subscription?\n * @internal\n */\nexport function isSubscriptionBuilder(\n value: unknown\n): value is AnySubscription {\n return value instanceof SubscriptionBuilder;\n}\n\n/**\n * Runtime check: is this metadata from a subscription?\n * @internal\n */\nexport function isSubscriptionMetadata(\n value: unknown\n): value is SubscriptionMetadata {\n return (\n value !== null &&\n typeof value === 'object' &&\n (value as any).protocol === 'subscription'\n );\n}\n","// biome-ignore-all lint/suspicious/useAdjacentOverloadSignatures: each method in ScopedEndpointFactoryMethods and EndpointFactory has a single signature; they are separate methods, not overloads\nimport type {\n InferType,\n ObjectSchemaBuilder,\n ParseStringSchemaBuilder,\n PropertyDescriptorTree,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport { SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR } from '@cleverbrush/schema';\nimport type {\n ActionResult,\n ContentResult,\n FileResult,\n JsonResult,\n NoContentResult,\n RawResult,\n RedirectResult,\n StatusCodeResult,\n StreamResult\n} from './ActionResult.js';\nimport type { CacheTagDefinition } from './CacheTag.js';\nimport { createCacheTagTree, serializeTag } from './CacheTag.js';\nimport type { RequestContext } from './RequestContext.js';\nimport {\n createSubscription,\n isSubscriptionBuilder,\n type SubscriptionBuilder,\n type SubscriptionHandlerEntry\n} from './Subscription.js';\nimport type {\n FilePart,\n Middleware,\n RejectedFile,\n UploadOptions\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Simplify — flattens intersection types for clean IDE tooltips\n// ---------------------------------------------------------------------------\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// ---------------------------------------------------------------------------\n// ActionContext — assembles the typed argument for a handler\n// ---------------------------------------------------------------------------\n\ntype HasKeys<T> = keyof T extends never ? false : true;\n\ntype ActionContextParts<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TPrincipal,\n TUpload extends boolean\n> = {\n context: RequestContext;\n} & (HasKeys<TParams> extends true ? { params: TParams } : {}) &\n (TBody extends undefined\n ? {}\n : {\n body: TBody extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TBody>\n : TBody;\n }) &\n (HasKeys<TQuery> extends true ? { query: TQuery } : {}) &\n (HasKeys<THeaders> extends true ? { headers: THeaders } : {}) &\n (TPrincipal extends undefined ? {} : { principal: TPrincipal }) &\n (TUpload extends true\n ? { files: Record<string, FilePart>; rejectedFiles?: RejectedFile[] }\n : {});\n\n/**\n * The fully-typed argument object passed to endpoint handlers.\n *\n * The shape is inferred from the `EndpointBuilder` chain — only the keys\n * actually configured (body, query, headers, params, principal) are present.\n */\nexport type ActionContext<E> =\n E extends EndpointBuilder<\n infer TParams,\n infer TBody,\n infer TQuery,\n infer THeaders,\n any,\n infer TPrincipal,\n any,\n any,\n any,\n infer TUpload\n >\n ? Simplify<\n ActionContextParts<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TPrincipal,\n TUpload\n >\n >\n : never;\n\n// ---------------------------------------------------------------------------\n// InferServices — maps { name: SchemaBuilder } to { name: InferType<Schema> }\n// ---------------------------------------------------------------------------\n\ntype InferServices<T> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : never;\n};\n\n/**\n * Extracts the injected service schemas map from an `EndpointBuilder` type.\n * Used internally by the `Handler` type to derive the `services` argument.\n */\nexport type ServiceSchemas<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n infer TServices,\n any,\n any,\n any,\n any,\n any\n >\n ? TServices\n : {};\n\ntype ResponseType<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TResponse,\n any,\n any\n >\n ? TResponse extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TResponse>\n : TResponse\n : any;\n\n/**\n * Extracts the `TResponses` map from an `EndpointBuilder` type.\n * `TResponses` is a `Record<number, BodyType>` inferred from `.responses()`.\n */\nexport type ResponsesOf<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TResponses,\n any\n >\n ? TResponses\n : never;\n\ntype HasResponses<E> = keyof ResponsesOf<E> extends never ? false : true;\n\n/**\n * The union of permitted return values for a handler whose endpoint\n * declared `.responses()`. Each member corresponds to one declared code:\n *\n * - `null` schema (e.g. 204) → `NoContentResult` for 204, `StatusCodeResult<K>` otherwise\n * - Non-null schema for code 200 → also allows a plain object (treated as 200 by the server)\n * - Non-null schema for other codes → `JsonResult<K, Body>`\n *\n * `FileResult`, `StreamResult`, `ContentResult`, `RedirectResult`, and\n * `RawResult` are always permitted as escape hatches for non-JSON responses.\n */\nexport type AllowedResponseReturn<TResponses extends Record<number, any>> =\n | {\n [K in keyof TResponses & number]: TResponses[K] extends null\n ? K extends 204\n ? NoContentResult\n : StatusCodeResult<K>\n : JsonResult<K, TResponses[K]>;\n }[keyof TResponses & number]\n | (200 extends keyof TResponses\n ? TResponses[200] extends null\n ? never\n : TResponses[200]\n : never)\n | FileResult\n | StreamResult\n | ContentResult\n | RedirectResult\n | RawResult;\n\n// ---------------------------------------------------------------------------\n// Handler — the action function type, inferred from an endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * The handler function type inferred from an `EndpointBuilder`.\n *\n * When the endpoint has injected services, the handler receives a second\n * `services` argument with all resolved service instances.\n */\ntype HandlerReturn<E> =\n HasResponses<E> extends true\n ? AllowedResponseReturn<ResponsesOf<E>>\n : ResponseType<E> | ActionResult;\n\nexport type Handler<E> =\n HasKeys<ServiceSchemas<E>> extends true\n ? (\n arg: ActionContext<E>,\n services: Simplify<InferServices<ServiceSchemas<E>>>\n ) => HandlerReturn<E> | Promise<HandlerReturn<E>>\n : (\n arg: ActionContext<E>\n ) => HandlerReturn<E> | Promise<HandlerReturn<E>>;\n\n// ---------------------------------------------------------------------------\n// Handler mapping — compile-time complete endpoint → handler binding\n// ---------------------------------------------------------------------------\n\ntype AnyEndpoint = EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\ntype AnySubscriptionBuilder = SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\n\n/**\n * A single handler entry in a {@link HandlerMap}.\n *\n * Either a bare handler function or an object with a `handler` function and\n * optional per-endpoint `middlewares`.\n */\nexport type HandlerEntry<E> =\n | Handler<E>\n | { handler: Handler<E>; middlewares?: Middleware[] };\n\n/**\n * A compile-time complete mapping from an endpoint group structure to\n * handler entries. Every endpoint key in every group must have a\n * corresponding {@link HandlerEntry} — omitting one is a type error.\n *\n * Subscription endpoints (created via `endpoint.subscription()`) are\n * mapped to {@link SubscriptionHandlerEntry} instead of {@link HandlerEntry}.\n *\n * @typeParam TEndpoints - A record of groups, each a record of endpoint\n * builders. Typically the return type of `defineApi()` with server-side\n * extensions applied.\n */\nexport type HandlerMap<TEndpoints> = {\n [G in keyof TEndpoints]: {\n [E in keyof TEndpoints[G]]: TEndpoints[G][E] extends AnySubscriptionBuilder\n ? SubscriptionHandlerEntry<TEndpoints[G][E]>\n : TEndpoints[G][E] extends AnyEndpoint\n ? HandlerEntry<TEndpoints[G][E]>\n : never;\n };\n};\n\n/**\n * The opaque result of {@link mapHandlers}. Passed to\n * `ServerBuilder.handleAll()` to register every endpoint at once.\n */\nexport interface HandlerMapping {\n /** @internal */\n readonly _entries: ReadonlyArray<{\n endpoint: AnyEndpoint;\n handler: (...args: any[]) => any;\n middlewares?: Middleware[];\n }>;\n /** @internal */\n readonly _subscriptions: ReadonlyArray<{\n endpoint: AnySubscriptionBuilder;\n handler: (...args: any[]) => any;\n middlewares?: Middleware[];\n }>;\n}\n\n/**\n * Binds a complete set of endpoint builders to their handlers with\n * compile-time exhaustiveness checking.\n *\n * TypeScript will report an error if any endpoint in `endpoints` is\n * missing from `handlers`, or if a handler's signature does not match\n * its endpoint. This is analogous to how `@cleverbrush/mapper` tracks\n * unmapped properties at the type level.\n *\n * @param endpoints - A grouped object of extended endpoint builders\n * (e.g. after `.authorize()` / `.inject()`).\n * @param handlers - A matching grouped object of handler functions or\n * `{ handler, middlewares }` entries.\n * @returns A {@link HandlerMapping} to pass to `ServerBuilder.handleAll()`.\n *\n * @example\n * ```ts\n * const mapping = mapHandlers(endpoints, {\n * auth: {\n * register: registerHandler,\n * login: loginHandler,\n * },\n * todos: {\n * list: listTodosHandler,\n * create: createTodoHandler,\n * export: { handler: exportHandler, middlewares: [auditLog] },\n * },\n * });\n *\n * server.handleAll(mapping);\n * ```\n */\nexport function mapHandlers<\n TEndpoints extends Record<\n string,\n Record<string, AnyEndpoint | AnySubscriptionBuilder>\n >\n>(endpoints: TEndpoints, handlers: HandlerMap<TEndpoints>): HandlerMapping {\n const entries: HandlerMapping['_entries'][number][] = [];\n const subscriptions: HandlerMapping['_subscriptions'][number][] = [];\n\n for (const groupKey of Object.keys(endpoints)) {\n const group = endpoints[groupKey]!;\n const handlerGroup = (handlers as any)[groupKey];\n\n for (const endpointKey of Object.keys(group)) {\n const ep = group[endpointKey]!;\n const entry = handlerGroup[endpointKey];\n\n const handler = typeof entry === 'function' ? entry : entry.handler;\n const middlewares =\n typeof entry === 'function' ? undefined : entry.middlewares;\n\n if (isSubscriptionBuilder(ep)) {\n subscriptions.push({ endpoint: ep, handler, middlewares });\n } else {\n entries.push({\n endpoint: ep as AnyEndpoint,\n handler,\n middlewares\n });\n }\n }\n }\n\n return { _entries: entries, _subscriptions: subscriptions };\n}\n\n// ---------------------------------------------------------------------------\n// EndpointBuilder — immutable builder for endpoint definitions\n// ---------------------------------------------------------------------------\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/**\n * Lightweight recursive property reference tree for type-safe runtime\n * expression building in `.links()` and `.callbacks()`.\n *\n * At runtime the actual value is a `PropertyDescriptorTree` from\n * `ObjectSchemaBuilder.getPropertiesFor()`, which provides `toJsonPointer()`\n * and the `SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR` marker used by the OpenAPI\n * generator to resolve path expressions automatically.\n */\nexport type PropertyRefTree<T> = {\n readonly [K in keyof T]-?: PropertyRefTree<T[K]>;\n};\n\n/**\n * A link from a response to a follow-up operation.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#link-object\n */\nexport interface LinkDefinition<TResponse = any> {\n /** `operationId` of the target operation. */\n readonly operationId: string;\n /**\n * Map of target parameter names to values.\n *\n * - `Record<string, string>` — raw OpenAPI runtime expressions such as\n * `'$response.body#/id'`.\n * - Callback `(response: PropertyRefTree<TResponse>) => Record<string, unknown>` —\n * type-safe selector; properties accessed on `response` are converted to\n * `$response.body#/<pointer>` expressions automatically.\n */\n readonly parameters?:\n | Record<string, string>\n | ((\n response: TResponse extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any\n >\n ? PropertyDescriptorTree<TResponse, TResponse>\n : PropertyRefTree<any>\n ) => Record<string, unknown>);\n /** Human-readable description of the link relationship. */\n readonly description?: string;\n /** Runtime expression or literal for the linked operation's request body. */\n readonly requestBody?: string;\n}\n\n/**\n * A callback declaration for async request/response patterns.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#callback-object\n */\nexport interface CallbackDefinition<TBody = any> {\n /**\n * Raw OpenAPI runtime expression for the callback URL,\n * e.g. `'{$request.body#/callbackUrl}'`.\n * Mutually exclusive with `urlFrom`.\n */\n readonly expression?: string;\n /**\n * Type-safe selector for the request body field holding the callback URL.\n * The generator converts the selected property to a\n * `'{$request.body#/<pointer>}'` expression automatically.\n * Mutually exclusive with `expression`.\n */\n readonly urlFrom?: (\n body: TBody extends ObjectSchemaBuilder<any, any, any, any, any>\n ? PropertyDescriptorTree<TBody, TBody>\n : PropertyRefTree<any>\n ) => unknown;\n /** HTTP method for the callback request (default: `'POST'`). */\n readonly method?: string;\n /** Short summary of the callback operation. */\n readonly summary?: string;\n /** Detailed description of the callback operation. */\n readonly description?: string;\n /** Request body schema for the callback payload. */\n readonly body?: SchemaBuilder<any, any, any, any, any>;\n /** Response schema expected from the callback consumer. */\n readonly response?: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Snapshot of all configuration set on an `EndpointBuilder`.\n * Used by the server for routing and by `@cleverbrush/server-openapi` for\n * spec generation.\n */\nexport interface EndpointMetadata {\n readonly method: string;\n readonly basePath: string;\n readonly pathTemplate: RoutePath;\n readonly bodySchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n /**\n * Authorization roles required for this endpoint.\n * - `null` → no authorization required (public)\n * - `[]` → any authenticated user\n * - `['admin', ...]` → user must have at least one of these roles\n */\n readonly authRoles: readonly string[] | null;\n readonly summary: string | null;\n readonly description: string | null;\n readonly tags: readonly string[];\n readonly operationId: string | null;\n readonly deprecated: boolean;\n readonly responseSchema: SchemaBuilder<any, any, any, any, any> | null;\n /**\n * Per-status-code response schemas declared via `.responses()`.\n * When non-null, takes precedence over `responseSchema` for OpenAPI generation\n * and constrains the handler return type to the declared codes.\n * A `null` schema value means the response has no body (e.g. 204).\n */\n readonly responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null;\n /**\n * A single example value for the request body, emitted as `example` on the\n * OpenAPI Media Type Object.\n */\n readonly example: unknown | null;\n /**\n * A map of named examples for the request body, emitted as `examples` on the\n * OpenAPI Media Type Object. Each entry follows the OpenAPI Example Object shape.\n */\n readonly examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null;\n /**\n * When set, the endpoint produces a binary file response instead of JSON.\n * The OpenAPI spec will emit the appropriate binary content type.\n */\n readonly producesFile: {\n contentType?: string;\n description?: string;\n } | null;\n /**\n * Multiple response content types for content-negotiated endpoints.\n * Keys are MIME types; an optional `schema` overrides the default response\n * schema for that content type. When set alongside `.producesFile()`,\n * `producesFile` takes precedence.\n */\n readonly produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null;\n /**\n * Schema describing response headers emitted on every response code.\n * Each property in the object schema becomes a header name with its\n * sub-schema and optional description.\n */\n readonly responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n /**\n * External documentation URL for this operation, emitted as `externalDocs`\n * on the OpenAPI Operation Object.\n */\n readonly externalDocs: { url: string; description?: string } | null;\n /**\n * Response links declared via `.links()`, emitted under the primary\n * success response's `links` map in the OpenAPI spec.\n */\n readonly links: Record<string, LinkDefinition> | null;\n /**\n * Callbacks declared via `.callbacks()`, emitted as `callbacks` on the\n * OpenAPI Operation Object.\n */\n readonly callbacks: Record<string, CallbackDefinition> | null;\n /**\n * When set, the endpoint accepts `multipart/form-data` uploads.\n * The configuration controls max file size, allowed MIME types, etc.\n * @see `EndpointBuilder.upload()`\n */\n readonly fileUpload: UploadOptions | null;\n /**\n * Cache tags declared via `.clearsCacheTag()`, providing tag-based cache\n * key computation for the client middleware.\n */\n readonly cacheTags: readonly CacheTagDefinition[];\n}\n\n/**\n * Immutable, fluent builder for HTTP endpoint definitions.\n *\n * All methods return a new builder instance — the original is never mutated.\n * Use the {@link endpoint} singleton (or {@link createEndpoints}) to obtain\n * the first builder in the chain.\n *\n * @example\n * ```ts\n * const GetUser = endpoint\n * .get('/api/users')\n * .query(object({ id: number().coerce() }))\n * .authorize(UserPrincipal, 'admin')\n * .returns(UserSchema)\n * .summary('Get a user by ID');\n * ```\n */\n/**\n * Infers the per-code body type map from a `.responses()` schema map.\n * Each schema maps to its `InferType`; a `null` schema maps to `null`\n * (meaning the response has no body).\n */\ntype InferResponsesMap<\n T extends Record<number, SchemaBuilder<any, any, any, any, any> | null>\n> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : null;\n};\n\n// ---------------------------------------------------------------------------\n// Cache-tag selector type — gives the consumer IDE hints when selecting\n// properties from the tree passed to the `.clearsCacheTag()` callback.\n// ---------------------------------------------------------------------------\n\n/**\n * A leaf node in a cache-tag property tree — mirrors the shape of the\n * actual runtime {@link PropertyDescriptor} so the compiler accepts\n * values selected by the consumer.\n */\ninterface CacheTagPropertyLeaf {\n readonly [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n readonly getValue: (obj: Record<string, unknown>) => {\n readonly value?: unknown;\n readonly success: boolean;\n };\n };\n}\n\n/** Recursively builds a typed property tree from an inferred object shape. */\ntype CacheTagPropertyTree<T> = CacheTagPropertyLeaf &\n (T extends Record<string, unknown>\n ? { readonly [K in keyof T]-?: CacheTagPropertyTree<T[K]> }\n : unknown);\n\n/**\n * The typed tree passed to the `.clearsCacheTag(name, selector)` callback.\n *\n * `p.params`, `p.query`, and `p.headers` provide IDE completion for\n * each schema's property names, while `p.body` resolves through the\n * body schema's `InferType`.\n */\ntype CacheTagSelector<TParams, TBody, TQuery, THeaders> = {\n readonly params: [keyof TParams] extends [never]\n ? Record<string, never>\n : TParams extends Record<string, unknown>\n ? CacheTagPropertyTree<TParams>\n : Record<string, never>;\n readonly body: TBody extends undefined\n ? undefined\n : TBody extends SchemaBuilder<any, any, any, any, any>\n ? CacheTagPropertyTree<InferType<TBody>>\n : Record<string, never>;\n readonly query: [keyof TQuery] extends [never]\n ? Record<string, never>\n : TQuery extends Record<string, unknown>\n ? CacheTagPropertyTree<TQuery>\n : Record<string, never>;\n readonly headers: [keyof THeaders] extends [never]\n ? Record<string, never>\n : THeaders extends Record<string, unknown>\n ? CacheTagPropertyTree<THeaders>\n : Record<string, never>;\n};\n\nexport class EndpointBuilder<\n TParams = {},\n TBody = undefined,\n TQuery = {},\n THeaders = {},\n TServices = {},\n TPrincipal = undefined,\n TRoles extends string = string,\n TResponse = any,\n TResponses extends Record<number, any> = {},\n TUpload extends boolean = false\n> {\n readonly #method: string;\n readonly #basePath: string;\n readonly #pathTemplate: RoutePath;\n readonly #bodySchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly #authRoles: readonly string[] | null;\n readonly #summary: string | null;\n readonly #description: string | null;\n readonly #tags: readonly string[];\n readonly #operationId: string | null;\n readonly #deprecated: boolean;\n readonly #responseSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null;\n readonly #example: unknown | null;\n readonly #examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null;\n readonly #producesFile: {\n contentType?: string;\n description?: string;\n } | null;\n readonly #produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null;\n readonly #responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #externalDocs: { url: string; description?: string } | null;\n readonly #links: Record<string, LinkDefinition> | null;\n readonly #callbacks: Record<string, CallbackDefinition> | null;\n readonly #fileUpload: UploadOptions | null;\n readonly #cacheTags: readonly CacheTagDefinition[];\n\n constructor(\n method: string,\n basePath: string,\n pathTemplate: RoutePath,\n bodySchema: SchemaBuilder<any, any, any, any, any> | null,\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null,\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null,\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null = null,\n authRoles: readonly string[] | null = null,\n summary: string | null = null,\n description: string | null = null,\n tags: readonly string[] = [],\n operationId: string | null = null,\n deprecated: boolean = false,\n responseSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null = null,\n example: unknown | null = null,\n examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null = null,\n producesFile: {\n contentType?: string;\n description?: string;\n } | null = null,\n produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null = null,\n responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n externalDocs: { url: string; description?: string } | null = null,\n links: Record<string, LinkDefinition> | null = null,\n callbacks: Record<string, CallbackDefinition> | null = null,\n fileUpload: UploadOptions | null = null,\n cacheTags: readonly CacheTagDefinition[] = []\n ) {\n this.#method = method;\n this.#basePath = basePath;\n this.#pathTemplate = pathTemplate;\n this.#bodySchema = bodySchema;\n this.#querySchema = querySchema;\n this.#headerSchema = headerSchema;\n this.#serviceSchemas = serviceSchemas;\n this.#authRoles = authRoles;\n this.#summary = summary;\n this.#description = description;\n this.#tags = tags;\n this.#operationId = operationId;\n this.#deprecated = deprecated;\n this.#responseSchema = responseSchema;\n this.#responsesSchemas = responsesSchemas;\n this.#example = example;\n this.#examples = examples;\n this.#producesFile = producesFile;\n this.#produces = produces;\n this.#responseHeaderSchema = responseHeaderSchema;\n this.#externalDocs = externalDocs;\n this.#links = links;\n this.#callbacks = callbacks;\n this.#fileUpload = fileUpload;\n this.#cacheTags = cacheTags;\n }\n\n /** Define the request body schema. Validation failures return 422 Problem Details. */\n body<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TSchema,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n schema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Define the query string schema (must be an object schema). Validation failures return 422. */\n query<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n InferType<TSchema>,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n schema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Define an expected request headers schema (must be an object schema). */\n headers<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n InferType<TSchema>,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n schema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Declare DI services to be resolved per-request and passed as the second handler argument. */\n inject<\n TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>\n >(\n schemas: TSchemas\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TSchemas,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n schemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as requiring authorization.\n *\n * Overloads:\n * - `authorize(principalSchema, ...roles)` — typed principal, optional role requirements\n * - `authorize(...roles)` — untyped principal (`unknown`), optional role requirements\n *\n * If no roles are specified, any authenticated user is allowed.\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n InferType<TSchema>,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n >;\n authorize(\n ...roles: TRoles[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n unknown,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n >;\n authorize(\n ...args: unknown[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n any,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n // First argument is a schema — remaining are roles\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n\n // Merge with inherited auth roles\n const merged = this.#authRoles ? [...this.#authRoles, ...roles] : roles;\n\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n merged,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as public — no authentication required.\n *\n * Calling `.public()` sets `authRoles` to `null`, overriding any\n * previously set authorization requirements (from `.authorize()` or\n * an inherited scoped factory).\n */\n public(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n null,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare the response type for OpenAPI spec generation.\n *\n * Overloads:\n * - `returns<T>()` — generic type only, no runtime schema\n * - `returns(schema)` — provides a schema for spec generation and type inference\n */\n returns<T>(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n T,\n TResponses,\n TUpload\n >;\n returns<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TSchema,\n TResponses,\n TUpload\n >;\n returns(\n _schema?: unknown\n ): EndpointBuilder<any, any, any, any, any, any, any, any, any, any> {\n const schema =\n _schema != null &&\n typeof _schema === 'object' &&\n 'introspect' in _schema\n ? (_schema as SchemaBuilder<any, any, any, any, any>)\n : null;\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n schema ?? this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare per-status-code response schemas for OpenAPI generation and\n * handler return-type enforcement.\n *\n * Pass `null` as the schema for body-less codes (e.g. 204).\n *\n * @example\n * ```ts\n * const ep = endpoint\n * .get('/api/todos/:id')\n * .responses({\n * 200: object({ id: number(), title: string() }),\n * 404: object({ message: string() }),\n * });\n *\n * const handler: Handler<typeof ep> = ({ params }) => {\n * const todo = todos.get(params.id);\n * if (!todo) return ActionResult.notFound({ message: 'Not found' });\n * return todo; // plain object → 200\n * };\n * ```\n */\n responses<\n const T extends Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n >\n >(\n map: T\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n InferResponsesMap<T>,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n map,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Short, human-readable summary for OpenAPI operation objects. */\n summary(\n text: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n text,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Longer description for OpenAPI operation objects. Supports Markdown. */\n description(\n text: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n text,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** OpenAPI tags grouping this operation in generated documentation. */\n tags(\n ...tags: string[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** A unique, stable identifier for this operation in OpenAPI spec. */\n operationId(\n id: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n id,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Mark this endpoint as deprecated in OpenAPI spec output. */\n deprecated(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n true,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Provide a single example value for the request body.\n *\n * Emitted as the `example` field on the OpenAPI Media Type Object\n * (`application/json`). Pre-fills the \"Try it out\" panel in Swagger UI.\n *\n * @param value - An example request body value.\n */\n example(\n value: TBody\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n value,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Provide named examples for the request body.\n *\n * Each entry is emitted under the `examples` map of the OpenAPI Media Type\n * Object following the Example Object shape (`{ summary?, description?, value }`).\n *\n * @param map - A record of named examples.\n */\n examples(\n map: Record<\n string,\n { summary?: string; description?: string; value: TBody }\n >\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n map,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare that this endpoint produces a binary file response.\n *\n * When set, the OpenAPI spec emits a binary content type instead of a JSON\n * schema for the success response. Takes precedence over `.returns()`.\n *\n * @param contentType - MIME type (default: `'application/octet-stream'`).\n * @param description - Optional response description for the spec.\n */\n producesFile(\n contentType?: string,\n description?: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n { contentType, description },\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as accepting `multipart/form-data` file uploads.\n *\n * When set, the server parses the request body with a streaming multipart\n * parser instead of the default JSON deserializer. File fields are made\n * available to the handler via `arg.files` (a `Record<string, FilePart>`),\n * while non-file form fields are validated against the body schema and\n * available via `arg.body`.\n *\n * @param options - Upload configuration (max file size, allowed MIME types, etc.).\n *\n * @example\n * ```ts\n * const UploadAvatar = endpoint\n * .post('/api/avatar')\n * .upload({ maxFileSize: 2 * 1024 * 1024, allowedMimeTypes: ['image/*'] })\n * .authorize(PrincipalSchema)\n * .responses({ 200: AvatarSchema });\n *\n * const handler: Handler<typeof UploadAvatar> = async ({ files }) => {\n * const avatar = files['avatar'];\n * // avatar: { filename, mimeType, buffer, size }\n * };\n * ```\n */\n upload(\n options?: UploadOptions\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n true\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n {\n maxFileSize: options?.maxFileSize ?? 10 * 1024 * 1024,\n allowedMimeTypes: options?.allowedMimeTypes,\n maxFileCount: options?.maxFileCount ?? 10\n },\n this.#cacheTags\n );\n }\n\n /**\n * The full path for this endpoint, combining `basePath` and `pathTemplate`.\n *\n * For static routes this is the exact URL path (e.g. `\"/todos\"`).\n * For dynamic routes the template placeholders are included\n * (e.g. `\"/todos/:id\"`).\n */\n get path(): string {\n const base = this.#basePath;\n const tpl = this.#pathTemplate;\n let suffix: string;\n if (typeof tpl === 'string') {\n suffix = tpl;\n } else {\n const { literals, segments } = tpl.introspect().templateDefinition;\n let s = '';\n for (let i = 0; i < segments.length; i++) {\n s += literals[i] + `:${segments[i].path}`;\n }\n s += literals[segments.length] ?? '';\n suffix = s;\n }\n if (suffix === '/') return base || '/';\n return base + suffix;\n }\n\n /** Return an immutable snapshot of this builder's configuration as {@link EndpointMetadata}. */\n introspect(): EndpointMetadata {\n return {\n method: this.#method,\n basePath: this.#basePath,\n pathTemplate: this.#pathTemplate,\n bodySchema: this.#bodySchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema,\n serviceSchemas: this.#serviceSchemas,\n authRoles: this.#authRoles,\n summary: this.#summary,\n description: this.#description,\n tags: this.#tags,\n operationId: this.#operationId,\n deprecated: this.#deprecated,\n responseSchema: this.#responseSchema,\n responsesSchemas: this.#responsesSchemas,\n example: this.#example,\n examples: this.#examples,\n producesFile: this.#producesFile,\n produces: this.#produces,\n responseHeaderSchema: this.#responseHeaderSchema,\n externalDocs: this.#externalDocs,\n links: this.#links,\n callbacks: this.#callbacks,\n fileUpload: this.#fileUpload,\n cacheTags: this.#cacheTags\n };\n }\n\n /**\n * Declare that this endpoint can produce responses in multiple content types.\n *\n * Each key is a MIME type. Provide an optional `schema` to override the\n * default response schema for that type; otherwise the schema from\n * `.returns()` / `.responses()` is reused for every declared content type.\n *\n * When used alongside `.producesFile()`, the binary response takes precedence.\n *\n * @param contentTypes - Map of MIME type → optional schema override.\n *\n * @example\n * ```ts\n * endpoint.get('/api/items')\n * .returns(object({ id: number(), name: string() }))\n * .produces({\n * 'text/csv': {},\n * 'application/xml': { schema: string() }\n * })\n * ```\n */\n produces(\n contentTypes: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n >\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n contentTypes,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare response headers emitted by this endpoint.\n *\n * The object schema's properties become header names in the OpenAPI spec;\n * each property's sub-schema and description are emitted in the `headers`\n * map on every response code.\n *\n * @param schema - Object schema whose properties describe the response headers.\n *\n * @example\n * ```ts\n * endpoint.get('/api/items')\n * .responseHeaders(object({\n * 'X-Total-Count': number().describe('Total number of items'),\n * 'X-Page': number()\n * }))\n * ```\n */\n responseHeaders<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n schema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Link external documentation to this operation.\n *\n * Emitted as the `externalDocs` field on the OpenAPI Operation Object.\n *\n * @param url - The URL to the external documentation.\n * @param description - Optional short description of the external docs.\n */\n externalDocs(\n url: string,\n description?: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n { url, description },\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare response links for OpenAPI spec generation.\n *\n * Links describe follow-up actions that can be taken based on the response,\n * emitted under the primary 2xx response's `links` map.\n *\n * @param defs - Record mapping link names to {@link LinkDefinition} objects.\n *\n * @example\n * ```ts\n * endpoint.get('/api/users/:id')\n * .returns(UserSchema)\n * .links({\n * GetUser: {\n * operationId: 'getUser',\n * parameters: (r) => ({ id: r.id }),\n * },\n * })\n * ```\n */\n links(\n defs: Record<string, LinkDefinition<TResponse>>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n defs as Record<string, LinkDefinition>,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare callbacks for OpenAPI spec generation.\n *\n * Callbacks describe async out-of-band requests that may be sent to a URL\n * provided in the request body, emitted as the `callbacks` field on the\n * OpenAPI Operation Object.\n *\n * @param defs - Record mapping callback names to {@link CallbackDefinition} objects.\n *\n * @example\n * ```ts\n * endpoint.post('/api/subscriptions')\n * .body(object({ callbackUrl: string() }))\n * .callbacks({\n * onEvent: {\n * urlFrom: (b) => b.callbackUrl,\n * method: 'POST',\n * body: EventSchema,\n * },\n * })\n * ```\n */\n callbacks(\n defs: Record<string, CallbackDefinition<TBody>>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n defs as Record<string, CallbackDefinition>,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare a cache group for this endpoint.\n *\n * Use on GET / query endpoints to group responses into a named cache.\n * The client-side {@code cacheTags} middleware caches responses keyed\n * by this tag and flushes matching entries when a mutation calls\n * {@link clearsCacheTag}.\n *\n * @overload Simple tag (no properties — single cache entry).\n * @overload Tag with property descriptors for fine-grained keys.\n *\n * @example\n * ```ts\n * // GET — responses cached under \"todo\" group, keyed by id\n * endpoint.get('/api/todos/:id')\n * .cacheTag('todo', p => ({\n * id: p.params.id\n * }))\n * ```\n */\n cacheTag(\n name: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n cacheTag(\n name: string,\n selector: (\n tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>\n ) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n cacheTag(\n name: string,\n selector?: (tree: any) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n > {\n return this.clearsCacheTag(name, selector!);\n }\n\n /**\n * Declare which cache groups are cleared when this mutation succeeds.\n *\n * Use on POST / PUT / PATCH / DELETE endpoints. When the mutation\n * completes, the {@code cacheTags} client middleware invalidates all\n * cache entries matching the declared tag names (prefix match).\n *\n * @overload Simple tag (clears all entries prefixed with the name).\n * @overload Tag with property descriptors for targeted invalidation.\n *\n * @example\n * ```ts\n * // PATCH — clears \"todo-list\" and \"todo:id=42\" on success\n * endpoint.patch('/api/todos/:id')\n * .clearsCacheTag('todo-list')\n * .clearsCacheTag('todo', p => ({\n * id: p.params.id\n * }))\n * ```\n */\n clearsCacheTag(\n name: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n clearsCacheTag(\n name: string,\n selector: (\n tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>\n ) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n clearsCacheTag(\n name: string,\n selector?: (tree: any) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n > {\n if (!selector) {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n [...this.#cacheTags, { name, properties: {} }]\n );\n }\n\n const paramsSchema = extractParamsObjectSchema(this.#pathTemplate);\n\n const tree = createCacheTagTree({\n paramsSchema,\n bodySchema: this.#bodySchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema\n });\n\n const descriptors = selector(tree);\n\n if (typeof descriptors !== 'object' || descriptors === null) {\n throw new Error(\n `Cache tag \"${name}\": selector must return an object ` +\n `with property descriptors (e.g. { id: p.query.id }).`\n );\n }\n\n const definition = serializeTag(name, descriptors);\n\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n [...this.#cacheTags, definition]\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// endpoint factory — creates EndpointBuilder instances\n// ---------------------------------------------------------------------------\n\n/**\n * Optional OpenAPI metadata fields accepted by `createEndpoint` / `createEndpoints`.\n */\nexport type EndpointMetadataDescriptors = {\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: string[];\n readonly operationId?: string;\n readonly deprecated?: boolean;\n};\n\nfunction createEndpoint<TParams>(\n method: string,\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>,\n authRoles?: readonly string[] | null,\n meta?: EndpointMetadataDescriptors\n): EndpointBuilder<TParams, undefined, {}, {}, {}, any, string, any, {}>;\n\nfunction createEndpoint(\n method: string,\n basePath: string,\n pathTemplate?: RoutePath,\n authRoles?: readonly string[] | null,\n meta?: EndpointMetadataDescriptors\n): EndpointBuilder<any> {\n return new EndpointBuilder(\n method,\n basePath,\n pathTemplate ?? '/',\n null,\n null,\n null,\n null,\n authRoles ?? null,\n meta?.summary ?? null,\n meta?.description ?? null,\n meta?.tags ?? [],\n meta?.operationId ?? null,\n meta?.deprecated ?? false,\n null,\n null,\n null,\n null,\n null,\n null,\n null\n );\n}\n\n// ---------------------------------------------------------------------------\n// ScopedEndpointFactory — resource-scoped endpoint creation\n// ---------------------------------------------------------------------------\n\ntype ScopedEndpointFactoryMethods<\n TPrincipal,\n TRoles extends string = string\n> = {\n get<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n post<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n put<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n patch<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n delete<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n head<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n options<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n post<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n put<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n patch<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n delete<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n head<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n options<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n /** Returns factory methods where all endpoints are public (no auth). */\n public(): ScopedEndpointFactoryMethods<TPrincipal, TRoles>;\n};\n\nexport type ScopedEndpointFactory<TRoles extends string = string> =\n ScopedEndpointFactoryMethods<undefined, TRoles> & {\n /**\n * Returns a new resource factory where all endpoints inherit\n * the given authorization requirements.\n *\n * - `authorize(principalSchema, ...roles)` — typed principal\n * - `authorize(...roles)` — untyped principal\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): ScopedEndpointFactoryMethods<InferType<TSchema>, TRoles>;\n authorize(\n ...roles: TRoles[]\n ): ScopedEndpointFactoryMethods<unknown, TRoles>;\n /** Returns factory methods where all endpoints are public (no auth). */\n public(): ScopedEndpointFactoryMethods<TRoles>;\n };\n\nfunction createScopedFactoryMethods(\n basePath: string,\n authRoles: readonly string[] | null\n): ScopedEndpointFactoryMethods<any> {\n return {\n get: (pathTemplate?) =>\n createEndpoint('GET', basePath, pathTemplate, authRoles),\n post: (pathTemplate?) =>\n createEndpoint('POST', basePath, pathTemplate, authRoles),\n put: (pathTemplate?) =>\n createEndpoint('PUT', basePath, pathTemplate, authRoles),\n patch: (pathTemplate?) =>\n createEndpoint('PATCH', basePath, pathTemplate, authRoles),\n delete: (pathTemplate?) =>\n createEndpoint('DELETE', basePath, pathTemplate, authRoles),\n head: (pathTemplate?) =>\n createEndpoint('HEAD', basePath, pathTemplate, authRoles),\n options: (pathTemplate?) =>\n createEndpoint('OPTIONS', basePath, pathTemplate, authRoles),\n public: () => createScopedFactoryMethods(basePath, null)\n };\n}\n\nfunction createScopedFactory(basePath: string): ScopedEndpointFactory {\n return {\n ...createScopedFactoryMethods(basePath, null),\n authorize(...args: unknown[]): ScopedEndpointFactoryMethods<any> {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n return createScopedFactoryMethods(basePath, roles);\n },\n public(): ScopedEndpointFactoryMethods<any> {\n return createScopedFactoryMethods(basePath, null);\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// endpoint factory — creates EndpointBuilder instances\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts an ObjectSchemaBuilder from a ParseStringSchemaBuilder path template.\n * Used for constructing the synthetic cache tag tree.\n */\nfunction extractParamsObjectSchema(\n pathTemplate: RoutePath\n): ObjectSchemaBuilder<any, any, any, any, any, any, any> | null {\n if (\n pathTemplate &&\n typeof pathTemplate !== 'string' &&\n typeof (pathTemplate as any).introspect === 'function'\n ) {\n const info = (pathTemplate as any).introspect();\n if (info.objectSchema) {\n return info.objectSchema;\n }\n }\n return null;\n}\n\ntype EndpointFactory<TRoles extends string = string> = {\n get<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n post<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n put<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n patch<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n delete<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n head<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n options<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n resource(basePath: string): ScopedEndpointFactory<TRoles>;\n subscription<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): SubscriptionBuilder<TParams, {}, {}, {}, undefined, TRoles>;\n};\n\n/**\n * Create a role-constrained endpoint factory. Roles are defined as a plain\n * `as const` object whose *values* become the string-literal union accepted\n * by `authorize()`.\n *\n * @example\n * ```ts\n * const Roles = { admin: 'admin', editor: 'editor' } as const;\n * const ep = createEndpoints(Roles);\n * ep.get('/api/admin').authorize(IPrincipal, 'admin'); // ✓\n * ep.get('/api/admin').authorize(IPrincipal, 'typo'); // ✗ type error\n * ```\n */\nexport function createEndpoints<const T extends Record<string, string>>(\n _roles: T\n): EndpointFactory<T[keyof T]> {\n return endpoint as EndpointFactory<T[keyof T]>;\n}\n\n/**\n * The global endpoint factory singleton.\n *\n * Creates `EndpointBuilder` instances for each HTTP method. Use\n * {@link createEndpoints} to get a role-constrained version.\n *\n * @example\n * ```ts\n * import { endpoint } from '@cleverbrush/server';\n *\n * const GetUsers = endpoint.get('/api/users');\n * const CreateUser = endpoint.post('/api/users').body(CreateUserSchema);\n * ```\n */\nexport const endpoint: EndpointFactory = {\n get: (basePath, pathTemplate?) =>\n createEndpoint('GET', basePath, pathTemplate),\n post: (basePath, pathTemplate?) =>\n createEndpoint('POST', basePath, pathTemplate),\n put: (basePath, pathTemplate?) =>\n createEndpoint('PUT', basePath, pathTemplate),\n patch: (basePath, pathTemplate?) =>\n createEndpoint('PATCH', basePath, pathTemplate),\n delete: (basePath, pathTemplate?) =>\n createEndpoint('DELETE', basePath, pathTemplate),\n head: (basePath, pathTemplate?) =>\n createEndpoint('HEAD', basePath, pathTemplate),\n options: (basePath, pathTemplate?) =>\n createEndpoint('OPTIONS', basePath, pathTemplate),\n resource: createScopedFactory,\n subscription: (basePath, pathTemplate?) =>\n createSubscription(basePath, pathTemplate)\n};\n","import {\n type InferType,\n type ObjectSchemaBuilder,\n object,\n type ParseStringSchemaBuilder,\n type ParseStringTemplateTag,\n type PropertyDescriptor,\n type PropertyDescriptorTree,\n parseString,\n type SchemaBuilder\n} from '@cleverbrush/schema';\n\ntype RouteTemplateTag<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n> = (\n strings: TemplateStringsArray,\n ...selectors: Array<\n (\n tree: PropertyDescriptorTree<\n ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >,\n ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >,\n string | number | boolean | Date\n >\n ) => PropertyDescriptor<\n ObjectSchemaBuilder<TProps, true, false, undefined, false, {}, []>,\n any,\n any\n >\n >\n) => ParseStringSchemaBuilder<\n InferType<\n ObjectSchemaBuilder<TProps, true, false, undefined, false, {}, []>\n >\n>;\n\nfunction createRouteTag<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n>(props: TProps): RouteTemplateTag<TProps> {\n type TSchema = ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >;\n const objectSchema = object(props) as unknown as TSchema;\n\n return ((strings: TemplateStringsArray, ...selectors: any[]) =>\n parseString(objectSchema, ($t: ParseStringTemplateTag<TSchema>) =>\n ($t as any)(strings, ...selectors)\n )) as any;\n}\n\n// Overload: route`/some/path` — used directly as a tagged template (no params)\nexport function route(\n strings: TemplateStringsArray,\n ...selectors: never[]\n): ParseStringSchemaBuilder<\n InferType<ObjectSchemaBuilder<{}, true, false, undefined, false, {}, []>>\n>;\n\n// Overload: route() — called with no args, returns a tagged template (no params)\nexport function route(): RouteTemplateTag<{}>;\n\n// Overload: route({ id: number() }) — called with props, returns a tagged template\nexport function route<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n>(props: TProps): RouteTemplateTag<TProps>;\n\n/**\n * Concise shorthand for defining a typed path template.\n *\n * @example With parameters\n * ```ts\n * const TodoById = route({ id: number().coerce() })`/${t => t.id}`;\n * ```\n *\n * @example Static path (no parameters)\n * ```ts\n * const Path = route`/some/path`;\n * // or\n * const Path = route()`/some/path`;\n * ```\n *\n * @param propsOrStrings - Either a property map for typed path segments,\n * or a `TemplateStringsArray` when used directly as a tagged template.\n * @returns A `ParseStringSchemaBuilder`, or a tagged-template function\n * that produces one.\n */\nexport function route(propsOrStrings?: any, ..._rest: any[]): any {\n // route`/some/path` — called as tagged template directly\n if (\n propsOrStrings != null &&\n Array.isArray((propsOrStrings as TemplateStringsArray).raw)\n ) {\n return createRouteTag({})(propsOrStrings as TemplateStringsArray);\n }\n\n // route() or route({...})\n return createRouteTag(propsOrStrings ?? {});\n}\n","/**\n * Browser-safe entry point for `@cleverbrush/server`.\n *\n * Re-exports the endpoint builder, factory functions and type helpers\n * needed to **define** API contracts without pulling in the Node.js server\n * runtime. Use this entry point in shared packages that are consumed by\n * both the backend (Node.js) and the frontend (browser).\n *\n * @example\n * ```ts\n * import { defineApi, endpoint, route } from '@cleverbrush/server/contract';\n * import { array, number, object, string } from '@cleverbrush/schema';\n *\n * const TodoSchema = object({ id: number(), title: string() });\n *\n * export const api = defineApi({\n * todos: {\n * list: endpoint.resource('/api/todos').get()\n * .responses({ 200: array(TodoSchema) }),\n * get: endpoint.resource('/api/todos').get(\n * route({ id: number().coerce() })`/${t => t.id}`\n * ).responses({ 200: TodoSchema }),\n * },\n * });\n * ```\n *\n * @module\n */\n\nexport type {\n CacheTagDefinition,\n CacheTagPropertyAccessor\n} from './CacheTag.js';\nexport {\n type ActionContext,\n type AllowedResponseReturn,\n type CallbackDefinition,\n createEndpoints,\n EndpointBuilder,\n type EndpointMetadata,\n type EndpointMetadataDescriptors,\n endpoint,\n type Handler,\n type LinkDefinition,\n type PropertyRefTree,\n type ResponsesOf,\n type ScopedEndpointFactory\n} from './Endpoint.js';\nexport { route } from './route.js';\nexport {\n SubscriptionBuilder,\n type SubscriptionContext,\n type SubscriptionHandler,\n type SubscriptionHandlerEntry,\n type SubscriptionMetadata,\n type TrackedEvent,\n tracked\n} from './Subscription.js';\nexport type { FilePart, UploadOptions } from './types.js';\n\nimport type { EndpointBuilder as _EB } from './Endpoint.js';\nimport type { SubscriptionBuilder as _SB } from './Subscription.js';\n\n// ---------------------------------------------------------------------------\n// defineApi — typed, one-level API contract grouping\n// ---------------------------------------------------------------------------\n\n/**\n * A record of named {@link EndpointBuilder} or {@link SubscriptionBuilder}\n * instances that form a single logical API group (e.g. \"todos\", \"auth\", \"live\").\n */\nexport type ApiGroup = Record<\n string,\n | _EB<any, any, any, any, any, any, any, any, any>\n | _SB<any, any, any, any, any, any, any, any>\n>;\n\n/**\n * A typed API contract with one level of grouping.\n *\n * Each key is a group name and each value is an {@link ApiGroup} — a\n * record of named endpoints.\n *\n * @example\n * ```ts\n * const contract: ApiContract = {\n * todos: { list: todosResource.get(), create: todosResource.post() },\n * auth: { login: endpoint.post('/api/auth/login') },\n * };\n * ```\n */\nexport type ApiContract = Record<string, ApiGroup>;\n\n/**\n * Defines a typed API contract with one level of grouping.\n *\n * The returned object is the **single source of truth** for both server and\n * client. The server imports it and extends each endpoint with\n * `.authorize()`, `.inject()`, and OpenAPI metadata. The client passes it\n * to `createClient()` from `@cleverbrush/client` to obtain a fully typed HTTP\n * client.\n *\n * At runtime the contract (and each group within it) is frozen with\n * `Object.freeze` to prevent accidental mutation — endpoint builders are\n * immutable by design, so every `.body()` / `.query()` / etc. call already\n * returns a **new** builder.\n *\n * @typeParam T - The exact shape of the contract, inferred from the argument.\n * @param contract - A record of named groups, each containing named endpoints.\n * @returns The same object, frozen and typed as `Readonly<T>`.\n *\n * @example\n * ```ts\n * import { defineApi, endpoint, route } from '@cleverbrush/server/contract';\n * import { array, number, object, string } from '@cleverbrush/schema';\n *\n * const TodoSchema = object({ id: number(), title: string(), completed: boolean() });\n * const todosResource = endpoint.resource('/api/todos');\n * const ById = route({ id: number().coerce() })`/${t => t.id}`;\n *\n * export const api = defineApi({\n * todos: {\n * list: todosResource.get()\n * .query(object({ page: number().optional(), limit: number().optional() }))\n * .responses({ 200: array(TodoSchema) }),\n * get: todosResource.get(ById)\n * .responses({ 200: TodoSchema }),\n * create: todosResource.post()\n * .body(object({ title: string() }))\n * .responses({ 201: TodoSchema }),\n * },\n * auth: {\n * login: endpoint.post('/api/auth/login')\n * .body(object({ email: string(), password: string() }))\n * .responses({ 200: object({ token: string() }) }),\n * },\n * });\n * ```\n */\nexport function defineApi<T extends ApiContract>(contract: T): Readonly<T> {\n for (const group of Object.values(contract)) {\n Object.freeze(group);\n }\n return Object.freeze(contract);\n}\n\n// ---------------------------------------------------------------------------\n// Contract composition utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Computes the merged type of two {@link ApiContract} objects.\n *\n * - Groups that only exist in `A` are kept as-is.\n * - Groups that only exist in `B` are kept as-is.\n * - Groups whose key appears in **both** `A` and `B` have their endpoint maps\n * intersected (`A[K] & B[K]`), making all endpoints from both sources\n * visible on the merged group.\n */\nexport type MergedContracts<A extends ApiContract, B extends ApiContract> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] & B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Merges two API contracts into one.\n *\n * This is the primary building block for **audience-scoped bundles**: define\n * a `publicApi` that is safe to ship to every client, and a separate\n * `adminApi` that is only imported by the admin application. Combine them\n * at the admin entry point with `mergeContracts`.\n *\n * - Groups that exist in only one contract are passed through unchanged.\n * - Groups that share a key have their endpoint maps **shallowly merged**\n * (later endpoints with the same name override earlier ones, same semantics\n * as `Object.assign`).\n * - The returned contract is frozen, matching the invariant of\n * {@link defineApi}.\n *\n * @typeParam A - The shape of the first contract.\n * @typeParam B - The shape of the second contract.\n * @param a - The base contract (e.g. the public API).\n * @param b - The contract to merge in (e.g. admin-only groups).\n * @returns A new frozen contract whose type is {@link MergedContracts}`<A, B>`.\n *\n * @example\n * ```ts\n * // shared/public-api.ts — safe to import in the client bundle\n * export const publicApi = defineApi({\n * todos: { list: ..., get: ..., create: ... },\n * auth: { login: ..., register: ... },\n * });\n *\n * // shared/admin-api.ts — only imported by the admin application\n * const adminApi = defineApi({\n * admin: { activityLog: ..., banUser: ... },\n * });\n *\n * // admin-app/contract.ts\n * import { mergeContracts } from '@cleverbrush/server/contract';\n * export const fullApi = mergeContracts(publicApi, adminApi);\n * // TypeScript sees: { todos, auth, admin } — fully typed\n *\n * // client-app/contract.ts\n * import { publicApi } from 'shared/public-api';\n * // TypeScript sees: { todos, auth } — admin groups are absent\n * ```\n */\nexport function mergeContracts<A extends ApiContract, B extends ApiContract>(\n a: A,\n b: B\n): Readonly<MergedContracts<A, B>> {\n const result: Record<string, ApiGroup> = {};\n\n for (const key of Object.keys(a) as (keyof A & string)[]) {\n result[key] = { ...a[key] };\n }\n\n for (const key of Object.keys(b) as (keyof B & string)[]) {\n if (Object.hasOwn(result, key)) {\n result[key] = { ...result[key], ...b[key] };\n } else {\n result[key] = { ...b[key] };\n }\n }\n\n for (const group of Object.values(result)) {\n Object.freeze(group);\n }\n\n return Object.freeze(result) as unknown as Readonly<MergedContracts<A, B>>;\n}\n\n/**\n * Returns a new contract containing only the specified groups.\n *\n * The TypeScript return type is `Pick<T, K>` — the compiler sees exactly the\n * selected groups and no others. This provides full type safety on the\n * narrowed contract when passed to `createClient()` or used as a server\n * handler map.\n *\n * @typeParam T - The shape of the source contract.\n * @typeParam K - The union of group keys to keep.\n * @param contract - The contract to select from.\n * @param groups - The group keys to include.\n * @returns A new frozen contract with only the listed groups.\n *\n * @example\n * ```ts\n * const fullApi = defineApi({ todos: {...}, auth: {...}, admin: {...}, debug: {...} });\n *\n * // Pick only the groups the frontend needs\n * const clientApi = pickGroups(fullApi, 'todos', 'auth');\n * // TypeScript: { todos: ..., auth: ... }\n * ```\n */\nexport function pickGroups<T extends ApiContract, K extends keyof T>(\n contract: T,\n ...groups: K[]\n): Readonly<Pick<T, K>> {\n const result = {} as Pick<T, K>;\n for (const key of groups) {\n (result as Record<string, ApiGroup>)[key as string] = contract[key];\n Object.freeze((result as Record<string, ApiGroup>)[key as string]);\n }\n return Object.freeze(result);\n}\n\n/**\n * Returns a new contract with the specified groups removed.\n *\n * The TypeScript return type is `Omit<T, K>` — the listed groups are absent\n * at both runtime and compile time. Useful for stripping debug, internal, or\n * admin groups before sharing a contract with less-privileged consumers.\n *\n * @typeParam T - The shape of the source contract.\n * @typeParam K - The union of group keys to remove.\n * @param contract - The contract to omit from.\n * @param groups - The group keys to exclude.\n * @returns A new frozen contract without the listed groups.\n *\n * @example\n * ```ts\n * const fullApi = defineApi({ todos: {...}, auth: {...}, admin: {...}, debug: {...} });\n *\n * // Strip internal groups before exporting to clients\n * const publicApi = omitGroups(fullApi, 'admin', 'debug');\n * // TypeScript: { todos: ..., auth: ... }\n * ```\n */\nexport function omitGroups<T extends ApiContract, K extends keyof T>(\n contract: T,\n ...groups: K[]\n): Readonly<Omit<T, K>> {\n const excluded = new Set<string>(groups as string[]);\n const result = {} as Omit<T, K>;\n for (const key of Object.keys(contract)) {\n if (!excluded.has(key)) {\n (result as Record<string, ApiGroup>)[key] = contract[key];\n Object.freeze((result as Record<string, ApiGroup>)[key]);\n }\n }\n return Object.freeze(result);\n}\n"],"mappings":"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","names":["object","SYMBOL_HAS_PROPERTIES","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","createCacheTagTree","schemas","props","ps","bs","syn","isPropertyDescriptor","value","serializeTag","name","descriptors","properties","key","inner","root","computeCacheKey","tag","entries","parts","accessor","a","b","result","TRACKED_SYMBOL","tracked","id","data","isTrackedEvent","value","SubscriptionBuilder","_SubscriptionBuilder","#basePath","#pathTemplate","#incomingSchema","#outgoingSchema","#querySchema","#headerSchema","#serviceSchemas","#authRoles","#summary","#description","#tags","#operationId","#deprecated","#externalDocs","basePath","pathTemplate","incomingSchema","outgoingSchema","querySchema","headerSchema","serviceSchemas","authRoles","summary","description","tags","operationId","deprecated","externalDocs","#clone","overrides","schema","schemas","args","roles","merged","text","url","createSubscription","isSubscriptionBuilder","mapHandlers","endpoints","handlers","entries","subscriptions","groupKey","group","handlerGroup","endpointKey","ep","entry","handler","middlewares","isSubscriptionBuilder","EndpointBuilder","_EndpointBuilder","#method","#basePath","#pathTemplate","#bodySchema","#querySchema","#headerSchema","#serviceSchemas","#authRoles","#summary","#description","#tags","#operationId","#deprecated","#responseSchema","#responsesSchemas","#example","#examples","#producesFile","#produces","#responseHeaderSchema","#externalDocs","#links","#callbacks","#fileUpload","#cacheTags","method","basePath","pathTemplate","bodySchema","querySchema","headerSchema","serviceSchemas","authRoles","summary","description","tags","operationId","deprecated","responseSchema","responsesSchemas","example","examples","producesFile","produces","responseHeaderSchema","externalDocs","links","callbacks","fileUpload","cacheTags","schema","schemas","args","roles","merged","_schema","map","text","id","value","contentType","options","base","tpl","suffix","literals","segments","s","i","contentTypes","url","defs","name","selector","paramsSchema","extractParamsObjectSchema","tree","createCacheTagTree","descriptors","definition","serializeTag","createEndpoint","meta","createScopedFactoryMethods","createScopedFactory","info","createEndpoints","_roles","endpoint","createSubscription","object","parseString","createRouteTag","props","objectSchema","strings","selectors","$t","route","propsOrStrings","_rest","defineApi","contract","group","mergeContracts","a","b","result","key","pickGroups","groups","omitGroups","excluded"]}
1
+ {"version":3,"sources":["../src/CacheTag.ts","../src/Subscription.ts","../src/Endpoint.ts","../src/route.ts","../src/contract.ts"],"sourcesContent":["import {\n type ObjectSchemaBuilder,\n object,\n type PropertyDescriptor,\n type SchemaBuilder,\n SYMBOL_HAS_PROPERTIES,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * An accessor that can extract a property value from a structured\n * request root. Wraps a {@link PropertyDescriptor}'s `getValue`\n * closure so the middleware layer does not need to know about schemas.\n */\nexport interface CacheTagPropertyAccessor {\n getValue(root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }): { value?: unknown; success: boolean };\n}\n\n/**\n * A serialisable cache-tag definition stored on endpoint metadata.\n *\n * `properties` maps human-readable key names (used as label segments\n * in the final cache key) to accessors that resolve the actual value\n * from call-time request data.\n */\nexport interface CacheTagDefinition {\n readonly name: string;\n readonly properties: Readonly<Record<string, CacheTagPropertyAccessor>>;\n}\n\n// ---------------------------------------------------------------------------\n// Synthetic schema construction\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a synthetic `object({ params, body, query, headers })` schema\n * from the endpoint's schema definitions and returns its\n * `PropertyDescriptorTree` so callers can write type-safe selectors like:\n *\n * ```ts\n * endpoint.cacheTag('todo', p => ({\n * id: p.query.id,\n * fromBodyId: p.body.id\n * }))\n * ```\n *\n * Only non-null schemas are included in the synthetic schema.\n */\nexport function createCacheTagTree(schemas: {\n paramsSchema?: SchemaBuilder<any, any, any, any, any> | null;\n bodySchema?: SchemaBuilder<any, any, any, any, any> | null;\n querySchema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null;\n headerSchema?: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n}): any {\n const props: Record<string, SchemaBuilder<any, any, any, any, any>> = {};\n\n if (schemas.paramsSchema) {\n const ps = schemas.paramsSchema;\n // ParseStringSchemaBuilder wraps an ObjectSchemaBuilder;\n // extract it so PropertyDescriptorTree can recurse into params.\n if (\n typeof (ps as any).introspect === 'function' &&\n (ps as any).introspect().objectSchema\n ) {\n props.params = (ps as any).introspect().objectSchema;\n } else if ((ps as any)[SYMBOL_HAS_PROPERTIES] === true) {\n props.params = ps;\n }\n }\n\n if (schemas.bodySchema) {\n const bs = schemas.bodySchema;\n if (\n (bs as any)[SYMBOL_HAS_PROPERTIES] === true ||\n typeof (bs as any).introspect === 'function'\n ) {\n props.body = bs;\n }\n }\n\n if (schemas.querySchema) {\n props.query = schemas.querySchema;\n }\n\n if (schemas.headerSchema) {\n props.headers = schemas.headerSchema;\n }\n\n const syn = object(props as any);\n return (object as any).getPropertiesFor(syn);\n}\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a value returned from a cache-tag selector is a valid\n * property descriptor.\n */\nfunction isPropertyDescriptor(value: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (typeof value !== 'object') return false;\n return (\n typeof (value as any)[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR] === 'object' &&\n (value as any)[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR] !== null\n );\n}\n\n/**\n * Serialises the result of a cache-tag selector callback into a\n * {@link CacheTagDefinition} that can be stored on endpoint metadata\n * and forwarded to the client middleware.\n *\n * Each value in `descriptors` must be a {@link PropertyDescriptor} —\n * its `getValue` closure is wrapped in a {@link CacheTagPropertyAccessor}.\n *\n * @throws If any value is not a valid property descriptor.\n */\nexport function serializeTag(\n name: string,\n descriptors: Record<string, unknown>\n): CacheTagDefinition {\n const properties: Record<string, CacheTagPropertyAccessor> = {};\n\n for (const [key, value] of Object.entries(descriptors)) {\n if (!isPropertyDescriptor(value)) {\n throw new Error(\n `Cache tag \"${name}\": property \"${key}\" is not a valid ` +\n `PropertyDescriptor. Make sure you select a leaf property ` +\n `from the tree (e.g. p.query.id, not p.query).`\n );\n }\n\n const inner = (value as PropertyDescriptor<any, any, any, any>)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ];\n\n properties[key] = {\n getValue: (root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }) => inner.getValue(root as any)\n };\n }\n\n return { name, properties };\n}\n\n// ---------------------------------------------------------------------------\n// Key computation (client-side)\n// ---------------------------------------------------------------------------\n\n/**\n * Computes a deterministic cache key from a tag definition and live\n * request data.\n *\n * - Simple tags (no properties) produce just the tag name.\n * - Tags with properties produce `name:key1=val1,key2=val2` where\n * keys are sorted alphabetically for determinism.\n *\n * Properties whose `getValue` returns `success: false` are skipped\n * (their value is not included in the key).\n */\nexport function computeCacheKey(\n tag: CacheTagDefinition,\n root: {\n params: Record<string, unknown>;\n body: unknown;\n query: Record<string, unknown>;\n headers: Record<string, string>;\n }\n): string {\n const entries = Object.entries(tag.properties);\n\n if (entries.length === 0) {\n return tag.name;\n }\n\n const parts: string[] = [];\n for (const [key, accessor] of entries.sort(([a], [b]) =>\n a.localeCompare(b)\n )) {\n const result = accessor.getValue(root);\n if (result.success && result.value !== undefined) {\n parts.push(`${key}=${String(result.value)}`);\n }\n }\n\n if (parts.length === 0) {\n return tag.name;\n }\n\n return `${tag.name}:${parts.join(',')}`;\n}\n","import type {\n InferType,\n ObjectSchemaBuilder,\n ParseStringSchemaBuilder,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport type { RequestContext } from './RequestContext.js';\nimport type { Middleware } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Simplify — flattens intersection types for clean IDE tooltips\n// ---------------------------------------------------------------------------\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// ---------------------------------------------------------------------------\n// tracked() — resumable event wrapper\n// ---------------------------------------------------------------------------\n\nconst TRACKED_SYMBOL = Symbol.for('cleverbrush.tracked');\n\n/**\n * A server-sent event wrapped with a unique ID for resumable subscriptions.\n *\n * When a client reconnects, it can send the last received event ID so the\n * server resumes from that point instead of replaying everything.\n *\n * @see {@link tracked}\n */\nexport interface TrackedEvent<T = unknown> {\n /** @internal Brand marker. */\n readonly [TRACKED_SYMBOL]: true;\n /** Unique event identifier for resume tracking. */\n readonly id: string;\n /** The actual event payload. */\n readonly data: T;\n}\n\n/**\n * Wrap a server-sent event with a unique ID for resumable subscriptions.\n *\n * When a handler yields a `TrackedEvent`, the server sends the event with its\n * ID. If the client disconnects and reconnects with the last received ID,\n * the handler can skip already-delivered events.\n *\n * @param id - A unique, monotonically increasing identifier for this event.\n * @param data - The event payload to send to the client.\n * @returns A {@link TrackedEvent} wrapper.\n *\n * @example\n * ```ts\n * async function* handler({ incoming }) {\n * yield tracked('evt-1', { message: 'Hello' });\n * yield tracked('evt-2', { message: 'World' });\n * }\n * ```\n */\nexport function tracked<T>(id: string, data: T): TrackedEvent<T> {\n return { [TRACKED_SYMBOL]: true, id, data };\n}\n\n/**\n * Check whether a value is a {@link TrackedEvent}.\n * @internal\n */\nexport function isTrackedEvent(value: unknown): value is TrackedEvent {\n return (\n value !== null &&\n typeof value === 'object' &&\n TRACKED_SYMBOL in value &&\n (value as any)[TRACKED_SYMBOL] === true\n );\n}\n\n// ---------------------------------------------------------------------------\n// SubscriptionContext — the typed argument for subscription handlers\n// ---------------------------------------------------------------------------\n\ntype HasKeys<T> = keyof T extends never ? false : true;\n\ntype SubscriptionContextParts<\n TParams,\n TQuery,\n THeaders,\n TPrincipal,\n TIncoming\n> = {\n context: RequestContext;\n signal: AbortSignal;\n} & (HasKeys<TParams> extends true ? { params: TParams } : {}) &\n (HasKeys<TQuery> extends true ? { query: TQuery } : {}) &\n (HasKeys<THeaders> extends true ? { headers: THeaders } : {}) &\n (TPrincipal extends undefined ? {} : { principal: TPrincipal }) &\n ([TIncoming] extends [undefined]\n ? {}\n : {\n incoming: AsyncIterable<\n TIncoming extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TIncoming>\n : TIncoming\n >;\n });\n\n/**\n * The fully-typed argument object passed to subscription handlers.\n *\n * The shape is inferred from the `SubscriptionBuilder` chain — only the keys\n * actually configured (query, headers, params, principal, incoming) are present.\n * Always includes `context` ({@link RequestContext}) and `signal` ({@link AbortSignal}).\n */\nexport type SubscriptionContext<E> =\n E extends SubscriptionBuilder<\n infer TParams,\n infer TQuery,\n infer THeaders,\n any,\n infer TPrincipal,\n any,\n infer TIncoming,\n any\n >\n ? Simplify<\n SubscriptionContextParts<\n TParams,\n TQuery,\n THeaders,\n TPrincipal,\n TIncoming\n >\n >\n : never;\n\n// ---------------------------------------------------------------------------\n// InferServices — maps { name: SchemaBuilder } to { name: InferType<Schema> }\n// ---------------------------------------------------------------------------\n\ntype InferServices<T> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : never;\n};\n\n/**\n * Extracts the injected service schemas map from a `SubscriptionBuilder`.\n */\nexport type SubscriptionServiceSchemas<E> =\n E extends SubscriptionBuilder<\n any,\n any,\n any,\n infer TServices,\n any,\n any,\n any,\n any\n >\n ? TServices\n : {};\n\n// ---------------------------------------------------------------------------\n// InferOutgoing — extracts the outgoing event type\n// ---------------------------------------------------------------------------\n\ntype OutgoingType<E> =\n E extends SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TOutgoing\n >\n ? TOutgoing extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TOutgoing> | TrackedEvent<InferType<TOutgoing>>\n : TOutgoing | TrackedEvent<TOutgoing>\n : any;\n\n// ---------------------------------------------------------------------------\n// SubscriptionHandler — the async generator function type\n// ---------------------------------------------------------------------------\n\n/**\n * The handler function type inferred from a `SubscriptionBuilder`.\n *\n * Must be an async generator that yields outgoing events. When the endpoint\n * has injected services, the handler receives a second `services` argument.\n *\n * @example\n * ```ts\n * const handler: SubscriptionHandler<typeof chatEndpoint> =\n * async function* ({ incoming, principal }) {\n * yield tracked('welcome', { text: 'Hello!' });\n * for await (const msg of incoming) {\n * yield { text: `${principal.name}: ${msg.text}` };\n * }\n * };\n * ```\n */\nexport type SubscriptionHandler<E> =\n HasKeys<SubscriptionServiceSchemas<E>> extends true\n ? (\n arg: SubscriptionContext<E>,\n services: Simplify<InferServices<SubscriptionServiceSchemas<E>>>\n ) => AsyncGenerator<OutgoingType<E>>\n : (arg: SubscriptionContext<E>) => AsyncGenerator<OutgoingType<E>>;\n\n// ---------------------------------------------------------------------------\n// SubscriptionMetadata — runtime introspection snapshot\n// ---------------------------------------------------------------------------\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/**\n * Snapshot of all configuration set on a `SubscriptionBuilder`.\n * Used by the server for WebSocket upgrade handling and by documentation\n * generators for AsyncAPI / OpenAPI spec output.\n */\nexport interface SubscriptionMetadata {\n /** Always `'subscription'`. Distinguishes from HTTP endpoint metadata. */\n readonly protocol: 'subscription';\n readonly basePath: string;\n readonly pathTemplate: RoutePath;\n readonly incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly authRoles: readonly string[] | null;\n readonly summary: string | null;\n readonly description: string | null;\n readonly tags: readonly string[];\n readonly operationId: string | null;\n readonly deprecated: boolean;\n readonly externalDocs: { url: string; description?: string } | null;\n}\n\n// ---------------------------------------------------------------------------\n// SubscriptionBuilder — immutable builder for WebSocket subscription endpoints\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable, fluent builder for WebSocket subscription endpoint definitions.\n *\n * All methods return a new builder instance — the original is never mutated.\n * Use `endpoint.subscription()` to obtain the first builder in the chain.\n *\n * @example\n * ```ts\n * const ChatRoom = endpoint\n * .subscription('/ws/chat')\n * .incoming(object({ text: string() }))\n * .outgoing(object({ user: string(), text: string(), timestamp: number() }))\n * .authorize(PrincipalSchema)\n * .summary('Real-time chat room');\n * ```\n */\nexport class SubscriptionBuilder<\n TParams = {},\n TQuery = {},\n THeaders = {},\n TServices = {},\n TPrincipal = undefined,\n TRoles extends string = string,\n TIncoming = undefined,\n TOutgoing = undefined\n> {\n readonly #basePath: string;\n readonly #pathTemplate: RoutePath;\n readonly #incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly #authRoles: readonly string[] | null;\n readonly #summary: string | null;\n readonly #description: string | null;\n readonly #tags: readonly string[];\n readonly #operationId: string | null;\n readonly #deprecated: boolean;\n readonly #externalDocs: { url: string; description?: string } | null;\n\n constructor(\n basePath: string,\n pathTemplate: RoutePath = '/',\n incomingSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n outgoingSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null = null,\n authRoles: readonly string[] | null = null,\n summary: string | null = null,\n description: string | null = null,\n tags: readonly string[] = [],\n operationId: string | null = null,\n deprecated: boolean = false,\n externalDocs: { url: string; description?: string } | null = null\n ) {\n this.#basePath = basePath;\n this.#pathTemplate = pathTemplate;\n this.#incomingSchema = incomingSchema;\n this.#outgoingSchema = outgoingSchema;\n this.#querySchema = querySchema;\n this.#headerSchema = headerSchema;\n this.#serviceSchemas = serviceSchemas;\n this.#authRoles = authRoles;\n this.#summary = summary;\n this.#description = description;\n this.#tags = tags;\n this.#operationId = operationId;\n this.#deprecated = deprecated;\n this.#externalDocs = externalDocs;\n }\n\n // -- Helper to clone with one field changed ---------------------------------\n\n #clone(\n overrides: Partial<{\n basePath: string;\n pathTemplate: RoutePath;\n incomingSchema: SchemaBuilder<any, any, any, any, any> | null;\n outgoingSchema: SchemaBuilder<any, any, any, any, any> | null;\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n authRoles: readonly string[] | null;\n summary: string | null;\n description: string | null;\n tags: readonly string[];\n operationId: string | null;\n deprecated: boolean;\n externalDocs: { url: string; description?: string } | null;\n }>\n ): SubscriptionBuilder<any, any, any, any, any, any, any, any> {\n return new SubscriptionBuilder(\n overrides.basePath ?? this.#basePath,\n overrides.pathTemplate ?? this.#pathTemplate,\n overrides.incomingSchema !== undefined\n ? overrides.incomingSchema\n : this.#incomingSchema,\n overrides.outgoingSchema !== undefined\n ? overrides.outgoingSchema\n : this.#outgoingSchema,\n overrides.querySchema !== undefined\n ? overrides.querySchema\n : this.#querySchema,\n overrides.headerSchema !== undefined\n ? overrides.headerSchema\n : this.#headerSchema,\n overrides.serviceSchemas !== undefined\n ? overrides.serviceSchemas\n : this.#serviceSchemas,\n overrides.authRoles !== undefined\n ? overrides.authRoles\n : this.#authRoles,\n overrides.summary !== undefined ? overrides.summary : this.#summary,\n overrides.description !== undefined\n ? overrides.description\n : this.#description,\n overrides.tags ?? this.#tags,\n overrides.operationId !== undefined\n ? overrides.operationId\n : this.#operationId,\n overrides.deprecated ?? this.#deprecated,\n overrides.externalDocs !== undefined\n ? overrides.externalDocs\n : this.#externalDocs\n );\n }\n\n /**\n * Define the schema for client→server messages.\n * Messages that do not match this schema are rejected with an error frame.\n */\n incoming<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TSchema,\n TOutgoing\n > {\n return this.#clone({ incomingSchema: schema }) as any;\n }\n\n /**\n * Define the schema for server→client events.\n * All yielded values are validated against this schema before sending.\n */\n outgoing<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TSchema\n > {\n return this.#clone({ outgoingSchema: schema }) as any;\n }\n\n /** Define the query string schema for the WebSocket upgrade URL. */\n query<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n InferType<TSchema>,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ querySchema: schema }) as any;\n }\n\n /** Define expected headers on the WebSocket upgrade request. */\n headers<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n InferType<TSchema>,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ headerSchema: schema }) as any;\n }\n\n /** Declare DI services to be resolved per-connection and passed as the second handler argument. */\n inject<\n TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>\n >(\n schemas: TSchemas\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TSchemas,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ serviceSchemas: schemas }) as any;\n }\n\n /**\n * Mark this subscription as requiring authorization.\n *\n * Overloads:\n * - `authorize(principalSchema, ...roles)` — typed principal, optional role requirements\n * - `authorize(...roles)` — untyped principal (`unknown`), optional role requirements\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n InferType<TSchema>,\n TRoles,\n TIncoming,\n TOutgoing\n >;\n authorize(\n ...roles: TRoles[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n unknown,\n TRoles,\n TIncoming,\n TOutgoing\n >;\n authorize(\n ...args: unknown[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n any,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n const merged = this.#authRoles ? [...this.#authRoles, ...roles] : roles;\n return this.#clone({ authRoles: merged }) as any;\n }\n\n /**\n * Mark this subscription as public — no authentication required.\n *\n * Calling `.public()` sets `authRoles` to `null`, overriding any\n * previously set authorization requirements.\n */\n public(): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ authRoles: null }) as any;\n }\n\n /** Short, human-readable summary for documentation. */\n summary(\n text: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ summary: text }) as any;\n }\n\n /** Longer description for documentation. Supports Markdown. */\n description(\n text: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ description: text }) as any;\n }\n\n /** Documentation tags for grouping. */\n tags(\n ...tags: string[]\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ tags }) as any;\n }\n\n /** A unique, stable identifier for documentation. */\n operationId(\n id: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ operationId: id }) as any;\n }\n\n /** Mark this subscription as deprecated in documentation. */\n deprecated(): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ deprecated: true }) as any;\n }\n\n /** Link external documentation. */\n externalDocs(\n url: string,\n description?: string\n ): SubscriptionBuilder<\n TParams,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TIncoming,\n TOutgoing\n > {\n return this.#clone({ externalDocs: { url, description } }) as any;\n }\n\n /** Return an immutable snapshot of this builder's configuration as {@link SubscriptionMetadata}. */\n introspect(): SubscriptionMetadata {\n return {\n protocol: 'subscription',\n basePath: this.#basePath,\n pathTemplate: this.#pathTemplate,\n incomingSchema: this.#incomingSchema,\n outgoingSchema: this.#outgoingSchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema,\n serviceSchemas: this.#serviceSchemas,\n authRoles: this.#authRoles,\n summary: this.#summary,\n description: this.#description,\n tags: this.#tags,\n operationId: this.#operationId,\n deprecated: this.#deprecated,\n externalDocs: this.#externalDocs\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Subscription handler mapping types\n// ---------------------------------------------------------------------------\n\ntype AnySubscription = SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\n\n/**\n * A single handler entry for a subscription in a handler map.\n *\n * Either a bare handler function or an object with a `handler` function\n * and optional per-subscription `middlewares`.\n */\nexport type SubscriptionHandlerEntry<E> =\n | SubscriptionHandler<E>\n | { handler: SubscriptionHandler<E>; middlewares?: Middleware[] };\n\n/**\n * Create a subscription endpoint builder.\n * @internal — used by the endpoint factory.\n */\nexport function createSubscription<TParams>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n): SubscriptionBuilder<TParams extends undefined ? {} : TParams>;\n\nexport function createSubscription(\n basePath: string,\n pathTemplate?: RoutePath\n): SubscriptionBuilder<any> {\n return new SubscriptionBuilder(basePath, pathTemplate ?? '/');\n}\n\n/**\n * Runtime check: is this endpoint builder a subscription?\n * @internal\n */\nexport function isSubscriptionBuilder(\n value: unknown\n): value is AnySubscription {\n return value instanceof SubscriptionBuilder;\n}\n\n/**\n * Runtime check: is this metadata from a subscription?\n * @internal\n */\nexport function isSubscriptionMetadata(\n value: unknown\n): value is SubscriptionMetadata {\n return (\n value !== null &&\n typeof value === 'object' &&\n (value as any).protocol === 'subscription'\n );\n}\n","// biome-ignore-all lint/suspicious/useAdjacentOverloadSignatures: each method in ScopedEndpointFactoryMethods and EndpointFactory has a single signature; they are separate methods, not overloads\nimport type {\n InferType,\n ObjectSchemaBuilder,\n ParseStringSchemaBuilder,\n PropertyDescriptorTree,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport { SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR } from '@cleverbrush/schema';\nimport type {\n ActionResult,\n ContentResult,\n FileResult,\n JsonResult,\n NoContentResult,\n RawResult,\n RedirectResult,\n StatusCodeResult,\n StreamResult\n} from './ActionResult.js';\nimport type { CacheTagDefinition } from './CacheTag.js';\nimport { createCacheTagTree, serializeTag } from './CacheTag.js';\nimport type { RequestContext } from './RequestContext.js';\nimport {\n createSubscription,\n isSubscriptionBuilder,\n type SubscriptionBuilder,\n type SubscriptionHandlerEntry\n} from './Subscription.js';\nimport type {\n FilePart,\n Middleware,\n RejectedFile,\n UploadOptions\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Simplify — flattens intersection types for clean IDE tooltips\n// ---------------------------------------------------------------------------\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// ---------------------------------------------------------------------------\n// ActionContext — assembles the typed argument for a handler\n// ---------------------------------------------------------------------------\n\ntype HasKeys<T> = keyof T extends never ? false : true;\n\ntype ActionContextParts<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TPrincipal,\n TUpload extends boolean\n> = {\n context: RequestContext;\n} & (HasKeys<TParams> extends true ? { params: TParams } : {}) &\n (TBody extends undefined\n ? {}\n : {\n body: TBody extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TBody>\n : TBody;\n }) &\n (HasKeys<TQuery> extends true ? { query: TQuery } : {}) &\n (HasKeys<THeaders> extends true ? { headers: THeaders } : {}) &\n (TPrincipal extends undefined ? {} : { principal: TPrincipal }) &\n (TUpload extends true\n ? { files: Record<string, FilePart>; rejectedFiles?: RejectedFile[] }\n : {});\n\n/**\n * The fully-typed argument object passed to endpoint handlers.\n *\n * The shape is inferred from the `EndpointBuilder` chain — only the keys\n * actually configured (body, query, headers, params, principal) are present.\n */\nexport type ActionContext<E> =\n E extends EndpointBuilder<\n infer TParams,\n infer TBody,\n infer TQuery,\n infer THeaders,\n any,\n infer TPrincipal,\n any,\n any,\n any,\n infer TUpload\n >\n ? Simplify<\n ActionContextParts<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TPrincipal,\n TUpload\n >\n >\n : never;\n\n// ---------------------------------------------------------------------------\n// InferServices — maps { name: SchemaBuilder } to { name: InferType<Schema> }\n// ---------------------------------------------------------------------------\n\ntype InferServices<T> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : never;\n};\n\n/**\n * Extracts the injected service schemas map from an `EndpointBuilder` type.\n * Used internally by the `Handler` type to derive the `services` argument.\n */\nexport type ServiceSchemas<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n infer TServices,\n any,\n any,\n any,\n any,\n any\n >\n ? TServices\n : {};\n\ntype ResponseType<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TResponse,\n any,\n any\n >\n ? TResponse extends SchemaBuilder<any, any, any, any, any>\n ? InferType<TResponse>\n : TResponse\n : any;\n\n/**\n * Extracts the `TResponses` map from an `EndpointBuilder` type.\n * `TResponses` is a `Record<number, BodyType>` inferred from `.responses()`.\n */\nexport type ResponsesOf<E> =\n E extends EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n infer TResponses,\n any\n >\n ? TResponses\n : never;\n\ntype HasResponses<E> = keyof ResponsesOf<E> extends never ? false : true;\n\n/**\n * The union of permitted return values for a handler whose endpoint\n * declared `.responses()`. Each member corresponds to one declared code:\n *\n * - `null` schema (e.g. 204) → `NoContentResult` for 204, `StatusCodeResult<K>` otherwise\n * - Non-null schema for code 200 → also allows a plain object (treated as 200 by the server)\n * - Non-null schema for other codes → `JsonResult<K, Body>`\n *\n * `FileResult`, `StreamResult`, `ContentResult`, `RedirectResult`, and\n * `RawResult` are always permitted as escape hatches for non-JSON responses.\n */\nexport type AllowedResponseReturn<TResponses extends Record<number, any>> =\n | {\n [K in keyof TResponses & number]: TResponses[K] extends null\n ? K extends 204\n ? NoContentResult\n : StatusCodeResult<K>\n : JsonResult<K, TResponses[K]>;\n }[keyof TResponses & number]\n | (200 extends keyof TResponses\n ? TResponses[200] extends null\n ? never\n : TResponses[200]\n : never)\n | FileResult\n | StreamResult\n | ContentResult\n | RedirectResult\n | RawResult;\n\n// ---------------------------------------------------------------------------\n// Handler — the action function type, inferred from an endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * The handler function type inferred from an `EndpointBuilder`.\n *\n * When the endpoint has injected services, the handler receives a second\n * `services` argument with all resolved service instances.\n */\ntype HandlerReturn<E> =\n HasResponses<E> extends true\n ? AllowedResponseReturn<ResponsesOf<E>>\n : ResponseType<E> | ActionResult;\n\nexport type Handler<E> =\n HasKeys<ServiceSchemas<E>> extends true\n ? (\n arg: ActionContext<E>,\n services: Simplify<InferServices<ServiceSchemas<E>>>\n ) => HandlerReturn<E> | Promise<HandlerReturn<E>>\n : (\n arg: ActionContext<E>\n ) => HandlerReturn<E> | Promise<HandlerReturn<E>>;\n\n// ---------------------------------------------------------------------------\n// Handler mapping — compile-time complete endpoint → handler binding\n// ---------------------------------------------------------------------------\n\ntype AnyEndpoint = EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\ntype AnySubscriptionBuilder = SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\n\n/**\n * A single handler entry in a {@link HandlerMap}.\n *\n * Either a bare handler function or an object with a `handler` function and\n * optional per-endpoint `middlewares`.\n */\nexport type HandlerEntry<E> =\n | Handler<E>\n | { handler: Handler<E>; middlewares?: Middleware[] };\n\n/**\n * A compile-time complete mapping from an endpoint group structure to\n * handler entries. Every endpoint key in every group must have a\n * corresponding {@link HandlerEntry} — omitting one is a type error.\n *\n * Subscription endpoints (created via `endpoint.subscription()`) are\n * mapped to {@link SubscriptionHandlerEntry} instead of {@link HandlerEntry}.\n *\n * @typeParam TEndpoints - A record of groups, each a record of endpoint\n * builders. Typically the return type of `defineApi()` with server-side\n * extensions applied.\n */\nexport type HandlerMap<TEndpoints> = {\n [G in keyof TEndpoints]: {\n [E in keyof TEndpoints[G]]: TEndpoints[G][E] extends AnySubscriptionBuilder\n ? SubscriptionHandlerEntry<TEndpoints[G][E]>\n : TEndpoints[G][E] extends AnyEndpoint\n ? HandlerEntry<TEndpoints[G][E]>\n : never;\n };\n};\n\n/**\n * The opaque result of {@link mapHandlers}. Passed to\n * `ServerBuilder.handleAll()` to register every endpoint at once.\n */\nexport interface HandlerMapping {\n /** @internal */\n readonly _entries: ReadonlyArray<{\n endpoint: AnyEndpoint;\n handler: (...args: any[]) => any;\n middlewares?: Middleware[];\n }>;\n /** @internal */\n readonly _subscriptions: ReadonlyArray<{\n endpoint: AnySubscriptionBuilder;\n handler: (...args: any[]) => any;\n middlewares?: Middleware[];\n }>;\n}\n\n/**\n * Binds a complete set of endpoint builders to their handlers with\n * compile-time exhaustiveness checking.\n *\n * TypeScript will report an error if any endpoint in `endpoints` is\n * missing from `handlers`, or if a handler's signature does not match\n * its endpoint. This is analogous to how `@cleverbrush/mapper` tracks\n * unmapped properties at the type level.\n *\n * @param endpoints - A grouped object of extended endpoint builders\n * (e.g. after `.authorize()` / `.inject()`).\n * @param handlers - A matching grouped object of handler functions or\n * `{ handler, middlewares }` entries.\n * @returns A {@link HandlerMapping} to pass to `ServerBuilder.handleAll()`.\n *\n * @example\n * ```ts\n * const mapping = mapHandlers(endpoints, {\n * auth: {\n * register: registerHandler,\n * login: loginHandler,\n * },\n * todos: {\n * list: listTodosHandler,\n * create: createTodoHandler,\n * export: { handler: exportHandler, middlewares: [auditLog] },\n * },\n * });\n *\n * server.handleAll(mapping);\n * ```\n */\nexport function mapHandlers<\n TEndpoints extends Record<\n string,\n Record<string, AnyEndpoint | AnySubscriptionBuilder>\n >\n>(endpoints: TEndpoints, handlers: HandlerMap<TEndpoints>): HandlerMapping {\n const entries: HandlerMapping['_entries'][number][] = [];\n const subscriptions: HandlerMapping['_subscriptions'][number][] = [];\n\n for (const groupKey of Object.keys(endpoints)) {\n const group = endpoints[groupKey]!;\n const handlerGroup = (handlers as any)[groupKey];\n\n for (const endpointKey of Object.keys(group)) {\n const ep = group[endpointKey]!;\n const entry = handlerGroup[endpointKey];\n\n const handler = typeof entry === 'function' ? entry : entry.handler;\n const middlewares =\n typeof entry === 'function' ? undefined : entry.middlewares;\n\n if (isSubscriptionBuilder(ep)) {\n subscriptions.push({ endpoint: ep, handler, middlewares });\n } else {\n entries.push({\n endpoint: ep as AnyEndpoint,\n handler,\n middlewares\n });\n }\n }\n }\n\n return { _entries: entries, _subscriptions: subscriptions };\n}\n\n// ---------------------------------------------------------------------------\n// EndpointBuilder — immutable builder for endpoint definitions\n// ---------------------------------------------------------------------------\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/**\n * Lightweight recursive property reference tree for type-safe runtime\n * expression building in `.links()` and `.callbacks()`.\n *\n * At runtime the actual value is a `PropertyDescriptorTree` from\n * `ObjectSchemaBuilder.getPropertiesFor()`, which provides `toJsonPointer()`\n * and the `SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR` marker used by the OpenAPI\n * generator to resolve path expressions automatically.\n */\nexport type PropertyRefTree<T> = {\n readonly [K in keyof T]-?: PropertyRefTree<T[K]>;\n};\n\n/**\n * A link from a response to a follow-up operation.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#link-object\n */\nexport interface LinkDefinition<TResponse = any> {\n /** `operationId` of the target operation. */\n readonly operationId: string;\n /**\n * Map of target parameter names to values.\n *\n * - `Record<string, string>` — raw OpenAPI runtime expressions such as\n * `'$response.body#/id'`.\n * - Callback `(response: PropertyRefTree<TResponse>) => Record<string, unknown>` —\n * type-safe selector; properties accessed on `response` are converted to\n * `$response.body#/<pointer>` expressions automatically.\n */\n readonly parameters?:\n | Record<string, string>\n | ((\n response: TResponse extends ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any\n >\n ? PropertyDescriptorTree<TResponse, TResponse>\n : PropertyRefTree<any>\n ) => Record<string, unknown>);\n /** Human-readable description of the link relationship. */\n readonly description?: string;\n /** Runtime expression or literal for the linked operation's request body. */\n readonly requestBody?: string;\n}\n\n/**\n * A callback declaration for async request/response patterns.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#callback-object\n */\nexport interface CallbackDefinition<TBody = any> {\n /**\n * Raw OpenAPI runtime expression for the callback URL,\n * e.g. `'{$request.body#/callbackUrl}'`.\n * Mutually exclusive with `urlFrom`.\n */\n readonly expression?: string;\n /**\n * Type-safe selector for the request body field holding the callback URL.\n * The generator converts the selected property to a\n * `'{$request.body#/<pointer>}'` expression automatically.\n * Mutually exclusive with `expression`.\n */\n readonly urlFrom?: (\n body: TBody extends ObjectSchemaBuilder<any, any, any, any, any>\n ? PropertyDescriptorTree<TBody, TBody>\n : PropertyRefTree<any>\n ) => unknown;\n /** HTTP method for the callback request (default: `'POST'`). */\n readonly method?: string;\n /** Short summary of the callback operation. */\n readonly summary?: string;\n /** Detailed description of the callback operation. */\n readonly description?: string;\n /** Request body schema for the callback payload. */\n readonly body?: SchemaBuilder<any, any, any, any, any>;\n /** Response schema expected from the callback consumer. */\n readonly response?: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Snapshot of all configuration set on an `EndpointBuilder`.\n * Used by the server for routing and by `@cleverbrush/server-openapi` for\n * spec generation.\n */\nexport interface EndpointMetadata {\n readonly method: string;\n readonly basePath: string;\n readonly pathTemplate: RoutePath;\n readonly bodySchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n /**\n * Authorization roles required for this endpoint.\n * - `null` → no authorization required (public)\n * - `[]` → any authenticated user\n * - `['admin', ...]` → user must have at least one of these roles\n */\n readonly authRoles: readonly string[] | null;\n readonly summary: string | null;\n readonly description: string | null;\n readonly tags: readonly string[];\n readonly operationId: string | null;\n readonly deprecated: boolean;\n readonly responseSchema: SchemaBuilder<any, any, any, any, any> | null;\n /**\n * Per-status-code response schemas declared via `.responses()`.\n * When non-null, takes precedence over `responseSchema` for OpenAPI generation\n * and constrains the handler return type to the declared codes.\n * A `null` schema value means the response has no body (e.g. 204).\n */\n readonly responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null;\n /**\n * A single example value for the request body, emitted as `example` on the\n * OpenAPI Media Type Object.\n */\n readonly example: unknown | null;\n /**\n * A map of named examples for the request body, emitted as `examples` on the\n * OpenAPI Media Type Object. Each entry follows the OpenAPI Example Object shape.\n */\n readonly examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null;\n /**\n * When set, the endpoint produces a binary file response instead of JSON.\n * The OpenAPI spec will emit the appropriate binary content type.\n */\n readonly producesFile: {\n contentType?: string;\n description?: string;\n } | null;\n /**\n * Multiple response content types for content-negotiated endpoints.\n * Keys are MIME types; an optional `schema` overrides the default response\n * schema for that content type. When set alongside `.producesFile()`,\n * `producesFile` takes precedence.\n */\n readonly produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null;\n /**\n * Schema describing response headers emitted on every response code.\n * Each property in the object schema becomes a header name with its\n * sub-schema and optional description.\n */\n readonly responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n /**\n * External documentation URL for this operation, emitted as `externalDocs`\n * on the OpenAPI Operation Object.\n */\n readonly externalDocs: { url: string; description?: string } | null;\n /**\n * Response links declared via `.links()`, emitted under the primary\n * success response's `links` map in the OpenAPI spec.\n */\n readonly links: Record<string, LinkDefinition> | null;\n /**\n * Callbacks declared via `.callbacks()`, emitted as `callbacks` on the\n * OpenAPI Operation Object.\n */\n readonly callbacks: Record<string, CallbackDefinition> | null;\n /**\n * When set, the endpoint accepts `multipart/form-data` uploads.\n * The configuration controls max file size, allowed MIME types, etc.\n * @see `EndpointBuilder.upload()`\n */\n readonly fileUpload: UploadOptions | null;\n /**\n * Cache tags declared via `.clearsCacheTag()`, providing tag-based cache\n * key computation for the client middleware.\n */\n readonly cacheTags: readonly CacheTagDefinition[];\n}\n\n/**\n * Immutable, fluent builder for HTTP endpoint definitions.\n *\n * All methods return a new builder instance — the original is never mutated.\n * Use the {@link endpoint} singleton (or {@link createEndpoints}) to obtain\n * the first builder in the chain.\n *\n * @example\n * ```ts\n * const GetUser = endpoint\n * .get('/api/users')\n * .query(object({ id: number().coerce() }))\n * .authorize(UserPrincipal, 'admin')\n * .returns(UserSchema)\n * .summary('Get a user by ID');\n * ```\n */\n/**\n * Infers the per-code body type map from a `.responses()` schema map.\n * Each schema maps to its `InferType`; a `null` schema maps to `null`\n * (meaning the response has no body).\n */\ntype InferResponsesMap<\n T extends Record<number, SchemaBuilder<any, any, any, any, any> | null>\n> = {\n [K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any>\n ? InferType<T[K]>\n : null;\n};\n\n// ---------------------------------------------------------------------------\n// Cache-tag selector type — gives the consumer IDE hints when selecting\n// properties from the tree passed to the `.clearsCacheTag()` callback.\n// ---------------------------------------------------------------------------\n\n/**\n * A leaf node in a cache-tag property tree — mirrors the shape of the\n * actual runtime {@link PropertyDescriptor} so the compiler accepts\n * values selected by the consumer.\n */\ninterface CacheTagPropertyLeaf {\n readonly [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {\n readonly getValue: (obj: Record<string, unknown>) => {\n readonly value?: unknown;\n readonly success: boolean;\n };\n };\n}\n\n/** Recursively builds a typed property tree from an inferred object shape. */\ntype CacheTagPropertyTree<T> = CacheTagPropertyLeaf &\n (T extends Record<string, unknown>\n ? { readonly [K in keyof T]-?: CacheTagPropertyTree<T[K]> }\n : unknown);\n\n/**\n * The typed tree passed to the `.clearsCacheTag(name, selector)` callback.\n *\n * `p.params`, `p.query`, and `p.headers` provide IDE completion for\n * each schema's property names, while `p.body` resolves through the\n * body schema's `InferType`.\n */\ntype CacheTagSelector<TParams, TBody, TQuery, THeaders> = {\n readonly params: [keyof TParams] extends [never]\n ? Record<string, never>\n : TParams extends Record<string, unknown>\n ? CacheTagPropertyTree<TParams>\n : Record<string, never>;\n readonly body: TBody extends undefined\n ? undefined\n : TBody extends SchemaBuilder<any, any, any, any, any>\n ? CacheTagPropertyTree<InferType<TBody>>\n : Record<string, never>;\n readonly query: [keyof TQuery] extends [never]\n ? Record<string, never>\n : TQuery extends Record<string, unknown>\n ? CacheTagPropertyTree<TQuery>\n : Record<string, never>;\n readonly headers: [keyof THeaders] extends [never]\n ? Record<string, never>\n : THeaders extends Record<string, unknown>\n ? CacheTagPropertyTree<THeaders>\n : Record<string, never>;\n};\n\nexport class EndpointBuilder<\n TParams = {},\n TBody = undefined,\n TQuery = {},\n THeaders = {},\n TServices = {},\n TPrincipal = undefined,\n TRoles extends string = string,\n TResponse = any,\n TResponses extends Record<number, any> = {},\n TUpload extends boolean = false\n> {\n readonly #method: string;\n readonly #basePath: string;\n readonly #pathTemplate: RoutePath;\n readonly #bodySchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null;\n readonly #authRoles: readonly string[] | null;\n readonly #summary: string | null;\n readonly #description: string | null;\n readonly #tags: readonly string[];\n readonly #operationId: string | null;\n readonly #deprecated: boolean;\n readonly #responseSchema: SchemaBuilder<any, any, any, any, any> | null;\n readonly #responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null;\n readonly #example: unknown | null;\n readonly #examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null;\n readonly #producesFile: {\n contentType?: string;\n description?: string;\n } | null;\n readonly #produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null;\n readonly #responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null;\n readonly #externalDocs: { url: string; description?: string } | null;\n readonly #links: Record<string, LinkDefinition> | null;\n readonly #callbacks: Record<string, CallbackDefinition> | null;\n readonly #fileUpload: UploadOptions | null;\n readonly #cacheTags: readonly CacheTagDefinition[];\n\n constructor(\n method: string,\n basePath: string,\n pathTemplate: RoutePath,\n bodySchema: SchemaBuilder<any, any, any, any, any> | null,\n querySchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null,\n headerSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null,\n serviceSchemas: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > | null = null,\n authRoles: readonly string[] | null = null,\n summary: string | null = null,\n description: string | null = null,\n tags: readonly string[] = [],\n operationId: string | null = null,\n deprecated: boolean = false,\n responseSchema: SchemaBuilder<any, any, any, any, any> | null = null,\n responsesSchemas: Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n > | null = null,\n example: unknown | null = null,\n examples: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null = null,\n producesFile: {\n contentType?: string;\n description?: string;\n } | null = null,\n produces: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n > | null = null,\n responseHeaderSchema: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n > | null = null,\n externalDocs: { url: string; description?: string } | null = null,\n links: Record<string, LinkDefinition> | null = null,\n callbacks: Record<string, CallbackDefinition> | null = null,\n fileUpload: UploadOptions | null = null,\n cacheTags: readonly CacheTagDefinition[] = []\n ) {\n this.#method = method;\n this.#basePath = basePath;\n this.#pathTemplate = pathTemplate;\n this.#bodySchema = bodySchema;\n this.#querySchema = querySchema;\n this.#headerSchema = headerSchema;\n this.#serviceSchemas = serviceSchemas;\n this.#authRoles = authRoles;\n this.#summary = summary;\n this.#description = description;\n this.#tags = tags;\n this.#operationId = operationId;\n this.#deprecated = deprecated;\n this.#responseSchema = responseSchema;\n this.#responsesSchemas = responsesSchemas;\n this.#example = example;\n this.#examples = examples;\n this.#producesFile = producesFile;\n this.#produces = produces;\n this.#responseHeaderSchema = responseHeaderSchema;\n this.#externalDocs = externalDocs;\n this.#links = links;\n this.#callbacks = callbacks;\n this.#fileUpload = fileUpload;\n this.#cacheTags = cacheTags;\n }\n\n /** Define the request body schema. Validation failures return 422 Problem Details. */\n body<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TSchema,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n schema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Define the query string schema (must be an object schema). Validation failures return 422. */\n query<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n InferType<TSchema>,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n schema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Define an expected request headers schema (must be an object schema). */\n headers<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n InferType<TSchema>,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n schema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Declare DI services to be resolved per-request and passed as the second handler argument. */\n inject<\n TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>\n >(\n schemas: TSchemas\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TSchemas,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n schemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as requiring authorization.\n *\n * Overloads:\n * - `authorize(principalSchema, ...roles)` — typed principal, optional role requirements\n * - `authorize(...roles)` — untyped principal (`unknown`), optional role requirements\n *\n * If no roles are specified, any authenticated user is allowed.\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n InferType<TSchema>,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n >;\n authorize(\n ...roles: TRoles[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n unknown,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n >;\n authorize(\n ...args: unknown[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n any,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n // First argument is a schema — remaining are roles\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n\n // Merge with inherited auth roles\n const merged = this.#authRoles ? [...this.#authRoles, ...roles] : roles;\n\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n merged,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as public — no authentication required.\n *\n * Calling `.public()` sets `authRoles` to `null`, overriding any\n * previously set authorization requirements (from `.authorize()` or\n * an inherited scoped factory).\n */\n public(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n null,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare the response type for OpenAPI spec generation.\n *\n * Overloads:\n * - `returns<T>()` — generic type only, no runtime schema\n * - `returns(schema)` — provides a schema for spec generation and type inference\n */\n returns<T>(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n T,\n TResponses,\n TUpload\n >;\n returns<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TSchema,\n TResponses,\n TUpload\n >;\n returns(\n _schema?: unknown\n ): EndpointBuilder<any, any, any, any, any, any, any, any, any, any> {\n const schema =\n _schema != null &&\n typeof _schema === 'object' &&\n 'introspect' in _schema\n ? (_schema as SchemaBuilder<any, any, any, any, any>)\n : null;\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n schema ?? this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare per-status-code response schemas for OpenAPI generation and\n * handler return-type enforcement.\n *\n * Pass `null` as the schema for body-less codes (e.g. 204).\n *\n * @example\n * ```ts\n * const ep = endpoint\n * .get('/api/todos/:id')\n * .responses({\n * 200: object({ id: number(), title: string() }),\n * 404: object({ message: string() }),\n * });\n *\n * const handler: Handler<typeof ep> = ({ params }) => {\n * const todo = todos.get(params.id);\n * if (!todo) return ActionResult.notFound({ message: 'Not found' });\n * return todo; // plain object → 200\n * };\n * ```\n */\n responses<\n const T extends Record<\n number,\n SchemaBuilder<any, any, any, any, any> | null\n >\n >(\n map: T\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n InferResponsesMap<T>,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n map,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Short, human-readable summary for OpenAPI operation objects. */\n summary(\n text: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n text,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Longer description for OpenAPI operation objects. Supports Markdown. */\n description(\n text: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n text,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** OpenAPI tags grouping this operation in generated documentation. */\n tags(\n ...tags: string[]\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** A unique, stable identifier for this operation in OpenAPI spec. */\n operationId(\n id: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n id,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /** Mark this endpoint as deprecated in OpenAPI spec output. */\n deprecated(): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n true,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Provide a single example value for the request body.\n *\n * Emitted as the `example` field on the OpenAPI Media Type Object\n * (`application/json`). Pre-fills the \"Try it out\" panel in Swagger UI.\n *\n * @param value - An example request body value.\n */\n example(\n value: TBody\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n value,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Provide named examples for the request body.\n *\n * Each entry is emitted under the `examples` map of the OpenAPI Media Type\n * Object following the Example Object shape (`{ summary?, description?, value }`).\n *\n * @param map - A record of named examples.\n */\n examples(\n map: Record<\n string,\n { summary?: string; description?: string; value: TBody }\n >\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n map,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare that this endpoint produces a binary file response.\n *\n * When set, the OpenAPI spec emits a binary content type instead of a JSON\n * schema for the success response. Takes precedence over `.returns()`.\n *\n * @param contentType - MIME type (default: `'application/octet-stream'`).\n * @param description - Optional response description for the spec.\n */\n producesFile(\n contentType?: string,\n description?: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n { contentType, description },\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Mark this endpoint as accepting `multipart/form-data` file uploads.\n *\n * When set, the server parses the request body with a streaming multipart\n * parser instead of the default JSON deserializer. File fields are made\n * available to the handler via `arg.files` (a `Record<string, FilePart>`),\n * while non-file form fields are validated against the body schema and\n * available via `arg.body`.\n *\n * @param options - Upload configuration (max file size, allowed MIME types, etc.).\n *\n * @example\n * ```ts\n * const UploadAvatar = endpoint\n * .post('/api/avatar')\n * .upload({ maxFileSize: 2 * 1024 * 1024, allowedMimeTypes: ['image/*'] })\n * .authorize(PrincipalSchema)\n * .responses({ 200: AvatarSchema });\n *\n * const handler: Handler<typeof UploadAvatar> = async ({ files }) => {\n * const avatar = files['avatar'];\n * // avatar: { filename, mimeType, buffer, size }\n * };\n * ```\n */\n upload(\n options?: UploadOptions\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n true\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n {\n maxFileSize: options?.maxFileSize ?? 10 * 1024 * 1024,\n allowedMimeTypes: options?.allowedMimeTypes,\n maxFileCount: options?.maxFileCount ?? 10\n },\n this.#cacheTags\n );\n }\n\n /**\n * The full path for this endpoint, combining `basePath` and `pathTemplate`.\n *\n * For static routes this is the exact URL path (e.g. `\"/todos\"`).\n * For dynamic routes the template placeholders are included\n * (e.g. `\"/todos/:id\"`).\n */\n get path(): string {\n const base = this.#basePath;\n const tpl = this.#pathTemplate;\n let suffix: string;\n if (typeof tpl === 'string') {\n suffix = tpl;\n } else {\n const { literals, segments } = tpl.introspect().templateDefinition;\n let s = '';\n for (let i = 0; i < segments.length; i++) {\n s += literals[i] + `:${segments[i].path}`;\n }\n s += literals[segments.length] ?? '';\n suffix = s;\n }\n if (suffix === '/') return base || '/';\n return base + suffix;\n }\n\n /** Return an immutable snapshot of this builder's configuration as {@link EndpointMetadata}. */\n introspect(): EndpointMetadata {\n return {\n method: this.#method,\n basePath: this.#basePath,\n pathTemplate: this.#pathTemplate,\n bodySchema: this.#bodySchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema,\n serviceSchemas: this.#serviceSchemas,\n authRoles: this.#authRoles,\n summary: this.#summary,\n description: this.#description,\n tags: this.#tags,\n operationId: this.#operationId,\n deprecated: this.#deprecated,\n responseSchema: this.#responseSchema,\n responsesSchemas: this.#responsesSchemas,\n example: this.#example,\n examples: this.#examples,\n producesFile: this.#producesFile,\n produces: this.#produces,\n responseHeaderSchema: this.#responseHeaderSchema,\n externalDocs: this.#externalDocs,\n links: this.#links,\n callbacks: this.#callbacks,\n fileUpload: this.#fileUpload,\n cacheTags: this.#cacheTags\n };\n }\n\n /**\n * Declare that this endpoint can produce responses in multiple content types.\n *\n * Each key is a MIME type. Provide an optional `schema` to override the\n * default response schema for that type; otherwise the schema from\n * `.returns()` / `.responses()` is reused for every declared content type.\n *\n * When used alongside `.producesFile()`, the binary response takes precedence.\n *\n * @param contentTypes - Map of MIME type → optional schema override.\n *\n * @example\n * ```ts\n * endpoint.get('/api/items')\n * .returns(object({ id: number(), name: string() }))\n * .produces({\n * 'text/csv': {},\n * 'application/xml': { schema: string() }\n * })\n * ```\n */\n produces(\n contentTypes: Record<\n string,\n { schema?: SchemaBuilder<any, any, any, any, any> }\n >\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n contentTypes,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare response headers emitted by this endpoint.\n *\n * The object schema's properties become header names in the OpenAPI spec;\n * each property's sub-schema and description are emitted in the `headers`\n * map on every response code.\n *\n * @param schema - Object schema whose properties describe the response headers.\n *\n * @example\n * ```ts\n * endpoint.get('/api/items')\n * .responseHeaders(object({\n * 'X-Total-Count': number().describe('Total number of items'),\n * 'X-Page': number()\n * }))\n * ```\n */\n responseHeaders<\n TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>\n >(\n schema: TSchema\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n schema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Link external documentation to this operation.\n *\n * Emitted as the `externalDocs` field on the OpenAPI Operation Object.\n *\n * @param url - The URL to the external documentation.\n * @param description - Optional short description of the external docs.\n */\n externalDocs(\n url: string,\n description?: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n { url, description },\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare response links for OpenAPI spec generation.\n *\n * Links describe follow-up actions that can be taken based on the response,\n * emitted under the primary 2xx response's `links` map.\n *\n * @param defs - Record mapping link names to {@link LinkDefinition} objects.\n *\n * @example\n * ```ts\n * endpoint.get('/api/users/:id')\n * .returns(UserSchema)\n * .links({\n * GetUser: {\n * operationId: 'getUser',\n * parameters: (r) => ({ id: r.id }),\n * },\n * })\n * ```\n */\n links(\n defs: Record<string, LinkDefinition<TResponse>>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n defs as Record<string, LinkDefinition>,\n this.#callbacks,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare callbacks for OpenAPI spec generation.\n *\n * Callbacks describe async out-of-band requests that may be sent to a URL\n * provided in the request body, emitted as the `callbacks` field on the\n * OpenAPI Operation Object.\n *\n * @param defs - Record mapping callback names to {@link CallbackDefinition} objects.\n *\n * @example\n * ```ts\n * endpoint.post('/api/subscriptions')\n * .body(object({ callbackUrl: string() }))\n * .callbacks({\n * onEvent: {\n * urlFrom: (b) => b.callbackUrl,\n * method: 'POST',\n * body: EventSchema,\n * },\n * })\n * ```\n */\n callbacks(\n defs: Record<string, CallbackDefinition<TBody>>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses,\n TUpload\n > {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n defs as Record<string, CallbackDefinition>,\n this.#fileUpload,\n this.#cacheTags\n );\n }\n\n /**\n * Declare a cache group for this endpoint.\n *\n * Use on GET / query endpoints to group responses into a named cache.\n * The client-side {@code cacheTags} middleware caches responses keyed\n * by this tag and flushes matching entries when a mutation calls\n * {@link clearsCacheTag}.\n *\n * @overload Simple tag (no properties — single cache entry).\n * @overload Tag with property descriptors for fine-grained keys.\n *\n * @example\n * ```ts\n * // GET — responses cached under \"todo\" group, keyed by id\n * endpoint.get('/api/todos/:id')\n * .cacheTag('todo', p => ({\n * id: p.params.id\n * }))\n * ```\n */\n cacheTag(\n name: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n cacheTag(\n name: string,\n selector: (\n tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>\n ) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n cacheTag(\n name: string,\n selector?: (tree: any) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n > {\n return this.clearsCacheTag(name, selector!);\n }\n\n /**\n * Declare which cache groups are cleared when this mutation succeeds.\n *\n * Use on POST / PUT / PATCH / DELETE endpoints. When the mutation\n * completes, the {@code cacheTags} client middleware invalidates all\n * cache entries matching the declared tag names (prefix match).\n *\n * @overload Simple tag (clears all entries prefixed with the name).\n * @overload Tag with property descriptors for targeted invalidation.\n *\n * @example\n * ```ts\n * // PATCH — clears \"todo-list\" and \"todo:id=42\" on success\n * endpoint.patch('/api/todos/:id')\n * .clearsCacheTag('todo-list')\n * .clearsCacheTag('todo', p => ({\n * id: p.params.id\n * }))\n * ```\n */\n clearsCacheTag(\n name: string\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n clearsCacheTag(\n name: string,\n selector: (\n tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>\n ) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n >;\n clearsCacheTag(\n name: string,\n selector?: (tree: any) => Record<string, unknown>\n ): EndpointBuilder<\n TParams,\n TBody,\n TQuery,\n THeaders,\n TServices,\n TPrincipal,\n TRoles,\n TResponse,\n TResponses\n > {\n if (!selector) {\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n [...this.#cacheTags, { name, properties: {} }]\n );\n }\n\n const paramsSchema = extractParamsObjectSchema(this.#pathTemplate);\n\n const tree = createCacheTagTree({\n paramsSchema,\n bodySchema: this.#bodySchema,\n querySchema: this.#querySchema,\n headerSchema: this.#headerSchema\n });\n\n const descriptors = selector(tree);\n\n if (typeof descriptors !== 'object' || descriptors === null) {\n throw new Error(\n `Cache tag \"${name}\": selector must return an object ` +\n `with property descriptors (e.g. { id: p.query.id }).`\n );\n }\n\n const definition = serializeTag(name, descriptors);\n\n return new EndpointBuilder(\n this.#method,\n this.#basePath,\n this.#pathTemplate,\n this.#bodySchema,\n this.#querySchema,\n this.#headerSchema,\n this.#serviceSchemas,\n this.#authRoles,\n this.#summary,\n this.#description,\n this.#tags,\n this.#operationId,\n this.#deprecated,\n this.#responseSchema,\n this.#responsesSchemas,\n this.#example,\n this.#examples,\n this.#producesFile,\n this.#produces,\n this.#responseHeaderSchema,\n this.#externalDocs,\n this.#links,\n this.#callbacks,\n this.#fileUpload,\n [...this.#cacheTags, definition]\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// endpoint factory — creates EndpointBuilder instances\n// ---------------------------------------------------------------------------\n\n/**\n * Optional OpenAPI metadata fields accepted by `createEndpoint` / `createEndpoints`.\n */\nexport type EndpointMetadataDescriptors = {\n readonly summary?: string;\n readonly description?: string;\n readonly tags?: string[];\n readonly operationId?: string;\n readonly deprecated?: boolean;\n};\n\nfunction createEndpoint<TParams>(\n method: string,\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>,\n authRoles?: readonly string[] | null,\n meta?: EndpointMetadataDescriptors\n): EndpointBuilder<TParams, undefined, {}, {}, {}, any, string, any, {}>;\n\nfunction createEndpoint(\n method: string,\n basePath: string,\n pathTemplate?: RoutePath,\n authRoles?: readonly string[] | null,\n meta?: EndpointMetadataDescriptors\n): EndpointBuilder<any> {\n return new EndpointBuilder(\n method,\n basePath,\n pathTemplate ?? '/',\n null,\n null,\n null,\n null,\n authRoles ?? null,\n meta?.summary ?? null,\n meta?.description ?? null,\n meta?.tags ?? [],\n meta?.operationId ?? null,\n meta?.deprecated ?? false,\n null,\n null,\n null,\n null,\n null,\n null,\n null\n );\n}\n\n// ---------------------------------------------------------------------------\n// ScopedEndpointFactory — resource-scoped endpoint creation\n// ---------------------------------------------------------------------------\n\ntype ScopedEndpointFactoryMethods<\n TPrincipal,\n TRoles extends string = string\n> = {\n get<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n post<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n put<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n patch<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n delete<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n head<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n options<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n post<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n put<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n patch<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n delete<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n head<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n options<TParams = {}>(\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<\n TParams extends undefined ? {} : TParams,\n undefined,\n {},\n {},\n {},\n TPrincipal,\n TRoles,\n any,\n {}\n >;\n /** Returns factory methods where all endpoints are public (no auth). */\n public(): ScopedEndpointFactoryMethods<TPrincipal, TRoles>;\n};\n\nexport type ScopedEndpointFactory<TRoles extends string = string> =\n ScopedEndpointFactoryMethods<undefined, TRoles> & {\n /**\n * Returns a new resource factory where all endpoints inherit\n * the given authorization requirements.\n *\n * - `authorize(principalSchema, ...roles)` — typed principal\n * - `authorize(...roles)` — untyped principal\n */\n authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(\n principalSchema: TSchema,\n ...roles: TRoles[]\n ): ScopedEndpointFactoryMethods<InferType<TSchema>, TRoles>;\n authorize(\n ...roles: TRoles[]\n ): ScopedEndpointFactoryMethods<unknown, TRoles>;\n /** Returns factory methods where all endpoints are public (no auth). */\n public(): ScopedEndpointFactoryMethods<TRoles>;\n };\n\nfunction createScopedFactoryMethods(\n basePath: string,\n authRoles: readonly string[] | null\n): ScopedEndpointFactoryMethods<any> {\n return {\n get: (pathTemplate?) =>\n createEndpoint('GET', basePath, pathTemplate, authRoles),\n post: (pathTemplate?) =>\n createEndpoint('POST', basePath, pathTemplate, authRoles),\n put: (pathTemplate?) =>\n createEndpoint('PUT', basePath, pathTemplate, authRoles),\n patch: (pathTemplate?) =>\n createEndpoint('PATCH', basePath, pathTemplate, authRoles),\n delete: (pathTemplate?) =>\n createEndpoint('DELETE', basePath, pathTemplate, authRoles),\n head: (pathTemplate?) =>\n createEndpoint('HEAD', basePath, pathTemplate, authRoles),\n options: (pathTemplate?) =>\n createEndpoint('OPTIONS', basePath, pathTemplate, authRoles),\n public: () => createScopedFactoryMethods(basePath, null)\n };\n}\n\nfunction createScopedFactory(basePath: string): ScopedEndpointFactory {\n return {\n ...createScopedFactoryMethods(basePath, null),\n authorize(...args: unknown[]): ScopedEndpointFactoryMethods<any> {\n let roles: string[];\n if (\n args.length > 0 &&\n typeof args[0] === 'object' &&\n args[0] !== null &&\n 'introspect' in args[0]\n ) {\n roles = args.slice(1) as string[];\n } else {\n roles = args as string[];\n }\n return createScopedFactoryMethods(basePath, roles);\n },\n public(): ScopedEndpointFactoryMethods<any> {\n return createScopedFactoryMethods(basePath, null);\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// endpoint factory — creates EndpointBuilder instances\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts an ObjectSchemaBuilder from a ParseStringSchemaBuilder path template.\n * Used for constructing the synthetic cache tag tree.\n */\nfunction extractParamsObjectSchema(\n pathTemplate: RoutePath\n): ObjectSchemaBuilder<any, any, any, any, any, any, any> | null {\n if (\n pathTemplate &&\n typeof pathTemplate !== 'string' &&\n typeof (pathTemplate as any).introspect === 'function'\n ) {\n const info = (pathTemplate as any).introspect();\n if (info.objectSchema) {\n return info.objectSchema;\n }\n }\n return null;\n}\n\ntype EndpointFactory<TRoles extends string = string> = {\n get<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n post<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n put<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n patch<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n delete<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n head<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n options<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;\n resource(basePath: string): ScopedEndpointFactory<TRoles>;\n subscription<TParams = {}>(\n basePath: string,\n pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>\n ): SubscriptionBuilder<TParams, {}, {}, {}, undefined, TRoles>;\n};\n\n/**\n * Create a role-constrained endpoint factory. Roles are defined as a plain\n * `as const` object whose *values* become the string-literal union accepted\n * by `authorize()`.\n *\n * @example\n * ```ts\n * const Roles = { admin: 'admin', editor: 'editor' } as const;\n * const ep = createEndpoints(Roles);\n * ep.get('/api/admin').authorize(IPrincipal, 'admin'); // ✓\n * ep.get('/api/admin').authorize(IPrincipal, 'typo'); // ✗ type error\n * ```\n */\nexport function createEndpoints<const T extends Record<string, string>>(\n _roles: T\n): EndpointFactory<T[keyof T]> {\n return endpoint as EndpointFactory<T[keyof T]>;\n}\n\n/**\n * The global endpoint factory singleton.\n *\n * Creates `EndpointBuilder` instances for each HTTP method. Use\n * {@link createEndpoints} to get a role-constrained version.\n *\n * @example\n * ```ts\n * import { endpoint } from '@cleverbrush/server';\n *\n * const GetUsers = endpoint.get('/api/users');\n * const CreateUser = endpoint.post('/api/users').body(CreateUserSchema);\n * ```\n */\nexport const endpoint: EndpointFactory = {\n get: (basePath, pathTemplate?) =>\n createEndpoint('GET', basePath, pathTemplate),\n post: (basePath, pathTemplate?) =>\n createEndpoint('POST', basePath, pathTemplate),\n put: (basePath, pathTemplate?) =>\n createEndpoint('PUT', basePath, pathTemplate),\n patch: (basePath, pathTemplate?) =>\n createEndpoint('PATCH', basePath, pathTemplate),\n delete: (basePath, pathTemplate?) =>\n createEndpoint('DELETE', basePath, pathTemplate),\n head: (basePath, pathTemplate?) =>\n createEndpoint('HEAD', basePath, pathTemplate),\n options: (basePath, pathTemplate?) =>\n createEndpoint('OPTIONS', basePath, pathTemplate),\n resource: createScopedFactory,\n subscription: (basePath, pathTemplate?) =>\n createSubscription(basePath, pathTemplate)\n};\n","import {\n type InferType,\n type ObjectSchemaBuilder,\n object,\n type ParseStringSchemaBuilder,\n type ParseStringTemplateTag,\n type PropertyDescriptor,\n type PropertyDescriptorTree,\n parseString,\n type SchemaBuilder\n} from '@cleverbrush/schema';\n\ntype RouteTemplateTag<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n> = (\n strings: TemplateStringsArray,\n ...selectors: Array<\n (\n tree: PropertyDescriptorTree<\n ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >,\n ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >,\n string | number | boolean | Date\n >\n ) => PropertyDescriptor<\n ObjectSchemaBuilder<TProps, true, false, undefined, false, {}, []>,\n any,\n any\n >\n >\n) => ParseStringSchemaBuilder<\n InferType<\n ObjectSchemaBuilder<TProps, true, false, undefined, false, {}, []>\n >\n>;\n\nfunction createRouteTag<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n>(props: TProps): RouteTemplateTag<TProps> {\n type TSchema = ObjectSchemaBuilder<\n TProps,\n true,\n false,\n undefined,\n false,\n {},\n []\n >;\n const objectSchema = object(props) as unknown as TSchema;\n\n return ((strings: TemplateStringsArray, ...selectors: any[]) =>\n parseString(objectSchema, ($t: ParseStringTemplateTag<TSchema>) =>\n ($t as any)(strings, ...selectors)\n )) as any;\n}\n\n// Overload: route`/some/path` — used directly as a tagged template (no params)\nexport function route(\n strings: TemplateStringsArray,\n ...selectors: never[]\n): ParseStringSchemaBuilder<\n InferType<ObjectSchemaBuilder<{}, true, false, undefined, false, {}, []>>\n>;\n\n// Overload: route() — called with no args, returns a tagged template (no params)\nexport function route(): RouteTemplateTag<{}>;\n\n// Overload: route({ id: number() }) — called with props, returns a tagged template\nexport function route<\n TProps extends Record<string, SchemaBuilder<any, any, any, any, any>>\n>(props: TProps): RouteTemplateTag<TProps>;\n\n/**\n * Concise shorthand for defining a typed path template.\n *\n * When multiple registered templates validate the same URL, the server ranks\n * them by specificity: exact static routes first, then routes with more\n * literal path segments, then routes with fewer dynamic segments.\n * Registration order breaks ties between equally specific routes. This\n * precedence applies to both HTTP endpoints and WebSocket subscriptions.\n *\n * @example With parameters\n * ```ts\n * const TodoById = route({ id: number().coerce() })`/${t => t.id}`;\n * ```\n *\n * @example Static path (no parameters)\n * ```ts\n * const Path = route`/some/path`;\n * // or\n * const Path = route()`/some/path`;\n * ```\n *\n * @param propsOrStrings - Either a property map for typed path segments,\n * or a `TemplateStringsArray` when used directly as a tagged template.\n * @returns A `ParseStringSchemaBuilder`, or a tagged-template function\n * that produces one.\n */\nexport function route(propsOrStrings?: any, ..._rest: any[]): any {\n // route`/some/path` — called as tagged template directly\n if (\n propsOrStrings != null &&\n Array.isArray((propsOrStrings as TemplateStringsArray).raw)\n ) {\n return createRouteTag({})(propsOrStrings as TemplateStringsArray);\n }\n\n // route() or route({...})\n return createRouteTag(propsOrStrings ?? {});\n}\n","/**\n * Browser-safe entry point for `@cleverbrush/server`.\n *\n * Re-exports the endpoint builder, factory functions and type helpers\n * needed to **define** API contracts without pulling in the Node.js server\n * runtime. Use this entry point in shared packages that are consumed by\n * both the backend (Node.js) and the frontend (browser).\n *\n * @example\n * ```ts\n * import { defineApi, endpoint, route } from '@cleverbrush/server/contract';\n * import { array, number, object, string } from '@cleverbrush/schema';\n *\n * const TodoSchema = object({ id: number(), title: string() });\n *\n * export const api = defineApi({\n * todos: {\n * list: endpoint.resource('/api/todos').get()\n * .responses({ 200: array(TodoSchema) }),\n * get: endpoint.resource('/api/todos').get(\n * route({ id: number().coerce() })`/${t => t.id}`\n * ).responses({ 200: TodoSchema }),\n * },\n * });\n * ```\n *\n * @module\n */\n\nexport type {\n CacheTagDefinition,\n CacheTagPropertyAccessor\n} from './CacheTag.js';\nexport {\n type ActionContext,\n type AllowedResponseReturn,\n type CallbackDefinition,\n createEndpoints,\n EndpointBuilder,\n type EndpointMetadata,\n type EndpointMetadataDescriptors,\n endpoint,\n type Handler,\n type LinkDefinition,\n type PropertyRefTree,\n type ResponsesOf,\n type ScopedEndpointFactory\n} from './Endpoint.js';\nexport { route } from './route.js';\nexport {\n SubscriptionBuilder,\n type SubscriptionContext,\n type SubscriptionHandler,\n type SubscriptionHandlerEntry,\n type SubscriptionMetadata,\n type TrackedEvent,\n tracked\n} from './Subscription.js';\nexport type { FilePart, UploadOptions } from './types.js';\n\nimport type { EndpointBuilder as _EB } from './Endpoint.js';\nimport type { SubscriptionBuilder as _SB } from './Subscription.js';\n\n// ---------------------------------------------------------------------------\n// defineApi — typed, one-level API contract grouping\n// ---------------------------------------------------------------------------\n\n/**\n * A record of named {@link EndpointBuilder} or {@link SubscriptionBuilder}\n * instances that form a single logical API group (e.g. \"todos\", \"auth\", \"live\").\n */\nexport type ApiGroup = Record<\n string,\n | _EB<any, any, any, any, any, any, any, any, any>\n | _SB<any, any, any, any, any, any, any, any>\n>;\n\n/**\n * A typed API contract with one level of grouping.\n *\n * Each key is a group name and each value is an {@link ApiGroup} — a\n * record of named endpoints.\n *\n * @example\n * ```ts\n * const contract: ApiContract = {\n * todos: { list: todosResource.get(), create: todosResource.post() },\n * auth: { login: endpoint.post('/api/auth/login') },\n * };\n * ```\n */\nexport type ApiContract = Record<string, ApiGroup>;\n\n/**\n * Defines a typed API contract with one level of grouping.\n *\n * The returned object is the **single source of truth** for both server and\n * client. The server imports it and extends each endpoint with\n * `.authorize()`, `.inject()`, and OpenAPI metadata. The client passes it\n * to `createClient()` from `@cleverbrush/client` to obtain a fully typed HTTP\n * client.\n *\n * At runtime the contract (and each group within it) is frozen with\n * `Object.freeze` to prevent accidental mutation — endpoint builders are\n * immutable by design, so every `.body()` / `.query()` / etc. call already\n * returns a **new** builder.\n *\n * @typeParam T - The exact shape of the contract, inferred from the argument.\n * @param contract - A record of named groups, each containing named endpoints.\n * @returns The same object, frozen and typed as `Readonly<T>`.\n *\n * @example\n * ```ts\n * import { defineApi, endpoint, route } from '@cleverbrush/server/contract';\n * import { array, number, object, string } from '@cleverbrush/schema';\n *\n * const TodoSchema = object({ id: number(), title: string(), completed: boolean() });\n * const todosResource = endpoint.resource('/api/todos');\n * const ById = route({ id: number().coerce() })`/${t => t.id}`;\n *\n * export const api = defineApi({\n * todos: {\n * list: todosResource.get()\n * .query(object({ page: number().optional(), limit: number().optional() }))\n * .responses({ 200: array(TodoSchema) }),\n * get: todosResource.get(ById)\n * .responses({ 200: TodoSchema }),\n * create: todosResource.post()\n * .body(object({ title: string() }))\n * .responses({ 201: TodoSchema }),\n * },\n * auth: {\n * login: endpoint.post('/api/auth/login')\n * .body(object({ email: string(), password: string() }))\n * .responses({ 200: object({ token: string() }) }),\n * },\n * });\n * ```\n */\nexport function defineApi<T extends ApiContract>(contract: T): Readonly<T> {\n for (const group of Object.values(contract)) {\n Object.freeze(group);\n }\n return Object.freeze(contract);\n}\n\n// ---------------------------------------------------------------------------\n// Contract composition utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Computes the merged type of two {@link ApiContract} objects.\n *\n * - Groups that only exist in `A` are kept as-is.\n * - Groups that only exist in `B` are kept as-is.\n * - Groups whose key appears in **both** `A` and `B` have their endpoint maps\n * intersected (`A[K] & B[K]`), making all endpoints from both sources\n * visible on the merged group.\n */\nexport type MergedContracts<A extends ApiContract, B extends ApiContract> = {\n readonly [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? A[K] & B[K]\n : A[K]\n : K extends keyof B\n ? B[K]\n : never;\n};\n\n/**\n * Merges two API contracts into one.\n *\n * This is the primary building block for **audience-scoped bundles**: define\n * a `publicApi` that is safe to ship to every client, and a separate\n * `adminApi` that is only imported by the admin application. Combine them\n * at the admin entry point with `mergeContracts`.\n *\n * - Groups that exist in only one contract are passed through unchanged.\n * - Groups that share a key have their endpoint maps **shallowly merged**\n * (later endpoints with the same name override earlier ones, same semantics\n * as `Object.assign`).\n * - The returned contract is frozen, matching the invariant of\n * {@link defineApi}.\n *\n * @typeParam A - The shape of the first contract.\n * @typeParam B - The shape of the second contract.\n * @param a - The base contract (e.g. the public API).\n * @param b - The contract to merge in (e.g. admin-only groups).\n * @returns A new frozen contract whose type is {@link MergedContracts}`<A, B>`.\n *\n * @example\n * ```ts\n * // shared/public-api.ts — safe to import in the client bundle\n * export const publicApi = defineApi({\n * todos: { list: ..., get: ..., create: ... },\n * auth: { login: ..., register: ... },\n * });\n *\n * // shared/admin-api.ts — only imported by the admin application\n * const adminApi = defineApi({\n * admin: { activityLog: ..., banUser: ... },\n * });\n *\n * // admin-app/contract.ts\n * import { mergeContracts } from '@cleverbrush/server/contract';\n * export const fullApi = mergeContracts(publicApi, adminApi);\n * // TypeScript sees: { todos, auth, admin } — fully typed\n *\n * // client-app/contract.ts\n * import { publicApi } from 'shared/public-api';\n * // TypeScript sees: { todos, auth } — admin groups are absent\n * ```\n */\nexport function mergeContracts<A extends ApiContract, B extends ApiContract>(\n a: A,\n b: B\n): Readonly<MergedContracts<A, B>> {\n const result: Record<string, ApiGroup> = {};\n\n for (const key of Object.keys(a) as (keyof A & string)[]) {\n result[key] = { ...a[key] };\n }\n\n for (const key of Object.keys(b) as (keyof B & string)[]) {\n if (Object.hasOwn(result, key)) {\n result[key] = { ...result[key], ...b[key] };\n } else {\n result[key] = { ...b[key] };\n }\n }\n\n for (const group of Object.values(result)) {\n Object.freeze(group);\n }\n\n return Object.freeze(result) as unknown as Readonly<MergedContracts<A, B>>;\n}\n\n/**\n * Returns a new contract containing only the specified groups.\n *\n * The TypeScript return type is `Pick<T, K>` — the compiler sees exactly the\n * selected groups and no others. This provides full type safety on the\n * narrowed contract when passed to `createClient()` or used as a server\n * handler map.\n *\n * @typeParam T - The shape of the source contract.\n * @typeParam K - The union of group keys to keep.\n * @param contract - The contract to select from.\n * @param groups - The group keys to include.\n * @returns A new frozen contract with only the listed groups.\n *\n * @example\n * ```ts\n * const fullApi = defineApi({ todos: {...}, auth: {...}, admin: {...}, debug: {...} });\n *\n * // Pick only the groups the frontend needs\n * const clientApi = pickGroups(fullApi, 'todos', 'auth');\n * // TypeScript: { todos: ..., auth: ... }\n * ```\n */\nexport function pickGroups<T extends ApiContract, K extends keyof T>(\n contract: T,\n ...groups: K[]\n): Readonly<Pick<T, K>> {\n const result = {} as Pick<T, K>;\n for (const key of groups) {\n (result as Record<string, ApiGroup>)[key as string] = contract[key];\n Object.freeze((result as Record<string, ApiGroup>)[key as string]);\n }\n return Object.freeze(result);\n}\n\n/**\n * Returns a new contract with the specified groups removed.\n *\n * The TypeScript return type is `Omit<T, K>` — the listed groups are absent\n * at both runtime and compile time. Useful for stripping debug, internal, or\n * admin groups before sharing a contract with less-privileged consumers.\n *\n * @typeParam T - The shape of the source contract.\n * @typeParam K - The union of group keys to remove.\n * @param contract - The contract to omit from.\n * @param groups - The group keys to exclude.\n * @returns A new frozen contract without the listed groups.\n *\n * @example\n * ```ts\n * const fullApi = defineApi({ todos: {...}, auth: {...}, admin: {...}, debug: {...} });\n *\n * // Strip internal groups before exporting to clients\n * const publicApi = omitGroups(fullApi, 'admin', 'debug');\n * // TypeScript: { todos: ..., auth: ... }\n * ```\n */\nexport function omitGroups<T extends ApiContract, K extends keyof T>(\n contract: T,\n ...groups: K[]\n): Readonly<Omit<T, K>> {\n const excluded = new Set<string>(groups as string[]);\n const result = {} as Omit<T, K>;\n for (const key of Object.keys(contract)) {\n if (!excluded.has(key)) {\n (result as Record<string, ApiGroup>)[key] = contract[key];\n Object.freeze((result as Record<string, ApiGroup>)[key]);\n }\n }\n return Object.freeze(result);\n}\n"],"mappings":"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","names":["object","SYMBOL_HAS_PROPERTIES","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","createCacheTagTree","schemas","props","ps","bs","syn","isPropertyDescriptor","value","serializeTag","name","descriptors","properties","key","inner","root","computeCacheKey","tag","entries","parts","accessor","a","b","result","TRACKED_SYMBOL","tracked","id","data","isTrackedEvent","value","SubscriptionBuilder","_SubscriptionBuilder","#basePath","#pathTemplate","#incomingSchema","#outgoingSchema","#querySchema","#headerSchema","#serviceSchemas","#authRoles","#summary","#description","#tags","#operationId","#deprecated","#externalDocs","basePath","pathTemplate","incomingSchema","outgoingSchema","querySchema","headerSchema","serviceSchemas","authRoles","summary","description","tags","operationId","deprecated","externalDocs","#clone","overrides","schema","schemas","args","roles","merged","text","url","createSubscription","isSubscriptionBuilder","mapHandlers","endpoints","handlers","entries","subscriptions","groupKey","group","handlerGroup","endpointKey","ep","entry","handler","middlewares","isSubscriptionBuilder","EndpointBuilder","_EndpointBuilder","#method","#basePath","#pathTemplate","#bodySchema","#querySchema","#headerSchema","#serviceSchemas","#authRoles","#summary","#description","#tags","#operationId","#deprecated","#responseSchema","#responsesSchemas","#example","#examples","#producesFile","#produces","#responseHeaderSchema","#externalDocs","#links","#callbacks","#fileUpload","#cacheTags","method","basePath","pathTemplate","bodySchema","querySchema","headerSchema","serviceSchemas","authRoles","summary","description","tags","operationId","deprecated","responseSchema","responsesSchemas","example","examples","producesFile","produces","responseHeaderSchema","externalDocs","links","callbacks","fileUpload","cacheTags","schema","schemas","args","roles","merged","_schema","map","text","id","value","contentType","options","base","tpl","suffix","literals","segments","s","i","contentTypes","url","defs","name","selector","paramsSchema","extractParamsObjectSchema","tree","createCacheTagTree","descriptors","definition","serializeTag","createEndpoint","meta","createScopedFactoryMethods","createScopedFactory","info","createEndpoints","_roles","endpoint","createSubscription","object","parseString","createRouteTag","props","objectSchema","strings","selectors","$t","route","propsOrStrings","_rest","defineApi","contract","group","mergeContracts","a","b","result","key","pickGroups","groups","omitGroups","excluded"]}