tiny-essentials 1.20.0 → 1.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +5 -0
  2. package/dist/v1/TinyBasicsEs.min.js +1 -1
  3. package/dist/v1/TinyDragger.min.js +1 -1
  4. package/dist/v1/TinyEssentials.min.js +1 -1
  5. package/dist/v1/TinyHtml.min.js +1 -1
  6. package/dist/v1/TinySmartScroller.min.js +1 -1
  7. package/dist/v1/TinyUploadClicker.min.js +1 -1
  8. package/dist/v1/basics/html.cjs +33 -2
  9. package/dist/v1/basics/html.d.mts +14 -4
  10. package/dist/v1/basics/html.mjs +27 -2
  11. package/dist/v1/basics/index.cjs +2 -0
  12. package/dist/v1/basics/index.d.mts +3 -1
  13. package/dist/v1/basics/index.mjs +3 -3
  14. package/dist/v1/basics/simpleMath.cjs +23 -4
  15. package/dist/v1/basics/simpleMath.d.mts +18 -4
  16. package/dist/v1/basics/simpleMath.mjs +22 -4
  17. package/dist/v1/index.cjs +2 -0
  18. package/dist/v1/index.d.mts +3 -1
  19. package/dist/v1/index.mjs +3 -3
  20. package/dist/v1/libs/TinyDragger.cjs +1 -1
  21. package/dist/v1/libs/TinyDragger.mjs +1 -1
  22. package/dist/v1/libs/TinyHtml.cjs +500 -105
  23. package/dist/v1/libs/TinyHtml.d.mts +251 -42
  24. package/dist/v1/libs/TinyHtml.mjs +448 -96
  25. package/dist/v1/libs/TinyIframeEvents.cjs +2 -1
  26. package/dist/v1/libs/TinyNewWinEvents.cjs +4 -2
  27. package/docs/v1/basics/html.md +78 -22
  28. package/docs/v1/basics/simpleMath.md +22 -4
  29. package/docs/v1/libs/TinyHtml.md +268 -6
  30. package/package.json +1 -1
  31. package/dist/v1/ColorSafeStringify.js +0 -235
  32. package/dist/v1/TinyAfterScrollWatcher.js +0 -219
  33. package/dist/v1/TinyBasicsEs.js +0 -9334
  34. package/dist/v1/TinyClipboard.js +0 -459
  35. package/dist/v1/TinyColorConverter.js +0 -617
  36. package/dist/v1/TinyDomReadyManager.js +0 -213
  37. package/dist/v1/TinyDragDropDetector.js +0 -307
  38. package/dist/v1/TinyDragger.js +0 -6569
  39. package/dist/v1/TinyEssentials.js +0 -20792
  40. package/dist/v1/TinyEvents.js +0 -402
  41. package/dist/v1/TinyHtml.js +0 -5545
  42. package/dist/v1/TinyIframeEvents.js +0 -854
  43. package/dist/v1/TinyLevelUp.js +0 -291
  44. package/dist/v1/TinyLocalStorage.js +0 -1440
  45. package/dist/v1/TinyNewWinEvents.js +0 -888
  46. package/dist/v1/TinyNotifications.js +0 -408
  47. package/dist/v1/TinyNotifyCenter.js +0 -493
  48. package/dist/v1/TinyPromiseQueue.js +0 -299
  49. package/dist/v1/TinyRateLimiter.js +0 -611
  50. package/dist/v1/TinySmartScroller.js +0 -7039
  51. package/dist/v1/TinyTextRangeEditor.js +0 -497
  52. package/dist/v1/TinyTimeout.js +0 -233
  53. package/dist/v1/TinyToastNotify.js +0 -441
  54. package/dist/v1/TinyUploadClicker.js +0 -14353
  55. package/dist/v1/UltraRandomMsgGen.js +0 -995
