tiny-essentials 1.19.2 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/v1/TinyEssentials.js +1009 -22
  2. package/dist/v1/TinyEssentials.min.js +1 -1
  3. package/dist/v1/TinyIframeEvents.js +854 -0
  4. package/dist/v1/TinyIframeEvents.min.js +1 -0
  5. package/dist/v1/TinyLocalStorage.js +109 -21
  6. package/dist/v1/TinyLocalStorage.min.js +1 -1
  7. package/dist/v1/TinyNewWinEvents.js +888 -0
  8. package/dist/v1/TinyNewWinEvents.min.js +1 -0
  9. package/dist/v1/TinyUploadClicker.js +1022 -37
  10. package/dist/v1/TinyUploadClicker.min.js +1 -1
  11. package/dist/v1/build/TinyIframeEvents.cjs +7 -0
  12. package/dist/v1/build/TinyIframeEvents.d.mts +3 -0
  13. package/dist/v1/build/TinyIframeEvents.mjs +2 -0
  14. package/dist/v1/build/TinyNewWinEvents.cjs +7 -0
  15. package/dist/v1/build/TinyNewWinEvents.d.mts +3 -0
  16. package/dist/v1/build/TinyNewWinEvents.mjs +2 -0
  17. package/dist/v1/index.cjs +4 -0
  18. package/dist/v1/index.d.mts +3 -1
  19. package/dist/v1/index.mjs +3 -1
  20. package/dist/v1/libs/TinyIframeEvents.cjs +409 -0
  21. package/dist/v1/libs/TinyIframeEvents.d.mts +193 -0
  22. package/dist/v1/libs/TinyIframeEvents.mjs +348 -0
  23. package/dist/v1/libs/TinyLocalStorage.cjs +109 -21
  24. package/dist/v1/libs/TinyLocalStorage.d.mts +29 -0
  25. package/dist/v1/libs/TinyLocalStorage.mjs +97 -21
  26. package/dist/v1/libs/TinyNewWinEvents.cjs +486 -0
  27. package/dist/v1/libs/TinyNewWinEvents.d.mts +241 -0
  28. package/dist/v1/libs/TinyNewWinEvents.mjs +437 -0
  29. package/docs/v1/README.md +2 -0
  30. package/docs/v1/libs/TinyIframeEvents.md +149 -0
  31. package/docs/v1/libs/TinyLocalStorage.md +53 -0
  32. package/docs/v1/libs/TinyNewWinEvents.md +186 -0
  33. package/docs/v1/libs/TinyNotifyCenter.md +1 -1
  34. package/package.json +1 -1
