@sv443-network/coreutils 3.2.0 → 3.3.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,28 @@
1
1
  # @sv443-network/coreutils
2
2
 
3
+ ## 3.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4af3dda: `DataStore` now extends `NanoEmitter` to allow for much better event-driven programming using the methods `on()`, `once()`, `onMulti()`, etc.
8
+ Currently, the following events are emitted by the `DataStore` class:
9
+
10
+ | Name | Description |
11
+ | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12
+ | `loadData` | Whenever the data is loaded from persistent storage with `DataStore.loadData()`. |
13
+ | `updateData` | When the data is updated with `DataStore.setData()` or `DataStore.runMigrations()`. |
14
+ | `updateDataSync` | When the memory cache was updated with `DataStore.setData()`, before the data is saved to persistent storage. Not emitted if `memoryCache` is set to `false`. |
15
+ | `migrateData` | For every called migration function with the resulting data. |
16
+ | `migrateId` | For every successfully migrated old ID. Gets passed the old and new ID. |
17
+ | `setDefaultData` | Whenever the data is reset to the default value with `DataStore.saveDefaultData()` (will not be called on the initial population of persistent storage with the default data in `DataStore.loadData()`). |
18
+ | `deleteData` | After the data was deleted from persistent storage with `DataStore.deleteData()`. |
19
+ | `error` | When an error occurs at any point. |
20
+ | `migrationError` | Only when an error occurs during a migration function. |
21
+
22
+ ### Patch Changes
23
+
24
+ - f7dbacf: Fixed DataStore error handling inconsistencies.
25
+
3
26
  ## 3.2.0
4
27
 
5
28
  ### Minor Changes
@@ -555,9 +555,208 @@ function truncStr(input, length, endStr = "...") {
555
555
  return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
556
556
  }
557
557
 
