react-mnemonic 1.2.1-beta1.0 → 1.4.0

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