react-mnemonic 1.0.0-beta.0 → 1.2.0-beta1

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.cts CHANGED
@@ -1,1719 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode } from 'react';
3
-
4
- /**
5
- * @fileoverview Codec implementations for encoding and decoding values to/from storage.
6
- *
7
- * This module provides the built-in JSON codec and a factory for creating custom
8
- * codecs. Codecs handle the bidirectional transformation between typed JavaScript
9
- * values and their string representations for storage.
10
- *
11
- * Codecs are a low-level mechanism for keys that opt out of the JSON Schema
12
- * validation system. When a schema is registered for a key, the schema's
13
- * JSON Schema is used for validation and the payload is stored as a JSON value
14
- * directly (no codec encoding needed).
15
- */
16
-
17
- /**
18
- * Custom error class for codec encoding and decoding failures.
19
- *
20
- * Thrown when a codec cannot successfully encode a value to a string or
21
- * decode a string back to its typed representation. This allows callers
22
- * to distinguish codec errors from other types of errors.
23
- *
24
- * @example
25
- * ```typescript
26
- * try {
27
- * const value = JSONCodec.decode('not-valid-json');
28
- * } catch (error) {
29
- * if (error instanceof CodecError) {
30
- * console.error('Failed to decode:', error.message);
31
- * }
32
- * }
33
- * ```
34
- */
35
- declare class CodecError extends Error {
36
- /**
37
- * The underlying error that caused the codec failure, if any.
38
- *
39
- * Useful for debugging when wrapping errors from JSON.parse or
40
- * other parsing operations.
41
- */
42
- readonly cause?: unknown;
43
- /**
44
- * Creates a new CodecError.
45
- *
46
- * @param message - Human-readable error description
47
- * @param cause - Optional underlying error that caused this failure
48
- */
49
- constructor(message: string, cause?: unknown);
50
- }
51
- /**
52
- * JSON codec for encoding and decoding JSON-serializable values.
53
- *
54
- * This is the default codec used by `useMnemonicKey` when no codec is specified.
55
- * It uses `JSON.stringify` for encoding and `JSON.parse` for decoding, making it
56
- * suitable for objects, arrays, and primitive values.
57
- *
58
- * @remarks
59
- * - Supports any JSON-serializable type: objects, arrays, strings, numbers, booleans, null
60
- * - Does not preserve JavaScript-specific types like Date, Map, Set, or undefined
61
- * - Throws standard JSON parsing errors for malformed JSON strings
62
- *
63
- * @example
64
- * ```typescript
65
- * // Used automatically as the default
66
- * const { value, set } = useMnemonicKey('userProfile', {
67
- * defaultValue: { name: 'Guest', preferences: { theme: 'dark' } }
68
- * // codec: JSONCodec is implicit
69
- * });
70
- *
71
- * // Can be specified explicitly
72
- * const { value, set } = useMnemonicKey('settings', {
73
- * defaultValue: { notifications: true },
74
- * codec: JSONCodec
75
- * });
76
- * ```
77
- *
78
- * @see {@link createCodec} - For custom encoding schemes
79
- */
80
- declare const JSONCodec: Codec<any>;
81
- /**
82
- * Factory function for creating custom codecs.
83
- *
84
- * Creates a `Codec<T>` from separate encode and decode functions. This is
85
- * useful for implementing custom serialization strategies for types that
86
- * aren't supported by JSONCodec. Using a custom codec on a key opts out
87
- * of JSON Schema validation for that key.
88
- *
89
- * @template T - The TypeScript type of values to encode/decode
90
- *
91
- * @param encode - Function that converts a typed value to a string
92
- * @param decode - Function that converts a string back to a typed value
93
- * @returns A `Codec<T>` object compatible with useMnemonicKey
94
- *
95
- * @example
96
- * ```typescript
97
- * // Codec for Date objects
98
- * const DateCodec = createCodec<Date>(
99
- * (date) => date.toISOString(),
100
- * (str) => new Date(str)
101
- * );
102
- *
103
- * const { value, set } = useMnemonicKey('lastLogin', {
104
- * defaultValue: new Date(),
105
- * codec: DateCodec
106
- * });
107
- * ```
108
- *
109
- * @example
110
- * ```typescript
111
- * // Codec for Set<string>
112
- * const StringSetCodec = createCodec<Set<string>>(
113
- * (set) => JSON.stringify(Array.from(set)),
114
- * (str) => new Set(JSON.parse(str))
115
- * );
116
- *
117
- * const { value, set } = useMnemonicKey('tags', {
118
- * defaultValue: new Set<string>(),
119
- * codec: StringSetCodec
120
- * });
121
- * ```
122
- *
123
- * @see {@link Codec} - The codec interface
124
- * @see {@link CodecError} - Error to throw when encoding/decoding fails
125
- * @see {@link JSONCodec} - Built-in codec for JSON values
126
- */
127
- declare function createCodec<T>(encode: (value: T) => string, decode: (encoded: string) => T): Codec<T>;
128
-
129
- /**
130
- * @fileoverview Schema versioning primitives for Mnemonic.
131
- *
132
- * This module defines the envelope format used to wrap every persisted value
133
- * and the error class thrown when schema-related operations fail.
134
- */
135
- /**
136
- * Error thrown for schema registry, versioning, and migration failures.
137
- *
138
- * Each instance carries a machine-readable {@link code} that categorises
139
- * the failure. When a `defaultValue` factory is provided to
140
- * `useMnemonicKey`, the `SchemaError` is passed as the `error` argument
141
- * so the factory can inspect the failure reason.
142
- *
143
- * Error codes:
144
- *
145
- * | Code | Meaning |
146
- * | ------------------------------- | --------------------------------------------------------------- |
147
- * | `INVALID_ENVELOPE` | The raw stored value is not a valid `MnemonicEnvelope`. |
148
- * | `SCHEMA_NOT_FOUND` | No schema registered for the stored key + version. |
149
- * | `WRITE_SCHEMA_REQUIRED` | Strict mode requires a schema to write, but none was found. |
150
- * | `MIGRATION_PATH_NOT_FOUND` | No contiguous migration path between the stored and latest version. |
151
- * | `MIGRATION_FAILED` | A migration step threw during execution. |
152
- * | `MIGRATION_GRAPH_INVALID` | The schema registry helper received an ambiguous or cyclic migration graph. |
153
- * | `RECONCILE_FAILED` | A read-time reconciliation hook threw or returned an unpersistable value. |
154
- * | `SCHEMA_REGISTRATION_CONFLICT` | `registerSchema` was called with a conflicting definition. |
155
- * | `TYPE_MISMATCH` | The decoded value failed JSON Schema validation. |
156
- * | `MODE_CONFIGURATION_INVALID` | The schema mode requires a capability the registry doesn't provide. |
157
- *
158
- * @example
159
- * ```typescript
160
- * defaultValue: (error) => {
161
- * if (error instanceof SchemaError) {
162
- * console.warn(`Schema issue [${error.code}]:`, error.message);
163
- * }
164
- * return { name: "Guest" };
165
- * }
166
- * ```
167
- *
168
- * @see {@link SchemaMode} - How the provider uses schemas
169
- * @see {@link SchemaRegistry} - Where schemas and migrations are registered
170
- */
171
- declare class SchemaError extends Error {
172
- /**
173
- * Machine-readable code identifying the category of schema failure.
174
- */
175
- readonly code: "INVALID_ENVELOPE" | "SCHEMA_NOT_FOUND" | "WRITE_SCHEMA_REQUIRED" | "MIGRATION_PATH_NOT_FOUND" | "MIGRATION_FAILED" | "MIGRATION_GRAPH_INVALID" | "RECONCILE_FAILED" | "SCHEMA_REGISTRATION_CONFLICT" | "TYPE_MISMATCH" | "MODE_CONFIGURATION_INVALID";
176
- /**
177
- * The underlying error that caused this failure, if any.
178
- */
179
- readonly cause?: unknown;
180
- /**
181
- * Creates a new SchemaError.
182
- *
183
- * @param code - Machine-readable failure category
184
- * @param message - Human-readable error description
185
- * @param cause - Optional underlying error
186
- */
187
- constructor(code: SchemaError["code"], message: string, cause?: unknown);
188
- }
189
-
190
- /**
191
- * @fileoverview JSON Schema subset validator for Mnemonic.
192
- *
193
- * This module implements a minimal JSON Schema validator sufficient for
194
- * validating localStorage state. Only a subset of JSON Schema keywords
195
- * are supported; see {@link JsonSchema} for the full list.
196
- *
197
- * JSON Schema documents are plain JSON objects (inherently serializable),
198
- * making them suitable for storage alongside the data they describe.
199
- */
200
- /**
201
- * Supported JSON Schema type keywords.
202
- *
203
- * `"integer"` is a JSON Schema keyword meaning "a number that is a whole number."
204
- */
205
- type JsonSchemaType = "string" | "number" | "integer" | "boolean" | "null" | "object" | "array";
206
- /**
207
- * A subset of JSON Schema sufficient for localStorage state management.
208
- *
209
- * Supported keywords:
210
- * type, enum, const,
211
- * minimum, maximum, exclusiveMinimum, exclusiveMaximum,
212
- * minLength, maxLength,
213
- * properties, required, additionalProperties,
214
- * items, minItems, maxItems
215
- *
216
- * Deliberately omitted: $ref, $id, $schema, $defs, allOf, anyOf,
217
- * oneOf, not, pattern, format, patternProperties, if/then/else,
218
- * dependencies, uniqueItems, multipleOf, propertyNames.
219
- *
220
- * An empty schema `{}` accepts any value.
221
- */
222
- interface JsonSchema {
223
- /** The expected JSON type(s). An array form like `["string", "null"]` accepts either type. */
224
- type?: JsonSchemaType | JsonSchemaType[];
225
- /** The value must be deeply equal to one of these entries. */
226
- enum?: readonly unknown[];
227
- /** The value must be deeply equal to this exact value. */
228
- const?: unknown;
229
- /** Inclusive lower bound for numbers. */
230
- minimum?: number;
231
- /** Inclusive upper bound for numbers. */
232
- maximum?: number;
233
- /** Exclusive lower bound for numbers. */
234
- exclusiveMinimum?: number;
235
- /** Exclusive upper bound for numbers. */
236
- exclusiveMaximum?: number;
237
- /** Minimum string length (inclusive). */
238
- minLength?: number;
239
- /** Maximum string length (inclusive). */
240
- maxLength?: number;
241
- /** Property name to sub-schema mapping for objects. */
242
- properties?: Record<string, JsonSchema>;
243
- /** Properties that must be present on the object. */
244
- required?: readonly string[];
245
- /**
246
- * Controls extra properties not listed in `properties`.
247
- * `false` disallows them. A schema validates their values.
248
- * `true` (or omitted) allows anything.
249
- */
250
- additionalProperties?: boolean | JsonSchema;
251
- /** Schema applied to every element of an array. */
252
- items?: JsonSchema;
253
- /** Minimum array length (inclusive). */
254
- minItems?: number;
255
- /** Maximum array length (inclusive). */
256
- maxItems?: number;
257
- }
258
- /**
259
- * A single validation error produced by {@link validateJsonSchema}.
260
- */
261
- type JsonSchemaValidationError = {
262
- /** JSON Pointer path to the failing value (e.g., "/foo/bar/0"). Empty string for root. */
263
- path: string;
264
- /** Human-readable error description. */
265
- message: string;
266
- /** The JSON Schema keyword that failed. */
267
- keyword: string;
268
- };
269
- /**
270
- * A pre-compiled validation function generated by {@link compileSchema}.
271
- *
272
- * Accepts a value and an optional JSON Pointer path for error reporting.
273
- * Returns an array of validation errors (empty = valid).
274
- */
275
- type CompiledValidator = (value: unknown, path?: string) => JsonSchemaValidationError[];
276
- /**
277
- * Pre-compiles a {@link JsonSchema} into a reusable validation function.
278
- *
279
- * Inspects the schema once and builds a specialized closure that
280
- * eliminates runtime branching for unused keywords, pre-converts
281
- * `required` arrays to `Set`s, recursively pre-compiles nested property
282
- * and item schemas, and pre-builds primitive `Set`s for O(1) enum
283
- * lookups when possible.
284
- *
285
- * Results are cached by schema object identity in a `WeakMap`, so
286
- * calling `compileSchema` with the same schema reference is free
287
- * after the first call.
288
- *
289
- * @param schema - The JSON Schema to compile
290
- * @returns A compiled validation function
291
- */
292
- declare function compileSchema(schema: JsonSchema): CompiledValidator;
293
- /**
294
- * Validates a value against a {@link JsonSchema}.
295
- *
296
- * Returns an empty array when the value is valid.
297
- * Returns one or more {@link JsonSchemaValidationError} entries on failure.
298
- * Short-circuits on type mismatch (does not report downstream keyword errors).
299
- *
300
- * @param value - The value to validate
301
- * @param schema - The JSON Schema to validate against
302
- * @param path - Internal: JSON Pointer path for error reporting (default: `""`)
303
- * @returns Array of validation errors (empty = valid)
304
- */
305
- declare function validateJsonSchema(value: unknown, schema: JsonSchema, path?: string): JsonSchemaValidationError[];
306
-
307
- /**
308
- * @fileoverview Type definitions for the Mnemonic library.
309
- *
310
- * This module defines the core types and interfaces used throughout the Mnemonic
311
- * library for type-safe, persistent state management in React applications.
312
- */
313
-
314
- /**
315
- * Codec for encoding and decoding values to and from storage.
316
- *
317
- * Codecs provide bidirectional transformations between typed values and their
318
- * string representations suitable for storage in localStorage or similar backends.
319
- *
320
- * Using a codec on a key opts out of JSON Schema validation. Schema-managed
321
- * keys store JSON values directly and are validated against their JSON Schema.
322
- *
323
- * @template T - The TypeScript type of the value to encode/decode
324
- *
325
- * @example
326
- * ```typescript
327
- * const DateCodec: Codec<Date> = {
328
- * encode: (date) => date.toISOString(),
329
- * decode: (str) => new Date(str)
330
- * };
331
- * ```
332
- *
333
- * @see {@link JSONCodec} - Default codec for JSON-serializable values
334
- * @see {@link createCodec} - Helper function to create custom codecs
335
- */
336
- interface Codec<T> {
337
- /**
338
- * Transforms a typed value into a string suitable for storage.
339
- *
340
- * @param value - The typed value to encode
341
- * @returns A string representation of the value
342
- * @throws {CodecError} If the value cannot be encoded
343
- */
344
- encode: (value: T) => string;
345
- /**
346
- * Transforms a stored string back into a typed value.
347
- *
348
- * @param encoded - The string representation from storage
349
- * @returns The decoded typed value
350
- * @throws {CodecError} If the string cannot be decoded
351
- */
352
- decode: (encoded: string) => T;
353
- }
354
- /**
355
- * Configuration options for MnemonicProvider.
356
- *
357
- * These options configure the behavior of the storage provider, including
358
- * namespace isolation, storage backend selection, cross-tab synchronization,
359
- * and developer tools integration.
360
- *
361
- * @example
362
- * ```tsx
363
- * <MnemonicProvider
364
- * namespace="myApp"
365
- * storage={localStorage}
366
- * enableDevTools={process.env.NODE_ENV === 'development'}
367
- * >
368
- * <App />
369
- * </MnemonicProvider>
370
- * ```
371
- */
372
- interface MnemonicProviderOptions {
373
- /**
374
- * Namespace prefix for all storage keys.
375
- *
376
- * All keys stored by this provider will be prefixed with `${namespace}.`
377
- * to avoid collisions between different parts of your application or
378
- * different applications sharing the same storage backend.
379
- *
380
- * @example
381
- * ```typescript
382
- * // With namespace="myApp", a key "user" becomes "myApp.user" in storage
383
- * namespace: "myApp"
384
- * ```
385
- */
386
- namespace: string;
387
- /**
388
- * Storage backend to use for persistence.
389
- *
390
- * Defaults to `window.localStorage` in browser environments. You can provide
391
- * a custom implementation (e.g., sessionStorage, AsyncStorage, or a mock for testing).
392
- *
393
- * @default window.localStorage
394
- *
395
- * @example
396
- * ```typescript
397
- * // Use sessionStorage instead of localStorage
398
- * storage: window.sessionStorage
399
- *
400
- * // Use a custom storage implementation
401
- * storage: {
402
- * getItem: (key) => myCustomStore.get(key),
403
- * setItem: (key, value) => myCustomStore.set(key, value),
404
- * removeItem: (key) => myCustomStore.delete(key)
405
- * }
406
- * ```
407
- */
408
- storage?: StorageLike;
409
- /**
410
- * Enable DevTools debugging interface.
411
- *
412
- * When enabled, registers this provider in the global
413
- * `window.__REACT_MNEMONIC_DEVTOOLS__` registry.
414
- *
415
- * The registry stores providers as weak references and exposes:
416
- * - `resolve(namespace)` to strengthen a provider reference and access
417
- * inspection methods.
418
- * - `list()` to enumerate provider availability.
419
- *
420
- * @default false
421
- *
422
- * @example
423
- * ```typescript
424
- * // Enable in development only
425
- * enableDevTools: process.env.NODE_ENV === 'development'
426
- *
427
- * // Then in browser console:
428
- * const provider = window.__REACT_MNEMONIC_DEVTOOLS__.resolve('myApp')
429
- * provider?.dump()
430
- * provider?.get('user')
431
- * provider?.set('user', { name: 'Test' })
432
- * ```
433
- */
434
- enableDevTools?: boolean;
435
- /**
436
- * Versioning and schema enforcement mode.
437
- *
438
- * Controls whether stored values require a registered schema, and how
439
- * missing schemas are handled. See {@link SchemaMode} for the behaviour
440
- * of each mode.
441
- *
442
- * @default "default"
443
- *
444
- * @see {@link SchemaMode} - Detailed description of each mode
445
- * @see {@link SchemaRegistry} - Registry supplied via `schemaRegistry`
446
- */
447
- schemaMode?: SchemaMode;
448
- /**
449
- * Schema registry used for version lookup and migration resolution.
450
- *
451
- * When provided, the library uses the registry to find the correct
452
- * JSON Schema for each stored version, and to resolve migration paths
453
- * when upgrading old data to the latest schema.
454
- *
455
- * Required when `schemaMode` is `"strict"` or `"autoschema"`.
456
- * Optional (but recommended) in `"default"` mode.
457
- *
458
- * @remarks
459
- * In `"default"` and `"strict"` modes, the registry is treated as
460
- * immutable after the provider initializes. Updates should be shipped
461
- * as part of a new app version and applied by remounting the provider.
462
- * `"autoschema"` remains mutable so inferred schemas can be registered
463
- * at runtime.
464
- *
465
- * @see {@link SchemaRegistry} - Interface the registry must implement
466
- * @see {@link KeySchema} - Schema definition stored in the registry
467
- */
468
- schemaRegistry?: SchemaRegistry;
469
- }
470
- /**
471
- * Controls how the provider enforces versioned schemas on stored values.
472
- *
473
- * - `"default"` — Schemas are optional. When a schema exists for the stored
474
- * version it is used for validation; otherwise the hook's `codec` option is
475
- * used directly with no validation. This is the recommended starting mode.
476
- *
477
- * - `"strict"` — Every read and write **must** have a registered schema for
478
- * the stored version. If no matching schema is found the value falls back
479
- * to `defaultValue` with a `SchemaError` (`SCHEMA_NOT_FOUND` on reads,
480
- * `WRITE_SCHEMA_REQUIRED` on writes).
481
- * When no schemas are registered and no explicit schema is provided, writes
482
- * fall back to a codec-encoded (v0) envelope.
483
- *
484
- * - `"autoschema"` — Like `"default"`, but when a key has **no** schema
485
- * registered at all, the library infers a JSON Schema at version 1 from the
486
- * first successfully decoded value and registers it via
487
- * `SchemaRegistry.registerSchema`. Subsequent reads/writes for that key
488
- * then behave as if the schema had been registered manually.
489
- *
490
- * @remarks
491
- * In `"default"` and `"strict"` modes, registry lookups are cached under the
492
- * assumption that the schema registry is immutable for the lifetime of the
493
- * provider. If you need to update schemas, publish a new app version and
494
- * remount the provider. `"autoschema"` does not assume immutability.
495
- *
496
- * @default "default"
497
- *
498
- * @see {@link SchemaRegistry} - Registry that stores schemas and migrations
499
- * @see {@link KeySchema} - Individual schema definition
500
- */
501
- type SchemaMode = "strict" | "default" | "autoschema";
502
- /**
503
- * Weak-reference shape used by the devtools registry.
504
- *
505
- * Matches the standard `WeakRef` API while keeping the public type surface
506
- * compatible with ES2020 TypeScript lib targets.
507
- */
508
- interface MnemonicDevToolsWeakRef<T extends object> {
509
- /**
510
- * Attempts to strengthen the weak reference.
511
- *
512
- * @returns The live object, or undefined if it was garbage-collected.
513
- */
514
- deref: () => T | undefined;
515
- }
516
- /**
517
- * Provider inspection API exposed through devtools registry resolution.
518
- *
519
- * Resolve a provider from the registry, then invoke these methods for manual
520
- * inspection/mutation from the browser console.
521
- */
522
- interface MnemonicDevToolsProviderApi {
523
- /** Access the underlying store instance. */
524
- getStore: () => Mnemonic;
525
- /** Dump all raw key-value pairs for the provider namespace. */
526
- dump: () => Record<string, string>;
527
- /** Read decoded value for an unprefixed key. */
528
- get: (key: string) => unknown;
529
- /** Write value for an unprefixed key (JSON-encoded). */
530
- set: (key: string, value: unknown) => void;
531
- /** Remove a single unprefixed key. */
532
- remove: (key: string) => void;
533
- /** Remove all keys in this provider namespace. */
534
- clear: () => void;
535
- /** List all unprefixed keys in this provider namespace. */
536
- keys: () => string[];
537
- }
538
- /**
539
- * Registry entry for a single provider namespace.
540
- */
541
- interface MnemonicDevToolsProviderEntry {
542
- /** Namespace key for this provider entry. */
543
- namespace: string;
544
- /** Weak reference to the provider inspection API. */
545
- weakRef: MnemonicDevToolsWeakRef<MnemonicDevToolsProviderApi>;
546
- /** Timestamp when this namespace was registered. */
547
- registeredAt: number;
548
- /** Timestamp when provider was last confirmed live. */
549
- lastSeenAt: number;
550
- /** Timestamp when provider was first observed unavailable, or null when live. */
551
- staleSince: number | null;
552
- }
553
- /**
554
- * Lightweight provider status returned by `list()`.
555
- */
556
- interface MnemonicDevToolsProviderDescriptor {
557
- /** Namespace registered by the provider. */
558
- namespace: string;
559
- /** Whether the provider can currently be resolved to a live API instance. */
560
- available: boolean;
561
- /** Timestamp when the provider namespace was first registered. */
562
- registeredAt: number;
563
- /** Timestamp when the provider was last observed as live. */
564
- lastSeenAt: number;
565
- /** Timestamp when the provider first became unavailable, or `null` when live. */
566
- staleSince: number | null;
567
- }
568
- /**
569
- * Environment capabilities reported by devtools registry.
570
- */
571
- interface MnemonicDevToolsCapabilities {
572
- /** Whether the runtime supports `WeakRef`. */
573
- weakRef: boolean;
574
- /** Whether the runtime supports `FinalizationRegistry`. */
575
- finalizationRegistry: boolean;
576
- }
577
- /**
578
- * Polling metadata for extension synchronization.
579
- */
580
- interface MnemonicDevToolsMeta {
581
- /** Monotonic registry version incremented on register and mutation events. */
582
- version: number;
583
- /** Timestamp of the most recent registry update. */
584
- lastUpdated: number;
585
- /** Short label describing the most recent registry change. */
586
- lastChange: string;
587
- }
588
- /**
589
- * Global devtools registry contract available on window.
590
- *
591
- * This is an advanced public API used by the browser console integration and
592
- * extension tooling. Direct namespace access
593
- * (`window.__REACT_MNEMONIC_DEVTOOLS__.myNamespace`) is not part of the
594
- * public API.
595
- */
596
- interface MnemonicDevToolsRegistry {
597
- /** Provider entries keyed by namespace. */
598
- providers: Record<string, MnemonicDevToolsProviderEntry>;
599
- /** Resolve a namespace to a live provider API when one is available. */
600
- resolve: (namespace: string) => MnemonicDevToolsProviderApi | null;
601
- /** List provider availability without strengthening weak references manually. */
602
- list: () => MnemonicDevToolsProviderDescriptor[];
603
- /** Runtime capabilities relevant to the registry implementation. */
604
- capabilities: MnemonicDevToolsCapabilities;
605
- /** Versioning metadata used by polling devtools integrations. */
606
- __meta: MnemonicDevToolsMeta;
607
- }
608
- /**
609
- * Schema definition for a single key at a specific version.
610
- *
611
- * Each registered schema binds a storage key + version number to a
612
- * JSON Schema that validates the payload. Schemas are fully serializable
613
- * (no functions).
614
- *
615
- * When the provider reads a value whose envelope version matches a
616
- * registered schema, the payload is validated against the schema's
617
- * JSON Schema definition.
618
- *
619
- * @example
620
- * ```typescript
621
- * const userSchemaV1: KeySchema = {
622
- * key: "user",
623
- * version: 1,
624
- * schema: {
625
- * type: "object",
626
- * properties: {
627
- * name: { type: "string" },
628
- * },
629
- * required: ["name"],
630
- * },
631
- * };
632
- * ```
633
- *
634
- * @see {@link SchemaRegistry} - Where schemas are registered and looked up
635
- * @see {@link MigrationRule} - How values migrate between schema versions
636
- * @see {@link JsonSchema} - The JSON Schema subset used for validation
637
- */
638
- type KeySchema = {
639
- /**
640
- * The unprefixed storage key this schema applies to.
641
- */
642
- key: string;
643
- /**
644
- * The version number for this schema.
645
- *
646
- * Must be a non-negative integer. Any version (including `0`) is valid.
647
- */
648
- version: number;
649
- /**
650
- * JSON Schema that validates the payload at this version.
651
- *
652
- * Only the subset of JSON Schema keywords defined in {@link JsonSchema}
653
- * are supported. An empty schema `{}` accepts any value.
654
- */
655
- schema: JsonSchema;
656
- };
657
- /**
658
- * A single migration step that transforms data from one schema version to
659
- * another, or normalizes data at the same version.
660
- *
661
- * Migration rules are composed into a {@link MigrationPath} by the
662
- * {@link SchemaRegistry} to upgrade stored data across multiple versions in
663
- * sequence (e.g. v1 -> v2 -> v3).
664
- *
665
- * When `fromVersion === toVersion`, the rule is a **write-time normalizer**
666
- * that runs on every write to that version. This is useful for data
667
- * normalization (trimming strings, clamping values, injecting defaults).
668
- *
669
- * @example
670
- * ```typescript
671
- * // Version upgrade migration
672
- * const userV1ToV2: MigrationRule = {
673
- * key: "user",
674
- * fromVersion: 1,
675
- * toVersion: 2,
676
- * migrate: (v1) => {
677
- * const old = v1 as { name: string };
678
- * return { firstName: old.name, lastName: "" };
679
- * },
680
- * };
681
- *
682
- * // Write-time normalizer (same version)
683
- * const trimUserV2: MigrationRule = {
684
- * key: "user",
685
- * fromVersion: 2,
686
- * toVersion: 2,
687
- * migrate: (v) => {
688
- * const user = v as { firstName: string; lastName: string };
689
- * return { firstName: user.firstName.trim(), lastName: user.lastName.trim() };
690
- * },
691
- * };
692
- * ```
693
- *
694
- * @see {@link MigrationPath} - Ordered list of rules applied in sequence
695
- * @see {@link SchemaRegistry.getMigrationPath} - How the path is resolved
696
- * @see {@link SchemaRegistry.getWriteMigration} - How write-time normalizers are resolved
697
- */
698
- type MigrationRule = {
699
- /**
700
- * The unprefixed storage key this rule applies to.
701
- */
702
- key: string;
703
- /**
704
- * The version the stored data is migrating **from**.
705
- *
706
- * Version `0` is allowed, enabling migrations from unversioned data.
707
- */
708
- fromVersion: number;
709
- /**
710
- * The version the stored data is migrating **to**.
711
- *
712
- * When equal to `fromVersion`, this rule is a write-time normalizer
713
- * that runs on every write to that version.
714
- */
715
- toVersion: number;
716
- /**
717
- * Transformation function that converts data from `fromVersion`
718
- * to `toVersion`.
719
- *
720
- * Receives the decoded value at `fromVersion` and must return
721
- * the value in the shape expected by `toVersion`.
722
- *
723
- * @param value - The decoded value at `fromVersion`
724
- * @returns The transformed value for `toVersion`
725
- */
726
- migrate: (value: unknown) => unknown;
727
- };
728
- /**
729
- * An ordered sequence of {@link MigrationRule} steps that upgrades stored
730
- * data from an older schema version to a newer one.
731
- *
732
- * The rules are applied in array order. Each step's output becomes the
733
- * next step's input. After the final step the result is validated against
734
- * the target schema and persisted back to storage so the migration only
735
- * runs once per key.
736
- *
737
- * @see {@link MigrationRule} - Individual migration step
738
- * @see {@link SchemaRegistry.getMigrationPath} - Resolves a path between versions
739
- */
740
- type MigrationPath = MigrationRule[];
741
- /**
742
- * Input options for {@link createSchemaRegistry}.
743
- *
744
- * Use this helper when your registry contents are known up front and do not
745
- * need runtime mutation. The returned registry is immutable and optimized for
746
- * the common `"default"` / `"strict"` setup.
747
- */
748
- interface CreateSchemaRegistryOptions {
749
- /**
750
- * Versioned schemas to index by key and version.
751
- *
752
- * Duplicate `key + version` pairs are rejected up front with
753
- * `SchemaError("SCHEMA_REGISTRATION_CONFLICT")`.
754
- */
755
- schemas?: readonly KeySchema[];
756
- /**
757
- * Migration rules to index by key and version edge.
758
- *
759
- * Write-time normalizers (`fromVersion === toVersion`) are indexed
760
- * separately from read-time migration edges. Ambiguous outgoing edges,
761
- * backward migrations, and duplicate write normalizers are rejected up
762
- * front with `SchemaError("MIGRATION_GRAPH_INVALID")`.
763
- */
764
- migrations?: readonly MigrationRule[];
765
- }
766
- /**
767
- * Lookup and registration API for key schemas and migration paths.
768
- *
769
- * Implementations of this interface are passed to `MnemonicProvider` via the
770
- * `schemaRegistry` option. The provider calls these methods at read and write
771
- * time to resolve the correct JSON Schema and migration chain for each
772
- * stored value.
773
- *
774
- * In `"default"` and `"strict"` modes, callers should treat registry contents
775
- * as immutable after provider initialization. The hook caches lookups to keep
776
- * read/write hot paths fast. `"autoschema"` remains mutable to support
777
- * inferred schema registration.
778
- *
779
- * @example
780
- * ```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
- * };
788
- *
789
- * <MnemonicProvider namespace="app" schemaRegistry={registry} schemaMode="strict">
790
- * <App />
791
- * </MnemonicProvider>
792
- * ```
793
- *
794
- * @see {@link KeySchema} - Schema definition
795
- * @see {@link MigrationPath} - Migration chain returned by `getMigrationPath`
796
- * @see {@link SchemaMode} - How the provider uses the registry
797
- */
798
- interface SchemaRegistry {
799
- /**
800
- * Look up the schema registered for a specific key and version.
801
- *
802
- * @param key - The unprefixed storage key
803
- * @param version - The version number to look up
804
- * @returns The matching schema, or `undefined` if none is registered
805
- */
806
- getSchema(key: string, version: number): KeySchema | undefined;
807
- /**
808
- * Look up the highest-version schema registered for a key.
809
- *
810
- * Used by the write path to determine which version to stamp on new
811
- * values, and by the read path to detect when a migration is needed.
812
- *
813
- * @param key - The unprefixed storage key
814
- * @returns The latest schema, or `undefined` if none is registered
815
- */
816
- getLatestSchema(key: string): KeySchema | undefined;
817
- /**
818
- * Resolve an ordered migration path between two versions of a key.
819
- *
820
- * Returns `null` when no contiguous path exists. The returned rules
821
- * are applied in order to transform data from `fromVersion` to
822
- * `toVersion`.
823
- *
824
- * @param key - The unprefixed storage key
825
- * @param fromVersion - The stored data's current version
826
- * @param toVersion - The target version to migrate to
827
- * @returns An ordered array of migration rules, or `null`
828
- */
829
- getMigrationPath(key: string, fromVersion: number, toVersion: number): MigrationPath | null;
830
- /**
831
- * Look up a write-time normalizer for a specific key and version.
832
- *
833
- * A write-time normalizer is a {@link MigrationRule} where
834
- * `fromVersion === toVersion`. It runs on every write to that version,
835
- * transforming the value before storage. The normalized value is
836
- * re-validated against the schema after transformation.
837
- *
838
- * Optional. When not implemented or returns `undefined`, no write-time
839
- * normalization is applied.
840
- *
841
- * @param key - The unprefixed storage key
842
- * @param version - The target schema version
843
- * @returns The normalizer rule, or `undefined` if none is registered
844
- */
845
- getWriteMigration?(key: string, version: number): MigrationRule | undefined;
846
- /**
847
- * Register a new schema.
848
- *
849
- * Optional. Required when `schemaMode` is `"autoschema"` so the
850
- * library can persist inferred schemas. Implementations should throw
851
- * if a schema already exists for the same key + version with a
852
- * conflicting definition.
853
- *
854
- * @param schema - The schema to register
855
- */
856
- registerSchema?(schema: KeySchema): void;
857
- }
858
- /**
859
- * Storage interface compatible with localStorage and custom storage implementations.
860
- *
861
- * Defines the minimum contract required for a storage backend. Compatible with
862
- * browser Storage API (localStorage, sessionStorage) and custom implementations
863
- * for testing or alternative storage solutions.
864
- *
865
- * @remarks
866
- * **Error handling contract**
867
- *
868
- * The library wraps every storage call in a try/catch. Errors are handled as
869
- * follows:
870
- *
871
- * - **`DOMException` with `name === "QuotaExceededError"`** — Logged once via
872
- * `console.error` with the prefix `[Mnemonic] Storage quota exceeded`.
873
- * Squelched until a write succeeds, then the flag resets.
874
- *
875
- * - **Other `DOMException` errors (including `SecurityError`)** — Logged once
876
- * via `console.error` with the prefix `[Mnemonic] Storage access error`.
877
- * Squelched until any storage operation succeeds, then the flag resets.
878
- *
879
- * - **All other error types** — Silently suppressed.
880
- *
881
- * Custom `StorageLike` implementations are encouraged to throw `DOMException`
882
- * for storage access failures so the library can surface diagnostics. Throwing
883
- * non-`DOMException` errors is safe but results in silent suppression.
884
- *
885
- * In all error cases the library falls back to its in-memory cache, so
886
- * components continue to function when the storage backend is unavailable.
887
- *
888
- * @example
889
- * ```typescript
890
- * // In-memory storage for testing
891
- * const mockStorage: StorageLike = {
892
- * items: new Map<string, string>(),
893
- * getItem(key) { return this.items.get(key) ?? null; },
894
- * setItem(key, value) { this.items.set(key, value); },
895
- * removeItem(key) { this.items.delete(key); },
896
- * get length() { return this.items.size; },
897
- * key(index) {
898
- * return Array.from(this.items.keys())[index] ?? null;
899
- * }
900
- * };
901
- * ```
902
- */
903
- type StorageLike = {
904
- /**
905
- * Retrieves the value associated with a key.
906
- *
907
- * @param key - The storage key to retrieve
908
- * @returns The stored value as a string, or null if not found
909
- */
910
- getItem(key: string): string | null;
911
- /**
912
- * Stores a key-value pair.
913
- *
914
- * @param key - The storage key
915
- * @param value - The string value to store
916
- */
917
- setItem(key: string, value: string): void;
918
- /**
919
- * Removes a key-value pair from storage.
920
- *
921
- * @param key - The storage key to remove
922
- */
923
- removeItem(key: string): void;
924
- /**
925
- * Returns the key at the specified index in storage.
926
- *
927
- * Optional method for enumeration support.
928
- *
929
- * @param index - The numeric index
930
- * @returns The key at the given index, or null if out of bounds
931
- */
932
- key?(index: number): string | null;
933
- /**
934
- * The number of items currently stored.
935
- *
936
- * Optional property for enumeration support.
937
- */
938
- readonly length?: number;
939
- /**
940
- * Subscribe to notifications when data changes externally.
941
- *
942
- * localStorage has built-in cross-tab notification via the browser's
943
- * native `storage` event (used by the `listenCrossTab` hook option).
944
- * Non-localStorage backends (IndexedDB, custom stores, etc.) lack this
945
- * built-in mechanism. Implementing `onExternalChange` allows those
946
- * adapters to provide equivalent cross-tab synchronization through
947
- * their own transport (e.g., BroadcastChannel).
948
- *
949
- * The callback accepts an optional `changedKeys` parameter:
950
- * - `callback()` or `callback(undefined)` triggers a blanket reload
951
- * of all actively subscribed keys.
952
- * - `callback(["ns.key1", "ns.key2"])` reloads only the specified
953
- * fully-qualified keys, which is more efficient when the adapter
954
- * knows exactly which keys changed.
955
- * - `callback([])` is a no-op.
956
- *
957
- * On a blanket reload the provider re-reads all actively subscribed
958
- * keys from the storage backend and emits change notifications for
959
- * any whose values differ from the cache.
960
- *
961
- * @param callback - Invoked when external data changes
962
- * @returns An unsubscribe function that removes the callback
963
- */
964
- onExternalChange?: (callback: (changedKeys?: string[]) => void) => () => void;
965
- };
966
- /**
967
- * Function type for unsubscribing from event listeners.
968
- *
969
- * Call this function to remove a subscription and stop receiving updates.
970
- *
971
- * @example
972
- * ```typescript
973
- * const unsubscribe = store.subscribeRaw('user', () => console.log('Updated!'));
974
- * // Later...
975
- * unsubscribe(); // Stop listening
976
- * ```
977
- */
978
- type Unsubscribe = () => void;
979
- /**
980
- * Callback function invoked when a subscribed value changes.
981
- *
982
- * Used by the external store contract to notify React when state updates.
983
- */
984
- type Listener = () => void;
985
- /**
986
- * Low-level Mnemonic store API provided via React Context.
987
- *
988
- * This interface powers `MnemonicProvider` internally and is also exposed to
989
- * advanced consumers through `MnemonicDevToolsProviderApi.getStore()`. Typical
990
- * application code should still prefer `useMnemonicKey`.
991
- *
992
- * All keys passed to these methods should be **unprefixed**. The store
993
- * automatically applies the namespace prefix internally.
994
- *
995
- * @remarks
996
- * This implements the React `useSyncExternalStore` contract for efficient,
997
- * tearing-free state synchronization. Most application code should still
998
- * prefer `useMnemonicKey`; this type mainly appears in the DevTools API via
999
- * `MnemonicDevToolsProviderApi.getStore()`.
1000
- */
1001
- type Mnemonic = {
1002
- /**
1003
- * The namespace prefix applied to all keys in storage.
1004
- *
1005
- * Keys are stored as `${prefix}${key}` in the underlying storage backend.
1006
- */
1007
- prefix: string;
1008
- /**
1009
- * Whether the active storage backend can enumerate keys in this namespace.
1010
- *
1011
- * This is `true` for `localStorage`-like backends that implement both
1012
- * `length` and `key(index)`. Namespace-wide recovery helpers rely on this
1013
- * capability unless the caller supplies an explicit key list.
1014
- */
1015
- canEnumerateKeys: boolean;
1016
- /**
1017
- * Subscribe to changes for a specific key.
1018
- *
1019
- * Follows the React external store subscription contract. The listener
1020
- * will be called whenever the value for this key changes.
1021
- *
1022
- * @param key - The unprefixed storage key to subscribe to
1023
- * @param listener - Callback invoked when the value changes
1024
- * @returns Unsubscribe function to stop listening
1025
- *
1026
- * @example
1027
- * ```typescript
1028
- * const unsubscribe = store.subscribeRaw('user', () => {
1029
- * console.log('User changed:', store.getRawSnapshot('user'));
1030
- * });
1031
- * ```
1032
- */
1033
- subscribeRaw: (key: string, listener: Listener) => Unsubscribe;
1034
- /**
1035
- * Get the current raw string value for a key.
1036
- *
1037
- * This is part of the external store snapshot contract. Values are
1038
- * cached in memory for stable snapshots.
1039
- *
1040
- * @param key - The unprefixed storage key
1041
- * @returns The raw string value, or null if not present
1042
- */
1043
- getRawSnapshot: (key: string) => string | null;
1044
- /**
1045
- * Write a raw string value to storage.
1046
- *
1047
- * Updates both the in-memory cache and the underlying storage backend,
1048
- * then notifies all subscribers for this key.
1049
- *
1050
- * @param key - The unprefixed storage key
1051
- * @param raw - The raw string value to store
1052
- */
1053
- setRaw: (key: string, raw: string) => void;
1054
- /**
1055
- * Remove a key from storage.
1056
- *
1057
- * Clears the value from both the cache and the underlying storage,
1058
- * then notifies all subscribers.
1059
- *
1060
- * @param key - The unprefixed storage key to remove
1061
- */
1062
- removeRaw: (key: string) => void;
1063
- /**
1064
- * Enumerate all keys in this namespace.
1065
- *
1066
- * Returns unprefixed keys that belong to this store's namespace.
1067
- *
1068
- * @returns Array of unprefixed key names
1069
- */
1070
- keys: () => string[];
1071
- /**
1072
- * Dump all key-value pairs in this namespace.
1073
- *
1074
- * Useful for debugging and DevTools integration.
1075
- *
1076
- * @returns Object mapping unprefixed keys to raw string values
1077
- */
1078
- dump: () => Record<string, string>;
1079
- /**
1080
- * The active schema enforcement mode for this provider.
1081
- *
1082
- * Propagated from the `schemaMode` provider option. Hooks read this
1083
- * to determine how to handle versioned envelopes.
1084
- *
1085
- * @see {@link SchemaMode}
1086
- */
1087
- schemaMode: SchemaMode;
1088
- /**
1089
- * The schema registry for this provider, if one was supplied.
1090
- *
1091
- * Hooks use this to look up schemas, resolve migration paths, and
1092
- * (in autoschema mode) register inferred schemas.
1093
- *
1094
- * @see {@link SchemaRegistry}
1095
- */
1096
- schemaRegistry?: SchemaRegistry;
1097
- };
1098
- /**
1099
- * Recovery action names emitted by {@link useMnemonicRecovery}.
1100
- */
1101
- type MnemonicRecoveryAction = "clear-all" | "clear-keys" | "clear-matching";
1102
- /**
1103
- * Recovery event payload emitted after a namespace recovery action completes.
1104
- */
1105
- interface MnemonicRecoveryEvent {
1106
- /**
1107
- * Recovery action that just ran.
1108
- */
1109
- action: MnemonicRecoveryAction;
1110
- /**
1111
- * Namespace where the recovery action ran.
1112
- */
1113
- namespace: string;
1114
- /**
1115
- * Unprefixed keys cleared by the action.
1116
- */
1117
- clearedKeys: string[];
1118
- }
1119
- /**
1120
- * Options for {@link useMnemonicRecovery}.
1121
- */
1122
- interface UseMnemonicRecoveryOptions {
1123
- /**
1124
- * Optional callback invoked after a recovery action completes.
1125
- *
1126
- * Useful for analytics, audit trails, support diagnostics, or user-facing
1127
- * confirmation toasts.
1128
- */
1129
- onRecover?: (event: MnemonicRecoveryEvent) => void;
1130
- }
1131
- /**
1132
- * Namespace-scoped recovery helpers returned by {@link useMnemonicRecovery}.
1133
- *
1134
- * These helpers operate on the current provider namespace and are intended for
1135
- * user-facing recovery UX such as "reset app data" or "clear stale filters".
1136
- */
1137
- interface MnemonicRecoveryHook {
1138
- /**
1139
- * Current provider namespace without the trailing storage prefix dot.
1140
- */
1141
- namespace: string;
1142
- /**
1143
- * Whether namespace keys can be enumerated automatically.
1144
- *
1145
- * `clearAll()` and `clearMatching()` require this to be `true`. If it is
1146
- * `false`, prefer `clearKeys([...])` with an explicit durable-key list.
1147
- */
1148
- canEnumerateKeys: boolean;
1149
- /**
1150
- * Lists all unprefixed keys currently visible in this namespace.
1151
- *
1152
- * Returns an empty array when no keys exist or when the storage backend
1153
- * cannot enumerate keys.
1154
- */
1155
- listKeys: () => string[];
1156
- /**
1157
- * Clears every key in the current namespace.
1158
- *
1159
- * @throws {Error} When the storage backend cannot enumerate namespace keys
1160
- */
1161
- clearAll: () => string[];
1162
- /**
1163
- * Clears a specific set of unprefixed keys in the current namespace.
1164
- *
1165
- * Duplicate keys are ignored.
1166
- */
1167
- clearKeys: (keys: readonly string[]) => string[];
1168
- /**
1169
- * Clears namespace keys whose names match the supplied predicate.
1170
- *
1171
- * @throws {Error} When the storage backend cannot enumerate namespace keys
1172
- */
1173
- clearMatching: (predicate: (key: string) => boolean) => string[];
1174
- }
1175
- /**
1176
- * Configuration options for the useMnemonicKey hook.
1177
- *
1178
- * These options control how a value is persisted, decoded, and
1179
- * synchronized across the application.
1180
- *
1181
- * @template T - The TypeScript type of the stored value
1182
- *
1183
- * @example
1184
- * ```typescript
1185
- * const { value, set } = useMnemonicKey<User>('currentUser', {
1186
- * defaultValue: { name: 'Guest', id: null },
1187
- * onMount: (user) => console.log('Loaded user:', user),
1188
- * onChange: (current, previous) => {
1189
- * console.log('User changed from', previous, 'to', current);
1190
- * },
1191
- * listenCrossTab: true
1192
- * });
1193
- * ```
1194
- */
1195
- type UseMnemonicKeyOptions<T> = {
1196
- /**
1197
- * Default value to use when no stored value exists, or when decoding/validation fails.
1198
- *
1199
- * Can be a literal value or a factory function that returns the default.
1200
- * Factory functions receive an optional error argument describing why the
1201
- * fallback is being used:
1202
- *
1203
- * - `undefined` — Nominal path: no value exists in storage for this key.
1204
- * - `CodecError` — The stored value could not be decoded by the codec.
1205
- * - `SchemaError` — A schema, migration, or validation issue occurred
1206
- * (e.g. missing schema, failed migration, JSON Schema validation failure).
1207
- *
1208
- * Static (non-function) default values ignore the error entirely.
1209
- *
1210
- * @remarks
1211
- * If a factory function is defined inline, it creates a new reference on
1212
- * every render, which forces internal memoization to recompute. For best
1213
- * performance, define the factory at module level or wrap it in `useCallback`:
1214
- *
1215
- * ```typescript
1216
- * // Module-level (stable reference, preferred)
1217
- * const getDefault = (error?: CodecError | SchemaError) => {
1218
- * if (error) console.warn('Fallback:', error.message);
1219
- * return { count: 0 };
1220
- * };
1221
- *
1222
- * // Or with useCallback inside a component
1223
- * const getDefault = useCallback(
1224
- * (error?: CodecError | SchemaError) => ({ count: 0 }),
1225
- * [],
1226
- * );
1227
- * ```
1228
- *
1229
- * @example
1230
- * ```typescript
1231
- * // Static default
1232
- * defaultValue: { count: 0 }
1233
- *
1234
- * // Factory with no error handling
1235
- * defaultValue: () => ({ timestamp: Date.now() })
1236
- *
1237
- * // Error-aware factory
1238
- * defaultValue: (error) => {
1239
- * if (error instanceof CodecError) {
1240
- * console.error('Corrupt data:', error.message);
1241
- * }
1242
- * if (error instanceof SchemaError) {
1243
- * console.warn('Schema issue:', error.code, error.message);
1244
- * }
1245
- * return { count: 0 };
1246
- * }
1247
- * ```
1248
- */
1249
- defaultValue: T | ((error?: CodecError | SchemaError) => T);
1250
- /**
1251
- * Codec for encoding and decoding values to/from storage.
1252
- *
1253
- * Determines how the typed value is serialized to a string and
1254
- * deserialized back. Defaults to JSONCodec if not specified.
1255
- *
1256
- * Using a codec is a low-level option that bypasses JSON Schema
1257
- * validation. Schema-managed keys store JSON values directly and
1258
- * are validated against their registered JSON Schema.
1259
- *
1260
- * @default JSONCodec
1261
- *
1262
- * @example
1263
- * ```typescript
1264
- * // Custom codec for dates
1265
- * codec: createCodec(
1266
- * (date) => date.toISOString(),
1267
- * (str) => new Date(str)
1268
- * )
1269
- * ```
1270
- */
1271
- codec?: Codec<T>;
1272
- /**
1273
- * Optional read-time reconciliation hook for persisted values.
1274
- *
1275
- * Runs after a stored value has been decoded and any read-time migrations
1276
- * have completed, but before the hook exposes the value to React. This is
1277
- * useful for selectively enforcing newly shipped defaults or normalizing
1278
- * legacy persisted values without discarding the whole key.
1279
- *
1280
- * If the reconciled value would persist differently from the pre-reconcile
1281
- * value, the hook rewrites storage once using the normal write path.
1282
- *
1283
- * @remarks
1284
- * Prefer schema migrations for structural changes that must always happen
1285
- * between explicit versions. Use `reconcile` for conditional, field-level
1286
- * adjustments that depend on application policy rather than a strict schema
1287
- * upgrade step.
1288
- *
1289
- * If `reconcile` throws a `SchemaError`, that error is preserved and passed
1290
- * to `defaultValue`. Any other thrown error is wrapped as
1291
- * `SchemaError("RECONCILE_FAILED")`.
1292
- *
1293
- * @param value - The decoded persisted value
1294
- * @param context - Metadata about the stored and latest schema versions
1295
- *
1296
- * @example
1297
- * ```typescript
1298
- * reconcile: (value, { persistedVersion }) => ({
1299
- * ...value,
1300
- * theme: persistedVersion < 2 ? "dark" : value.theme,
1301
- * })
1302
- * ```
1303
- */
1304
- reconcile?: (value: T, context: ReconcileContext) => T;
1305
- /**
1306
- * Callback invoked once when the hook is first mounted.
1307
- *
1308
- * Receives the initial value (either from storage or the default).
1309
- * Useful for triggering side effects based on the loaded state.
1310
- *
1311
- * @param value - The initial value loaded on mount
1312
- *
1313
- * @example
1314
- * ```typescript
1315
- * onMount: (theme) => {
1316
- * document.body.className = theme;
1317
- * console.log('Theme loaded:', theme);
1318
- * }
1319
- * ```
1320
- */
1321
- onMount?: (value: T) => void;
1322
- /**
1323
- * Callback invoked whenever the value changes.
1324
- *
1325
- * Receives both the new value and the previous value. This is called
1326
- * for all changes, including those triggered by other components or tabs.
1327
- *
1328
- * @param value - The new current value
1329
- * @param prev - The previous value
1330
- *
1331
- * @example
1332
- * ```typescript
1333
- * onChange: (newTheme, oldTheme) => {
1334
- * document.body.classList.remove(oldTheme);
1335
- * document.body.classList.add(newTheme);
1336
- * console.log(`Theme changed: ${oldTheme} -> ${newTheme}`);
1337
- * }
1338
- * ```
1339
- */
1340
- onChange?: (value: T, prev: T) => void;
1341
- /**
1342
- * Enable listening for changes from other browser tabs.
1343
- *
1344
- * When true, uses the browser's `storage` event to detect changes
1345
- * made to localStorage in other tabs and synchronizes them to this component.
1346
- *
1347
- * Only effective when using localStorage as the storage backend.
1348
- *
1349
- * @default false
1350
- *
1351
- * @example
1352
- * ```typescript
1353
- * // Enable cross-tab sync for shared state
1354
- * listenCrossTab: true
1355
- * ```
1356
- *
1357
- * @remarks
1358
- * The `storage` event only fires for changes made in *other* tabs,
1359
- * not the current tab. Changes within the same tab are synchronized
1360
- * automatically via React's state management.
1361
- */
1362
- listenCrossTab?: boolean;
1363
- /**
1364
- * Optional schema controls for this key.
1365
- *
1366
- * Allows overriding the version written by the `set` function. When
1367
- * omitted, the library writes using the highest registered schema for
1368
- * this key, or version `0` when no schemas are registered.
1369
- *
1370
- * @example
1371
- * ```typescript
1372
- * // Pin writes to schema version 2 even if version 3 exists
1373
- * const { value, set } = useMnemonicKey("user", {
1374
- * defaultValue: { name: "" },
1375
- * schema: { version: 2 },
1376
- * });
1377
- * ```
1378
- */
1379
- schema?: {
1380
- /**
1381
- * Explicit schema version to use when writing values.
1382
- *
1383
- * When set, the `set` and `reset` functions encode using the
1384
- * schema registered at this version instead of the latest. Useful
1385
- * during gradual rollouts where not all consumers have been
1386
- * updated yet.
1387
- *
1388
- * Must reference a version that exists in the `SchemaRegistry`.
1389
- * If not found the write falls back to the latest schema (default
1390
- * mode) or fails with a `SchemaError` (strict mode).
1391
- */
1392
- version?: number;
1393
- };
1394
- };
1395
- /**
1396
- * Metadata passed to `UseMnemonicKeyOptions.reconcile`.
1397
- */
1398
- type ReconcileContext = {
1399
- /**
1400
- * The unprefixed storage key being reconciled.
1401
- */
1402
- key: string;
1403
- /**
1404
- * The version found in the persisted envelope that was read.
1405
- */
1406
- persistedVersion: number;
1407
- /**
1408
- * The latest registered schema version for the key, when available.
1409
- */
1410
- latestVersion?: number;
1411
- };
1412
-
1413
- /**
1414
- * Props for the MnemonicProvider component.
1415
- *
1416
- * Extends MnemonicProviderOptions with required children prop.
1417
- *
1418
- * @see {@link MnemonicProviderOptions} - Configuration options
1419
- * @see {@link MnemonicProvider} - Provider component
1420
- */
1421
- interface MnemonicProviderProps extends MnemonicProviderOptions {
1422
- /**
1423
- * React children to render within the provider.
1424
- */
1425
- children: ReactNode;
1426
- }
1427
- /**
1428
- * React Context provider for namespace-isolated persistent state.
1429
- *
1430
- * Creates a scoped storage environment where all keys are automatically prefixed
1431
- * with the namespace to prevent collisions. Implements an in-memory cache with
1432
- * read-through behavior to the underlying storage backend (localStorage by default).
1433
- *
1434
- * This provider must wrap any components that use `useMnemonicKey`. Multiple
1435
- * providers with different namespaces can coexist in the same application.
1436
- *
1437
- * @param props - Provider configuration and children
1438
- * @param props.children - React children to render within the provider
1439
- * @param props.namespace - Unique namespace for isolating storage keys
1440
- * @param props.storage - Optional custom storage backend (defaults to localStorage)
1441
- * @param props.enableDevTools - Enable DevTools debugging interface (defaults to false)
1442
- * @param props.schemaMode - Schema enforcement mode (default: "default")
1443
- * @param props.schemaRegistry - Optional schema registry for storing schemas and migrations
1444
- *
1445
- * @example
1446
- * ```tsx
1447
- * // Basic usage with default settings
1448
- * function App() {
1449
- * return (
1450
- * <MnemonicProvider namespace="myApp">
1451
- * <MyComponents />
1452
- * </MnemonicProvider>
1453
- * );
1454
- * }
1455
- * ```
1456
- *
1457
- * @example
1458
- * ```tsx
1459
- * // With custom storage backend
1460
- * function App() {
1461
- * return (
1462
- * <MnemonicProvider
1463
- * namespace="myApp"
1464
- * storage={window.sessionStorage}
1465
- * >
1466
- * <MyComponents />
1467
- * </MnemonicProvider>
1468
- * );
1469
- * }
1470
- * ```
1471
- *
1472
- * @example
1473
- * ```tsx
1474
- * // With DevTools enabled (development only)
1475
- * function App() {
1476
- * return (
1477
- * <MnemonicProvider
1478
- * namespace="myApp"
1479
- * enableDevTools={process.env.NODE_ENV === 'development'}
1480
- * >
1481
- * <MyComponents />
1482
- * </MnemonicProvider>
1483
- * );
1484
- * }
1485
- *
1486
- * // Then in browser console:
1487
- * const dt = window.__REACT_MNEMONIC_DEVTOOLS__.resolve('myApp')
1488
- * dt?.dump()
1489
- * dt?.get('user')
1490
- * dt?.set('theme', 'dark')
1491
- * ```
1492
- *
1493
- * @example
1494
- * ```tsx
1495
- * // Multiple providers with different namespaces
1496
- * function App() {
1497
- * return (
1498
- * <MnemonicProvider namespace="user-prefs">
1499
- * <UserSettings />
1500
- * <MnemonicProvider namespace="app-state">
1501
- * <Dashboard />
1502
- * </MnemonicProvider>
1503
- * </MnemonicProvider>
1504
- * );
1505
- * }
1506
- * ```
1507
- *
1508
- * @remarks
1509
- * - Creates a stable store instance that only recreates when namespace, storage, or enableDevTools change
1510
- * - All storage operations are cached in memory for fast reads
1511
- * - Storage failures are handled gracefully (logged but not thrown)
1512
- * - In SSR environments, the provider works but no storage persistence occurs
1513
- * - The store implements React's useSyncExternalStore contract for efficient updates
1514
- *
1515
- * @see {@link useMnemonicKey} - Hook for using persistent state
1516
- * @see {@link MnemonicProviderOptions} - Configuration options
1517
- */
1518
- declare function MnemonicProvider({ children, namespace, storage, enableDevTools, schemaMode, schemaRegistry, }: MnemonicProviderProps): react_jsx_runtime.JSX.Element;
1519
-
1520
- /**
1521
- * React hook for persistent, type-safe state management.
1522
- *
1523
- * Creates a stateful value that persists to storage and synchronizes across
1524
- * components. Works like `useState` but with persistent storage, automatic
1525
- * encoding/decoding, JSON Schema validation, and optional cross-tab synchronization.
1526
- *
1527
- * Must be used within a `MnemonicProvider`. Uses React's `useSyncExternalStore`
1528
- * internally for efficient, tearing-free state synchronization.
1529
- *
1530
- * @template T - The TypeScript type of the stored value
1531
- *
1532
- * @param key - The storage key (unprefixed, namespace is applied automatically)
1533
- * @param options - Configuration options controlling persistence, encoding, and behavior
1534
- *
1535
- * @returns Object with the current value and methods to update it
1536
- *
1537
- * @throws {Error} If used outside of a MnemonicProvider
1538
- */
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
- };
1549
-
1550
- /**
1551
- * Hook for namespace-scoped recovery actions such as hard reset and selective clear.
1552
- *
1553
- * Applications can use this to offer self-service recovery UX for corrupt or
1554
- * legacy persisted state. The hook operates on the current provider namespace.
1555
- *
1556
- * @param options - Optional recovery callback for telemetry/auditing
1557
- * @returns Namespace recovery helpers
1558
- *
1559
- * @throws {Error} If used outside of a MnemonicProvider
1560
- */
1561
- declare function useMnemonicRecovery(options?: UseMnemonicRecoveryOptions): MnemonicRecoveryHook;
1562
-
1563
- /**
1564
- * Create an immutable schema registry for common default/strict-mode setups.
1565
- *
1566
- * The helper indexes schemas and migrations up front, validates duplicate and
1567
- * ambiguous definitions, and returns a {@link SchemaRegistry} ready to pass to
1568
- * `MnemonicProvider`.
1569
- *
1570
- * @param options - Initial schema and migration definitions
1571
- * @returns An indexed immutable schema registry
1572
- *
1573
- * @throws {SchemaError} With `SCHEMA_REGISTRATION_CONFLICT` for duplicate
1574
- * schemas, or `MIGRATION_GRAPH_INVALID` for invalid migration graphs
1575
- */
1576
- declare function createSchemaRegistry(options?: CreateSchemaRegistryOptions): SchemaRegistry;
1577
-
1578
- /**
1579
- * Adapter functions for structural migration helpers.
1580
- *
1581
- * These helpers assume tree-like data, but not a specific node shape. Supply a
1582
- * `StructuralTreeHelpers<T>` when your nodes use custom field names. When your
1583
- * nodes already look like `{ id, children }`, the exported helpers work without
1584
- * any adapter configuration.
1585
- *
1586
- * @template T - Tree node type
1587
- */
1588
- interface StructuralTreeHelpers<T> {
1589
- /**
1590
- * Returns the stable identifier for a node.
1591
- */
1592
- getId: (node: T) => string;
1593
- /**
1594
- * Returns the node's child list, or `undefined` when it has no children.
1595
- */
1596
- getChildren: (node: T) => readonly T[] | undefined;
1597
- /**
1598
- * Returns a copy of the node with a new child list.
1599
- */
1600
- withChildren: (node: T, children: T[]) => T;
1601
- /**
1602
- * Returns a copy of the node with a new identifier.
1603
- */
1604
- withId: (node: T, id: string) => T;
1605
- }
1606
- /**
1607
- * Default tree node shape supported by the structural migration helpers.
1608
- *
1609
- * If your nodes already use `id` and `children`, you can call the helpers
1610
- * directly without supplying `StructuralTreeHelpers`.
1611
- *
1612
- * @template T - Tree node type
1613
- */
1614
- interface StructuralNode<T> {
1615
- /**
1616
- * Stable node identifier used for lookup and rename operations.
1617
- */
1618
- id: string;
1619
- /**
1620
- * Optional child nodes.
1621
- */
1622
- children?: readonly T[];
1623
- }
1624
- /**
1625
- * Finds the first node with the requested id using depth-first traversal.
1626
- *
1627
- * @template T - Tree node type (must extend `{ id: string; children?: readonly T[] }` when `helpers` is omitted)
1628
- * @param root - Root node to search
1629
- * @param id - Target node id
1630
- * @returns The matching node, or `undefined`
1631
- */
1632
- declare function findNodeById<T extends StructuralNode<T>>(root: T, id: string): T | undefined;
1633
- /**
1634
- * Finds the first node with the requested id using depth-first traversal.
1635
- *
1636
- * @template T - Tree node type
1637
- * @param root - Root node to search
1638
- * @param id - Target node id
1639
- * @param helpers - Adapter for custom node shapes
1640
- * @returns The matching node, or `undefined`
1641
- */
1642
- declare function findNodeById<T>(root: T, id: string, helpers: StructuralTreeHelpers<T>): T | undefined;
1643
- /**
1644
- * Inserts a child under the target parent when none of that parent's existing
1645
- * direct children share the same id. Returns the original tree when the parent
1646
- * is missing or the child is already present as a direct child.
1647
- *
1648
- * @template T - Tree node type (must extend `{ id: string; children?: readonly T[] }` when `helpers` is omitted)
1649
- * @param root - Root node to update
1650
- * @param parentId - Parent node that should receive the child
1651
- * @param child - Child node to append
1652
- * @returns Updated tree with the child inserted once
1653
- */
1654
- declare function insertChildIfMissing<T extends StructuralNode<T>>(root: T, parentId: string, child: T): T;
1655
- /**
1656
- * Inserts a child under the target parent when no existing child shares the
1657
- * same id. Returns the original tree when the parent is missing or the child is
1658
- * already present.
1659
- *
1660
- * @template T - Tree node type
1661
- * @param root - Root node to update
1662
- * @param parentId - Parent node that should receive the child
1663
- * @param child - Child node to append
1664
- * @param helpers - Adapter for custom node shapes
1665
- * @returns Updated tree with the child inserted once
1666
- */
1667
- declare function insertChildIfMissing<T>(root: T, parentId: string, child: T, helpers: StructuralTreeHelpers<T>): T;
1668
- /**
1669
- * Renames every node with the source id while preserving tree structure.
1670
- * Returns the original tree when the source id is missing or the target id
1671
- * already exists elsewhere.
1672
- *
1673
- * @template T - Tree node type (must extend `{ id: string; children?: readonly T[] }` when `helpers` is omitted)
1674
- * @param root - Root node to update
1675
- * @param currentId - Existing id to rename
1676
- * @param nextId - Replacement id
1677
- * @returns Updated tree with matching node ids renamed
1678
- */
1679
- declare function renameNode<T extends StructuralNode<T>>(root: T, currentId: string, nextId: string): T;
1680
- /**
1681
- * Renames every node with the source id while preserving tree structure.
1682
- * Returns the original tree when the source id is missing or the target id
1683
- * already exists elsewhere.
1684
- *
1685
- * @template T - Tree node type
1686
- * @param root - Root node to update
1687
- * @param currentId - Existing id to rename
1688
- * @param nextId - Replacement id
1689
- * @param helpers - Adapter for custom node shapes
1690
- * @returns Updated tree with matching node ids renamed
1691
- */
1692
- declare function renameNode<T>(root: T, currentId: string, nextId: string, helpers: StructuralTreeHelpers<T>): T;
1693
- /**
1694
- * Deduplicates each node's immediate children while preserving the first child
1695
- * encountered for each key. The helper traverses the full tree and returns the
1696
- * original root when no duplicates are removed.
1697
- *
1698
- * @template T - Tree node type (must extend `{ id: string; children?: readonly T[] }` when `helpers` is omitted)
1699
- * @template K - Deduplication key type
1700
- * @param root - Root node to normalize
1701
- * @param getKey - Function that computes a dedupe key for each child
1702
- * @returns Updated tree with duplicate siblings removed
1703
- */
1704
- declare function dedupeChildrenBy<T extends StructuralNode<T>, K>(root: T, getKey: (node: T) => K): T;
1705
- /**
1706
- * Deduplicates each node's immediate children while preserving the first child
1707
- * encountered for each key. The helper traverses the full tree and returns the
1708
- * original root when no duplicates are removed.
1709
- *
1710
- * @template T - Tree node type
1711
- * @template K - Deduplication key type
1712
- * @param root - Root node to normalize
1713
- * @param getKey - Function that computes a dedupe key for each child
1714
- * @param helpers - Adapter for custom node shapes
1715
- * @returns Updated tree with duplicate siblings removed
1716
- */
1717
- declare function dedupeChildrenBy<T, K>(root: T, getKey: (node: T) => K, helpers: StructuralTreeHelpers<T>): T;
1718
-
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 };
1
+ export { MnemonicProvider, MnemonicProviderProps, StructuralNode, StructuralTreeHelpers, createSchemaRegistry, dedupeChildrenBy, defineKeySchema, defineMigration, defineWriteMigration, findNodeById, insertChildIfMissing, renameNode, useMnemonicKey } from './schema.cjs';
2
+ export { C as Codec, c as CodecError, D as CompiledValidator, y as CreateSchemaRegistryOptions, I as InferJsonSchemaValue, J as JSONCodec, A as JsonSchema, E as JsonSchemaType, F as JsonSchemaValidationError, K as KeySchema, L as Listener, G as MigrationPath, B as MigrationRule, d as Mnemonic, e as MnemonicDevToolsCapabilities, f as MnemonicDevToolsMeta, g as MnemonicDevToolsProviderApi, h as MnemonicDevToolsProviderDescriptor, i as MnemonicDevToolsProviderEntry, j as MnemonicDevToolsRegistry, k as MnemonicDevToolsWeakRef, l as MnemonicHydrationMode, a as MnemonicKeyDescriptor, m as MnemonicKeySSRConfig, b as MnemonicKeyState, M as MnemonicProviderOptions, n as MnemonicProviderSSRConfig, o as MnemonicRecoveryAction, p as MnemonicRecoveryEvent, q as MnemonicRecoveryHook, R as ReconcileContext, H as SchemaBoundKeyOptions, S as SchemaError, r as SchemaMode, z as SchemaRegistry, s as StorageLike, T as TypedJsonSchema, N as TypedKeySchema, t as Unsubscribe, U as UseMnemonicKeyOptions, u as UseMnemonicRecoveryOptions, O as compileSchema, v as createCodec, w as defineMnemonicKey, P as mnemonicSchema, x as useMnemonicRecovery, Q as validateJsonSchema } from './key-BvFvcKiR.cjs';
3
+ import 'react/jsx-runtime';
4
+ import 'react';