558
+ // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
559
+ var createNanoEvents = () => ({
560
+ emit(event, ...args) {
561
+ for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
562
+ callbacks[i](...args);
563
+ }
564
+ },
565
+ events: {},
566
+ on(event, cb) {
567
+ ;
568
+ (this.events[event] ||= []).push(cb);
569
+ return () => {
570
+ var _a;
571
+ this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
572
+ };
573
+ }
574
+ });
575
+
576
+ // lib/NanoEmitter.ts
577
+ var NanoEmitter = class {
578
+ events = createNanoEvents();
579
+ eventUnsubscribes = [];
580
+ emitterOptions;
581
+ /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
582
+ constructor(options = {}) {
583
+ this.emitterOptions = {
584
+ publicEmit: false,
585
+ ...options
586
+ };
587
+ }
588
+ //#region on
589
+ /**
590
+ * Subscribes to an event and calls the callback when it's emitted.
591
+ * @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 "_")
592
+ * @returns Returns a function that can be called to unsubscribe the event listener
593
+ * @example ```ts
594
+ * const emitter = new NanoEmitter<{
595
+ * foo: (bar: string) => void;
596
+ * }>({
597
+ * publicEmit: true,
598
+ * });
599
+ *
600
+ * let i = 0;
601
+ * const unsub = emitter.on("foo", (bar) => {
602
+ * // unsubscribe after 10 events:
603
+ * if(++i === 10) unsub();
604
+ * console.log(bar);
605
+ * });
606
+ *
607
+ * emitter.emit("foo", "bar");
608
+ * ```
609
+ */
610
+ on(event, cb) {
611
+ let unsub;
612
+ const unsubProxy = () => {
613
+ if (!unsub)
614
+ return;
615
+ unsub();
616
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
617
+ };
618
+ unsub = this.events.on(event, cb);
619
+ this.eventUnsubscribes.push(unsub);
620
+ return unsubProxy;
621
+ }
622
+ //#region once
623
+ /**
624
+ * Subscribes to an event and calls the callback or resolves the Promise only once 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
+ * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
627
+ * @returns Returns a Promise that resolves with the event arguments when the event is emitted
628
+ * @example ```ts
629
+ * const emitter = new NanoEmitter<{
630
+ * foo: (bar: string) => void;
631
+ * }>();
632
+ *
633
+ * // Promise syntax:
634
+ * const [bar] = await emitter.once("foo");
635
+ * console.log(bar);
636
+ *
637
+ * // Callback syntax:
638
+ * emitter.once("foo", (bar) => console.log(bar));
639
+ * ```
640
+ */
641
+ once(event, cb) {
642
+ return new Promise((resolve) => {
643
+ let unsub;
644
+ const onceProxy = ((...args) => {
645
+ cb == null ? void 0 : cb(...args);
646
+ unsub == null ? void 0 : unsub();
647
+ resolve(args);
648
+ });
649
+ unsub = this.events.on(event, onceProxy);
650
+ this.eventUnsubscribes.push(unsub);
651
+ });
652
+ }
653
+ //#region onMulti
654
+ /**
655
+ * 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.
656
+ * @param options An object or array of objects with the following properties:
657
+ * `callback` (required) is the function that will be called when the conditions are met.
658
+ *
659
+ * 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.
660
+ * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
661
+ *
662
+ * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
663
+ * 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.
664
+ * 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.
665
+ * At least one of `oneOf` or `allOf` must be provided.
666
+ *
667
+ * @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.
668
+ */
669
+ onMulti(options) {
670
+ const allUnsubs = [];
671
+ const unsubAll = () => {
672
+ for (const unsub of allUnsubs)
673
+ unsub();
674
+ allUnsubs.splice(0, allUnsubs.length);
675
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
676
+ };
677
+ for (const opts of Array.isArray(options) ? options : [options]) {
678
+ const optsWithDefaults = {
679
+ allOf: [],
680
+ oneOf: [],
681
+ once: false,
682
+ ...opts
683
+ };
684
+ const {
685
+ oneOf,
686
+ allOf,
687
+ once,
688
+ signal,
689
+ callback
690
+ } = optsWithDefaults;
691
+ if (signal == null ? void 0 : signal.aborted)
692
+ return unsubAll;
693
+ if (oneOf.length === 0 && allOf.length === 0)
694
+ throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
695
+ const curEvtUnsubs = [];
696
+ const checkUnsubAllEvt = (force = false) => {
697
+ if (!(signal == null ? void 0 : signal.aborted) && !force)
698
+ return;
699
+ for (const unsub of curEvtUnsubs)
700
+ unsub();
701
+ curEvtUnsubs.splice(0, curEvtUnsubs.length);
702
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
703
+ };
704
+ const allOfEmitted = /* @__PURE__ */ new Set();
705
+ const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
706
+ for (const event of oneOf) {
707
+ const unsub = this.events.on(event, ((...args) => {
708
+ checkUnsubAllEvt();
709
+ if (allOfConditionMet()) {
710
+ callback(event, ...args);
711
+ if (once)
712
+ checkUnsubAllEvt(true);
713
+ }
714
+ }));
715
+ curEvtUnsubs.push(unsub);
716
+ }
717
+ for (const event of allOf) {
718
+ const unsub = this.events.on(event, ((...args) => {
719
+ checkUnsubAllEvt();
720
+ allOfEmitted.add(event);
721
+ if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
722
+ callback(event, ...args);
723
+ if (once)
724
+ checkUnsubAllEvt(true);
725
+ }
726
+ }));
727
+ curEvtUnsubs.push(unsub);
728
+ }
729
+ allUnsubs.push(() => checkUnsubAllEvt(true));
730
+ }
731
+ return unsubAll;
732
+ }
733
+ //#region emit
734
+ /**
735
+ * Emits an event on this instance.
736
+ * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
737
+ * @param event The event to emit
738
+ * @param args The arguments to pass to the event listeners
739
+ * @returns Returns true if `publicEmit` is true and the event was emitted successfully
740
+ */
741
+ emit(event, ...args) {
742
+ if (this.emitterOptions.publicEmit) {
743
+ this.events.emit(event, ...args);
744
+ return true;
745
+ }
746
+ return false;
747
+ }
748
+ //#region unsubscribeAll
749
+ /** Unsubscribes all event listeners from this instance */
750
+ unsubscribeAll() {
751
+ for (const unsub of this.eventUnsubscribes)
752
+ unsub();
753
+ this.eventUnsubscribes = [];
754
+ }
755
+ };
756
+
558
757
  // lib/DataStore.ts
559
758
  var dsFmtVer = 1;
560
- var DataStore = class {
759
+ var DataStore = class extends NanoEmitter {
561
760
  id;
562
761
  formatVersion;
563
762
  defaultData;
@@ -589,6 +788,7 @@ var DataStore = class {
589
788
  * @param opts The options for this DataStore instance
590
789
  */
591
790
  constructor(opts) {
791
+ super(opts.nanoEmitterOptions);
592
792
  this.id = opts.id;
593
793
  this.formatVersion = opts.formatVersion;
594
794
  this.defaultData = opts.defaultData;
@@ -659,30 +859,26 @@ var DataStore = class {
659
859
  this.migrateIds = [];
660
860
  }
661
861
  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));
862
+ const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
663
863
  if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
664
- await this.saveDefaultData();
665
- return this.engine.deepCopy(this.defaultData);
864
+ await this.saveDefaultData(false);
865
+ const data = this.engine.deepCopy(this.defaultData);
866
+ this.events.emit("loadData", data);
867
+ return data;
666
868
  }
667
869
  const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
668
870
  const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
669
871
  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
872
  let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
676
873
  if (storedFmtVer < this.formatVersion && this.migrations)
677
874
  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);
