@stainless-code/persist
Version:
Hydration-aware persistence for any reactive store — zero-dep persistSource core; codecs, backends, cross-tab transport, source + framework hydration adapters ship as opt-in recipes
60 lines (43 loc) • 2.48 kB
Markdown
name: persist-standard-schema
description: Validate persisted state with Standard Schema (~standard) via withStandardSchema / withStandardSchemaAsync / createStandardSchemaStorage. Use for Zod/Yup (etc.) sync vs async lanes and PersistDecodeRethrowError.
license: MIT
metadata:
type: composition
library: "@stainless-code/persist"
library_version: "0.4.1"
requires:
- persist
sources:
- stainless-code/persist:src/adapters/codecs/standard-schema.ts
- stainless-code/persist:apps/docs/content/recipes/standard-schema.mdx
# Standard Schema codec / wraps
Types for `~standard` are **vendored** — no runtime peer. Bring Zod / Yup / Valibot yourself.
Validates persisted **`state` only** (not envelope `version` / `timestamp` / `buster`). Encode writes schema **output** (defaults/transforms applied).
## Sync vs async lanes
| API | When |
| -------------------------------------------------------------- | ------------------------------------------------ |
| `withStandardSchema` / `createStandardSchemaStorage` | Sync `validate` (Zod ≥3.24 / v4 via `~standard`) |
| `withStandardSchemaAsync` / `createStandardSchemaStorageAsync` | Async `validate` (Yup, async Zod) |
Wrong lane: sync wrap + async schema → `PersistDecodeRethrowError` (rethrown; **not** `clearCorrupt`). Async lane → async hydrate → gate UI (`react-persist`, …).
Sync wrap is still Promise-aware for async **backend** `getItem` (e.g. IDB).
## Minimal wiring
```ts
import { withStandardSchema } from "@stainless-code/persist/codecs/standard-schema";
import { createIdbStorage } from "@stainless-code/persist/backends/idb";
const storage = withStandardSchema(createIdbStorage<Prefs>()!, prefsSchema, {
clearCorruptOnFailure: true,
});
```
JSON sugar: `createStandardSchemaStorage(() => localStorage, schema)`.
## Common mistakes
- **Async schema on the sync wrap.**
- **Treating wrong-lane throws as corrupt** — they must not clear storage.
- **Schema-checking the whole envelope** — only `state`.
- **Skipping `useHydrated` on the async lane.**
- **Stacking wrap + `standardSchemaCodec`** — double-validates; pick one.
## API surface
- `standardSchemaCodec(schema)` · `withStandardSchema` / `withStandardSchemaAsync`
- `createStandardSchemaStorage` / `createStandardSchemaStorageAsync`
See also: `persist-idb` for async backends.