@@ -1,1440 +0,0 @@
1
- /******/ (() => { // webpackBootstrap
2
- /******/ "use strict";
3
- /******/ // The require scope
4
- /******/ var __webpack_require__ = {};
5
- /******/
6
- /************************************************************************/
7
- /******/ /* webpack/runtime/define property getters */
8
- /******/ (() => {
9
- /******/ // define getter functions for harmony exports
10
- /******/ __webpack_require__.d = (exports, definition) => {
11
- /******/ for(var key in definition) {
12
- /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
13
- /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
14
- /******/ }
15
- /******/ }
16
- /******/ };
17
- /******/ })();
18
- /******/
19
- /******/ /* webpack/runtime/hasOwnProperty shorthand */
20
- /******/ (() => {
21
- /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
22
- /******/ })();
23
- /******/
24
- /************************************************************************/
25
- var __webpack_exports__ = {};
26
-
27
- // EXPORTS
28
- __webpack_require__.d(__webpack_exports__, {
29
- TinyLocalStorage: () => (/* reexport */ libs_TinyLocalStorage)
30
- });
31
-
32
- ;// ./src/v1/basics/objChecker.mjs
33
- /**
34
- * Counts the number of elements in an array or the number of properties in an object.
35
- *
36
- * @param {Array<*>|Record<string | number | symbol, any>} obj - The array or object to count.
37
- * @returns {number} - The count of items (array elements or object keys), or `0` if the input is neither an array nor an object.
38
- *
39
- * @example
40
- * countObj([1, 2, 3]); // 3
41
- * countObj({ a: 1, b: 2 }); // 2
42
- * countObj('not an object'); // 0
43
- */
44
- function countObj(obj) {
45
- // Is Array
46
- if (Array.isArray(obj)) return obj.length;
47
- // Object
48
- if (isJsonObject(obj)) return Object.keys(obj).length;
49
- // Nothing
50
- return 0;
51
- }
52
-
53
- /**
54
- * Determines whether a given value is a pure JSON object (plain object).
55
- *
56
- * A pure object satisfies the following:
57
- * - It is not null.
58
- * - Its type is "object".
59
- * - Its internal [[Class]] is "[object Object]".
60
- * - It is not an instance of built-in types like Array, Date, Map, Set, etc.
61
- *
62
- * This function is useful for strict data validation when you want to ensure
63
- * a value is a clean JSON-compatible object, free of class instances or special types.
64
- *
65
- * @param {unknown} value - The value to test.
66
- * @returns {value is Record<string | number | symbol, unknown>} Returns true if the value is a pure object.
67
- */
68
- function isJsonObject(value) {
69
- if (value === null || typeof value !== 'object') return false;
70
- if (Array.isArray(value)) return false;
71
- if (Object.prototype.toString.call(value) !== '[object Object]') return false;
72
- return true;
73
- }
74
-
75
- ;// ./src/v1/libs/TinyEvents.mjs
76
- /**
77
- * A generic event listener callback function.
78
- *
79
- * @callback handler
80
- * @param {...any} payload - The data payload passed when the event is triggered.
81
- * @returns {void}
82
- */
83
-
84
- /**
85
- * TinyEvents provides a minimalistic event emitter system similar to Node.js's EventEmitter,
86
- * enabling components to subscribe to, emit, and manage events and their listeners.
87
- *
88
- * Features include:
89
- * - Adding/removing event listeners (`on`, `off`, `offAll`, `offAllTypes`)
90
- * - One-time listeners (`once`)
91
- * - Emitting events (`emit`)
92
- * - Listener inspection and limits (`listenerCount`, `listeners`, `eventNames`)
93
- * - Maximum listener control (`setMaxListeners`, `getMaxListeners`)
94
- *
95
- * This class is useful for lightweight, dependency-free publish/subscribe event handling
96
- * within modular JavaScript applications.
97
- *
98
- * @class
99
- */
100
- class TinyEvents {
101
- /** @type {Map<string, { handler: handler; config: { once: boolean } }[]>} */
102
- #listeners = new Map();
103
-
104
- /** @type {number} */
105
- #maxListeners = 10;
106
-
107
- /** @type {boolean} */
108
- #throwMaxListeners = false;
109
-
110
- /**
111
- * Enables or disables throwing an error when the maximum number of listeners is exceeded.
112
- *
113
- * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
114
- */
115
- setThrowOnMaxListeners(shouldThrow) {
116
- if (typeof shouldThrow !== 'boolean')
117
- throw new TypeError('setThrowOnMaxListeners(value): value must be a boolean');
118
- this.#throwMaxListeners = shouldThrow;
119
- }
120
-
121
- /**
122
- * Checks whether an error will be thrown when the max listener limit is exceeded.
123
- *
124
- * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
125
- */
126
- getThrowOnMaxListeners() {
127
- return this.#throwMaxListeners;
128
- }
129
-
130
- ///////////////////////////////////////////////////
131
-
132
- /**
133
- * Internal method to prepend a listener with options.
134
- *
135
- * @param {string} event - Event name.
136
- * @param {handler} handler - The callback function.
137
- * @param {Object} [settings={}] - Optional settings.
138
- * @param {boolean} [settings.once=false] - If the listener should be executed once.
139
- */
140
- #prepend(event, handler, { once = false } = {}) {
141
- let eventData = this.#listeners.get(event);
142
- if (!Array.isArray(eventData)) {
143
- eventData = [];
144
- this.#listeners.set(event, eventData);
145
- }
146
- eventData.unshift({ handler, config: { once } });
147
-
148
- const max = this.#maxListeners;
149
- if (max > 0 && eventData.length > max) {
150
- const warnMessage =
151
- `Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
152
- `Use setMaxListeners() to increase limit.`;
153
- if (!this.#throwMaxListeners) console.warn(warnMessage);
154
- else throw new Error(warnMessage);
155
- }
156
- }
157
-
158
- /**
159
- * Adds a listener to the beginning of the listeners array for the specified event.
160
- *
161
- * @param {string} event - Event name.
162
- * @param {handler} handler - The callback function.
163
- */
164
- prependListener(event, handler) {
165
- if (typeof event !== 'string')
166
- throw new TypeError('prepend(event, handler): event name must be a string');
167
- if (typeof handler !== 'function')
168
- throw new TypeError('prepend(event, handler): handler must be a function');
169
- this.#prepend(event, handler);
170
- }
171
-
172
- /**
173
- * Adds a one-time listener to the beginning of the listeners array for the specified event.
174
- *
175
- * @param {string} event - Event name.
176
- * @param {handler} handler - The callback function.
177
- * @returns {handler} - The wrapped handler used internally.
178
- */
179
- prependListenerOnce(event, handler) {
180
- if (typeof event !== 'string')
181
- throw new TypeError('prependOnceListener(event, handler): event name must be a string');
182
- if (typeof handler !== 'function')
183
- throw new TypeError('prependOnceListener(event, handler): handler must be a function');
184
-
185
- /** @type {handler} */
186
- const wrapped = (...args) => {
187
- this.off(event, wrapped);
188
- handler(...args);
189
- };
190
-
191
- this.#prepend(event, wrapped, { once: true });
192
- return wrapped;
193
- }
194
-
195
- ////////////////////////////////////////////////////////////
196
-
197
- /**
198
- * Adds a event listener.
199
- *
200
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
201
- * @param {handler} handler - Callback function to be called when event fires.
202
- * @param {Object} [settings={}] - Optional settings.
203
- * @param {boolean} [settings.once=false] - This is a once event.
204
- */
205
- #on(event, handler, { once = false } = {}) {
206
- let eventData = this.#listeners.get(event);
207
- if (!Array.isArray(eventData)) {
208
- eventData = [];
209
- this.#listeners.set(event, eventData);
210
- }
211
- eventData.push({ handler, config: { once } });
212
- // Warn if listener count exceeds the max allowed
213
- const max = this.#maxListeners;
214
- if (max > 0 && eventData.length > max) {
215
- const warnMessage =
216
- `Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
217
- `Use setMaxListeners() to increase limit.`;
218
- if (!this.#throwMaxListeners) console.warn(warnMessage);
219
- else throw new Error(warnMessage);
220
- }
221
- }
222
-
223
- /**
224
- * Adds a event listener.
225
- *
226
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
227
- * @param {handler} handler - Callback function to be called when event fires.
228
- */
229
- on(event, handler) {
230
- if (typeof event !== 'string')
231
- throw new TypeError('on(event, handler): event name must be a string');
232
- if (typeof handler !== 'function')
233
- throw new TypeError('on(event, handler): handler must be a function');
234
- return this.#on(event, handler);
235
- }
236
-
237
- /**
238
- * Registers an event listener that runs only once, then is removed.
239
- *
240
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
241
- * @param {handler} handler - The callback function to run on event.
242
- * @returns {handler} - The wrapped version of the handler.
243
- */
244
- once(event, handler) {
245
- if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
246
- if (typeof handler !== 'function')
247
- throw new TypeError('once(event, handler): handler must be a function');
248
-
249
- /** @type {handler} */
250
- const wrapped = (e) => {
251
- this.off(event, wrapped);
252
- if (typeof handler === 'function') handler(e);
253
- };
254
- this.#on(event, wrapped, { once: true });
255
- return wrapped;
256
- }
257
-
258
- /**
259
- * Adds a event listener.
260
- *
261
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
262
- * @param {handler} handler - Callback function to be called when event fires.
263
- */
264
- appendListener(event, handler) {
265
- return this.on(event, handler);
266
- }
267
-
268
- /**
269
- * Registers an event listener that runs only once, then is removed.
270
- *
271
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
272
- * @param {handler} handler - The callback function to run on event.
273
- * @returns {handler} - The wrapped version of the handler.
274
- */
275
- appendListenerOnce(event, handler) {
276
- return this.once(event, handler);
277
- }
278
-
279
- ///////////////////////////////////////////////
280
-
281
- /**
282
- * Removes a previously registered event listener.
283
- *
284
- * @param {string} event - The name of the event to remove the handler from.
285
- * @param {handler} handler - The specific callback function to remove.
286
- */
287
- off(event, handler) {
288
- if (typeof event !== 'string')
289
- throw new TypeError('off(event, handler): event name must be a string');
290
- if (typeof handler !== 'function')
291
- throw new TypeError('off(event, handler): handler must be a function');
292
-
293
- const listeners = this.#listeners.get(event);
294
- if (!Array.isArray(listeners)) return;
295
-
296
- const index = listeners.findIndex((listener) => listener.handler === handler);
297
- if (index !== -1) listeners.splice(index, 1);
298
-
299
- // Optionally clean up empty arrays (optional)
300
- if (listeners.length === 0) this.#listeners.delete(event);
301
- }
302
-
303
- /**
304
- * Removes all event listeners of a specific type from the element.
305
- *
306
- * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
307
- */
308
- offAll(event) {
309
- if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
310
- this.#listeners.delete(event);
311
- }
312
-
313
- /**
314
- * Removes all event listeners of all types from the element.
315
- */
316
- offAllTypes() {
317
- this.#listeners.clear();
318
- }
319
-
320
- /////////////////////////////////////////////
321
-
322
- /**
323
- * Returns the number of listeners for a given event.
324
- *
325
- * @param {string} event - The name of the event.
326
- * @returns {number} Number of listeners for the event.
327
- */
328
- listenerCount(event) {
329
- if (typeof event !== 'string')
330
- throw new TypeError('listenerCount(event): event name must be a string');
331
-
332
- const listeners = this.#listeners.get(event);
333
- return Array.isArray(listeners) ? listeners.length : 0;
334
- }
335
-
336
- /**
337
- * Returns a copy of the array of listeners for the specified event.
338
- *
339
- * @param {string} event - The name of the event.
340
- * @returns {handler[]} Array of listener functions.
341
- */
342
- listeners(event) {
343
- if (typeof event !== 'string')
344
- throw new TypeError('listeners(event): event name must be a string');
345
-
346
- const listeners = this.#listeners.get(event);
347
- return Array.isArray(listeners)
348
- ? [...listeners]
349
- .filter((listener) => !listener.config.once)
350
- .map((listener) => listener.handler)
351
- : [];
352
- }
353
-
354
- /**
355
- * Returns a copy of the array of listeners for the specified event.
356
- *
357
- * @param {string} event - The name of the event.
358
- * @returns {handler[]} Array of listener functions.
359
- */
360
- onceListeners(event) {
361
- if (typeof event !== 'string')
362
- throw new TypeError('onceListeners(event): event name must be a string');
363
-
364
- const listeners = this.#listeners.get(event);
365
- return Array.isArray(listeners)
366
- ? [...listeners]
367
- .filter((listener) => listener.config.once)
368
- .map((listener) => listener.handler)
369
- : [];
370
- }
371
-
372
- /**
373
- * Returns a copy of the internal listeners array for the specified event,
374
- * including wrapper functions like those used by `.once()`.
375
- * @param {string | symbol} event - The event name.
376
- * @returns {handler[]} An array of raw listener functions.
377
- */
378
- allListeners(event) {
379
- if (typeof event !== 'string')
380
- throw new TypeError('allListeners(event): event name must be a string');
381
- const listeners = this.#listeners.get(event);
382
- return Array.isArray(listeners) ? [...listeners].map((listener) => listener.handler) : [];
383
- }
384
-
385
- /**
386
- * Returns an array of event names for which there are registered listeners.
387
- *
388
- * @returns {string[]} Array of registered event names.
389
- */
390
- eventNames() {
391
- return [...this.#listeners.keys()];
392
- }
393
-
394
- /**
395
- * Emits an event, triggering all registered handlers for that event.
396
- *
397
- * @param {string} event - The event name to emit.
398
- * @param {...any} payload - Optional data to pass to each handler.
399
- * @returns {boolean} True if any listeners were called, false otherwise.
400
- */
401
- emit(event, ...payload) {
402
- if (typeof event !== 'string')
403
- throw new TypeError('emit(event, data): event name must be a string');
404
-
405
- const listeners = this.#listeners.get(event);
406
- if (!Array.isArray(listeners) || listeners.length === 0) return false;
407
-
408
- // Call all listeners with the provided data
409
- listeners.forEach((listener) => listener.handler(...payload));
410
- return true;
411
- }
412
-
413
- ///////////////////////////////////
414
-
415
- /**
416
- * Sets the maximum number of listeners per event before a warning is shown.
417
- *
418
- * @param {number} n - The maximum number of listeners.
419
- */
420
- setMaxListeners(n) {
421
- if (!Number.isInteger(n) || n < 0)
422
- throw new TypeError('setMaxListeners(n): n must be a non-negative integer');
423
- this.#maxListeners = n;
424
- }
425
-
426
- /**
427
- * Gets the maximum number of listeners allowed per event.
428
- *
429
- * @returns {number} The maximum number of listeners.
430
- */
431
- getMaxListeners() {
432
- return this.#maxListeners;
433
- }
434
- }
435
-
436
- /* harmony default export */ const libs_TinyEvents = (TinyEvents);
437
-
438
- ;// ./src/v1/libs/TinyLocalStorage.mjs
439
-
440
-
441
-
442
- /** @type {Map<any, EncodeFn>} */
443
- const customEncoders = new Map();
444
-
445
- /** @type {Map<any, DecodeFn>} */
446
- const customDecoders = new Map();
447
-
448
- /** @type {Set<any>} */
449
- const customTypesFreezed = new Set([
450
- 'string',
451
- 'boolean',
452
- 'number',
453
- 'object',
454
- 'array',
455
- String,
456
- Boolean,
457
- Number,
458
- Object,
459
- Array,
460
- BigInt,
461
- Symbol,
462
- ]);
463
-
464
- /**
465
- * A function that encodes a value into a serializable JSON-compatible format.
466
- *
467
- * @callback EncodeFn
468
- * @param {any} value - The value to encode.
469
- * @param {encodeSpecialJson} encodeSpecialJson - Recursive encoder helper.
470
- * @returns {any} The encoded value.
471
- */
472
-
473
- /**
474
- * An object that defines how to check and decode a specific serialized type.
475
- *
476
- * @typedef {Object} DecodeFn
477
- * @property {(value: any) => any} check - Checks if the value matches the custom encoded structure.
478
- * @property {(value: any, decodeSpecialJson: decodeSpecialJson) => any} decode - Decodes the structure back into its original form.
479
- */
480
-
481
- /**
482
- * Encodes extended JSON-compatible structures recursively.
483
- * @callback encodeSpecialJson
484
- * @param {any} value
485
- * @returns {any}
486
- */
487
-
488
- /**
489
- * Decodes extended JSON-compatible structures recursively.
490
- * @callback decodeSpecialJson
491
- * @param {any} value
492
- * @returns {any}
493
- */
494
-
495
- /**
496
- * Represents a value that can be safely stored and restored using JSON in `localStorage`,
497
- * including structures like arrays, plain objects, Map and Set.
498
- *
499
- * - `Record<string|number|symbol, any>` → plain object (e.g., `{ key: value }`)
500
- * - `any[]` → array of any JSON-serializable values
501
- * - `Map<string|number|symbol, any>` → converted to `{ __map__: true, data: [[k, v], ...] }`
502
- * - `Set<any>` → converted to `{ __set__: true, data: [v1, v2, ...] }`
503
- *
504
- * These conversions allow complex structures to be restored after JSON serialization.
505
- *
506
- * @typedef {(Record<string|number|symbol, any> | any[] | Map<string|number|symbol, any> | Set<any>)} LocalStorageJsonValue
507
- */
508
-
509
- /**
510
- * A powerful wrapper for Web Storage (`localStorage` or `sessionStorage`) that supports
511
- * type-safe methods and full JSON-like structure encoding and decoding.
512
- *
513
- * `TinyLocalStorage` allows storing and retrieving complex types such as:
514
- * - `Map`, `Set`
515
- * - `Date`, `RegExp`
516
- * - `BigInt`, `Symbol`
517
- * - `undefined`, `null`
518
- * - Plain objects and arrays
519
- *
520
- * Includes:
521
- * - Type-specific `set` and `get` methods (`setDate`, `getBool`, etc.)
522
- * - `getValue()` to retrieve any structure regardless of type
523
- * - Auto-encoding/decoding with support for custom types via `registerJsonType`
524
- * - Built-in event system (`TinyEvents`) to listen for changes
525
- * - Optional fallback values on decoding errors
526
- *
527
- * Supports registering and unregistering custom types via:
528
- * - `registerJsonType(...)`
529
- * - `deleteJsonType(...)`
530
- *
531
- * This class is suitable for applications that require structured persistence in the browser.
532
- */
533
- class TinyLocalStorage {
534
- /** @typedef {import('./TinyEvents.mjs').handler} handler */
535
-
536
- #events = new libs_TinyEvents();
537
-
538
- /**
539
- * Enables or disables throwing an error when the maximum number of listeners is exceeded.
540
- *
541
- * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
542
- */
543
- setThrowOnMaxListeners(shouldThrow) {
544
- return this.#events.setThrowOnMaxListeners(shouldThrow);
545
- }
546
-
547
- /**
548
- * Checks whether an error will be thrown when the max listener limit is exceeded.
549
- *
550
- * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
551
- */
552
- getThrowOnMaxListeners() {
553
- return this.#events.getThrowOnMaxListeners();
554
- }
555
-
556
- /////////////////////////////////////////////////////////////
557
-
558
- /**
559
- * Adds a listener to the beginning of the listeners array for the specified event.
560
- *
561
- * @param {string} event - Event name.
562
- * @param {handler} handler - The callback function.
563
- */
564
- prependListener(event, handler) {
565
- return this.#events.prependListener(event, handler);
566
- }
567
-
568
- /**
569
- * Adds a one-time listener to the beginning of the listeners array for the specified event.
570
- *
571
- * @param {string} event - Event name.
572
- * @param {handler} handler - The callback function.
573
- * @returns {handler} - The wrapped handler used internally.
574
- */
575
- prependListenerOnce(event, handler) {
576
- return this.#events.prependListenerOnce(event, handler);
577
- }
578
-
579
- //////////////////////////////////////////////////////////////////////
580
-
581
- /**
582
- * Adds a event listener.
583
- *
584
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
585
- * @param {handler} handler - Callback function to be called when event fires.
586
- */
587
- appendListener(event, handler) {
588
- return this.#events.appendListener(event, handler);
589
- }
590
-
591
- /**
592
- * Registers an event listener that runs only once, then is removed.
593
- *
594
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
595
- * @param {handler} handler - The callback function to run on event.
596
- * @returns {handler} - The wrapped version of the handler.
597
- */
598
- appendListenerOnce(event, handler) {
599
- return this.#events.appendListenerOnce(event, handler);
600
- }
601
-
602
- /**
603
- * Adds a event listener.
604
- *
605
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
606
- * @param {handler} handler - Callback function to be called when event fires.
607
- */
608
- on(event, handler) {
609
- return this.#events.on(event, handler);
610
- }
611
-
612
- /**
613
- * Registers an event listener that runs only once, then is removed.
614
- *
615
- * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
616
- * @param {handler} handler - The callback function to run on event.
617
- * @returns {handler} - The wrapped version of the handler.
618
- */
619
- once(event, handler) {
620
- return this.#events.once(event, handler);
621
- }
622
-
623
- ////////////////////////////////////////////////////////////////////
624
-
625
- /**
626
- * Removes a previously registered event listener.
627
- *
628
- * @param {string} event - The name of the event to remove the handler from.
629
- * @param {handler} handler - The specific callback function to remove.
630
- */
631
- off(event, handler) {
632
- return this.#events.off(event, handler);
633
- }
634
-
635
- /**
636
- * Removes all event listeners of a specific type from the element.
637
- *
638
- * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
639
- */
640
- offAll(event) {
641
- return this.#events.offAll(event);
642
- }
643
-
644
- /**
645
- * Removes all event listeners of all types from the element.
646
- */
647
- offAllTypes() {
648
- return this.#events.offAllTypes();
649
- }
650
-
651
- ////////////////////////////////////////////////////////////
652
-
653
- /**
654
- * Returns the number of listeners for a given event.
655
- *
656
- * @param {string} event - The name of the event.
657
- * @returns {number} Number of listeners for the event.
658
- */
659
- listenerCount(event) {
660
- return this.#events.listenerCount(event);
661
- }
662
-
663
- /**
664
- * Returns a copy of the array of listeners for the specified event.
665
- *
666
- * @param {string} event - The name of the event.
667
- * @returns {handler[]} Array of listener functions.
668
- */
669
- listeners(event) {
670
- return this.#events.listeners(event);
671
- }
672
-
673
- /**
674
- * Returns a copy of the array of listeners for the specified event.
675
- *
676
- * @param {string} event - The name of the event.
677
- * @returns {handler[]} Array of listener functions.
678
- */
679
- onceListeners(event) {
680
- return this.#events.onceListeners(event);
681
- }
682
-
683
- /**
684
- * Returns a copy of the internal listeners array for the specified event,
685
- * including wrapper functions like those used by `.once()`.
686
- * @param {string | symbol} event - The event name.
687
- * @returns {handler[]} An array of raw listener functions.
688
- */
689
- allListeners(event) {
690
- return this.#events.allListeners(event);
691
- }
692
-
693
- /**
694
- * Returns an array of event names for which there are registered listeners.
695
- *
696
- * @returns {string[]} Array of registered event names.
697
- */
698
- eventNames() {
699
- return this.#events.eventNames();
700
- }
701
-
702
- //////////////////////////////////////////////////////
703
-
704
- /**
705
- * Emits an event, triggering all registered handlers for that event.
706
- *
707
- * @param {string} event - The event name to emit.
708
- * @param {...any} payload - Optional data to pass to each handler.
709
- * @returns {boolean} True if any listeners were called, false otherwise.
710
- */
711
- emit(event, ...payload) {
712
- return this.#events.emit(event, ...payload);
713
- }
714
-
715
- /**
716
- * Sets the maximum number of listeners per event before a warning is shown.
717
- *
718
- * @param {number} n - The maximum number of listeners.
719
- */
720
- setMaxListeners(n) {
721
- return this.#events.setMaxListeners(n);
722
- }
723
-
724
- /**
725
- * Gets the maximum number of listeners allowed per event.
726
- *
727
- * @returns {number} The maximum number of listeners.
728
- */
729
- getMaxListeners() {
730
- return this.#events.getMaxListeners();
731
- }
732
-
733
- ///////////////////////////////////////////////////
734
-
735
- /**
736
- * Checks whether a JSON-serializable type is already registered.
737
- *
738
- * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
739
- * or a reference to a class/constructor function.
740
- * @returns {boolean} True if the type has both an encoder and decoder registered.
741
- */
742
- static hasJsonType(type) {
743
- return (customEncoders.has(type) && customDecoders.has(type)) || customTypesFreezed.has(type);
744
- }
745
-
746
- /**
747
- * Registers a new JSON-serializable type with its encoder and decoder.
748
- *
749
- * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
750
- * or a reference to a class/constructor function.
751
- * @param {EncodeFn} encodeFn - A function that receives a value of this type and returns a JSON-safe representation.
752
- * @param {DecodeFn} decodeFn - An object with `check(value)` to identify this type and `decode(value)` to restore it.
753
- * @param {boolean} [freezeType=false] - If true, prevents this type from being unregistered later.
754
- * @throws {Error} Throws an error if the type is frozen and cannot be registered.
755
- */
756
- static registerJsonType(type, encodeFn, decodeFn, freezeType = false) {
757
- if (customTypesFreezed.has(type))
758
- throw new Error(`Cannot register type "${type}" because it is frozen.`);
759
- customEncoders.set(type, encodeFn);
760
- customDecoders.set(type, decodeFn);
761
- if (freezeType) customTypesFreezed.add(type);
762
- }
763
-
764
- /**
765
- * Unregisters a previously registered custom type from the encoding/decoding system.
766
- *
767
- * @param {any} type - The primitive name or constructor reference used in the registration.
768
- * @returns {boolean} Returns true if the type existed and was removed, or false if it wasn't found.
769
- * @throws {Error} Throws an error if the type is frozen and cannot be unregistered.
770
- */
771
- static deleteJsonType(type) {
772
- if (customTypesFreezed.has(type))
773
- throw new Error(`Cannot remove type "${type}" because it is frozen.`);
774
- let isDeleted = false;
775
- if (customEncoders.delete(type)) isDeleted = true;
776
- if (customDecoders.delete(type)) isDeleted = true;
777
- return isDeleted;
778
- }
779
-
780
- //////////////////////////////////////////////////////
781
-
782
- /**
783
- * Recursively serializes a value to a JSON-compatible format.
784
- *
785
- * This includes custom types (via `registerJsonType`), plus support for:
786
- * - `undefined` → `{ __undefined__: true }`
787
- * - `null` → `{ __null__: true }`
788
- *
789
- * @type {encodeSpecialJson}
790
- */
791
- static encodeSpecialJson(value) {
792
- if (typeof value === 'undefined') return { __undefined__: true };
793
- if (value === null) return { __null__: true };
794
- for (const [type, encoder] of customEncoders.entries()) {
795
- if ((typeof type !== 'string' && value instanceof type) || typeof value === type) {
796
- return encoder(value, TinyLocalStorage.encodeSpecialJson);
797
- }
798
- }
799
-
800
- if (Array.isArray(value)) {
801
- return value.map(TinyLocalStorage.encodeSpecialJson);
802
- }
803
-
804
- if (isJsonObject(value)) {
805
- const encoded = {};
806
- for (const key in value) {
807
- // @ts-ignore
808
- encoded[key] = TinyLocalStorage.encodeSpecialJson(value[key]);
809
- }
810
- return encoded;
811
- }
812
-
813
- return value;
814
- }
815
-
816
- /**
817
- * Recursively deserializes a JSON-compatible value into its original structure.
818
- *
819
- * Automatically handles:
820
- * - `__undefined__` → `undefined`
821
- * - `__null__` → `null`
822
- * - Any type registered via `registerJsonType`
823
- *
824
- * @type {decodeSpecialJson}
825
- */
826
- static decodeSpecialJson(value) {
827
- const isJson = isJsonObject(value);
828
- if (isJson) {
829
- if (value.__undefined__) return undefined;
830
- if (value.__null__) return null;
831
- }
832
-
833
- if (Array.isArray(value)) {
834
- return value.map(TinyLocalStorage.decodeSpecialJson);
835
- }
836
-
837
- if (isJson) {
838
- for (const [type, decoder] of customDecoders.entries()) {
839
- if (decoder.check && decoder.check(value)) {
840
- return decoder.decode(value, TinyLocalStorage.decodeSpecialJson);
841
- }
842
- }
843
-
844
- const decoded = {};
845
- for (const key in value) {
846
- // @ts-ignore
847
- decoded[key] = TinyLocalStorage.decodeSpecialJson(value[key]);
848
- }
849
- return decoded;
850
- }
851
-
852
- return value;
853
- }
854
-
855
- //////////////////////////////////////////////////////
856
-
857
- /** @type {Storage} */
858
- #localStorage = window.localStorage;
859
-
860
- /** @type {string|null} */
861
- #dbKey = null;
862
-
863
- /** @type {number} */
864
- #version = 0;
865
-
866
- /** @type {(ev: StorageEvent) => any} */
867
- #storageEvent = (ev) => this.emit('storage', ev);
868
-
869
- /**
870
- * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
871
- *
872
- * Adds listener for the native `storage` event to support tab synchronization.
873
- * @param {string} [dbName] - Unique database name.
874
- */
875
- constructor(dbName) {
876
- if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
877
- throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
878
- if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
879
- window.addEventListener('storage', this.#storageEvent);
880
- }
881
-
882
- /**
883
- * Validates that a given key does not conflict with internal database keys.
884
- *
885
- * This method is used to prevent accidental overwriting of reserved `LSDB::` keys
886
- * in `localStorage`, which are used internally by TinyLocalStorage for versioning
887
- * and data management.
888
- *
889
- * @param {string} name - The key to validate before writing to localStorage.
890
- * @throws {Error} If the key starts with `LSDB::`.
891
- */
892
- #isProtectedDbKey(name) {
893
- if (typeof name === 'string' && name.startsWith('LSDB::'))
894
- throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
895
- }
896
-
897
- /**
898
- * Updates the version of the storage and triggers migration if needed.
899
- *
900
- * @param {number} version - Desired version of the database.
901
- * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
902
- * @throws {Error} If the database key has not been initialized.
903
- * @throws {TypeError} If `version` is not a valid positive number.
904
- * @throws {TypeError} If `onUpgrade` is not a function.
905
- */
906
- updateStorageVersion(version, onUpgrade) {
907
- if (typeof this.#dbKey !== 'string')
908
- throw new Error(
909
- 'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
910
- );
911
- if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
912
- throw new TypeError('TinyLocalStorage: version must be a positive number.');
913
- if (typeof onUpgrade !== 'function')
914
- throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
915
-
916
- // @ts-ignore
917
- const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
918
- if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
919
- throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
920
-
921
- if (version < savedVersion)
922
- throw new Error(
923
- `TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
924
- );
925
-
926
- if (version > savedVersion) {
927
- onUpgrade(savedVersion, version);
928
- localStorage.setItem(this.#dbKey, String(version));
929
- this.#version = version;
930
- }
931
- }
932
-
933
- /**
934
- * Gets the current database key used in localStorage.
935
- *
936
- * @returns {string|null} The database key, or null if not set.
937
- */
938
- getDbKey() {
939
- return this.#dbKey;
940
- }
941
-
942
- /**
943
- * Gets the current version of the database.
944
- *
945
- * @returns {number} The current version number.
946
- */
947
- getVersion() {
948
- return this.#version;
949
- }
950
-
951
- /**
952
- * Defines a custom storage interface (e.g. `sessionStorage`).
953
- *
954
- * @param {Storage} localstorage - A valid Storage object (localStorage or sessionStorage).
955
- */
956
- setLocalStorage(localstorage) {
957
- if (!(localstorage instanceof Storage))
958
- throw new TypeError('Argument must be a valid instance of Storage.');
959
- this.#localStorage = localstorage;
960
- }
961
-
962
- /**
963
- * Checks if `localStorage` is supported by the current environment.
964
- *
965
- * @returns {boolean} True if `localStorage` exists, false otherwise.
966
- */
967
- localStorageExists() {
968
- return this.#localStorage instanceof Storage;
969
- }
970
-
971
- /**
972
- * Automatically serializes nested instances.
973
- *
974
- * @param {string} name - The key under which to store the data.
975
- * @param {*} data - The data to be serialized.
976
- * @returns {*}
977
- */
978
- #setJson(name, data) {
979
- if (typeof name !== 'string' || !name.length)
980
- throw new TypeError('Key must be a non-empty string.');
981
- return TinyLocalStorage.encodeSpecialJson(data);
982
- }
983
-
984
- /**
985
- * Stores a JSON-compatible value in `localStorage`.
986
- *
987
- * Automatically serializes nested `Map` and `Set` instances.
988
- *
989
- * @param {string} name - The key under which to store the data.
990
- * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
991
- */
992
- setJson(name, data) {
993
- this.#isProtectedDbKey(name);
994
- if (
995
- !isJsonObject(data) &&
996
- !Array.isArray(data) &&
997
- !(data instanceof Map) &&
998
- !(data instanceof Set)
999
- ) {
1000
- throw new TypeError('The storage value is not a valid JSON-compatible structure.');
1001
- }
1002
- const encoded = this.#setJson(name, data);
1003
- this.emit('setJson', name, data);
1004
- return this.#localStorage.setItem(name, JSON.stringify(encoded));
1005
- }
1006
-
1007
- /**
1008
- * Retrieves a value from `localStorage`.
1009
- *
1010
- * Automatically restores nested instances.
1011
- *
1012
- * @param {string} name - The key to retrieve.
1013
- * @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
1014
- * @returns {{ decoded: any, fallback: any }} The parsed object or fallback.
1015
- */
1016
- #getJson(name, defaultData) {
1017
- if (typeof name !== 'string' || !name.length)
1018
- throw new TypeError('Key must be a non-empty string.');
1019
-
1020
- const raw = this.#localStorage.getItem(name);
1021
- const fallbackTypes = {
1022
- obj: () => ({}),
1023
- array: () => [],
1024
- map: () => new Map(),
1025
- set: () => new Set(),
1026
- };
1027
-
1028
- const fallback =
1029
- // @ts-ignore
1030
- typeof fallbackTypes[defaultData] === 'function' ? fallbackTypes[defaultData]() : null;
1031
-
1032
- let parsed;
1033
-
1034
- try {
1035
- // @ts-ignore
1036
- parsed = JSON.parse(raw);
1037
- } catch {
1038
- // @ts-ignore
1039
- return fallback;
1040
- }
1041
-
1042
- return { decoded: TinyLocalStorage.decodeSpecialJson(parsed), fallback };
1043
- }
1044
-
1045
- /**
1046
- * Retrieves and parses a JSON value from `localStorage`.
1047
- *
1048
- * Automatically restores nested `Map` and `Set` instances.
1049
- *
1050
- * @param {string} name - The key to retrieve.
1051
- * @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
1052
- * @returns {LocalStorageJsonValue|null} The parsed object or fallback.
1053
- */
1054
- getJson(name, defaultData) {
1055
- const { decoded, fallback } = this.#getJson(name, defaultData);
1056
- if (
1057
- decoded instanceof Map ||
1058
- decoded instanceof Set ||
1059
- Array.isArray(decoded) ||
1060
- isJsonObject(decoded)
1061
- )
1062
- return decoded;
1063
- return fallback;
1064
- }
1065
-
1066
- /**
1067
- * Stores a Date in localStorage.
1068
- * @param {string} name
1069
- * @param {Date} data
1070
- */
1071
- setDate(name, data) {
1072
- this.#isProtectedDbKey(name);
1073
- if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
1074
- const encoded = this.#setJson(name, data);
1075
- this.emit('setDate', name, data);
1076
- return this.#localStorage.setItem(name, JSON.stringify(encoded));
1077
- }
1078
-
1079
- /**
1080
- * Retrieves a Date from localStorage.
1081
- * @param {string} name
1082
- * @returns {Date|null}
1083
- */
1084
- getDate(name) {
1085
- const value = this.#getJson(name).decoded;
1086
- return value instanceof Date ? value : null;
1087
- }
1088
-
1089
- /**
1090
- * Stores a RegExp in localStorage.
1091
- * @param {string} name
1092
- * @param {RegExp} data
1093
- */
1094
- setRegExp(name, data) {
1095
- this.#isProtectedDbKey(name);
1096
- if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
1097
- const encoded = this.#setJson(name, data);
1098
- this.emit('setRegExp', name, data);
1099
- return this.#localStorage.setItem(name, JSON.stringify(encoded));
1100
- }
1101
-
1102
- /**
1103
- * Retrieves a RegExp from localStorage.
1104
- * @param {string} name
1105
- * @returns {RegExp|null}
1106
- */
1107
- getRegExp(name) {
1108
- const value = this.#getJson(name).decoded;
1109
- return value instanceof RegExp ? value : null;
1110
- }
1111
-
1112
- /**
1113
- * Stores a BigInt in localStorage.
1114
- * @param {string} name
1115
- * @param {bigint} data
1116
- */
1117
- setBigInt(name, data) {
1118
- this.#isProtectedDbKey(name);
1119
- if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
1120
- const encoded = this.#setJson(name, data);
1121
- this.emit('setBigInt', name, data);
1122
- return this.#localStorage.setItem(name, JSON.stringify(encoded));
1123
- }
1124
-
1125
- /**
1126
- * Retrieves a BigInt from localStorage.
1127
- * @param {string} name
1128
- * @returns {bigint|null}
1129
- */
1130
- getBigInt(name) {
1131
- const value = this.#getJson(name).decoded;
1132
- return typeof value === 'bigint' ? value : null;
1133
- }
1134
-
1135
- /**
1136
- * Stores a Symbol in localStorage.
1137
- * Only global symbols (`Symbol.for`) will preserve the key.
1138
- * @param {string} name
1139
- * @param {symbol} data
1140
- */
1141
- setSymbol(name, data) {
1142
- this.#isProtectedDbKey(name);
1143
- if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
1144
- const encoded = this.#setJson(name, data);
1145
- this.emit('setSymbol', name, data);
1146
- return this.#localStorage.setItem(name, JSON.stringify(encoded));
1147
- }
1148
-
1149
- /**
1150
- * Retrieves a Symbol from localStorage.
1151
- * @param {string} name
1152
- * @returns {symbol|null}
1153
- */
1154
- getSymbol(name) {
1155
- const value = this.#getJson(name).decoded;
1156
- return typeof value === 'symbol' ? value : null;
1157
- }
1158
-
1159
- /**
1160
- * Retrieves a value from `localStorage`.
1161
- *
1162
- * @param {string} name - The key to retrieve.
1163
- * @returns {any} The stored value or null if not found.
1164
- */
1165
- getValue(name) {
1166
- return this.#getJson(name).decoded ?? null;
1167
- }
1168
-
1169
- /**
1170
- * Stores a raw string value in `localStorage`.
1171
- *
1172
- * @param {string} name - The key to use.
1173
- * @param {any} data - The data to store.
1174
- */
1175
- setItem(name, data) {
1176
- this.#isProtectedDbKey(name);
1177
- if (typeof name !== 'string' || !name.length)
1178
- throw new TypeError('Key must be a non-empty string.');
1179
- this.emit('setItem', name, data);
1180
- return this.#localStorage.setItem(name, data);
1181
- }
1182
-
1183
- /**
1184
- * Retrieves a raw string value from `localStorage`.
1185
- *
1186
- * @param {string} name - The key to retrieve.
1187
- * @returns {string|null} The stored value or null if not found.
1188
- */
1189
- getItem(name) {
1190
- if (typeof name !== 'string' || !name.length)
1191
- throw new TypeError('Key must be a non-empty string.');
1192
- return this.#localStorage.getItem(name);
1193
- }
1194
-
1195
- /**
1196
- * Stores a string in `localStorage`, ensuring the data is a valid string.
1197
- *
1198
- * @param {string} name - The key to store the string under.
1199
- * @param {string} data - The string to store.
1200
- */
1201
- setString(name, data) {
1202
- this.#isProtectedDbKey(name);
1203
- if (typeof name !== 'string' || !name.length)
1204
- throw new TypeError('Key must be a non-empty string.');
1205
- if (typeof data !== 'string') throw new TypeError('Value must be a string.');
1206
-
1207
- this.emit('setString', name, data);
1208
- return this.#localStorage.setItem(
1209
- name,
1210
- JSON.stringify({
1211
- __string__: true,
1212
- value: data,
1213
- }),
1214
- );
1215
- }
1216
-
1217
- /**
1218
- * Retrieves a string value from `localStorage`.
1219
- *
1220
- * @param {string} name - The key to retrieve.
1221
- * @returns {string|null} The string if valid, or null.
1222
- */
1223
- getString(name) {
1224
- if (typeof name !== 'string' || !name.length)
1225
- throw new TypeError('Key must be a non-empty string.');
1226
- let value = this.#localStorage.getItem(name);
1227
- try {
1228
- /** @type {{ value: string; __string__: boolean }} */
1229
- // @ts-ignore
1230
- value = JSON.parse(value);
1231
- if (!isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
1232
- value = value.value;
1233
- } catch {
1234
- value = null;
1235
- }
1236
- if (typeof value === 'string') return value;
1237
- return null;
1238
- }
1239
-
1240
- /**
1241
- * Stores a number value in `localStorage`.
1242
- *
1243
- * @param {string} name - The key to use.
1244
- * @param {number} data - The number to store.
1245
- */
1246
- setNumber(name, data) {
1247
- this.#isProtectedDbKey(name);
1248
- if (typeof name !== 'string' || !name.length)
1249
- throw new TypeError('Key must be a non-empty string.');
1250
- if (typeof data !== 'number') throw new TypeError('Value must be a number.');
1251
- this.emit('setNumber', name, data);
1252
- return this.#localStorage.setItem(name, String(data));
1253
- }
1254
-
1255
- /**
1256
- * Retrieves a number from `localStorage`.
1257
- *
1258
- * @param {string} name - The key to retrieve.
1259
- * @returns {number|null} The number or null if invalid.
1260
- */
1261
- getNumber(name) {
1262
- if (typeof name !== 'string' || !name.length)
1263
- throw new TypeError('Key must be a non-empty string.');
1264
-
1265
- /** @type {number|string|null} */
1266
- let number = this.#localStorage.getItem(name);
1267
- if (typeof number === 'number') return number;
1268
- if (typeof number === 'string' && number.length > 0) {
1269
- number = parseFloat(number);
1270
- if (!Number.isNaN(number)) return number;
1271
- }
1272
- return null;
1273
- }
1274
-
1275
- /**
1276
- * Stores a boolean value in `localStorage`.
1277
- *
1278
- * @param {string} name - The key to use.
1279
- * @param {boolean} data - The boolean value to store.
1280
- */
1281
- setBool(name, data) {
1282
- this.#isProtectedDbKey(name);
1283
- if (typeof name !== 'string' || !name.length)
1284
- throw new TypeError('Key must be a non-empty string.');
1285
- if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
1286
- this.emit('setBool', name, data);
1287
- return this.#localStorage.setItem(name, String(data));
1288
- }
1289
-
1290
- /**
1291
- * Retrieves a boolean value from `localStorage`.
1292
- *
1293
- * @param {string} name - The key to retrieve.
1294
- * @returns {boolean|null} The boolean or null if invalid.
1295
- */
1296
- getBool(name) {
1297
- if (typeof name !== 'string' || !name.length)
1298
- throw new TypeError('Key must be a non-empty string.');
1299
-
1300
- const value = this.#localStorage.getItem(name);
1301
- if (typeof value === 'boolean') return value;
1302
- if (typeof value === 'string') {
1303
- if (value === 'true') return true;
1304
- if (value === 'false') return false;
1305
- }
1306
-
1307
- return null;
1308
- }
1309
-
1310
- /**
1311
- * Removes a value from `localStorage`.
1312
- *
1313
- * @param {string} name - The key to remove.
1314
- */
1315
- removeItem(name) {
1316
- if (typeof name !== 'string' || !name.length)
1317
- throw new TypeError('Key must be a non-empty string.');
1318
-
1319
- this.emit('removeItem', name);
1320
- return this.#localStorage.removeItem(name);
1321
- }
1322
-
1323
- /**
1324
- * Clears all data from `localStorage`.
1325
- */
1326
- clearLocalStorage() {
1327
- return this.#localStorage.clear();
1328
- }
1329
-
1330
- /**
1331
- * Destroys the storage instance by removing the storage event listener.
1332
- */
1333
- destroy() {
1334
- window.removeEventListener('storage', this.#storageEvent);
1335
- this.#events.offAllTypes();
1336
- }
1337
- }
1338
-
1339
- // First registers
1340
-
1341
- // Map
1342
- TinyLocalStorage.registerJsonType(
1343
- Map,
1344
- (value, encodeSpecialJson) => ({
1345
- __map__: true,
1346
- data: Array.from(value.entries()).map(([k, v]) => [k, encodeSpecialJson(v)]),
1347
- }),
1348
- {
1349
- check: (value) => value.__map__,
1350
- /** @param {{ data: any[] }} value */
1351
- decode: (value, decodeSpecialJson) =>
1352
- new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
1353
- },
1354
- true,
1355
- );
1356
-
1357
- // Set
1358
- TinyLocalStorage.registerJsonType(
1359
- Set,
1360
- (value, encodeSpecialJson) => ({
1361
- __set__: true,
1362
- data: Array.from(value).map(encodeSpecialJson),
1363
- }),
1364
- {
1365
- check: (value) => value.__set__,
1366
- decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
1367
- },
1368
- true,
1369
- );
1370
-
1371
- // Date
1372
- TinyLocalStorage.registerJsonType(
1373
- Date,
1374
- (value) => ({
1375
- __date__: true,
1376
- value: value.toISOString(),
1377
- }),
1378
- {
1379
- check: (value) => value.__date__,
1380
- decode: (value) => new Date(value.value),
1381
- },
1382
- true,
1383
- );
1384
-
1385
- // Regex
1386
- TinyLocalStorage.registerJsonType(
1387
- RegExp,
1388
- (value) => ({
1389
- __regexp__: true,
1390
- source: value.source,
1391
- flags: value.flags,
1392
- }),
1393
- {
1394
- check: (value) => value.__regexp__,
1395
- decode: (value) => new RegExp(value.source, value.flags),
1396
- },
1397
- true,
1398
- );
1399
-
1400
- // Big Int
1401
- TinyLocalStorage.registerJsonType(
1402
- 'bigint',
1403
- (value) => ({
1404
- __bigint__: true,
1405
- value: value.toString(),
1406
- }),
1407
- {
1408
- check: (value) => value.__bigint__,
1409
- decode: (value) => BigInt(value.value),
1410
- },
1411
- true,
1412
- );
1413
-
1414
- // Symbol
1415
- TinyLocalStorage.registerJsonType(
1416
- 'symbol',
1417
- (value) => ({
1418
- __symbol__: true,
1419
- key: Symbol.keyFor(value) ?? value.description ?? null,
1420
- }),
1421
- {
1422
- check: (value) => value.__symbol__,
1423
- decode: (value) => {
1424
- const key = value.key;
1425
- return key != null ? Symbol.for(key) : Symbol();
1426
- },
1427
- },
1428
- true,
1429
- );
1430
-
1431
- /* harmony default export */ const libs_TinyLocalStorage = (TinyLocalStorage);
1432
-
1433
- ;// ./src/v1/build/TinyLocalStorage.mjs
1434
-
1435
-
1436
-
1437
-
1438
- window.TinyLocalStorage = __webpack_exports__.TinyLocalStorage;
1439
- /******/ })()
1440
- ;