react-mnemonic 1.0.0-beta.0 → 1.1.0-beta0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -304,6 +304,51 @@ declare function compileSchema(schema: JsonSchema): CompiledValidator;
304
304
  */
305
305
  declare function validateJsonSchema(value: unknown, schema: JsonSchema, path?: string): JsonSchemaValidationError[];
306
306
 
307
+ declare const typedSchemaBrand: unique symbol;
308
+ declare const optionalSchemaBrand: unique symbol;
309
+ type JsonPrimitive = string | number | boolean | null;
310
+ type JsonConstValue = JsonPrimitive | readonly JsonConstValue[] | {
311
+ readonly [key: string]: JsonConstValue;
312
+ };
313
+ type TypedJsonSchema<T> = JsonSchema & {
314
+ readonly [typedSchemaBrand]?: T;
315
+ };
316
+ type OptionalTypedJsonSchema<T> = TypedJsonSchema<T> & {
317
+ readonly [optionalSchemaBrand]: true;
318
+ };
319
+ type InferJsonSchemaValue<TSchema> = TSchema extends TypedJsonSchema<infer TValue> ? TValue : unknown;
320
+ type ObjectValueFromSchemas<TShape extends Record<string, TypedJsonSchema<unknown> | OptionalTypedJsonSchema<unknown>>> = {
321
+ [K in keyof TShape as TShape[K] extends OptionalTypedJsonSchema<unknown> ? never : K]: InferJsonSchemaValue<TShape[K]>;
322
+ } & {
323
+ [K in keyof TShape as TShape[K] extends OptionalTypedJsonSchema<unknown> ? K : never]?: InferJsonSchemaValue<TShape[K]>;
324
+ };
325
+ type StringSchemaOptions = Pick<JsonSchema, "minLength" | "maxLength">;
326
+ type NumberSchemaOptions = Pick<JsonSchema, "minimum" | "maximum" | "exclusiveMinimum" | "exclusiveMaximum">;
327
+ type ArraySchemaOptions = Pick<JsonSchema, "minItems" | "maxItems">;
328
+ type ObjectSchemaOptions = Pick<JsonSchema, "additionalProperties">;
329
+ /**
330
+ * Builder helpers for strongly typed schemas backed by Mnemonic's built-in
331
+ * JSON Schema subset.
332
+ *
333
+ * The returned schemas are plain `JsonSchema` objects at runtime, so they can
334
+ * be registered directly in `createSchemaRegistry(...)` while also carrying a
335
+ * phantom TypeScript type for inference.
336
+ */
337
+ declare const mnemonicSchema: {
338
+ string(options?: StringSchemaOptions): TypedJsonSchema<string>;
339
+ number(options?: NumberSchemaOptions): TypedJsonSchema<number>;
340
+ integer(options?: NumberSchemaOptions): TypedJsonSchema<number>;
341
+ boolean(): TypedJsonSchema<boolean>;
342
+ nullValue(): TypedJsonSchema<null>;
343
+ literal<const TValue extends JsonConstValue>(value: TValue): TypedJsonSchema<TValue>;
344
+ enum<const TValues extends readonly [JsonPrimitive, ...JsonPrimitive[]]>(values: TValues): TypedJsonSchema<TValues[number]>;
345
+ optional<T>(schema: TypedJsonSchema<T>): OptionalTypedJsonSchema<T>;
346
+ nullable<T>(schema: TypedJsonSchema<T>): TypedJsonSchema<T | null>;
347
+ array<TItemSchema extends TypedJsonSchema<unknown>>(itemSchema: TItemSchema, options?: ArraySchemaOptions): TypedJsonSchema<InferJsonSchemaValue<TItemSchema>[]>;
348
+ object<TShape extends Record<string, TypedJsonSchema<unknown> | OptionalTypedJsonSchema<unknown>>>(shape: TShape, options?: ObjectSchemaOptions): TypedJsonSchema<ObjectValueFromSchemas<TShape>>;
349
+ record<TValueSchema extends TypedJsonSchema<unknown>>(valueSchema: TValueSchema): TypedJsonSchema<Record<string, InferJsonSchemaValue<TValueSchema>>>;
350
+ };
351
+
307
352
  /**
308
353
  * @fileoverview Type definitions for the Mnemonic library.
309
354
  *
@@ -311,6 +356,7 @@ declare function validateJsonSchema(value: unknown, schema: JsonSchema, path?: s
311
356
  * library for type-safe, persistent state management in React applications.
312
357
  */
313
358
 
