@sv443-network/userutils 9.3.0 → 9.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 9.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5075831: Added `Mixins` class for allowing multiple sources to modify values in a controlled way
8
+ - 99dedfd: Added unit tests
9
+ - 7530fd0: Added `Debouncer.getListeners()` method to get an array of all listener functions
10
+ - 48306da: Added `stores` filter parameter to the `DataStoreSerializer` methods `loadStoresData()`, `resetStoresData()` and `deleteStoresData()`
11
+
12
+ ### Patch Changes
13
+
14
+ - f6a68c7: Fixed error when calling `interceptEvent()` in a non-GM environment
15
+
3
16
  ## 9.3.0
4
17
 
5
18
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <!-- #region Description -->
4
4
  ## UserUtils
5
5
  General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more.
6
- Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
6
+ Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require` or `<script>`
7
7
  The library works in any DOM environment with or without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
8
8
 
9
9
  You may want to check out my [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
@@ -11,12 +11,14 @@ If you like using this library, please consider [supporting the development ❤
11
11
 
12
12
  <br>
13
13
 
14
- [![minified bundle size badge](https://badgen.net/bundlephobia/min/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
15
- [![minified and gzipped bundle size badge](https://badgen.net/bundlephobia/minzip/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
16
- [![tree shaking support badge](https://badgen.net/bundlephobia/tree-shaking/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
14
+ [![Tree shaking support badge](https://badgen.net/bundlephobia/tree-shaking/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
15
+ [![Code coverage percentage badge](https://img.shields.io/coverallsCoverage/github/Sv443-Network/UserUtils?branch=main)](https://coveralls.io/github/Sv443-Network/UserUtils)
16
+ [![Minified bundle size badge](https://badgen.net/bundlephobia/min/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
17
+ [![Minified and gzipped bundle size badge](https://badgen.net/bundlephobia/minzip/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
17
18
 
18
- [![github stargazers badge](https://badgen.net/github/stars/Sv443-Network/UserUtils?icon=github)](https://github.com/Sv443-Network/UserUtils/stargazers)
19
- [![discord server badge](https://badgen.net/discord/online-members/aBH4uRG?icon=discord)](https://dc.sv443.net/)
19
+ [![Discord server badge](https://badgen.net/discord/online-members/aBH4uRG?icon=discord)](https://dc.sv443.net/)
20
+ [![Github stargazers badge](https://badgen.net/github/stars/Sv443-Network/UserUtils?icon=github)](https://github.com/Sv443-Network/UserUtils/stargazers)
21
+ [![Support development on Github Sponsors badge](https://img.shields.io/github/sponsors/Sv443?icon=github)](https://github.com/sponsors/Sv443)
20
22
 
21
23
  <sup>
22
24
  View the documentation of previous major releases:
@@ -63,6 +65,7 @@ View the documentation of previous major releases:
63
65
  - [`DataStore`](./docs.md#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
64
66
  - [`DataStoreSerializer`](./docs.md#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
65
67
  - [`Dialog`](./docs.md#dialog) - class for creating custom modal dialogs with a promise-based API and a generic, default style
68
+ - [`Mixins`](./docs.md#mixins) - class for creating mixin functions that allow multiple sources to modify a target value in a highly flexible way
66
69
  - [`NanoEmitter`](./docs.md#nanoemitter) - tiny event emitter class with a focus on performance and simplicity (based on [nanoevents](https://npmjs.com/package/nanoevents))
67
70
  - [`Debouncer`](./docs.md#debouncer) - class for debouncing function calls with a given timeout
68
71
  - [`debounce()`](./docs.md#debounce) - function wrapper for the Debouncer class for easier usage
package/dist/index.cjs CHANGED
@@ -3,6 +3,8 @@
3
3
  var nanoevents = require('nanoevents');
4
4
 
5
5
  var __defProp = Object.defineProperty;
6
+ var __defProps = Object.defineProperties;
7
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
8
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
10
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -18,6 +20,7 @@ var __spreadValues = (a, b) => {
18
20
  }
19
21
  return a;
20
22
  };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
24
  var __objRest = (source, exclude) => {
22
25
  var target = {};
23
26
  for (var prop in source)
@@ -250,16 +253,16 @@ function addGlobalStyle(style) {
250
253
  function preloadImages(srcUrls, rejects = false) {
251
254
  const promises = srcUrls.map((src) => new Promise((res, rej) => {
252
255
  const image = new Image();
253
- image.src = src;
254
256
  image.addEventListener("load", () => res(image));
255
257
  image.addEventListener("error", (evt) => rejects && rej(evt));
258
+ image.src = src;
256
259
  }));
257
260
  return Promise.allSettled(promises);
258
261
  }
259
262
  function openInNewTab(href, background, additionalProps) {
260
- var _a;
261
263
  try {
262
- (_a = GM.openInTab) == null ? void 0 : _a.call(GM, href, background);
264
+ if (typeof window.GM === "object")
265
+ GM.openInTab(href, background);
263
266
  } catch (e) {
264
267
  const openElem = document.createElement("a");
265
268
  Object.assign(openElem, __spreadValues({
@@ -276,12 +279,17 @@ function openInNewTab(href, background, additionalProps) {
276
279
  });
277
280
  document.body.appendChild(openElem);
278
281
  openElem.click();
279
- setTimeout(openElem.remove, 0);
282
+ setTimeout(() => {
283
+ try {
284
+ openElem.remove();
285
+ } catch (e2) {
286
+ }
287
+ }, 0);
280
288
  }
281
289
  }
282
290
  function interceptEvent(eventObject, eventName, predicate = () => true) {
283
291
  var _a;
284
- if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
292
+ if (typeof window.GM === "object" && ((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
285
293
  throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
286
294
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
287
295
  if (isNaN(Error.stackTraceLimit))
@@ -441,6 +449,8 @@ function computeHash(input, algorithm = "SHA-256") {
441
449
  });
442
450
  }
443
451
  function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
452
+ if (length < 1)
453
+ throw new RangeError("The length argument must be at least 1");
444
454
  if (radix < 2 || radix > 36)
445
455
  throw new RangeError("The radix argument must be between 2 and 36");
446
456
  let arr = [];
@@ -785,7 +795,7 @@ var DataStoreSerializer = class _DataStoreSerializer {
785
795
  deserializePartial(stores, data) {
786
796
  return __async(this, null, function* () {
787
797
  const deserStores = typeof data === "string" ? JSON.parse(data) : data;
788
- if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
798
+ if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
789
799
  throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
790
800
  for (const storeData of deserStores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id))) {
791
801
  const storeInst = this.stores.find((s) => s.id === storeData.id);
@@ -818,41 +828,59 @@ Has: ${checksum}`);
818
828
  /**
819
829
  * Loads the persistent data of the DataStore instances into the in-memory cache.
820
830
  * Also triggers the migration process if the data format has changed.
831
+ * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be loaded
821
832
  * @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }`
822
833
  */
