UNPKG

@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

52 lines (39 loc) • 1.69 kB
--- name: persist-encrypted description: AES-GCM encrypt a string-wire StateStorage with @stainless-code/persist/backends/encrypted (createEncryptedStorage). Backend wrapper — compose with createStorage + codec; wrong key rejects hydrate (not clearCorrupt). license: MIT metadata: type: composition library: "@stainless-code/persist" library_version: "0.4.1" requires: - persist sources: - stainless-code/persist:src/adapters/backends/encrypted.ts --- # Encrypted backend wrapper **Not a `StorageCodec`.** Wraps an inner string `StateStorage` with WebCrypto AES-GCM (`base64(iv).base64(ct)`). Returns `undefined` if `crypto.subtle` missing. Stack with compression: **compress → encrypt**. Decrypt failure (wrong key / tamper) → backend `getItem` **rejects** → `onError` phase `"hydrate"` — **not** the codec `clearCorruptOnFailure` path. ## Minimal wiring ```ts import { createStorage, jsonCodec } from "@stainless-code/persist"; import { createEncryptedStorage } from "@stainless-code/persist/backends/encrypted"; const key = await crypto.subtle.generateKey( { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"], ); const storage = createStorage<Prefs>( () => createEncryptedStorage(() => localStorage, { key })!, jsonCodec(), { clearCorruptOnFailure: true }, ); ``` Richer graphs → compose with `persist-seroval` instead of `jsonCodec`. ## Common mistakes - **Treating this as a sync codec.** - **Expecting clearCorrupt on decrypt fail.** - **Encrypt-then-compress** — reverse of the documented stack. ## API surface - `createEncryptedStorage(getStorage, { key }) → StateStorage<string> | undefined` See also: `persist-compressed`.