359
+ declare const keySchemaValueBrand: unique symbol;
314
360
  /**
315
361
  * Codec for encoding and decoding values to and from storage.
316
362
  *
@@ -388,7 +434,8 @@ interface MnemonicProviderOptions {
388
434
  * Storage backend to use for persistence.
389
435
  *
390
436
  * Defaults to `window.localStorage` in browser environments. You can provide
391
- * a custom implementation (e.g., sessionStorage, AsyncStorage, or a mock for testing).
437
+ * a synchronous custom implementation (e.g., sessionStorage, an in-memory
438
+ * cache facade over IndexedDB, or a mock for testing).
392
439
  *
393
440
  * @default window.localStorage
394
441
  *
@@ -425,7 +472,7 @@ interface MnemonicProviderOptions {
425
472
  * enableDevTools: process.env.NODE_ENV === 'development'
426
473
  *
427
474
  * // Then in browser console:
428
- * const provider = window.__REACT_MNEMONIC_DEVTOOLS__.resolve('myApp')
475
+ * const provider = window.__REACT_MNEMONIC_DEVTOOLS__?.resolve('myApp')
429
476
  * provider?.dump()
430
477
  * provider?.get('user')
431
478
  * provider?.set('user', { name: 'Test' })
@@ -466,6 +513,24 @@ interface MnemonicProviderOptions {
466
513
  * @see {@link KeySchema} - Schema definition stored in the registry
467
514
  */
468
515
  schemaRegistry?: SchemaRegistry;
516
+ /**
517
+ * Server-rendering and hydration defaults for descendant hooks.
518
+ *
519
+ * Provider-level SSR settings establish the default hydration strategy for
520
+ * all `useMnemonicKey(...)` calls in this namespace. Individual hooks may
521
+ * still override the strategy when a specific key needs different behavior.
522
+ *
523
+ * @example
524
+ * ```tsx
525
+ * <MnemonicProvider
526
+ * namespace="app"
527
+ * ssr={{ hydration: "client-only" }}
528
+ * >
529
+ * <App />
530
+ * </MnemonicProvider>
531
+ * ```
532
+ */
533
+ ssr?: MnemonicProviderSSRConfig;
469
534
  }
470
535
  /**
471
536
  * Controls how the provider enforces versioned schemas on stored values.
@@ -499,6 +564,57 @@ interface MnemonicProviderOptions {
499
564
  * @see {@link KeySchema} - Individual schema definition
500
565
  */
501
566
  type SchemaMode = "strict" | "default" | "autoschema";
567
+ /**
568
+ * Controls when a hook should read persisted storage during client rendering.
569
+ *
570
+ * - `"immediate"` — Default. The server snapshot is used during SSR/hydration,
571
+ * and the hook reads persisted storage as soon as React switches to the
572
+ * client snapshot.
573
+ *
574
+ * - `"client-only"` — Defers all storage reads until after the component has
575
+ * mounted on the client. This is useful when you want a deterministic server
576
+ * placeholder and prefer the persisted value to appear only after hydration
577
+ * completes.
578
+ */
579
+ type MnemonicHydrationMode = "immediate" | "client-only";
580
+ /**
581
+ * Provider-level SSR defaults shared by descendant hooks.
582
+ */
583
+ interface MnemonicProviderSSRConfig {
584
+ /**
585
+ * Default hydration strategy for descendant `useMnemonicKey(...)` hooks.
586
+ *
587
+ * @default "immediate"
588
+ */
589
+ hydration?: MnemonicHydrationMode;
590
+ }
591
+ /**
592
+ * Hook-level SSR controls for `useMnemonicKey(...)`.
593
+ *
594
+ * Lets a key render a deterministic server snapshot and optionally delay
595
+ * reading persisted storage until after client mount.
596
+ *
597
+ * @template T - The decoded value type for the key
598
+ */
599
+ interface MnemonicKeySSRConfig<T> {
600
+ /**
601
+ * Value to expose during SSR and hydration instead of `defaultValue`.
602
+ *
603
+ * This value is not persisted automatically. Once hydration completes,
604
+ * the hook transitions to the stored value (if any) or back to
605
+ * `defaultValue`.
606
+ *
607
+ * Factory functions should be deterministic across server render and
608
+ * client hydration to avoid markup mismatches.
609
+ */
610
+ serverValue?: T | (() => T);
611
+ /**
612
+ * Hydration strategy for this key.
613
+ *
614
+ * When omitted, inherits the provider default.
615
+ */
616
+ hydration?: MnemonicHydrationMode;
617
+ }
502
618
  /**
503
619
  * Weak-reference shape used by the devtools registry.
504
620
  *
@@ -635,11 +751,11 @@ interface MnemonicDevToolsRegistry {
635
751
  * @see {@link MigrationRule} - How values migrate between schema versions
636
752
  * @see {@link JsonSchema} - The JSON Schema subset used for validation
637
753
  */
