tiny-essentials 1.18.1 → 1.19.1

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 (74) hide show
  1. package/README.md +17 -3
  2. package/dist/node_modules/firebase-functions/lib/common/trace.cjs +0 -1
  3. package/dist/node_modules/firebase-functions/lib/logger/index.cjs +1 -0
  4. package/dist/v1/ColorSafeStringify.min.js +1 -1
  5. package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
  6. package/dist/v1/TinyBasicsEs.js +13 -6
  7. package/dist/v1/TinyBasicsEs.min.js +1 -1
  8. package/dist/v1/TinyClipboard.min.js +1 -1
  9. package/dist/v1/TinyColorConverter.js +617 -0
  10. package/dist/v1/TinyColorConverter.min.js +1 -0
  11. package/dist/v1/TinyDomReadyManager.min.js +1 -1
  12. package/dist/v1/TinyDragger.min.js +1 -1
  13. package/dist/v1/TinyEssentials.js +2635 -482
  14. package/dist/v1/TinyEssentials.min.js +1 -1
  15. package/dist/v1/TinyEvents.js +402 -0
  16. package/dist/v1/TinyEvents.min.js +1 -0
  17. package/dist/v1/TinyHtml.min.js +1 -1
  18. package/dist/v1/TinyLocalStorage.js +1292 -0
  19. package/dist/v1/TinyLocalStorage.min.js +1 -0
  20. package/dist/v1/TinyNotifications.min.js +1 -1
  21. package/dist/v1/TinyNotifyCenter.min.js +1 -1
  22. package/dist/v1/TinyPromiseQueue.min.js +1 -1
  23. package/dist/v1/TinyRateLimiter.js +2 -1
  24. package/dist/v1/TinyRateLimiter.min.js +1 -1
  25. package/dist/v1/TinySmartScroller.js +570 -52
  26. package/dist/v1/TinySmartScroller.min.js +1 -1
  27. package/dist/v1/TinyTextRangeEditor.min.js +1 -1
  28. package/dist/v1/TinyTimeout.js +233 -0
  29. package/dist/v1/TinyTimeout.min.js +1 -0
  30. package/dist/v1/TinyToastNotify.min.js +1 -1
  31. package/dist/v1/TinyUploadClicker.js +1457 -106
  32. package/dist/v1/TinyUploadClicker.min.js +1 -1
  33. package/dist/v1/UltraRandomMsgGen.min.js +1 -1
  34. package/dist/v1/basics/html.cjs +13 -6
  35. package/dist/v1/basics/html.d.mts +12 -4
  36. package/dist/v1/basics/html.mjs +13 -6
  37. package/dist/v1/build/TinyColorConverter.cjs +7 -0
  38. package/dist/v1/build/TinyColorConverter.d.mts +3 -0
  39. package/dist/v1/build/TinyColorConverter.mjs +2 -0
  40. package/dist/v1/build/TinyEvents.cjs +7 -0
  41. package/dist/v1/build/TinyEvents.d.mts +3 -0
  42. package/dist/v1/build/TinyEvents.mjs +2 -0
  43. package/dist/v1/build/TinyLocalStorage.cjs +7 -0
  44. package/dist/v1/build/TinyLocalStorage.d.mts +3 -0
  45. package/dist/v1/build/TinyLocalStorage.mjs +2 -0
  46. package/dist/v1/build/TinyTimeout.cjs +7 -0
  47. package/dist/v1/build/TinyTimeout.d.mts +3 -0
  48. package/dist/v1/build/TinyTimeout.mjs +2 -0
  49. package/dist/v1/index.cjs +8 -0
  50. package/dist/v1/index.d.mts +5 -1
  51. package/dist/v1/index.mjs +5 -1
  52. package/dist/v1/libs/TinyColorConverter.cjs +578 -0
  53. package/dist/v1/libs/TinyColorConverter.d.mts +396 -0
  54. package/dist/v1/libs/TinyColorConverter.mjs +520 -0
  55. package/dist/v1/libs/TinyEvents.cjs +363 -0
  56. package/dist/v1/libs/TinyEvents.d.mts +160 -0
  57. package/dist/v1/libs/TinyEvents.mjs +328 -0
  58. package/dist/v1/libs/TinyLocalStorage.cjs +847 -0
  59. package/dist/v1/libs/TinyLocalStorage.d.mts +407 -0
  60. package/dist/v1/libs/TinyLocalStorage.mjs +740 -0
  61. package/dist/v1/libs/TinySmartScroller.cjs +207 -52
  62. package/dist/v1/libs/TinySmartScroller.d.mts +164 -16
  63. package/dist/v1/libs/TinySmartScroller.mjs +181 -52
  64. package/dist/v1/libs/TinyTimeout.cjs +194 -0
  65. package/dist/v1/libs/TinyTimeout.d.mts +89 -0
  66. package/dist/v1/libs/TinyTimeout.mjs +179 -0
  67. package/dist/v1/libs/TinyUploadClicker.cjs +1 -0
  68. package/docs/v1/README.md +4 -0
  69. package/docs/v1/libs/TinyColorConverter.md +220 -0
  70. package/docs/v1/libs/TinyEvents.md +199 -0
  71. package/docs/v1/libs/TinyLocalStorage.md +350 -0
  72. package/docs/v1/libs/TinyRateLimiter.md +0 -3
  73. package/docs/v1/libs/TinyTimeout.md +190 -0
  74. package/package.json +28 -5
