@sv443-network/coreutils 3.1.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.
@@ -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;
@@ -566,6 +765,7 @@ var DataStore = class {
566
765
  compressionFormat = "deflate-raw";
567
766
  memoryCache;
568
767
  engine;
768
+ keyPrefix;
569
769
  options;
570
770
  /**
571
771
  * Whether all first-init checks should be done.
@@ -588,6 +788,7 @@ var DataStore = class {
588
788
  * @param opts The options for this DataStore instance
589
789
  */
590
790
  constructor(opts) {
791
+ super(opts.nanoEmitterOptions);
591
792
  this.id = opts.id;
592
793
  this.formatVersion = opts.formatVersion;
593
794
  this.defaultData = opts.defaultData;
@@ -597,6 +798,7 @@ var DataStore = class {
597
798
  if (opts.migrateIds)
598
799
  this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
599
800
  this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
801
+ this.keyPrefix = opts.keyPrefix ?? "__ds-";
600
802
  this.options = opts;
601
803
  if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
602
804
  this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
@@ -638,13 +840,13 @@ var DataStore = class {
638
840
  promises.push(this.engine.setValue(newKey, value));
639
841
  promises.push(this.engine.deleteValue(oldKey));
640
842
  };
641
- migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
843
+ migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
642
844
  if (!isNaN(oldVer))
643
- migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
845
+ migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
644
846
  if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
645
- migrateFmt(`_uucfgenc-${this.id}`, `__ds-${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? this.compressionFormat ?? null : null);
847
+ migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? this.compressionFormat ?? null : null);
646
848
  else {
647
- promises.push(this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat));
849
+ promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
648
850
  promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
649
851
  }
650
852
  await Promise.allSettled(promises);
@@ -656,31 +858,27 @@ var DataStore = class {
656
858
  await this.migrateId(this.migrateIds);
657
859
  this.migrateIds = [];
658
860
  }
659
- const storedDataRaw = await this.engine.getValue(`__ds-${this.id}-dat`, null);
660
- let storedFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
861
+ const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
862
+ const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
661
863
  if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
662
- await this.saveDefaultData();
663
- 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;
664
868
  }
665
869
  const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
666
- const encodingFmt = String(await this.engine.getValue(`__ds-${this.id}-enf`, null));
870
+ const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
667
871
  const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
668
- let saveData = false;
669
- if (isNaN(storedFmtVer)) {
670
- await this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
671
- saveData = true;
672
- }
673
872
  let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
674
873
  if (storedFmtVer < this.formatVersion && this.migrations)
675
874
  parsed = await this.runMigrations(parsed, storedFmtVer);
676
- if (saveData)
677
- await this.setData(parsed);
678
- if (this.memoryCache)
679
- return this.cachedData = this.engine.deepCopy(parsed);
680
- else
681
- 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;
682
878
  } catch (err) {
879
+ const error = err instanceof Error ? err : new Error(String(err));
683
880
  console.warn("Error while parsing JSON data, resetting it to the default value.", err);
881
+ this.events.emit("error", error);
684
882
  await this.saveDefaultData();
685
883
  return this.defaultData;
686
884
  }
@@ -699,27 +897,47 @@ var DataStore = class {
699
897
  //#region setData
700
898
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
701
899
  setData(data) {
702
- if (this.memoryCache)
900
+ const dataCopy = this.engine.deepCopy(data);
901
+ if (this.memoryCache) {
703
902
  this.cachedData = data;
903
+ this.events.emit("updateDataSync", dataCopy);
904
+ }
704
905
  return new Promise(async (resolve) => {
705
- await Promise.allSettled([
706
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
707
- this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
708
- this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
906
+ const results = await Promise.allSettled([
907
+ this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
908
+ this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
909
+ this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
709
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
+ }
710
918
  resolve();
711
919
  });
712
920
  }
713
921
  //#region saveDefaultData
714
- /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
715
- 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) {
716
927
  if (this.memoryCache)
717
928
  this.cachedData = this.defaultData;
718
- await Promise.allSettled([
719
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
720
- this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
721
- this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
929
+ const results = await Promise.allSettled([
930
+ this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
931
+ this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
932
+ this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
722
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
+ }
723
941
  }
724
942
  //#region deleteData
725
943
  /**
@@ -730,11 +948,12 @@ var DataStore = class {
730
948
  async deleteData() {
731
949
  var _a, _b;
732
950
  await Promise.allSettled([
733
- this.engine.deleteValue(`__ds-${this.id}-dat`),
734
- this.engine.deleteValue(`__ds-${this.id}-ver`),
735
- this.engine.deleteValue(`__ds-${this.id}-enf`)
951
+ this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
952
+ this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
953
+ this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
736
954
  ]);
737
955
  await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
956
+ this.events.emit("deleteData");
738
957
  }
739
958
  //#region encodingEnabled
740
959
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
@@ -755,30 +974,35 @@ var DataStore = class {
755
974
  let newData = oldData;
756
975
  const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
757
976
  let lastFmtVer = oldFmtVer;
758
- for (const [fmtVer, migrationFunc] of sortedMigrations) {
977
+ for (let i = 0; i < sortedMigrations.length; i++) {
978
+ const [fmtVer, migrationFunc] = sortedMigrations[i];
759
979
  const ver = Number(fmtVer);
760
980
  if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
761
981
  try {
762
982
  const migRes = migrationFunc(newData);
763
983
  newData = migRes instanceof Promise ? await migRes : migRes;
764
984
  lastFmtVer = oldFmtVer = ver;
985
+ const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
986
+ this.events.emit("migrateData", ver, newData, isFinal);
765
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);
766
991
  if (!resetOnError)
767
- throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
992
+ throw migError;
768
993
  await this.saveDefaultData();
769
994
  return this.engine.deepCopy(this.defaultData);
770
995
  }
771
996
  }
772
997
  }
773
998
  await Promise.allSettled([
774
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
775
- this.engine.setValue(`__ds-${this.id}-ver`, lastFmtVer),
776
- this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
999
+ this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
1000
+ this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
1001
+ this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
777
1002
  ]);
778
- if (this.memoryCache)
779
- return this.cachedData = this.engine.deepCopy(newData);
780
- else
781
- 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;
782
1006
  }
783
1007
  //#region migrateId
784
1008
  /**
@@ -790,9 +1014,9 @@ var DataStore = class {
790
1014
  await Promise.all(ids.map(async (id) => {
791
1015
  const [data, fmtVer, isEncoded] = await (async () => {
792
1016
  const [d, f, e] = await Promise.all([
793
- this.engine.getValue(`__ds-${id}-dat`, JSON.stringify(this.defaultData)),
794
- this.engine.getValue(`__ds-${id}-ver`, NaN),
795
- this.engine.getValue(`__ds-${id}-enf`, null)
1017
+ this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
1018
+ this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
1019
+ this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
796
1020
  ]);
797
1021
  return [d, Number(f), Boolean(e) && String(e) !== "null"];
798
1022
  })();
@@ -800,13 +1024,14 @@ var DataStore = class {
800
1024
  return;
801
1025
  const parsed = await this.engine.deserializeData(data, isEncoded);
802
1026
  await Promise.allSettled([
803
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
804
- this.engine.setValue(`__ds-${this.id}-ver`, fmtVer),
805
- this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat),
806
- this.engine.deleteValue(`__ds-${id}-dat`),
807
- this.engine.deleteValue(`__ds-${id}-ver`),
808
- this.engine.deleteValue(`__ds-${id}-enf`)
1027
+ this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
1028
+ this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
1029
+ this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
1030
+ this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
1031
+ this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
1032
+ this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
809
1033
  ]);
1034
+ this.events.emit("migrateId", id, this.id);
810
1035
  }));
811
1036
  }
812
1037
  };
@@ -1175,205 +1400,6 @@ Has: ${checksum}`);
1175
1400
  }
1176
1401
  };
1177
1402
 
1178
- // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
1179
- var createNanoEvents = () => ({
1180
- emit(event, ...args) {
1181
- for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
1182
- callbacks[i](...args);
1183
- }
1184
- },
1185
- events: {},
1186
- on(event, cb) {
1187
- ;
1188
- (this.events[event] ||= []).push(cb);
1189
- return () => {
1190
- var _a;
1191
- this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
1192
- };
1193
- }
1194
- });
1195
-
1196
- // lib/NanoEmitter.ts
1197
- var NanoEmitter = class {
1198
- events = createNanoEvents();
1199
- eventUnsubscribes = [];
1200
- emitterOptions;
1201
- /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
1202
- constructor(options = {}) {
1203
- this.emitterOptions = {
1204
- publicEmit: false,
1205
- ...options
1206
- };
1207
- }
1208
- //#region on
1209
- /**
1210
- * Subscribes to an event and calls the callback when it's emitted.
1211
- * @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 "_")
1212
- * @returns Returns a function that can be called to unsubscribe the event listener
1213
- * @example ```ts
1214
- * const emitter = new NanoEmitter<{
1215
- * foo: (bar: string) => void;
1216
- * }>({
1217
- * publicEmit: true,
1218
- * });
1219
- *
1220
- * let i = 0;
1221
- * const unsub = emitter.on("foo", (bar) => {
1222
- * // unsubscribe after 10 events:
1223
- * if(++i === 10) unsub();
1224
- * console.log(bar);
1225
- * });
1226
- *
1227
- * emitter.emit("foo", "bar");
1228
- * ```
1229
- */
1230
- on(event, cb) {
1231
- let unsub;
1232
- const unsubProxy = () => {
1233
- if (!unsub)
1234
- return;
1235
- unsub();
1236
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
1237
- };
1238
- unsub = this.events.on(event, cb);
1239
- this.eventUnsubscribes.push(unsub);
1240
- return unsubProxy;
1241
- }
1242
- //#region once
1243
- /**
1244
- * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
1245
- * @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 "_")
1246
- * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
1247
- * @returns Returns a Promise that resolves with the event arguments when the event is emitted
1248
- * @example ```ts
1249
- * const emitter = new NanoEmitter<{
1250
- * foo: (bar: string) => void;
1251
- * }>();
1252
- *
1253
- * // Promise syntax:
1254
- * const [bar] = await emitter.once("foo");
1255
- * console.log(bar);
1256
- *
1257
- * // Callback syntax:
1258
- * emitter.once("foo", (bar) => console.log(bar));
1259
- * ```
1260
- */
1261
- once(event, cb) {
1262
- return new Promise((resolve) => {
1263
- let unsub;
1264
- const onceProxy = ((...args) => {
1265
- cb == null ? void 0 : cb(...args);
1266
- unsub == null ? void 0 : unsub();
1267
- resolve(args);
1268
- });
1269
- unsub = this.events.on(event, onceProxy);
1270
- this.eventUnsubscribes.push(unsub);
1271
- });
1272
- }
1273
- //#region onMulti
1274
- /**
1275
- * 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.
1276
- * @param options An object or array of objects with the following properties:
1277
- * `callback` (required) is the function that will be called when the conditions are met.
1278
- *
1279
- * 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.
1280
- * If `signal` is provided, the subscription will be canceled when the given signal is aborted.
1281
- *
1282
- * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
1283
- * 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.
1284
- * 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.
1285
- * At least one of `oneOf` or `allOf` must be provided.
1286
- *
1287
- * @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.
1288
- */
1289
- onMulti(options) {
1290
- const allUnsubs = [];
1291
- const unsubAll = () => {
1292
- for (const unsub of allUnsubs)
1293
- unsub();
1294
- allUnsubs.splice(0, allUnsubs.length);
1295
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
1296
- };
1297
- for (const opts of Array.isArray(options) ? options : [options]) {
1298
- const optsWithDefaults = {
1299
- allOf: [],
1300
- oneOf: [],
1301
- once: false,
1302
- ...opts
1303
- };
1304
- const {
1305
- oneOf,
1306
- allOf,
1307
- once,
1308
- signal,
1309
- callback
1310
- } = optsWithDefaults;
1311
- if (signal == null ? void 0 : signal.aborted)
1312
- return unsubAll;
1313
- if (oneOf.length === 0 && allOf.length === 0)
1314
- throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
1315
- const curEvtUnsubs = [];
1316
- const checkUnsubAllEvt = (force = false) => {
1317
- if (!(signal == null ? void 0 : signal.aborted) && !force)
1318
- return;
1319
- for (const unsub of curEvtUnsubs)
1320
- unsub();
1321
- curEvtUnsubs.splice(0, curEvtUnsubs.length);
1322
- this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
1323
- };
1324
- const allOfEmitted = /* @__PURE__ */ new Set();
1325
- const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
1326
- for (const event of oneOf) {
1327
- const unsub = this.events.on(event, ((...args) => {
1328
- checkUnsubAllEvt();
1329
- if (allOfConditionMet()) {
1330
- callback(event, ...args);
1331
- if (once)
1332
- checkUnsubAllEvt(true);
1333
- }
1334
- }));
1335
- curEvtUnsubs.push(unsub);
1336
- }
1337
- for (const event of allOf) {
1338
- const unsub = this.events.on(event, ((...args) => {
1339
- checkUnsubAllEvt();
1340
- allOfEmitted.add(event);
1341
- if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
1342
- callback(event, ...args);
1343
- if (once)
1344
- checkUnsubAllEvt(true);
1345
- }
1346
- }));
1347
- curEvtUnsubs.push(unsub);
1348
- }
1349
- allUnsubs.push(() => checkUnsubAllEvt(true));
1350
- }
1351
- return unsubAll;
1352
- }
1353
- //#region emit
1354
- /**
1355
- * Emits an event on this instance.
1356
- * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1357
- * @param event The event to emit
1358
- * @param args The arguments to pass to the event listeners
1359
- * @returns Returns true if `publicEmit` is true and the event was emitted successfully
1360
- */
1361
- emit(event, ...args) {
1362
- if (this.emitterOptions.publicEmit) {
1363
- this.events.emit(event, ...args);
1364
- return true;
1365
- }
1366
- return false;
1367
- }
1368
- //#region unsubscribeAll
1369
- /** Unsubscribes all event listeners from this instance */
1370
- unsubscribeAll() {
1371
- for (const unsub of this.eventUnsubscribes)
1372
- unsub();
1373
- this.eventUnsubscribes = [];
1374
- }
1375
- };
1376
-
1377
1403
  // lib/Debouncer.ts
1378
1404
  var Debouncer = class extends NanoEmitter {
1379
1405
  /**