tiny-essentials 1.19.2 → 1.20.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.
Files changed (34) hide show
  1. package/dist/v1/TinyEssentials.js +1009 -22
  2. package/dist/v1/TinyEssentials.min.js +1 -1
  3. package/dist/v1/TinyIframeEvents.js +854 -0
  4. package/dist/v1/TinyIframeEvents.min.js +1 -0
  5. package/dist/v1/TinyLocalStorage.js +109 -21
  6. package/dist/v1/TinyLocalStorage.min.js +1 -1
  7. package/dist/v1/TinyNewWinEvents.js +888 -0
  8. package/dist/v1/TinyNewWinEvents.min.js +1 -0
  9. package/dist/v1/TinyUploadClicker.js +1022 -37
  10. package/dist/v1/TinyUploadClicker.min.js +1 -1
  11. package/dist/v1/build/TinyIframeEvents.cjs +7 -0
  12. package/dist/v1/build/TinyIframeEvents.d.mts +3 -0
  13. package/dist/v1/build/TinyIframeEvents.mjs +2 -0
  14. package/dist/v1/build/TinyNewWinEvents.cjs +7 -0
  15. package/dist/v1/build/TinyNewWinEvents.d.mts +3 -0
  16. package/dist/v1/build/TinyNewWinEvents.mjs +2 -0
  17. package/dist/v1/index.cjs +4 -0
  18. package/dist/v1/index.d.mts +3 -1
  19. package/dist/v1/index.mjs +3 -1
  20. package/dist/v1/libs/TinyIframeEvents.cjs +409 -0
  21. package/dist/v1/libs/TinyIframeEvents.d.mts +193 -0
  22. package/dist/v1/libs/TinyIframeEvents.mjs +348 -0
  23. package/dist/v1/libs/TinyLocalStorage.cjs +109 -21
  24. package/dist/v1/libs/TinyLocalStorage.d.mts +29 -0
  25. package/dist/v1/libs/TinyLocalStorage.mjs +97 -21
  26. package/dist/v1/libs/TinyNewWinEvents.cjs +486 -0
  27. package/dist/v1/libs/TinyNewWinEvents.d.mts +241 -0
  28. package/dist/v1/libs/TinyNewWinEvents.mjs +437 -0
  29. package/docs/v1/README.md +2 -0
  30. package/docs/v1/libs/TinyIframeEvents.md +149 -0
  31. package/docs/v1/libs/TinyLocalStorage.md +53 -0
  32. package/docs/v1/libs/TinyNewWinEvents.md +186 -0
  33. package/docs/v1/libs/TinyNotifyCenter.md +1 -1
  34. package/package.json +1 -1
@@ -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
  }