@@ -1,13 +1,6 @@
1
1
  /******/ (() => { // webpackBootstrap
2
2
  /******/ var __webpack_modules__ = ({
3
3
 
4
- /***/ 133:
5
- /***/ (() => {
6
-
7
- /* (ignored) */
8
-
9
- /***/ }),
10
-
11
4
  /***/ 251:
12
5
  /***/ ((__unused_webpack_module, exports) => {
13
6
 
@@ -1924,13 +1917,6 @@ var hexSliceLookupTable = (function () {
1924
1917
  })()
1925
1918
 
1926
1919
 
1927
- /***/ }),
1928
-
1929
- /***/ 370:
1930
- /***/ (() => {
1931
-
1932
- /* (ignored) */
1933
-
1934
1920
  /***/ }),
1935
1921
 
1936
1922
  /***/ 526:
@@ -9061,8 +9047,6 @@ function isScrolledIntoView(element) {
9061
9047
  return elemBottom <= viewportBottom && elemTop >= viewportTop;
9062
9048
  }
9063
9049
 
9064
- // EXTERNAL MODULE: fs (ignored)
9065
- var fs_ignored_ = __webpack_require__(133);
9066
9050
  // EXTERNAL MODULE: ./node_modules/path-browserify/index.js
9067
9051
  var path_browserify = __webpack_require__(975);
9068
9052
  ;// ./src/v1/fileManager/normalFuncs.mjs
@@ -9471,8 +9455,6 @@ function renameFilePadNumbers(dirPath, totalDigits = 3, extensions = []) {
9471
9455
  );
9472
9456
  }
9473
9457
 
9474
- // EXTERNAL MODULE: fs/promises (ignored)
9475
- var promises_ignored_ = __webpack_require__(370);
9476
9458
  ;// ./src/v1/fileManager/asyncFuncs.mjs
9477
9459
 
9478
9460
 
@@ -10712,10 +10694,374 @@ class TinyDragger {
10712
10694
 
10713
10695
  /* harmony default export */ const libs_TinyDragger = ((/* unused pure expression or super */ null && (TinyDragger)));
10714
10696
 
10697
+ ;// ./src/v1/libs/TinyEvents.mjs
10698
+ /**
10699
+ * A generic event listener callback function.
10700
+ *
10701
+ * @callback handler
10702
+ * @param {...any} payload - The data payload passed when the event is triggered.
10703
+ * @returns {void}
10704
+ */
10705
+
10706
+ /**
10707
+ * TinyEvents provides a minimalistic event emitter system similar to Node.js's EventEmitter,
10708
+ * enabling components to subscribe to, emit, and manage events and their listeners.
10709
+ *
10710
+ * Features include:
10711
+ * - Adding/removing event listeners (`on`, `off`, `offAll`, `offAllTypes`)
10712
+ * - One-time listeners (`once`)
10713
+ * - Emitting events (`emit`)
10714
+ * - Listener inspection and limits (`listenerCount`, `listeners`, `eventNames`)
10715
+ * - Maximum listener control (`setMaxListeners`, `getMaxListeners`)
10716
+ *
10717
+ * This class is useful for lightweight, dependency-free publish/subscribe event handling
10718
+ * within modular JavaScript applications.
10719
+ *
10720
+ * @class
10721
+ */
10722
+ class TinyEvents {
10723
+ /** @type {Map<string, { handler: handler; config: { once: boolean } }[]>} */
10724
+ #listeners = new Map();
10725
+
10726
+ /** @type {number} */
10727
+ #maxListeners = 10;
10728
+
10729
+ /** @type {boolean} */
10730
+ #throwMaxListeners = false;
10731
+
10732
+ /**
10733
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
10734
+ *
10735
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
10736
+ */
10737
+ setThrowOnMaxListeners(shouldThrow) {
10738
+ if (typeof shouldThrow !== 'boolean')
10739
+ throw new TypeError('setThrowOnMaxListeners(value): value must be a boolean');
10740
+ this.#throwMaxListeners = shouldThrow;
10741
+ }
10742
+
10743
+ /**
10744
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
10745
+ *
10746
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
10747
+ */
10748
+ getThrowOnMaxListeners() {
10749
+ return this.#throwMaxListeners;
10750
+ }
10751
+
10752
+ ///////////////////////////////////////////////////
10753
+
10754
+ /**
10755
+ * Internal method to prepend a listener with options.
10756
+ *
10757
+ * @param {string} event - Event name.
10758
+ * @param {handler} handler - The callback function.
10759
+ * @param {Object} [settings={}] - Optional settings.
10760
+ * @param {boolean} [settings.once=false] - If the listener should be executed once.
10761
+ */
10762
+ #prepend(event, handler, { once = false } = {}) {
10763
+ let eventData = this.#listeners.get(event);
10764
+ if (!Array.isArray(eventData)) {
10765
+ eventData = [];
10766
+ this.#listeners.set(event, eventData);
10767
+ }
10768
+ eventData.unshift({ handler, config: { once } });
10769
+
10770
+ const max = this.#maxListeners;
10771
+ if (max > 0 && eventData.length > max) {
10772
+ const warnMessage =
10773
+ `Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
10774
+ `Use setMaxListeners() to increase limit.`;
10775
+ if (!this.#throwMaxListeners) console.warn(warnMessage);
10776
+ else throw new Error(warnMessage);
10777
+ }
10778
+ }
10779
+
10780
+ /**
10781
+ * Adds a listener to the beginning of the listeners array for the specified event.
10782
+ *
10783
+ * @param {string} event - Event name.
10784
+ * @param {handler} handler - The callback function.
10785
+ */
10786
+ prependListener(event, handler) {
10787
+ if (typeof event !== 'string')
10788
+ throw new TypeError('prepend(event, handler): event name must be a string');
10789
+ if (typeof handler !== 'function')
10790
+ throw new TypeError('prepend(event, handler): handler must be a function');
10791
+ this.#prepend(event, handler);
10792
+ }
10793
+
10794
+ /**
10795
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
10796
+ *
10797
+ * @param {string} event - Event name.
10798
+ * @param {handler} handler - The callback function.
10799
+ * @returns {handler} - The wrapped handler used internally.
10800
+ */
10801
+ prependListenerOnce(event, handler) {
10802
+ if (typeof event !== 'string')
10803
+ throw new TypeError('prependOnceListener(event, handler): event name must be a string');
10804
+ if (typeof handler !== 'function')
10805
+ throw new TypeError('prependOnceListener(event, handler): handler must be a function');
10806
+
10807
+ /** @type {handler} */
10808
+ const wrapped = (...args) => {
10809
+ this.off(event, wrapped);
10810
+ handler(...args);
10811
+ };
10812
+
10813
+ this.#prepend(event, wrapped, { once: true });
10814
+ return wrapped;
10815
+ }
10816
+
10817
+ ////////////////////////////////////////////////////////////
10818
+
10819
+ /**
10820
+ * Adds a event listener.
10821
+ *
10822
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
10823
+ * @param {handler} handler - Callback function to be called when event fires.
10824
+ * @param {Object} [settings={}] - Optional settings.
10825
+ * @param {boolean} [settings.once=false] - This is a once event.
10826
+ */
10827
+ #on(event, handler, { once = false } = {}) {
10828
+ let eventData = this.#listeners.get(event);
10829
+ if (!Array.isArray(eventData)) {
10830
+ eventData = [];
10831
+ this.#listeners.set(event, eventData);
10832
+ }
10833
+ eventData.push({ handler, config: { once } });
10834
+ // Warn if listener count exceeds the max allowed
10835
+ const max = this.#maxListeners;
10836
+ if (max > 0 && eventData.length > max) {
10837
+ const warnMessage =
10838
+ `Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
10839
+ `Use setMaxListeners() to increase limit.`;
10840
+ if (!this.#throwMaxListeners) console.warn(warnMessage);
10841
+ else throw new Error(warnMessage);
10842
+ }
10843
+ }
10844
+
10845
+ /**
10846
+ * Adds a event listener.
10847
+ *
10848
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
10849
+ * @param {handler} handler - Callback function to be called when event fires.
10850
+ */
10851
+ on(event, handler) {
10852
+ if (typeof event !== 'string')
10853
+ throw new TypeError('on(event, handler): event name must be a string');
10854
+ if (typeof handler !== 'function')
10855
+ throw new TypeError('on(event, handler): handler must be a function');
10856
+ return this.#on(event, handler);
10857
+ }
10858
+
10859
+ /**
10860
+ * Registers an event listener that runs only once, then is removed.
10861
+ *
10862
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
10863
+ * @param {handler} handler - The callback function to run on event.
10864
+ * @returns {handler} - The wrapped version of the handler.
10865
+ */
10866
+ once(event, handler) {
10867
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
10868
+ if (typeof handler !== 'function')
10869
+ throw new TypeError('once(event, handler): handler must be a function');
10870
+
10871
+ /** @type {handler} */
10872
+ const wrapped = (e) => {
10873
+ this.off(event, wrapped);
10874
+ if (typeof handler === 'function') handler(e);
10875
+ };
10876
+ this.#on(event, wrapped, { once: true });
10877
+ return wrapped;
10878
+ }
10879
+
10880
+ /**
10881
+ * Adds a event listener.
10882
+ *
10883
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
10884
+ * @param {handler} handler - Callback function to be called when event fires.
10885
+ */
10886
+ appendListener(event, handler) {
10887
+ return this.on(event, handler);
10888
+ }
10889
+
10890
+ /**
10891
+ * Registers an event listener that runs only once, then is removed.
10892
+ *
10893
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
10894
+ * @param {handler} handler - The callback function to run on event.
10895
+ * @returns {handler} - The wrapped version of the handler.
10896
+ */
10897
+ appendListenerOnce(event, handler) {
10898
+ return this.once(event, handler);
10899
+ }
10900
+
10901
+ ///////////////////////////////////////////////
10902
+
10903
+ /**
10904
+ * Removes a previously registered event listener.
10905
+ *
10906
+ * @param {string} event - The name of the event to remove the handler from.
10907
+ * @param {handler} handler - The specific callback function to remove.
10908
+ */
10909
+ off(event, handler) {
10910
+ if (typeof event !== 'string')
10911
+ throw new TypeError('off(event, handler): event name must be a string');
10912
+ if (typeof handler !== 'function')
10913
+ throw new TypeError('off(event, handler): handler must be a function');
10914
+
10915
+ const listeners = this.#listeners.get(event);
10916
+ if (!Array.isArray(listeners)) return;
10917
+
10918
+ const index = listeners.findIndex((listener) => listener.handler === handler);
10919
+ if (index !== -1) listeners.splice(index, 1);
10920
+
10921
+ // Optionally clean up empty arrays (optional)
10922
+ if (listeners.length === 0) this.#listeners.delete(event);
10923
+ }
10924
+
10925
+ /**
10926
+ * Removes all event listeners of a specific type from the element.
10927
+ *
10928
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
10929
+ */
10930
+ offAll(event) {
10931
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
10932
+ this.#listeners.delete(event);
10933
+ }
10934
+
10935
+ /**
10936
+ * Removes all event listeners of all types from the element.
10937
+ */
10938
+ offAllTypes() {
10939
+ this.#listeners.clear();
10940
+ }
10941
+
10942
+ /////////////////////////////////////////////
10943
+
10944
+ /**
10945
+ * Returns the number of listeners for a given event.
10946
+ *
10947
+ * @param {string} event - The name of the event.
10948
+ * @returns {number} Number of listeners for the event.
10949
+ */
10950
+ listenerCount(event) {
10951
+ if (typeof event !== 'string')
10952
+ throw new TypeError('listenerCount(event): event name must be a string');
10953
+
10954
+ const listeners = this.#listeners.get(event);
10955
+ return Array.isArray(listeners) ? listeners.length : 0;
10956
+ }
10957
+
10958
+ /**
10959
+ * Returns a copy of the array of listeners for the specified event.
10960
+ *
10961
+ * @param {string} event - The name of the event.
10962
+ * @returns {handler[]} Array of listener functions.
10963
+ */
10964
+ listeners(event) {
10965
+ if (typeof event !== 'string')
10966
+ throw new TypeError('listeners(event): event name must be a string');
10967
+
10968
+ const listeners = this.#listeners.get(event);
10969
+ return Array.isArray(listeners)
10970
+ ? [...listeners]
10971
+ .filter((listener) => !listener.config.once)
10972
+ .map((listener) => listener.handler)
10973
+ : [];
10974
+ }
10975
+
10976
+ /**
10977
+ * Returns a copy of the array of listeners for the specified event.
10978
+ *
10979
+ * @param {string} event - The name of the event.
10980
+ * @returns {handler[]} Array of listener functions.
10981
+ */
10982
+ onceListeners(event) {
10983
+ if (typeof event !== 'string')
10984
+ throw new TypeError('onceListeners(event): event name must be a string');
10985
+
10986
+ const listeners = this.#listeners.get(event);
10987
+ return Array.isArray(listeners)
10988
+ ? [...listeners]
10989
+ .filter((listener) => listener.config.once)
10990
+ .map((listener) => listener.handler)
10991
+ : [];
10992
+ }
10993
+
10994
+ /**
10995
+ * Returns a copy of the internal listeners array for the specified event,
10996
+ * including wrapper functions like those used by `.once()`.
10997
+ * @param {string | symbol} event - The event name.
10998
+ * @returns {handler[]} An array of raw listener functions.
10999
+ */
11000
+ allListeners(event) {
11001
+ if (typeof event !== 'string')
11002
+ throw new TypeError('allListeners(event): event name must be a string');
11003
+ const listeners = this.#listeners.get(event);
11004
+ return Array.isArray(listeners) ? [...listeners].map((listener) => listener.handler) : [];
11005
+ }
11006
+
11007
+ /**
11008
+ * Returns an array of event names for which there are registered listeners.
11009
+ *
11010
+ * @returns {string[]} Array of registered event names.
11011
+ */
11012
+ eventNames() {
11013
+ return [...this.#listeners.keys()];
11014
+ }
11015
+
11016
+ /**
11017
+ * Emits an event, triggering all registered handlers for that event.
11018
+ *
11019
+ * @param {string} event - The event name to emit.
11020
+ * @param {...any} payload - Optional data to pass to each handler.
11021
+ * @returns {boolean} True if any listeners were called, false otherwise.
11022
+ */
11023
+ emit(event, ...payload) {
11024
+ if (typeof event !== 'string')
11025
+ throw new TypeError('emit(event, data): event name must be a string');
11026
+
11027
+ const listeners = this.#listeners.get(event);
11028
+ if (!Array.isArray(listeners) || listeners.length === 0) return false;
11029
+
11030
+ // Call all listeners with the provided data
11031
+ listeners.forEach((listener) => listener.handler(...payload));
11032
+ return true;
11033
+ }
11034
+
11035
+ ///////////////////////////////////
11036
+
11037
+ /**
11038
+ * Sets the maximum number of listeners per event before a warning is shown.
11039
+ *
11040
+ * @param {number} n - The maximum number of listeners.
11041
+ */
11042
+ setMaxListeners(n) {
11043
+ if (!Number.isInteger(n) || n < 0)
11044
+ throw new TypeError('setMaxListeners(n): n must be a non-negative integer');
11045
+ this.#maxListeners = n;
11046
+ }
11047
+
11048
+ /**
11049
+ * Gets the maximum number of listeners allowed per event.
11050
+ *
11051
+ * @returns {number} The maximum number of listeners.
11052
+ */
11053
+ getMaxListeners() {
11054
+ return this.#maxListeners;
11055
+ }
11056
+ }
11057
+
11058
+ /* harmony default export */ const libs_TinyEvents = (TinyEvents);
11059
+
10715
11060
  ;// ./src/v1/libs/TinySmartScroller.mjs
10716
11061
 
10717
11062
 
10718
11063
 
11064
+
10719
11065
  /**
10720
11066
  * Represents the dimensions of a DOM element.
10721
11067
  *
@@ -10762,52 +11108,248 @@ class TinyDragger {
10762
11108
  class TinySmartScroller {
10763
11109
  static Utils = { ...collision_namespaceObject, TinyHtml: libs_TinyHtml };
10764
11110
 
10765
- /** @type {WeakMap<Element, NodeSizes>} */
10766
- #oldSizes = new WeakMap();
10767
- /** @type {WeakMap<Element, NodeSizes>} */
10768
- #newSizes = new WeakMap();
11111
+ #events = new libs_TinyEvents();
10769
11112
 
10770
- /** @type {WeakMap<Element, boolean>} */
10771
- #newVisibles = new WeakMap();
10772
- /** @type {WeakMap<Element, boolean>} */
10773
- #oldVisibles = new WeakMap();
11113
+ /**
11114
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
11115
+ *
11116
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
11117
+ */
11118
+ setThrowOnMaxListeners(shouldThrow) {
11119
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
11120
+ }
10774
11121
 
10775
- /** @type {WeakMap<Element, boolean>} */
10776
- #newVisiblesByTime = new WeakMap();
10777
- /** @type {WeakMap<Element, boolean>} */
10778
- #oldVisiblesByTime = new WeakMap();
11122
+ /**
11123
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
11124
+ *
11125
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
11126
+ */
11127
+ getThrowOnMaxListeners() {
11128
+ return this.#events.getThrowOnMaxListeners();
11129
+ }
10779
11130
 
10780
- /** @type {Record<string, ScrollListenersFunc[]>} */
10781
- #scrollListeners = {};
11131
+ /////////////////////////////////////////////////////////////
10782
11132
 
10783
- /** @type {ResizeObserver|null} */
10784
- #resizeObserver = null;
10785
- /** @type {MutationObserver|null} */
10786
- #mutationObserver = null;
11133
+ /**
11134
+ * Adds a listener to the beginning of the listeners array for the specified event.
11135
+ *
11136
+ * @param {string} event - Event name.
11137
+ * @param {ScrollListenersFunc} handler - The callback function.
11138
+ */
11139
+ prependListener(event, handler) {
11140
+ return this.#events.prependListener(event, handler);
11141
+ }
10787
11142
 
10788
- /** @type {Set<string>} */
10789
- #loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
11143
+ /**
11144
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
11145
+ *
11146
+ * @param {string} event - Event name.
11147
+ * @param {ScrollListenersFunc} handler - The callback function.
11148
+ * @returns {ScrollListenersFunc} - The wrapped handler used internally.
11149
+ */
11150
+ prependListenerOnce(event, handler) {
11151
+ return this.#events.prependListenerOnce(event, handler);
11152
+ }
10790
11153
 
10791
- /** @type {null|EventListenerOrEventListenerObject} */
10792
- #handler = null;
11154
+ //////////////////////////////////////////////////////////////////////
10793
11155
 
10794
- #isAtBottom = false;
10795
- #isAtTop = false;
10796
- #isAtCustomTop = false;
10797
- #isAtCustomBottom = false;
11156
+ /**
11157
+ * Adds a event listener.
11158
+ *
11159
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11160
+ * @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
11161
+ */
11162
+ appendListener(event, handler) {
11163
+ return this.#events.appendListener(event, handler);
11164
+ }
10798
11165
 
10799
- #querySelector = '';
10800
- #useWindow = false;
10801
- #destroyed = false;
10802
- #scrollPaused = false;
10803
- #autoScrollBottom = false;
10804
- #observeMutations = false;
10805
- #preserveScrollOnLayoutShift = false;
10806
- #debounceTime = 0;
10807
- #elemAmount = 0;
10808
- #elemOldAmount = 0;
10809
- #lastKnownScrollBottomOffset = 0;
10810
- #extraScrollBoundary = 0;
11166
+ /**
11167
+ * Registers an event listener that runs only once, then is removed.
11168
+ *
11169
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11170
+ * @param {ScrollListenersFunc} handler - The callback function to run on event.
11171
+ * @returns {ScrollListenersFunc} - The wrapped version of the handler.
11172
+ */
11173
+ appendListenerOnce(event, handler) {
11174
+ return this.#events.appendListenerOnce(event, handler);
11175
+ }
11176
+
11177
+ /**
11178
+ * Adds a event listener.
11179
+ *
11180
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11181
+ * @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
11182
+ */
11183
+ on(event, handler) {
11184
+ return this.#events.on(event, handler);
11185
+ }
11186
+
11187
+ /**
11188
+ * Registers an event listener that runs only once, then is removed.
11189
+ *
11190
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11191
+ * @param {ScrollListenersFunc} handler - The callback function to run on event.
11192
+ * @returns {ScrollListenersFunc} - The wrapped version of the handler.
11193
+ */
11194
+ once(event, handler) {
11195
+ return this.#events.once(event, handler);
11196
+ }
11197
+
11198
+ ////////////////////////////////////////////////////////////////////
11199
+
11200
+ /**
11201
+ * Removes a previously registered event listener.
11202
+ *
11203
+ * @param {string} event - The name of the event to remove the handler from.
11204
+ * @param {ScrollListenersFunc} handler - The specific callback function to remove.
11205
+ */
11206
+ off(event, handler) {
11207
+ return this.#events.off(event, handler);
11208
+ }
11209
+
11210
+ /**
11211
+ * Removes all event listeners of a specific type from the element.
11212
+ *
11213
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
11214
+ */
11215
+ offAll(event) {
11216
+ return this.#events.offAll(event);
11217
+ }
11218
+
11219
+ /**
11220
+ * Removes all event listeners of all types from the element.
11221
+ */
11222
+ offAllTypes() {
11223
+ return this.#events.offAllTypes();
11224
+ }
11225
+
11226
+ ////////////////////////////////////////////////////////////
11227
+
11228
+ /**
11229
+ * Returns the number of listeners for a given event.
11230
+ *
11231
+ * @param {string} event - The name of the event.
11232
+ * @returns {number} Number of listeners for the event.
11233
+ */
11234
+ listenerCount(event) {
11235
+ return this.#events.listenerCount(event);
11236
+ }
11237
+
11238
+ /**
11239
+ * Returns a copy of the array of listeners for the specified event.
11240
+ *
11241
+ * @param {string} event - The name of the event.
11242
+ * @returns {ScrollListenersFunc[]} Array of listener functions.
11243
+ */
11244
+ listeners(event) {
11245
+ return this.#events.listeners(event);
11246
+ }
11247
+
11248
+ /**
11249
+ * Returns a copy of the array of listeners for the specified event.
11250
+ *
11251
+ * @param {string} event - The name of the event.
11252
+ * @returns {ScrollListenersFunc[]} Array of listener functions.
11253
+ */
11254
+ onceListeners(event) {
11255
+ return this.#events.onceListeners(event);
11256
+ }
11257
+
11258
+ /**
11259
+ * Returns a copy of the internal listeners array for the specified event,
11260
+ * including wrapper functions like those used by `.once()`.
11261
+ * @param {string | symbol} event - The event name.
11262
+ * @returns {ScrollListenersFunc[]} An array of raw listener functions.
11263
+ */
11264
+ allListeners(event) {
11265
+ return this.#events.allListeners(event);
11266
+ }
11267
+
11268
+ /**
11269
+ * Returns an array of event names for which there are registered listeners.
11270
+ *
11271
+ * @returns {string[]} Array of registered event names.
11272
+ */
11273
+ eventNames() {
11274
+ return this.#events.eventNames();
11275
+ }
11276
+
11277
+ //////////////////////////////////////////////////////
11278
+
11279
+ /**
11280
+ * Emits an event, triggering all registered handlers for that event.
11281
+ *
11282
+ * @param {string} event - The event name to emit.
11283
+ * @param {...any} payload - Optional data to pass to each handler.
11284
+ * @returns {boolean} True if any listeners were called, false otherwise.
11285
+ */
11286
+ emit(event, ...payload) {
11287
+ return this.#events.emit(event, ...payload);
11288
+ }
11289
+
11290
+ /**
11291
+ * Sets the maximum number of listeners per event before a warning is shown.
11292
+ *
11293
+ * @param {number} n - The maximum number of listeners.
11294
+ */
11295
+ setMaxListeners(n) {
11296
+ return this.#events.setMaxListeners(n);
11297
+ }
11298
+
11299
+ /**
11300
+ * Gets the maximum number of listeners allowed per event.
11301
+ *
11302
+ * @returns {number} The maximum number of listeners.
11303
+ */
11304
+ getMaxListeners() {
11305
+ return this.#events.getMaxListeners();
11306
+ }
11307
+
11308
+ ///////////////////////////////////////////////////
11309
+
11310
+ /** @type {WeakMap<Element, NodeSizes>} */
11311
+ #oldSizes = new WeakMap();
11312
+ /** @type {WeakMap<Element, NodeSizes>} */
11313
+ #newSizes = new WeakMap();
11314
+
11315
+ /** @type {WeakMap<Element, boolean>} */
11316
+ #newVisibles = new WeakMap();
11317
+ /** @type {WeakMap<Element, boolean>} */
11318
+ #oldVisibles = new WeakMap();
11319
+
11320
+ /** @type {WeakMap<Element, boolean>} */
11321
+ #newVisiblesByTime = new WeakMap();
11322
+ /** @type {WeakMap<Element, boolean>} */
11323
+ #oldVisiblesByTime = new WeakMap();
11324
+
11325
+ /** @type {ResizeObserver|null} */
11326
+ #resizeObserver = null;
11327
+ /** @type {MutationObserver|null} */
11328
+ #mutationObserver = null;
11329
+
11330
+ /** @type {Set<string>} */
11331
+ #loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
11332
+
11333
+ /** @type {null|EventListenerOrEventListenerObject} */
11334
+ #handler = null;
11335
+
11336
+ #isAtBottom = false;
11337
+ #isAtTop = false;
11338
+ #isAtCustomTop = false;
11339
+ #isAtCustomBottom = false;
11340
+
11341
+ #querySelector = '';
11342
+ #useWindow = false;
11343
+ #destroyed = false;
11344
+ #scrollPaused = false;
11345
+ #autoScrollBottom = false;
11346
+ #observeMutations = false;
11347
+ #preserveScrollOnLayoutShift = false;
11348
+ #debounceTime = 0;
11349
+ #elemAmount = 0;
11350
+ #elemOldAmount = 0;
11351
+ #lastKnownScrollBottomOffset = 0;
11352
+ #extraScrollBoundary = 0;
10811
11353
 
10812
11354
  /** @type {Set<string>} */
10813
11355
  #attributeFilter;
@@ -11093,46 +11635,6 @@ class TinySmartScroller {
11093
11635
  this.#sizeFilter.delete(handler);
11094
11636
  }
11095
11637
 
11096
- /**
11097
- * Adds a scroll-related event listener.
11098
- *
11099
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11100
- * @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
11101
- */
11102
- on(event, handler) {
11103
- if (this.#destroyed) return;
11104
- if (typeof event !== 'string')
11105
- throw new TypeError('on(event, handler): event name must be a string');
11106
- if (typeof handler !== 'function')
11107
- throw new TypeError('on(event, handler): handler must be a function');
11108
-
11109
- if (!this.#scrollListeners[event]) this.#scrollListeners[event] = [];
11110
- this.#scrollListeners[event].push(handler);
11111
- }
11112
-
11113
- /**
11114
- * Removes a previously registered scroll-related event listener.
11115
- *
11116
- * @param {string} event - The name of the event to remove the handler from.
11117
- * @param {ScrollListenersFunc} handler - The specific callback function to remove.
11118
- */
11119
- off(event, handler) {
11120
- if (this.#destroyed) return;
11121
- if (typeof event !== 'string')
11122
- throw new TypeError('off(event, handler): event name must be a string');
11123
- if (typeof handler !== 'function')
11124
- throw new TypeError('off(event, handler): handler must be a function');
11125
-
11126
- const listeners = this.#scrollListeners[event];
11127
- if (!Array.isArray(listeners)) return;
11128
-
11129
- const index = listeners.indexOf(handler);
11130
- if (index !== -1) listeners.splice(index, 1);
11131
-
11132
- // Optionally clean up empty arrays (optional)
11133
- if (listeners.length === 0) delete this.#scrollListeners[event];
11134
- }
11135
-
11136
11638
  /**
11137
11639
  * Checks which elements inside the target are currently visible and updates internal maps.
11138
11640
  *
@@ -11157,12 +11659,10 @@ class TinySmartScroller {
11157
11659
  *
11158
11660
  * @param {string} event - Event name.
11159
11661
  * @param {*} [payload] - Optional event data payload.
11662
+ * @deprecated - Use emit() instead.
11160
11663
  */
11161
11664
  _emit(event, payload) {
11162
- if (this.#destroyed) return;
11163
- if (typeof event !== 'string')
11164
- throw new TypeError('_emit(event, payload): event name must be a string');
11165
- (this.#scrollListeners[event] || []).forEach((fn) => fn(payload));
11665
+ this.emit(event, payload);
11166
11666
  }
11167
11667
 
11168
11668
  /**
@@ -11208,13 +11708,13 @@ class TinySmartScroller {
11208
11708
  this.#lastKnownScrollBottomOffset = scrollHeight - scrollTop - clientHeight;
11209
11709
 
11210
11710
  // Send results
11211
- this._emit('onScrollBoundary', { status: atResult, ...scrollResult, scrollCache });
11212
- this._emit('onExtraScrollBoundary', { status: atCustomResult, ...scrollResult, scrollCache });
11711
+ this.emit('onScrollBoundary', { status: atResult, ...scrollResult, scrollCache });
11712
+ this.emit('onExtraScrollBoundary', { status: atCustomResult, ...scrollResult, scrollCache });
11213
11713
 
11214
11714
  if (!this.#scrollPaused) {
11215
- this._emit('onAutoScroll', { ...scrollResult, scrollCache });
11715
+ this.emit('onAutoScroll', { ...scrollResult, scrollCache });
11216
11716
  } else {
11217
- this._emit('onScrollPause', { ...scrollResult, scrollCache });
11717
+ this.emit('onScrollPause', { ...scrollResult, scrollCache });
11218
11718
  }
11219
11719
  }
11220
11720
 
@@ -11680,7 +12180,7 @@ class TinySmartScroller {
11680
12180
  this.#oldVisiblesByTime = new WeakMap();
11681
12181
 
11682
12182
  // Cleans listeners and filters
11683
- this.#scrollListeners = {};
12183
+ this.#events.offAllTypes();
11684
12184
  this.#sizeFilter.clear();
11685
12185
  this.#loadTags.clear();
11686
12186
  }
@@ -11688,7 +12188,858 @@ class TinySmartScroller {
11688
12188
 
11689
12189
  /* harmony default export */ const libs_TinySmartScroller = ((/* unused pure expression or super */ null && (TinySmartScroller)));
11690
12190
 
11691
- ;// ./src/v1/index.mjs
12191
+ ;// ./src/v1/libs/TinyLocalStorage.mjs
12192
+
12193
+
12194
+
12195
+ /** @type {Map<any, EncodeFn>} */
12196
+ const customEncoders = new Map();
12197
+
12198
+ /** @type {Map<any, DecodeFn>} */
12199
+ const customDecoders = new Map();
12200
+
12201
+ /**
12202
+ * A function that encodes a value into a serializable JSON-compatible format.
12203
+ *
12204
+ * @callback EncodeFn
12205
+ * @param {any} value - The value to encode.
12206
+ * @param {encodeSpecialJson} encodeSpecialJson - Recursive encoder helper.
12207
+ * @returns {any} The encoded value.
12208
+ */
12209
+
12210
+ /**
12211
+ * An object that defines how to check and decode a specific serialized type.
12212
+ *
12213
+ * @typedef {Object} DecodeFn
12214
+ * @property {(value: any) => any} check - Checks if the value matches the custom encoded structure.
12215
+ * @property {(value: any, decodeSpecialJson: decodeSpecialJson) => any} decode - Decodes the structure back into its original form.
12216
+ */
12217
+
12218
+ /**
12219
+ * Encodes extended JSON-compatible structures recursively.
12220
+ * @callback encodeSpecialJson
12221
+ * @param {any} value
12222
+ * @returns {any}
12223
+ */
12224
+
12225
+ /**
12226
+ * Decodes extended JSON-compatible structures recursively.
12227
+ * @callback decodeSpecialJson
12228
+ * @param {any} value
12229
+ * @returns {any}
12230
+ */
12231
+
12232
+ /**
12233
+ * Represents a value that can be safely stored and restored using JSON in `localStorage`,
12234
+ * including structures like arrays, plain objects, Map and Set.
12235
+ *
12236
+ * - `Record<string|number|symbol, any>` → plain object (e.g., `{ key: value }`)
12237
+ * - `any[]` → array of any JSON-serializable values
12238
+ * - `Map<string|number|symbol, any>` → converted to `{ __map__: true, data: [[k, v], ...] }`
12239
+ * - `Set<any>` → converted to `{ __set__: true, data: [v1, v2, ...] }`
12240
+ *
12241
+ * These conversions allow complex structures to be restored after JSON serialization.
12242
+ *
12243
+ * @typedef {(Record<string|number|symbol, any> | any[] | Map<string|number|symbol, any> | Set<any>)} LocalStorageJsonValue
12244
+ */
12245
+
12246
+ /**
12247
+ * A powerful wrapper for Web Storage (`localStorage` or `sessionStorage`) that supports
12248
+ * type-safe methods and full JSON-like structure encoding and decoding.
12249
+ *
12250
+ * `TinyLocalStorage` allows storing and retrieving complex types such as:
12251
+ * - `Map`, `Set`
12252
+ * - `Date`, `RegExp`
12253
+ * - `BigInt`, `Symbol`
12254
+ * - `undefined`, `null`
12255
+ * - Plain objects and arrays
12256
+ *
12257
+ * Includes:
12258
+ * - Type-specific `set` and `get` methods (`setDate`, `getBool`, etc.)
12259
+ * - `getValue()` to retrieve any structure regardless of type
12260
+ * - Auto-encoding/decoding with support for custom types via `registerJsonType`
12261
+ * - Built-in event system (`TinyEvents`) to listen for changes
12262
+ * - Optional fallback values on decoding errors
12263
+ *
12264
+ * Supports registering and unregistering custom types via:
12265
+ * - `registerJsonType(...)`
12266
+ * - `deleteJsonType(...)`
12267
+ *
12268
+ * This class is suitable for applications that require structured persistence in the browser.
12269
+ */
12270
+ class TinyLocalStorage {
12271
+ /** @typedef {import('./TinyEvents.mjs').handler} handler */
12272
+
12273
+ #events = new libs_TinyEvents();
12274
+
12275
+ /**
12276
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
12277
+ *
12278
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
12279
+ */
12280
+ setThrowOnMaxListeners(shouldThrow) {
12281
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
12282
+ }
12283
+
12284
+ /**
12285
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
12286
+ *
12287
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
12288
+ */
12289
+ getThrowOnMaxListeners() {
12290
+ return this.#events.getThrowOnMaxListeners();
12291
+ }
12292
+
12293
+ /////////////////////////////////////////////////////////////
12294
+
12295
+ /**
12296
+ * Adds a listener to the beginning of the listeners array for the specified event.
12297
+ *
12298
+ * @param {string} event - Event name.
12299
+ * @param {handler} handler - The callback function.
12300
+ */
12301
+ prependListener(event, handler) {
12302
+ return this.#events.prependListener(event, handler);
12303
+ }
12304
+
12305
+ /**
12306
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
12307
+ *
12308
+ * @param {string} event - Event name.
12309
+ * @param {handler} handler - The callback function.
12310
+ * @returns {handler} - The wrapped handler used internally.
12311
+ */
12312
+ prependListenerOnce(event, handler) {
12313
+ return this.#events.prependListenerOnce(event, handler);
12314
+ }
12315
+
12316
+ //////////////////////////////////////////////////////////////////////
12317
+
12318
+ /**
12319
+ * Adds a event listener.
12320
+ *
12321
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
12322
+ * @param {handler} handler - Callback function to be called when event fires.
12323
+ */
12324
+ appendListener(event, handler) {
12325
+ return this.#events.appendListener(event, handler);
12326
+ }
12327
+
12328
+ /**
12329
+ * Registers an event listener that runs only once, then is removed.
12330
+ *
12331
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
12332
+ * @param {handler} handler - The callback function to run on event.
12333
+ * @returns {handler} - The wrapped version of the handler.
12334
+ */
12335
+ appendListenerOnce(event, handler) {
12336
+ return this.#events.appendListenerOnce(event, handler);
12337
+ }
12338
+
12339
+ /**
12340
+ * Adds a event listener.
12341
+ *
12342
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
12343
+ * @param {handler} handler - Callback function to be called when event fires.
12344
+ */
12345
+ on(event, handler) {
12346
+ return this.#events.on(event, handler);
12347
+ }
12348
+
12349
+ /**
12350
+ * Registers an event listener that runs only once, then is removed.
12351
+ *
12352
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
12353
+ * @param {handler} handler - The callback function to run on event.
12354
+ * @returns {handler} - The wrapped version of the handler.
12355
+ */
12356
+ once(event, handler) {
12357
+ return this.#events.once(event, handler);
12358
+ }
12359
+
12360
+ ////////////////////////////////////////////////////////////////////
12361
+
12362
+ /**
12363
+ * Removes a previously registered event listener.
12364
+ *
12365
+ * @param {string} event - The name of the event to remove the handler from.
12366
+ * @param {handler} handler - The specific callback function to remove.
12367
+ */
12368
+ off(event, handler) {
12369
+ return this.#events.off(event, handler);
12370
+ }
12371
+
12372
+ /**
12373
+ * Removes all event listeners of a specific type from the element.
12374
+ *
12375
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
12376
+ */
12377
+ offAll(event) {
12378
+ return this.#events.offAll(event);
12379
+ }
12380
+
12381
+ /**
12382
+ * Removes all event listeners of all types from the element.
12383
+ */
12384
+ offAllTypes() {
12385
+ return this.#events.offAllTypes();
12386
+ }
12387
+
12388
+ ////////////////////////////////////////////////////////////
12389
+
12390
+ /**
12391
+ * Returns the number of listeners for a given event.
12392
+ *
12393
+ * @param {string} event - The name of the event.
12394
+ * @returns {number} Number of listeners for the event.
12395
+ */
12396
+ listenerCount(event) {
12397
+ return this.#events.listenerCount(event);
12398
+ }
12399
+
12400
+ /**
12401
+ * Returns a copy of the array of listeners for the specified event.
12402
+ *
12403
+ * @param {string} event - The name of the event.
12404
+ * @returns {handler[]} Array of listener functions.
12405
+ */
12406
+ listeners(event) {
12407
+ return this.#events.listeners(event);
12408
+ }
12409
+
12410
+ /**
12411
+ * Returns a copy of the array of listeners for the specified event.
12412
+ *
12413
+ * @param {string} event - The name of the event.
12414
+ * @returns {handler[]} Array of listener functions.
12415
+ */
12416
+ onceListeners(event) {
12417
+ return this.#events.onceListeners(event);
12418
+ }
12419
+
12420
+ /**
12421
+ * Returns a copy of the internal listeners array for the specified event,
12422
+ * including wrapper functions like those used by `.once()`.
12423
+ * @param {string | symbol} event - The event name.
12424
+ * @returns {handler[]} An array of raw listener functions.
12425
+ */
12426
+ allListeners(event) {
12427
+ return this.#events.allListeners(event);
12428
+ }
12429
+
12430
+ /**
12431
+ * Returns an array of event names for which there are registered listeners.
12432
+ *
12433
+ * @returns {string[]} Array of registered event names.
12434
+ */
12435
+ eventNames() {
12436
+ return this.#events.eventNames();
12437
+ }
12438
+
12439
+ //////////////////////////////////////////////////////
12440
+
12441
+ /**
12442
+ * Emits an event, triggering all registered handlers for that event.
12443
+ *
12444
+ * @param {string} event - The event name to emit.
12445
+ * @param {...any} payload - Optional data to pass to each handler.
12446
+ * @returns {boolean} True if any listeners were called, false otherwise.
12447
+ */
12448
+ emit(event, ...payload) {
12449
+ return this.#events.emit(event, ...payload);
12450
+ }
12451
+
12452
+ /**
12453
+ * Sets the maximum number of listeners per event before a warning is shown.
12454
+ *
12455
+ * @param {number} n - The maximum number of listeners.
12456
+ */
12457
+ setMaxListeners(n) {
12458
+ return this.#events.setMaxListeners(n);
12459
+ }
12460
+
12461
+ /**
12462
+ * Gets the maximum number of listeners allowed per event.
12463
+ *
12464
+ * @returns {number} The maximum number of listeners.
12465
+ */
12466
+ getMaxListeners() {
12467
+ return this.#events.getMaxListeners();
12468
+ }
12469
+
12470
+ ///////////////////////////////////////////////////
12471
+
12472
+ /**
12473
+ * Registers a new JSON-serializable type with its encoder and decoder.
12474
+ *
12475
+ * @param {any} type - The type or primitive type name (e.g. `"bigint"`, `"symbol"`, etc).
12476
+ * @param {EncodeFn} encodeFn - The function that encodes the value.
12477
+ * @param {DecodeFn} decodeFn - An object with `check` and `decode` methods for restoring the value.
12478
+ */
12479
+ static registerJsonType(type, encodeFn, decodeFn) {
12480
+ customEncoders.set(type, encodeFn);
12481
+ customDecoders.set(type, decodeFn);
12482
+ }
12483
+
12484
+ /**
12485
+ * Removes a previously registered custom type from the encoding/decoding system.
12486
+ *
12487
+ * @param {string} type - The primitive name or constructor reference used in registration.
12488
+ */
12489
+ static deleteJsonType(type) {
12490
+ customEncoders.delete(type);
12491
+ customDecoders.delete(type);
12492
+ }
12493
+
12494
+ //////////////////////////////////////////////////////
12495
+
12496
+ /**
12497
+ * Recursively serializes a value to a JSON-compatible format.
12498
+ *
12499
+ * This includes custom types (via `registerJsonType`), plus support for:
12500
+ * - `undefined` → `{ __undefined__: true }`
12501
+ * - `null` → `{ __null__: true }`
12502
+ *
12503
+ * @type {encodeSpecialJson}
12504
+ */
12505
+ static encodeSpecialJson(value) {
12506
+ if (typeof value === 'undefined') return { __undefined__: true };
12507
+ if (value === null) return { __null__: true };
12508
+ for (const [type, encoder] of customEncoders.entries()) {
12509
+ if ((typeof type !== 'string' && value instanceof type) || typeof value === type) {
12510
+ return encoder(value, TinyLocalStorage.encodeSpecialJson);
12511
+ }
12512
+ }
12513
+
12514
+ if (Array.isArray(value)) {
12515
+ return value.map(TinyLocalStorage.encodeSpecialJson);
12516
+ }
12517
+
12518
+ if (isJsonObject(value)) {
12519
+ const encoded = {};
12520
+ for (const key in value) {
12521
+ // @ts-ignore
12522
+ encoded[key] = TinyLocalStorage.encodeSpecialJson(value[key]);
12523
+ }
12524
+ return encoded;
12525
+ }
12526
+
12527
+ return value;
12528
+ }
12529
+
12530
+ /**
12531
+ * Recursively deserializes a JSON-compatible value into its original structure.
12532
+ *
12533
+ * Automatically handles:
12534
+ * - `__undefined__` → `undefined`
12535
+ * - `__null__` → `null`
12536
+ * - Any type registered via `registerJsonType`
12537
+ *
12538
+ * @type {decodeSpecialJson}
12539
+ */
12540
+ static decodeSpecialJson(value) {
12541
+ const isJson = isJsonObject(value);
12542
+ if (isJson) {
12543
+ if (value.__undefined__) return undefined;
12544
+ if (value.__null__) return null;
12545
+ }
12546
+
12547
+ if (Array.isArray(value)) {
12548
+ return value.map(TinyLocalStorage.decodeSpecialJson);
12549
+ }
12550
+
12551
+ if (isJson) {
12552
+ for (const [type, decoder] of customDecoders.entries()) {
12553
+ if (decoder.check && decoder.check(value)) {
12554
+ return decoder.decode(value, TinyLocalStorage.decodeSpecialJson);
12555
+ }
12556
+ }
12557
+
12558
+ const decoded = {};
12559
+ for (const key in value) {
12560
+ // @ts-ignore
12561
+ decoded[key] = TinyLocalStorage.decodeSpecialJson(value[key]);
12562
+ }
12563
+ return decoded;
12564
+ }
12565
+
12566
+ return value;
12567
+ }
12568
+
12569
+ //////////////////////////////////////////////////////
12570
+
12571
+ /** @type {Storage} */
12572
+ #localStorage = window.localStorage;
12573
+
12574
+ /** @type {(ev: StorageEvent) => any} */
12575
+ #storageEvent = (ev) => this.emit('storage', ev);
12576
+
12577
+ /**
12578
+ * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
12579
+ *
12580
+ * Adds listener for the native `storage` event to support tab synchronization.
12581
+ */
12582
+ constructor() {
12583
+ window.addEventListener('storage', this.#storageEvent);
12584
+ }
12585
+
12586
+ /**
12587
+ * Defines a custom storage interface (e.g. `sessionStorage`).
12588
+ *
12589
+ * @param {Storage} localstorage - A valid Storage object (localStorage or sessionStorage).
12590
+ */
12591
+ setLocalStorage(localstorage) {
12592
+ if (!(localstorage instanceof Storage))
12593
+ throw new Error('Argument must be a valid instance of Storage.');
12594
+ this.#localStorage = localstorage;
12595
+ }
12596
+
12597
+ /**
12598
+ * Checks if `localStorage` is supported by the current environment.
12599
+ *
12600
+ * @returns {boolean} True if `localStorage` exists, false otherwise.
12601
+ */
12602
+ localStorageExists() {
12603
+ return this.#localStorage instanceof Storage;
12604
+ }
12605
+
12606
+ /**
12607
+ * Automatically serializes nested instances.
12608
+ *
12609
+ * @param {string} name - The key under which to store the data.
12610
+ * @param {*} data - The data to be serialized.
12611
+ * @returns {*}
12612
+ */
12613
+ #setJson(name, data) {
12614
+ if (typeof name !== 'string' || !name.length)
12615
+ throw new Error('Key must be a non-empty string.');
12616
+ return TinyLocalStorage.encodeSpecialJson(data);
12617
+ }
12618
+
12619
+ /**
12620
+ * Stores a JSON-compatible value in `localStorage`.
12621
+ *
12622
+ * Automatically serializes nested `Map` and `Set` instances.
12623
+ *
12624
+ * @param {string} name - The key under which to store the data.
12625
+ * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
12626
+ */
12627
+ setJson(name, data) {
12628
+ if (
12629
+ !isJsonObject(data) &&
12630
+ !Array.isArray(data) &&
12631
+ !(data instanceof Map) &&
12632
+ !(data instanceof Set)
12633
+ ) {
12634
+ throw new Error('The storage value is not a valid JSON-compatible structure.');
12635
+ }
12636
+ const encoded = this.#setJson(name, data);
12637
+ this.emit('setJson', name, data);
12638
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12639
+ }
12640
+
12641
+ /**
12642
+ * Retrieves a value from `localStorage`.
12643
+ *
12644
+ * Automatically restores nested instances.
12645
+ *
12646
+ * @param {string} name - The key to retrieve.
12647
+ * @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
12648
+ * @returns {{ decoded: any, fallback: any }} The parsed object or fallback.
12649
+ */
12650
+ #getJson(name, defaultData) {
12651
+ if (typeof name !== 'string' || !name.length)
12652
+ throw new Error('Key must be a non-empty string.');
12653
+
12654
+ const raw = this.#localStorage.getItem(name);
12655
+ const fallbackTypes = {
12656
+ obj: () => ({}),
12657
+ array: () => [],
12658
+ map: () => new Map(),
12659
+ set: () => new Set(),
12660
+ };
12661
+
12662
+ const fallback =
12663
+ // @ts-ignore
12664
+ typeof fallbackTypes[defaultData] === 'function' ? fallbackTypes[defaultData]() : null;
12665
+
12666
+ let parsed;
12667
+
12668
+ try {
12669
+ // @ts-ignore
12670
+ parsed = JSON.parse(raw);
12671
+ } catch {
12672
+ // @ts-ignore
12673
+ return fallback;
12674
+ }
12675
+
12676
+ return { decoded: TinyLocalStorage.decodeSpecialJson(parsed), fallback };
12677
+ }
12678
+
12679
+ /**
12680
+ * Retrieves and parses a JSON value from `localStorage`.
12681
+ *
12682
+ * Automatically restores nested `Map` and `Set` instances.
12683
+ *
12684
+ * @param {string} name - The key to retrieve.
12685
+ * @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
12686
+ * @returns {LocalStorageJsonValue|null} The parsed object or fallback.
12687
+ */
12688
+ getJson(name, defaultData) {
12689
+ const { decoded, fallback } = this.#getJson(name, defaultData);
12690
+ if (
12691
+ decoded instanceof Map ||
12692
+ decoded instanceof Set ||
12693
+ Array.isArray(decoded) ||
12694
+ isJsonObject(decoded)
12695
+ )
12696
+ return decoded;
12697
+ return fallback;
12698
+ }
12699
+
12700
+ /**
12701
+ * Stores a Date in localStorage.
12702
+ * @param {string} name
12703
+ * @param {Date} data
12704
+ */
12705
+ setDate(name, data) {
12706
+ if (!(data instanceof Date)) throw new Error('Value must be a Date.');
12707
+ const encoded = this.#setJson(name, data);
12708
+ this.emit('setDate', name, data);
12709
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12710
+ }
12711
+
12712
+ /**
12713
+ * Retrieves a Date from localStorage.
12714
+ * @param {string} name
12715
+ * @returns {Date|null}
12716
+ */
12717
+ getDate(name) {
12718
+ const value = this.#getJson(name).decoded;
12719
+ return value instanceof Date ? value : null;
12720
+ }
12721
+
12722
+ /**
12723
+ * Stores a RegExp in localStorage.
12724
+ * @param {string} name
12725
+ * @param {RegExp} data
12726
+ */
12727
+ setRegExp(name, data) {
12728
+ if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
12729
+ const encoded = this.#setJson(name, data);
12730
+ this.emit('setRegExp', name, data);
12731
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12732
+ }
12733
+
12734
+ /**
12735
+ * Retrieves a RegExp from localStorage.
12736
+ * @param {string} name
12737
+ * @returns {RegExp|null}
12738
+ */
12739
+ getRegExp(name) {
12740
+ const value = this.#getJson(name).decoded;
12741
+ return value instanceof RegExp ? value : null;
12742
+ }
12743
+
12744
+ /**
12745
+ * Stores a BigInt in localStorage.
12746
+ * @param {string} name
12747
+ * @param {bigint} data
12748
+ */
12749
+ setBigInt(name, data) {
12750
+ if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
12751
+ const encoded = this.#setJson(name, data);
12752
+ this.emit('setBigInt', name, data);
12753
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12754
+ }
12755
+
12756
+ /**
12757
+ * Retrieves a BigInt from localStorage.
12758
+ * @param {string} name
12759
+ * @returns {bigint|null}
12760
+ */
12761
+ getBigInt(name) {
12762
+ const value = this.#getJson(name).decoded;
12763
+ return typeof value === 'bigint' ? value : null;
12764
+ }
12765
+
12766
+ /**
12767
+ * Stores a Symbol in localStorage.
12768
+ * Only global symbols (`Symbol.for`) will preserve the key.
12769
+ * @param {string} name
12770
+ * @param {symbol} data
12771
+ */
12772
+ setSymbol(name, data) {
12773
+ if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
12774
+ const encoded = this.#setJson(name, data);
12775
+ this.emit('setSymbol', name, data);
12776
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12777
+ }
12778
+
12779
+ /**
12780
+ * Retrieves a Symbol from localStorage.
12781
+ * @param {string} name
12782
+ * @returns {symbol|null}
12783
+ */
12784
+ getSymbol(name) {
12785
+ const value = this.#getJson(name).decoded;
12786
+ return typeof value === 'symbol' ? value : null;
12787
+ }
12788
+
12789
+ /**
12790
+ * Retrieves a value from `localStorage`.
12791
+ *
12792
+ * @param {string} name - The key to retrieve.
12793
+ * @returns {any} The stored value or null if not found.
12794
+ */
12795
+ getValue(name) {
12796
+ return this.#getJson(name).decoded ?? null;
12797
+ }
12798
+
12799
+ /**
12800
+ * Stores a raw string value in `localStorage`.
12801
+ *
12802
+ * @param {string} name - The key to use.
12803
+ * @param {any} data - The data to store.
12804
+ */
12805
+ setItem(name, data) {
12806
+ if (typeof name !== 'string' || !name.length)
12807
+ throw new Error('Key must be a non-empty string.');
12808
+ this.emit('setItem', name, data);
12809
+ return this.#localStorage.setItem(name, data);
12810
+ }
12811
+
12812
+ /**
12813
+ * Retrieves a raw string value from `localStorage`.
12814
+ *
12815
+ * @param {string} name - The key to retrieve.
12816
+ * @returns {string|null} The stored value or null if not found.
12817
+ */
12818
+ getItem(name) {
12819
+ if (typeof name !== 'string' || !name.length)
12820
+ throw new Error('Key must be a non-empty string.');
12821
+ return this.#localStorage.getItem(name);
12822
+ }
12823
+
12824
+ /**
12825
+ * Stores a string in `localStorage`, ensuring the data is a valid string.
12826
+ *
12827
+ * @param {string} name - The key to store the string under.
12828
+ * @param {string} data - The string to store.
12829
+ */
12830
+ setString(name, data) {
12831
+ if (typeof name !== 'string' || !name.length)
12832
+ throw new Error('Key must be a non-empty string.');
12833
+ if (typeof data !== 'string') throw new Error('Value must be a string.');
12834
+
12835
+ this.emit('setString', name, data);
12836
+ return this.#localStorage.setItem(name, data);
12837
+ }
12838
+
12839
+ /**
12840
+ * Retrieves a string value from `localStorage`.
12841
+ *
12842
+ * @param {string} name - The key to retrieve.
12843
+ * @returns {string|null} The string if valid, or null.
12844
+ */
12845
+ getString(name) {
12846
+ if (typeof name !== 'string' || !name.length)
12847
+ throw new Error('Key must be a non-empty string.');
12848
+ let value = this.#localStorage.getItem(name);
12849
+ if (typeof value === 'string') return value;
12850
+ return null;
12851
+ }
12852
+
12853
+ /**
12854
+ * Stores a number value in `localStorage`.
12855
+ *
12856
+ * @param {string} name - The key to use.
12857
+ * @param {number} data - The number to store.
12858
+ */
12859
+ setNumber(name, data) {
12860
+ if (typeof name !== 'string' || !name.length)
12861
+ throw new Error('Key must be a non-empty string.');
12862
+ if (typeof data !== 'number') throw new Error('Value must be a number.');
12863
+ this.emit('setNumber', name, data);
12864
+ return this.#localStorage.setItem(name, String(data));
12865
+ }
12866
+
12867
+ /**
12868
+ * Retrieves a number from `localStorage`.
12869
+ *
12870
+ * @param {string} name - The key to retrieve.
12871
+ * @returns {number|null} The number or null if invalid.
12872
+ */
12873
+ getNumber(name) {
12874
+ if (typeof name !== 'string' || !name.length)
12875
+ throw new Error('Key must be a non-empty string.');
12876
+
12877
+ /** @type {number|string|null} */
12878
+ let number = this.#localStorage.getItem(name);
12879
+ if (typeof number === 'number') return number;
12880
+ if (typeof number === 'string' && number.length > 0) {
12881
+ number = parseFloat(number);
12882
+ if (!Number.isNaN(number)) return number;
12883
+ }
12884
+ return null;
12885
+ }
12886
+
12887
+ /**
12888
+ * Stores a boolean value in `localStorage`.
12889
+ *
12890
+ * @param {string} name - The key to use.
12891
+ * @param {boolean} data - The boolean value to store.
12892
+ */
12893
+ setBool(name, data) {
12894
+ if (typeof name !== 'string' || !name.length)
12895
+ throw new Error('Key must be a non-empty string.');
12896
+ if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
12897
+ this.emit('setBool', name, data);
12898
+ return this.#localStorage.setItem(name, String(data));
12899
+ }
12900
+
12901
+ /**
12902
+ * Retrieves a boolean value from `localStorage`.
12903
+ *
12904
+ * @param {string} name - The key to retrieve.
12905
+ * @returns {boolean|null} The boolean or null if invalid.
12906
+ */
12907
+ getBool(name) {
12908
+ if (typeof name !== 'string' || !name.length)
12909
+ throw new Error('Key must be a non-empty string.');
12910
+
12911
+ const value = this.#localStorage.getItem(name);
12912
+ if (typeof value === 'boolean') return value;
12913
+ if (typeof value === 'string') {
12914
+ if (value === 'true') return true;
12915
+ if (value === 'false') return false;
12916
+ }
12917
+
12918
+ return null;
12919
+ }
12920
+
12921
+ /**
12922
+ * Removes a value from `localStorage`.
12923
+ *
12924
+ * @param {string} name - The key to remove.
12925
+ */
12926
+ removeItem(name) {
12927
+ if (typeof name !== 'string' || !name.length)
12928
+ throw new Error('Key must be a non-empty string.');
12929
+
12930
+ this.emit('removeItem', name);
12931
+ return this.#localStorage.removeItem(name);
12932
+ }
12933
+
12934
+ /**
12935
+ * Clears all data from `localStorage`.
12936
+ */
12937
+ clearLocalStorage() {
12938
+ return this.#localStorage.clear();
12939
+ }
12940
+
12941
+ /**
12942
+ * Destroys the storage instance by removing the storage event listener.
12943
+ */
12944
+ destroy() {
12945
+ window.removeEventListener('storage', this.#storageEvent);
12946
+ this.#events.offAllTypes();
12947
+ }
12948
+ }
12949
+
12950
+ // First registers
12951
+
12952
+ // Map
12953
+ TinyLocalStorage.registerJsonType(
12954
+ Map,
12955
+ (value, encodeSpecialJson) => ({
12956
+ __map__: true,
12957
+ data: Array.from(value.entries()).map(([k, v]) => [k, encodeSpecialJson(v)]),
12958
+ }),
12959
+ {
12960
+ check: (value) => value.__map__,
12961
+ /** @param {{ data: any[] }} value */
12962
+ decode: (value, decodeSpecialJson) =>
12963
+ new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
12964
+ },
12965
+ );
12966
+
12967
+ // Set
12968
+ TinyLocalStorage.registerJsonType(
12969
+ Set,
12970
+ (value, encodeSpecialJson) => ({
12971
+ __set__: true,
12972
+ data: Array.from(value).map(encodeSpecialJson),
12973
+ }),
12974
+ {
12975
+ check: (value) => value.__set__,
12976
+ decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
12977
+ },
12978
+ );
12979
+
12980
+ // Date
12981
+ TinyLocalStorage.registerJsonType(
12982
+ Date,
12983
+ (value) => ({
12984
+ __date__: true,
12985
+ value: value.toISOString(),
12986
+ }),
12987
+ {
12988
+ check: (value) => value.__date__,
12989
+ decode: (value) => new Date(value.value),
12990
+ },
12991
+ );
12992
+
12993
+ // Regex
12994
+ TinyLocalStorage.registerJsonType(
12995
+ RegExp,
12996
+ (value) => ({
12997
+ __regexp__: true,
12998
+ source: value.source,
12999
+ flags: value.flags,
13000
+ }),
13001
+ {
13002
+ check: (value) => value.__regexp__,
13003
+ decode: (value) => new RegExp(value.source, value.flags),
13004
+ },
13005
+ );
13006
+
13007
+ // Big Int
13008
+ TinyLocalStorage.registerJsonType(
13009
+ 'bigint',
13010
+ (value) => ({
13011
+ __bigint__: true,
13012
+ value: value.toString(),
13013
+ }),
13014
+ {
13015
+ check: (value) => value.__bigint__,
13016
+ decode: (value) => BigInt(value.value),
13017
+ },
13018
+ );
13019
+
13020
+ // Symbol
13021
+ TinyLocalStorage.registerJsonType(
13022
+ 'symbol',
13023
+ (value) => ({
13024
+ __symbol__: true,
13025
+ key: Symbol.keyFor(value) ?? value.description ?? null,
13026
+ }),
13027
+ {
13028
+ check: (value) => value.__symbol__,
13029
+ decode: (value) => {
13030
+ const key = value.key;
13031
+ return key != null ? Symbol.for(key) : Symbol();
13032
+ },
13033
+ },
13034
+ );
13035
+
13036
+ /* harmony default export */ const libs_TinyLocalStorage = ((/* unused pure expression or super */ null && (TinyLocalStorage)));
13037
+
13038
+ ;// ./src/v1/index.mjs
13039
+
13040
+
13041
+
13042
+
11692
13043
 
11693
13044
 
11694
13045