first commit
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Config schemas
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides Zod boundary schemas for `IMessageWatcherConfig` and
|
||||
`IMessageAdapterConfig`, plus the `parseWatcherConfig` /
|
||||
`parseAdapterConfig` helpers that callers use to validate untrusted
|
||||
input before constructing a watcher or adapter. Both schemas are
|
||||
strict — extra keys are rejected — so consumers cannot silently
|
||||
smuggle additional fields past the boundary.
|
||||
|
||||
## Features
|
||||
|
||||
- Strict object validation (`.strict()`) — unknown properties are
|
||||
rejected at the boundary, preventing prototype-pollution-style
|
||||
smuggling.
|
||||
- `pollIntervalMs` constrained to positive integers; `sinceDate`
|
||||
required to be a `Date` instance (no millisecond-since-epoch
|
||||
numbers); `chatFilter` constrained to string arrays.
|
||||
- Custom `SqliteReaderSchema` confirms the optional injected
|
||||
`sqliteReader` exposes a callable `open()` member.
|
||||
- Failures throw `IMessageInvalidConfigError`
|
||||
(`IMESSAGE.INVALID_CONFIG`) carrying a serialisable `issues` array
|
||||
in `context` so problem+json responders can surface the Zod path +
|
||||
reason without re-running the schema.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { parseWatcherConfig } from "@suaveplan/imessage-adapter/schemas";
|
||||
|
||||
const config = parseWatcherConfig({ pollIntervalMs: 5000 });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
parseWatcherConfig,
|
||||
IMessageInvalidConfigError,
|
||||
} from "@suaveplan/imessage-adapter";
|
||||
|
||||
try {
|
||||
parseWatcherConfig({ pollIntervalMs: -1, oops: true });
|
||||
} catch (err) {
|
||||
if (err instanceof IMessageInvalidConfigError) {
|
||||
return Response.json(
|
||||
{ issues: err.context["issues"] },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `IMessageWatcherConfigSchema: z.ZodObject` — validates watcher config.
|
||||
- `IMessageAdapterConfigSchema` — alias for the watcher schema.
|
||||
- `SqliteReaderSchema: z.ZodType<unknown>` — structural check for
|
||||
`SqliteReader`.
|
||||
- `parseWatcherConfig(input: unknown): IMessageWatcherConfig` — throws
|
||||
`IMessageInvalidConfigError` on failure.
|
||||
- `parseAdapterConfig(input: unknown): IMessageAdapterConfig` — throws
|
||||
`IMessageInvalidConfigError` on failure.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schemas use `z.custom` for the `SqliteReader` slot because the
|
||||
reader is a structural type — there is no Zod-expressible shape that
|
||||
captures every legal implementation. The check confirms the presence
|
||||
of a function-typed `open` member, which is enough to surface
|
||||
mistakes (passing a stray object or `null`) without inspecting the
|
||||
reader's behaviour. Issue arrays are projected through `toIssues`
|
||||
which strips Zod's internal symbols so the resulting record is safe
|
||||
to JSON-serialise.
|
||||
Reference in New Issue
Block a user