638
- type KeySchema = {
754
+ type KeySchema<TValue = unknown, K extends string = string, TSchema extends JsonSchema = JsonSchema> = {
639
755
  /**
640
756
  * The unprefixed storage key this schema applies to.
641
757
  */
642
- key: string;
758
+ key: K;
643
759
  /**
644
760
  * The version number for this schema.
645
761
  *
@@ -652,7 +768,15 @@ type KeySchema = {
652
768
  * Only the subset of JSON Schema keywords defined in {@link JsonSchema}
653
769
  * are supported. An empty schema `{}` accepts any value.
654
770
  */
655
- schema: JsonSchema;
771
+ schema: TSchema;
772
+ /**
773
+ * Phantom type linking this runtime schema to its decoded TypeScript value.
774
+ *
775
+ * This field is never set at runtime. It exists only so helpers such as
776
+ * `defineKeySchema(...)`, `defineMnemonicKey(...)`, and `defineMigration(...)`
777
+ * can preserve a single source of truth between schema shape and value type.
778
+ */
779
+ readonly [keySchemaValueBrand]?: TValue;
656
780
  };
657
781
  /**
658
782
  * A single migration step that transforms data from one schema version to
@@ -695,11 +819,11 @@ type KeySchema = {
695
819
  * @see {@link SchemaRegistry.getMigrationPath} - How the path is resolved
696
820
  * @see {@link SchemaRegistry.getWriteMigration} - How write-time normalizers are resolved
697
821
  */
698
- type MigrationRule = {
822
+ type MigrationRule<TFrom = unknown, TTo = unknown, K extends string = string> = {
699
823
  /**
700
824
  * The unprefixed storage key this rule applies to.
701
825
  */
702
- key: string;
826
+ key: K;
703
827
  /**
704
828
  * The version the stored data is migrating **from**.
705
829
  *
@@ -723,7 +847,7 @@ type MigrationRule = {
723
847
  * @param value - The decoded value at `fromVersion`
724
848
  * @returns The transformed value for `toVersion`
725
849
  */
726
- migrate: (value: unknown) => unknown;
850
+ migrate(value: TFrom): TTo;
727
851
  };
728
852
  /**
729
853
  * An ordered sequence of {@link MigrationRule} steps that upgrades stored
@@ -737,13 +861,17 @@ type MigrationRule = {
737
861
  * @see {@link MigrationRule} - Individual migration step
738
862
  * @see {@link SchemaRegistry.getMigrationPath} - Resolves a path between versions
739
863
  */
740
- type MigrationPath = MigrationRule[];
864
+ type MigrationPath<K extends string = string> = MigrationRule<unknown, unknown, K>[];
741
865
  /**
742
866
  * Input options for {@link createSchemaRegistry}.
743
867
  *
744
868
  * Use this helper when your registry contents are known up front and do not
745
869
  * need runtime mutation. The returned registry is immutable and optimized for
746
870
  * the common `"default"` / `"strict"` setup.
871
+ *
872
+ * For most apps, this should be your default entry point for schema-managed
873
+ * persistence. Implement {@link SchemaRegistry} manually only when you need
874
+ * custom lookup behavior or runtime schema registration beyond autoschema mode.
747
875
  */
748
876
  interface CreateSchemaRegistryOptions {
749
877
  /**
@@ -776,15 +904,19 @@ interface CreateSchemaRegistryOptions {
776
904
  * read/write hot paths fast. `"autoschema"` remains mutable to support
777
905
  * inferred schema registration.
778
906
  *
907
+ * Most applications should prefer {@link createSchemaRegistry} instead of
908
+ * implementing this interface manually. Manual implementations are mainly for
909
+ * advanced cases such as custom backing stores, dynamic schema discovery, or
910
+ * adapter layers around an existing registry system.
911
+ *
779
912
  * @example
780
913
  * ```typescript
781
- * const registry: SchemaRegistry = {
782
- * getSchema: (key, version) => schemas.get(`${key}@${version}`),
783
- * getLatestSchema: (key) => latestByKey.get(key),
784
- * getMigrationPath: (key, from, to) => buildPath(key, from, to),
785
- * getWriteMigration: (key, version) => normalizers.get(`${key}@${version}`),
786
- * registerSchema: (schema) => { schemas.set(`${schema.key}@${schema.version}`, schema); },
787
- * };
914
+ * const registry = createSchemaRegistry({
915
+ * schemas: [
916
+ * { key: "settings", version: 1, schema: { type: "object", required: ["theme"] } },
917
+ * ],
918
+ * migrations: [],
919
+ * });
788
920
  *
789
921
  * <MnemonicProvider namespace="app" schemaRegistry={registry} schemaMode="strict">
790
922
  * <App />
@@ -856,12 +988,17 @@ interface SchemaRegistry {
856
988
  registerSchema?(schema: KeySchema): void;
857
989
  }
858
990
  /**
859
- * Storage interface compatible with localStorage and custom storage implementations.
991
+ * Storage interface compatible with localStorage and synchronous custom storage implementations.
860
992
  *
861
993
  * Defines the minimum contract required for a storage backend. Compatible with
862
994
  * browser Storage API (localStorage, sessionStorage) and custom implementations
863
995
  * for testing or alternative storage solutions.
864
996
  *
997
+ * All methods in this contract are intentionally synchronous. Promise-returning
998
+ * adapters such as React Native `AsyncStorage` are not directly supported by
999
+ * `StorageLike`; instead, keep a synchronous in-memory cache and flush to async
1000
+ * persistence outside the hook contract.
1001
+ *
865
1002
  * @remarks
866
1003
  * **Error handling contract**
867
1004
  *
@@ -885,6 +1022,10 @@ interface SchemaRegistry {
885
1022
  * In all error cases the library falls back to its in-memory cache, so
886
1023
  * components continue to function when the storage backend is unavailable.
887
1024
  *
1025
+ * Promise-returning `getItem`, `setItem`, or `removeItem` implementations are
1026
+ * treated as an invalid contract at runtime. Mnemonic logs the misuse once and
1027
+ * falls back to its in-memory cache for safety.
1028
+ *
888
1029
  * @example
889
1030
  * ```typescript
890
1031
  * // In-memory storage for testing
@@ -906,6 +1047,8 @@ type StorageLike = {
906
1047
  *
907
1048
  * @param key - The storage key to retrieve
908
1049
  * @returns The stored value as a string, or null if not found
1050
+ *
1051
+ * @remarks Must return synchronously. Returning a Promise is unsupported.
909
1052
  */
910
1053
  getItem(key: string): string | null;
911
1054
  /**
@@ -913,12 +1056,16 @@ type StorageLike = {
913
1056
  *
914
1057
  * @param key - The storage key
915
1058
  * @param value - The string value to store
1059
+ *
1060
+ * @remarks Must complete synchronously. Returning a Promise is unsupported.
916
1061
  */
917
1062
  setItem(key: string, value: string): void;
918
1063
  /**
919
1064
  * Removes a key-value pair from storage.
920
1065
  *
921
1066
  * @param key - The storage key to remove
1067
+ *
1068
+ * @remarks Must complete synchronously. Returning a Promise is unsupported.
922
1069
  */
923
1070
  removeItem(key: string): void;
924
1071
  /**
@@ -1085,6 +1232,19 @@ type Mnemonic = {
1085
1232
  * @see {@link SchemaMode}
1086
1233
  */
1087
1234
  schemaMode: SchemaMode;
1235
+ /**
1236
+ * Default hydration strategy inherited by descendant hooks.
1237
+ */
1238
+ ssrHydration: MnemonicHydrationMode;
1239
+ /**
1240
+ * How this provider can observe external changes from other tabs/processes.
1241
+ *
1242
+ * Hooks use this for development diagnostics when callers opt into
1243
+ * cross-tab synchronization on a backend that cannot actually deliver it.
1244
+ *
1245
+ * When omitted, consumers should treat this as equivalent to `"none"`.
1246
+ */
1247
+ crossTabSyncMode?: "browser-storage-event" | "custom-external-change" | "none";
1088
1248
  /**
1089
1249
  * The schema registry for this provider, if one was supplied.
1090
1250
  *
@@ -1172,6 +1332,96 @@ interface MnemonicRecoveryHook {
1172
1332
  */
1173
1333
  clearMatching: (predicate: (key: string) => boolean) => string[];
1174
1334
  }
1335
+ /**
1336
+ * Return shape from {@link useMnemonicKey}.
1337
+ *
1338
+ * This mirrors the familiar `useState` mental model while making the storage
1339
+ * semantics explicit:
1340
+ *
1341
+ * - `set(...)` writes a new persisted value
1342
+ * - `reset()` writes `defaultValue` back into storage
1343
+ * - `remove()` deletes the key entirely so reads fall back to `defaultValue`
1344
+ *
1345
+ * See the
1346
+ * [Clearable Persisted Values guide](https://thirtytwobits.github.io/react-mnemonic/docs/guides/clearable-persisted-values)
1347
+ * for the semantic differences between clearing, resetting, and removing a key.
1348
+ *
1349
+ * @template T - The decoded value type for the key
1350
+ *
1351
+ * @see {@link UseMnemonicKeyOptions} - Hook configuration and lifecycle details
1352
+ */
1353
+ interface MnemonicKeyState<T> {
1354
+ /**
1355
+ * Current decoded value, or the default when the key is absent or invalid.
1356
+ */
1357
+ value: T;
1358
+ /**
1359
+ * Persist a new value.
1360
+ *
1361
+ * Accepts either a direct replacement value or an updater function that
1362
+ * receives the current decoded value.
1363
+ */
1364
+ set: (next: T | ((current: T) => T)) => void;
1365
+ /**
1366
+ * Reset the key back to `defaultValue` and persist that default.
1367
+ */
1368
+ reset: () => void;
1369
+ /**
1370
+ * Delete the key from storage entirely.
1371
+ *
1372
+ * Future reads will fall back to `defaultValue` until the key is written
1373
+ * again.
1374
+ */
1375
+ remove: () => void;
1376
+ }
1377
+ /**
1378
+ * Reusable, importable contract for a single persisted key.
1379
+ *
1380
+ * Descriptors package the key name and its `useMnemonicKey(...)` options into
1381
+ * a stable object that can be defined once at module scope and reused across
1382
+ * components. This helps keep persistence behavior explicit and consistent,
1383
+ * especially when the same key appears in multiple parts of an application.
1384
+ *
1385
+ * @template T - The decoded value type for the key
1386
+ * @template K - The literal key name
1387
+ *
1388
+ * @example
1389
+ * ```typescript
1390
+ * const themeKey = defineMnemonicKey("theme", {
1391
+ * defaultValue: "light" as "light" | "dark",
1392
+ * listenCrossTab: true,
1393
+ * });
1394
+ *
1395
+ * const { value, set } = useMnemonicKey(themeKey);
1396
+ * ```
1397
+ *
1398
+ * @see {@link defineMnemonicKey} - Helper for creating descriptors
1399
+ * @see {@link useMnemonicKey} - Hook that consumes descriptors
1400
+ */
1401
+ interface MnemonicKeyDescriptor<T, K extends string = string> {
1402
+ /**
1403
+ * Unprefixed storage key name.
1404
+ */
1405
+ readonly key: K;
1406
+ /**
1407
+ * Canonical options for this key.
1408
+ */
1409
+ readonly options: UseMnemonicKeyOptions<T>;
1410
+ }
1411
+ /**
1412
+ * Key descriptor options inferred from a typed key schema.
1413
+ *
1414
+ * This mirrors `UseMnemonicKeyOptions<T>` but intentionally omits the `schema`
1415
+ * override so the descriptor stays pinned to the supplied key schema version.
1416
+ */
1417
+ type SchemaBoundKeyOptions<T> = Omit<UseMnemonicKeyOptions<T>, "schema">;
1418
+ /**
1419
+ * Typed key schema shape inferred from a schema helper or branded JSON Schema.
1420
+ *
1421
+ * Useful when you want a versioned schema object to carry its decoded
1422
+ * TypeScript value through registries, descriptors, and migration helpers.
1423
+ */
1424
+ type TypedKeySchema<TSchema extends JsonSchema, K extends string = string> = KeySchema<InferJsonSchemaValue<TSchema>, K, TSchema>;
1175
1425
  /**
1176
1426
  * Configuration options for the useMnemonicKey hook.
1177
1427
  *
@@ -1286,6 +1536,9 @@ type UseMnemonicKeyOptions<T> = {
1286
1536
  * adjustments that depend on application policy rather than a strict schema
1287
1537
  * upgrade step.
1288
1538
  *
1539
+ * See the Schema Migration guide for migration-vs-reconciliation guidance:
1540
+ * https://thirtytwobits.github.io/react-mnemonic/docs/guides/schema-migration
1541
+ *
1289
1542
  * If `reconcile` throws a `SchemaError`, that error is preserved and passed
1290
1543
  * to `defaultValue`. Any other thrown error is wrapped as
1291
1544
  * `SchemaError("RECONCILE_FAILED")`.
@@ -1360,6 +1613,22 @@ type UseMnemonicKeyOptions<T> = {
1360
1613
  * automatically via React's state management.
1361
1614
  */
1362
1615
  listenCrossTab?: boolean;
1616
+ /**
1617
+ * Server-rendering controls for this key.
1618
+ *
1619
+ * Use this when the server should render a value other than
1620
+ * `defaultValue`, or when persisted storage should only be read after the
1621
+ * component has mounted on the client.
1622
+ *
1623
+ * @example
1624
+ * ```typescript
1625
+ * ssr: {
1626
+ * serverValue: { theme: "system" },
1627
+ * hydration: "client-only",
1628
+ * }
1629
+ * ```
1630
+ */
1631
+ ssr?: MnemonicKeySSRConfig<T>;
1363
1632
  /**
1364
1633
  * Optional schema controls for this key.
1365
1634
  *
@@ -1418,11 +1687,11 @@ type ReconcileContext = {
1418
1687
  * @see {@link MnemonicProviderOptions} - Configuration options
1419
1688
  * @see {@link MnemonicProvider} - Provider component
1420
1689
  */
1421
- interface MnemonicProviderProps extends MnemonicProviderOptions {
1690
+ interface MnemonicProviderProps extends Readonly<MnemonicProviderOptions> {
1422
1691
  /**
1423
1692
  * React children to render within the provider.
1424
1693
  */
1425
- children: ReactNode;
1694
+ readonly children: ReactNode;
1426
1695
  }
1427
1696
  /**
1428
1697
  * React Context provider for namespace-isolated persistent state.
@@ -1437,10 +1706,11 @@ interface MnemonicProviderProps extends MnemonicProviderOptions {
1437
1706
  * @param props - Provider configuration and children
1438
1707
  * @param props.children - React children to render within the provider
1439
1708
  * @param props.namespace - Unique namespace for isolating storage keys
1440
- * @param props.storage - Optional custom storage backend (defaults to localStorage)
1709
+ * @param props.storage - Optional synchronous custom storage backend (defaults to localStorage)
1441
1710
  * @param props.enableDevTools - Enable DevTools debugging interface (defaults to false)
1442
1711
  * @param props.schemaMode - Schema enforcement mode (default: "default")
1443
1712
  * @param props.schemaRegistry - Optional schema registry for storing schemas and migrations
1713
+ * @param props.ssr - Optional SSR defaults for descendant hooks
1444
1714
  *
1445
1715
  * @example
1446
1716
  * ```tsx
@@ -1456,7 +1726,7 @@ interface MnemonicProviderProps extends MnemonicProviderOptions {
1456
1726
  *
1457
1727
  * @example
1458
1728
  * ```tsx
1459
- * // With custom storage backend
1729
+ * // With a synchronous custom storage backend
1460
1730
  * function App() {
1461
1731
  * return (
1462
1732
  * <MnemonicProvider
@@ -1505,17 +1775,34 @@ interface MnemonicProviderProps extends MnemonicProviderOptions {
1505
1775
  * }
1506
1776
  * ```
1507
1777
  *
1778
+ * @example
1779
+ * ```tsx
1780
+ * // Delay persisted storage reads until after client mount
1781
+ * function App() {
1782
+ * return (
1783
+ * <MnemonicProvider
1784
+ * namespace="myApp"
1785
+ * ssr={{ hydration: "client-only" }}
1786
+ * >
1787
+ * <MyComponents />
1788
+ * </MnemonicProvider>
1789
+ * );
1790
+ * }
1791
+ * ```
1792
+ *
1508
1793
  * @remarks
1509
1794
  * - Creates a stable store instance that only recreates when namespace, storage, or enableDevTools change
1510
1795
  * - All storage operations are cached in memory for fast reads
1511
1796
  * - Storage failures are handled gracefully (logged but not thrown)
1512
- * - In SSR environments, the provider works but no storage persistence occurs
1797
+ * - `StorageLike` is intentionally synchronous for v1; async persistence must sit behind a sync facade
1798
+ * - In SSR environments, the provider is safe by default: hooks render
1799
+ * `defaultValue` unless configured with an explicit `ssr.serverValue`
1513
1800
  * - The store implements React's useSyncExternalStore contract for efficient updates
1514
1801
  *
1515
1802
  * @see {@link useMnemonicKey} - Hook for using persistent state
1516
1803
  * @see {@link MnemonicProviderOptions} - Configuration options
1517
1804
  */
1518
- declare function MnemonicProvider({ children, namespace, storage, enableDevTools, schemaMode, schemaRegistry, }: MnemonicProviderProps): react_jsx_runtime.JSX.Element;
1805
+ declare function MnemonicProvider({ children, namespace, storage, enableDevTools, schemaMode, schemaRegistry, ssr, }: MnemonicProviderProps): react_jsx_runtime.JSX.Element;
1519
1806
 
1520
1807
  /**
1521
1808
  * React hook for persistent, type-safe state management.
@@ -1527,25 +1814,33 @@ declare function MnemonicProvider({ children, namespace, storage, enableDevTools
1527
1814
  * Must be used within a `MnemonicProvider`. Uses React's `useSyncExternalStore`
1528
1815
  * internally for efficient, tearing-free state synchronization.
1529
1816
  *
1817
+ * Read lifecycle, in order:
1818
+ * 1. Load the raw stored envelope for `key`
1819
+ * 2. Decode the payload (codec or schema-managed JSON)
1820
+ * 3. Validate and migrate when schemas are registered
1821
+ * 4. Run `reconcile(...)` if provided
1822
+ * 5. Fall back to `defaultValue` when the key is absent or invalid
1823
+ *
1824
+ * Write semantics:
1825
+ * - `set(...)` persists a new value
1826
+ * - `reset()` persists `defaultValue`
1827
+ * - `remove()` deletes the key entirely so future reads fall back to `defaultValue`
1828
+ *
1829
+ * For guide-level background, see the
1830
+ * [Schema Migration guide](https://thirtytwobits.github.io/react-mnemonic/docs/guides/schema-migration)
1831
+ * and the
1832
+ * [Clearable Persisted Values guide](https://thirtytwobits.github.io/react-mnemonic/docs/guides/clearable-persisted-values).
1833
+ *
1530
1834
  * @template T - The TypeScript type of the stored value
1531
1835
  *
1532
- * @param key - The storage key (unprefixed, namespace is applied automatically)
1533
- * @param options - Configuration options controlling persistence, encoding, and behavior
1836
+ * @returns Persistent state handle with the current value and mutation helpers
1534
1837
  *
1535
- * @returns Object with the current value and methods to update it
1838
+ * @see {@link UseMnemonicKeyOptions} - Hook configuration and lifecycle details
1536
1839
  *
1537
1840
  * @throws {Error} If used outside of a MnemonicProvider
1538
1841
  */
1539
- declare function useMnemonicKey<T>(key: string, options: UseMnemonicKeyOptions<T>): {
1540
- /** Current decoded value, or the default when the key is absent or invalid. */
1541
- value: T;
1542
- /** Persist a new value (direct or updater function). */
1543
- set: (next: T | ((cur: T) => T)) => void;
1544
- /** Reset to `defaultValue` and persist it. */
1545
- reset: () => void;
1546
- /** Delete the key from storage entirely. */
1547
- remove: () => void;
1548
- };
1842
+ declare function useMnemonicKey<T, K extends string>(descriptor: MnemonicKeyDescriptor<T, K>): MnemonicKeyState<T>;
1843
+ declare function useMnemonicKey<T>(key: string, options: UseMnemonicKeyOptions<T>): MnemonicKeyState<T>;
1549
1844
 
1550
1845
  /**
1551
1846
  * Hook for namespace-scoped recovery actions such as hard reset and selective clear.
@@ -1553,6 +1848,10 @@ declare function useMnemonicKey<T>(key: string, options: UseMnemonicKeyOptions<T
1553
1848
  * Applications can use this to offer self-service recovery UX for corrupt or
1554
1849
  * legacy persisted state. The hook operates on the current provider namespace.
1555
1850
  *
1851
+ * See the
1852
+ * [Reset and Recovery guide](https://thirtytwobits.github.io/react-mnemonic/docs/guides/reset-and-recovery)
1853
+ * for soft-reset and hard-reset patterns.
1854
+ *
1556
1855
  * @param options - Optional recovery callback for telemetry/auditing
1557
1856
  * @returns Namespace recovery helpers
1558
1857
  *
@@ -1560,6 +1859,33 @@ declare function useMnemonicKey<T>(key: string, options: UseMnemonicKeyOptions<T
1560
1859
  */
1561
1860
  declare function useMnemonicRecovery(options?: UseMnemonicRecoveryOptions): MnemonicRecoveryHook;
1562
1861
 
1862
+ /**
1863
+ * Define a reusable, importable contract for a persisted key.
1864
+ *
1865
+ * This packages the storage key and the canonical `useMnemonicKey(...)`
1866
+ * options into a single object that can be shared across components, docs,
1867
+ * and generated code.
1868
+ *
1869
+ * @template K - The literal storage key name
1870
+ * @template T - The decoded value type for the key
1871
+ *
1872
+ * @param key - The unprefixed storage key
1873
+ * @param options - Canonical hook options for the key
1874
+ * @returns A descriptor that can be passed directly to `useMnemonicKey(...)`
1875
+ *
1876
+ * @example
1877
+ * ```typescript
1878
+ * const themeKey = defineMnemonicKey("theme", {
1879
+ * defaultValue: "light" as "light" | "dark",
1880
+ * listenCrossTab: true,
1881
+ * });
1882
+ *
1883
+ * const { value, set } = useMnemonicKey(themeKey);
1884
+ * ```
1885
+ */
1886
+ declare function defineMnemonicKey<const K extends string, T>(key: K, options: UseMnemonicKeyOptions<T>): MnemonicKeyDescriptor<T, K>;
1887
+ declare function defineMnemonicKey<const K extends string, TSchema extends KeySchema<unknown, K, JsonSchema>>(keySchema: TSchema, options: SchemaBoundKeyOptions<TSchema extends KeySchema<infer TValue, string, JsonSchema> ? TValue : never>): MnemonicKeyDescriptor<TSchema extends KeySchema<infer TValue, string, JsonSchema> ? TValue : never, TSchema["key"]>;
1888
+
1563
1889
  /**
1564
1890
  * Create an immutable schema registry for common default/strict-mode setups.
1565
1891
  *
@@ -1567,6 +1893,13 @@ declare function useMnemonicRecovery(options?: UseMnemonicRecoveryOptions): Mnem
1567
1893
  * ambiguous definitions, and returns a {@link SchemaRegistry} ready to pass to
1568
1894
  * `MnemonicProvider`.
1569
1895
  *
1896
+ * Most applications should prefer this helper over manually implementing
1897
+ * {@link SchemaRegistry}.
1898
+ *
1899
+ * See the
1900
+ * [Schema Migration guide](https://thirtytwobits.github.io/react-mnemonic/docs/guides/schema-migration)
1901
+ * for end-to-end registry and migration patterns.
1902
+ *
1570
1903
  * @param options - Initial schema and migration definitions
1571
1904
  * @returns An indexed immutable schema registry
1572
1905
  *
@@ -1575,6 +1908,23 @@ declare function useMnemonicRecovery(options?: UseMnemonicRecoveryOptions): Mnem
1575
1908
  */
1576
1909
  declare function createSchemaRegistry(options?: CreateSchemaRegistryOptions): SchemaRegistry;
1577
1910
 
1911
+ /**
1912
+ * Create a versioned key schema that preserves the decoded value type inferred
1913
+ * from a typed schema helper.
1914
+ */
1915
+ declare function defineKeySchema<const K extends string, TSchema extends JsonSchema>(key: K, version: number, schema: TSchema): KeySchema<InferJsonSchemaValue<TSchema>, K, TSchema>;
1916
+ /**
1917
+ * Create a typed migration rule between two key schema versions.
1918
+ *
1919
+ * The `migrate(...)` callback is inferred from the source and target schemas,
1920
+ * which keeps migration logic aligned with the registered runtime schemas.
1921
+ */
1922
+ declare function defineMigration<const K extends string, TFrom, TTo>(fromSchema: KeySchema<TFrom, K>, toSchema: KeySchema<TTo, K>, migrate: (value: TFrom) => TTo): MigrationRule<TFrom, TTo, K>;
1923
+ /**
1924
+ * Create a typed write-time normalization rule for a single key schema.
1925
+ */
1926
+ declare function defineWriteMigration<const K extends string, TValue>(schema: KeySchema<TValue, K>, migrate: (value: TValue) => TValue): MigrationRule<TValue, TValue, K>;
1927
+
1578
1928
  /**
1579
1929
  * Adapter functions for structural migration helpers.
1580
1930
  *
@@ -1716,4 +2066,4 @@ declare function dedupeChildrenBy<T extends StructuralNode<T>, K>(root: T, getKe
1716
2066
  */
1717
2067
  declare function dedupeChildrenBy<T, K>(root: T, getKey: (node: T) => K, helpers: StructuralTreeHelpers<T>): T;
1718
2068
 
1719
- export { type Codec, CodecError, type CompiledValidator, type CreateSchemaRegistryOptions, JSONCodec, type JsonSchema, type JsonSchemaType, type JsonSchemaValidationError, type KeySchema, type Listener, type MigrationPath, type MigrationRule, type Mnemonic, type MnemonicDevToolsCapabilities, type MnemonicDevToolsMeta, type MnemonicDevToolsProviderApi, type MnemonicDevToolsProviderDescriptor, type MnemonicDevToolsProviderEntry, type MnemonicDevToolsRegistry, type MnemonicDevToolsWeakRef, MnemonicProvider, type MnemonicProviderOptions, type MnemonicProviderProps, type MnemonicRecoveryAction, type MnemonicRecoveryEvent, type MnemonicRecoveryHook, type ReconcileContext, SchemaError, type SchemaMode, type SchemaRegistry, type StorageLike, type StructuralNode, type StructuralTreeHelpers, type Unsubscribe, type UseMnemonicKeyOptions, type UseMnemonicRecoveryOptions, compileSchema, createCodec, createSchemaRegistry, dedupeChildrenBy, findNodeById, insertChildIfMissing, renameNode, useMnemonicKey, useMnemonicRecovery, validateJsonSchema };
2069
+ export { type Codec, CodecError, type CompiledValidator, type CreateSchemaRegistryOptions, type InferJsonSchemaValue, JSONCodec, type JsonSchema, type JsonSchemaType, type JsonSchemaValidationError, type KeySchema, type Listener, type MigrationPath, type MigrationRule, type Mnemonic, type MnemonicDevToolsCapabilities, type MnemonicDevToolsMeta, type MnemonicDevToolsProviderApi, type MnemonicDevToolsProviderDescriptor, type MnemonicDevToolsProviderEntry, type MnemonicDevToolsRegistry, type MnemonicDevToolsWeakRef, type MnemonicHydrationMode, type MnemonicKeyDescriptor, type MnemonicKeySSRConfig, type MnemonicKeyState, MnemonicProvider, type MnemonicProviderOptions, type MnemonicProviderProps, type MnemonicProviderSSRConfig, type MnemonicRecoveryAction, type MnemonicRecoveryEvent, type MnemonicRecoveryHook, type ReconcileContext, type SchemaBoundKeyOptions, SchemaError, type SchemaMode, type SchemaRegistry, type StorageLike, type StructuralNode, type StructuralTreeHelpers, type TypedJsonSchema, type TypedKeySchema, type Unsubscribe, type UseMnemonicKeyOptions, type UseMnemonicRecoveryOptions, compileSchema, createCodec, createSchemaRegistry, dedupeChildrenBy, defineKeySchema, defineMigration, defineMnemonicKey, defineWriteMigration, findNodeById, insertChildIfMissing, mnemonicSchema, renameNode, useMnemonicKey, useMnemonicRecovery, validateJsonSchema };