// Single responsibility: the blessed `t` namespace. One import, one way to declare a schema. // Every member delegates to the active provider, so `configureSchemaProvider()` takes effect // even for modules that captured `t` at import time. import type { ArrayBounds } from './array-schema'; import type { AnySchema, Refinement, Schema, Shape } from './builder'; import type { JsonValue } from './json-value'; import type { MoneyValue } from './money-value'; import { schemaProvider } from './provider'; import type { InferInput, InferOutput, StandardSchemaV1 } from './standard'; import type { NumberSchema, ObjectSchema, StringSchema, TNamespace } from './validators'; function provider(): TNamespace { return schemaProvider().t; } export const t: TNamespace = { get string(): StringSchema { return provider().string; }, get number(): NumberSchema { return provider().number; }, get boolean(): Schema { return provider().boolean; }, get uuid(): StringSchema { return provider().uuid; }, get email(): StringSchema { return provider().email; }, get url(): StringSchema { return provider().url; }, get date(): Schema { return provider().date; }, get money(): Schema { return provider().money; }, get timezone(): StringSchema { return provider().timezone; }, get locale(): StringSchema { return provider().locale; }, get slug(): StringSchema { return provider().slug; }, get cursor(): StringSchema { return provider().cursor; }, /** * `t.json()` — any JSON value: null, a boolean, a finite number, a string, and arrays and plain * objects of them, at most `JSON_MAX_DEPTH` deep. The column it pairs with is entity's * `json(t.json())`, a `jsonb` that stores what it was sent. */ json(): Schema { return provider().json(); }, object(shape: S): ObjectSchema { return provider().object(shape); }, /** * `t.array(t.uuid, { min: 1, max: 20 })` — inclusive item-count bounds on the array itself, so * OpenAPI and the MCP tool schema publish them as `minItems`/`maxItems`. Omitted, any length. */ array( items: S, bounds?: ArrayBounds, ): Schema[], InferOutput[]> { return provider().array(items, bounds); }, enum(values: V): Schema { return provider().enum(values); }, /** * Variadic form of `enum`, and the blessed spelling: it spreads a `const` tuple of codes * straight from the domain package (`t.enumerated(...PLAN_CODES)`) and avoids the reserved * word at the call site. */ enumerated( ...values: V ): Schema { return provider().enum(values); }, literal(value: V): Schema { return provider().literal(value); }, union( ...members: S ): Schema, InferOutput> { return provider().union(...members); }, /** * `t.discriminatedUnion('kind', postBody, pageBody)` — the blessed spelling for a tagged shape. * A failure reports the branch `kind` named, so the caller reads one field error instead of * every branch's reasons concatenated. */ discriminatedUnion( discriminant: string, ...members: S ): Schema, InferOutput> { return provider().discriminatedUnion(discriminant, ...members); }, /** * `t.refine(range, { name: 'end-after-start', message: 'endDate must be after startDate', … })` * — a cross-field rule declared ON the schema, so OpenAPI, the MCP tool schema and the form * binding all state it instead of it living in a handler where no projection can see it. * * Delegates to the method for the reason `t.nullable` does: `refine` composes an existing * schema rather than building one, so a swapped provider's schemas keep their own behaviour. */ refine(schema: Schema, refinement: Refinement): Schema { return schema.refine(refinement); }, record( values: S, ): Schema>>, Record>> { return provider().record(values); }, /** `t.nullable(t.url)` — the column may hold null, which is not the same as being absent. */ nullable(schema: S): Schema | null, InferOutput | null> { return schema.nullable() as Schema | null, InferOutput | null>; }, optional( schema: S, ): Schema | undefined, InferOutput | undefined> { return provider().optional(schema); }, }; /** `type PublishInput = Infer` */ export type Infer = InferOutput;