@@ -2894,8 +2894,10 @@ __webpack_require__.d(__webpack_exports__, {
2894
2894
  TinyDragger: () => (/* reexport */ libs_TinyDragger),
2895
2895
  TinyEvents: () => (/* reexport */ libs_TinyEvents),
2896
2896
  TinyHtml: () => (/* reexport */ libs_TinyHtml),
2897
+ TinyIframeEvents: () => (/* reexport */ libs_TinyIframeEvents),
2897
2898
  TinyLevelUp: () => (/* reexport */ userLevel),
2898
2899
  TinyLocalStorage: () => (/* reexport */ libs_TinyLocalStorage),
2900
+ TinyNewWinEvents: () => (/* reexport */ libs_TinyNewWinEvents),
2899
2901
  TinyNotifications: () => (/* reexport */ libs_TinyNotifications),
2900
2902
  TinyNotifyCenter: () => (/* reexport */ libs_TinyNotifyCenter),
2901
2903
  TinyPromiseQueue: () => (/* reexport */ libs_TinyPromiseQueue),
@@ -19272,6 +19274,12 @@ class TinyLocalStorage {
19272
19274
  /** @type {Storage} */
19273
19275
  #localStorage = window.localStorage;
19274
19276
 
19277
+ /** @type {string|null} */
19278
+ #dbKey = null;
19279
+
19280
+ /** @type {number} */
19281
+ #version = 0;
19282
+
19275
19283
  /** @type {(ev: StorageEvent) => any} */
19276
19284
  #storageEvent = (ev) => this.emit('storage', ev);
19277
19285
 
@@ -19279,11 +19287,84 @@ class TinyLocalStorage {
19279
19287
  * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
19280
19288
  *
19281
19289
  * Adds listener for the native `storage` event to support tab synchronization.
19290
+ * @param {string} [dbName] - Unique database name.
19282
19291
  */
19283
- constructor() {
19292
+ constructor(dbName) {
19293
+ if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
19294
+ throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
19295
+ if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
19284
19296
  window.addEventListener('storage', this.#storageEvent);
19285
19297
  }
19286
19298
 
19299
+ /**
19300
+ * Validates that a given key does not conflict with internal database keys.
19301
+ *
19302
+ * This method is used to prevent accidental overwriting of reserved `LSDB::` keys
19303
+ * in `localStorage`, which are used internally by TinyLocalStorage for versioning
19304
+ * and data management.
19305
+ *
19306
+ * @param {string} name - The key to validate before writing to localStorage.
19307
+ * @throws {Error} If the key starts with `LSDB::`.
19308
+ */
19309
+ #isProtectedDbKey(name) {
19310
+ if (typeof name === 'string' && name.startsWith('LSDB::'))
19311
+ throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
19312
+ }
19313
+
19314
+ /**
19315
+ * Updates the version of the storage and triggers migration if needed.
19316
+ *
19317
+ * @param {number} version - Desired version of the database.
19318
+ * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
19319
+ * @throws {Error} If the database key has not been initialized.
19320
+ * @throws {TypeError} If `version` is not a valid positive number.
19321
+ * @throws {TypeError} If `onUpgrade` is not a function.
19322
+ */
19323
+ updateStorageVersion(version, onUpgrade) {
19324
+ if (typeof this.#dbKey !== 'string')
19325
+ throw new Error(
19326
+ 'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
19327
+ );
19328
+ if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
19329
+ throw new TypeError('TinyLocalStorage: version must be a positive number.');
19330
+ if (typeof onUpgrade !== 'function')
19331
+ throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
19332
+
19333
+ // @ts-ignore
19334
+ const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
19335
+ if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
19336
+ throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
19337
+
19338
+ if (version < savedVersion)
19339
+ throw new Error(
19340
+ `TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
19341
+ );
19342
+
19343
+ if (version > savedVersion) {
19344
+ onUpgrade(savedVersion, version);
19345
+ localStorage.setItem(this.#dbKey, String(version));
19346
+ this.#version = version;
19347
+ }
19348
+ }
19349
+
19350
+ /**
19351
+ * Gets the current database key used in localStorage.
19352
+ *
19353
+ * @returns {string|null} The database key, or null if not set.
19354
+ */
19355
+ getDbKey() {
19356
+ return this.#dbKey;
19357
+ }
19358
+
19359
+ /**
19360
+ * Gets the current version of the database.
19361
+ *
19362
+ * @returns {number} The current version number.
19363
+ */
19364
+ getVersion() {
19365
+ return this.#version;
19366
+ }
19367
+
19287
19368
  /**
19288
19369
  * Defines a custom storage interface (e.g. `sessionStorage`).
19289
19370
  *
@@ -19291,7 +19372,7 @@ class TinyLocalStorage {
19291
19372
  */
19292
19373
  setLocalStorage(localstorage) {
19293
19374
  if (!(localstorage instanceof Storage))
19294
- throw new Error('Argument must be a valid instance of Storage.');
19375
+ throw new TypeError('Argument must be a valid instance of Storage.');
19295
19376
  this.#localStorage = localstorage;
19296
19377
  }
19297
19378
 
@@ -19313,7 +19394,7 @@ class TinyLocalStorage {
19313
19394
  */
19314
19395
  #setJson(name, data) {
19315
19396
  if (typeof name !== 'string' || !name.length)
19316
- throw new Error('Key must be a non-empty string.');
19397
+ throw new TypeError('Key must be a non-empty string.');
19317
19398
  return TinyLocalStorage.encodeSpecialJson(data);
19318
19399
  }
19319
19400
 
@@ -19326,13 +19407,14 @@ class TinyLocalStorage {
19326
19407
  * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
19327
19408
  */
19328
19409
  setJson(name, data) {
19410
+ this.#isProtectedDbKey(name);
19329
19411
  if (
19330
19412
  !isJsonObject(data) &&
19331
19413
  !Array.isArray(data) &&
19332
19414
  !(data instanceof Map) &&
19333
19415
  !(data instanceof Set)
19334
19416
  ) {
19335
- throw new Error('The storage value is not a valid JSON-compatible structure.');
19417
+ throw new TypeError('The storage value is not a valid JSON-compatible structure.');
19336
19418
  }
19337
19419
  const encoded = this.#setJson(name, data);
19338
19420
  this.emit('setJson', name, data);
@@ -19350,7 +19432,7 @@ class TinyLocalStorage {
19350
19432
  */
19351
19433
  #getJson(name, defaultData) {
19352
19434
  if (typeof name !== 'string' || !name.length)
19353
- throw new Error('Key must be a non-empty string.');
19435
+ throw new TypeError('Key must be a non-empty string.');
19354
19436
 
19355
19437
  const raw = this.#localStorage.getItem(name);
19356
19438
  const fallbackTypes = {
@@ -19404,7 +19486,8 @@ class TinyLocalStorage {
19404
19486
  * @param {Date} data
19405
19487
  */
19406
19488
  setDate(name, data) {
19407
- if (!(data instanceof Date)) throw new Error('Value must be a Date.');
19489
+ this.#isProtectedDbKey(name);
19490
+ if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
19408
19491
  const encoded = this.#setJson(name, data);
19409
19492
  this.emit('setDate', name, data);
19410
19493
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -19426,7 +19509,8 @@ class TinyLocalStorage {
19426
19509
  * @param {RegExp} data
19427
19510
  */
19428
19511
  setRegExp(name, data) {
19429
- if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
19512
+ this.#isProtectedDbKey(name);
19513
+ if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
19430
19514
  const encoded = this.#setJson(name, data);
19431
19515
  this.emit('setRegExp', name, data);
19432
19516
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -19448,7 +19532,8 @@ class TinyLocalStorage {
19448
19532
  * @param {bigint} data
19449
19533
  */
19450
19534
  setBigInt(name, data) {
19451
- if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
19535
+ this.#isProtectedDbKey(name);
19536
+ if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
19452
19537
  const encoded = this.#setJson(name, data);
19453
19538
  this.emit('setBigInt', name, data);
19454
19539
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -19471,7 +19556,8 @@ class TinyLocalStorage {
19471
19556
  * @param {symbol} data
19472
19557
  */
19473
19558
  setSymbol(name, data) {
19474
- if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
19559
+ this.#isProtectedDbKey(name);
19560
+ if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
19475
19561
  const encoded = this.#setJson(name, data);
19476
19562
  this.emit('setSymbol', name, data);
19477
19563
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -19504,8 +19590,9 @@ class TinyLocalStorage {
19504
19590
  * @param {any} data - The data to store.
19505
19591
  */
19506
19592
  setItem(name, data) {
19593
+ this.#isProtectedDbKey(name);
19507
19594
  if (typeof name !== 'string' || !name.length)
19508
- throw new Error('Key must be a non-empty string.');
19595
+ throw new TypeError('Key must be a non-empty string.');
19509
19596
  this.emit('setItem', name, data);
19510
19597
  return this.#localStorage.setItem(name, data);
19511
19598
  }
@@ -19518,7 +19605,7 @@ class TinyLocalStorage {
19518
19605
  */
19519
19606
  getItem(name) {
19520
19607
  if (typeof name !== 'string' || !name.length)
19521
- throw new Error('Key must be a non-empty string.');
19608
+ throw new TypeError('Key must be a non-empty string.');
19522
19609
  return this.#localStorage.getItem(name);
19523
19610
  }
19524
19611
 
@@ -19529,9 +19616,10 @@ class TinyLocalStorage {
19529
19616
  * @param {string} data - The string to store.
19530
19617
  */
19531
19618
  setString(name, data) {
19619
+ this.#isProtectedDbKey(name);
19532
19620
  if (typeof name !== 'string' || !name.length)
19533
- throw new Error('Key must be a non-empty string.');
19534
- if (typeof data !== 'string') throw new Error('Value must be a string.');
19621
+ throw new TypeError('Key must be a non-empty string.');
19622
+ if (typeof data !== 'string') throw new TypeError('Value must be a string.');
19535
19623
 
19536
19624
  this.emit('setString', name, data);
19537
19625
  return this.#localStorage.setItem(
@@ -19551,7 +19639,7 @@ class TinyLocalStorage {
19551
19639
  */
19552
19640
  getString(name) {
19553
19641
  if (typeof name !== 'string' || !name.length)
19554
- throw new Error('Key must be a non-empty string.');
19642
+ throw new TypeError('Key must be a non-empty string.');
19555
19643
  let value = this.#localStorage.getItem(name);
19556
19644
  try {
19557
19645
  /** @type {{ value: string; __string__: boolean }} */
@@ -19573,9 +19661,10 @@ class TinyLocalStorage {
19573
19661
  * @param {number} data - The number to store.
19574
19662
  */
19575
19663
  setNumber(name, data) {
19664
+ this.#isProtectedDbKey(name);
19576
19665
  if (typeof name !== 'string' || !name.length)
19577
- throw new Error('Key must be a non-empty string.');
19578
- if (typeof data !== 'number') throw new Error('Value must be a number.');
19666
+ throw new TypeError('Key must be a non-empty string.');
19667
+ if (typeof data !== 'number') throw new TypeError('Value must be a number.');
19579
19668
  this.emit('setNumber', name, data);
19580
19669
  return this.#localStorage.setItem(name, String(data));
19581
19670
  }
@@ -19588,7 +19677,7 @@ class TinyLocalStorage {
19588
19677
  */
19589
19678
  getNumber(name) {
19590
19679
  if (typeof name !== 'string' || !name.length)
19591
- throw new Error('Key must be a non-empty string.');
19680
+ throw new TypeError('Key must be a non-empty string.');
19592
19681
 
19593
19682
  /** @type {number|string|null} */
19594
19683
  let number = this.#localStorage.getItem(name);
@@ -19607,9 +19696,10 @@ class TinyLocalStorage {
19607
19696
  * @param {boolean} data - The boolean value to store.
19608
19697
  */
19609
19698
  setBool(name, data) {
19699
+ this.#isProtectedDbKey(name);
19610
19700
  if (typeof name !== 'string' || !name.length)
19611
- throw new Error('Key must be a non-empty string.');
19612
- if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
19701
+ throw new TypeError('Key must be a non-empty string.');
19702
+ if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
19613
19703
  this.emit('setBool', name, data);
19614
19704
  return this.#localStorage.setItem(name, String(data));
19615
19705
  }
@@ -19622,7 +19712,7 @@ class TinyLocalStorage {
19622
19712
  */
19623
19713
  getBool(name) {
19624
19714
  if (typeof name !== 'string' || !name.length)
19625
- throw new Error('Key must be a non-empty string.');
19715
+ throw new TypeError('Key must be a non-empty string.');
19626
19716
 
19627
19717
  const value = this.#localStorage.getItem(name);
19628
19718
  if (typeof value === 'boolean') return value;
@@ -19641,7 +19731,7 @@ class TinyLocalStorage {
19641
19731
  */
19642
19732
  removeItem(name) {
19643
19733
  if (typeof name !== 'string' || !name.length)
19644
- throw new Error('Key must be a non-empty string.');
19734
+ throw new TypeError('Key must be a non-empty string.');
19645
19735
 
19646
19736
  this.emit('removeItem', name);
19647
19737
  return this.#localStorage.removeItem(name);
@@ -19757,7 +19847,904 @@ TinyLocalStorage.registerJsonType(
19757
19847
 
19758
19848
  /* harmony default export */ const libs_TinyLocalStorage = (TinyLocalStorage);
19759
19849
 
19760
- ;// ./src/v1/index.mjs
19850
+ ;// ./src/v1/libs/TinyIframeEvents.mjs
19851
+
19852
+
19853
+
19854
+ /** @type {WeakMap<Window, TinyIframeEvents>} */
19855
+ const instances = new WeakMap();
19856
+
19857
+ /**
19858
+ * @callback handler
19859
+ * A function to handle incoming event payloads.
19860
+ * @param {any} payload - The data sent by the emitter.
19861
+ * @param {MessageEvent<any>} event - Metadata about the message.
19862
+ */
19863
+
19864
+ /**
19865
+ * A flexible event routing system for structured communication
19866
+ * between a parent window and its iframe using `postMessage`.
19867
+ *
19868
+ * This class abstracts the complexity of cross-origin and window-type handling,
19869
+ * allowing both the iframe and parent to:
19870
+ * - Send events with arbitrary payloads
19871
+ * - Listen to specific event names
19872
+ * - Filter events by origin and source
19873
+ * - Work symmetrically from both sides with automatic direction handling
19874
+ *
19875
+ * Use this class when building applications that require modular, event-driven
19876
+ * communication across embedded frames.
19877
+ */
19878
+ class TinyIframeEvents {
19879
+ #events = new libs_TinyEvents();
19880
+
19881
+ /**
19882
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
19883
+ *
19884
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
19885
+ */
19886
+ setThrowOnMaxListeners(shouldThrow) {
19887
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
19888
+ }
19889
+
19890
+ /**
19891
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
19892
+ *
19893
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
19894
+ */
19895
+ getThrowOnMaxListeners() {
19896
+ return this.#events.getThrowOnMaxListeners();
19897
+ }
19898
+
19899
+ /////////////////////////////////////////////////////////////
19900
+
19901
+ /**
19902
+ * Adds a listener to the beginning of the listeners array for the specified event.
19903
+ *
19904
+ * @param {string} event - Event name.
19905
+ * @param {handler} handler - The callback function.
19906
+ */
19907
+ prependListener(event, handler) {
19908
+ return this.#events.prependListener(event, handler);
19909
+ }
19910
+
19911
+ /**
19912
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
19913
+ *
19914
+ * @param {string} event - Event name.
19915
+ * @param {handler} handler - The callback function.
19916
+ * @returns {handler} - The wrapped handler used internally.
19917
+ */
19918
+ prependListenerOnce(event, handler) {
19919
+ return this.#events.prependListenerOnce(event, handler);
19920
+ }
19921
+
19922
+ //////////////////////////////////////////////////////////////////////
19923
+
19924
+ /**
19925
+ * Adds a event listener.
19926
+ *
19927
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
19928
+ * @param {handler} handler - Callback function to be called when event fires.
19929
+ */
19930
+ appendListener(event, handler) {
19931
+ return this.#events.appendListener(event, handler);
19932
+ }
19933
+
19934
+ /**
19935
+ * Registers an event listener that runs only once, then is removed.
19936
+ *
19937
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
19938
+ * @param {handler} handler - The callback function to run on event.
19939
+ * @returns {handler} - The wrapped version of the handler.
19940
+ */
19941
+ appendListenerOnce(event, handler) {
19942
+ return this.#events.appendListenerOnce(event, handler);
19943
+ }
19944
+
19945
+ /**
19946
+ * Adds a event listener.
19947
+ *
19948
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
19949
+ * @param {handler} handler - Callback function to be called when event fires.
19950
+ */
19951
+ on(event, handler) {
19952
+ return this.#events.on(event, handler);
19953
+ }
19954
+
19955
+ /**
19956
+ * Registers an event listener that runs only once, then is removed.
19957
+ *
19958
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
19959
+ * @param {handler} handler - The callback function to run on event.
19960
+ * @returns {handler} - The wrapped version of the handler.
19961
+ */
19962
+ once(event, handler) {
19963
+ return this.#events.once(event, handler);
19964
+ }
19965
+
19966
+ ////////////////////////////////////////////////////////////////////
19967
+
19968
+ /**
19969
+ * Removes a previously registered event listener.
19970
+ *
19971
+ * @param {string} event - The name of the event to remove the handler from.
19972
+ * @param {handler} handler - The specific callback function to remove.
19973
+ */
19974
+ off(event, handler) {
19975
+ return this.#events.off(event, handler);
19976
+ }
19977
+
19978
+ /**
19979
+ * Removes all event listeners of a specific type from the element.
19980
+ *
19981
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
19982
+ */
19983
+ offAll(event) {
19984
+ return this.#events.offAll(event);
19985
+ }
19986
+
19987
+ /**
19988
+ * Removes all event listeners of all types from the element.
19989
+ */
19990
+ offAllTypes() {
19991
+ return this.#events.offAllTypes();
19992
+ }
19993
+
19994
+ ////////////////////////////////////////////////////////////
19995
+
19996
+ /**
19997
+ * Returns the number of listeners for a given event.
19998
+ *
19999
+ * @param {string} event - The name of the event.
20000
+ * @returns {number} Number of listeners for the event.
20001
+ */
20002
+ listenerCount(event) {
20003
+ return this.#events.listenerCount(event);
20004
+ }
20005
+
20006
+ /**
20007
+ * Returns a copy of the array of listeners for the specified event.
20008
+ *
20009
+ * @param {string} event - The name of the event.
20010
+ * @returns {handler[]} Array of listener functions.
20011
+ */
20012
+ listeners(event) {
20013
+ return this.#events.listeners(event);
20014
+ }
20015
+
20016
+ /**
20017
+ * Returns a copy of the array of listeners for the specified event.
20018
+ *
20019
+ * @param {string} event - The name of the event.
20020
+ * @returns {handler[]} Array of listener functions.
20021
+ */
20022
+ onceListeners(event) {
20023
+ return this.#events.onceListeners(event);
20024
+ }
20025
+
20026
+ /**
20027
+ * Returns a copy of the internal listeners array for the specified event,
20028
+ * including wrapper functions like those used by `.once()`.
20029
+ * @param {string | symbol} event - The event name.
20030
+ * @returns {handler[]} An array of raw listener functions.
20031
+ */
20032
+ allListeners(event) {
20033
+ return this.#events.allListeners(event);
20034
+ }
20035
+
20036
+ /**
20037
+ * Returns an array of event names for which there are registered listeners.
20038
+ *
20039
+ * @returns {string[]} Array of registered event names.
20040
+ */
20041
+ eventNames() {
20042
+ return this.#events.eventNames();
20043
+ }
20044
+
20045
+ //////////////////////////////////////////////////////
20046
+
20047
+ /**
20048
+ * Sets the maximum number of listeners per event before a warning is shown.
20049
+ *
20050
+ * @param {number} n - The maximum number of listeners.
20051
+ */
20052
+ setMaxListeners(n) {
20053
+ return this.#events.setMaxListeners(n);
20054
+ }
20055
+
20056
+ /**
20057
+ * Gets the maximum number of listeners allowed per event.
20058
+ *
20059
+ * @returns {number} The maximum number of listeners.
20060
+ */
20061
+ getMaxListeners() {
20062
+ return this.#events.getMaxListeners();
20063
+ }
20064
+
20065
+ ///////////////////////////////////////////////////
20066
+
20067
+ /** @type {Window} */
20068
+ #targetWindow;
20069
+
20070
+ /** @type {string} */
20071
+ #targetOrigin;
20072
+
20073
+ /** @type {string} */
20074
+ #selfType;
20075
+
20076
+ /** @type {boolean} */
20077
+ #isDestroyed = false;
20078
+
20079
+ /** @type {boolean} */
20080
+ #ready = false;
20081
+
20082
+ /**
20083
+ * @typedef {object} IframeEventBase
20084
+ * @property {string} eventName - The name of the custom event route.
20085
+ * @property {any} payload - The data being sent (can be any type).
20086
+ * @property {'iframe' | 'parent'} direction - Indicates the sender: 'iframe' or 'parent'.
20087
+ */
20088
+
20089
+ /**
20090
+ * Queue of messages emitted before connection is ready
20091
+ * @type {IframeEventBase[]}
20092
+ */
20093
+ #pendingQueue = [];
20094
+
20095
+ /** @type {string} Internal message type for routed communication */
20096
+ #secretEventName = '__tinyIframeEvent__';
20097
+
20098
+ /**
20099
+ * Gets the internal secret iframe event name.
20100
+ * @returns {string}
20101
+ */
20102
+ get secretEventName() {
20103
+ return this.#secretEventName;
20104
+ }
20105
+
20106
+ /**
20107
+ * Sets the internal secret iframe event name.
20108
+ * @param {string} name
20109
+ * @throws {TypeError} If the value is not a string.
20110
+ */
20111
+ set secretEventName(name) {
20112
+ if (typeof name !== 'string') throw new TypeError('TinyIframeEvents: secretEventName must be a string.');
20113
+ this.#secretEventName = name;
20114
+ }
20115
+
20116
+ /**
20117
+ * Creates a new TinyIframeEvents instance to manage communication between iframe and parent.
20118
+ * Automatically determines the current context (`iframe` or `parent`) based on the `targetWindow`.
20119
+ *
20120
+ * @param {Object} config - Configuration object.
20121
+ * @param {HTMLIFrameElement} [config.targetIframe] - The target window to post messages to. Defaults to `window.parent` (assumes this is inside an iframe).
20122
+ * @param {string} [config.targetOrigin] - The target origin to restrict messages to. Defaults to `window.location.origin`.
20123
+ */
20124
+ constructor({ targetIframe, targetOrigin } = {}) {
20125
+ if (
20126
+ typeof targetIframe !== 'undefined' &&
20127
+ (!(targetIframe instanceof HTMLIFrameElement) || !targetIframe.contentWindow)
20128
+ )
20129
+ throw new TypeError(
20130
+ `[TinyIframeEvents] Invalid "targetIframe" provided: expected a HTML Iframe Element, received ${typeof targetIframe}`,
20131
+ );
20132
+ if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
20133
+ throw new TypeError(
20134
+ `[TinyIframeEvents] Invalid "targetOrigin" provided: expected a string, received ${typeof targetOrigin}`,
20135
+ );
20136
+
20137
+ this.#targetWindow = targetIframe?.contentWindow ?? window.parent;
20138
+ this.#targetOrigin = targetOrigin ?? window.location.origin;
20139
+ this.#selfType = !targetIframe ? 'iframe' : 'parent';
20140
+ if (instances.has(this.#targetWindow)) throw new Error('Duplicate window reference.');
20141
+
20142
+ this._boundOnMessage = this.#onMessage.bind(this);
20143
+ this._boundOnceMessage = this.#onceMessage.bind(this);
20144
+
20145
+ if (
20146
+ this.#targetWindow.document.readyState === 'complete' ||
20147
+ this.#targetWindow.document.readyState === 'interactive'
20148
+ )
20149
+ this.#onceMessage();
20150
+ else {
20151
+ this.#targetWindow.addEventListener('load', this._boundOnceMessage, false);
20152
+ this.#targetWindow.addEventListener('DOMContentLoaded', this._boundOnceMessage, false);
20153
+ }
20154
+
20155
+ window.addEventListener('message', this._boundOnMessage, false);
20156
+ instances.set(this.#targetWindow, this);
20157
+ }
20158
+
20159
+ /**
20160
+ * Marks the communication as ready and flushes any queued messages.
20161
+ */
20162
+ #onceMessage() {
20163
+ if (this.#ready) return;
20164
+ this.#ready = true;
20165
+ this.#flushQueue();
20166
+ }
20167
+
20168
+ /**
20169
+ * Internal handler for the message event. Filters and dispatches incoming messages.
20170
+ *
20171
+ * @param {MessageEvent<any>} event - The message event received via `postMessage`.
20172
+ */
20173
+ #onMessage(event) {
20174
+ const { data, source } = event;
20175
+
20176
+ // Reject non-object or unrelated messages
20177
+ if (!isJsonObject(data) || !data[this.#secretEventName]) return;
20178
+
20179
+ const { eventName, payload, direction } = data;
20180
+
20181
+ // Reject if not from the expected window (for security)
20182
+ if (source !== this.#targetWindow) return;
20183
+
20184
+ // Reject if direction is not meant for this side
20185
+ if (
20186
+ (this.#selfType === 'iframe' && direction !== 'iframe') ||
20187
+ (this.#selfType === 'parent' && direction !== 'parent')
20188
+ )
20189
+ return;
20190
+
20191
+ this.#events.emit(/** @type {string} */ (eventName), payload, event);
20192
+ }
20193
+
20194
+ /**
20195
+ * Sends an event to the target window.
20196
+ *
20197
+ * @param {string} eventName - A unique name identifying the event.
20198
+ * @param {*} payload - The data to send with the event. Can be any serializable value.
20199
+ * @throws {Error} If `eventName` is not a string.
20200
+ */
20201
+ emit(eventName, payload) {
20202
+ if (typeof eventName !== 'string') throw new TypeError('Event name must be a string.');
20203
+ if (this.#isDestroyed) throw new Error('Cannot emit: instance has been destroyed.');
20204
+
20205
+ /** @type {IframeEventBase} */
20206
+ const message = {
20207
+ [this.#secretEventName]: true,
20208
+ eventName,
20209
+ payload,
20210
+ direction: this.#selfType === 'parent' ? 'iframe' : 'parent',
20211
+ };
20212
+
20213
+ if (!this.#ready) {
20214
+ this.#pendingQueue.push(message);
20215
+ return;
20216
+ }
20217
+
20218
+ this.#targetWindow.postMessage(message, this.#targetOrigin);
20219
+ }
20220
+
20221
+ /**
20222
+ * Sends all pending messages queued before handshake completion.
20223
+ *
20224
+ * @returns {void}
20225
+ */
20226
+ #flushQueue() {
20227
+ while (this.#pendingQueue.length) {
20228
+ const data = this.#pendingQueue.shift();
20229
+ if (data) this.#targetWindow.postMessage(data, this.#targetOrigin);
20230
+ }
20231
+ }
20232
+
20233
+ /**
20234
+ * Checks if the communication instance has been destroyed.
20235
+ *
20236
+ * @returns {boolean}
20237
+ */
20238
+ isDestroyed() {
20239
+ return this.#isDestroyed;
20240
+ }
20241
+
20242
+ /**
20243
+ * Unsubscribes all registered event listeners and removes the message handler.
20244
+ * Call this when the instance is no longer needed to prevent memory leaks.
20245
+ */
20246
+ destroy() {
20247
+ this.#isDestroyed = true;
20248
+ window.removeEventListener('message', this._boundOnMessage);
20249
+ this.#targetWindow.removeEventListener('load', this._boundOnceMessage, false);
20250
+ this.#targetWindow.removeEventListener('DOMContentLoaded', this._boundOnceMessage, false);
20251
+ this.#events.offAllTypes();
20252
+ this.#pendingQueue = [];
20253
+ instances.delete(this.#targetWindow);
20254
+ }
20255
+ }
20256
+
20257
+ /* harmony default export */ const libs_TinyIframeEvents = (TinyIframeEvents);
20258
+
20259
+ ;// ./src/v1/libs/TinyNewWinEvents.mjs
20260
+
20261
+
20262
+ /**
20263
+ * Stores polling intervals associated with window references.
20264
+ * Used to detect when the window is closed.
20265
+ *
20266
+ * @type {WeakMap<Window, NodeJS.Timeout>}
20267
+ */
20268
+ const pollClosedInterval = new WeakMap();
20269
+
20270
+ /**
20271
+ * @callback handler
20272
+ * A function to handle incoming event payloads.
20273
+ * @param {any} payload - The data sent by the emitter.
20274
+ * @param {MessageEvent<any>} event - Metadata about the message.
20275
+ */
20276
+
20277
+ /**
20278
+ * TinyNewWinEvents provides structured communication between a main window
20279
+ * and a child window (created using window.open) using postMessage.
20280
+ *
20281
+ * It supports routing, queuing messages until handshake is ready,
20282
+ * connection status checks, and close detection.
20283
+ *
20284
+ * @class
20285
+ */
20286
+ class TinyNewWinEvents {
20287
+ #events = new libs_TinyEvents();
20288
+
20289
+ /**
20290
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
20291
+ *
20292
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
20293
+ */
20294
+ setThrowOnMaxListeners(shouldThrow) {
20295
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
20296
+ }
20297
+
20298
+ /**
20299
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
20300
+ *
20301
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
20302
+ */
20303
+ getThrowOnMaxListeners() {
20304
+ return this.#events.getThrowOnMaxListeners();
20305
+ }
20306
+
20307
+ /////////////////////////////////////////////////////////////
20308
+
20309
+ /**
20310
+ * Adds a listener to the beginning of the listeners array for the specified event.
20311
+ *
20312
+ * @param {string} event - Event name.
20313
+ * @param {handler} handler - The callback function.
20314
+ */
20315
+ prependListener(event, handler) {
20316
+ return this.#events.prependListener(event, handler);
20317
+ }
20318
+
20319
+ /**
20320
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
20321
+ *
20322
+ * @param {string} event - Event name.
20323
+ * @param {handler} handler - The callback function.
20324
+ * @returns {handler} - The wrapped handler used internally.
20325
+ */
20326
+ prependListenerOnce(event, handler) {
20327
+ return this.#events.prependListenerOnce(event, handler);
20328
+ }
20329
+
20330
+ //////////////////////////////////////////////////////////////////////
20331
+
20332
+ /**
20333
+ * Adds a event listener.
20334
+ *
20335
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
20336
+ * @param {handler} handler - Callback function to be called when event fires.
20337
+ */
20338
+ appendListener(event, handler) {
20339
+ return this.#events.appendListener(event, handler);
20340
+ }
20341
+
20342
+ /**
20343
+ * Registers an event listener that runs only once, then is removed.
20344
+ *
20345
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
20346
+ * @param {handler} handler - The callback function to run on event.
20347
+ * @returns {handler} - The wrapped version of the handler.
20348
+ */
20349
+ appendListenerOnce(event, handler) {
20350
+ return this.#events.appendListenerOnce(event, handler);
20351
+ }
20352
+
20353
+ /**
20354
+ * Adds a event listener.
20355
+ *
20356
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
20357
+ * @param {handler} handler - Callback function to be called when event fires.
20358
+ */
20359
+ on(event, handler) {
20360
+ return this.#events.on(event, handler);
20361
+ }
20362
+
20363
+ /**
20364
+ * Registers an event listener that runs only once, then is removed.
20365
+ *
20366
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
20367
+ * @param {handler} handler - The callback function to run on event.
20368
+ * @returns {handler} - The wrapped version of the handler.
20369
+ */
20370
+ once(event, handler) {
20371
+ return this.#events.once(event, handler);
20372
+ }
20373
+
20374
+ ////////////////////////////////////////////////////////////////////
20375
+
20376
+ /**
20377
+ * Removes a previously registered event listener.
20378
+ *
20379
+ * @param {string} event - The name of the event to remove the handler from.
20380
+ * @param {handler} handler - The specific callback function to remove.
20381
+ */
20382
+ off(event, handler) {
20383
+ return this.#events.off(event, handler);
20384
+ }
20385
+
20386
+ /**
20387
+ * Removes all event listeners of a specific type from the element.
20388
+ *
20389
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
20390
+ */
20391
+ offAll(event) {
20392
+ return this.#events.offAll(event);
20393
+ }
20394
+
20395
+ /**
20396
+ * Removes all event listeners of all types from the element.
20397
+ */
20398
+ offAllTypes() {
20399
+ return this.#events.offAllTypes();
20400
+ }
20401
+
20402
+ ////////////////////////////////////////////////////////////
20403
+
20404
+ /**
20405
+ * Returns the number of listeners for a given event.
20406
+ *
20407
+ * @param {string} event - The name of the event.
20408
+ * @returns {number} Number of listeners for the event.
20409
+ */
20410
+ listenerCount(event) {
20411
+ return this.#events.listenerCount(event);
20412
+ }
20413
+
20414
+ /**
20415
+ * Returns a copy of the array of listeners for the specified event.
20416
+ *
20417
+ * @param {string} event - The name of the event.
20418
+ * @returns {handler[]} Array of listener functions.
20419
+ */
20420
+ listeners(event) {
20421
+ return this.#events.listeners(event);
20422
+ }
20423
+
20424
+ /**
20425
+ * Returns a copy of the array of listeners for the specified event.
20426
+ *
20427
+ * @param {string} event - The name of the event.
20428
+ * @returns {handler[]} Array of listener functions.
20429
+ */
20430
+ onceListeners(event) {
20431
+ return this.#events.onceListeners(event);
20432
+ }
20433
+
20434
+ /**
20435
+ * Returns a copy of the internal listeners array for the specified event,
20436
+ * including wrapper functions like those used by `.once()`.
20437
+ * @param {string | symbol} event - The event name.
20438
+ * @returns {handler[]} An array of raw listener functions.
20439
+ */
20440
+ allListeners(event) {
20441
+ return this.#events.allListeners(event);
20442
+ }
20443
+
20444
+ /**
20445
+ * Returns an array of event names for which there are registered listeners.
20446
+ *
20447
+ * @returns {string[]} Array of registered event names.
20448
+ */
20449
+ eventNames() {
20450
+ return this.#events.eventNames();
20451
+ }
20452
+
20453
+ //////////////////////////////////////////////////////
20454
+
20455
+ /**
20456
+ * Sets the maximum number of listeners per event before a warning is shown.
20457
+ *
20458
+ * @param {number} n - The maximum number of listeners.
20459
+ */
20460
+ setMaxListeners(n) {
20461
+ return this.#events.setMaxListeners(n);
20462
+ }
20463
+
20464
+ /**
20465
+ * Gets the maximum number of listeners allowed per event.
20466
+ *
20467
+ * @returns {number} The maximum number of listeners.
20468
+ */
20469
+ getMaxListeners() {
20470
+ return this.#events.getMaxListeners();
20471
+ }
20472
+
20473
+ ///////////////////////////////////////////////////
20474
+
20475
+ /** @type {Window|null} Reference to the opened or parent window */
20476
+ #windowRef;
20477
+
20478
+ /** @type {string} Expected origin for postMessage communication */
20479
+ #targetOrigin;
20480
+
20481
+ /** @type {{ route: string, payload: any }[]} Queue of messages emitted before connection is ready */
20482
+ #pendingQueue = [];
20483
+
20484
+ /** @type {boolean} True if handshake between windows is complete */
20485
+ #ready = false;
20486
+
20487
+ /** @type {boolean} True if this instance is the main window (host) */
20488
+ #isHost = false;
20489
+
20490
+ /** @type {NodeJS.Timeout|null} Interval for polling child window closure */
20491
+ #pollClosedInterval = null;
20492
+
20493
+ /** @type {string} Internal message type for handshake */
20494
+ #readyEventName = '__TNE_READY__';
20495
+
20496
+ /** @type {string} Internal message type for routed communication */
20497
+ #routeEventName = '__TNE_ROUTE__';
20498
+
20499
+ /**
20500
+ * Gets the internal handshake event name.
20501
+ * @returns {string}
20502
+ */
20503
+ get readyEventName() {
20504
+ return this.#readyEventName;
20505
+ }
20506
+
20507
+ /**
20508
+ * Sets the internal handshake event name.
20509
+ * @param {string} name
20510
+ * @throws {TypeError} If the value is not a string.
20511
+ */
20512
+ set readyEventName(name) {
20513
+ if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: readyEventName must be a string.');
20514
+ this.#readyEventName = name;
20515
+ }
20516
+
20517
+ /**
20518
+ * Gets the internal route event name.
20519
+ * @returns {string}
20520
+ */
20521
+ get routeEventName() {
20522
+ return this.#routeEventName;
20523
+ }
20524
+
20525
+ /**
20526
+ * Sets the internal route event name.
20527
+ * @param {string} name
20528
+ * @throws {TypeError} If the value is not a string.
20529
+ */
20530
+ set routeEventName(name) {
20531
+ if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: routeEventName must be a string.');
20532
+ this.#routeEventName = name;
20533
+ }
20534
+
20535
+ /**
20536
+ * Initializes a TinyNewWinEvents instance for communication.
20537
+ *
20538
+ * @param {Object} [settings={}] Configuration object.
20539
+ * @param {string} [settings.targetOrigin] Origin to enforce in postMessage.
20540
+ * @param {string} [settings.url] URL string to open.
20541
+ * @param {string} [settings.name] Window name (required if opening a new window).
20542
+ * @param {string} [settings.features] Features string for `window.open`.
20543
+ *
20544
+ * @throws {Error} If `name` is "_blank", which is not allowed.
20545
+ * @throws {TypeError} If `targetOrigin`, `url`, or `features` are not strings (when provided).
20546
+ * @throws {Error} If the window reference is invalid or already being tracked.
20547
+ */
20548
+ constructor({ targetOrigin, url, name, features } = {}) {
20549
+ if (typeof name === 'string' && name === '_blank')
20550
+ throw new Error(
20551
+ 'TinyNewWinEvents: The window name "_blank" is not supported. Please use a custom name to allow tracking.',
20552
+ );
20553
+ if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
20554
+ throw new TypeError('TinyNewWinEvents: The "targetOrigin" option must be a string.');
20555
+ if (typeof url !== 'undefined' && typeof url !== 'string')
20556
+ throw new TypeError('TinyNewWinEvents: The "url" option must be a string.');
20557
+ if (typeof features !== 'undefined' && typeof features !== 'string')
20558
+ throw new TypeError('TinyNewWinEvents: The "features" option must be a string if provided.');
20559
+
20560
+ // Open Page
20561
+ if (typeof url === 'undefined') this.#windowRef = window.opener;
20562
+ // Main Page
20563
+ else {
20564
+ this.#windowRef = typeof url === 'string' ? window.open(url, name, features) : url;
20565
+ this.#isHost = true;
20566
+ }
20567
+
20568
+ if (!this.#windowRef || pollClosedInterval.has(this.#windowRef))
20569
+ throw new Error('Invalid or duplicate window reference.');
20570
+ this.#targetOrigin = targetOrigin ?? window.location.origin;
20571
+ this._handleMessage = this.#handleMessage.bind(this);
20572
+ window.addEventListener('message', this._handleMessage, false);
20573
+
20574
+ // Sends handshake if it is host (main window)
20575
+ if (!this.#isHost) {
20576
+ this.#postRaw(this.#readyEventName, null);
20577
+ this.#startCloseWatcher();
20578
+ }
20579
+ }
20580
+
20581
+ /**
20582
+ * Returns the internal window reference.
20583
+ *
20584
+ * @returns {Window|null}
20585
+ */
20586
+ getWin() {
20587
+ return this.#windowRef;
20588
+ }
20589
+
20590
+ /**
20591
+ * Internal message handler.
20592
+ *
20593
+ * @param {MessageEvent} event
20594
+ * @returns {void}
20595
+ */
20596
+ #handleMessage(event) {
20597
+ if (!event.source || (this.#windowRef && event.source !== this.#windowRef)) return;
20598
+ const { type, route, payload } = event.data || {};
20599
+
20600
+ if (type === this.#readyEventName) {
20601
+ this.#ready = true;
20602
+ this.#flushQueue();
20603
+ this.#startCloseWatcher(); // start watcher after handshake (for child window)
20604
+ if (this.#isHost) this.#postRaw(this.#readyEventName, null);
20605
+ return;
20606
+ }
20607
+
20608
+ if (type === this.#routeEventName) this.#events.emit(route, payload, event);
20609
+ }
20610
+
20611
+ /**
20612
+ * Sends all pending messages queued before handshake completion.
20613
+ *
20614
+ * @returns {void}
20615
+ */
20616
+ #flushQueue() {
20617
+ while (this.#pendingQueue.length) {
20618
+ const data = this.#pendingQueue.shift();
20619
+ if (data) {
20620
+ const { route, payload } = data;
20621
+ this.emit(route, payload);
20622
+ }
20623
+ }
20624
+ }
20625
+
20626
+ /**
20627
+ * Sends a raw postMessage with given type and payload.
20628
+ *
20629
+ * @param {string} type Internal message type
20630
+ * @param {any} payload Data to send
20631
+ * @param {string} [route=''] Optional route name
20632
+ * @returns {void}
20633
+ */
20634
+ #postRaw(type, payload, route = '') {
20635
+ if (this.#windowRef && this.#windowRef.closed) return;
20636
+ this.#windowRef?.postMessage({ type, route, payload }, this.#targetOrigin);
20637
+ }
20638
+
20639
+ /**
20640
+ * Closes the child window (only allowed from the host).
20641
+ *
20642
+ * @returns {void}
20643
+ */
20644
+ close() {
20645
+ if (!this.#isHost) throw new Error('Only host can close the window.');
20646
+ if (this.#windowRef && !this.#windowRef.closed) this.#windowRef.close();
20647
+ }
20648
+
20649
+ /**
20650
+ * Emits a message to the other window on a specific route.
20651
+ * If the handshake is not yet complete, the message is queued.
20652
+ * Throws an error if the instance has already been destroyed.
20653
+ *
20654
+ * @param {string} route - Route name used to identify the message handler.
20655
+ * @param {any} payload - Data to send along with the message.
20656
+ * @throws {Error} If the instance is already destroyed.
20657
+ * @returns {void}
20658
+ */
20659
+ emit(route, payload) {
20660
+ if (typeof route !== 'string') throw new TypeError('Event name must be a string.');
20661
+ if (this.isDestroyed()) throw new Error('Cannot emit: instance has been destroyed.');
20662
+ if (!this.#ready) {
20663
+ this.#pendingQueue.push({ route, payload });
20664
+ return;
20665
+ }
20666
+ this.#postRaw(this.#routeEventName, payload, route);
20667
+ }
20668
+
20669
+ /**
20670
+ * Checks if the connection is active and the window is still open.
20671
+ *
20672
+ * @returns {boolean}
20673
+ */
20674
+ isConnected() {
20675
+ return this.#ready && this.#windowRef && !this.#windowRef.closed ? true : false;
20676
+ }
20677
+
20678
+ /**
20679
+ * Starts polling to detect when the window is closed.
20680
+ *
20681
+ * @returns {void}
20682
+ */
20683
+ #startCloseWatcher() {
20684
+ if (!this.#windowRef || this.#pollClosedInterval) return;
20685
+ this.#pollClosedInterval = setInterval(() => {
20686
+ if (this.#windowRef?.closed) {
20687
+ this.#events.emit('WINDOW_REF_CLOSED');
20688
+ this.destroy();
20689
+ }
20690
+ }, 500);
20691
+ pollClosedInterval.set(this.#windowRef, this.#pollClosedInterval);
20692
+ }
20693
+
20694
+ /**
20695
+ * Registers a callback for when the window is closed.
20696
+ *
20697
+ * @param {handler} callback Callback to run on close
20698
+ * @returns {void}
20699
+ */
20700
+ onClose(callback) {
20701
+ return this.#events.on('WINDOW_REF_CLOSED', callback);
20702
+ }
20703
+
20704
+ /**
20705
+ * Unregisters a previously registered close callback.
20706
+ *
20707
+ * @param {handler} callback Callback to remove
20708
+ * @returns {void}
20709
+ */
20710
+ offClose(callback) {
20711
+ return this.#events.off('WINDOW_REF_CLOSED', callback);
20712
+ }
20713
+
20714
+ /**
20715
+ * Checks if the communication instance has been destroyed.
20716
+ *
20717
+ * @returns {boolean}
20718
+ */
20719
+ isDestroyed() {
20720
+ return !this.#windowRef;
20721
+ }
20722
+
20723
+ /**
20724
+ * Destroys the communication instance, cleaning up all resources and listeners.
20725
+ *
20726
+ * @returns {void}
20727
+ */
20728
+ destroy() {
20729
+ if (!this.#windowRef) return;
20730
+ if (this.#pollClosedInterval) {
20731
+ clearInterval(this.#pollClosedInterval);
20732
+ this.#pollClosedInterval = null;
20733
+ pollClosedInterval.delete(this.#windowRef);
20734
+ }
20735
+ window.removeEventListener('message', this._handleMessage);
20736
+ this.#pendingQueue = [];
20737
+ this.#ready = false;
20738
+ this.#windowRef = null;
20739
+ this.#events.offAllTypes();
20740
+ }
20741
+ }
20742
+
20743
+ /* harmony default export */ const libs_TinyNewWinEvents = (TinyNewWinEvents);
20744
+
20745
+ ;// ./src/v1/index.mjs
20746
+
20747
+
19761
20748
 
19762
20749
 
19763
20750