# Safety Checks Schemas ## Purpose Zod schemas for the configuration types of `@suaveplan/bitpacker-validation`'s safety subsystem: `SafetyLimits` (the absolute caps applied by every `SafetyChecker`), `SafetyPerformanceOptions` (selective skip flags for hot-path code), and `SchemaSafetyLimits` (per-schema embedded `_safety` overrides). Each schema is used at the `SafetyChecker` constructor boundary so a misconfigured limits bundle is rejected at the call site rather than producing surprising allow/deny behaviour deep in a pack/unpack chain. The schemas are deliberately strict — `.strict()` rejects unknown keys, and numeric fields are bounded to non-negative integers because every limit is either a count (`maxRecursionDepth`, `maxArrayLength`, `maxObjectKeys`) or a byte count (`maxBufferSize`). A negative limit would be either a developer mistake or an attempt to bypass safety; either way, refusing it at parse time prevents the SafetyChecker from entering an undefined state. ## Features - `safetyLimitsSchema` — strict, all fields required, numerics non-negative integers - `safetyPerformanceOptionsSchema` — strict, all `skip*` flags optional booleans - `schemaSafetyLimitsSchema` — strict, every override optional, `customValidators` is `string[]` - `as unknown as z.ZodType` casts where Zod's `T | undefined` collides with `exactOptionalPropertyTypes` - `.strict()` on every schema — unknown keys throw ## Usage: Basic Example ```ts import { SafetyChecker } from "@suaveplan/bitpacker-validation"; import { safetyLimitsSchema } from "@suaveplan/bitpacker-validation"; const limits = safetyLimitsSchema.parse({ maxBufferSize: 1024 * 1024, maxRecursionDepth: 32, maxArrayLength: 5000, maxObjectKeys: 200, allowInfiniteFloats: false, allowNaNFloats: false, }); const checker = new SafetyChecker(limits); ``` ## Usage: Advanced Example ```ts import { SafetyChecker, SafetyError, safetyLimitsSchema, safetyPerformanceOptionsSchema, schemaSafetyLimitsSchema, } from "@suaveplan/bitpacker-validation"; function makeSafeChecker(rawLimits: unknown, rawPerf: unknown): SafetyChecker { const limitsResult = safetyLimitsSchema.safeParse(rawLimits); if (!limitsResult.success) { throw new SafetyError( `Invalid safety limits: ${limitsResult.error.message}`, "INVALID_LIMITS" ); } const perfResult = safetyPerformanceOptionsSchema.safeParse(rawPerf); if (!perfResult.success) { throw new SafetyError( `Invalid performance options: ${perfResult.error.message}`, "INVALID_PERF" ); } return new SafetyChecker(limitsResult.data, perfResult.data); } // Embedded per-schema override: const blob = schemaSafetyLimitsSchema.parse({ maxBufferSize: 64 * 1024, customValidators: ["png-magic-bytes"], }); ``` ## API Reference ### `safetyLimitsSchema: z.ZodType` Validates a fully-populated `SafetyLimits` bundle. Every numeric field is required and must be a non-negative integer; both boolean flags are required. Throws `ZodError` on missing fields, negative values, or unknown keys. ### `safetyPerformanceOptionsSchema: z.ZodType` Validates a `SafetyPerformanceOptions` bundle of optional `skip*` boolean flags. An empty bundle is valid. Throws `ZodError` on unknown keys or non-boolean values. ### `schemaSafetyLimitsSchema: z.ZodType` Validates an embedded `_safety` block. Every field is optional. `customValidators` must be a `string[]` (the names of validators registered out-of-band). Throws `ZodError` on unknown keys, negative numerics, or non-string validator names. ## Implementation Notes - **Non-negative integer bounds.** Every numeric field is bounded by `.int().nonnegative()`. A floating-point limit would be ambiguous (round half-up? half-down?) and a negative limit is meaningless. Refusing both at parse time keeps the SafetyChecker contract simple. - **`as unknown as z.ZodType` cast.** Optional boolean fields under `exactOptionalPropertyTypes: true` produce `T | undefined` from Zod's `.optional()`, which doesn't match the target's `?:` declaration. The cast is purely a TypeScript shim — runtime semantics are identical. - **`.strict()` everywhere.** `SafetyChecker` reads only the documented fields. A typo'd key would otherwise be silently dropped, leaving the caller convinced they had configured a limit they hadn't. Strict-mode is the only safe default for safety configuration. - **No cross-field validation.** The schema does not enforce e.g. `maxBufferSize ≥ maxArrayLength * minElementSize` — that's a SafetyChecker invariant, not a config invariant. The schemas validate shape; the checker validates semantics.