875
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
876
+ this.events.emit("loadData", result);
877
+ return result;
684
878
  } catch (err) {
879
+ const error = err instanceof Error ? err : new Error(String(err));
685
880
  console.warn("Error while parsing JSON data, resetting it to the default value.", err);
881
+ this.events.emit("error", error);
686
882
  await this.saveDefaultData();
687
883
  return this.defaultData;
688
884
  }
@@ -701,27 +897,47 @@ var DataStore = class {
701
897
  //#region setData
702
898
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
703
899
  setData(data) {
704
- if (this.memoryCache)
900
+ const dataCopy = this.engine.deepCopy(data);
901
+ if (this.memoryCache) {
705
902
  this.cachedData = data;
903
+ this.events.emit("updateDataSync", dataCopy);
904
+ }
706
905
  return new Promise(async (resolve) => {
707
- await Promise.allSettled([
906
+ const results = await Promise.allSettled([
708
907
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
709
908
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
710
909
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
711
910
  ]);
911
+ if (results.every((r) => r.status === "fulfilled"))
912
+ this.events.emit("updateData", dataCopy);
913
+ else {
914
+ const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
915
+ console.error(error);
916
+ this.events.emit("error", error);
917
+ }
712
918
  resolve();
713
919
  });
714
920
  }
715
921
  //#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() {
922
+ /**
923
+ * Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
924
+ * @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
925
+ */
926
+ async saveDefaultData(emitEvent = true) {
718
927
  if (this.memoryCache)
719
928
  this.cachedData = this.defaultData;
720
- await Promise.allSettled([
929
+ const results = await Promise.allSettled([
721
930
  this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
722
931
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
723
932
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
724
933
  ]);
934
+ if (results.every((r) => r.status === "fulfilled"))
935
+ emitEvent && this.events.emit("setDefaultData", this.defaultData);
936
+ else {
937
+ const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
938
+ console.error(error);
939
+ this.events.emit("error", error);
940
+ }
725
941
  }
726
942
  //#region deleteData
727
943
  /**
@@ -737,6 +953,7 @@ var DataStore = class {
737
953
  this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
738
954
  ]);
739
955
  await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
956
+ this.events.emit("deleteData");
740
957
  }
741
958
  //#region encodingEnabled
742
959
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
@@ -757,16 +974,22 @@ var DataStore = class {
757
974
  let newData = oldData;
758
975
  const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
759
976
  let lastFmtVer = oldFmtVer;
760
- for (const [fmtVer, migrationFunc] of sortedMigrations) {
977
+ for (let i = 0; i < sortedMigrations.length; i++) {
978
+ const [fmtVer, migrationFunc] = sortedMigrations[i];
761
979
  const ver = Number(fmtVer);
762
980
  if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
763
981
  try {
764
982
  const migRes = migrationFunc(newData);
765
983
  newData = migRes instanceof Promise ? await migRes : migRes;
766
984
  lastFmtVer = oldFmtVer = ver;
985
+ const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
986
+ this.events.emit("migrateData", ver, newData, isFinal);
767
987
  } catch (err) {
988
+ const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
989
+ this.events.emit("migrationError", ver, migError);
990
+ this.events.emit("error", migError);
768
991
  if (!resetOnError)
769
- throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
992
+ throw migError;
770
993
  await this.saveDefaultData();
771
994
  return this.engine.deepCopy(this.defaultData);
772
995
  }
@@ -777,10 +1000,9 @@ var DataStore = class {
777
1000
  this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
778
1001
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
779
1002
  ]);
780
- if (this.memoryCache)
781
- return this.cachedData = this.engine.deepCopy(newData);
782
- else
783
- return this.engine.deepCopy(newData);
1003
+ const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
1004
+ this.events.emit("updateData", result);
1005
+ return result;
784
1006
  }
785
1007
  //#region migrateId
786
1008
  /**
@@ -809,6 +1031,7 @@ var DataStore = class {
809
1031
  this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
810
1032
  this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
811
1033
  ]);
1034
+ this.events.emit("migrateId", id, this.id);
812
1035
  }));
813
1036
  }
814
1037
  };
@@ -1177,205 +1400,6 @@ Has: ${checksum}`);
1177
1400
  }
1178
1401
  };
1179
1402
 
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
1403
  // lib/Debouncer.ts
1380
1404
  var Debouncer = class extends NanoEmitter {
1381
1405
  /**