/** * Copyright (c) 2026 AzerothJS. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * Responses compiled from a schema declaration. * * `json(data)` walks the value at runtime - JSON.stringify introspects every key of every * response. But an API route's response shape is usually DECLARED already (the same * declaration that validates it). jsonEncoder() compiles that declaration ONCE into a * serializer specialized to the shape - key strings prebuilt, field order fixed, primitive * fields encoded inline with escape-guarded quoting - and returns a constructor producing * the kernel's lazy Response. The declaration-driven twin of `readValidated`: one reads the * boundary through the schema, the other writes it. * * Same structural-typing stance as readValidated's SchemaLike: `@azerothjs/schema`'s Schema * satisfies {@link EncodableSchema} without an import coupling - any node whose metadata is * missing or unrecognized (a custom combinator, record/union) falls back to JSON.stringify * for that node, so output always matches what JSON.stringify would produce for declared * shapes. */ import type { SchemaMeta } from '@azerothjs/schema'; /** The structural metadata shape this module compiles from (@azerothjs/schema's SchemaMeta). */ export interface EncoderMeta { /** The declaration kinds, imported from THE declaration so the two unions cannot drift. */ kind: SchemaMeta['kind']; shape?: Record>; /** array: the item schema; record: the value schema. */ item?: EncodableSchema; optional?: boolean; /** Set by .nullable(): null is a valid value and encodes as JSON null. */ nullable?: boolean; /** literal: the expected value - a compile-time constant for the encoder. */ value?: string | number | boolean; } /** * The STRUCTURAL shape of a declaration this module accepts - `@azerothjs/schema`'s Schema * satisfies it. Structural for the SHAPE (any compatible declaration compiles); the kind union * is imported from the declaration package so the two cannot drift. */ export interface EncodableSchema { meta?: EncoderMeta; /** Present so the value type infers from the declaration; never called here. */ parse(value: unknown): T; } /** * Compiles a response declaration into a JSON Response constructor. Declare the shape once * (the same combinators that validate request bodies), build the encoder at module scope, * and return it from handlers - each response serializes through the precompiled shape * instead of JSON.stringify's per-call introspection: * * ```ts * import { object, string } from '@azerothjs/schema'; * import { jsonEncoder } from '@azerothjs/http'; * * const userJson = jsonEncoder(object({ id: string(), name: string() })); * app.get('/users/:id', async (context) => userJson(await loadUser(context.params.id))); * ``` * * Output is byte-identical to `json(data)` for values matching the declaration; a field the * declaration does not cover is simply not emitted (the same boundary discipline object() * applies to input), and any node the metadata cannot describe falls back to JSON.stringify. */ export declare function jsonEncoder(schema: EncodableSchema): (data: T, init?: ResponseInit) => Response;