823
- loadStoresData() {
834
+ loadStoresData(stores) {
824
835
  return __async(this, null, function* () {
825
- return Promise.allSettled(this.stores.map(
826
- (store) => __async(this, null, function* () {
836
+ return Promise.allSettled(
837
+ this.getStoresFiltered(stores).map((store) => __async(this, null, function* () {
827
838
  return {
828
839
  id: store.id,
829
840
  data: yield store.loadData()
830
841
  };
831
- })
832
- ));
842
+ }))
843
+ );
833
844
  });
834
845
  }
835
- /** Resets the persistent data of the DataStore instances to their default values. */
836
- resetStoresData() {
846
+ /**
847
+ * Resets the persistent and in-memory data of the DataStore instances to their default values.
848
+ * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
849
+ */
850
+ resetStoresData(stores) {
837
851
  return __async(this, null, function* () {
838
- return Promise.allSettled(this.stores.map((store) => store.saveDefaultData()));
852
+ return Promise.allSettled(
853
+ this.getStoresFiltered(stores).map((store) => store.saveDefaultData())
854
+ );
839
855
  });
840
856
  }
841
857
  /**
842
858
  * Deletes the persistent data of the DataStore instances.
843
- * Leaves the in-memory data untouched.
859
+ * Leaves the in-memory data untouched.
860
+ * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
844
861
  */
845
- deleteStoresData() {
862
+ deleteStoresData(stores) {
846
863
  return __async(this, null, function* () {
847
- return Promise.allSettled(this.stores.map((store) => store.deleteData()));
864
+ return Promise.allSettled(
865
+ this.getStoresFiltered(stores).map((store) => store.deleteData())
866
+ );
848
867
  });
849
868
  }
869
+ /** Checks if a given value is an array of SerializedDataStore objects */
870
+ static isSerializedDataStoreObjArray(obj) {
871
+ return Array.isArray(obj) && obj.every((o) => typeof o === "object" && o !== null && "id" in o && "data" in o && "formatVersion" in o && "encoded" in o);
872
+ }
850
873
  /** Checks if a given value is a SerializedDataStore object */
851
- static isSerializedDataStore(obj) {
874
+ static isSerializedDataStoreObj(obj) {
852
875
  return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
853
876
  }
877
+ /** Returns the DataStore instances whose IDs match the provided array or function */
878
+ getStoresFiltered(stores) {
879
+ return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
880
+ }
854
881
  };
855
882
  var NanoEmitter = class {
883
+ /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
856
884
  constructor(options = {}) {
857
885
  __publicField(this, "events", nanoevents.createNanoEvents());
858
886
  __publicField(this, "eventUnsubscribes", []);
@@ -861,7 +889,27 @@ var NanoEmitter = class {
861
889
  publicEmit: false
862
890
  }, options);
863
891
  }
864
- /** Subscribes to an event - returns a function that unsubscribes the event listener */
892
+ /**
893
+ * Subscribes to an event and calls the callback when it's emitted.
894
+ * @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
895
+ * @returns Returns a function that can be called to unsubscribe the event listener
896
+ * @example ```ts
897
+ * const emitter = new NanoEmitter<{
898
+ * foo: (bar: string) => void;
899
+ * }>({
900
+ * publicEmit: true,
901
+ * });
902
+ *
903
+ * let i = 0;
904
+ * const unsub = emitter.on("foo", (bar) => {
905
+ * // unsubscribe after 10 events:
906
+ * if(++i === 10) unsub();
907
+ * console.log(bar);
908
+ * });
909
+ *
910
+ * emitter.emit("foo", "bar");
911
+ * ```
912
+ */
865
913
  on(event, cb) {
866
914
  let unsub;
867
915
  const unsubProxy = () => {
@@ -874,19 +922,43 @@ var NanoEmitter = class {
874
922
  this.eventUnsubscribes.push(unsub);
875
923
  return unsubProxy;
876
924
  }
877
- /** Subscribes to an event and calls the callback or resolves the Promise only once */
925
+ /**
926
+ * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
927
+ * @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
928
+ * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
929
+ * @returns Returns a Promise that resolves with the event arguments when the event is emitted
930
+ * @example ```ts
931
+ * const emitter = new NanoEmitter<{
932
+ * foo: (bar: string) => void;
933
+ * }>();
934
+ *
935
+ * // Promise syntax:
936
+ * const [bar] = await emitter.once("foo");
937
+ * console.log(bar);
938
+ *
939
+ * // Callback syntax:
940
+ * emitter.once("foo", (bar) => console.log(bar));
941
+ * ```
942
+ */
878
943
  once(event, cb) {
879
944
  return new Promise((resolve) => {
880
945
  let unsub;
881
946
  const onceProxy = (...args) => {
882
- unsub();
883
947
  cb == null ? undefined : cb(...args);
948
+ unsub == null ? undefined : unsub();
884
949
  resolve(args);
885
950
  };
886
- unsub = this.on(event, onceProxy);
951
+ unsub = this.events.on(event, onceProxy);
952
+ this.eventUnsubscribes.push(unsub);
887
953
  });
888
954
  }
889
- /** Emits an event on this instance - Needs `publicEmit` to be set to true in the constructor! */
955
+ /**
956
+ * Emits an event on this instance.
957
+ * ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
958
+ * @param event The event to emit
959
+ * @param args The arguments to pass to the event listeners
960
+ * @returns Returns true if `publicEmit` is true and the event was emitted successfully
961
+ */
890
962
  emit(event, ...args) {
891
963
  if (this.emitterOptions.publicEmit) {
892
964
  this.events.emit(event, ...args);
@@ -894,7 +966,7 @@ var NanoEmitter = class {
894
966
  }
895
967
  return false;
896
968
  }
897
- /** Unsubscribes all event listeners */
969
+ /** Unsubscribes all event listeners from this instance */
898
970
  unsubscribeAll() {
899
971
  for (const unsub of this.eventUnsubscribes)
900
972
  unsub();
@@ -934,6 +1006,10 @@ var Debouncer = class extends NanoEmitter {
934
1006
  removeAllListeners() {
935
1007
  this.listeners = [];
936
1008
  }
1009
+ /** Returns all registered listeners */
1010
+ getListeners() {
1011
+ return this.listeners;
1012
+ }
937
1013
  //#region timeout
938
1014
  /** Sets the timeout for the debouncer */
939
1015
  setTimeout(timeout) {
@@ -962,7 +1038,7 @@ var Debouncer = class extends NanoEmitter {
962
1038
  const cl = (...a) => {
963
1039
  this.queuedCall = undefined;
964
1040
  this.emit("call", ...a);
965
- this.listeners.forEach((l) => l.apply(this, a));
1041
+ this.listeners.forEach((l) => l.call(this, ...a));
966
1042
  };
967
1043
  const setRepeatTimeout = () => {
968
1044
  this.activeTimeout = setTimeout(() => {
@@ -1436,8 +1512,6 @@ function autoPlural(term, num, pluralType = "auto") {
1436
1512
  return `${term}${n === 1 ? "" : "s"}`;
1437
1513
  case "-ies":
1438
1514
  return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
1439
- default:
1440
- return String(term);
1441
1515
  }
1442
1516
  }
1443
1517
  function insertValues(input, ...values) {
@@ -1496,6 +1570,112 @@ function purifyObj(obj) {
1496
1570
  return Object.assign(/* @__PURE__ */ Object.create(null), obj);
1497
1571
  }
1498
1572
 
1573
+ // lib/Mixins.ts
1574
+ var Mixins = class {
1575
+ /**
1576
+ * Creates a new Mixins instance.
1577
+ * @param config Configuration object to customize the behavior.
1578
+ */
1579
+ constructor(config = {}) {
1580
+ /** List of all registered mixins */
1581
+ __publicField(this, "mixins", []);
1582
+ /** Default configuration object for mixins */
1583
+ __publicField(this, "defaultMixinCfg");
1584
+ /** Whether the priorities should auto-increment if not specified */
1585
+ __publicField(this, "autoIncPrioEnabled");
1586
+ /** The current auto-increment priority counter */
1587
+ __publicField(this, "autoIncPrioCounter", /* @__PURE__ */ new Map());
1588
+ var _a, _b, _c;
1589
+ this.defaultMixinCfg = purifyObj({
1590
+ priority: (_a = config.defaultPriority) != null ? _a : 0,
1591
+ stopPropagation: (_b = config.defaultStopPropagation) != null ? _b : false,
1592
+ signal: config.defaultSignal
1593
+ });
1594
+ this.autoIncPrioEnabled = (_c = config.autoIncrementPriority) != null ? _c : false;
1595
+ }
1596
+ //#region public
1597
+ /**
1598
+ * Adds a mixin function to the given {@linkcode mixinKey}.
1599
+ * If no priority is specified, it will be calculated via the protected method {@linkcode calcPriority()} based on the constructor configuration, or fall back to the default priority.
1600
+ * @param mixinKey The key to identify the mixin function.
1601
+ * @param mixinFn The function to be called to apply the mixin. The first argument is the input value, the second argument is the context object (if any).
1602
+ * @param config Configuration object to customize the mixin behavior, or just the priority if a number is passed.
1603
+ * @returns Returns a cleanup function, to be called when this mixin is no longer needed.
1604
+ */
1605
+ add(mixinKey, mixinFn, config = purifyObj({})) {
1606
+ const calcPrio = typeof config === "number" ? config : this.calcPriority(mixinKey, config);
1607
+ const mixin = purifyObj(__spreadValues(__spreadValues(__spreadProps(__spreadValues({}, this.defaultMixinCfg), {
1608
+ key: mixinKey,
1609
+ fn: mixinFn
1610
+ }), typeof config === "object" ? config : {}), typeof calcPrio === "number" && !isNaN(calcPrio) ? { priority: calcPrio } : {}));
1611
+ this.mixins.push(mixin);
1612
+ const rem = () => {
1613
+ this.mixins = this.mixins.filter((m) => m !== mixin);
1614
+ };
1615
+ if (mixin.signal)
1616
+ mixin.signal.addEventListener("abort", rem, { once: true });
1617
+ return rem;
1618
+ }
1619
+ /** Returns a list of all added mixins with their keys and configuration objects, but not their functions */
1620
+ list() {
1621
+ return this.mixins.map((_a) => {
1622
+ var _b = _a, rest = __objRest(_b, ["fn"]);
1623
+ return rest;
1624
+ });
1625
+ }
1626
+ /**
1627
+ * Applies all mixins with the given key to the input value, respecting the priority and stopPropagation settings.
1628
+ * If additional context is set in the MixinMap, it will need to be passed as the third argument.
1629
+ * @returns The modified value after all mixins have been applied.
1630
+ */
1631
+ resolve(mixinKey, inputValue, ...inputCtx) {
1632
+ const mixins = this.mixins.filter((m) => m.key === mixinKey);
1633
+ const sortedMixins = [...mixins].sort((a, b) => b.priority - a.priority);
1634
+ let result = inputValue;
1635
+ for (let i = 0; i < sortedMixins.length; i++) {
1636
+ const mixin = sortedMixins[i];
1637
+ result = mixin.fn(result, ...inputCtx);
1638
+ if (result instanceof Promise) {
1639
+ return (() => __async(this, null, function* () {
1640
+ result = yield result;
1641
+ if (mixin.stopPropagation)
1642
+ return result;
1643
+ for (let j = i + 1; j < sortedMixins.length; j++) {
1644
+ const mixin2 = sortedMixins[j];
1645
+ result = yield mixin2.fn(result, ...inputCtx);
1646
+ if (mixin2.stopPropagation)
1647
+ break;
1648
+ }
1649
+ return result;
1650
+ }))();
1651
+ } else if (mixin.stopPropagation)
1652
+ break;
1653
+ }
1654
+ return result;
1655
+ }
1656
+ //#region protected
1657
+ /** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
1658
+ calcPriority(mixinKey, config) {
1659
+ var _a;
1660
+ if (config.priority !== undefined)
1661
+ return undefined;
1662
+ if (!this.autoIncPrioEnabled)
1663
+ return (_a = config.priority) != null ? _a : this.defaultMixinCfg.priority;
1664
+ if (!this.autoIncPrioCounter.has(mixinKey))
1665
+ this.autoIncPrioCounter.set(mixinKey, this.defaultMixinCfg.priority);
1666
+ let prio = this.autoIncPrioCounter.get(mixinKey);
1667
+ while (this.mixins.some((m) => m.key === mixinKey && m.priority === prio))
1668
+ prio++;
1669
+ this.autoIncPrioCounter.set(mixinKey, prio + 1);
1670
+ return prio;
1671
+ }
1672
+ /** Removes all mixins with the given key */
1673
+ removeAll(mixinKey) {
1674
+ this.mixins.filter((m) => m.key === mixinKey);
1675
+ this.mixins = this.mixins.filter((m) => m.key !== mixinKey);
1676
+ }
1677
+ };
1678
+
1499
1679
  // lib/SelectorObserver.ts
1500
1680
  var SelectorObserver = class {
1501
1681
  constructor(baseElement, options = {}) {
@@ -1753,15 +1933,15 @@ function getFallbackLanguage() {
1753
1933
  return fallbackLang;
1754
1934
  }
1755
1935
  function addTransform(transform) {
1756
- const [pattern, fn] = transform;
1936
+ const [regex, fn] = transform;
1757
1937
  valTransforms.push({
1758
1938
  fn,
1759
- regex: typeof pattern === "string" ? new RegExp(pattern, "gm") : pattern
1939
+ regex
1760
1940
  });
1761
1941
  }
1762
1942
  function deleteTransform(patternOrFn) {
1763
1943
  const idx = valTransforms.findIndex(
1764
- (t) => typeof patternOrFn === "function" ? t.fn === patternOrFn : typeof patternOrFn === "string" ? t.regex.source === patternOrFn : t.regex === patternOrFn
1944
+ (t) => typeof patternOrFn === "function" ? t.fn === patternOrFn : t.regex === patternOrFn
1765
1945
  );
1766
1946
  if (idx !== -1) {
1767
1947
  valTransforms.splice(idx, 1);
@@ -1838,6 +2018,7 @@ exports.DataStoreSerializer = DataStoreSerializer;
1838
2018
  exports.Debouncer = Debouncer;
1839
2019
  exports.Dialog = Dialog;
1840
2020
  exports.MigrationError = MigrationError;
2021
+ exports.Mixins = Mixins;
1841
2022
  exports.NanoEmitter = NanoEmitter;
1842
2023
  exports.PlatformError = PlatformError;
1843
2024
  exports.SelectorObserver = SelectorObserver;