autotel
Version:
Write Once, Observe Anywhere
105 lines (104 loc) • 4.53 kB
text/typescript
import { r as SchemaLike } from "./define-event-HZRizPwz.cjs";
//#region src/validate.d.ts
type ValidationMode = 'observe' | 'reject';
type ValidationSeverity = 'info' | 'warning' | 'error';
/** A single failing field, stripped of any payload values. */
interface ValidationIssue {
/** Dotted field path, e.g. `items.0.price`. Never a value. */
path: string;
/** Issue code (e.g. Zod's `invalid_type`, `too_small`). Never a value. */
code: string;
/** Declared type/constraint summary, e.g. `string`. Never a received value. */
expected?: string;
}
/** Everything the recorder needs — already PII-stripped by the caller. */
interface ValidationMismatch {
/** Contract id, e.g. `POST /orders` or `order.placed`. */
name: string;
boundary: string;
mode: ValidationMode;
issues: ValidationIssue[];
hash?: string;
severity?: ValidationSeverity;
}
type MismatchListener = (mismatch: ValidationMismatch) => void;
/**
* Register an explicit handler called on every recorded mismatch — the opt-in
* seam for escalating to security events, a webhook, or a custom sink. There is
* no automatic, package-presence-driven escalation: nothing fires here unless
* you (or a package you wire up) register a handler.
*
* Multiple subscribers coexist: a package (e.g. `autotel-audit` bridging to
* security events) and your own app code (a webhook, a logger) can both
* register and all fire. Returns an unsubscribe fn that removes only this
* handler; registering the same function twice is a no-op (Set semantics).
*/
declare function onValidationMismatch(handler: MismatchListener): () => void;
/**
* Record a validation mismatch as telemetry: `validation.*` attributes on the
* active span (if any) and an increment on `autotel.validation.mismatches`.
* Fail-open — never throws, so instrumentation can't break the boundary.
*/
declare function recordValidationMismatch(mismatch: ValidationMismatch): void;
/**
* Normalise an arbitrary validation error into PII-safe issues. Reads only
* `path`, `code`, and (when it is a declared type name) `expected` — and never
* `message`, `received`, or any value-bearing field. Understands the Zod shape
* (`error.issues`) and a generic `error.errors` fallback; returns `[]` for
* anything unrecognised.
*/
declare function formatValidationIssues(error: unknown): ValidationIssue[];
interface DefineValidatorOptions<S> {
/** Where validation runs. Defaults to `input`. */
boundary?: string;
/** `reject` (default): record then throw. `observe`: record then continue. */
onMismatch?: ValidationMode;
/** Project the schema to JSON Schema for a stable `validation.hash`. */
toJsonSchema?: (schema: S) => unknown;
severity?: ValidationSeverity;
/** Build the error thrown in `reject` mode (defaults to a 400 structured error). */
onReject?: (issues: ValidationIssue[], name: string) => Error;
}
type ValidatorResult<T> = {
success: true;
data: T;
} | {
success: false;
issues: ValidationIssue[];
};
interface Validator<T> {
readonly name: string;
readonly mode: ValidationMode;
/** Validate and record on failure; never throws. */
safeParse(input: unknown): ValidatorResult<T>;
/**
* Validate, record on failure, then apply the mode: `reject` throws,
* `observe` returns the raw input so the handler can continue.
*/
parse(input: unknown): T;
}
/**
* Declare an expected input shape once and get a validator that records every
* mismatch as telemetry.
*
* @example
* ```ts
* import { z } from 'zod';
* import { defineValidator } from 'autotel/validate';
*
* const OrderBody = defineValidator('POST /orders', z.object({
* items: z.array(z.object({ sku: z.string(), qty: z.number().int() })),
* }), { boundary: 'http', toJsonSchema: (s) => z.toJSONSchema(s) });
*
* // reject mode (default): records + throws a 400-shaped structured error
* const order = OrderBody.parse(req.body);
*
* // observe mode: records, returns the result, never throws
* const result = OrderBody.safeParse(req.body);
* if (!result.success) metrics.onDrift(result.issues);
* ```
*/
declare function defineValidator<T, S extends SchemaLike<T>>(name: string, schema: S, options?: DefineValidatorOptions<S>): Validator<T>;
//#endregion
export { DefineValidatorOptions, type SchemaLike, ValidationIssue, ValidationMismatch, ValidationMode, ValidationSeverity, Validator, ValidatorResult, defineValidator, formatValidationIssues, onValidationMismatch, recordValidationMismatch };
//# sourceMappingURL=validate.d.cts.map