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