react-mnemonic 1.1.0-beta0 → 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.
@@ -0,0 +1,2276 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/Mnemonic/provider.tsx
7
+
8
+ // src/Mnemonic/runtime.ts
9
+ function getGlobalProcess() {
10
+ return globalThis.process;
11
+ }
12
+ function getRuntimeNodeEnv() {
13
+ const runtimeProcess = getGlobalProcess();
14
+ if (runtimeProcess?.env?.NODE_ENV !== void 0) {
15
+ return runtimeProcess.env.NODE_ENV;
16
+ }
17
+ return void 0;
18
+ }
19
+ function getNativeBrowserStorages() {
20
+ const globalWindow = globalThis.window;
21
+ if (!globalWindow) return [];
22
+ const storages = [];
23
+ const addStorage = (getter) => {
24
+ try {
25
+ storages.push(getter());
26
+ } catch {
27
+ }
28
+ };
29
+ addStorage(() => globalWindow.localStorage);
30
+ addStorage(() => globalWindow.sessionStorage);
31
+ return storages;
32
+ }
33
+ var MnemonicContext = react.createContext(null);
34
+ function useMnemonic() {
35
+ const context = react.useContext(MnemonicContext);
36
+ if (!context) {
37
+ throw new Error("useMnemonic must be used within a MnemonicProvider");
38
+ }
39
+ return context;
40
+ }
41
+ function defaultBrowserStorage() {
42
+ const globalWindow = globalThis.window;
43
+ if (globalWindow === void 0) return void 0;
44
+ try {
45
+ return globalWindow.localStorage;
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
50
+ function detectEnumerableStorage(storage) {
51
+ if (!storage) return false;
52
+ try {
53
+ return typeof storage.length === "number" && typeof storage.key === "function";
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+ function isProductionRuntime() {
59
+ const env = getRuntimeNodeEnv();
60
+ if (env === void 0) {
61
+ return true;
62
+ }
63
+ return env === "production";
64
+ }
65
+ function weakRefConstructor() {
66
+ const ctor = globalThis.WeakRef;
67
+ return typeof ctor === "function" ? ctor : null;
68
+ }
69
+ function hasFinalizationRegistry() {
70
+ return typeof globalThis.FinalizationRegistry === "function";
71
+ }
72
+ function isPromiseLike(value) {
73
+ if (value == null) return false;
74
+ if (typeof value !== "object" && typeof value !== "function") return false;
75
+ return typeof value.then === "function";
76
+ }
77
+ function getCrossTabSyncMode(requestedStorage, activeStorage) {
78
+ const isExplicitNativeBrowserStorage = activeStorage !== void 0 && requestedStorage !== void 0 && getNativeBrowserStorages().includes(activeStorage);
79
+ if (requestedStorage === void 0 && activeStorage !== void 0 || isExplicitNativeBrowserStorage) {
80
+ return "browser-storage-event";
81
+ }
82
+ if (typeof activeStorage?.onExternalChange === "function") {
83
+ return "custom-external-change";
84
+ }
85
+ return "none";
86
+ }
87
+ function getDevToolsWindow() {
88
+ return globalThis.window;
89
+ }
90
+ function sanitizeDevToolsRoot(root) {
91
+ const reserved = /* @__PURE__ */ new Set(["providers", "resolve", "list", "capabilities", "__meta"]);
92
+ for (const key of Object.keys(root)) {
93
+ if (reserved.has(key)) continue;
94
+ const descriptor = Object.getOwnPropertyDescriptor(root, key);
95
+ if (descriptor && !descriptor.configurable) continue;
96
+ try {
97
+ delete root[key];
98
+ } catch {
99
+ }
100
+ }
101
+ }
102
+ function ensureDevToolsRoot(enableDevTools) {
103
+ if (!enableDevTools) return null;
104
+ const globalWindow = getDevToolsWindow();
105
+ if (!globalWindow) return null;
106
+ const weakRefSupported = weakRefConstructor() !== null;
107
+ const finalizationRegistrySupported = hasFinalizationRegistry();
108
+ const existing = globalWindow.__REACT_MNEMONIC_DEVTOOLS__;
109
+ const root = existing && typeof existing === "object" ? existing : {};
110
+ sanitizeDevToolsRoot(root);
111
+ if (!root.providers || typeof root.providers !== "object") {
112
+ root.providers = {};
113
+ }
114
+ if (!root.capabilities || typeof root.capabilities !== "object") {
115
+ root.capabilities = {};
116
+ }
117
+ const capabilities = root.capabilities;
118
+ capabilities.weakRef = weakRefSupported;
119
+ capabilities.finalizationRegistry = finalizationRegistrySupported;
120
+ if (!root.__meta || typeof root.__meta !== "object") {
121
+ root.__meta = {
122
+ version: 0,
123
+ lastUpdated: Date.now(),
124
+ lastChange: ""
125
+ };
126
+ }
127
+ const meta = root.__meta;
128
+ if (typeof meta.version !== "number" || !Number.isFinite(meta.version)) {
129
+ meta.version = 0;
130
+ }
131
+ if (typeof meta.lastUpdated !== "number" || !Number.isFinite(meta.lastUpdated)) {
132
+ meta.lastUpdated = Date.now();
133
+ }
134
+ if (typeof meta.lastChange !== "string") {
135
+ meta.lastChange = "";
136
+ }
137
+ const providers = root.providers;
138
+ if (typeof root.resolve !== "function") {
139
+ root.resolve = (namespace) => {
140
+ const entry = providers[namespace];
141
+ if (!entry || typeof entry.weakRef?.deref !== "function") return null;
142
+ const live = entry.weakRef.deref();
143
+ if (live) {
144
+ entry.lastSeenAt = Date.now();
145
+ entry.staleSince = null;
146
+ return live;
147
+ }
148
+ entry.staleSince ?? (entry.staleSince = Date.now());
149
+ return null;
150
+ };
151
+ }
152
+ if (typeof root.list !== "function") {
153
+ root.list = () => Object.entries(providers).map(([namespace, entry]) => {
154
+ const live = typeof entry.weakRef?.deref === "function" ? entry.weakRef.deref() : void 0;
155
+ const available = Boolean(live);
156
+ if (available) {
157
+ entry.lastSeenAt = Date.now();
158
+ entry.staleSince = null;
159
+ } else {
160
+ entry.staleSince ?? (entry.staleSince = Date.now());
161
+ }
162
+ return {
163
+ namespace,
164
+ available,
165
+ registeredAt: entry.registeredAt,
166
+ lastSeenAt: entry.lastSeenAt,
167
+ staleSince: entry.staleSince
168
+ };
169
+ }).sort((left, right) => left.namespace.localeCompare(right.namespace));
170
+ }
171
+ globalWindow.__REACT_MNEMONIC_DEVTOOLS__ = root;
172
+ return root;
173
+ }
174
+ function bumpDevToolsVersion(root, namespace, reason) {
175
+ if (!root) return;
176
+ root.__meta.version += 1;
177
+ root.__meta.lastUpdated = Date.now();
178
+ root.__meta.lastChange = `${namespace}.${reason}`;
179
+ }
180
+ function decodeDevToolsValue(raw) {
181
+ try {
182
+ return JSON.parse(raw);
183
+ } catch {
184
+ return raw;
185
+ }
186
+ }
187
+ function readStorageRaw(storage, storageKey, callbacks) {
188
+ if (!storage) return null;
189
+ try {
190
+ const raw = storage.getItem(storageKey);
191
+ if (isPromiseLike(raw)) {
192
+ callbacks.onAsyncViolation("getItem", raw);
193
+ return null;
194
+ }
195
+ callbacks.onAccessSuccess();
196
+ return raw;
197
+ } catch (error) {
198
+ callbacks.onAccessError(error);
199
+ return null;
200
+ }
201
+ }
202
+ function enumerateNamespaceKeys(storage, prefix, callbacks) {
203
+ if (!storage) {
204
+ return [];
205
+ }
206
+ const keys = [];
207
+ try {
208
+ const storageLength = storage.length;
209
+ const getStorageKey = storage.key;
210
+ if (typeof storageLength !== "number" || typeof getStorageKey !== "function") {
211
+ return [];
212
+ }
213
+ for (let index = 0; index < storageLength; index++) {
214
+ const fullKey = getStorageKey.call(storage, index);
215
+ if (!fullKey?.startsWith(prefix)) continue;
216
+ keys.push(fullKey.slice(prefix.length));
217
+ }
218
+ callbacks.onAccessSuccess();
219
+ } catch (error) {
220
+ callbacks.onAccessError(error);
221
+ }
222
+ return keys;
223
+ }
224
+ function syncCacheEntryFromStorage({
225
+ key,
226
+ storageKey,
227
+ storage,
228
+ cache,
229
+ emit,
230
+ callbacks
231
+ }) {
232
+ const fresh = readStorageRaw(storage, storageKey, callbacks);
233
+ const cached = cache.get(key) ?? null;
234
+ if (fresh === cached) {
235
+ return false;
236
+ }
237
+ cache.set(key, fresh);
238
+ emit(key);
239
+ return true;
240
+ }
241
+ function reloadNamedKeysFromStorage({
242
+ changedKeys,
243
+ prefix,
244
+ storage,
245
+ listeners,
246
+ cache,
247
+ emit,
248
+ callbacks
249
+ }) {
250
+ let changed = false;
251
+ for (const fullStorageKey of changedKeys) {
252
+ if (!fullStorageKey.startsWith(prefix)) continue;
253
+ const key = fullStorageKey.slice(prefix.length);
254
+ const listenerSet = listeners.get(key);
255
+ if (listenerSet && listenerSet.size > 0) {
256
+ changed = syncCacheEntryFromStorage({
257
+ key,
258
+ storageKey: fullStorageKey,
259
+ storage,
260
+ cache,
261
+ emit,
262
+ callbacks
263
+ }) || changed;
264
+ continue;
265
+ }
266
+ if (cache.has(key)) {
267
+ cache.delete(key);
268
+ }
269
+ }
270
+ return changed;
271
+ }
272
+ function reloadSubscribedKeysFromStorage({
273
+ prefix,
274
+ storage,
275
+ listeners,
276
+ cache,
277
+ emit,
278
+ callbacks
279
+ }) {
280
+ let changed = false;
281
+ for (const [key, listenerSet] of listeners) {
282
+ if (listenerSet.size === 0) continue;
283
+ changed = syncCacheEntryFromStorage({
284
+ key,
285
+ storageKey: `${prefix}${key}`,
286
+ storage,
287
+ cache,
288
+ emit,
289
+ callbacks
290
+ }) || changed;
291
+ }
292
+ for (const key of cache.keys()) {
293
+ const listenerSet = listeners.get(key);
294
+ if (listenerSet && listenerSet.size > 0) continue;
295
+ cache.delete(key);
296
+ }
297
+ return changed;
298
+ }
299
+ function createDevToolsProviderApi({
300
+ store,
301
+ dump,
302
+ keys,
303
+ readThrough,
304
+ writeRaw,
305
+ removeRaw
306
+ }) {
307
+ return {
308
+ getStore: () => store,
309
+ dump: () => {
310
+ const data = dump();
311
+ console.table(
312
+ Object.entries(data).map(([key, value]) => ({
313
+ key,
314
+ value,
315
+ decoded: decodeDevToolsValue(value)
316
+ }))
317
+ );
318
+ return data;
319
+ },
320
+ get: (key) => {
321
+ const raw = readThrough(key);
322
+ if (raw == null) return void 0;
323
+ return decodeDevToolsValue(raw);
324
+ },
325
+ set: (key, value) => {
326
+ writeRaw(key, JSON.stringify(value));
327
+ },
328
+ remove: (key) => removeRaw(key),
329
+ clear: () => {
330
+ for (const key of keys()) {
331
+ removeRaw(key);
332
+ }
333
+ },
334
+ keys
335
+ };
336
+ }
337
+ function createReloadFromStorage({
338
+ storage,
339
+ hasAsyncContractViolation,
340
+ prefix,
341
+ listeners,
342
+ cache,
343
+ emit,
344
+ callbacks,
345
+ devToolsRoot,
346
+ namespace
347
+ }) {
348
+ return (changedKeys) => {
349
+ if (!storage || hasAsyncContractViolation()) return;
350
+ if (changedKeys?.length === 0) return;
351
+ const isFullReload = changedKeys === void 0;
352
+ const changed = isFullReload ? reloadSubscribedKeysFromStorage({
353
+ prefix,
354
+ storage,
355
+ listeners,
356
+ cache,
357
+ emit,
358
+ callbacks
359
+ }) : reloadNamedKeysFromStorage({
360
+ changedKeys,
361
+ prefix,
362
+ storage,
363
+ listeners,
364
+ cache,
365
+ emit,
366
+ callbacks
367
+ });
368
+ if (changed) {
369
+ bumpDevToolsVersion(devToolsRoot, namespace, isFullReload ? "reload:full" : "reload:granular");
370
+ }
371
+ };
372
+ }
373
+ function registerDevToolsProvider({
374
+ devToolsRoot,
375
+ namespace,
376
+ store,
377
+ dump,
378
+ keys,
379
+ readThrough,
380
+ writeRaw,
381
+ removeRaw
382
+ }) {
383
+ let infoMessage = `[Mnemonic DevTools] Namespace "${namespace}" available via window.__REACT_MNEMONIC_DEVTOOLS__.resolve("${namespace}")`;
384
+ if (!devToolsRoot.capabilities.weakRef) {
385
+ console.info(
386
+ `[Mnemonic DevTools] WeakRef is not available; registry provider "${namespace}" was not registered.`
387
+ );
388
+ return;
389
+ }
390
+ const existingLive = devToolsRoot.resolve(namespace);
391
+ if (existingLive) {
392
+ const duplicateMessage = `[Mnemonic DevTools] Duplicate provider namespace "${namespace}" detected. Each window must have at most one live MnemonicProvider per namespace.`;
393
+ if (!isProductionRuntime()) {
394
+ throw new Error(duplicateMessage);
395
+ }
396
+ console.warn(`${duplicateMessage} Keeping the first provider and ignoring the duplicate.`);
397
+ console.info(
398
+ `[Mnemonic DevTools] Namespace "${namespace}" already registered. Keeping existing provider reference.`
399
+ );
400
+ return;
401
+ }
402
+ const providerApi = createDevToolsProviderApi({
403
+ store,
404
+ dump,
405
+ keys,
406
+ readThrough,
407
+ writeRaw,
408
+ removeRaw
409
+ });
410
+ const WeakRefCtor = weakRefConstructor();
411
+ if (!WeakRefCtor) {
412
+ console.info(`[Mnemonic DevTools] WeakRef became unavailable while registering "${namespace}".`);
413
+ return;
414
+ }
415
+ store.__devToolsProviderApiHold = providerApi;
416
+ const now = Date.now();
417
+ devToolsRoot.providers[namespace] = {
418
+ namespace,
419
+ weakRef: new WeakRefCtor(providerApi),
420
+ registeredAt: now,
421
+ lastSeenAt: now,
422
+ staleSince: null
423
+ };
424
+ bumpDevToolsVersion(devToolsRoot, namespace, "registry:namespace-registered");
425
+ console.info(infoMessage);
426
+ }
427
+ function MnemonicProvider({
428
+ children,
429
+ namespace,
430
+ storage,
431
+ enableDevTools = false,
432
+ schemaMode = "default",
433
+ schemaRegistry,
434
+ ssr
435
+ }) {
436
+ if (schemaMode === "strict" && !schemaRegistry) {
437
+ throw new Error("MnemonicProvider strict mode requires schemaRegistry");
438
+ }
439
+ if (schemaMode === "autoschema" && typeof schemaRegistry?.registerSchema !== "function") {
440
+ throw new Error("MnemonicProvider autoschema mode requires schemaRegistry.registerSchema");
441
+ }
442
+ const store = react.useMemo(() => {
443
+ const prefix = `${namespace}.`;
444
+ const st = storage ?? defaultBrowserStorage();
445
+ const ssrHydration = ssr?.hydration ?? "immediate";
446
+ const devToolsRoot = ensureDevToolsRoot(enableDevTools);
447
+ const canEnumerateKeys = detectEnumerableStorage(st);
448
+ const crossTabSyncMode = getCrossTabSyncMode(storage, st);
449
+ const cache = /* @__PURE__ */ new Map();
450
+ const listeners = /* @__PURE__ */ new Map();
451
+ let quotaErrorLogged = false;
452
+ let accessErrorLogged = false;
453
+ let asyncContractViolationDetected = false;
454
+ const storageAccessCallbacks = {
455
+ onAccessError: (err) => logAccessError(err),
456
+ onAccessSuccess: () => {
457
+ accessErrorLogged = false;
458
+ },
459
+ onAsyncViolation: (method, thenable) => handleAsyncStorageContractViolation(method, thenable)
460
+ };
461
+ const fullKey = (key) => prefix + key;
462
+ const emit = (key) => {
463
+ const set = listeners.get(key);
464
+ if (!set) return;
465
+ for (const fn of set) fn();
466
+ };
467
+ const logAccessError = (err) => {
468
+ if (!accessErrorLogged && err instanceof DOMException && err.name !== "QuotaExceededError") {
469
+ console.error(
470
+ `[Mnemonic] Storage access error (${err.name}): ${err.message}. Data is cached in memory but may not persist.`
471
+ );
472
+ accessErrorLogged = true;
473
+ }
474
+ };
475
+ const handleAsyncStorageContractViolation = (method, thenable) => {
476
+ asyncContractViolationDetected = true;
477
+ void Promise.resolve(thenable).catch(() => void 0);
478
+ if (accessErrorLogged) return;
479
+ console.error(
480
+ `[Mnemonic] StorageLike.${method} returned a Promise. StorageLike must remain synchronous for react-mnemonic v1. Wrap async persistence behind a synchronous cache facade instead.`
481
+ );
482
+ accessErrorLogged = true;
483
+ };
484
+ const readThrough = (key) => {
485
+ if (cache.has(key)) return cache.get(key) ?? null;
486
+ if (!st || asyncContractViolationDetected) {
487
+ cache.set(key, null);
488
+ return null;
489
+ }
490
+ const raw = readStorageRaw(st, fullKey(key), storageAccessCallbacks);
491
+ cache.set(key, raw);
492
+ return raw;
493
+ };
494
+ const writeRaw = (key, raw) => {
495
+ cache.set(key, raw);
496
+ if (st && !asyncContractViolationDetected) {
497
+ try {
498
+ const result = st.setItem(fullKey(key), raw);
499
+ if (isPromiseLike(result)) {
500
+ handleAsyncStorageContractViolation("setItem", result);
501
+ } else {
502
+ quotaErrorLogged = false;
503
+ accessErrorLogged = false;
504
+ }
505
+ } catch (err) {
506
+ if (!quotaErrorLogged && err instanceof DOMException && err.name === "QuotaExceededError") {
507
+ console.error(
508
+ `[Mnemonic] Storage quota exceeded writing key "${key}". Data is cached in memory but will not persist.`
509
+ );
510
+ quotaErrorLogged = true;
511
+ }
512
+ logAccessError(err);
513
+ }
514
+ }
515
+ emit(key);
516
+ bumpDevToolsVersion(devToolsRoot, namespace, `set:${key}`);
517
+ };
518
+ const removeRaw = (key) => {
519
+ cache.set(key, null);
520
+ if (st && !asyncContractViolationDetected) {
521
+ try {
522
+ const result = st.removeItem(fullKey(key));
523
+ if (isPromiseLike(result)) {
524
+ handleAsyncStorageContractViolation("removeItem", result);
525
+ } else {
526
+ accessErrorLogged = false;
527
+ }
528
+ } catch (err) {
529
+ logAccessError(err);
530
+ }
531
+ }
532
+ emit(key);
533
+ bumpDevToolsVersion(devToolsRoot, namespace, `remove:${key}`);
534
+ };
535
+ const subscribeRaw = (key, listener) => {
536
+ let set = listeners.get(key);
537
+ if (!set) {
538
+ set = /* @__PURE__ */ new Set();
539
+ listeners.set(key, set);
540
+ }
541
+ set.add(listener);
542
+ readThrough(key);
543
+ return () => {
544
+ const s = listeners.get(key);
545
+ if (!s) return;
546
+ s.delete(listener);
547
+ if (s.size === 0) listeners.delete(key);
548
+ };
549
+ };
550
+ const getRawSnapshot = (key) => readThrough(key);
551
+ const keys = () => {
552
+ if (asyncContractViolationDetected) {
553
+ return Array.from(cache.entries()).filter(([, value]) => value != null).map(([key]) => key);
554
+ }
555
+ if (!canEnumerateKeys) return [];
556
+ return enumerateNamespaceKeys(st, prefix, storageAccessCallbacks);
557
+ };
558
+ const dump = () => {
559
+ const out = {};
560
+ for (const k of keys()) {
561
+ const raw = readThrough(k);
562
+ if (raw != null) out[k] = raw;
563
+ }
564
+ return out;
565
+ };
566
+ const reloadFromStorage = createReloadFromStorage({
567
+ storage: st,
568
+ hasAsyncContractViolation: () => asyncContractViolationDetected,
569
+ prefix,
570
+ listeners,
571
+ cache,
572
+ emit,
573
+ callbacks: storageAccessCallbacks,
574
+ devToolsRoot,
575
+ namespace
576
+ });
577
+ const store2 = {
578
+ prefix,
579
+ canEnumerateKeys,
580
+ subscribeRaw,
581
+ getRawSnapshot,
582
+ setRaw: writeRaw,
583
+ removeRaw,
584
+ keys,
585
+ dump,
586
+ reloadFromStorage,
587
+ schemaMode,
588
+ ssrHydration,
589
+ crossTabSyncMode,
590
+ ...schemaRegistry ? { schemaRegistry } : {}
591
+ };
592
+ if (devToolsRoot) {
593
+ registerDevToolsProvider({
594
+ devToolsRoot,
595
+ namespace,
596
+ store: store2,
597
+ dump,
598
+ keys,
599
+ readThrough,
600
+ writeRaw,
601
+ removeRaw
602
+ });
603
+ }
604
+ return store2;
605
+ }, [namespace, storage, enableDevTools, schemaMode, schemaRegistry, ssr?.hydration]);
606
+ react.useEffect(() => {
607
+ if (!storage?.onExternalChange) return;
608
+ return storage.onExternalChange((changedKeys) => store.reloadFromStorage(changedKeys));
609
+ }, [storage, store]);
610
+ return /* @__PURE__ */ jsxRuntime.jsx(MnemonicContext.Provider, { value: store, children });
611
+ }
612
+
613
+ // src/Mnemonic/codecs.ts
614
+ var CodecError = class extends Error {
615
+ /**
616
+ * Creates a new CodecError.
617
+ *
618
+ * @param message - Human-readable error description
619
+ * @param cause - Optional underlying error that caused this failure
620
+ */
621
+ constructor(message, cause) {
622
+ super(message);
623
+ this.name = "CodecError";
624
+ this.cause = cause;
625
+ Object.setPrototypeOf(this, new.target.prototype);
626
+ }
627
+ };
628
+ var JSONCodec = {
629
+ encode: (value) => JSON.stringify(value),
630
+ decode: (encoded) => JSON.parse(encoded)
631
+ };
632
+ function createCodec(encode, decode) {
633
+ return { encode, decode };
634
+ }
635
+
636
+ // src/Mnemonic/schema.ts
637
+ var SchemaError = class extends Error {
638
+ /**
639
+ * Creates a new SchemaError.
640
+ *
641
+ * @param code - Machine-readable failure category
642
+ * @param message - Human-readable error description
643
+ * @param cause - Optional underlying error
644
+ */
645
+ constructor(code, message, cause) {
646
+ super(message);
647
+ this.name = "SchemaError";
648
+ this.code = code;
649
+ this.cause = cause;
650
+ Object.setPrototypeOf(this, new.target.prototype);
651
+ }
652
+ };
653
+
654
+ // src/Mnemonic/json-schema.ts
655
+ function matchesType(value, type) {
656
+ switch (type) {
657
+ case "string":
658
+ return typeof value === "string";
659
+ case "number":
660
+ return typeof value === "number" && Number.isFinite(value);
661
+ case "integer":
662
+ return typeof value === "number" && Number.isInteger(value);
663
+ case "boolean":
664
+ return typeof value === "boolean";
665
+ case "null":
666
+ return value === null;
667
+ case "object":
668
+ return typeof value === "object" && value !== null && !Array.isArray(value);
669
+ case "array":
670
+ return Array.isArray(value);
671
+ default:
672
+ return false;
673
+ }
674
+ }
675
+ function jsonDeepEqualArray(a, b) {
676
+ if (a.length !== b.length) return false;
677
+ for (let i = 0; i < a.length; i++) {
678
+ if (!jsonDeepEqual(a[i], b[i])) return false;
679
+ }
680
+ return true;
681
+ }
682
+ function jsonDeepEqualObject(a, b) {
683
+ const aKeys = Object.keys(a);
684
+ const bKeys = Object.keys(b);
685
+ if (aKeys.length !== bKeys.length) return false;
686
+ for (const key of aKeys) {
687
+ if (!objectHasOwn(b, key)) return false;
688
+ if (!jsonDeepEqual(a[key], b[key])) return false;
689
+ }
690
+ return true;
691
+ }
692
+ function jsonDeepEqual(a, b) {
693
+ if (a === b) return true;
694
+ if (a === null || b === null) return false;
695
+ if (typeof a !== typeof b) return false;
696
+ if (Array.isArray(a)) {
697
+ if (!Array.isArray(b)) return false;
698
+ return jsonDeepEqualArray(a, b);
699
+ }
700
+ if (typeof a === "object") {
701
+ if (Array.isArray(b)) return false;
702
+ return jsonDeepEqualObject(a, b);
703
+ }
704
+ return false;
705
+ }
706
+ var compiledCache = /* @__PURE__ */ new WeakMap();
707
+ function compileSchema(schema) {
708
+ const cached = compiledCache.get(schema);
709
+ if (cached) return cached;
710
+ const compiled = buildValidator(schema);
711
+ compiledCache.set(schema, compiled);
712
+ return compiled;
713
+ }
714
+ function isJsonPrimitive(value) {
715
+ return value === null || typeof value !== "object";
716
+ }
717
+ function isJsonObjectRecord(value) {
718
+ return typeof value === "object" && value !== null && !Array.isArray(value);
719
+ }
720
+ function objectHasOwn(value, property) {
721
+ const hasOwn = Object.hasOwn;
722
+ if (typeof hasOwn === "function") {
723
+ return hasOwn(value, property);
724
+ }
725
+ return Object.getOwnPropertyDescriptor(value, property) !== void 0;
726
+ }
727
+ function buildValidator(schema) {
728
+ const typeStep = buildTypeValidationStep(schema);
729
+ const validationSteps = [
730
+ buildEnumValidationStep(schema),
731
+ buildConstValidationStep(schema),
732
+ buildNumberValidationStep(schema),
733
+ buildStringValidationStep(schema),
734
+ buildObjectValidationStep(schema),
735
+ buildArrayValidationStep(schema)
736
+ ].filter((step) => step !== null);
737
+ if (typeStep === null && validationSteps.length === 0) {
738
+ return (_value, _path) => [];
739
+ }
740
+ return (value, path = "") => {
741
+ const errors = [];
742
+ if (typeStep && !typeStep(value, path, errors)) {
743
+ return errors;
744
+ }
745
+ for (const step of validationSteps) {
746
+ step(value, path, errors);
747
+ }
748
+ return errors;
749
+ };
750
+ }
751
+ function buildTypeValidationStep(schema) {
752
+ if (schema.type === void 0) {
753
+ return null;
754
+ }
755
+ const resolvedTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
756
+ const typeLabel = JSON.stringify(schema.type);
757
+ return (value, path, errors) => {
758
+ if (resolvedTypes.some((type) => matchesType(value, type))) {
759
+ return true;
760
+ }
761
+ errors.push({
762
+ path,
763
+ message: `Expected type ${typeLabel}, got ${jsonTypeLabel(value)}`,
764
+ keyword: "type"
765
+ });
766
+ return false;
767
+ };
768
+ }
769
+ function buildEnumValidationStep(schema) {
770
+ if (schema.enum === void 0) {
771
+ return null;
772
+ }
773
+ const enumPrimitiveSet = new Set(schema.enum.filter((member) => isJsonPrimitive(member)));
774
+ const enumComplexMembers = schema.enum.filter((member) => !isJsonPrimitive(member));
775
+ return (value, path, errors) => {
776
+ const primitiveMatch = isJsonPrimitive(value) && enumPrimitiveSet.has(value);
777
+ const complexMatch = !primitiveMatch && enumComplexMembers.some((entry) => jsonDeepEqual(value, entry));
778
+ if (primitiveMatch || complexMatch) {
779
+ return;
780
+ }
781
+ errors.push({
782
+ path,
783
+ message: `Value does not match any enum member`,
784
+ keyword: "enum"
785
+ });
786
+ };
787
+ }
788
+ function buildConstValidationStep(schema) {
789
+ if (!("const" in schema)) {
790
+ return null;
791
+ }
792
+ return (value, path, errors) => {
793
+ if (jsonDeepEqual(value, schema.const)) {
794
+ return;
795
+ }
796
+ errors.push({
797
+ path,
798
+ message: `Value does not match const`,
799
+ keyword: "const"
800
+ });
801
+ };
802
+ }
803
+ function buildNumberValidationStep(schema) {
804
+ const hasMinimum = schema.minimum !== void 0;
805
+ const hasMaximum = schema.maximum !== void 0;
806
+ const hasExMin = schema.exclusiveMinimum !== void 0;
807
+ const hasExMax = schema.exclusiveMaximum !== void 0;
808
+ if (!hasMinimum && !hasMaximum && !hasExMin && !hasExMax) {
809
+ return null;
810
+ }
811
+ const minimum = schema.minimum;
812
+ const maximum = schema.maximum;
813
+ const exMin = schema.exclusiveMinimum;
814
+ const exMax = schema.exclusiveMaximum;
815
+ return (value, path, errors) => {
816
+ if (typeof value !== "number") {
817
+ return;
818
+ }
819
+ if (hasMinimum && value < minimum) {
820
+ errors.push({
821
+ path,
822
+ message: `Value ${value} is less than minimum ${minimum}`,
823
+ keyword: "minimum"
824
+ });
825
+ }
826
+ if (hasMaximum && value > maximum) {
827
+ errors.push({
828
+ path,
829
+ message: `Value ${value} is greater than maximum ${maximum}`,
830
+ keyword: "maximum"
831
+ });
832
+ }
833
+ if (hasExMin && value <= exMin) {
834
+ errors.push({
835
+ path,
836
+ message: `Value ${value} is not greater than exclusiveMinimum ${exMin}`,
837
+ keyword: "exclusiveMinimum"
838
+ });
839
+ }
840
+ if (hasExMax && value >= exMax) {
841
+ errors.push({
842
+ path,
843
+ message: `Value ${value} is not less than exclusiveMaximum ${exMax}`,
844
+ keyword: "exclusiveMaximum"
845
+ });
846
+ }
847
+ };
848
+ }
849
+ function buildStringValidationStep(schema) {
850
+ const hasMinLength = schema.minLength !== void 0;
851
+ const hasMaxLength = schema.maxLength !== void 0;
852
+ if (!hasMinLength && !hasMaxLength) {
853
+ return null;
854
+ }
855
+ const minLength = schema.minLength;
856
+ const maxLength = schema.maxLength;
857
+ return (value, path, errors) => {
858
+ if (typeof value !== "string") {
859
+ return;
860
+ }
861
+ if (hasMinLength && value.length < minLength) {
862
+ errors.push({
863
+ path,
864
+ message: `String length ${value.length} is less than minLength ${minLength}`,
865
+ keyword: "minLength"
866
+ });
867
+ }
868
+ if (hasMaxLength && value.length > maxLength) {
869
+ errors.push({
870
+ path,
871
+ message: `String length ${value.length} is greater than maxLength ${maxLength}`,
872
+ keyword: "maxLength"
873
+ });
874
+ }
875
+ };
876
+ }
877
+ function buildObjectValidationStep(schema) {
878
+ const requiredKeys = schema.required ?? [];
879
+ const propertyValidators = schema.properties ? Object.entries(schema.properties).map(([name, propertySchema]) => [
880
+ name,
881
+ compileSchema(propertySchema)
882
+ ]) : null;
883
+ const checkAdditional = schema.additionalProperties !== void 0 && schema.additionalProperties !== true;
884
+ const additionalIsFalse = schema.additionalProperties === false;
885
+ const additionalValidator = checkAdditional && !additionalIsFalse ? compileSchema(schema.additionalProperties) : null;
886
+ const definedPropKeys = checkAdditional ? new Set(Object.keys(schema.properties ?? {})) : null;
887
+ const objectValidationSteps = [];
888
+ if (requiredKeys.length > 0) {
889
+ objectValidationSteps.push(createRequiredPropertyStep(requiredKeys));
890
+ }
891
+ if (propertyValidators !== null) {
892
+ objectValidationSteps.push(createDeclaredPropertyStep(propertyValidators));
893
+ }
894
+ if (checkAdditional) {
895
+ objectValidationSteps.push(
896
+ createAdditionalPropertyStep({
897
+ additionalIsFalse,
898
+ additionalValidator,
899
+ definedPropKeys: definedPropKeys ?? /* @__PURE__ */ new Set()
900
+ })
901
+ );
902
+ }
903
+ if (objectValidationSteps.length === 0) {
904
+ return null;
905
+ }
906
+ return (value, path, errors) => {
907
+ if (!isJsonObjectRecord(value)) {
908
+ return;
909
+ }
910
+ for (const step of objectValidationSteps) {
911
+ step(value, path, errors);
912
+ }
913
+ };
914
+ }
915
+ function createRequiredPropertyStep(requiredKeys) {
916
+ return (value, path, errors) => {
917
+ for (const requiredKey of requiredKeys) {
918
+ if (objectHasOwn(value, requiredKey)) {
919
+ continue;
920
+ }
921
+ errors.push({
922
+ path,
923
+ message: `Missing required property "${requiredKey}"`,
924
+ keyword: "required"
925
+ });
926
+ }
927
+ };
928
+ }
929
+ function createDeclaredPropertyStep(propertyValidators) {
930
+ return (value, path, errors) => {
931
+ for (const [propertyName, validator] of propertyValidators) {
932
+ if (!objectHasOwn(value, propertyName)) {
933
+ continue;
934
+ }
935
+ errors.push(...validator(value[propertyName], `${path}/${propertyName}`));
936
+ }
937
+ };
938
+ }
939
+ function createAdditionalPropertyStep({
940
+ additionalIsFalse,
941
+ additionalValidator,
942
+ definedPropKeys
943
+ }) {
944
+ return (value, path, errors) => {
945
+ for (const objectKey of Object.keys(value)) {
946
+ if (definedPropKeys.has(objectKey)) {
947
+ continue;
948
+ }
949
+ if (additionalIsFalse) {
950
+ errors.push({
951
+ path,
952
+ message: `Additional property "${objectKey}" is not allowed`,
953
+ keyword: "additionalProperties"
954
+ });
955
+ continue;
956
+ }
957
+ errors.push(...additionalValidator(value[objectKey], `${path}/${objectKey}`));
958
+ }
959
+ };
960
+ }
961
+ function buildArrayValidationStep(schema) {
962
+ const hasMinItems = schema.minItems !== void 0;
963
+ const hasMaxItems = schema.maxItems !== void 0;
964
+ const itemsValidator = schema.items ? compileSchema(schema.items) : null;
965
+ if (!hasMinItems && !hasMaxItems && itemsValidator === null) {
966
+ return null;
967
+ }
968
+ const minItems = schema.minItems;
969
+ const maxItems = schema.maxItems;
970
+ return (value, path, errors) => {
971
+ if (!Array.isArray(value)) {
972
+ return;
973
+ }
974
+ if (hasMinItems && value.length < minItems) {
975
+ errors.push({
976
+ path,
977
+ message: `Array length ${value.length} is less than minItems ${minItems}`,
978
+ keyword: "minItems"
979
+ });
980
+ }
981
+ if (hasMaxItems && value.length > maxItems) {
982
+ errors.push({
983
+ path,
984
+ message: `Array length ${value.length} is greater than maxItems ${maxItems}`,
985
+ keyword: "maxItems"
986
+ });
987
+ }
988
+ if (itemsValidator === null) {
989
+ return;
990
+ }
991
+ for (const [index, item] of value.entries()) {
992
+ errors.push(...itemsValidator(item, `${path}/${index}`));
993
+ }
994
+ };
995
+ }
996
+ function validateJsonSchema(value, schema, path = "") {
997
+ const compiled = compileSchema(schema);
998
+ return compiled(value, path);
999
+ }
1000
+ function jsonTypeLabel(value) {
1001
+ if (value === null) return "null";
1002
+ if (Array.isArray(value)) return "array";
1003
+ return typeof value;
1004
+ }
1005
+ function inferJsonSchema(sample) {
1006
+ if (sample === null) return { type: "null" };
1007
+ if (Array.isArray(sample)) return { type: "array" };
1008
+ switch (typeof sample) {
1009
+ case "string":
1010
+ return { type: "string" };
1011
+ case "number":
1012
+ return { type: "number" };
1013
+ case "boolean":
1014
+ return { type: "boolean" };
1015
+ case "object":
1016
+ return { type: "object" };
1017
+ default:
1018
+ return {};
1019
+ }
1020
+ }
1021
+ var SSR_SNAPSHOT_TOKEN = /* @__PURE__ */ Symbol("mnemonic:ssr-snapshot");
1022
+ var diagnosticContractRegistry = /* @__PURE__ */ new WeakMap();
1023
+ var diagnosticWarningRegistry = /* @__PURE__ */ new WeakMap();
1024
+ var diagnosticObjectIds = /* @__PURE__ */ new WeakMap();
1025
+ var nextDiagnosticObjectId = 1;
1026
+ function serializeEnvelope(version, payload) {
1027
+ return JSON.stringify({
1028
+ version,
1029
+ payload
1030
+ });
1031
+ }
1032
+ function withReadMetadata(value, rewriteRaw, extra) {
1033
+ const result = { value };
1034
+ if (extra !== void 0) {
1035
+ Object.assign(result, extra);
1036
+ }
1037
+ if (rewriteRaw !== void 0) result.rewriteRaw = rewriteRaw;
1038
+ return result;
1039
+ }
1040
+ function isDevelopmentRuntime() {
1041
+ return getRuntimeNodeEnv() === "development";
1042
+ }
1043
+ function getDiagnosticWarnings(api) {
1044
+ let warnings = diagnosticWarningRegistry.get(api);
1045
+ if (!warnings) {
1046
+ warnings = /* @__PURE__ */ new Set();
1047
+ diagnosticWarningRegistry.set(api, warnings);
1048
+ }
1049
+ return warnings;
1050
+ }
1051
+ function warnOnce(api, id, message) {
1052
+ const warnings = getDiagnosticWarnings(api);
1053
+ if (warnings.has(id)) return;
1054
+ warnings.add(id);
1055
+ console.warn(message);
1056
+ }
1057
+ function stableDiagnosticValue(value) {
1058
+ if (typeof value === "function") {
1059
+ const source = Function.prototype.toString.call(value).split(/\s+/).join(" ").trim();
1060
+ const name = value.name || "anonymous";
1061
+ return `[factory:${name}/${value.length}:${source}]`;
1062
+ }
1063
+ if (typeof value === "bigint") return `${value.toString()}n`;
1064
+ if (typeof value === "symbol") return value.toString();
1065
+ if (value === void 0) return "undefined";
1066
+ try {
1067
+ return JSON.stringify(value);
1068
+ } catch {
1069
+ const tag = Object.prototype.toString.call(value);
1070
+ if (value !== null && (typeof value === "object" || typeof value === "function")) {
1071
+ return `${tag}#${getDiagnosticObjectId(value)}`;
1072
+ }
1073
+ return tag;
1074
+ }
1075
+ }
1076
+ function isObjectLike(value) {
1077
+ return value !== null && (typeof value === "object" || typeof value === "function");
1078
+ }
1079
+ function objectHasOwn2(value, property) {
1080
+ const hasOwn = Object.hasOwn;
1081
+ if (typeof hasOwn === "function") {
1082
+ return hasOwn(value, property);
1083
+ }
1084
+ return Object.getOwnPropertyDescriptor(value, property) !== void 0;
1085
+ }
1086
+ function getDiagnosticObjectId(value) {
1087
+ const existing = diagnosticObjectIds.get(value);
1088
+ if (existing !== void 0) return existing;
1089
+ const id = nextDiagnosticObjectId++;
1090
+ diagnosticObjectIds.set(value, id);
1091
+ return id;
1092
+ }
1093
+ function buildContractFingerprint({
1094
+ api,
1095
+ key,
1096
+ defaultValue,
1097
+ codecOpt,
1098
+ schemaVersion,
1099
+ reconcile,
1100
+ listenCrossTab,
1101
+ ssrOptions
1102
+ }) {
1103
+ const codecSignature = codecOpt == null || !isObjectLike(codecOpt) ? "default-json-codec" : `codec:${stableDiagnosticValue(codecOpt.encode)}:${stableDiagnosticValue(codecOpt.decode)}`;
1104
+ const reconcileSignature = reconcile == null || !isObjectLike(reconcile) ? "no-reconcile" : `reconcile:${stableDiagnosticValue(reconcile)}`;
1105
+ return JSON.stringify({
1106
+ key,
1107
+ defaultValue: stableDiagnosticValue(defaultValue),
1108
+ codec: codecSignature,
1109
+ schemaVersion: schemaVersion ?? null,
1110
+ listenCrossTab: Boolean(listenCrossTab),
1111
+ reconcile: reconcileSignature,
1112
+ ssrHydration: ssrOptions?.hydration ?? null,
1113
+ hasServerValue: ssrOptions?.serverValue !== void 0,
1114
+ providerHydration: api.ssrHydration ?? null
1115
+ });
1116
+ }
1117
+ function resolveMnemonicKeyArgs(keyOrDescriptor, options) {
1118
+ if (typeof keyOrDescriptor !== "string") {
1119
+ return keyOrDescriptor;
1120
+ }
1121
+ if (!options) {
1122
+ throw new Error("useMnemonicKey requires options when called with a string key");
1123
+ }
1124
+ return {
1125
+ key: keyOrDescriptor,
1126
+ options
1127
+ };
1128
+ }
1129
+ function useMnemonicKeyShared(keyOrDescriptor, options, schemaVersion) {
1130
+ const descriptor = resolveMnemonicKeyArgs(keyOrDescriptor, options);
1131
+ const key = descriptor.key;
1132
+ const resolvedOptions = descriptor.options;
1133
+ const api = useMnemonic();
1134
+ const {
1135
+ defaultValue,
1136
+ onMount,
1137
+ onChange,
1138
+ listenCrossTab,
1139
+ codec: codecOpt,
1140
+ schema,
1141
+ reconcile,
1142
+ ssr: ssrOptions
1143
+ } = resolvedOptions;
1144
+ const codec = codecOpt ?? JSONCodec;
1145
+ const hydrationMode = ssrOptions?.hydration ?? api.ssrHydration;
1146
+ const [hasMounted, setHasMounted] = react.useState(hydrationMode !== "client-only");
1147
+ const developmentRuntime = isDevelopmentRuntime();
1148
+ const contractFingerprint = react.useMemo(
1149
+ () => developmentRuntime ? buildContractFingerprint({
1150
+ api,
1151
+ key,
1152
+ defaultValue,
1153
+ codecOpt,
1154
+ ...schemaVersion === void 0 ? {} : { schemaVersion },
1155
+ reconcile,
1156
+ listenCrossTab,
1157
+ ssrOptions
1158
+ }) : null,
1159
+ [
1160
+ developmentRuntime,
1161
+ api,
1162
+ key,
1163
+ defaultValue,
1164
+ codecOpt,
1165
+ schemaVersion,
1166
+ reconcile,
1167
+ listenCrossTab,
1168
+ ssrOptions?.hydration,
1169
+ ssrOptions?.serverValue
1170
+ ]
1171
+ );
1172
+ const getFallback = react.useCallback(
1173
+ (error) => typeof defaultValue === "function" ? defaultValue(error) : defaultValue,
1174
+ [defaultValue]
1175
+ );
1176
+ const getServerValue = react.useCallback(() => {
1177
+ const serverValue = ssrOptions?.serverValue;
1178
+ if (serverValue === void 0) {
1179
+ return getFallback();
1180
+ }
1181
+ return typeof serverValue === "function" ? serverValue() : serverValue;
1182
+ }, [getFallback, ssrOptions?.serverValue]);
1183
+ const parseEnvelope = react.useCallback(
1184
+ (rawText) => {
1185
+ try {
1186
+ const parsed = JSON.parse(rawText);
1187
+ if (typeof parsed !== "object" || parsed == null || !Number.isInteger(parsed.version) || parsed.version < 0 || !objectHasOwn2(parsed, "payload")) {
1188
+ return {
1189
+ ok: false,
1190
+ error: new SchemaError("INVALID_ENVELOPE", `Invalid envelope for key "${key}"`)
1191
+ };
1192
+ }
1193
+ return { ok: true, envelope: parsed };
1194
+ } catch (err) {
1195
+ return {
1196
+ ok: false,
1197
+ error: new SchemaError("INVALID_ENVELOPE", `Invalid envelope for key "${key}"`, err)
1198
+ };
1199
+ }
1200
+ },
1201
+ [key]
1202
+ );
1203
+ const decodeStringPayload = react.useCallback(
1204
+ (payload, activeCodec) => {
1205
+ try {
1206
+ return activeCodec.decode(payload);
1207
+ } catch (err) {
1208
+ throw err instanceof CodecError ? err : new CodecError(`Codec decode failed for key "${key}"`, err);
1209
+ }
1210
+ },
1211
+ [key]
1212
+ );
1213
+ const buildFallbackResult = react.useCallback(
1214
+ (error, extra) => {
1215
+ return withReadMetadata(getFallback(error), void 0, extra);
1216
+ },
1217
+ [getFallback]
1218
+ );
1219
+ return {
1220
+ api,
1221
+ key,
1222
+ codec,
1223
+ codecOpt,
1224
+ schema,
1225
+ reconcile,
1226
+ onMount,
1227
+ onChange,
1228
+ listenCrossTab,
1229
+ getFallback,
1230
+ getServerValue,
1231
+ parseEnvelope,
1232
+ decodeStringPayload,
1233
+ buildFallbackResult,
1234
+ developmentRuntime,
1235
+ contractFingerprint,
1236
+ hasMounted,
1237
+ setHasMounted,
1238
+ hydrationMode,
1239
+ ssrOptions
1240
+ };
1241
+ }
1242
+ function useApplyReconcile({
1243
+ key,
1244
+ reconcile,
1245
+ buildFallbackResult
1246
+ }) {
1247
+ return react.useCallback(
1248
+ ({
1249
+ value,
1250
+ rewriteRaw,
1251
+ extra,
1252
+ persistedVersion,
1253
+ latestVersion,
1254
+ serializeForPersist,
1255
+ deriveExtra
1256
+ }) => {
1257
+ if (!reconcile) {
1258
+ return withReadMetadata(value, rewriteRaw, extra);
1259
+ }
1260
+ const context = {
1261
+ key,
1262
+ persistedVersion
1263
+ };
1264
+ if (latestVersion !== void 0) {
1265
+ context.latestVersion = latestVersion;
1266
+ }
1267
+ const baselineSerialized = (() => {
1268
+ try {
1269
+ return serializeForPersist(value);
1270
+ } catch {
1271
+ return rewriteRaw;
1272
+ }
1273
+ })();
1274
+ try {
1275
+ const reconciled = reconcile(value, context);
1276
+ const nextExtra = deriveExtra ? deriveExtra(reconciled, extra) : extra;
1277
+ const nextSerialized = serializeForPersist(reconciled);
1278
+ const nextRewriteRaw = baselineSerialized === void 0 || nextSerialized !== baselineSerialized ? nextSerialized : rewriteRaw;
1279
+ return withReadMetadata(reconciled, nextRewriteRaw, nextExtra);
1280
+ } catch (err) {
1281
+ const typedErr = err instanceof SchemaError ? err : new SchemaError("RECONCILE_FAILED", `Reconciliation failed for key "${key}"`, err);
1282
+ return buildFallbackResult(typedErr, extra);
1283
+ }
1284
+ },
1285
+ [buildFallbackResult, key, reconcile]
1286
+ );
1287
+ }
1288
+ function useMnemonicKeyState(shared, config) {
1289
+ const {
1290
+ api,
1291
+ key,
1292
+ codecOpt,
1293
+ schema,
1294
+ onMount,
1295
+ onChange,
1296
+ listenCrossTab,
1297
+ getFallback,
1298
+ getServerValue,
1299
+ developmentRuntime,
1300
+ contractFingerprint,
1301
+ hasMounted,
1302
+ setHasMounted,
1303
+ hydrationMode,
1304
+ ssrOptions
1305
+ } = shared;
1306
+ const { decodeForRead, encodeForWrite, additionalDevWarnings, onDecodedEffect } = config;
1307
+ const getServerRawSnapshot = react.useCallback(
1308
+ () => ssrOptions?.serverValue === void 0 ? null : SSR_SNAPSHOT_TOKEN,
1309
+ [ssrOptions?.serverValue]
1310
+ );
1311
+ const deferStorageRead = hydrationMode === "client-only" && !hasMounted;
1312
+ const subscribe = react.useCallback(
1313
+ (listener) => {
1314
+ if (deferStorageRead) {
1315
+ return () => void 0;
1316
+ }
1317
+ return api.subscribeRaw(key, listener);
1318
+ },
1319
+ [api, deferStorageRead, key]
1320
+ );
1321
+ const raw = react.useSyncExternalStore(
1322
+ subscribe,
1323
+ () => deferStorageRead ? getServerRawSnapshot() : api.getRawSnapshot(key),
1324
+ getServerRawSnapshot
1325
+ );
1326
+ const decoded = react.useMemo(() => {
1327
+ if (raw === SSR_SNAPSHOT_TOKEN) {
1328
+ return withReadMetadata(getServerValue());
1329
+ }
1330
+ return decodeForRead(raw);
1331
+ }, [decodeForRead, getServerValue, raw]);
1332
+ const value = decoded.value;
1333
+ react.useEffect(() => {
1334
+ if (!developmentRuntime) return;
1335
+ const globalWindow = globalThis.window;
1336
+ if (listenCrossTab && (api.crossTabSyncMode ?? "none") === "none" && globalWindow !== void 0) {
1337
+ warnOnce(
1338
+ api,
1339
+ `listenCrossTab:${key}`,
1340
+ `[Mnemonic] useMnemonicKey("${key}") enabled listenCrossTab, but the active storage backend may not be able to notify external changes. If you're using a custom Storage-like wrapper around localStorage, ensure it forwards browser "storage" events or implements storage.onExternalChange(...); otherwise, use localStorage or implement storage.onExternalChange(...) on your custom backend.`
1341
+ );
1342
+ }
1343
+ additionalDevWarnings?.({
1344
+ api,
1345
+ key,
1346
+ listenCrossTab,
1347
+ codecOpt,
1348
+ schema,
1349
+ warnOnce: (id, message) => warnOnce(api, id, message)
1350
+ });
1351
+ let keyContracts = diagnosticContractRegistry.get(api);
1352
+ if (!keyContracts) {
1353
+ keyContracts = /* @__PURE__ */ new Map();
1354
+ diagnosticContractRegistry.set(api, keyContracts);
1355
+ }
1356
+ if (contractFingerprint === null) {
1357
+ return;
1358
+ }
1359
+ const previousContract = keyContracts.get(key);
1360
+ if (previousContract === void 0) {
1361
+ keyContracts.set(key, contractFingerprint);
1362
+ return;
1363
+ }
1364
+ if (previousContract === contractFingerprint) {
1365
+ return;
1366
+ }
1367
+ warnOnce(
1368
+ api,
1369
+ `contract-conflict:${key}`,
1370
+ `[Mnemonic] Conflicting useMnemonicKey contracts detected for key "${key}" in namespace "${api.prefix.slice(0, -1)}". Reuse a shared descriptor with defineMnemonicKey(...) or align defaultValue/codec/schema/reconcile options so every consumer describes the same persisted contract.`
1371
+ );
1372
+ }, [
1373
+ additionalDevWarnings,
1374
+ api,
1375
+ key,
1376
+ developmentRuntime,
1377
+ contractFingerprint,
1378
+ listenCrossTab,
1379
+ codecOpt,
1380
+ schema,
1381
+ api.crossTabSyncMode
1382
+ ]);
1383
+ react.useEffect(() => {
1384
+ if (hasMounted) return;
1385
+ setHasMounted(true);
1386
+ }, [hasMounted, setHasMounted]);
1387
+ react.useEffect(() => {
1388
+ if (decoded.rewriteRaw && decoded.rewriteRaw !== raw) {
1389
+ api.setRaw(key, decoded.rewriteRaw);
1390
+ }
1391
+ }, [api, decoded.rewriteRaw, key, raw]);
1392
+ react.useEffect(() => {
1393
+ onDecodedEffect?.(decoded);
1394
+ }, [decoded, onDecodedEffect]);
1395
+ const prevRef = react.useRef(value);
1396
+ const mounted = react.useRef(false);
1397
+ react.useEffect(() => {
1398
+ if (mounted.current) return;
1399
+ mounted.current = true;
1400
+ onMount?.(value);
1401
+ prevRef.current = value;
1402
+ }, []);
1403
+ react.useEffect(() => {
1404
+ const prev = prevRef.current;
1405
+ if (Object.is(prev, value)) return;
1406
+ prevRef.current = value;
1407
+ onChange?.(value, prev);
1408
+ }, [value, onChange]);
1409
+ react.useEffect(() => {
1410
+ if (!listenCrossTab) return;
1411
+ const globalWindow = globalThis.window;
1412
+ if (globalWindow === void 0) return;
1413
+ const storageKey = api.prefix + key;
1414
+ const handler = (e) => {
1415
+ if (e.key === null) {
1416
+ api.removeRaw(key);
1417
+ return;
1418
+ }
1419
+ if (e.key !== storageKey) return;
1420
+ if (e.newValue == null) {
1421
+ api.removeRaw(key);
1422
+ return;
1423
+ }
1424
+ api.setRaw(key, e.newValue);
1425
+ };
1426
+ globalWindow.addEventListener("storage", handler);
1427
+ return () => globalWindow.removeEventListener("storage", handler);
1428
+ }, [listenCrossTab, api, key]);
1429
+ const set = react.useMemo(() => {
1430
+ return (next) => {
1431
+ const nextVal = typeof next === "function" ? next(decodeForRead(api.getRawSnapshot(key)).value) : next;
1432
+ try {
1433
+ const encoded = encodeForWrite(nextVal);
1434
+ api.setRaw(key, encoded);
1435
+ } catch (err) {
1436
+ if (err instanceof SchemaError) {
1437
+ console.error(`[Mnemonic] Schema error for key "${key}" (${err.code}):`, err.message);
1438
+ return;
1439
+ }
1440
+ if (err instanceof CodecError) {
1441
+ console.error(`[Mnemonic] Codec encode error for key "${key}":`, err.message);
1442
+ return;
1443
+ }
1444
+ console.error(`[Mnemonic] Failed to persist key "${key}":`, err);
1445
+ }
1446
+ };
1447
+ }, [api, key, decodeForRead, encodeForWrite]);
1448
+ const reset = react.useMemo(() => {
1449
+ return () => {
1450
+ const v = getFallback();
1451
+ try {
1452
+ const encoded = encodeForWrite(v);
1453
+ api.setRaw(key, encoded);
1454
+ } catch (err) {
1455
+ if (err instanceof SchemaError) {
1456
+ console.error(`[Mnemonic] Schema error for key "${key}" (${err.code}):`, err.message);
1457
+ return;
1458
+ }
1459
+ if (err instanceof CodecError) {
1460
+ console.error(`[Mnemonic] Codec encode error for key "${key}":`, err.message);
1461
+ }
1462
+ return;
1463
+ }
1464
+ };
1465
+ }, [api, key, getFallback, encodeForWrite]);
1466
+ const remove = react.useMemo(() => {
1467
+ return () => api.removeRaw(key);
1468
+ }, [api, key]);
1469
+ return react.useMemo(
1470
+ () => ({
1471
+ value,
1472
+ set,
1473
+ reset,
1474
+ remove
1475
+ }),
1476
+ [value, set, reset, remove]
1477
+ );
1478
+ }
1479
+
1480
+ // src/Mnemonic/use.ts
1481
+ function useSchemaMnemonicKey(descriptor) {
1482
+ const shared = useMnemonicKeyShared(descriptor, void 0, descriptor.options.schema?.version);
1483
+ const { api, key, codec, codecOpt, schema, reconcile, parseEnvelope, decodeStringPayload, buildFallbackResult } = shared;
1484
+ const schemaMode = api.schemaMode;
1485
+ const schemaRegistry = api.schemaRegistry;
1486
+ const validateAgainstSchema = react.useCallback(
1487
+ (value, jsonSchema) => {
1488
+ const errors = validateJsonSchema(value, jsonSchema);
1489
+ if (errors.length > 0) {
1490
+ const message = errors.map((e) => `${e.path || "/"}: ${e.message}`).join("; ");
1491
+ throw new SchemaError("TYPE_MISMATCH", `Schema validation failed for key "${key}": ${message}`);
1492
+ }
1493
+ },
1494
+ [key]
1495
+ );
1496
+ const registryCache = react.useMemo(() => {
1497
+ if (!schemaRegistry || schemaMode === "autoschema") return null;
1498
+ return {
1499
+ latestSchema: void 0,
1500
+ latestSchemaSet: false,
1501
+ schemaByVersion: /* @__PURE__ */ new Map(),
1502
+ migrationPaths: /* @__PURE__ */ new Map()
1503
+ };
1504
+ }, [schemaRegistry, schemaMode, key]);
1505
+ const getSchemaForVersion = react.useCallback(
1506
+ (version) => {
1507
+ if (!schemaRegistry) return void 0;
1508
+ if (!registryCache) return schemaRegistry.getSchema(key, version);
1509
+ if (registryCache.schemaByVersion.has(version)) {
1510
+ return registryCache.schemaByVersion.get(version);
1511
+ }
1512
+ const nextSchema = schemaRegistry.getSchema(key, version);
1513
+ registryCache.schemaByVersion.set(version, nextSchema);
1514
+ return nextSchema;
1515
+ },
1516
+ [schemaRegistry, registryCache, key]
1517
+ );
1518
+ const getLatestSchemaForKey = react.useCallback(() => {
1519
+ if (!schemaRegistry) return void 0;
1520
+ if (!registryCache) return schemaRegistry.getLatestSchema(key);
1521
+ if (registryCache.latestSchemaSet) return registryCache.latestSchema;
1522
+ const nextSchema = schemaRegistry.getLatestSchema(key);
1523
+ registryCache.latestSchema = nextSchema;
1524
+ registryCache.latestSchemaSet = true;
1525
+ return nextSchema;
1526
+ }, [schemaRegistry, registryCache, key]);
1527
+ const getMigrationPathForKey = react.useCallback(
1528
+ (fromVersion, toVersion) => {
1529
+ if (!schemaRegistry) return null;
1530
+ if (!registryCache) return schemaRegistry.getMigrationPath(key, fromVersion, toVersion) ?? null;
1531
+ const cacheKey = `${fromVersion}->${toVersion}`;
1532
+ if (registryCache.migrationPaths.has(cacheKey)) {
1533
+ return registryCache.migrationPaths.get(cacheKey) ?? null;
1534
+ }
1535
+ const path = schemaRegistry.getMigrationPath(key, fromVersion, toVersion) ?? null;
1536
+ registryCache.migrationPaths.set(cacheKey, path);
1537
+ return path;
1538
+ },
1539
+ [schemaRegistry, registryCache, key]
1540
+ );
1541
+ const buildSchemaManagedResult = react.useCallback((version, value) => {
1542
+ return serializeEnvelope(version, value);
1543
+ }, []);
1544
+ const applyReconcile = useApplyReconcile({
1545
+ key,
1546
+ reconcile,
1547
+ buildFallbackResult
1548
+ });
1549
+ const resolveTargetWriteSchema = react.useCallback(() => {
1550
+ const explicitVersion = schema?.version;
1551
+ const latestSchema = getLatestSchemaForKey();
1552
+ if (explicitVersion === void 0) return latestSchema;
1553
+ const explicitSchema = getSchemaForVersion(explicitVersion);
1554
+ if (explicitSchema) return explicitSchema;
1555
+ return schemaMode === "strict" ? void 0 : latestSchema;
1556
+ }, [getLatestSchemaForKey, getSchemaForVersion, schema?.version, schemaMode]);
1557
+ const encodeForWrite = react.useCallback(
1558
+ (nextValue) => {
1559
+ const explicitVersion = schema?.version;
1560
+ const targetSchema = resolveTargetWriteSchema();
1561
+ if (!targetSchema) {
1562
+ if (explicitVersion !== void 0 && schemaMode === "strict") {
1563
+ throw new SchemaError(
1564
+ "WRITE_SCHEMA_REQUIRED",
1565
+ `Write requires schema for key "${key}" in strict mode`
1566
+ );
1567
+ }
1568
+ return serializeEnvelope(0, codec.encode(nextValue));
1569
+ }
1570
+ let valueToStore = nextValue;
1571
+ const writeMigration = schemaRegistry?.getWriteMigration?.(key, targetSchema.version);
1572
+ if (writeMigration) {
1573
+ try {
1574
+ valueToStore = writeMigration.migrate(valueToStore);
1575
+ } catch (err) {
1576
+ throw err instanceof SchemaError ? err : new SchemaError("MIGRATION_FAILED", `Write-time migration failed for key "${key}"`, err);
1577
+ }
1578
+ }
1579
+ validateAgainstSchema(valueToStore, targetSchema.schema);
1580
+ return buildSchemaManagedResult(targetSchema.version, valueToStore);
1581
+ },
1582
+ [
1583
+ schema?.version,
1584
+ key,
1585
+ schemaMode,
1586
+ codec,
1587
+ schemaRegistry,
1588
+ validateAgainstSchema,
1589
+ resolveTargetWriteSchema,
1590
+ buildSchemaManagedResult
1591
+ ]
1592
+ );
1593
+ const decodeAutoschemaEnvelope = react.useCallback(
1594
+ (envelope, latestSchema) => {
1595
+ if (latestSchema) {
1596
+ return buildFallbackResult(
1597
+ new SchemaError("SCHEMA_NOT_FOUND", `No schema for key "${key}" v${envelope.version}`)
1598
+ );
1599
+ }
1600
+ if (!schemaRegistry || typeof schemaRegistry.registerSchema !== "function") {
1601
+ return buildFallbackResult(
1602
+ new SchemaError(
1603
+ "MODE_CONFIGURATION_INVALID",
1604
+ `Autoschema mode requires schema registry registration for key "${key}"`
1605
+ )
1606
+ );
1607
+ }
1608
+ try {
1609
+ const decoded = typeof envelope.payload === "string" ? decodeStringPayload(envelope.payload, codec) : envelope.payload;
1610
+ const inferSchemaForValue = (value) => ({
1611
+ key,
1612
+ version: 1,
1613
+ schema: inferJsonSchema(value)
1614
+ });
1615
+ const inferred = inferSchemaForValue(decoded);
1616
+ return applyReconcile({
1617
+ value: decoded,
1618
+ extra: { pendingSchema: inferred },
1619
+ rewriteRaw: buildSchemaManagedResult(inferred.version, decoded),
1620
+ persistedVersion: envelope.version,
1621
+ serializeForPersist: (value) => buildSchemaManagedResult(inferred.version, value),
1622
+ deriveExtra: (value) => ({
1623
+ pendingSchema: inferSchemaForValue(value)
1624
+ })
1625
+ });
1626
+ } catch (err) {
1627
+ const typedErr = err instanceof SchemaError || err instanceof CodecError ? err : new SchemaError("TYPE_MISMATCH", `Autoschema inference failed for key "${key}"`, err);
1628
+ return buildFallbackResult(typedErr);
1629
+ }
1630
+ },
1631
+ [
1632
+ applyReconcile,
1633
+ buildFallbackResult,
1634
+ buildSchemaManagedResult,
1635
+ codec,
1636
+ decodeStringPayload,
1637
+ key,
1638
+ schemaRegistry
1639
+ ]
1640
+ );
1641
+ const decodeCodecManagedEnvelope = react.useCallback(
1642
+ (envelope, latestSchema) => {
1643
+ if (typeof envelope.payload !== "string") {
1644
+ return applyReconcile({
1645
+ value: envelope.payload,
1646
+ persistedVersion: envelope.version,
1647
+ ...latestSchema ? { latestVersion: latestSchema.version } : {},
1648
+ serializeForPersist: encodeForWrite
1649
+ });
1650
+ }
1651
+ try {
1652
+ const decoded = decodeStringPayload(envelope.payload, codec);
1653
+ return applyReconcile({
1654
+ value: decoded,
1655
+ persistedVersion: envelope.version,
1656
+ ...latestSchema ? { latestVersion: latestSchema.version } : {},
1657
+ serializeForPersist: encodeForWrite
1658
+ });
1659
+ } catch (err) {
1660
+ return buildFallbackResult(err);
1661
+ }
1662
+ },
1663
+ [applyReconcile, buildFallbackResult, codec, decodeStringPayload, encodeForWrite]
1664
+ );
1665
+ const decodeSchemaManagedEnvelope = react.useCallback(
1666
+ (envelope, schemaForVersion, latestSchema) => {
1667
+ let current;
1668
+ try {
1669
+ current = envelope.payload;
1670
+ validateAgainstSchema(current, schemaForVersion.schema);
1671
+ } catch (err) {
1672
+ const typedErr = err instanceof SchemaError || err instanceof CodecError ? err : new SchemaError("TYPE_MISMATCH", `Schema decode failed for key "${key}"`, err);
1673
+ return buildFallbackResult(typedErr);
1674
+ }
1675
+ if (!latestSchema || envelope.version >= latestSchema.version) {
1676
+ return applyReconcile({
1677
+ value: current,
1678
+ persistedVersion: envelope.version,
1679
+ ...latestSchema ? { latestVersion: latestSchema.version } : {},
1680
+ serializeForPersist: encodeForWrite
1681
+ });
1682
+ }
1683
+ const path = getMigrationPathForKey(envelope.version, latestSchema.version);
1684
+ if (!path) {
1685
+ return buildFallbackResult(
1686
+ new SchemaError(
1687
+ "MIGRATION_PATH_NOT_FOUND",
1688
+ `No migration path for key "${key}" from v${envelope.version} to v${latestSchema.version}`
1689
+ )
1690
+ );
1691
+ }
1692
+ try {
1693
+ let migrated = current;
1694
+ for (const step of path) {
1695
+ migrated = step.migrate(migrated);
1696
+ }
1697
+ validateAgainstSchema(migrated, latestSchema.schema);
1698
+ return applyReconcile({
1699
+ value: migrated,
1700
+ rewriteRaw: buildSchemaManagedResult(latestSchema.version, migrated),
1701
+ persistedVersion: envelope.version,
1702
+ latestVersion: latestSchema.version,
1703
+ serializeForPersist: encodeForWrite
1704
+ });
1705
+ } catch (err) {
1706
+ const typedErr = err instanceof SchemaError || err instanceof CodecError ? err : new SchemaError("MIGRATION_FAILED", `Migration failed for key "${key}"`, err);
1707
+ return buildFallbackResult(typedErr);
1708
+ }
1709
+ },
1710
+ [
1711
+ applyReconcile,
1712
+ buildFallbackResult,
1713
+ buildSchemaManagedResult,
1714
+ encodeForWrite,
1715
+ getMigrationPathForKey,
1716
+ key,
1717
+ validateAgainstSchema
1718
+ ]
1719
+ );
1720
+ const decodeForRead = react.useCallback(
1721
+ (rawText) => {
1722
+ if (rawText == null) return buildFallbackResult();
1723
+ const parsed = parseEnvelope(rawText);
1724
+ if (!parsed.ok) return buildFallbackResult(parsed.error);
1725
+ const envelope = parsed.envelope;
1726
+ const schemaForVersion = getSchemaForVersion(envelope.version);
1727
+ const latestSchema = getLatestSchemaForKey();
1728
+ if (schemaMode === "strict" && !schemaForVersion) {
1729
+ return buildFallbackResult(
1730
+ new SchemaError("SCHEMA_NOT_FOUND", `No schema for key "${key}" v${envelope.version}`)
1731
+ );
1732
+ }
1733
+ if (schemaMode === "autoschema" && !schemaForVersion) {
1734
+ return decodeAutoschemaEnvelope(envelope, latestSchema);
1735
+ }
1736
+ if (!schemaForVersion) {
1737
+ return decodeCodecManagedEnvelope(envelope, latestSchema);
1738
+ }
1739
+ return decodeSchemaManagedEnvelope(envelope, schemaForVersion, latestSchema);
1740
+ },
1741
+ [
1742
+ buildFallbackResult,
1743
+ decodeAutoschemaEnvelope,
1744
+ decodeCodecManagedEnvelope,
1745
+ decodeSchemaManagedEnvelope,
1746
+ parseEnvelope,
1747
+ schemaMode,
1748
+ getSchemaForVersion,
1749
+ getLatestSchemaForKey,
1750
+ key
1751
+ ]
1752
+ );
1753
+ const additionalDevWarnings = react.useCallback(
1754
+ ({ warnOnce: warnOnce2 }) => {
1755
+ if (!codecOpt || schema?.version === void 0 || !api.schemaRegistry) return;
1756
+ warnOnce2(
1757
+ `codec+schema:${key}`,
1758
+ `[Mnemonic] useMnemonicKey("${key}") received both a custom codec and schema.version. Schema-managed reads/writes do not use the codec path. Remove the codec for schema-managed storage, or remove schema.version if you intended codec-only persistence.`
1759
+ );
1760
+ },
1761
+ [api.schemaRegistry, codecOpt, key, schema?.version]
1762
+ );
1763
+ const onDecodedEffect = react.useCallback(
1764
+ (decoded) => {
1765
+ if (!decoded.pendingSchema || !schemaRegistry?.registerSchema) return;
1766
+ if (schemaRegistry.getSchema(decoded.pendingSchema.key, decoded.pendingSchema.version)) return;
1767
+ try {
1768
+ schemaRegistry.registerSchema(decoded.pendingSchema);
1769
+ } catch {
1770
+ }
1771
+ },
1772
+ [schemaRegistry]
1773
+ );
1774
+ return useMnemonicKeyState(shared, {
1775
+ decodeForRead,
1776
+ encodeForWrite,
1777
+ additionalDevWarnings,
1778
+ onDecodedEffect
1779
+ });
1780
+ }
1781
+ function useMnemonicKey(keyOrDescriptor, options) {
1782
+ return useSchemaMnemonicKey(resolveMnemonicKeyArgs(keyOrDescriptor, options));
1783
+ }
1784
+ function uniqueKeys(keys) {
1785
+ return [...new Set(keys)];
1786
+ }
1787
+ function isDevelopmentRuntime2() {
1788
+ return getRuntimeNodeEnv() === "development";
1789
+ }
1790
+ var recoveryDiagnosticWarnings = /* @__PURE__ */ new WeakMap();
1791
+ function warnRecoveryOnce(api, id, message) {
1792
+ let warnings = recoveryDiagnosticWarnings.get(api);
1793
+ if (!warnings) {
1794
+ warnings = /* @__PURE__ */ new Set();
1795
+ recoveryDiagnosticWarnings.set(api, warnings);
1796
+ }
1797
+ if (warnings.has(id)) return;
1798
+ warnings.add(id);
1799
+ console.warn(message);
1800
+ }
1801
+ function useMnemonicRecovery(options = {}) {
1802
+ const api = useMnemonic();
1803
+ const { onRecover } = options;
1804
+ const namespace = react.useMemo(() => api.prefix.endsWith(".") ? api.prefix.slice(0, -1) : api.prefix, [api.prefix]);
1805
+ const emitRecovery = react.useCallback(
1806
+ (action, clearedKeys) => {
1807
+ const event = {
1808
+ action,
1809
+ namespace,
1810
+ clearedKeys
1811
+ };
1812
+ onRecover?.(event);
1813
+ },
1814
+ [namespace, onRecover]
1815
+ );
1816
+ const listKeys = react.useCallback(() => api.keys(), [api]);
1817
+ const clearResolvedKeys = react.useCallback(
1818
+ (action, keys) => {
1819
+ const clearedKeys = uniqueKeys(keys);
1820
+ for (const key of clearedKeys) {
1821
+ api.removeRaw(key);
1822
+ }
1823
+ emitRecovery(action, clearedKeys);
1824
+ return clearedKeys;
1825
+ },
1826
+ [api, emitRecovery]
1827
+ );
1828
+ const clearKeys = react.useCallback(
1829
+ (keys) => clearResolvedKeys("clear-keys", keys),
1830
+ [clearResolvedKeys]
1831
+ );
1832
+ const clearAll = react.useCallback(() => {
1833
+ if (!api.canEnumerateKeys) {
1834
+ if (isDevelopmentRuntime2()) {
1835
+ warnRecoveryOnce(
1836
+ api,
1837
+ "recovery-clear-all-non-enumerable",
1838
+ `[Mnemonic] clearAll() requires an enumerable storage backend in namespace "${namespace}". Use clearKeys([...]) with an explicit durable-key list, or supply a storage backend that implements length and key(index).`
1839
+ );
1840
+ }
1841
+ throw new Error(
1842
+ "clearAll requires an enumerable storage backend. Use clearKeys([...]) with an explicit key list instead."
1843
+ );
1844
+ }
1845
+ return clearResolvedKeys("clear-all", api.keys());
1846
+ }, [api, clearResolvedKeys, namespace]);
1847
+ const clearMatching = react.useCallback(
1848
+ (predicate) => {
1849
+ if (!api.canEnumerateKeys) {
1850
+ if (isDevelopmentRuntime2()) {
1851
+ warnRecoveryOnce(
1852
+ api,
1853
+ "recovery-clear-matching-non-enumerable",
1854
+ `[Mnemonic] clearMatching() requires an enumerable storage backend in namespace "${namespace}". Use clearKeys([...]) with an explicit durable-key list, or supply a storage backend that implements length and key(index).`
1855
+ );
1856
+ }
1857
+ throw new Error(
1858
+ "clearMatching requires an enumerable storage backend. Use clearKeys([...]) with an explicit key list instead."
1859
+ );
1860
+ }
1861
+ return clearResolvedKeys(
1862
+ "clear-matching",
1863
+ api.keys().filter((key) => predicate(key))
1864
+ );
1865
+ },
1866
+ [api, clearResolvedKeys, namespace]
1867
+ );
1868
+ return react.useMemo(
1869
+ () => ({
1870
+ namespace,
1871
+ canEnumerateKeys: api.canEnumerateKeys,
1872
+ listKeys,
1873
+ clearAll,
1874
+ clearKeys,
1875
+ clearMatching
1876
+ }),
1877
+ [namespace, api.canEnumerateKeys, listKeys, clearAll, clearKeys, clearMatching]
1878
+ );
1879
+ }
1880
+
1881
+ // src/Mnemonic/key.ts
1882
+ function defineMnemonicKey(keyOrSchema, options) {
1883
+ if (typeof keyOrSchema !== "string") {
1884
+ return Object.freeze({
1885
+ key: keyOrSchema.key,
1886
+ options: {
1887
+ ...options,
1888
+ schema: { version: keyOrSchema.version }
1889
+ }
1890
+ });
1891
+ }
1892
+ return Object.freeze({
1893
+ key: keyOrSchema,
1894
+ options
1895
+ });
1896
+ }
1897
+
1898
+ // src/Mnemonic/schema-registry.ts
1899
+ function schemaVersionKey(key, version) {
1900
+ return `${key}:${version}`;
1901
+ }
1902
+ function migrationVersionKey(key, fromVersion) {
1903
+ return `${key}:${fromVersion}`;
1904
+ }
1905
+ function validateVersion(value, label) {
1906
+ if (!Number.isInteger(value) || value < 0) {
1907
+ throw new SchemaError("MIGRATION_GRAPH_INVALID", `${label} must be a non-negative integer`);
1908
+ }
1909
+ }
1910
+ function createSchemaRegistry(options = {}) {
1911
+ const { schemas = [], migrations = [] } = options;
1912
+ const schemasByKeyAndVersion = /* @__PURE__ */ new Map();
1913
+ const latestSchemaByKey = /* @__PURE__ */ new Map();
1914
+ const writeMigrationsByKeyAndVersion = /* @__PURE__ */ new Map();
1915
+ const migrationsByKeyAndFromVersion = /* @__PURE__ */ new Map();
1916
+ for (const schema of schemas) {
1917
+ validateVersion(schema.version, `Schema version for key "${schema.key}"`);
1918
+ const id = schemaVersionKey(schema.key, schema.version);
1919
+ if (schemasByKeyAndVersion.has(id)) {
1920
+ throw new SchemaError(
1921
+ "SCHEMA_REGISTRATION_CONFLICT",
1922
+ `Duplicate schema registered for key "${schema.key}" version ${schema.version}`
1923
+ );
1924
+ }
1925
+ schemasByKeyAndVersion.set(id, schema);
1926
+ const currentLatest = latestSchemaByKey.get(schema.key);
1927
+ if (!currentLatest || schema.version > currentLatest.version) {
1928
+ latestSchemaByKey.set(schema.key, schema);
1929
+ }
1930
+ }
1931
+ for (const migration of migrations) {
1932
+ validateVersion(migration.fromVersion, `Migration fromVersion for key "${migration.key}"`);
1933
+ validateVersion(migration.toVersion, `Migration toVersion for key "${migration.key}"`);
1934
+ if (migration.toVersion < migration.fromVersion) {
1935
+ throw new SchemaError(
1936
+ "MIGRATION_GRAPH_INVALID",
1937
+ `Backward migration "${migration.key}" ${migration.fromVersion} -> ${migration.toVersion} is not supported`
1938
+ );
1939
+ }
1940
+ if (migration.fromVersion === migration.toVersion) {
1941
+ const id = schemaVersionKey(migration.key, migration.fromVersion);
1942
+ if (writeMigrationsByKeyAndVersion.has(id)) {
1943
+ throw new SchemaError(
1944
+ "MIGRATION_GRAPH_INVALID",
1945
+ `Duplicate write migration registered for key "${migration.key}" version ${migration.fromVersion}`
1946
+ );
1947
+ }
1948
+ writeMigrationsByKeyAndVersion.set(id, migration);
1949
+ continue;
1950
+ }
1951
+ const edgeKey = migrationVersionKey(migration.key, migration.fromVersion);
1952
+ if (migrationsByKeyAndFromVersion.has(edgeKey)) {
1953
+ const existing = migrationsByKeyAndFromVersion.get(edgeKey);
1954
+ throw new SchemaError(
1955
+ "MIGRATION_GRAPH_INVALID",
1956
+ `Ambiguous migration graph for key "${migration.key}" at version ${migration.fromVersion}: ${existing.fromVersion} -> ${existing.toVersion} conflicts with ${migration.fromVersion} -> ${migration.toVersion}`
1957
+ );
1958
+ }
1959
+ migrationsByKeyAndFromVersion.set(edgeKey, migration);
1960
+ }
1961
+ return {
1962
+ getSchema(key, version) {
1963
+ return schemasByKeyAndVersion.get(schemaVersionKey(key, version));
1964
+ },
1965
+ getLatestSchema(key) {
1966
+ return latestSchemaByKey.get(key);
1967
+ },
1968
+ getMigrationPath(key, fromVersion, toVersion) {
1969
+ if (fromVersion === toVersion) return [];
1970
+ if (toVersion < fromVersion) return null;
1971
+ const path = [];
1972
+ let currentVersion = fromVersion;
1973
+ while (currentVersion < toVersion) {
1974
+ const next = migrationsByKeyAndFromVersion.get(migrationVersionKey(key, currentVersion));
1975
+ if (!next) return null;
1976
+ path.push(next);
1977
+ currentVersion = next.toVersion;
1978
+ }
1979
+ return currentVersion === toVersion ? path : null;
1980
+ },
1981
+ getWriteMigration(key, version) {
1982
+ return writeMigrationsByKeyAndVersion.get(schemaVersionKey(key, version));
1983
+ }
1984
+ };
1985
+ }
1986
+
1987
+ // src/Mnemonic/schema-helpers.ts
1988
+ function defineKeySchema(key, version, schema) {
1989
+ return Object.freeze({
1990
+ key,
1991
+ version,
1992
+ schema
1993
+ });
1994
+ }
1995
+ function defineMigration(fromSchema, toSchema, migrate) {
1996
+ if (fromSchema.key !== toSchema.key) {
1997
+ throw new SchemaError(
1998
+ "MIGRATION_GRAPH_INVALID",
1999
+ `Migration schemas must target the same key: "${fromSchema.key}" !== "${toSchema.key}"`
2000
+ );
2001
+ }
2002
+ return Object.freeze({
2003
+ key: fromSchema.key,
2004
+ fromVersion: fromSchema.version,
2005
+ toVersion: toSchema.version,
2006
+ migrate
2007
+ });
2008
+ }
2009
+ function defineWriteMigration(schema, migrate) {
2010
+ return Object.freeze({
2011
+ key: schema.key,
2012
+ fromVersion: schema.version,
2013
+ toVersion: schema.version,
2014
+ migrate
2015
+ });
2016
+ }
2017
+
2018
+ // src/Mnemonic/typed-schema.ts
2019
+ var optionalSchemaMarker = /* @__PURE__ */ Symbol("mnemonicOptionalSchema");
2020
+ function cloneSchema(schema) {
2021
+ const clone = { ...schema };
2022
+ if (schema[optionalSchemaMarker]) {
2023
+ Object.defineProperty(clone, optionalSchemaMarker, {
2024
+ value: true,
2025
+ enumerable: false,
2026
+ configurable: false
2027
+ });
2028
+ }
2029
+ return clone;
2030
+ }
2031
+ function isOptionalSchema(schema) {
2032
+ return Boolean(schema[optionalSchemaMarker]);
2033
+ }
2034
+ function markOptional(schema) {
2035
+ const clone = cloneSchema(schema);
2036
+ Object.defineProperty(clone, optionalSchemaMarker, {
2037
+ value: true,
2038
+ enumerable: false,
2039
+ configurable: false
2040
+ });
2041
+ return clone;
2042
+ }
2043
+ function withoutOptionalMarker(schema) {
2044
+ const clone = { ...schema };
2045
+ delete clone[optionalSchemaMarker];
2046
+ return clone;
2047
+ }
2048
+ function withType(type, extra = {}) {
2049
+ return {
2050
+ type,
2051
+ ...extra
2052
+ };
2053
+ }
2054
+ function toTypeArray(type) {
2055
+ if (type === void 0) return null;
2056
+ return Array.isArray(type) ? [...type] : [type];
2057
+ }
2058
+ function appendNullType(type) {
2059
+ const types = toTypeArray(type);
2060
+ if (types === null) {
2061
+ return void 0;
2062
+ }
2063
+ return types.includes("null") ? types : [...types, "null"];
2064
+ }
2065
+ function nullableSchema(schema) {
2066
+ if (schema.enum) {
2067
+ const nullableType = appendNullType(schema.type);
2068
+ return {
2069
+ ...schema,
2070
+ ...nullableType ? { type: nullableType } : {},
2071
+ enum: schema.enum.includes(null) ? schema.enum : [...schema.enum, null]
2072
+ };
2073
+ }
2074
+ if ("const" in schema) {
2075
+ const { const: constValue, ...rest } = schema;
2076
+ const nullableType = appendNullType(rest.type);
2077
+ if (constValue === null || constValue === void 0) {
2078
+ return {
2079
+ ...schema,
2080
+ ...nullableType ? { type: nullableType } : {}
2081
+ };
2082
+ }
2083
+ return {
2084
+ ...rest,
2085
+ ...nullableType ? { type: nullableType } : {},
2086
+ enum: [constValue, null]
2087
+ };
2088
+ }
2089
+ const types = toTypeArray(schema.type);
2090
+ if (types === null) {
2091
+ throw new SchemaError(
2092
+ "MODE_CONFIGURATION_INVALID",
2093
+ "mnemonicSchema.nullable(...) requires a schema with type, enum, or const"
2094
+ );
2095
+ }
2096
+ return {
2097
+ ...schema,
2098
+ type: types.includes("null") ? types : [...types, "null"]
2099
+ };
2100
+ }
2101
+ var mnemonicSchema = {
2102
+ string(options = {}) {
2103
+ return withType("string", options);
2104
+ },
2105
+ number(options = {}) {
2106
+ return withType("number", options);
2107
+ },
2108
+ integer(options = {}) {
2109
+ return withType("integer", options);
2110
+ },
2111
+ boolean() {
2112
+ return withType("boolean");
2113
+ },
2114
+ nullValue() {
2115
+ return withType("null");
2116
+ },
2117
+ literal(value) {
2118
+ return {
2119
+ const: value
2120
+ };
2121
+ },
2122
+ enum(values) {
2123
+ return {
2124
+ enum: values
2125
+ };
2126
+ },
2127
+ optional(schema) {
2128
+ return markOptional(schema);
2129
+ },
2130
+ nullable(schema) {
2131
+ return nullableSchema(schema);
2132
+ },
2133
+ array(itemSchema, options = {}) {
2134
+ return withType("array", {
2135
+ items: withoutOptionalMarker(itemSchema),
2136
+ ...options
2137
+ });
2138
+ },
2139
+ object(shape, options = {}) {
2140
+ const properties = {};
2141
+ const required = [];
2142
+ for (const [name, schema] of Object.entries(shape)) {
2143
+ properties[name] = withoutOptionalMarker(schema);
2144
+ if (!isOptionalSchema(schema)) {
2145
+ required.push(name);
2146
+ }
2147
+ }
2148
+ const result = {
2149
+ type: "object",
2150
+ properties,
2151
+ ...options
2152
+ };
2153
+ if (required.length > 0) {
2154
+ result.required = required;
2155
+ }
2156
+ return result;
2157
+ },
2158
+ record(valueSchema) {
2159
+ return withType("object", {
2160
+ additionalProperties: withoutOptionalMarker(valueSchema)
2161
+ });
2162
+ }
2163
+ };
2164
+
2165
+ // src/Mnemonic/structural-migrations.ts
2166
+ function resolveHelpers(helpers) {
2167
+ if (helpers) return helpers;
2168
+ return {
2169
+ getId: (node) => node.id,
2170
+ getChildren: (node) => node.children,
2171
+ withChildren: (node, children) => ({ ...node, children }),
2172
+ withId: (node, id) => ({ ...node, id })
2173
+ };
2174
+ }
2175
+ function findNodeById(root, id, helpers) {
2176
+ const tree = resolveHelpers(helpers);
2177
+ if (tree.getId(root) === id) return root;
2178
+ for (const child of tree.getChildren(root) ?? []) {
2179
+ const match = findNodeById(child, id, tree);
2180
+ if (match) return match;
2181
+ }
2182
+ return void 0;
2183
+ }
2184
+ function insertChildIfMissing(root, parentId, child, helpers) {
2185
+ const tree = resolveHelpers(helpers);
2186
+ const childId = tree.getId(child);
2187
+ const visit = (node) => {
2188
+ if (tree.getId(node) === parentId) {
2189
+ const children2 = [...tree.getChildren(node) ?? []];
2190
+ if (children2.some((existing) => tree.getId(existing) === childId)) {
2191
+ return [node, false];
2192
+ }
2193
+ return [tree.withChildren(node, [...children2, child]), true];
2194
+ }
2195
+ const children = tree.getChildren(node);
2196
+ if (!children?.length) return [node, false];
2197
+ let inserted = false;
2198
+ let changed = false;
2199
+ const nextChildren = children.map((existingChild) => {
2200
+ if (inserted) return existingChild;
2201
+ const [nextChild, didInsert] = visit(existingChild);
2202
+ inserted || (inserted = didInsert);
2203
+ changed || (changed = nextChild !== existingChild);
2204
+ return nextChild;
2205
+ });
2206
+ if (!changed) return [node, inserted];
2207
+ return [tree.withChildren(node, nextChildren), inserted];
2208
+ };
2209
+ return visit(root)[0];
2210
+ }
2211
+ function renameNode(root, currentId, nextId, helpers) {
2212
+ const tree = resolveHelpers(helpers);
2213
+ if (currentId === nextId) return root;
2214
+ if (!findNodeById(root, currentId, tree)) return root;
2215
+ if (findNodeById(root, nextId, tree)) return root;
2216
+ const visit = (node) => {
2217
+ let nextNode = tree.getId(node) === currentId ? tree.withId(node, nextId) : node;
2218
+ const children = tree.getChildren(nextNode);
2219
+ if (!children?.length) return nextNode;
2220
+ let changed = nextNode !== node;
2221
+ const nextChildren = children.map((child) => {
2222
+ const nextChild = visit(child);
2223
+ changed || (changed = nextChild !== child);
2224
+ return nextChild;
2225
+ });
2226
+ if (!changed) return node;
2227
+ return tree.withChildren(nextNode, nextChildren);
2228
+ };
2229
+ return visit(root);
2230
+ }
2231
+ function dedupeChildrenBy(root, getKey, helpers) {
2232
+ const tree = resolveHelpers(helpers);
2233
+ const visit = (node) => {
2234
+ const children = tree.getChildren(node);
2235
+ if (!children?.length) return node;
2236
+ let changed = false;
2237
+ const seen = /* @__PURE__ */ new Set();
2238
+ const nextChildren = [];
2239
+ for (const child of children) {
2240
+ const normalizedChild = visit(child);
2241
+ changed || (changed = normalizedChild !== child);
2242
+ const key = getKey(normalizedChild);
2243
+ if (seen.has(key)) {
2244
+ changed = true;
2245
+ continue;
2246
+ }
2247
+ seen.add(key);
2248
+ nextChildren.push(normalizedChild);
2249
+ }
2250
+ if (!changed && nextChildren.length === children.length) return node;
2251
+ return tree.withChildren(node, nextChildren);
2252
+ };
2253
+ return visit(root);
2254
+ }
2255
+
2256
+ exports.CodecError = CodecError;
2257
+ exports.JSONCodec = JSONCodec;
2258
+ exports.MnemonicProvider = MnemonicProvider;
2259
+ exports.SchemaError = SchemaError;
2260
+ exports.compileSchema = compileSchema;
2261
+ exports.createCodec = createCodec;
2262
+ exports.createSchemaRegistry = createSchemaRegistry;
2263
+ exports.dedupeChildrenBy = dedupeChildrenBy;
2264
+ exports.defineKeySchema = defineKeySchema;
2265
+ exports.defineMigration = defineMigration;
2266
+ exports.defineMnemonicKey = defineMnemonicKey;
2267
+ exports.defineWriteMigration = defineWriteMigration;
2268
+ exports.findNodeById = findNodeById;
2269
+ exports.insertChildIfMissing = insertChildIfMissing;
2270
+ exports.mnemonicSchema = mnemonicSchema;
2271
+ exports.renameNode = renameNode;
2272
+ exports.useMnemonicKey = useMnemonicKey;
2273
+ exports.useMnemonicRecovery = useMnemonicRecovery;
2274
+ exports.validateJsonSchema = validateJsonSchema;
2275
+ //# sourceMappingURL=schema.cjs.map
2276
+ //# sourceMappingURL=schema.cjs.map