Initial Commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# 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<T>` 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<SafetyLimits>`
|
||||
|
||||
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<SafetyPerformanceOptions>`
|
||||
|
||||
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<SchemaSafetyLimits>`
|
||||
|
||||
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<T>` 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.
|
||||
Reference in New Issue
Block a user