tiny-essentials 1.19.1 → 1.19.3
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/v1/TinyEssentials.js +178 -30
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyLocalStorage.js +179 -31
- package/dist/v1/TinyLocalStorage.min.js +1 -1
- package/dist/v1/TinyUploadClicker.js +178 -30
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/libs/TinyLocalStorage.cjs +178 -30
- package/dist/v1/libs/TinyLocalStorage.d.mts +49 -7
- package/dist/v1/libs/TinyLocalStorage.mjs +166 -36
- package/docs/v1/libs/TinyLocalStorage.md +76 -4
- package/package.json +1 -1
|
@@ -4,6 +4,21 @@ import TinyEvents from './TinyEvents.mjs';
|
|
|
4
4
|
const customEncoders = new Map();
|
|
5
5
|
/** @type {Map<any, DecodeFn>} */
|
|
6
6
|
const customDecoders = new Map();
|
|
7
|
+
/** @type {Set<any>} */
|
|
8
|
+
const customTypesFreezed = new Set([
|
|
9
|
+
'string',
|
|
10
|
+
'boolean',
|
|
11
|
+
'number',
|
|
12
|
+
'object',
|
|
13
|
+
'array',
|
|
14
|
+
String,
|
|
15
|
+
Boolean,
|
|
16
|
+
Number,
|
|
17
|
+
Object,
|
|
18
|
+
Array,
|
|
19
|
+
BigInt,
|
|
20
|
+
Symbol,
|
|
21
|
+
]);
|
|
7
22
|
/**
|
|
8
23
|
* A function that encodes a value into a serializable JSON-compatible format.
|
|
9
24
|
*
|
|
@@ -243,25 +258,50 @@ class TinyLocalStorage {
|
|
|
243
258
|
return this.#events.getMaxListeners();
|
|
244
259
|
}
|
|
245
260
|
///////////////////////////////////////////////////
|
|
261
|
+
/**
|
|
262
|
+
* Checks whether a JSON-serializable type is already registered.
|
|
263
|
+
*
|
|
264
|
+
* @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
|
|
265
|
+
* or a reference to a class/constructor function.
|
|
266
|
+
* @returns {boolean} True if the type has both an encoder and decoder registered.
|
|
267
|
+
*/
|
|
268
|
+
static hasJsonType(type) {
|
|
269
|
+
return (customEncoders.has(type) && customDecoders.has(type)) || customTypesFreezed.has(type);
|
|
270
|
+
}
|
|
246
271
|
/**
|
|
247
272
|
* Registers a new JSON-serializable type with its encoder and decoder.
|
|
248
273
|
*
|
|
249
|
-
* @param {any} type - The type
|
|
250
|
-
*
|
|
251
|
-
* @param {
|
|
274
|
+
* @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
|
|
275
|
+
* or a reference to a class/constructor function.
|
|
276
|
+
* @param {EncodeFn} encodeFn - A function that receives a value of this type and returns a JSON-safe representation.
|
|
277
|
+
* @param {DecodeFn} decodeFn - An object with `check(value)` to identify this type and `decode(value)` to restore it.
|
|
278
|
+
* @param {boolean} [freezeType=false] - If true, prevents this type from being unregistered later.
|
|
279
|
+
* @throws {Error} Throws an error if the type is frozen and cannot be registered.
|
|
252
280
|
*/
|
|
253
|
-
static registerJsonType(type, encodeFn, decodeFn) {
|
|
281
|
+
static registerJsonType(type, encodeFn, decodeFn, freezeType = false) {
|
|
282
|
+
if (customTypesFreezed.has(type))
|
|
283
|
+
throw new Error(`Cannot register type "${type}" because it is frozen.`);
|
|
254
284
|
customEncoders.set(type, encodeFn);
|
|
255
285
|
customDecoders.set(type, decodeFn);
|
|
286
|
+
if (freezeType)
|
|
287
|
+
customTypesFreezed.add(type);
|
|
256
288
|
}
|
|
257
289
|
/**
|
|
258
|
-
*
|
|
290
|
+
* Unregisters a previously registered custom type from the encoding/decoding system.
|
|
259
291
|
*
|
|
260
|
-
* @param {
|
|
292
|
+
* @param {any} type - The primitive name or constructor reference used in the registration.
|
|
293
|
+
* @returns {boolean} Returns true if the type existed and was removed, or false if it wasn't found.
|
|
294
|
+
* @throws {Error} Throws an error if the type is frozen and cannot be unregistered.
|
|
261
295
|
*/
|
|
262
296
|
static deleteJsonType(type) {
|
|
263
|
-
|
|
264
|
-
|
|
297
|
+
if (customTypesFreezed.has(type))
|
|
298
|
+
throw new Error(`Cannot remove type "${type}" because it is frozen.`);
|
|
299
|
+
let isDeleted = false;
|
|
300
|
+
if (customEncoders.delete(type))
|
|
301
|
+
isDeleted = true;
|
|
302
|
+
if (customDecoders.delete(type))
|
|
303
|
+
isDeleted = true;
|
|
304
|
+
return isDeleted;
|
|
265
305
|
}
|
|
266
306
|
//////////////////////////////////////////////////////
|
|
267
307
|
/**
|
|
@@ -335,16 +375,83 @@ class TinyLocalStorage {
|
|
|
335
375
|
//////////////////////////////////////////////////////
|
|
336
376
|
/** @type {Storage} */
|
|
337
377
|
#localStorage = window.localStorage;
|
|
378
|
+
/** @type {string|null} */
|
|
379
|
+
#dbKey = null;
|
|
380
|
+
/** @type {number} */
|
|
381
|
+
#version = 0;
|
|
338
382
|
/** @type {(ev: StorageEvent) => any} */
|
|
339
383
|
#storageEvent = (ev) => this.emit('storage', ev);
|
|
340
384
|
/**
|
|
341
385
|
* Initializes the TinyLocalStorage instance and sets up cross-tab sync.
|
|
342
386
|
*
|
|
343
387
|
* Adds listener for the native `storage` event to support tab synchronization.
|
|
388
|
+
* @param {string} [dbName] - Unique database name.
|
|
344
389
|
*/
|
|
345
|
-
constructor() {
|
|
390
|
+
constructor(dbName) {
|
|
391
|
+
if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
|
|
392
|
+
throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
|
|
393
|
+
if (typeof dbName === 'string')
|
|
394
|
+
this.#dbKey = `LSDB::${dbName}`;
|
|
346
395
|
window.addEventListener('storage', this.#storageEvent);
|
|
347
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Validates that a given key does not conflict with internal database keys.
|
|
399
|
+
*
|
|
400
|
+
* This method is used to prevent accidental overwriting of reserved `LSDB::` keys
|
|
401
|
+
* in `localStorage`, which are used internally by TinyLocalStorage for versioning
|
|
402
|
+
* and data management.
|
|
403
|
+
*
|
|
404
|
+
* @param {string} name - The key to validate before writing to localStorage.
|
|
405
|
+
* @throws {Error} If the key starts with `LSDB::`.
|
|
406
|
+
*/
|
|
407
|
+
#isProtectedDbKey(name) {
|
|
408
|
+
if (typeof name === 'string' && name.startsWith('LSDB::'))
|
|
409
|
+
throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Updates the version of the storage and triggers migration if needed.
|
|
413
|
+
*
|
|
414
|
+
* @param {number} version - Desired version of the database.
|
|
415
|
+
* @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
|
|
416
|
+
* @throws {Error} If the database key has not been initialized.
|
|
417
|
+
* @throws {TypeError} If `version` is not a valid positive number.
|
|
418
|
+
* @throws {TypeError} If `onUpgrade` is not a function.
|
|
419
|
+
*/
|
|
420
|
+
updateStorageVersion(version, onUpgrade) {
|
|
421
|
+
if (typeof this.#dbKey !== 'string')
|
|
422
|
+
throw new Error('TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.');
|
|
423
|
+
if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
|
|
424
|
+
throw new TypeError('TinyLocalStorage: version must be a positive number.');
|
|
425
|
+
if (typeof onUpgrade !== 'function')
|
|
426
|
+
throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
|
|
427
|
+
// @ts-ignore
|
|
428
|
+
const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
|
|
429
|
+
if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
|
|
430
|
+
throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
|
|
431
|
+
if (version < savedVersion)
|
|
432
|
+
throw new Error(`TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`);
|
|
433
|
+
if (version > savedVersion) {
|
|
434
|
+
onUpgrade(savedVersion, version);
|
|
435
|
+
localStorage.setItem(this.#dbKey, String(version));
|
|
436
|
+
this.#version = version;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Gets the current database key used in localStorage.
|
|
441
|
+
*
|
|
442
|
+
* @returns {string|null} The database key, or null if not set.
|
|
443
|
+
*/
|
|
444
|
+
getDbKey() {
|
|
445
|
+
return this.#dbKey;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Gets the current version of the database.
|
|
449
|
+
*
|
|
450
|
+
* @returns {number} The current version number.
|
|
451
|
+
*/
|
|
452
|
+
getVersion() {
|
|
453
|
+
return this.#version;
|
|
454
|
+
}
|
|
348
455
|
/**
|
|
349
456
|
* Defines a custom storage interface (e.g. `sessionStorage`).
|
|
350
457
|
*
|
|
@@ -352,7 +459,7 @@ class TinyLocalStorage {
|
|
|
352
459
|
*/
|
|
353
460
|
setLocalStorage(localstorage) {
|
|
354
461
|
if (!(localstorage instanceof Storage))
|
|
355
|
-
throw new
|
|
462
|
+
throw new TypeError('Argument must be a valid instance of Storage.');
|
|
356
463
|
this.#localStorage = localstorage;
|
|
357
464
|
}
|
|
358
465
|
/**
|
|
@@ -372,7 +479,7 @@ class TinyLocalStorage {
|
|
|
372
479
|
*/
|
|
373
480
|
#setJson(name, data) {
|
|
374
481
|
if (typeof name !== 'string' || !name.length)
|
|
375
|
-
throw new
|
|
482
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
376
483
|
return TinyLocalStorage.encodeSpecialJson(data);
|
|
377
484
|
}
|
|
378
485
|
/**
|
|
@@ -384,11 +491,12 @@ class TinyLocalStorage {
|
|
|
384
491
|
* @param {LocalStorageJsonValue} data - The data to be serialized and stored.
|
|
385
492
|
*/
|
|
386
493
|
setJson(name, data) {
|
|
494
|
+
this.#isProtectedDbKey(name);
|
|
387
495
|
if (!isJsonObject(data) &&
|
|
388
496
|
!Array.isArray(data) &&
|
|
389
497
|
!(data instanceof Map) &&
|
|
390
498
|
!(data instanceof Set)) {
|
|
391
|
-
throw new
|
|
499
|
+
throw new TypeError('The storage value is not a valid JSON-compatible structure.');
|
|
392
500
|
}
|
|
393
501
|
const encoded = this.#setJson(name, data);
|
|
394
502
|
this.emit('setJson', name, data);
|
|
@@ -405,7 +513,7 @@ class TinyLocalStorage {
|
|
|
405
513
|
*/
|
|
406
514
|
#getJson(name, defaultData) {
|
|
407
515
|
if (typeof name !== 'string' || !name.length)
|
|
408
|
-
throw new
|
|
516
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
409
517
|
const raw = this.#localStorage.getItem(name);
|
|
410
518
|
const fallbackTypes = {
|
|
411
519
|
obj: () => ({}),
|
|
@@ -451,8 +559,9 @@ class TinyLocalStorage {
|
|
|
451
559
|
* @param {Date} data
|
|
452
560
|
*/
|
|
453
561
|
setDate(name, data) {
|
|
562
|
+
this.#isProtectedDbKey(name);
|
|
454
563
|
if (!(data instanceof Date))
|
|
455
|
-
throw new
|
|
564
|
+
throw new TypeError('Value must be a Date.');
|
|
456
565
|
const encoded = this.#setJson(name, data);
|
|
457
566
|
this.emit('setDate', name, data);
|
|
458
567
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -472,8 +581,9 @@ class TinyLocalStorage {
|
|
|
472
581
|
* @param {RegExp} data
|
|
473
582
|
*/
|
|
474
583
|
setRegExp(name, data) {
|
|
584
|
+
this.#isProtectedDbKey(name);
|
|
475
585
|
if (!(data instanceof RegExp))
|
|
476
|
-
throw new
|
|
586
|
+
throw new TypeError('Value must be a RegExp.');
|
|
477
587
|
const encoded = this.#setJson(name, data);
|
|
478
588
|
this.emit('setRegExp', name, data);
|
|
479
589
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -493,8 +603,9 @@ class TinyLocalStorage {
|
|
|
493
603
|
* @param {bigint} data
|
|
494
604
|
*/
|
|
495
605
|
setBigInt(name, data) {
|
|
606
|
+
this.#isProtectedDbKey(name);
|
|
496
607
|
if (typeof data !== 'bigint')
|
|
497
|
-
throw new
|
|
608
|
+
throw new TypeError('Value must be a BigInt.');
|
|
498
609
|
const encoded = this.#setJson(name, data);
|
|
499
610
|
this.emit('setBigInt', name, data);
|
|
500
611
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -515,8 +626,9 @@ class TinyLocalStorage {
|
|
|
515
626
|
* @param {symbol} data
|
|
516
627
|
*/
|
|
517
628
|
setSymbol(name, data) {
|
|
629
|
+
this.#isProtectedDbKey(name);
|
|
518
630
|
if (typeof data !== 'symbol')
|
|
519
|
-
throw new
|
|
631
|
+
throw new TypeError('Value must be a Symbol.');
|
|
520
632
|
const encoded = this.#setJson(name, data);
|
|
521
633
|
this.emit('setSymbol', name, data);
|
|
522
634
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -546,8 +658,9 @@ class TinyLocalStorage {
|
|
|
546
658
|
* @param {any} data - The data to store.
|
|
547
659
|
*/
|
|
548
660
|
setItem(name, data) {
|
|
661
|
+
this.#isProtectedDbKey(name);
|
|
549
662
|
if (typeof name !== 'string' || !name.length)
|
|
550
|
-
throw new
|
|
663
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
551
664
|
this.emit('setItem', name, data);
|
|
552
665
|
return this.#localStorage.setItem(name, data);
|
|
553
666
|
}
|
|
@@ -559,7 +672,7 @@ class TinyLocalStorage {
|
|
|
559
672
|
*/
|
|
560
673
|
getItem(name) {
|
|
561
674
|
if (typeof name !== 'string' || !name.length)
|
|
562
|
-
throw new
|
|
675
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
563
676
|
return this.#localStorage.getItem(name);
|
|
564
677
|
}
|
|
565
678
|
/**
|
|
@@ -569,12 +682,16 @@ class TinyLocalStorage {
|
|
|
569
682
|
* @param {string} data - The string to store.
|
|
570
683
|
*/
|
|
571
684
|
setString(name, data) {
|
|
685
|
+
this.#isProtectedDbKey(name);
|
|
572
686
|
if (typeof name !== 'string' || !name.length)
|
|
573
|
-
throw new
|
|
687
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
574
688
|
if (typeof data !== 'string')
|
|
575
|
-
throw new
|
|
689
|
+
throw new TypeError('Value must be a string.');
|
|
576
690
|
this.emit('setString', name, data);
|
|
577
|
-
return this.#localStorage.setItem(name,
|
|
691
|
+
return this.#localStorage.setItem(name, JSON.stringify({
|
|
692
|
+
__string__: true,
|
|
693
|
+
value: data,
|
|
694
|
+
}));
|
|
578
695
|
}
|
|
579
696
|
/**
|
|
580
697
|
* Retrieves a string value from `localStorage`.
|
|
@@ -584,8 +701,19 @@ class TinyLocalStorage {
|
|
|
584
701
|
*/
|
|
585
702
|
getString(name) {
|
|
586
703
|
if (typeof name !== 'string' || !name.length)
|
|
587
|
-
throw new
|
|
704
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
588
705
|
let value = this.#localStorage.getItem(name);
|
|
706
|
+
try {
|
|
707
|
+
/** @type {{ value: string; __string__: boolean }} */
|
|
708
|
+
// @ts-ignore
|
|
709
|
+
value = JSON.parse(value);
|
|
710
|
+
if (!isJsonObject(value) || !value.__string__ || typeof value.value !== 'string')
|
|
711
|
+
return null;
|
|
712
|
+
value = value.value;
|
|
713
|
+
}
|
|
714
|
+
catch {
|
|
715
|
+
value = null;
|
|
716
|
+
}
|
|
589
717
|
if (typeof value === 'string')
|
|
590
718
|
return value;
|
|
591
719
|
return null;
|
|
@@ -597,10 +725,11 @@ class TinyLocalStorage {
|
|
|
597
725
|
* @param {number} data - The number to store.
|
|
598
726
|
*/
|
|
599
727
|
setNumber(name, data) {
|
|
728
|
+
this.#isProtectedDbKey(name);
|
|
600
729
|
if (typeof name !== 'string' || !name.length)
|
|
601
|
-
throw new
|
|
730
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
602
731
|
if (typeof data !== 'number')
|
|
603
|
-
throw new
|
|
732
|
+
throw new TypeError('Value must be a number.');
|
|
604
733
|
this.emit('setNumber', name, data);
|
|
605
734
|
return this.#localStorage.setItem(name, String(data));
|
|
606
735
|
}
|
|
@@ -612,7 +741,7 @@ class TinyLocalStorage {
|
|
|
612
741
|
*/
|
|
613
742
|
getNumber(name) {
|
|
614
743
|
if (typeof name !== 'string' || !name.length)
|
|
615
|
-
throw new
|
|
744
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
616
745
|
/** @type {number|string|null} */
|
|
617
746
|
let number = this.#localStorage.getItem(name);
|
|
618
747
|
if (typeof number === 'number')
|
|
@@ -631,10 +760,11 @@ class TinyLocalStorage {
|
|
|
631
760
|
* @param {boolean} data - The boolean value to store.
|
|
632
761
|
*/
|
|
633
762
|
setBool(name, data) {
|
|
763
|
+
this.#isProtectedDbKey(name);
|
|
634
764
|
if (typeof name !== 'string' || !name.length)
|
|
635
|
-
throw new
|
|
765
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
636
766
|
if (typeof data !== 'boolean')
|
|
637
|
-
throw new
|
|
767
|
+
throw new TypeError('Value must be a boolean.');
|
|
638
768
|
this.emit('setBool', name, data);
|
|
639
769
|
return this.#localStorage.setItem(name, String(data));
|
|
640
770
|
}
|
|
@@ -646,7 +776,7 @@ class TinyLocalStorage {
|
|
|
646
776
|
*/
|
|
647
777
|
getBool(name) {
|
|
648
778
|
if (typeof name !== 'string' || !name.length)
|
|
649
|
-
throw new
|
|
779
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
650
780
|
const value = this.#localStorage.getItem(name);
|
|
651
781
|
if (typeof value === 'boolean')
|
|
652
782
|
return value;
|
|
@@ -665,7 +795,7 @@ class TinyLocalStorage {
|
|
|
665
795
|
*/
|
|
666
796
|
removeItem(name) {
|
|
667
797
|
if (typeof name !== 'string' || !name.length)
|
|
668
|
-
throw new
|
|
798
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
669
799
|
this.emit('removeItem', name);
|
|
670
800
|
return this.#localStorage.removeItem(name);
|
|
671
801
|
}
|
|
@@ -692,7 +822,7 @@ TinyLocalStorage.registerJsonType(Map, (value, encodeSpecialJson) => ({
|
|
|
692
822
|
check: (value) => value.__map__,
|
|
693
823
|
/** @param {{ data: any[] }} value */
|
|
694
824
|
decode: (value, decodeSpecialJson) => new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
|
|
695
|
-
});
|
|
825
|
+
}, true);
|
|
696
826
|
// Set
|
|
697
827
|
TinyLocalStorage.registerJsonType(Set, (value, encodeSpecialJson) => ({
|
|
698
828
|
__set__: true,
|
|
@@ -700,7 +830,7 @@ TinyLocalStorage.registerJsonType(Set, (value, encodeSpecialJson) => ({
|
|
|
700
830
|
}), {
|
|
701
831
|
check: (value) => value.__set__,
|
|
702
832
|
decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
|
|
703
|
-
});
|
|
833
|
+
}, true);
|
|
704
834
|
// Date
|
|
705
835
|
TinyLocalStorage.registerJsonType(Date, (value) => ({
|
|
706
836
|
__date__: true,
|
|
@@ -708,7 +838,7 @@ TinyLocalStorage.registerJsonType(Date, (value) => ({
|
|
|
708
838
|
}), {
|
|
709
839
|
check: (value) => value.__date__,
|
|
710
840
|
decode: (value) => new Date(value.value),
|
|
711
|
-
});
|
|
841
|
+
}, true);
|
|
712
842
|
// Regex
|
|
713
843
|
TinyLocalStorage.registerJsonType(RegExp, (value) => ({
|
|
714
844
|
__regexp__: true,
|
|
@@ -717,7 +847,7 @@ TinyLocalStorage.registerJsonType(RegExp, (value) => ({
|
|
|
717
847
|
}), {
|
|
718
848
|
check: (value) => value.__regexp__,
|
|
719
849
|
decode: (value) => new RegExp(value.source, value.flags),
|
|
720
|
-
});
|
|
850
|
+
}, true);
|
|
721
851
|
// Big Int
|
|
722
852
|
TinyLocalStorage.registerJsonType('bigint', (value) => ({
|
|
723
853
|
__bigint__: true,
|
|
@@ -725,7 +855,7 @@ TinyLocalStorage.registerJsonType('bigint', (value) => ({
|
|
|
725
855
|
}), {
|
|
726
856
|
check: (value) => value.__bigint__,
|
|
727
857
|
decode: (value) => BigInt(value.value),
|
|
728
|
-
});
|
|
858
|
+
}, true);
|
|
729
859
|
// Symbol
|
|
730
860
|
TinyLocalStorage.registerJsonType('symbol', (value) => ({
|
|
731
861
|
__symbol__: true,
|
|
@@ -736,5 +866,5 @@ TinyLocalStorage.registerJsonType('symbol', (value) => ({
|
|
|
736
866
|
const key = value.key;
|
|
737
867
|
return key != null ? Symbol.for(key) : Symbol();
|
|
738
868
|
},
|
|
739
|
-
});
|
|
869
|
+
}, true);
|
|
740
870
|
export default TinyLocalStorage;
|
|
@@ -32,6 +32,59 @@ console.log(storage.getDate('today') instanceof Date); // true
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
## 🏁 `constructor(dbName?: string)`
|
|
36
|
+
|
|
37
|
+
Initializes the `TinyLocalStorage` instance and sets up cross-tab synchronization.
|
|
38
|
+
|
|
39
|
+
### 🔧 Parameters
|
|
40
|
+
|
|
41
|
+
* `dbName` (optional, `string`) – A unique name for the database. It becomes the base key used internally in `localStorage`.
|
|
42
|
+
|
|
43
|
+
### 📡 Behavior
|
|
44
|
+
|
|
45
|
+
* Automatically adds a `storage` event listener to support syncing across browser tabs.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🔁 `updateStorageVersion(version: number, onUpgrade: (oldVersion: number, newVersion: number) => void)`
|
|
50
|
+
|
|
51
|
+
Updates the version of the database. If the new version is higher than the current one, it triggers the provided migration callback.
|
|
52
|
+
|
|
53
|
+
### 🔧 Parameters
|
|
54
|
+
|
|
55
|
+
* `version` (`number`) – The desired new version of the database. Must be a positive integer.
|
|
56
|
+
* `onUpgrade` (`function`) – A callback function executed during upgrade. Receives `oldVersion` and `newVersion` as arguments.
|
|
57
|
+
|
|
58
|
+
### ⚠️ Throws
|
|
59
|
+
|
|
60
|
+
* `Error` – If the database key hasn't been initialized via the constructor.
|
|
61
|
+
* `TypeError` – If `version` is invalid (not a number, NaN, or < 1).
|
|
62
|
+
* `TypeError` – If `onUpgrade` is not a function.
|
|
63
|
+
* `TypeError` – If the saved version in `localStorage` is invalid.
|
|
64
|
+
* `Error` – If the provided `version` is lower than the stored version (downgrade is not supported).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🔑 `getDbKey(): string | null`
|
|
69
|
+
|
|
70
|
+
Returns the current internal database key used in `localStorage`.
|
|
71
|
+
|
|
72
|
+
### 🔙 Returns
|
|
73
|
+
|
|
74
|
+
* `string | null` – The full storage key prefix (e.g. `LSDB::yourDbName`), or `null` if uninitialized.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🧮 `getVersion(): number`
|
|
79
|
+
|
|
80
|
+
Returns the current active version of the storage system.
|
|
81
|
+
|
|
82
|
+
### 🔙 Returns
|
|
83
|
+
|
|
84
|
+
* `number` – The version number currently in use.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
35
88
|
## 📦 Storage Methods
|
|
36
89
|
|
|
37
90
|
### `setJson(key, data)`
|
|
@@ -243,7 +296,7 @@ Returns `true` if the browser supports the configured storage backend.
|
|
|
243
296
|
|
|
244
297
|
## 🧩 Custom Type Registration
|
|
245
298
|
|
|
246
|
-
### `registerJsonType(type, encodeFn, decodeFn)`
|
|
299
|
+
### `registerJsonType(type, encodeFn, decodeFn, freezeType?)`
|
|
247
300
|
|
|
248
301
|
Registers support for custom types with encoder/decoder logic.
|
|
249
302
|
|
|
@@ -254,10 +307,15 @@ registerJsonType(
|
|
|
254
307
|
decodeFn: {
|
|
255
308
|
check: (value: any) => boolean,
|
|
256
309
|
decode: (value: any, decodeSpecialJson: DecodeFn) => any
|
|
257
|
-
}
|
|
310
|
+
},
|
|
311
|
+
freezeType?: boolean // default: false
|
|
258
312
|
): void
|
|
259
313
|
```
|
|
260
314
|
|
|
315
|
+
Registers a custom type by associating it with encoder and decoder logic. If `freezeType` is set to `true`, the type becomes immutable and cannot be unregistered or overwritten later.
|
|
316
|
+
|
|
317
|
+
> ⚠️ Throws an error if trying to register a type that is already frozen.
|
|
318
|
+
|
|
261
319
|
---
|
|
262
320
|
|
|
263
321
|
### `deleteJsonType(type)`
|
|
@@ -265,10 +323,24 @@ registerJsonType(
|
|
|
265
323
|
Unregisters a previously registered custom type.
|
|
266
324
|
|
|
267
325
|
```ts
|
|
268
|
-
deleteJsonType(type: any):
|
|
326
|
+
deleteJsonType(type: any): boolean
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Removes the encoder and decoder associated with the given type. Returns `true` if the type was found and removed, or `false` if it was not registered.
|
|
330
|
+
|
|
331
|
+
> ❌ Throws an error if the type was marked as frozen via `freezeType`.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### `hasJsonType(type)`
|
|
336
|
+
|
|
337
|
+
Checks whether a type is currently registered for custom encoding/decoding.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
hasJsonType(type: any): boolean
|
|
269
341
|
```
|
|
270
342
|
|
|
271
|
-
|
|
343
|
+
Returns `true` if the type has both encoder and decoder registered, otherwise `false`.
|
|
272
344
|
|
|
273
345
|
---
|
|
274
346
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-essentials",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.3",
|
|
4
4
|
"description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "npm run test:mjs && npm run test:cjs && npm run test:js",
|