@sv443-network/coreutils 1.0.0 → 2.0.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.
@@ -41,6 +41,19 @@ function mapRange(value, range1min, range1max, range2min, range2max) {
41
41
  return value * (range2max / range1max);
42
42
  return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
43
43
  }
44
+ function overflowVal(value, minOrMax, max) {
45
+ const min = typeof max === "number" ? minOrMax : 0;
46
+ max = typeof max === "number" ? max : minOrMax;
47
+ if (min > max)
48
+ throw new RangeError(`Parameter "min" can't be bigger than "max"`);
49
+ if (isNaN(value) || isNaN(min) || isNaN(max) || !isFinite(value) || !isFinite(min) || !isFinite(max))
50
+ return NaN;
51
+ if (value >= min && value <= max)
52
+ return value;
53
+ const range = max - min + 1;
54
+ const wrappedValue = ((value - min) % range + range) % range + min;
55
+ return wrappedValue;
56
+ }
44
57
  function randRange(...args) {
45
58
  let min, max, enhancedEntropy = false;
46
59
  if (typeof args[0] === "number" && typeof args[1] === "number")
@@ -136,10 +149,8 @@ function darkenColor(color, percent, upperCase = false) {
136
149
  return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
137
150
  else if (color.startsWith("rgba"))
138
151
  return `rgba(${r}, ${g}, ${b}, ${a ?? NaN})`;
139
- else if (color.startsWith("rgb"))
140
- return `rgb(${r}, ${g}, ${b})`;
141
152
  else
142
- throw new TypeError("Unsupported color format");
153
+ return `rgb(${r}, ${g}, ${b})`;
143
154
  }
144
155
  function hexToRgb(hex) {
145
156
  hex = (hex.startsWith("#") ? hex.slice(1) : hex).trim();
@@ -172,22 +183,22 @@ function atoab(str) {
172
183
  return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
173
184
  }
174
185
  async function compress(input, compressionFormat, outputType = "string") {
175
- const byteArray = input instanceof ArrayBuffer ? input : new TextEncoder().encode((input == null ? void 0 : input.toString()) ?? String(input));
186
+ const byteArray = input instanceof Uint8Array ? input : new TextEncoder().encode((input == null ? void 0 : input.toString()) ?? String(input));
176
187
  const comp = new CompressionStream(compressionFormat);
177
188
  const writer = comp.writable.getWriter();
178
189
  writer.write(byteArray);
179
190
  writer.close();
180
- const buf = await new Response(comp.readable).arrayBuffer();
181
- return outputType === "arrayBuffer" ? buf : abtoa(buf);
191
+ const uintArr = new Uint8Array(await new Response(comp.readable).arrayBuffer());
192
+ return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
182
193
  }
183
194
  async function decompress(input, compressionFormat, outputType = "string") {
184
- const byteArray = input instanceof ArrayBuffer ? input : atoab((input == null ? void 0 : input.toString()) ?? String(input));
195
+ const byteArray = input instanceof Uint8Array ? input : atoab((input == null ? void 0 : input.toString()) ?? String(input));
185
196
  const decomp = new DecompressionStream(compressionFormat);
186
197
  const writer = decomp.writable.getWriter();
187
198
  writer.write(byteArray);
188
199
  writer.close();
189
- const buf = await new Response(decomp.readable).arrayBuffer();
190
- return outputType === "arrayBuffer" ? buf : new TextDecoder().decode(buf);
200
+ const uintArr = new Uint8Array(await new Response(decomp.readable).arrayBuffer());
201
+ return outputType === "arrayBuffer" ? uintArr : new TextDecoder().decode(uintArr);
191
202
  }
192
203
  async function computeHash(input, algorithm = "SHA-256") {
193
204
  let data;
@@ -433,7 +444,14 @@ var DataStore = class {
433
444
  decodeData;
434
445
  compressionFormat = "deflate-raw";
435
446
  engine;
447
+ options;
448
+ /**
449
+ * Whether all first-init checks should be done.
450
+ * This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
451
+ * This is set to `true` by default. Create a subclass and set it to `false` before calling {@linkcode loadData()} if you want to explicitly skip these checks.
452
+ */
436
453
  firstInit = true;
454
+ /** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
437
455
  cachedData;
438
456
  migrations;
439
457
  migrateIds = [];
@@ -441,13 +459,13 @@ var DataStore = class {
441
459
  * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
442
460
  * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
443
461
  *
444
- * - ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
445
462
  * - ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
446
463
  *
447
464
  * @template TData The type of the data that is saved in persistent storage for the currently set format version (will be automatically inferred from `defaultData` if not provided) - **This has to be a JSON-compatible object!** (no undefined, circular references, etc.)
448
465
  * @param opts The options for this DataStore instance
449
466
  */
450
467
  constructor(opts) {
468
+ var _a;
451
469
  this.id = opts.id;
452
470
  this.formatVersion = opts.formatVersion;
453
471
  this.defaultData = opts.defaultData;
@@ -458,8 +476,9 @@ var DataStore = class {
458
476
  this.encodeData = opts.encodeData;
459
477
  this.decodeData = opts.decodeData;
460
478
  this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
479
+ this.options = opts;
461
480
  if (typeof opts.compressionFormat === "undefined")
462
- opts.compressionFormat = "deflate-raw";
481
+ opts.compressionFormat = ((_a = opts.encodeData) == null ? void 0 : _a[0]) ?? "deflate-raw";
463
482
  if (typeof opts.compressionFormat === "string") {
464
483
  this.encodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
465
484
  this.decodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
@@ -494,9 +513,14 @@ var DataStore = class {
494
513
  promises.push(this.engine.setValue(newKey, value));
495
514
  promises.push(this.engine.deleteValue(oldKey));
496
515
  };
497
- oldData && migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
498
- !isNaN(oldVer) && migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
499
- typeof oldEnc === "boolean" && migrateFmt(`_uucfgenc-${this.id}`, `__ds-${this.id}-enc`, oldEnc === true ? this.compressionFormat : null);
516
+ if (oldData)
517
+ migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
518
+ if (!isNaN(oldVer))
519
+ migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
520
+ if (typeof oldEnc === "boolean")
521
+ migrateFmt(`_uucfgenc-${this.id}`, `__ds-${this.id}-enf`, oldEnc === true ? Boolean(this.compressionFormat) || null : null);
522
+ else
523
+ promises.push(this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat));
500
524
  await Promise.allSettled(promises);
501
525
  }
502
526
  await this.engine.setValue("__ds_fmt_ver", dsFmtVer);
@@ -506,21 +530,22 @@ var DataStore = class {
506
530
  await this.migrateId(this.migrateIds);
507
531
  this.migrateIds = [];
508
532
  }
509
- const gmData = await this.engine.getValue(`__ds-${this.id}-dat`, JSON.stringify(this.defaultData));
510
- let gmFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
511
- if (typeof gmData !== "string") {
533
+ const storedData = await this.engine.getValue(`__ds-${this.id}-dat`, JSON.stringify(this.defaultData));
534
+ let storedFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
535
+ if (typeof storedData !== "string") {
512
536
  await this.saveDefaultData();
513
537
  return { ...this.defaultData };
514
538
  }
515
- const isEncoded = Boolean(await this.engine.getValue(`__ds-${this.id}-enc`, false));
539
+ const encodingFmt = String(await this.engine.getValue(`__ds-${this.id}-enf`, null));
540
+ const isEncoded = encodingFmt !== "null" && encodingFmt !== "false";
516
541
  let saveData = false;
517
- if (isNaN(gmFmtVer)) {
518
- await this.engine.setValue(`__ds-${this.id}-ver`, gmFmtVer = this.formatVersion);
542
+ if (isNaN(storedFmtVer)) {
543
+ await this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
519
544
  saveData = true;
520
545
  }
521
- let parsed = await this.engine.deserializeData(gmData, isEncoded);
522
- if (gmFmtVer < this.formatVersion && this.migrations)
523
- parsed = await this.runMigrations(parsed, gmFmtVer);
546
+ let parsed = await this.engine.deserializeData(storedData, isEncoded);
547
+ if (storedFmtVer < this.formatVersion && this.migrations)
548
+ parsed = await this.runMigrations(parsed, storedFmtVer);
524
549
  if (saveData)
525
550
  await this.setData(parsed);
526
551
  return this.cachedData = this.engine.deepCopy(parsed);
@@ -540,12 +565,11 @@ var DataStore = class {
540
565
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
541
566
  setData(data) {
542
567
  this.cachedData = data;
543
- const useEncoding = this.encodingEnabled();
544
568
  return new Promise(async (resolve) => {
545
569
  await Promise.allSettled([
546
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data, useEncoding)),
570
+ this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
547
571
  this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
548
- this.engine.setValue(`__ds-${this.id}-enc`, useEncoding)
572
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
549
573
  ]);
550
574
  resolve();
551
575
  });
@@ -553,30 +577,29 @@ var DataStore = class {
553
577
  /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
554
578
  async saveDefaultData() {
555
579
  this.cachedData = this.defaultData;
556
- const useEncoding = this.encodingEnabled();
557
580
  await Promise.allSettled([
558
- this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData, useEncoding)),
581
+ this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
559
582
  this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
560
- this.engine.setValue(`__ds-${this.id}-enc`, useEncoding)
583
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
561
584
  ]);
562
585
  }
563
586
  /**
564
- * Call this method to clear all persistently stored data associated with this DataStore instance.
587
+ * Call this method to clear all persistently stored data associated with this DataStore instance, including the storage container (if supported by the DataStoreEngine).
565
588
  * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
566
- * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
567
- *
568
- * - ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"`
589
+ * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
569
590
  */
570
591
  async deleteData() {
592
+ var _a, _b;
571
593
  await Promise.allSettled([
572
594
  this.engine.deleteValue(`__ds-${this.id}-dat`),
573
595
  this.engine.deleteValue(`__ds-${this.id}-ver`),
574
- this.engine.deleteValue(`__ds-${this.id}-enc`)
596
+ this.engine.deleteValue(`__ds-${this.id}-enf`)
575
597
  ]);
598
+ await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
576
599
  }
577
600
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
578
601
  encodingEnabled() {
579
- return Boolean(this.encodeData && this.decodeData);
602
+ return Boolean(this.encodeData && this.decodeData) && this.compressionFormat !== null || Boolean(this.compressionFormat);
580
603
  }
581
604
  //#region migrations
582
605
  /**
@@ -610,7 +633,7 @@ var DataStore = class {
610
633
  await Promise.allSettled([
611
634
  this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(newData)),
612
635
  this.engine.setValue(`__ds-${this.id}-ver`, lastFmtVer),
613
- this.engine.setValue(`__ds-${this.id}-enc`, this.encodingEnabled())
636
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
614
637
  ]);
615
638
  return this.cachedData = { ...newData };
616
639
  }
@@ -621,19 +644,24 @@ var DataStore = class {
621
644
  async migrateId(oldIds) {
622
645
  const ids = Array.isArray(oldIds) ? oldIds : [oldIds];
623
646
  await Promise.all(ids.map(async (id) => {
624
- const data = await this.engine.getValue(`__ds-${id}-dat`, JSON.stringify(this.defaultData));
625
- const fmtVer = Number(await this.engine.getValue(`__ds-${id}-ver`, NaN));
626
- const isEncoded = Boolean(await this.engine.getValue(`__ds-${id}-enc`, false));
647
+ const [data, fmtVer, isEncoded] = await (async () => {
648
+ const [d, f, e] = await Promise.all([
649
+ this.engine.getValue(`__ds-${id}-dat`, JSON.stringify(this.defaultData)),
650
+ this.engine.getValue(`__ds-${id}-ver`, NaN),
651
+ this.engine.getValue(`__ds-${id}-enf`, null)
652
+ ]);
653
+ return [d, Number(f), Boolean(e) && String(e) !== "null"];
654
+ })();
627
655
  if (data === void 0 || isNaN(fmtVer))
628
656
  return;
629
657
  const parsed = await this.engine.deserializeData(data, isEncoded);
630
658
  await Promise.allSettled([
631
659
  this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(parsed)),
632
660
  this.engine.setValue(`__ds-${this.id}-ver`, fmtVer),
633
- this.engine.setValue(`__ds-${this.id}-enc`, isEncoded),
661
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat),
634
662
  this.engine.deleteValue(`__ds-${id}-dat`),
635
663
  this.engine.deleteValue(`__ds-${id}-ver`),
636
- this.engine.deleteValue(`__ds-${id}-enc`)
664
+ this.engine.deleteValue(`__ds-${id}-enf`)
637
665
  ]);
638
666
  }));
639
667
  }
@@ -643,7 +671,11 @@ var DataStore = class {
643
671
  var DataStoreEngine = class {
644
672
  dataStoreOptions;
645
673
  // setDataStoreOptions() is called from inside the DataStore constructor to set this value
646
- /** Called by DataStore on creation, to pass its options */
674
+ constructor(options) {
675
+ if (options)
676
+ this.dataStoreOptions = options;
677
+ }
678
+ /** Called by DataStore on creation, to pass its options. Only call this if you are using this instance standalone! */
647
679
  setDataStoreOptions(dataStoreOptions) {
648
680
  this.dataStoreOptions = dataStoreOptions;
649
681
  }
@@ -651,6 +683,7 @@ var DataStoreEngine = class {
651
683
  /** Serializes the given object to a string, optionally encoded with `options.encodeData` if {@linkcode useEncoding} is set to true */
652
684
  async serializeData(data, useEncoding) {
653
685
  var _a, _b, _c, _d, _e;
686
+ this.ensureDataStoreOptions();
654
687
  const stringData = JSON.stringify(data);
655
688
  if (!useEncoding || !((_a = this.dataStoreOptions) == null ? void 0 : _a.encodeData) || !((_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData))
656
689
  return stringData;
@@ -662,12 +695,20 @@ var DataStoreEngine = class {
662
695
  /** Deserializes the given string to a JSON object, optionally decoded with `options.decodeData` if {@linkcode useEncoding} is set to true */
663
696
  async deserializeData(data, useEncoding) {
664
697
  var _a, _b, _c;
698
+ this.ensureDataStoreOptions();
665
699
  let decRes = ((_a = this.dataStoreOptions) == null ? void 0 : _a.decodeData) && useEncoding ? (_c = (_b = this.dataStoreOptions.decodeData) == null ? void 0 : _b[1]) == null ? void 0 : _c.call(_b, data) : void 0;
666
700
  if (decRes instanceof Promise)
667
701
  decRes = await decRes;
668
702
  return JSON.parse(decRes ?? data);
669
703
  }
670
704
  //#region misc api
705
+ /** Throws an error if the DataStoreOptions are not set or invalid */
706
+ ensureDataStoreOptions() {
707
+ if (!this.dataStoreOptions)
708
+ throw new DatedError("DataStoreEngine must be initialized with DataStore options before use. If you are using this instance standalone, set them in the constructor or call `setDataStoreOptions()` with the DataStore options.");
709
+ if (!this.dataStoreOptions.id)
710
+ throw new DatedError("DataStoreEngine must be initialized with a valid DataStore ID");
711
+ }
671
712
  /**
672
713
  * Copies a JSON-compatible object and loses all its internal references in the process.
673
714
  * Uses [`structuredClone()`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) if available, otherwise falls back to `JSON.parse(JSON.stringify(obj))`.
@@ -686,11 +727,11 @@ var BrowserStorageEngine = class extends DataStoreEngine {
686
727
  /**
687
728
  * Creates an instance of `BrowserStorageEngine`.
688
729
  *
689
- * ⚠️ Requires a DOM environment
690
- * ⚠️ Don't reuse this engine across multiple {@linkcode DataStore} instances
730
+ * - ⚠️ Requires a DOM environment
731
+ * - ⚠️ Don't reuse engines across multiple {@linkcode DataStore} instances
691
732
  */
692
733
  constructor(options) {
693
- super();
734
+ super(options == null ? void 0 : options.dataStoreOptions);
694
735
  this.options = {
695
736
  type: "localStorage",
696
737
  ...options
@@ -722,11 +763,11 @@ var FileStorageEngine = class extends DataStoreEngine {
722
763
  /**
723
764
  * Creates an instance of `FileStorageEngine`.
724
765
  *
725
- * ⚠️ Requires Node.js or Deno with Node compatibility (v1.31+)
726
- * ⚠️ Don't reuse this engine across multiple {@linkcode DataStore} instances
766
+ * - ⚠️ Requires Node.js or Deno with Node compatibility (v1.31+)
767
+ * - ⚠️ Don't reuse engines across multiple {@linkcode DataStore} instances
727
768
  */
728
769
  constructor(options) {
729
- super();
770
+ super(options == null ? void 0 : options.dataStoreOptions);
730
771
  this.options = {
731
772
  filePath: (id) => `.ds-${id}`,
732
773
  ...options
@@ -735,30 +776,32 @@ var FileStorageEngine = class extends DataStoreEngine {
735
776
  //#region json file
736
777
  /** Reads the file contents */
737
778
  async readFile() {
738
- var _a, _b, _c;
779
+ var _a, _b, _c, _d;
780
+ this.ensureDataStoreOptions();
739
781
  try {
740
782
  if (!fs)
741
- fs = (await import("fs/promises")).default;
783
+ fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
742
784
  if (!fs)
743
785
  throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
744
786
  const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
745
787
  const data = await fs.readFile(path, "utf-8");
746
- return data ? JSON.parse(await ((_c = (_b = (_a = this.dataStoreOptions) == null ? void 0 : _a.decodeData) == null ? void 0 : _b[1]) == null ? void 0 : _c.call(_b, data)) ?? data) : void 0;
788
+ return data ? JSON.parse(await ((_d = (_c = (_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData) == null ? void 0 : _c[1]) == null ? void 0 : _d.call(_c, data)) ?? data) : void 0;
747
789
  } catch {
748
790
  return void 0;
749
791
  }
750
792
  }
751
793
  /** Overwrites the file contents */
752
794
  async writeFile(data) {
753
- var _a, _b, _c;
795
+ var _a, _b, _c, _d;
796
+ this.ensureDataStoreOptions();
754
797
  try {
755
798
  if (!fs)
756
- fs = (await import("fs/promises")).default;
799
+ fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
757
800
  if (!fs)
758
801
  throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
759
802
  const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
760
803
  await fs.mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
761
- await fs.writeFile(path, await ((_c = (_b = (_a = this.dataStoreOptions) == null ? void 0 : _a.encodeData) == null ? void 0 : _b[1]) == null ? void 0 : _c.call(_b, JSON.stringify(data))) ?? JSON.stringify(data, void 0, 2), "utf-8");
804
+ await fs.writeFile(path, await ((_d = (_c = (_b = this.dataStoreOptions) == null ? void 0 : _b.encodeData) == null ? void 0 : _c[1]) == null ? void 0 : _d.call(_c, JSON.stringify(data))) ?? JSON.stringify(data, void 0, 2), "utf-8");
762
805
  } catch (err) {
763
806
  console.error("Error writing file:", err);
764
807
  }
@@ -792,6 +835,21 @@ var FileStorageEngine = class extends DataStoreEngine {
792
835
  delete data[name];
793
836
  await this.writeFile(data);
794
837
  }
838
+ /** Deletes the file that contains the data of this DataStore. */
839
+ async deleteStorage() {
840
+ var _a;
841
+ this.ensureDataStoreOptions();
842
+ try {
843
+ if (!fs)
844
+ fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
845
+ if (!fs)
846
+ throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
847
+ const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
848
+ await fs.unlink(path);
849
+ } catch (err) {
850
+ console.error("Error deleting file:", err);
851
+ }
852
+ }
795
853
  };
796
854
 
797
855
  // lib/DataStoreSerializer.ts
@@ -819,14 +877,16 @@ var DataStoreSerializer = class _DataStoreSerializer {
819
877
  * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
820
878
  */
821
879
  async serializePartial(stores, useEncoding = true, stringified = true) {
880
+ var _a;
822
881
  const serData = [];
823
882
  for (const storeInst of this.stores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id))) {
824
- const data = useEncoding && storeInst.encodingEnabled() ? await storeInst.encodeData[1](JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
883
+ const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
884
+ const data = encoded ? await storeInst.encodeData[1](JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
825
885
  serData.push({
826
886
  id: storeInst.id,
827
887
  data,
828
888
  formatVersion: storeInst.formatVersion,
829
- encoded: useEncoding && storeInst.encodingEnabled(),
889
+ encoded,
830
890
  checksum: this.options.addChecksum ? await this.calcChecksum(data) : void 0
831
891
  });
832
892
  }
@@ -950,6 +1010,7 @@ var NanoEmitter = class {
950
1010
  ...options
951
1011
  };
952
1012
  }
1013
+ //#region on
953
1014
  /**
954
1015
  * Subscribes to an event and calls the callback when it's emitted.
955
1016
  * @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 "_")
@@ -983,6 +1044,7 @@ var NanoEmitter = class {
983
1044
  this.eventUnsubscribes.push(unsub);
984
1045
  return unsubProxy;
985
1046
  }
1047
+ //#region once
986
1048
  /**
987
1049
  * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
988
1050
  * @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 "_")
@@ -1013,9 +1075,90 @@ var NanoEmitter = class {
1013
1075
  this.eventUnsubscribes.push(unsub);
1014
1076
  });
1015
1077
  }
1078
+ //#region onMulti
1079
+ /**
1080
+ * 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.
1081
+ * @param options An object or array of objects with the following properties:
1082
+ * `callback` (required) is the function that will be called when the conditions are met.
1083
+ *
1084
+ * 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.
1085
+ * If `signal` is provided, the subscription will be aborted when the given signal is aborted.
1086
+ *
1087
+ * If `oneOf` is used, the callback will be called when any of the matching events are emitted.
1088
+ * 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.
1089
+ * You may use a combination of the above two options, but at least one of them must be provided.
1090
+ *
1091
+ * @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.
1092
+ */
1093
+ onMulti(options) {
1094
+ const allUnsubs = [];
1095
+ const unsubAll = () => {
1096
+ for (const unsub of allUnsubs)
1097
+ unsub();
1098
+ allUnsubs.splice(0, allUnsubs.length);
1099
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
1100
+ };
1101
+ for (const opts of Array.isArray(options) ? options : [options]) {
1102
+ const optsWithDefaults = {
1103
+ allOf: [],
1104
+ oneOf: [],
1105
+ once: false,
1106
+ ...opts
1107
+ };
1108
+ const {
1109
+ oneOf,
1110
+ allOf,
1111
+ once,
1112
+ signal,
1113
+ callback
1114
+ } = optsWithDefaults;
1115
+ if (signal == null ? void 0 : signal.aborted)
1116
+ return unsubAll;
1117
+ const curEvtUnsubs = [];
1118
+ const checkUnsubAllEvt = (force = false) => {
1119
+ if (!(signal == null ? void 0 : signal.aborted) && !force)
1120
+ return;
1121
+ for (const unsub of curEvtUnsubs)
1122
+ unsub();
1123
+ curEvtUnsubs.splice(0, curEvtUnsubs.length);
1124
+ this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
1125
+ };
1126
+ for (const event of oneOf) {
1127
+ const unsub = this.events.on(event, (...args) => {
1128
+ checkUnsubAllEvt();
1129
+ callback(event, ...args);
1130
+ if (once)
1131
+ checkUnsubAllEvt(true);
1132
+ });
1133
+ curEvtUnsubs.push(unsub);
1134
+ }
1135
+ const allOfEmitted = /* @__PURE__ */ new Set();
1136
+ const checkAllOf = (event, ...args) => {
1137
+ checkUnsubAllEvt();
1138
+ allOfEmitted.add(event);
1139
+ if (allOfEmitted.size === allOf.length) {
1140
+ callback(event, ...args);
1141
+ if (once)
1142
+ checkUnsubAllEvt(true);
1143
+ }
1144
+ };
1145
+ for (const event of allOf) {
1146
+ const unsub = this.events.on(event, (...args) => {
1147
+ checkUnsubAllEvt();
1148
+ checkAllOf(event, ...args);
1149
+ });
1150
+ curEvtUnsubs.push(unsub);
1151
+ }
1152
+ if (oneOf.length === 0 && allOf.length === 0)
1153
+ throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
1154
+ allUnsubs.push(() => checkUnsubAllEvt(true));
1155
+ }
1156
+ return unsubAll;
1157
+ }
1158
+ //#region emit
1016
1159
  /**
1017
1160
  * Emits an event on this instance.
1018
- * ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1161
+ * - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
1019
1162
  * @param event The event to emit
1020
1163
  * @param args The arguments to pass to the event listeners
1021
1164
  * @returns Returns true if `publicEmit` is true and the event was emitted successfully
@@ -1027,6 +1170,7 @@ var NanoEmitter = class {
1027
1170
  }
1028
1171
  return false;
1029
1172
  }
1173
+ //#region unsubscribeAll
1030
1174
  /** Unsubscribes all event listeners from this instance */
1031
1175
  unsubscribeAll() {
1032
1176
  for (const unsub of this.eventUnsubscribes)
@@ -1174,6 +1318,7 @@ export {
1174
1318
  joinArrayReadable,
1175
1319
  lightenColor,
1176
1320
  mapRange,
1321
+ overflowVal,
1177
1322
  pauseFor,
1178
1323
  pureObj,
1179
1324
  randRange,