@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.
@@ -73,6 +73,7 @@ __export(lib_exports, {
73
73
  consumeGen: () => consumeGen,
74
74
  consumeStringGen: () => consumeStringGen,
75
75
  createProgressBar: () => createProgressBar,
76
+ createRecurringTask: () => createRecurringTask,
76
77
  darkenColor: () => darkenColor,
77
78
  debounce: () => debounce,
78
79
  decompress: () => decompress,
@@ -478,12 +479,45 @@ function getCallStack(asArray, lines = Infinity) {
478
479
  if (typeof lines !== "number" || isNaN(lines) || lines < 0)
479
480
  throw new TypeError("lines parameter must be a non-negative number");
480
481
  try {
481
- throw new Error("This is to capture a stack trace with CoreUtils.getCallStack(). (If you see this somewhere, you can safely ignore it.)");
482
+ throw new CustomError("GetCallStack", "Capturing a stack trace with CoreUtils.getCallStack(). If you see this anywhere, you can safely ignore it.");
482
483
  } catch (err) {
483
484
  const stack = (err.stack ?? "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
484
485
  return asArray !== false ? stack : stack.join("\n");
485
486
  }
486
487
  }
488
+ function createRecurringTask(options) {
489
+ var _a;
490
+ let iterations = 0;
491
+ let aborted = false;
492
+ (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
493
+ aborted = true;
494
+ }, { once: true });
495
+ const runRecurringTask = async (initial = false) => {
496
+ var _a2;
497
+ if (aborted)
498
+ return;
499
+ try {
500
+ if ((options.immediate ?? true) || !initial) {
501
+ iterations++;
502
+ if (await ((_a2 = options.condition) == null ? void 0 : _a2.call(options, iterations - 1)) ?? true) {
503
+ const val = await options.task(iterations - 1);
504
+ if (options.onSuccess)
505
+ await options.onSuccess(val, iterations - 1);
506
+ }
507
+ }
508
+ } catch (err) {
509
+ if (options.onError)
510
+ await options.onError(err, iterations - 1);
511
+ if (options.abortOnError)
512
+ aborted = true;
513
+ if (!options.onError && !options.abortOnError)
514
+ throw err;
515
+ }
516
+ if (!aborted && (typeof options.maxIterations !== "number" || iterations < options.maxIterations))
517
+ setTimeout(runRecurringTask, options.timeout);
518
+ };
519
+ return runRecurringTask(true);
520
+ }
487
521
 
488
522
  // lib/text.ts
489
523
  function autoPlural(term, num, pluralType = "auto") {
@@ -573,9 +607,208 @@ function truncStr(input, length, endStr = "...") {
573
607
  return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
574
608
  }
575
609
 
610
+ // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
611
+ var createNanoEvents = () => ({
612
+ emit(event, ...args) {
613
+ for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
614
+ callbacks[i](...args);
615
+ }
616
+ },
617
+ events: {},
618
+ on(event, cb) {
619
+ ;
620
+ (this.events[event] ||= []).push(cb);
621
+ return () => {
622
+ var _a;
623
+ this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
624
+ };
625
+ }
626
+ });
627
+
628
+ // lib/NanoEmitter.ts
629
+ var NanoEmitter = class {
630
+ events = createNanoEvents();
631
+ eventUnsubscribes = [];
632
+ emitterOptions;
633
+ /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
634
+ constructor(options = {}) {
635
+ this.emitterOptions = {
636
+ publicEmit: false,
637
+ ...options
638
+ };
639
+ }
640
+ //#region on
641
+ /**
642
+ * Subscribes to an event and calls the callback when it's emitted.
643
+ * @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 "_")
644
+ * @returns Returns a function that can be called to unsubscribe the event listener
645
+ * @example ```ts
646
+ * const emitter = new NanoEmitter<{
647
+ * foo: (bar: string) => void;
648
+ * }>({
649
+ * publicEmit: true,
650
+ * });
651
+ *
652
+ * let i = 0;
653
+ * const unsub = emitter.on("foo", (bar) => {
654
+ * // unsubscribe after 10 events:
655
+ * if(++i === 10) unsub();
656
+ * console.log(bar);
657
+ * });
658
+ *
659
+ * emitter.emit("foo", "bar");
660
+ * ```
661
+ */
662
+ on(event, cb) {
663
+ let unsub;
664
+ const unsubProxy = () => {
665
+ if (!unsub)
666
+ return;
667
+ unsub();
668
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
669
+ };
670
+ unsub = this.events.on(event, cb);
671
+ this.eventUnsubscribes.push(unsub);
672
+ return unsubProxy;
673
+ }
674
+ //#region once
675
+ /**
676
+ * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
677
+ * @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 "_")
678
+ * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
679
+ * @returns Returns a Promise that resolves with the event arguments when the event is emitted
680
+ * @example ```ts
681
+ * const emitter = new NanoEmitter<{
682
+ * foo: (bar: string) => void;
683
+ * }>();
684
+ *
685
+ * // Promise syntax:
686
+ * const [bar] = await emitter.once("foo");
687
+ * console.log(bar);
688
+ *
689
+ * // Callback syntax:
690
+ * emitter.once("foo", (bar) => console.log(bar));
691
+ * ```
692
+ */
693
+ once(event, cb) {
694
+ return new Promise((resolve) => {
695
+ let unsub;
696
+ const onceProxy = ((...args) => {
697
+ cb == null ? void 0 : cb(...args);
698
+ unsub == null ? void 0 : unsub();
699
+ resolve(args);
700
+ });
701
+ unsub = this.events.on(event, onceProxy);
702
+ this.eventUnsubscribes.push(unsub);
703
+ });
704
+ }
705
+ //#region onMulti
706
+ /**
707
+ * 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.
708
+ * @param options An object or array of objects with the following properties:
709
+ * `callback` (required) is the function that will be called when the conditions are met.
710
+ *
711
+ * 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.
712
+ * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
713
+ *
714
+ * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
715
+ * 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.
716
+ * 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.
717
+ * At least one of `oneOf` or `allOf` must be provided.
718
+ *
719
+ * @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.
720
+ */
721
+ onMulti(options) {
722
+ const allUnsubs = [];
723
+ const unsubAll = () => {
724
+ for (const unsub of allUnsubs)
725
+ unsub();
726
+ allUnsubs.splice(0, allUnsubs.length);
727
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
728
+ };
729
+ for (const opts of Array.isArray(options) ? options : [options]) {
730
+ const optsWithDefaults = {
731
+ allOf: [],
732
+ oneOf: [],
733
+ once: false,
734
+ ...opts
735
+ };
736
+ const {
737
+ oneOf,
738
+ allOf,
739
+ once,
740
+ signal,
741
+ callback
742
+ } = optsWithDefaults;
743
+ if (signal == null ? void 0 : signal.aborted)
744
+ return unsubAll;
745
+ if (oneOf.length === 0 && allOf.length === 0)
746
+ throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
747
+ const curEvtUnsubs = [];
748
+ const checkUnsubAllEvt = (force = false) => {
749
+ if (!(signal == null ? void 0 : signal.aborted) && !force)
750
+ return;
751
+ for (const unsub of curEvtUnsubs)
752
+ unsub();
753
+ curEvtUnsubs.splice(0, curEvtUnsubs.length);
754
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
755
+ };
756
+ const allOfEmitted = /* @__PURE__ */ new Set();
757
+ const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
758
+ for (const event of oneOf) {
759
+ const unsub = this.events.on(event, ((...args) => {
760
+ checkUnsubAllEvt();
761
+ if (allOfConditionMet()) {
762
+ callback(event, ...args);
763
+ if (once)
764
+ checkUnsubAllEvt(true);
765
+ }
766
+ }));
767
+ curEvtUnsubs.push(unsub);
768
+ }
769
+ for (const event of allOf) {
770
+ const unsub = this.events.on(event, ((...args) => {
771
+ checkUnsubAllEvt();
772
+ allOfEmitted.add(event);
773
+ if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
774
+ callback(event, ...args);
775
+ if (once)
776
+ checkUnsubAllEvt(true);
777
+ }
778
+ }));
779
+ curEvtUnsubs.push(unsub);
780
+ }
781
+ allUnsubs.push(() => checkUnsubAllEvt(true));
782
+ }
783
+ return unsubAll;
784
+ }
785
+ //#region emit
786
+ /**
787
+ * Emits an event on this instance.
788
+ * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
789
+ * @param event The event to emit
790
+ * @param args The arguments to pass to the event listeners
791
+ * @returns Returns true if `publicEmit` is true and the event was emitted successfully
792
+ */
793
+ emit(event, ...args) {
794
+ if (this.emitterOptions.publicEmit) {
795
+ this.events.emit(event, ...args);
796
+ return true;
797
+ }
798
+ return false;
799
+ }
800
+ //#region unsubscribeAll
801
+ /** Unsubscribes all event listeners from this instance */
802
+ unsubscribeAll() {
803
+ for (const unsub of this.eventUnsubscribes)
804
+ unsub();
805
+ this.eventUnsubscribes = [];
806
+ }
807
+ };
808
+
576
809
  // lib/DataStore.ts
577
810
  var dsFmtVer = 1;
578
- var DataStore = class {
811
+ var DataStore = class extends NanoEmitter {
579
812
  id;
580
813
  formatVersion;
581
814
  defaultData;
@@ -607,6 +840,7 @@ var DataStore = class {
607
840
  * @param opts The options for this DataStore instance
608
841
  */
609
842
  constructor(opts) {
843
+ super(opts.nanoEmitterOptions);
610
844
  this.id = opts.id;
611
845
  this.formatVersion = opts.formatVersion;
612
846
  this.defaultData = opts.defaultData;
@@ -677,30 +911,26 @@ var DataStore = class {
677
911
  this.migrateIds = [];
678
912
  }
679
913
  const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
680
- let storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
914
+ const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
681
915
  if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
682
- await this.saveDefaultData();
683
- return this.engine.deepCopy(this.defaultData);
916
+ await this.saveDefaultData(false);
917
+ const data = this.engine.deepCopy(this.defaultData);
918
+ this.events.emit("loadData", data);
919
+ return data;
684
920
  }
685
921
  const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
686
922
  const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
687
923
  const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
688
- let saveData = false;
689
- if (isNaN(storedFmtVer)) {
690
- await this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, storedFmtVer = this.formatVersion);
691
- saveData = true;
692
- }
693
924
  let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
694
925
  if (storedFmtVer < this.formatVersion && this.migrations)
695
926
  parsed = await this.runMigrations(parsed, storedFmtVer);
696
- if (saveData)
697
- await this.setData(parsed);
698
- if (this.memoryCache)
699
- return this.cachedData = this.engine.deepCopy(parsed);
700
- else
701
- return this.engine.deepCopy(parsed);
927
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
928
+ this.events.emit("loadData", result);
929
+ return result;
702
930
  } catch (err) {
931
+ const error = err instanceof Error ? err : new Error(String(err));
703
932
  console.warn("Error while parsing JSON data, resetting it to the default value.", err);
933
+ this.events.emit("error", error);
704
934
  await this.saveDefaultData();
705
935
  return this.defaultData;
706
936
  }
@@ -719,27 +949,47 @@ var DataStore = class {
719
949
  //#region setData
720
950
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
721
951
  setData(data) {
722
- if (this.memoryCache)
952
+ const dataCopy = this.engine.deepCopy(data);
953
+ if (this.memoryCache) {
723
954
  this.cachedData = data;
955
+ this.events.emit("updateDataSync", dataCopy);
956
+ }
724
957
  return new Promise(async (resolve) => {
725
- await Promise.allSettled([
958
+ const results = await Promise.allSettled([
726
959
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
727
960
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
728
961
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
729
962
  ]);
963
+ if (results.every((r) => r.status === "fulfilled"))
964
+ this.events.emit("updateData", dataCopy);
965
+ else {
966
+ const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
967
+ console.error(error);
968
+ this.events.emit("error", error);
969
+ }
730
970
  resolve();
731
971
  });
732
972
  }
733
973
  //#region saveDefaultData
734
- /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
735
- async saveDefaultData() {
974
+ /**
975
+ * Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
976
+ * @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
977
+ */
978
+ async saveDefaultData(emitEvent = true) {
736
979
  if (this.memoryCache)
737
980
  this.cachedData = this.defaultData;
738
- await Promise.allSettled([
981
+ const results = await Promise.allSettled([
739
982
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
740
983
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
741
984
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
742
985
  ]);
986
+ if (results.every((r) => r.status === "fulfilled"))
987
+ emitEvent && this.events.emit("setDefaultData", this.defaultData);
988
+ else {
989
+ const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
990
+ console.error(error);
991
+ this.events.emit("error", error);
992
+ }
743
993
  }
744
994
  //#region deleteData
745
995
  /**
@@ -755,6 +1005,7 @@ var DataStore = class {
755
1005
  this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
756
1006
  ]);
757
1007
  await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
1008
+ this.events.emit("deleteData");
758
1009
  }
759
1010
  //#region encodingEnabled
760
1011
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
@@ -775,16 +1026,22 @@ var DataStore = class {
775
1026
  let newData = oldData;
776
1027
  const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
777
1028
  let lastFmtVer = oldFmtVer;
778
- for (const [fmtVer, migrationFunc] of sortedMigrations) {
1029
+ for (let i = 0; i < sortedMigrations.length; i++) {
1030
+ const [fmtVer, migrationFunc] = sortedMigrations[i];
779
1031
  const ver = Number(fmtVer);
780
1032
  if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
781
1033
  try {
782
1034
  const migRes = migrationFunc(newData);
783
1035
  newData = migRes instanceof Promise ? await migRes : migRes;
784
1036
  lastFmtVer = oldFmtVer = ver;
1037
+ const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
1038
+ this.events.emit("migrateData", ver, newData, isFinal);
785
1039
  } catch (err) {
1040
+ const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
1041
+ this.events.emit("migrationError", ver, migError);
1042
+ this.events.emit("error", migError);
786
1043
  if (!resetOnError)
787
- throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
1044
+ throw migError;
788
1045
  await this.saveDefaultData();
789
1046
  return this.engine.deepCopy(this.defaultData);
790
1047
  }
@@ -795,10 +1052,9 @@ var DataStore = class {
795
1052
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
796
1053
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
797
1054
  ]);
798
- if (this.memoryCache)
799
- return this.cachedData = this.engine.deepCopy(newData);
800
- else
801
- return this.engine.deepCopy(newData);
1055
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
1056
+ this.events.emit("updateData", result);
1057
+ return result;
802
1058
  }
803
1059
  //#region migrateId
804
1060
  /**
@@ -827,6 +1083,7 @@ var DataStore = class {
827
1083
  this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
828
1084
  this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
829
1085
  ]);
1086
+ this.events.emit("migrateId", id, this.id);
830
1087
  }));
831
1088
  }
832
1089
  };
@@ -1071,7 +1328,10 @@ var DataStoreSerializer = class _DataStoreSerializer {
1071
1328
  ...options
1072
1329
  };
1073
1330
  }
1074
- /** Calculates the checksum of a string */
1331
+ /**
1332
+ * Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
1333
+ * Override this in a subclass if a custom checksum method is needed.
1334
+ */
1075
1335
  async calcChecksum(input) {
1076
1336
  return computeHash(input, "SHA-256");
1077
1337
  }
@@ -1195,214 +1455,15 @@ Has: ${checksum}`);
1195
1455
  }
1196
1456
  };
1197
1457
 
1198
- // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
1199
- var createNanoEvents = () => ({
1200
- emit(event, ...args) {
1201
- for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
1202
- callbacks[i](...args);
1203
- }
1204
- },
1205
- events: {},
1206
- on(event, cb) {
1207
- ;
1208
- (this.events[event] ||= []).push(cb);
1209
- return () => {
1210
- var _a;
1211
- this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
1212
- };
1213
- }
1214
- });
1215
-
1216
- // lib/NanoEmitter.ts
1217
- var NanoEmitter = class {
1218
- events = createNanoEvents();
1219
- eventUnsubscribes = [];
1220
- emitterOptions;
1221
- /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
1222
- constructor(options = {}) {
1223
- this.emitterOptions = {
1224
- publicEmit: false,
1225
- ...options
1226
- };
1227
- }
1228
- //#region on
1229
- /**
1230
- * Subscribes to an event and calls the callback when it's emitted.
1231
- * @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 "_")
1232
- * @returns Returns a function that can be called to unsubscribe the event listener
1233
- * @example ```ts
1234
- * const emitter = new NanoEmitter<{
1235
- * foo: (bar: string) => void;
1236
- * }>({
1237
- * publicEmit: true,
1238
- * });
1239
- *
1240
- * let i = 0;
1241
- * const unsub = emitter.on("foo", (bar) => {
1242
- * // unsubscribe after 10 events:
1243
- * if(++i === 10) unsub();
1244
- * console.log(bar);
1245
- * });
1246
- *
1247
- * emitter.emit("foo", "bar");
1248
- * ```
1249
- */
1250
- on(event, cb) {
1251
- let unsub;
1252
- const unsubProxy = () => {
1253
- if (!unsub)
1254
- return;
1255
- unsub();
1256
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
1257
- };
1258
- unsub = this.events.on(event, cb);
1259
- this.eventUnsubscribes.push(unsub);
1260
- return unsubProxy;
1261
- }
1262
- //#region once
1263
- /**
1264
- * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
1265
- * @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 "_")
1266
- * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
1267
- * @returns Returns a Promise that resolves with the event arguments when the event is emitted
1268
- * @example ```ts
1269
- * const emitter = new NanoEmitter<{
1270
- * foo: (bar: string) => void;
1271
- * }>();
1272
- *
1273
- * // Promise syntax:
1274
- * const [bar] = await emitter.once("foo");
1275
- * console.log(bar);
1276
- *
1277
- * // Callback syntax:
1278
- * emitter.once("foo", (bar) => console.log(bar));
1279
- * ```
1280
- */
1281
- once(event, cb) {
1282
- return new Promise((resolve) => {
1283
- let unsub;
1284
- const onceProxy = ((...args) => {
1285
- cb == null ? void 0 : cb(...args);
1286
- unsub == null ? void 0 : unsub();
1287
- resolve(args);
1288
- });
1289
- unsub = this.events.on(event, onceProxy);
1290
- this.eventUnsubscribes.push(unsub);
1291
- });
1292
- }
1293
- //#region onMulti
1294
- /**
1295
- * 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.
1296
- * @param options An object or array of objects with the following properties:
1297
- * `callback` (required) is the function that will be called when the conditions are met.
1298
- *
1299
- * 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.
1300
- * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
1301
- *
1302
- * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
1303
- * 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.
1304
- * 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.
1305
- * At least one of `oneOf` or `allOf` must be provided.
1306
- *
1307
- * @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.
1308
- */
1309
- onMulti(options) {
1310
- const allUnsubs = [];
1311
- const unsubAll = () => {
1312
- for (const unsub of allUnsubs)
1313
- unsub();
1314
- allUnsubs.splice(0, allUnsubs.length);
1315
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
1316
- };
1317
- for (const opts of Array.isArray(options) ? options : [options]) {
1318
- const optsWithDefaults = {
1319
- allOf: [],
1320
- oneOf: [],
1321
- once: false,
1322
- ...opts
1323
- };
1324
- const {
1325
- oneOf,
1326
- allOf,
1327
- once,
1328
- signal,
1329
- callback
1330
- } = optsWithDefaults;
1331
- if (signal == null ? void 0 : signal.aborted)
1332
- return unsubAll;
1333
- if (oneOf.length === 0 && allOf.length === 0)
1334
- throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
1335
- const curEvtUnsubs = [];
1336
- const checkUnsubAllEvt = (force = false) => {
1337
- if (!(signal == null ? void 0 : signal.aborted) && !force)
1338
- return;
1339
- for (const unsub of curEvtUnsubs)
1340
- unsub();
1341
- curEvtUnsubs.splice(0, curEvtUnsubs.length);
1342
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
1343
- };
1344
- const allOfEmitted = /* @__PURE__ */ new Set();
1345
- const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
1346
- for (const event of oneOf) {
1347
- const unsub = this.events.on(event, ((...args) => {
1348
- checkUnsubAllEvt();
1349
- if (allOfConditionMet()) {
1350
- callback(event, ...args);
1351
- if (once)
1352
- checkUnsubAllEvt(true);
1353
- }
1354
- }));
1355
- curEvtUnsubs.push(unsub);
1356
- }
1357
- for (const event of allOf) {
1358
- const unsub = this.events.on(event, ((...args) => {
1359
- checkUnsubAllEvt();
1360
- allOfEmitted.add(event);
1361
- if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
1362
- callback(event, ...args);
1363
- if (once)
1364
- checkUnsubAllEvt(true);
1365
- }
1366
- }));
1367
- curEvtUnsubs.push(unsub);
1368
- }
1369
- allUnsubs.push(() => checkUnsubAllEvt(true));
1370
- }
1371
- return unsubAll;
1372
- }
1373
- //#region emit
1374
- /**
1375
- * Emits an event on this instance.
1376
- * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1377
- * @param event The event to emit
1378
- * @param args The arguments to pass to the event listeners
1379
- * @returns Returns true if `publicEmit` is true and the event was emitted successfully
1380
- */
1381
- emit(event, ...args) {
1382
- if (this.emitterOptions.publicEmit) {
1383
- this.events.emit(event, ...args);
1384
- return true;
1385
- }
1386
- return false;
1387
- }
1388
- //#region unsubscribeAll
1389
- /** Unsubscribes all event listeners from this instance */
1390
- unsubscribeAll() {
1391
- for (const unsub of this.eventUnsubscribes)
1392
- unsub();
1393
- this.eventUnsubscribes = [];
1394
- }
1395
- };
1396
-
1397
- // lib/Debouncer.ts
1398
- var Debouncer = class extends NanoEmitter {
1458
+ // lib/Debouncer.ts
1459
+ var Debouncer = class extends NanoEmitter {
1399
1460
  /**
1400
1461
  * Creates a new debouncer with the specified timeout and edge type.
1401
1462
  * @param timeout Timeout in milliseconds between letting through calls - defaults to 200
1402
1463
  * @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"
1403
1464
  */
1404
- constructor(timeout = 200, type = "immediate") {
1405
- super();
1465
+ constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
1466
+ super(nanoEmitterOptions);
1406
1467
  this.timeout = timeout;
1407
1468
  this.type = type;
1408
1469
  }
@@ -1433,173 +1494,7 @@ var Debouncer = class extends NanoEmitter {
1433
1494
  //#region timeout
1434
1495
  /** Sets the timeout for the debouncer */
1435
1496
  setTimeout(timeout) {
1436
- this.emit("change", this.timeout = timeout, this.type);
1437
- }
1438
- /** Returns the current timeout */
1439
- getTimeout() {
1440
- return this.timeout;
1441
- }
1442
- /** Whether the timeout is currently active, meaning any latest call to the {@linkcode call()} method will be queued */
1443
- isTimeoutActive() {
1444
- return typeof this.activeTimeout !== "undefined";
1445
- }
1446
- //#region type
1447
- /** Sets the edge type for the debouncer */
1448
- setType(type) {
1449
- this.emit("change", this.timeout, this.type = type);
1450
- }
1451
- /** Returns the current edge type */
1452
- getType() {
1453
- return this.type;
1454
- }
1455
- //#region call
1456
- /** Use this to call the debouncer with the specified arguments that will be passed to all listener functions registered with {@linkcode addListener()} */
1457
- call(...args) {
1458
- const cl = (...a) => {
1459
- this.queuedCall = void 0;
1460
- this.emit("call", ...a);
1461
- this.listeners.forEach((l) => l.call(this, ...a));
1462
- };
1463
- const setRepeatTimeout = () => {
1464
- this.activeTimeout = setTimeout(() => {
1465
- if (this.queuedCall) {
1466
- this.queuedCall();
1467
- setRepeatTimeout();
1468
- } else
1469
- this.activeTimeout = void 0;
1470
- }, this.timeout);
1471
- };
1472
- switch (this.type) {
1473
- case "immediate":
1474
- if (typeof this.activeTimeout === "undefined") {
1475
- cl(...args);
1476
- setRepeatTimeout();
1477
- } else
1478
- this.queuedCall = () => cl(...args);
1479
- break;
1480
- case "idle":
1481
- if (this.activeTimeout)
1482
- clearTimeout(this.activeTimeout);
1483
- this.activeTimeout = setTimeout(() => {
1484
- cl(...args);
1485
- this.activeTimeout = void 0;
1486
- }, this.timeout);
1487
- break;
1488
- default:
1489
- throw new TypeError(`Invalid debouncer type: ${this.type}`);
1490
- }
1491
- }
1492
- };
1493
- function debounce(fn, timeout = 200, type = "immediate") {
1494
- const debouncer = new Debouncer(timeout, type);
1495
- debouncer.addListener(fn);
1496
- const func = ((...args) => debouncer.call(...args));
1497
- func.debouncer = debouncer;
1498
- return func;
1499
- }
1500
- `oneOf` or `allOf` or both must be provided in the options");
1501
- const curEvtUnsubs = [];
1502
- const checkUnsubAllEvt = (force = false) => {
1503
- if (!(signal == null ? void 0 : signal.aborted) && !force)
1504
- return;
1505
- for (const unsub of curEvtUnsubs)
1506
- unsub();
1507
- curEvtUnsubs.splice(0, curEvtUnsubs.length);
1508
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
1509
- };
1510
- const allOfEmitted = /* @__PURE__ */ new Set();
1511
- const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
1512
- for (const event of oneOf) {
1513
- const unsub = this.events.on(event, ((...args) => {
1514
- checkUnsubAllEvt();
1515
- if (allOfConditionMet()) {
1516
- callback(event, ...args);
1517
- if (once)
1518
- checkUnsubAllEvt(true);
1519
- }
1520
- }));
1521
- curEvtUnsubs.push(unsub);
1522
- }
1523
- for (const event of allOf) {
1524
- const unsub = this.events.on(event, ((...args) => {
1525
- checkUnsubAllEvt();
1526
- allOfEmitted.add(event);
1527
- if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
1528
- callback(event, ...args);
1529
- if (once)
1530
- checkUnsubAllEvt(true);
1531
- }
1532
- }));
1533
- curEvtUnsubs.push(unsub);
1534
- }
1535
- allUnsubs.push(() => checkUnsubAllEvt(true));
1536
- }
1537
- return unsubAll;
1538
- }
1539
- //#region emit
1540
- /**
1541
- * Emits an event on this instance.
1542
- * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1543
- * @param event The event to emit
1544
- * @param args The arguments to pass to the event listeners
1545
- * @returns Returns true if `publicEmit` is true and the event was emitted successfully
1546
- */
1547
- emit(event, ...args) {
1548
- if (this.emitterOptions.publicEmit) {
1549
- this.events.emit(event, ...args);
1550
- return true;
1551
- }
1552
- return false;
1553
- }
1554
- //#region unsubscribeAll
1555
- /** Unsubscribes all event listeners from this instance */
1556
- unsubscribeAll() {
1557
- for (const unsub of this.eventUnsubscribes)
1558
- unsub();
1559
- this.eventUnsubscribes = [];
1560
- }
1561
- };
1562
-
1563
- // lib/Debouncer.ts
1564
- var Debouncer = class extends NanoEmitter {
1565
- /**
1566
- * Creates a new debouncer with the specified timeout and edge type.
1567
- * @param timeout Timeout in milliseconds between letting through calls - defaults to 200
1568
- * @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"
1569
- */
1570
- constructor(timeout = 200, type = "immediate") {
1571
- super();
1572
- this.timeout = timeout;
1573
- this.type = type;
1574
- /** All registered listener functions and the time they were attached */
1575
- __publicField(this, "listeners", []);
1576
- /** The currently active timeout */
1577
- __publicField(this, "activeTimeout");
1578
- /** The latest queued call */
1579
- __publicField(this, "queuedCall");
1580
- }
1581
- //#region listeners
1582
- /** Adds a listener function that will be called on timeout */
1583
- addListener(fn) {
1584
- this.listeners.push(fn);
1585
- }
1586
- /** Removes the listener with the specified function reference */
1587
- removeListener(fn) {
1588
- const idx = this.listeners.findIndex((l) => l === fn);
1589
- idx !== -1 && this.listeners.splice(idx, 1);
1590
- }
1591
- /** Removes all listeners */
1592
- removeAllListeners() {
1593
- this.listeners = [];
1594
- }
1595
- /** Returns all registered listeners */
1596
- getListeners() {
1597
- return this.listeners;
1598
- }
1599
- //#region timeout
1600
- /** Sets the timeout for the debouncer */
1601
- setTimeout(timeout) {
1602
- this.emit("change", this.timeout = timeout, this.type);
1497
+ this.events.emit("change", this.timeout = timeout, this.type);
1603
1498
  }
1604
1499
  /** Returns the current timeout */
1605
1500
  getTimeout() {
@@ -1612,7 +1507,7 @@ var Debouncer = class extends NanoEmitter {
1612
1507
  //#region type
1613
1508
  /** Sets the edge type for the debouncer */
1614
1509
  setType(type) {
1615
- this.emit("change", this.timeout, this.type = type);
1510
+ this.events.emit("change", this.timeout, this.type = type);
1616
1511
  }
1617
1512
  /** Returns the current edge type */
1618
1513
  getType() {
@@ -1623,7 +1518,7 @@ var Debouncer = class extends NanoEmitter {
1623
1518
  call(...args) {
1624
1519
  const cl = (...a) => {
1625
1520
  this.queuedCall = void 0;
1626
- this.emit("call", ...a);
1521
+ this.events.emit("call", ...a);
1627
1522
  this.listeners.forEach((l) => l.call(this, ...a));
1628
1523
  };
1629
1524
  const setRepeatTimeout = () => {
@@ -1656,16 +1551,14 @@ var Debouncer = class extends NanoEmitter {
1656
1551
  }
1657
1552
  }
1658
1553
  };
1659
- function debounce(fn, timeout = 200, type = "immediate") {
1660
- const debouncer = new Debouncer(timeout, type);
1554
+ function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
1555
+ const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
1661
1556
  debouncer.addListener(fn);
1662
1557
  const func = ((...args) => debouncer.call(...args));
1663
1558
  func.debouncer = debouncer;
1664
1559
  return func;
1665
1560
  }
1666
1561
 
1667
- if(__exports != exports)module.exports = exports;return module.exports}));
1668
- //# sourceMappingURL=CoreUtils.umd.js.map
1669
1562
 
1670
1563
 
1671
1564
  if (typeof module.exports == "object" && typeof exports == "object") {