gjendje 1.3.0 → 1.3.2
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/{chunk-ARCB4TVB.cjs → chunk-3YGK3425.cjs} +22 -1
- package/dist/{chunk-UEEINLOF.js → chunk-JRTZTDT6.js} +118 -20
- package/dist/{chunk-KVY36YX4.cjs → chunk-RB7FJ7WC.cjs} +183 -85
- package/dist/{chunk-FAISWCIZ.js → chunk-YPT6TO4H.js} +21 -2
- package/dist/devtools.cjs +6 -6
- package/dist/devtools.d.cts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +1 -1
- package/dist/index.cjs +50 -37
- package/dist/index.d.cts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +10 -5
- package/dist/react.cjs +20 -9
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +21 -10
- package/dist/server.cjs +5 -5
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/dist/{types-DmJrN5KI.d.cts → types-DzWE2M22.d.cts} +9 -0
- package/dist/{types-DmJrN5KI.d.ts → types-DzWE2M22.d.ts} +9 -0
- package/dist/vue.d.cts +1 -1
- package/dist/vue.d.ts +1 -1
- package/package.json +3 -2
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
var globalConfig = {};
|
|
5
5
|
var PERSISTENT_SCOPES = /* @__PURE__ */ new Set(["local", "session", "bucket"]);
|
|
6
6
|
function configure(config) {
|
|
7
|
-
|
|
7
|
+
for (const key of Object.keys(config)) {
|
|
8
|
+
const value = config[key];
|
|
9
|
+
if (value === void 0) {
|
|
10
|
+
delete globalConfig[key];
|
|
11
|
+
} else {
|
|
12
|
+
globalConfig[key] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
8
15
|
if (globalConfig.registry === false && globalConfig.scope && PERSISTENT_SCOPES.has(globalConfig.scope)) {
|
|
9
16
|
log(
|
|
10
17
|
"warn",
|
|
@@ -12,6 +19,9 @@ function configure(config) {
|
|
|
12
19
|
);
|
|
13
20
|
}
|
|
14
21
|
}
|
|
22
|
+
function resetConfig() {
|
|
23
|
+
globalConfig = {};
|
|
24
|
+
}
|
|
15
25
|
function getConfig() {
|
|
16
26
|
return globalConfig;
|
|
17
27
|
}
|
|
@@ -102,16 +112,27 @@ function unregisterByKey(rKey) {
|
|
|
102
112
|
function getRegistry() {
|
|
103
113
|
return registry;
|
|
104
114
|
}
|
|
115
|
+
function destroyAll() {
|
|
116
|
+
const instances = [...registry.values()];
|
|
117
|
+
for (const instance of instances) {
|
|
118
|
+
if (!instance.isDestroyed) {
|
|
119
|
+
instance.destroy();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
registry.clear();
|
|
123
|
+
}
|
|
105
124
|
|
|
106
125
|
exports.PERSISTENT_SCOPES = PERSISTENT_SCOPES;
|
|
107
126
|
exports.configure = configure;
|
|
108
127
|
exports.createListeners = createListeners;
|
|
128
|
+
exports.destroyAll = destroyAll;
|
|
109
129
|
exports.getConfig = getConfig;
|
|
110
130
|
exports.getRegistered = getRegistered;
|
|
111
131
|
exports.getRegistry = getRegistry;
|
|
112
132
|
exports.log = log;
|
|
113
133
|
exports.registerNew = registerNew;
|
|
114
134
|
exports.reportError = reportError;
|
|
135
|
+
exports.resetConfig = resetConfig;
|
|
115
136
|
exports.safeCall = safeCall;
|
|
116
137
|
exports.safeCallChange = safeCallChange;
|
|
117
138
|
exports.safeCallConfig = safeCallConfig;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { safeCall, getConfig, log, scopedKey, getRegistered, registerNew, PERSISTENT_SCOPES, reportError, safeCallConfig, safeCallChange, unregisterByKey, createListeners } from './chunk-
|
|
1
|
+
import { safeCall, getConfig, log, scopedKey, getRegistered, registerNew, PERSISTENT_SCOPES, reportError, safeCallConfig, safeCallChange, unregisterByKey, createListeners } from './chunk-YPT6TO4H.js';
|
|
2
2
|
|
|
3
3
|
// src/batch.ts
|
|
4
4
|
var depth = 0;
|
|
@@ -24,8 +24,27 @@ function notify(fn) {
|
|
|
24
24
|
}
|
|
25
25
|
fn();
|
|
26
26
|
}
|
|
27
|
+
var MAX_FLUSH_ITERATIONS = 100;
|
|
27
28
|
function flush() {
|
|
29
|
+
let iterations = 0;
|
|
28
30
|
while (queue.length > 0) {
|
|
31
|
+
if (++iterations > MAX_FLUSH_ITERATIONS) {
|
|
32
|
+
console.error(
|
|
33
|
+
"[gjendje] Batch flush exceeded maximum iterations \u2014 possible infinite loop. Delivering remaining notifications once before stopping."
|
|
34
|
+
);
|
|
35
|
+
const remaining = queue;
|
|
36
|
+
queue = [];
|
|
37
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
38
|
+
const fn = remaining[i];
|
|
39
|
+
try {
|
|
40
|
+
if (fn) fn();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error("[gjendje] Notification threw during best-effort delivery:", err);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
queue = [];
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
29
48
|
generation++;
|
|
30
49
|
const current = queue;
|
|
31
50
|
queue = [];
|
|
@@ -64,6 +83,28 @@ function shallowEqual(a, b) {
|
|
|
64
83
|
return true;
|
|
65
84
|
}
|
|
66
85
|
if (Array.isArray(b)) return false;
|
|
86
|
+
if (a instanceof Set) {
|
|
87
|
+
if (!(b instanceof Set)) return false;
|
|
88
|
+
if (a.size !== b.size) return false;
|
|
89
|
+
for (const item of a) {
|
|
90
|
+
if (!b.has(item)) return false;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (a instanceof Map) {
|
|
95
|
+
if (!(b instanceof Map)) return false;
|
|
96
|
+
if (a.size !== b.size) return false;
|
|
97
|
+
for (const [key, val] of a) {
|
|
98
|
+
if (!b.has(key) || !Object.is(val, b.get(key))) return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (a instanceof Date) {
|
|
103
|
+
return b instanceof Date && a.getTime() === b.getTime();
|
|
104
|
+
}
|
|
105
|
+
if (a instanceof RegExp) {
|
|
106
|
+
return b instanceof RegExp && a.toString() === b.toString();
|
|
107
|
+
}
|
|
67
108
|
const objA = a;
|
|
68
109
|
const objB = b;
|
|
69
110
|
const keysA = Object.keys(objA);
|
|
@@ -210,9 +251,9 @@ function notifyWatchers(watchers, prev, next) {
|
|
|
210
251
|
// src/persist.ts
|
|
211
252
|
function isVersionedValue(value) {
|
|
212
253
|
if (!isRecord(value)) return false;
|
|
213
|
-
return "v" in value && "data" in value && Number.isSafeInteger(value.v);
|
|
254
|
+
return Object.keys(value).length === 2 && "v" in value && "data" in value && Number.isSafeInteger(value.v);
|
|
214
255
|
}
|
|
215
|
-
function readAndMigrate(raw, options, key, scope) {
|
|
256
|
+
function readAndMigrate(raw, options, key, scope, onFallback) {
|
|
216
257
|
const currentVersion = options.version ?? 1;
|
|
217
258
|
const defaultValue = options.default;
|
|
218
259
|
try {
|
|
@@ -244,6 +285,7 @@ function readAndMigrate(raw, options, key, scope) {
|
|
|
244
285
|
const validationErr = new ValidationError(key, scope, data);
|
|
245
286
|
safeCallConfig(config.onError, { key, scope, error: validationErr });
|
|
246
287
|
}
|
|
288
|
+
onFallback?.();
|
|
247
289
|
return defaultValue;
|
|
248
290
|
}
|
|
249
291
|
return data;
|
|
@@ -253,6 +295,7 @@ function readAndMigrate(raw, options, key, scope) {
|
|
|
253
295
|
const readErr = new StorageReadError(key, scope, err);
|
|
254
296
|
safeCallConfig(getConfig().onError, { key, scope, error: readErr });
|
|
255
297
|
}
|
|
298
|
+
onFallback?.();
|
|
256
299
|
return defaultValue;
|
|
257
300
|
}
|
|
258
301
|
}
|
|
@@ -290,12 +333,12 @@ function runMigrations(data, fromVersion, toVersion, migrations, key, scope) {
|
|
|
290
333
|
try {
|
|
291
334
|
current = migrateFn(current);
|
|
292
335
|
} catch (err) {
|
|
293
|
-
log("warn", `Migration from v${v} failed \u2014
|
|
336
|
+
log("warn", `Migration from v${v} failed \u2014 discarding partially migrated data.`);
|
|
337
|
+
const migrationErr = new MigrationError(key ?? "", scope ?? "memory", v, toVersion, err);
|
|
294
338
|
if (key && scope) {
|
|
295
|
-
const migrationErr = new MigrationError(key, scope, v, toVersion, err);
|
|
296
339
|
safeCallConfig(getConfig().onError, { key, scope, error: migrationErr });
|
|
297
340
|
}
|
|
298
|
-
|
|
341
|
+
throw migrationErr;
|
|
299
342
|
}
|
|
300
343
|
}
|
|
301
344
|
}
|
|
@@ -309,11 +352,40 @@ function createStorageAdapter(storage, key, options) {
|
|
|
309
352
|
let cachedRaw;
|
|
310
353
|
let cachedValue;
|
|
311
354
|
let cacheValid = false;
|
|
355
|
+
const backupKey = `${key}:__gjendje_backup`;
|
|
356
|
+
function backupRawData(raw) {
|
|
357
|
+
try {
|
|
358
|
+
if (storage.getItem(backupKey) === null) {
|
|
359
|
+
storage.setItem(backupKey, raw);
|
|
360
|
+
log(
|
|
361
|
+
"warn",
|
|
362
|
+
`Original data for key "${key}" backed up to "${backupKey}" after migration/validation failure.`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
} catch (backupErr) {
|
|
366
|
+
const scope = options.scope ?? "local";
|
|
367
|
+
log(
|
|
368
|
+
"error",
|
|
369
|
+
`Failed to backup data for key "${key}" to "${backupKey}" \u2014 original data may be lost.`
|
|
370
|
+
);
|
|
371
|
+
reportError(key, scope, backupErr);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
312
374
|
function parse(raw) {
|
|
313
375
|
if (serialize) {
|
|
314
|
-
|
|
376
|
+
const value = serialize.parse(raw);
|
|
377
|
+
if (options.validate && !options.validate(value)) {
|
|
378
|
+
const scope = options.scope ?? "local";
|
|
379
|
+
const config = getConfig();
|
|
380
|
+
safeCallConfig(config.onValidationFail, { key, scope, value });
|
|
381
|
+
const validationErr = new ValidationError(key, scope, value);
|
|
382
|
+
safeCallConfig(config.onError, { key, scope, error: validationErr });
|
|
383
|
+
backupRawData(raw);
|
|
384
|
+
return defaultValue;
|
|
385
|
+
}
|
|
386
|
+
return value;
|
|
315
387
|
}
|
|
316
|
-
return readAndMigrate(raw, options, key, options.scope);
|
|
388
|
+
return readAndMigrate(raw, options, key, options.scope, () => backupRawData(raw));
|
|
317
389
|
}
|
|
318
390
|
function read() {
|
|
319
391
|
if (cacheValid) return cachedValue;
|
|
@@ -367,6 +439,7 @@ function createStorageAdapter(storage, key, options) {
|
|
|
367
439
|
safeCallConfig(getConfig().onQuotaExceeded, { key, scope, error: writeErr });
|
|
368
440
|
}
|
|
369
441
|
reportError(key, scope, writeErr);
|
|
442
|
+
throw writeErr;
|
|
370
443
|
}
|
|
371
444
|
}
|
|
372
445
|
let lastNotifiedValue = defaultValue;
|
|
@@ -452,6 +525,7 @@ function createBucketAdapter(key, bucketOptions, options) {
|
|
|
452
525
|
let delegateUnsub;
|
|
453
526
|
const ready = (async () => {
|
|
454
527
|
if (!isBucketSupported()) return;
|
|
528
|
+
let hadUserWrite = false;
|
|
455
529
|
try {
|
|
456
530
|
const openOptions = {
|
|
457
531
|
persisted: bucketOptions.persisted ?? false,
|
|
@@ -485,7 +559,7 @@ function createBucketAdapter(key, bucketOptions, options) {
|
|
|
485
559
|
const storage = await bucket.localStorage();
|
|
486
560
|
if (isDestroyed) return;
|
|
487
561
|
const currentValue = delegate.get();
|
|
488
|
-
|
|
562
|
+
hadUserWrite = !shallowEqual(currentValue, defaultValue);
|
|
489
563
|
delegate.destroy?.();
|
|
490
564
|
delegate = createStorageAdapter(storage, key, options);
|
|
491
565
|
if (isDestroyed) {
|
|
@@ -507,10 +581,12 @@ function createBucketAdapter(key, bucketOptions, options) {
|
|
|
507
581
|
reportError(key, "bucket", err);
|
|
508
582
|
}
|
|
509
583
|
if (isDestroyed) return;
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
584
|
+
if (!hadUserWrite) {
|
|
585
|
+
const storedValue = delegate.get();
|
|
586
|
+
if (!shallowEqual(storedValue, defaultValue)) {
|
|
587
|
+
lastNotifiedValue = storedValue;
|
|
588
|
+
notify(notifyListeners);
|
|
589
|
+
}
|
|
514
590
|
}
|
|
515
591
|
delegateUnsub = delegate.subscribe((value) => {
|
|
516
592
|
lastNotifiedValue = value;
|
|
@@ -646,7 +722,7 @@ function withSync(adapter, key, scope) {
|
|
|
646
722
|
}
|
|
647
723
|
|
|
648
724
|
// src/adapters/url.ts
|
|
649
|
-
function createUrlAdapter(key, defaultValue, serializer, persist) {
|
|
725
|
+
function createUrlAdapter(key, defaultValue, serializer, persist, urlReplace) {
|
|
650
726
|
if (typeof window === "undefined") {
|
|
651
727
|
throw new Error("[gjendje] URL scope is not available in this environment.");
|
|
652
728
|
}
|
|
@@ -686,11 +762,19 @@ function createUrlAdapter(key, defaultValue, serializer, persist) {
|
|
|
686
762
|
}
|
|
687
763
|
const search = params.toString();
|
|
688
764
|
const newUrl = search ? `${window.location.pathname}?${search}${window.location.hash}` : `${window.location.pathname}${window.location.hash}`;
|
|
689
|
-
|
|
765
|
+
if (urlReplace) {
|
|
766
|
+
window.history.replaceState(null, "", newUrl);
|
|
767
|
+
} else {
|
|
768
|
+
window.history.pushState(null, "", newUrl);
|
|
769
|
+
}
|
|
690
770
|
cachedSearch = search ? `?${search}` : "";
|
|
691
771
|
cachedValue = persist ? mergeKeys(toStore, defaultValue, persist) : value;
|
|
692
|
-
} catch {
|
|
772
|
+
} catch (e) {
|
|
693
773
|
cachedSearch = void 0;
|
|
774
|
+
const writeErr = new StorageWriteError(key, "url", e);
|
|
775
|
+
log("error", writeErr.message);
|
|
776
|
+
reportError(key, "url", writeErr);
|
|
777
|
+
throw writeErr;
|
|
694
778
|
}
|
|
695
779
|
}
|
|
696
780
|
let lastNotifiedValue = defaultValue;
|
|
@@ -775,7 +859,8 @@ function resolveAdapter(storageKey, scope, options) {
|
|
|
775
859
|
storageKey,
|
|
776
860
|
options.default,
|
|
777
861
|
options.serialize ?? { stringify: JSON.stringify, parse: JSON.parse },
|
|
778
|
-
options.persist
|
|
862
|
+
options.persist,
|
|
863
|
+
options.urlReplace
|
|
779
864
|
);
|
|
780
865
|
case "server":
|
|
781
866
|
if (!_serverAdapterFactory) {
|
|
@@ -818,6 +903,7 @@ var StateImpl = class {
|
|
|
818
903
|
this._s = preallocatedState ?? {
|
|
819
904
|
lastValue: adapter.get(),
|
|
820
905
|
isDestroyed: false,
|
|
906
|
+
hasUserWrite: false,
|
|
821
907
|
interceptors: void 0,
|
|
822
908
|
changeHandlers: void 0,
|
|
823
909
|
settled: RESOLVED,
|
|
@@ -878,8 +964,14 @@ var StateImpl = class {
|
|
|
878
964
|
let next = typeof valueOrUpdater === "function" ? valueOrUpdater(prev) : valueOrUpdater;
|
|
879
965
|
next = this._applyInterceptors(next, prev);
|
|
880
966
|
if (this._options.isEqual?.(next, prev)) return;
|
|
967
|
+
try {
|
|
968
|
+
this._adapter.set(next);
|
|
969
|
+
} catch (err) {
|
|
970
|
+
if (err instanceof StorageWriteError) return;
|
|
971
|
+
throw err;
|
|
972
|
+
}
|
|
881
973
|
s.lastValue = next;
|
|
882
|
-
|
|
974
|
+
s.hasUserWrite = true;
|
|
883
975
|
s.settled = this._adapter.ready;
|
|
884
976
|
this._notifyChange(next, prev);
|
|
885
977
|
}
|
|
@@ -892,8 +984,14 @@ var StateImpl = class {
|
|
|
892
984
|
const prev = this._adapter.get();
|
|
893
985
|
const next = this._applyInterceptors(this._defaultValue, prev);
|
|
894
986
|
if (this._options.isEqual?.(next, prev)) return;
|
|
987
|
+
try {
|
|
988
|
+
this._adapter.set(next);
|
|
989
|
+
} catch (err) {
|
|
990
|
+
if (err instanceof StorageWriteError) return;
|
|
991
|
+
throw err;
|
|
992
|
+
}
|
|
895
993
|
s.lastValue = next;
|
|
896
|
-
|
|
994
|
+
s.hasUserWrite = true;
|
|
897
995
|
s.settled = this._adapter.ready;
|
|
898
996
|
safeCallConfig(this._config.onReset, { key: this.key, scope: this.scope, previousValue: prev });
|
|
899
997
|
this._notifyChange(next, prev);
|
|
@@ -1298,7 +1396,7 @@ function createBase(key, options) {
|
|
|
1298
1396
|
const instance = new StateImpl(key, scope, rKey, adapter, options, config);
|
|
1299
1397
|
if (isSsrMode && !isServer()) {
|
|
1300
1398
|
instance._s.hydrated = afterHydration(() => {
|
|
1301
|
-
if (instance.isDestroyed) return;
|
|
1399
|
+
if (instance.isDestroyed || instance._s.hasUserWrite) return;
|
|
1302
1400
|
const currentValue = instance.get();
|
|
1303
1401
|
if (!shallowEqual(currentValue, options.default)) return;
|
|
1304
1402
|
let realAdapter;
|