tiny-essentials 1.19.2 → 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.
@@ -421,6 +421,12 @@ class TinyLocalStorage {
421
421
  /** @type {Storage} */
422
422
  #localStorage = window.localStorage;
423
423
 
424
+ /** @type {string|null} */
425
+ #dbKey = null;
426
+
427
+ /** @type {number} */
428
+ #version = 0;
429
+
424
430
  /** @type {(ev: StorageEvent) => any} */
425
431
  #storageEvent = (ev) => this.emit('storage', ev);
426
432
 
@@ -428,11 +434,84 @@ class TinyLocalStorage {
428
434
  * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
429
435
  *
430
436
  * Adds listener for the native `storage` event to support tab synchronization.
437
+ * @param {string} [dbName] - Unique database name.
431
438
  */
432
- constructor() {
439
+ constructor(dbName) {
440
+ if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
441
+ throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
442
+ if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
433
443
  window.addEventListener('storage', this.#storageEvent);
434
444
  }
435
445
 
446
+ /**
447
+ * Validates that a given key does not conflict with internal database keys.
448
+ *
449
+ * This method is used to prevent accidental overwriting of reserved `LSDB::` keys
450
+ * in `localStorage`, which are used internally by TinyLocalStorage for versioning
451
+ * and data management.
452
+ *
453
+ * @param {string} name - The key to validate before writing to localStorage.
454
+ * @throws {Error} If the key starts with `LSDB::`.
455
+ */
456
+ #isProtectedDbKey(name) {
457
+ if (typeof name === 'string' && name.startsWith('LSDB::'))
458
+ throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
459
+ }
460
+
461
+ /**
462
+ * Updates the version of the storage and triggers migration if needed.
463
+ *
464
+ * @param {number} version - Desired version of the database.
465
+ * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
466
+ * @throws {Error} If the database key has not been initialized.
467
+ * @throws {TypeError} If `version` is not a valid positive number.
468
+ * @throws {TypeError} If `onUpgrade` is not a function.
469
+ */
470
+ updateStorageVersion(version, onUpgrade) {
471
+ if (typeof this.#dbKey !== 'string')
472
+ throw new Error(
473
+ 'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
474
+ );
475
+ if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
476
+ throw new TypeError('TinyLocalStorage: version must be a positive number.');
477
+ if (typeof onUpgrade !== 'function')
478
+ throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
479
+
480
+ // @ts-ignore
481
+ const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
482
+ if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
483
+ throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
484
+
485
+ if (version < savedVersion)
486
+ throw new Error(
487
+ `TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
488
+ );
489
+
490
+ if (version > savedVersion) {
491
+ onUpgrade(savedVersion, version);
492
+ localStorage.setItem(this.#dbKey, String(version));
493
+ this.#version = version;
494
+ }
495
+ }
496
+
497
+ /**
498
+ * Gets the current database key used in localStorage.
499
+ *
500
+ * @returns {string|null} The database key, or null if not set.
501
+ */
502
+ getDbKey() {
503
+ return this.#dbKey;
504
+ }
505
+
506
+ /**
507
+ * Gets the current version of the database.
508
+ *
509
+ * @returns {number} The current version number.
510
+ */
511
+ getVersion() {
512
+ return this.#version;
513
+ }
514
+
436
515
  /**
437
516
  * Defines a custom storage interface (e.g. `sessionStorage`).
438
517
  *
@@ -440,7 +519,7 @@ class TinyLocalStorage {
440
519
  */
441
520
  setLocalStorage(localstorage) {
442
521
  if (!(localstorage instanceof Storage))
443
- throw new Error('Argument must be a valid instance of Storage.');
522
+ throw new TypeError('Argument must be a valid instance of Storage.');
444
523
  this.#localStorage = localstorage;
445
524
  }
446
525
 
@@ -462,7 +541,7 @@ class TinyLocalStorage {
462
541
  */
463
542
  #setJson(name, data) {
464
543
  if (typeof name !== 'string' || !name.length)
465
- throw new Error('Key must be a non-empty string.');
544
+ throw new TypeError('Key must be a non-empty string.');
466
545
  return TinyLocalStorage.encodeSpecialJson(data);
467
546
  }
468
547
 
@@ -475,13 +554,14 @@ class TinyLocalStorage {
475
554
  * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
476
555
  */
477
556
  setJson(name, data) {
557
+ this.#isProtectedDbKey(name);
478
558
  if (
479
559
  !objChecker.isJsonObject(data) &&
480
560
  !Array.isArray(data) &&
481
561
  !(data instanceof Map) &&
482
562
  !(data instanceof Set)
483
563
  ) {
484
- throw new Error('The storage value is not a valid JSON-compatible structure.');
564
+ throw new TypeError('The storage value is not a valid JSON-compatible structure.');
485
565
  }
486
566
  const encoded = this.#setJson(name, data);
487
567
  this.emit('setJson', name, data);
@@ -499,7 +579,7 @@ class TinyLocalStorage {
499
579
  */
500
580
  #getJson(name, defaultData) {
501
581
  if (typeof name !== 'string' || !name.length)
502
- throw new Error('Key must be a non-empty string.');
582
+ throw new TypeError('Key must be a non-empty string.');
503
583
 
504
584
  const raw = this.#localStorage.getItem(name);
505
585
  const fallbackTypes = {
@@ -553,7 +633,8 @@ class TinyLocalStorage {
553
633
  * @param {Date} data
554
634
  */
555
635
  setDate(name, data) {
556
- if (!(data instanceof Date)) throw new Error('Value must be a Date.');
636
+ this.#isProtectedDbKey(name);
637
+ if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
557
638
  const encoded = this.#setJson(name, data);
558
639
  this.emit('setDate', name, data);
559
640
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -575,7 +656,8 @@ class TinyLocalStorage {
575
656
  * @param {RegExp} data
576
657
  */
577
658
  setRegExp(name, data) {
578
- if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
659
+ this.#isProtectedDbKey(name);
660
+ if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
579
661
  const encoded = this.#setJson(name, data);
580
662
  this.emit('setRegExp', name, data);
581
663
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -597,7 +679,8 @@ class TinyLocalStorage {
597
679
  * @param {bigint} data
598
680
  */
599
681
  setBigInt(name, data) {
600
- if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
682
+ this.#isProtectedDbKey(name);
683
+ if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
601
684
  const encoded = this.#setJson(name, data);
602
685
  this.emit('setBigInt', name, data);
603
686
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -620,7 +703,8 @@ class TinyLocalStorage {
620
703
  * @param {symbol} data
621
704
  */
622
705
  setSymbol(name, data) {
623
- if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
706
+ this.#isProtectedDbKey(name);
707
+ if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
624
708
  const encoded = this.#setJson(name, data);
625
709
  this.emit('setSymbol', name, data);
626
710
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -653,8 +737,9 @@ class TinyLocalStorage {
653
737
  * @param {any} data - The data to store.
654
738
  */
655
739
  setItem(name, data) {
740
+ this.#isProtectedDbKey(name);
656
741
  if (typeof name !== 'string' || !name.length)
657
- throw new Error('Key must be a non-empty string.');
742
+ throw new TypeError('Key must be a non-empty string.');
658
743
  this.emit('setItem', name, data);
659
744
  return this.#localStorage.setItem(name, data);
660
745
  }
@@ -667,7 +752,7 @@ class TinyLocalStorage {
667
752
  */
668
753
  getItem(name) {
669
754
  if (typeof name !== 'string' || !name.length)
670
- throw new Error('Key must be a non-empty string.');
755
+ throw new TypeError('Key must be a non-empty string.');
671
756
  return this.#localStorage.getItem(name);
672
757
  }
673
758
 
@@ -678,9 +763,10 @@ class TinyLocalStorage {
678
763
  * @param {string} data - The string to store.
679
764
  */
680
765
  setString(name, data) {
766
+ this.#isProtectedDbKey(name);
681
767
  if (typeof name !== 'string' || !name.length)
682
- throw new Error('Key must be a non-empty string.');
683
- if (typeof data !== 'string') throw new Error('Value must be a string.');
768
+ throw new TypeError('Key must be a non-empty string.');
769
+ if (typeof data !== 'string') throw new TypeError('Value must be a string.');
684
770
 
685
771
  this.emit('setString', name, data);
686
772
  return this.#localStorage.setItem(
@@ -700,7 +786,7 @@ class TinyLocalStorage {
700
786
  */
701
787
  getString(name) {
702
788
  if (typeof name !== 'string' || !name.length)
703
- throw new Error('Key must be a non-empty string.');
789
+ throw new TypeError('Key must be a non-empty string.');
704
790
  let value = this.#localStorage.getItem(name);
705
791
  try {
706
792
  /** @type {{ value: string; __string__: boolean }} */
@@ -722,9 +808,10 @@ class TinyLocalStorage {
722
808
  * @param {number} data - The number to store.
723
809
  */
724
810
  setNumber(name, data) {
811
+ this.#isProtectedDbKey(name);
725
812
  if (typeof name !== 'string' || !name.length)
726
- throw new Error('Key must be a non-empty string.');
727
- if (typeof data !== 'number') throw new Error('Value must be a number.');
813
+ throw new TypeError('Key must be a non-empty string.');
814
+ if (typeof data !== 'number') throw new TypeError('Value must be a number.');
728
815
  this.emit('setNumber', name, data);
729
816
  return this.#localStorage.setItem(name, String(data));
730
817
  }
@@ -737,7 +824,7 @@ class TinyLocalStorage {
737
824
  */
738
825
  getNumber(name) {
739
826
  if (typeof name !== 'string' || !name.length)
740
- throw new Error('Key must be a non-empty string.');
827
+ throw new TypeError('Key must be a non-empty string.');
741
828
 
742
829
  /** @type {number|string|null} */
743
830
  let number = this.#localStorage.getItem(name);
@@ -756,9 +843,10 @@ class TinyLocalStorage {
756
843
  * @param {boolean} data - The boolean value to store.
757
844
  */
758
845
  setBool(name, data) {
846
+ this.#isProtectedDbKey(name);
759
847
  if (typeof name !== 'string' || !name.length)
760
- throw new Error('Key must be a non-empty string.');
761
- if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
848
+ throw new TypeError('Key must be a non-empty string.');
849
+ if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
762
850
  this.emit('setBool', name, data);
763
851
  return this.#localStorage.setItem(name, String(data));
764
852
  }
@@ -771,7 +859,7 @@ class TinyLocalStorage {
771
859
  */
772
860
  getBool(name) {
773
861
  if (typeof name !== 'string' || !name.length)
774
- throw new Error('Key must be a non-empty string.');
862
+ throw new TypeError('Key must be a non-empty string.');
775
863
 
776
864
  const value = this.#localStorage.getItem(name);
777
865
  if (typeof value === 'boolean') return value;
@@ -790,7 +878,7 @@ class TinyLocalStorage {
790
878
  */
791
879
  removeItem(name) {
792
880
  if (typeof name !== 'string' || !name.length)
793
- throw new Error('Key must be a non-empty string.');
881
+ throw new TypeError('Key must be a non-empty string.');
794
882
 
795
883
  this.emit('removeItem', name);
796
884
  return this.#localStorage.removeItem(name);
@@ -130,6 +130,13 @@ declare class TinyLocalStorage {
130
130
  static deleteJsonType(type: any): boolean;
131
131
  static encodeSpecialJson(value: any): any;
132
132
  static decodeSpecialJson(value: any): any;
133
+ /**
134
+ * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
135
+ *
136
+ * Adds listener for the native `storage` event to support tab synchronization.
137
+ * @param {string} [dbName] - Unique database name.
138
+ */
139
+ constructor(dbName?: string);
133
140
  /**
134
141
  * Enables or disables throwing an error when the maximum number of listeners is exceeded.
135
142
  *
@@ -258,6 +265,28 @@ declare class TinyLocalStorage {
258
265
  * @returns {number} The maximum number of listeners.
259
266
  */
260
267
  getMaxListeners(): number;
268
+ /**
269
+ * Updates the version of the storage and triggers migration if needed.
270
+ *
271
+ * @param {number} version - Desired version of the database.
272
+ * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
273
+ * @throws {Error} If the database key has not been initialized.
274
+ * @throws {TypeError} If `version` is not a valid positive number.
275
+ * @throws {TypeError} If `onUpgrade` is not a function.
276
+ */
277
+ updateStorageVersion(version: number, onUpgrade: (oldVersion: number, newVersion: number) => void): void;
278
+ /**
279
+ * Gets the current database key used in localStorage.
280
+ *
281
+ * @returns {string|null} The database key, or null if not set.
282
+ */
283
+ getDbKey(): string | null;
284
+ /**
285
+ * Gets the current version of the database.
286
+ *
287
+ * @returns {number} The current version number.
288
+ */
289
+ getVersion(): number;
261
290
  /**
262
291
  * Defines a custom storage interface (e.g. `sessionStorage`).
263
292
  *
@@ -375,16 +375,83 @@ class TinyLocalStorage {
375
375
  //////////////////////////////////////////////////////
376
376
  /** @type {Storage} */
377
377
  #localStorage = window.localStorage;
378
+ /** @type {string|null} */
379
+ #dbKey = null;
380
+ /** @type {number} */
381
+ #version = 0;
378
382
  /** @type {(ev: StorageEvent) => any} */
379
383
  #storageEvent = (ev) => this.emit('storage', ev);
380
384
  /**
381
385
  * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
382
386
  *
383
387
  * Adds listener for the native `storage` event to support tab synchronization.
388
+ * @param {string} [dbName] - Unique database name.
384
389
  */
385
- 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}`;
386
395
  window.addEventListener('storage', this.#storageEvent);
387
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
+ }
388
455
  /**
389
456
  * Defines a custom storage interface (e.g. `sessionStorage`).
390
457
  *
@@ -392,7 +459,7 @@ class TinyLocalStorage {
392
459
  */
393
460
  setLocalStorage(localstorage) {
394
461
  if (!(localstorage instanceof Storage))
395
- throw new Error('Argument must be a valid instance of Storage.');
462
+ throw new TypeError('Argument must be a valid instance of Storage.');
396
463
  this.#localStorage = localstorage;
397
464
  }
398
465
  /**
@@ -412,7 +479,7 @@ class TinyLocalStorage {
412
479
  */
413
480
  #setJson(name, data) {
414
481
  if (typeof name !== 'string' || !name.length)
415
- throw new Error('Key must be a non-empty string.');
482
+ throw new TypeError('Key must be a non-empty string.');
416
483
  return TinyLocalStorage.encodeSpecialJson(data);
417
484
  }
418
485
  /**
@@ -424,11 +491,12 @@ class TinyLocalStorage {
424
491
  * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
425
492
  */
426
493
  setJson(name, data) {
494
+ this.#isProtectedDbKey(name);
427
495
  if (!isJsonObject(data) &&
428
496
  !Array.isArray(data) &&
429
497
  !(data instanceof Map) &&
430
498
  !(data instanceof Set)) {
431
- throw new Error('The storage value is not a valid JSON-compatible structure.');
499
+ throw new TypeError('The storage value is not a valid JSON-compatible structure.');
432
500
  }
433
501
  const encoded = this.#setJson(name, data);
434
502
  this.emit('setJson', name, data);
@@ -445,7 +513,7 @@ class TinyLocalStorage {
445
513
  */
446
514
  #getJson(name, defaultData) {
447
515
  if (typeof name !== 'string' || !name.length)
448
- throw new Error('Key must be a non-empty string.');
516
+ throw new TypeError('Key must be a non-empty string.');
449
517
  const raw = this.#localStorage.getItem(name);
450
518
  const fallbackTypes = {
451
519
  obj: () => ({}),
@@ -491,8 +559,9 @@ class TinyLocalStorage {
491
559
  * @param {Date} data
492
560
  */
493
561
  setDate(name, data) {
562
+ this.#isProtectedDbKey(name);
494
563
  if (!(data instanceof Date))
495
- throw new Error('Value must be a Date.');
564
+ throw new TypeError('Value must be a Date.');
496
565
  const encoded = this.#setJson(name, data);
497
566
  this.emit('setDate', name, data);
498
567
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -512,8 +581,9 @@ class TinyLocalStorage {
512
581
  * @param {RegExp} data
513
582
  */
514
583
  setRegExp(name, data) {
584
+ this.#isProtectedDbKey(name);
515
585
  if (!(data instanceof RegExp))
516
- throw new Error('Value must be a RegExp.');
586
+ throw new TypeError('Value must be a RegExp.');
517
587
  const encoded = this.#setJson(name, data);
518
588
  this.emit('setRegExp', name, data);
519
589
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -533,8 +603,9 @@ class TinyLocalStorage {
533
603
  * @param {bigint} data
534
604
  */
535
605
  setBigInt(name, data) {
606
+ this.#isProtectedDbKey(name);
536
607
  if (typeof data !== 'bigint')
537
- throw new Error('Value must be a BigInt.');
608
+ throw new TypeError('Value must be a BigInt.');
538
609
  const encoded = this.#setJson(name, data);
539
610
  this.emit('setBigInt', name, data);
540
611
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -555,8 +626,9 @@ class TinyLocalStorage {
555
626
  * @param {symbol} data
556
627
  */
557
628
  setSymbol(name, data) {
629
+ this.#isProtectedDbKey(name);
558
630
  if (typeof data !== 'symbol')
559
- throw new Error('Value must be a Symbol.');
631
+ throw new TypeError('Value must be a Symbol.');
560
632
  const encoded = this.#setJson(name, data);
561
633
  this.emit('setSymbol', name, data);
562
634
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -586,8 +658,9 @@ class TinyLocalStorage {
586
658
  * @param {any} data - The data to store.
587
659
  */
588
660
  setItem(name, data) {
661
+ this.#isProtectedDbKey(name);
589
662
  if (typeof name !== 'string' || !name.length)
590
- throw new Error('Key must be a non-empty string.');
663
+ throw new TypeError('Key must be a non-empty string.');
591
664
  this.emit('setItem', name, data);
592
665
  return this.#localStorage.setItem(name, data);
593
666
  }
@@ -599,7 +672,7 @@ class TinyLocalStorage {
599
672
  */
600
673
  getItem(name) {
601
674
  if (typeof name !== 'string' || !name.length)
602
- throw new Error('Key must be a non-empty string.');
675
+ throw new TypeError('Key must be a non-empty string.');
603
676
  return this.#localStorage.getItem(name);
604
677
  }
605
678
  /**
@@ -609,10 +682,11 @@ class TinyLocalStorage {
609
682
  * @param {string} data - The string to store.
610
683
  */
611
684
  setString(name, data) {
685
+ this.#isProtectedDbKey(name);
612
686
  if (typeof name !== 'string' || !name.length)
613
- throw new Error('Key must be a non-empty string.');
687
+ throw new TypeError('Key must be a non-empty string.');
614
688
  if (typeof data !== 'string')
615
- throw new Error('Value must be a string.');
689
+ throw new TypeError('Value must be a string.');
616
690
  this.emit('setString', name, data);
617
691
  return this.#localStorage.setItem(name, JSON.stringify({
618
692
  __string__: true,
@@ -627,7 +701,7 @@ class TinyLocalStorage {
627
701
  */
628
702
  getString(name) {
629
703
  if (typeof name !== 'string' || !name.length)
630
- throw new Error('Key must be a non-empty string.');
704
+ throw new TypeError('Key must be a non-empty string.');
631
705
  let value = this.#localStorage.getItem(name);
632
706
  try {
633
707
  /** @type {{ value: string; __string__: boolean }} */
@@ -651,10 +725,11 @@ class TinyLocalStorage {
651
725
  * @param {number} data - The number to store.
652
726
  */
653
727
  setNumber(name, data) {
728
+ this.#isProtectedDbKey(name);
654
729
  if (typeof name !== 'string' || !name.length)
655
- throw new Error('Key must be a non-empty string.');
730
+ throw new TypeError('Key must be a non-empty string.');
656
731
  if (typeof data !== 'number')
657
- throw new Error('Value must be a number.');
732
+ throw new TypeError('Value must be a number.');
658
733
  this.emit('setNumber', name, data);
659
734
  return this.#localStorage.setItem(name, String(data));
660
735
  }
@@ -666,7 +741,7 @@ class TinyLocalStorage {
666
741
  */
667
742
  getNumber(name) {
668
743
  if (typeof name !== 'string' || !name.length)
669
- throw new Error('Key must be a non-empty string.');
744
+ throw new TypeError('Key must be a non-empty string.');
670
745
  /** @type {number|string|null} */
671
746
  let number = this.#localStorage.getItem(name);
672
747
  if (typeof number === 'number')
@@ -685,10 +760,11 @@ class TinyLocalStorage {
685
760
  * @param {boolean} data - The boolean value to store.
686
761
  */
687
762
  setBool(name, data) {
763
+ this.#isProtectedDbKey(name);
688
764
  if (typeof name !== 'string' || !name.length)
689
- throw new Error('Key must be a non-empty string.');
765
+ throw new TypeError('Key must be a non-empty string.');
690
766
  if (typeof data !== 'boolean')
691
- throw new Error('Value must be a boolean.');
767
+ throw new TypeError('Value must be a boolean.');
692
768
  this.emit('setBool', name, data);
693
769
  return this.#localStorage.setItem(name, String(data));
694
770
  }
@@ -700,7 +776,7 @@ class TinyLocalStorage {
700
776
  */
701
777
  getBool(name) {
702
778
  if (typeof name !== 'string' || !name.length)
703
- throw new Error('Key must be a non-empty string.');
779
+ throw new TypeError('Key must be a non-empty string.');
704
780
  const value = this.#localStorage.getItem(name);
705
781
  if (typeof value === 'boolean')
706
782
  return value;
@@ -719,7 +795,7 @@ class TinyLocalStorage {
719
795
  */
720
796
  removeItem(name) {
721
797
  if (typeof name !== 'string' || !name.length)
722
- throw new Error('Key must be a non-empty string.');
798
+ throw new TypeError('Key must be a non-empty string.');
723
799
  this.emit('removeItem', name);
724
800
  return this.#localStorage.removeItem(name);
725
801
  }
@@ -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)`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.19.2",
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",