@sv443-network/coreutils 3.2.0 → 3.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.
@@ -55,6 +55,7 @@ __export(lib_exports, {
55
55
  consumeGen: () => consumeGen,
56
56
  consumeStringGen: () => consumeStringGen,
57
57
  createProgressBar: () => createProgressBar,
58
+ createRecurringTask: () => createRecurringTask,
58
59
  darkenColor: () => darkenColor,
59
60
  debounce: () => debounce,
60
61
  decompress: () => decompress,
@@ -460,12 +461,45 @@ function getCallStack(asArray, lines = Infinity) {
460
461
  if (typeof lines !== "number" || isNaN(lines) || lines < 0)
461
462
  throw new TypeError("lines parameter must be a non-negative number");
462
463
  try {
463
- throw new Error("This is to capture a stack trace with CoreUtils.getCallStack(). (If you see this somewhere, you can safely ignore it.)");
464
+ throw new CustomError("GetCallStack", "Capturing a stack trace with CoreUtils.getCallStack(). If you see this anywhere, you can safely ignore it.");
464
465
  } catch (err) {
465
466
  const stack = (err.stack ?? "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
466
467
  return asArray !== false ? stack : stack.join("\n");
467
468
  }
468
469
  }
470
+ function createRecurringTask(options) {
471
+ var _a;
472
+ let iterations = 0;
473
+ let aborted = false;
474
+ (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
475
+ aborted = true;
476
+ }, { once: true });
477
+ const runRecurringTask = async (initial = false) => {
478
+ var _a2;
479
+ if (aborted)
480
+ return;
481
+ try {
482
+ if ((options.immediate ?? true) || !initial) {
483
+ iterations++;
484
+ if (await ((_a2 = options.condition) == null ? void 0 : _a2.call(options, iterations - 1)) ?? true) {
485
+ const val = await options.task(iterations - 1);
486
+ if (options.onSuccess)
487
+ await options.onSuccess(val, iterations - 1);
488
+ }
489
+ }
490
+ } catch (err) {
491
+ if (options.onError)
492
+ await options.onError(err, iterations - 1);
493
+ if (options.abortOnError)
494
+ aborted = true;
495
+ if (!options.onError && !options.abortOnError)
496
+ throw err;
497
+ }
498
+ if (!aborted && (typeof options.maxIterations !== "number" || iterations < options.maxIterations))
499
+ setTimeout(runRecurringTask, options.timeout);
500
+ };
501
+ return runRecurringTask(true);
502
+ }
469
503
 
470
504
  // lib/text.ts
471
505
  function autoPlural(term, num, pluralType = "auto") {
@@ -555,9 +589,208 @@ function truncStr(input, length, endStr = "...") {
555
589
  return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
556
590
  }
557
591
 
592
+ // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
593
+ var createNanoEvents = () => ({
594
+ emit(event, ...args) {
595
+ for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
596
+ callbacks[i](...args);
597
+ }
598
+ },
599
+ events: {},
600
+ on(event, cb) {
601
+ ;
602
+ (this.events[event] ||= []).push(cb);
603
+ return () => {
604
+ var _a;
605
+ this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
606
+ };
607
+ }
608
+ });
609
+
610
+ // lib/NanoEmitter.ts
611
+ var NanoEmitter = class {
612
+ events = createNanoEvents();
613
+ eventUnsubscribes = [];
614
+ emitterOptions;
615
+ /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
616
+ constructor(options = {}) {
617
+ this.emitterOptions = {
618
+ publicEmit: false,
619
+ ...options
620
+ };
621
+ }
622
+ //#region on
623
+ /**
624
+ * Subscribes to an event and calls the callback when it's emitted.
625
+ * @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 "_")
626
+ * @returns Returns a function that can be called to unsubscribe the event listener
627
+ * @example ```ts
628
+ * const emitter = new NanoEmitter<{
629
+ * foo: (bar: string) => void;
630
+ * }>({
631
+ * publicEmit: true,
632
+ * });
633
+ *
634
+ * let i = 0;
635
+ * const unsub = emitter.on("foo", (bar) => {
636
+ * // unsubscribe after 10 events:
637
+ * if(++i === 10) unsub();
638
+ * console.log(bar);
639
+ * });
640
+ *
641
+ * emitter.emit("foo", "bar");
642
+ * ```
643
+ */
644
+ on(event, cb) {
645
+ let unsub;
646
+ const unsubProxy = () => {
647
+ if (!unsub)
648
+ return;
649
+ unsub();
650
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
651
+ };
652
+ unsub = this.events.on(event, cb);
653
+ this.eventUnsubscribes.push(unsub);
654
+ return unsubProxy;
655
+ }
656
+ //#region once
657
+ /**
658
+ * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
659
+ * @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 "_")
660
+ * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
661
+ * @returns Returns a Promise that resolves with the event arguments when the event is emitted
662
+ * @example ```ts
663
+ * const emitter = new NanoEmitter<{
664
+ * foo: (bar: string) => void;
665
+ * }>();
666
+ *
667
+ * // Promise syntax:
668
+ * const [bar] = await emitter.once("foo");
669
+ * console.log(bar);
670
+ *
671
+ * // Callback syntax:
672
+ * emitter.once("foo", (bar) => console.log(bar));
673
+ * ```
674
+ */
675
+ once(event, cb) {
676
+ return new Promise((resolve) => {
677
+ let unsub;
678
+ const onceProxy = ((...args) => {
679
+ cb == null ? void 0 : cb(...args);
680
+ unsub == null ? void 0 : unsub();
681
+ resolve(args);
682
+ });
683
+ unsub = this.events.on(event, onceProxy);
684
+ this.eventUnsubscribes.push(unsub);
685
+ });
686
+ }
687
+ //#region onMulti
688
+ /**
689
+ * Allows subscribing to multiple events and calling the callback only when one of, all of, or a subset of the events are emitted, either continuously or only once.
690
+ * @param options An object or array of objects with the following properties:
691
+ * `callback` (required) is the function that will be called when the conditions are met.
692
+ *
693
+ * Set `once` to true to call the callback only once for the first event (or set of events) that match the criteria, then stop listening.
694
+ * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
695
+ *
696
+ * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
697
+ * If `allOf` is used, the callback will be called after all of the matching events are emitted at least once, then any time any of them are emitted.
698
+ * If both `oneOf` and `allOf` are used together, the callback will be called when any of the `oneOf` events are emitted AND all of the `allOf` events have been emitted at least once.
699
+ * At least one of `oneOf` or `allOf` must be provided.
700
+ *
701
+ * @returns Returns a function that can be called to unsubscribe all listeners created by this call. Alternatively, pass an `AbortSignal` to all options objects to achieve the same effect or for finer control.
702
+ */
703
+ onMulti(options) {
704
+ const allUnsubs = [];
705
+ const unsubAll = () => {
706
+ for (const unsub of allUnsubs)
707
+ unsub();
708
+ allUnsubs.splice(0, allUnsubs.length);
709
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
710
+ };
711
+ for (const opts of Array.isArray(options) ? options : [options]) {
712
+ const optsWithDefaults = {
713
+ allOf: [],
714
+ oneOf: [],
715
+ once: false,
716
+ ...opts
717
+ };
718
+ const {
719
+ oneOf,
720
+ allOf,
721
+ once,
722
+ signal,
723
+ callback
724
+ } = optsWithDefaults;
725
+ if (signal == null ? void 0 : signal.aborted)
726
+ return unsubAll;
727
+ if (oneOf.length === 0 && allOf.length === 0)
728
+ throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
729
+ const curEvtUnsubs = [];
730
+ const checkUnsubAllEvt = (force = false) => {
731
+ if (!(signal == null ? void 0 : signal.aborted) && !force)
732
+ return;
733
+ for (const unsub of curEvtUnsubs)
734
+ unsub();
735
+ curEvtUnsubs.splice(0, curEvtUnsubs.length);
736
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
737
+ };
738
+ const allOfEmitted = /* @__PURE__ */ new Set();
739
+ const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
740
+ for (const event of oneOf) {
741
+ const unsub = this.events.on(event, ((...args) => {
742
+ checkUnsubAllEvt();
743
+ if (allOfConditionMet()) {
744
+ callback(event, ...args);
745
+ if (once)
746
+ checkUnsubAllEvt(true);
747
+ }
748
+ }));
749
+ curEvtUnsubs.push(unsub);
750
+ }
751
+ for (const event of allOf) {
752
+ const unsub = this.events.on(event, ((...args) => {
753
+ checkUnsubAllEvt();
754
+ allOfEmitted.add(event);
755
+ if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
756
+ callback(event, ...args);
757
+ if (once)
758
+ checkUnsubAllEvt(true);
759
+ }
760
+ }));
761
+ curEvtUnsubs.push(unsub);
762
+ }
763
+ allUnsubs.push(() => checkUnsubAllEvt(true));
764
+ }
765
+ return unsubAll;
766
+ }
767
+ //#region emit
768
+ /**
769
+ * Emits an event on this instance.
770
+ * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
771
+ * @param event The event to emit
772
+ * @param args The arguments to pass to the event listeners
773
+ * @returns Returns true if `publicEmit` is true and the event was emitted successfully
774
+ */
775
+ emit(event, ...args) {
776
+ if (this.emitterOptions.publicEmit) {
777
+ this.events.emit(event, ...args);
778
+ return true;
779
+ }
780
+ return false;
781
+ }
782
+ //#region unsubscribeAll
783
+ /** Unsubscribes all event listeners from this instance */
784
+ unsubscribeAll() {
785
+ for (const unsub of this.eventUnsubscribes)
786
+ unsub();
787
+ this.eventUnsubscribes = [];
788
+ }
789
+ };
790
+
558
791
  // lib/DataStore.ts
559
792
  var dsFmtVer = 1;
560
- var DataStore = class {
793
+ var DataStore = class extends NanoEmitter {
561
794
  id;
562
795
  formatVersion;
563
796
  defaultData;
@@ -589,6 +822,7 @@ var DataStore = class {
589
822
  * @param opts The options for this DataStore instance
590
823
  */
591
824
  constructor(opts) {
825
+ super(opts.nanoEmitterOptions);
592
826
  this.id = opts.id;
593
827
  this.formatVersion = opts.formatVersion;
594
828
  this.defaultData = opts.defaultData;
@@ -659,30 +893,26 @@ var DataStore = class {
659
893
  this.migrateIds = [];
660
894
  }
661
895
  const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
662
- let storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
896
+ const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
663
897
  if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
664
- await this.saveDefaultData();
665
- return this.engine.deepCopy(this.defaultData);
898
+ await this.saveDefaultData(false);
899
+ const data = this.engine.deepCopy(this.defaultData);
900
+ this.events.emit("loadData", data);
901
+ return data;
666
902
  }
667
903
  const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
668
904
  const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
669
905
  const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
670
- let saveData = false;
671
- if (isNaN(storedFmtVer)) {
672
- await this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, storedFmtVer = this.formatVersion);
673
- saveData = true;
674
- }
675
906
  let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
676
907
  if (storedFmtVer < this.formatVersion && this.migrations)
677
908
  parsed = await this.runMigrations(parsed, storedFmtVer);
678
- if (saveData)
679
- await this.setData(parsed);
680
- if (this.memoryCache)
681
- return this.cachedData = this.engine.deepCopy(parsed);
682
- else
683
- return this.engine.deepCopy(parsed);
909
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
910
+ this.events.emit("loadData", result);
911
+ return result;
684
912
  } catch (err) {
913
+ const error = err instanceof Error ? err : new Error(String(err));
685
914
  console.warn("Error while parsing JSON data, resetting it to the default value.", err);
915
+ this.events.emit("error", error);
686
916
  await this.saveDefaultData();
687
917
  return this.defaultData;
688
918
  }
@@ -701,27 +931,47 @@ var DataStore = class {
701
931
  //#region setData
702
932
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
703
933
  setData(data) {
704
- if (this.memoryCache)
934
+ const dataCopy = this.engine.deepCopy(data);
935
+ if (this.memoryCache) {
705
936
  this.cachedData = data;
937
+ this.events.emit("updateDataSync", dataCopy);
938
+ }
706
939
  return new Promise(async (resolve) => {
707
- await Promise.allSettled([
940
+ const results = await Promise.allSettled([
708
941
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
709
942
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
710
943
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
711
944
  ]);
945
+ if (results.every((r) => r.status === "fulfilled"))
946
+ this.events.emit("updateData", dataCopy);
947
+ else {
948
+ const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
949
+ console.error(error);
950
+ this.events.emit("error", error);
951
+ }
712
952
  resolve();
713
953
  });
714
954
  }
715
955
  //#region saveDefaultData
716
- /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
717
- async saveDefaultData() {
956
+ /**
957
+ * Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
958
+ * @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
959
+ */
960
+ async saveDefaultData(emitEvent = true) {
718
961
  if (this.memoryCache)
719
962
  this.cachedData = this.defaultData;
720
- await Promise.allSettled([
963
+ const results = await Promise.allSettled([
721
964
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
722
965
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
723
966
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
724
967
  ]);
968
+ if (results.every((r) => r.status === "fulfilled"))
969
+ emitEvent && this.events.emit("setDefaultData", this.defaultData);
970
+ else {
971
+ const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
972
+ console.error(error);
973
+ this.events.emit("error", error);
974
+ }
725
975
  }
726
976
  //#region deleteData
727
977
  /**
@@ -737,6 +987,7 @@ var DataStore = class {
737
987
  this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
738
988
  ]);
739
989
  await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
990
+ this.events.emit("deleteData");
740
991
  }
741
992
  //#region encodingEnabled
742
993
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
@@ -757,16 +1008,22 @@ var DataStore = class {
757
1008
  let newData = oldData;
758
1009
  const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
759
1010
  let lastFmtVer = oldFmtVer;
760
- for (const [fmtVer, migrationFunc] of sortedMigrations) {
1011
+ for (let i = 0; i < sortedMigrations.length; i++) {
1012
+ const [fmtVer, migrationFunc] = sortedMigrations[i];
761
1013
  const ver = Number(fmtVer);
762
1014
  if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
763
1015
  try {
764
1016
  const migRes = migrationFunc(newData);
765
1017
  newData = migRes instanceof Promise ? await migRes : migRes;
766
1018
  lastFmtVer = oldFmtVer = ver;
1019
+ const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
1020
+ this.events.emit("migrateData", ver, newData, isFinal);
767
1021
  } catch (err) {
1022
+ const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
1023
+ this.events.emit("migrationError", ver, migError);
1024
+ this.events.emit("error", migError);
768
1025
  if (!resetOnError)
769
- throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
1026
+ throw migError;
770
1027
  await this.saveDefaultData();
771
1028
  return this.engine.deepCopy(this.defaultData);
772
1029
  }
@@ -777,10 +1034,9 @@ var DataStore = class {
777
1034
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
778
1035
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
779
1036
  ]);
780
- if (this.memoryCache)
781
- return this.cachedData = this.engine.deepCopy(newData);
782
- else
783
- return this.engine.deepCopy(newData);
1037
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
1038
+ this.events.emit("updateData", result);
1039
+ return result;
784
1040
  }
785
1041
  //#region migrateId
786
1042
  /**
@@ -809,6 +1065,7 @@ var DataStore = class {
809
1065
  this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
810
1066
  this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
811
1067
  ]);
1068
+ this.events.emit("migrateId", id, this.id);
812
1069
  }));
813
1070
  }
814
1071
  };
@@ -1053,7 +1310,10 @@ var DataStoreSerializer = class _DataStoreSerializer {
1053
1310
  ...options
1054
1311
  };
1055
1312
  }
1056
- /** Calculates the checksum of a string */
1313
+ /**
1314
+ * Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
1315
+ * Override this in a subclass if a custom checksum method is needed.
1316
+ */
1057
1317
  async calcChecksum(input) {
1058
1318
  return computeHash(input, "SHA-256");
1059
1319
  }
@@ -1177,205 +1437,6 @@ Has: ${checksum}`);
1177
1437
  }
1178
1438
  };
1179
1439
 
1180
- // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
1181
- var createNanoEvents = () => ({
1182
- emit(event, ...args) {
1183
- for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
1184
- callbacks[i](...args);
1185
- }
1186
- },
1187
- events: {},
1188
- on(event, cb) {
1189
- ;
1190
- (this.events[event] ||= []).push(cb);
1191
- return () => {
1192
- var _a;
1193
- this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
1194
- };
1195
- }
1196
- });
1197
-
1198
- // lib/NanoEmitter.ts
1199
- var NanoEmitter = class {
1200
- events = createNanoEvents();
1201
- eventUnsubscribes = [];
1202
- emitterOptions;
1203
- /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
1204
- constructor(options = {}) {
1205
- this.emitterOptions = {
1206
- publicEmit: false,
1207
- ...options
1208
- };
1209
- }
1210
- //#region on
1211
- /**
1212
- * Subscribes to an event and calls the callback when it's emitted.
1213
- * @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 "_")
1214
- * @returns Returns a function that can be called to unsubscribe the event listener
1215
- * @example ```ts
1216
- * const emitter = new NanoEmitter<{
1217
- * foo: (bar: string) => void;
1218
- * }>({
1219
- * publicEmit: true,
1220
- * });
1221
- *
1222
- * let i = 0;
1223
- * const unsub = emitter.on("foo", (bar) => {
1224
- * // unsubscribe after 10 events:
1225
- * if(++i === 10) unsub();
1226
- * console.log(bar);
1227
- * });
1228
- *
1229
- * emitter.emit("foo", "bar");
1230
- * ```
1231
- */
1232
- on(event, cb) {
1233
- let unsub;
1234
- const unsubProxy = () => {
1235
- if (!unsub)
1236
- return;
1237
- unsub();
1238
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
1239
- };
1240
- unsub = this.events.on(event, cb);
1241
- this.eventUnsubscribes.push(unsub);
1242
- return unsubProxy;
1243
- }
1244
- //#region once
1245
- /**
1246
- * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
1247
- * @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 "_")
1248
- * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
1249
- * @returns Returns a Promise that resolves with the event arguments when the event is emitted
1250
- * @example ```ts
1251
- * const emitter = new NanoEmitter<{
1252
- * foo: (bar: string) => void;
1253
- * }>();
1254
- *
1255
- * // Promise syntax:
1256
- * const [bar] = await emitter.once("foo");
1257
- * console.log(bar);
1258
- *
1259
- * // Callback syntax:
1260
- * emitter.once("foo", (bar) => console.log(bar));
1261
- * ```
1262
- */
1263
- once(event, cb) {
1264
- return new Promise((resolve) => {
1265
- let unsub;
1266
- const onceProxy = ((...args) => {
1267
- cb == null ? void 0 : cb(...args);
1268
- unsub == null ? void 0 : unsub();
1269
- resolve(args);
1270
- });
1271
- unsub = this.events.on(event, onceProxy);
1272
- this.eventUnsubscribes.push(unsub);
1273
- });
1274
- }
1275
- //#region onMulti
1276
- /**
1277
- * Allows subscribing to multiple events and calling the callback only when one of, all of, or a subset of the events are emitted, either continuously or only once.
1278
- * @param options An object or array of objects with the following properties:
1279
- * `callback` (required) is the function that will be called when the conditions are met.
1280
- *
1281
- * Set `once` to true to call the callback only once for the first event (or set of events) that match the criteria, then stop listening.
1282
- * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
1283
- *
1284
- * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
1285
- * If `allOf` is used, the callback will be called after all of the matching events are emitted at least once, then any time any of them are emitted.
1286
- * If both `oneOf` and `allOf` are used together, the callback will be called when any of the `oneOf` events are emitted AND all of the `allOf` events have been emitted at least once.
1287
- * At least one of `oneOf` or `allOf` must be provided.
1288
- *
1289
- * @returns Returns a function that can be called to unsubscribe all listeners created by this call. Alternatively, pass an `AbortSignal` to all options objects to achieve the same effect or for finer control.
1290
- */
1291
- onMulti(options) {
1292
- const allUnsubs = [];
1293
- const unsubAll = () => {
1294
- for (const unsub of allUnsubs)
1295
- unsub();
1296
- allUnsubs.splice(0, allUnsubs.length);
1297
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
1298
- };
1299
- for (const opts of Array.isArray(options) ? options : [options]) {
1300
- const optsWithDefaults = {
1301
- allOf: [],
1302
- oneOf: [],
1303
- once: false,
1304
- ...opts
1305
- };
1306
- const {
1307
- oneOf,
1308
- allOf,
1309
- once,
1310
- signal,
1311
- callback
1312
- } = optsWithDefaults;
1313
- if (signal == null ? void 0 : signal.aborted)
1314
- return unsubAll;
1315
- if (oneOf.length === 0 && allOf.length === 0)
1316
- throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
1317
- const curEvtUnsubs = [];
1318
- const checkUnsubAllEvt = (force = false) => {
1319
- if (!(signal == null ? void 0 : signal.aborted) && !force)
1320
- return;
1321
- for (const unsub of curEvtUnsubs)
1322
- unsub();
1323
- curEvtUnsubs.splice(0, curEvtUnsubs.length);
1324
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
1325
- };
1326
- const allOfEmitted = /* @__PURE__ */ new Set();
1327
- const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
1328
- for (const event of oneOf) {
1329
- const unsub = this.events.on(event, ((...args) => {
1330
- checkUnsubAllEvt();
1331
- if (allOfConditionMet()) {
1332
- callback(event, ...args);
1333
- if (once)
1334
- checkUnsubAllEvt(true);
1335
- }
1336
- }));
1337
- curEvtUnsubs.push(unsub);
1338
- }
1339
- for (const event of allOf) {
1340
- const unsub = this.events.on(event, ((...args) => {
1341
- checkUnsubAllEvt();
1342
- allOfEmitted.add(event);
1343
- if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
1344
- callback(event, ...args);
1345
- if (once)
1346
- checkUnsubAllEvt(true);
1347
- }
1348
- }));
1349
- curEvtUnsubs.push(unsub);
1350
- }
1351
- allUnsubs.push(() => checkUnsubAllEvt(true));
1352
- }
1353
- return unsubAll;
1354
- }
1355
- //#region emit
1356
- /**
1357
- * Emits an event on this instance.
1358
- * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1359
- * @param event The event to emit
1360
- * @param args The arguments to pass to the event listeners
1361
- * @returns Returns true if `publicEmit` is true and the event was emitted successfully
1362
- */
1363
- emit(event, ...args) {
1364
- if (this.emitterOptions.publicEmit) {
1365
- this.events.emit(event, ...args);
1366
- return true;
1367
- }
1368
- return false;
1369
- }
1370
- //#region unsubscribeAll
1371
- /** Unsubscribes all event listeners from this instance */
1372
- unsubscribeAll() {
1373
- for (const unsub of this.eventUnsubscribes)
1374
- unsub();
1375
- this.eventUnsubscribes = [];
1376
- }
1377
- };
1378
-
1379
1440
  // lib/Debouncer.ts
1380
1441
  var Debouncer = class extends NanoEmitter {
1381
1442
  /**
@@ -1383,8 +1444,8 @@ var Debouncer = class extends NanoEmitter {
1383
1444
  * @param timeout Timeout in milliseconds between letting through calls - defaults to 200
1384
1445
  * @param type The edge type to use for the debouncer - see {@linkcode DebouncerType} for details or [the documentation for an explanation and diagram](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debouncer) - defaults to "immediate"
1385
1446
  */
1386
- constructor(timeout = 200, type = "immediate") {
1387
- super();
1447
+ constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
1448
+ super(nanoEmitterOptions);
1388
1449
  this.timeout = timeout;
1389
1450
  this.type = type;
1390
1451
  }
@@ -1415,7 +1476,7 @@ var Debouncer = class extends NanoEmitter {
1415
1476
  //#region timeout
1416
1477
  /** Sets the timeout for the debouncer */
1417
1478
  setTimeout(timeout) {
1418
- this.emit("change", this.timeout = timeout, this.type);
1479
+ this.events.emit("change", this.timeout = timeout, this.type);
1419
1480
  }
1420
1481
  /** Returns the current timeout */
1421
1482
  getTimeout() {
@@ -1428,7 +1489,7 @@ var Debouncer = class extends NanoEmitter {
1428
1489
  //#region type
1429
1490
  /** Sets the edge type for the debouncer */
1430
1491
  setType(type) {
1431
- this.emit("change", this.timeout, this.type = type);
1492
+ this.events.emit("change", this.timeout, this.type = type);
1432
1493
  }
1433
1494
  /** Returns the current edge type */
1434
1495
  getType() {
@@ -1439,7 +1500,7 @@ var Debouncer = class extends NanoEmitter {
1439
1500
  call(...args) {
1440
1501
  const cl = (...a) => {
1441
1502
  this.queuedCall = void 0;
1442
- this.emit("call", ...a);
1503
+ this.events.emit("call", ...a);
1443
1504
  this.listeners.forEach((l) => l.call(this, ...a));
1444
1505
  };
1445
1506
  const setRepeatTimeout = () => {
@@ -1472,8 +1533,8 @@ var Debouncer = class extends NanoEmitter {
1472
1533
  }
1473
1534
  }
1474
1535
  };
1475
- function debounce(fn, timeout = 200, type = "immediate") {
1476
- const debouncer = new Debouncer(timeout, type);
1536
+ function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
1537
+ const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
1477
1538
  debouncer.addListener(fn);
1478
1539
  const func = ((...args) => debouncer.call(...args));
1479
1540
  func.debouncer = debouncer;