@sv443-network/coreutils 0.0.1 → 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;
@@ -296,6 +307,18 @@ function setImmediateTimeoutLoop(callback, interval, signal) {
296
307
  signal == null ? void 0 : signal.addEventListener("abort", cleanup);
297
308
  loop();
298
309
  }
310
+ function scheduleExit(code = 0, timeout = 0) {
311
+ if (timeout < 0)
312
+ throw new TypeError("Timeout must be a non-negative number");
313
+ let exit;
314
+ if (typeof process !== "undefined" && "exit" in process)
315
+ exit = () => process.exit(code);
316
+ else if (typeof Deno !== "undefined" && "exit" in Deno)
317
+ exit = () => Deno.exit(code);
318
+ else
319
+ throw new Error("Cannot exit the process, no exit method available");
320
+ setTimeout(exit, timeout);
321
+ }
299
322
 
300
323
  // lib/text.ts
301
324
  function autoPlural(term, num, pluralType = "auto") {
@@ -421,7 +444,14 @@ var DataStore = class {
421
444
  decodeData;
422
445
  compressionFormat = "deflate-raw";
423
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
+ */
424
453
  firstInit = true;
454
+ /** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
425
455
  cachedData;
426
456
  migrations;
427
457
  migrateIds = [];
@@ -429,13 +459,13 @@ var DataStore = class {
429
459
  * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
430
460
  * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
431
461
  *
432
- * - ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
433
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`
434
463
  *
435
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.)
436
465
  * @param opts The options for this DataStore instance
437
466
  */
438
467
  constructor(opts) {
468
+ var _a;
439
469
  this.id = opts.id;
440
470
  this.formatVersion = opts.formatVersion;
441
471
  this.defaultData = opts.defaultData;
@@ -446,8 +476,9 @@ var DataStore = class {
446
476
  this.encodeData = opts.encodeData;
447
477
  this.decodeData = opts.decodeData;
448
478
  this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
479
+ this.options = opts;
449
480
  if (typeof opts.compressionFormat === "undefined")
450
- opts.compressionFormat = "deflate-raw";
481
+ opts.compressionFormat = ((_a = opts.encodeData) == null ? void 0 : _a[0]) ?? "deflate-raw";
451
482
  if (typeof opts.compressionFormat === "string") {
452
483
  this.encodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
453
484
  this.decodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
@@ -482,9 +513,14 @@ var DataStore = class {
482
513
  promises.push(this.engine.setValue(newKey, value));
483
514
  promises.push(this.engine.deleteValue(oldKey));
484
515
  };
485
- oldData && migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
486
- !isNaN(oldVer) && migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
487
- 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));
488
524
  await Promise.allSettled(promises);
489
525
  }
490
526
  await this.engine.setValue("__ds_fmt_ver", dsFmtVer);
@@ -494,21 +530,22 @@ var DataStore = class {
494
530
  await this.migrateId(this.migrateIds);
495
531
  this.migrateIds = [];
496
532
  }
497
- const gmData = await this.engine.getValue(`__ds-${this.id}-dat`, JSON.stringify(this.defaultData));
498
- let gmFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
499
- 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") {
500
536
  await this.saveDefaultData();
501
537
  return { ...this.defaultData };
502
538
  }
503
- 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";
504
541
  let saveData = false;
505
- if (isNaN(gmFmtVer)) {
506
- 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);
507
544
  saveData = true;
508
545
  }
509
- let parsed = await this.engine.deserializeData(gmData, isEncoded);
510
- if (gmFmtVer < this.formatVersion && this.migrations)
511
- 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);
512
549
  if (saveData)
513
550
  await this.setData(parsed);
514
551
  return this.cachedData = this.engine.deepCopy(parsed);
@@ -528,12 +565,11 @@ var DataStore = class {
528
565
  /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
529
566
  setData(data) {
530
567
  this.cachedData = data;
531
- const useEncoding = this.encodingEnabled();
532
568
  return new Promise(async (resolve) => {
533
569
  await Promise.allSettled([
534
- 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())),
535
571
  this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
536
- this.engine.setValue(`__ds-${this.id}-enc`, useEncoding)
572
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
537
573
  ]);
538
574
  resolve();
539
575
  });
@@ -541,30 +577,29 @@ var DataStore = class {
541
577
  /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
542
578
  async saveDefaultData() {
543
579
  this.cachedData = this.defaultData;
544
- const useEncoding = this.encodingEnabled();
545
580
  await Promise.allSettled([
546
- 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())),
547
582
  this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
548
- this.engine.setValue(`__ds-${this.id}-enc`, useEncoding)
583
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
549
584
  ]);
550
585
  }
551
586
  /**
552
- * 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).
553
588
  * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
554
- * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
555
- *
556
- * - ⚠️ 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.
557
590
  */
558
591
  async deleteData() {
592
+ var _a, _b;
559
593
  await Promise.allSettled([
560
594
  this.engine.deleteValue(`__ds-${this.id}-dat`),
561
595
  this.engine.deleteValue(`__ds-${this.id}-ver`),
562
- this.engine.deleteValue(`__ds-${this.id}-enc`)
596
+ this.engine.deleteValue(`__ds-${this.id}-enf`)
563
597
  ]);
598
+ await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
564
599
  }
565
600
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
566
601
  encodingEnabled() {
567
- return Boolean(this.encodeData && this.decodeData);
602
+ return Boolean(this.encodeData && this.decodeData) && this.compressionFormat !== null || Boolean(this.compressionFormat);
568
603
  }
569
604
  //#region migrations
570
605
  /**
@@ -598,7 +633,7 @@ var DataStore = class {
598
633
  await Promise.allSettled([
599
634
  this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(newData)),
600
635
  this.engine.setValue(`__ds-${this.id}-ver`, lastFmtVer),
601
- this.engine.setValue(`__ds-${this.id}-enc`, this.encodingEnabled())
636
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
602
637
  ]);
603
638
  return this.cachedData = { ...newData };
604
639
  }
@@ -609,19 +644,24 @@ var DataStore = class {
609
644
  async migrateId(oldIds) {
610
645
  const ids = Array.isArray(oldIds) ? oldIds : [oldIds];
611
646
  await Promise.all(ids.map(async (id) => {
612
- const data = await this.engine.getValue(`__ds-${id}-dat`, JSON.stringify(this.defaultData));
613
- const fmtVer = Number(await this.engine.getValue(`__ds-${id}-ver`, NaN));
614
- 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
+ })();
615
655
  if (data === void 0 || isNaN(fmtVer))
616
656
  return;
617
657
  const parsed = await this.engine.deserializeData(data, isEncoded);
618
658
  await Promise.allSettled([
619
659
  this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(parsed)),
620
660
  this.engine.setValue(`__ds-${this.id}-ver`, fmtVer),
621
- this.engine.setValue(`__ds-${this.id}-enc`, isEncoded),
661
+ this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat),
622
662
  this.engine.deleteValue(`__ds-${id}-dat`),
623
663
  this.engine.deleteValue(`__ds-${id}-ver`),
624
- this.engine.deleteValue(`__ds-${id}-enc`)
664
+ this.engine.deleteValue(`__ds-${id}-enf`)
625
665
  ]);
626
666
  }));
627
667
  }
@@ -631,7 +671,11 @@ var DataStore = class {
631
671
  var DataStoreEngine = class {
632
672
  dataStoreOptions;
633
673
  // setDataStoreOptions() is called from inside the DataStore constructor to set this value
634
- /** 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! */
635
679
  setDataStoreOptions(dataStoreOptions) {
636
680
  this.dataStoreOptions = dataStoreOptions;
637
681
  }
@@ -639,6 +683,7 @@ var DataStoreEngine = class {
639
683
  /** Serializes the given object to a string, optionally encoded with `options.encodeData` if {@linkcode useEncoding} is set to true */
640
684
  async serializeData(data, useEncoding) {
641
685
  var _a, _b, _c, _d, _e;
686
+ this.ensureDataStoreOptions();
642
687
  const stringData = JSON.stringify(data);
643
688
  if (!useEncoding || !((_a = this.dataStoreOptions) == null ? void 0 : _a.encodeData) || !((_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData))
644
689
  return stringData;
@@ -650,12 +695,20 @@ var DataStoreEngine = class {
650
695
  /** Deserializes the given string to a JSON object, optionally decoded with `options.decodeData` if {@linkcode useEncoding} is set to true */
651
696
  async deserializeData(data, useEncoding) {
652
697
  var _a, _b, _c;
698
+ this.ensureDataStoreOptions();
653
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;
654
700
  if (decRes instanceof Promise)
655
701
  decRes = await decRes;
656
702
  return JSON.parse(decRes ?? data);
657
703
  }
658
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
+ }
659
712
  /**
660
713
  * Copies a JSON-compatible object and loses all its internal references in the process.
661
714
  * Uses [`structuredClone()`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) if available, otherwise falls back to `JSON.parse(JSON.stringify(obj))`.
@@ -674,11 +727,11 @@ var BrowserStorageEngine = class extends DataStoreEngine {
674
727
  /**
675
728
  * Creates an instance of `BrowserStorageEngine`.
676
729
  *
677
- * ⚠️ Requires a DOM environment
678
- * ⚠️ 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
679
732
  */
680
733
  constructor(options) {
681
- super();
734
+ super(options == null ? void 0 : options.dataStoreOptions);
682
735
  this.options = {
683
736
  type: "localStorage",
684
737
  ...options
@@ -710,11 +763,11 @@ var FileStorageEngine = class extends DataStoreEngine {
710
763
  /**
711
764
  * Creates an instance of `FileStorageEngine`.
712
765
  *
713
- * ⚠️ Requires Node.js or Deno with Node compatibility
714
- * ⚠️ 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
715
768
  */
716
769
  constructor(options) {
717
- super();
770
+ super(options == null ? void 0 : options.dataStoreOptions);
718
771
  this.options = {
719
772
  filePath: (id) => `.ds-${id}`,
720
773
  ...options
@@ -723,28 +776,32 @@ var FileStorageEngine = class extends DataStoreEngine {
723
776
  //#region json file
724
777
  /** Reads the file contents */
725
778
  async readFile() {
726
- var _a, _b, _c;
779
+ var _a, _b, _c, _d;
780
+ this.ensureDataStoreOptions();
727
781
  try {
728
782
  if (!fs)
729
- fs = (await import("node:fs/promises")).default;
783
+ fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
784
+ if (!fs)
785
+ throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
730
786
  const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
731
787
  const data = await fs.readFile(path, "utf-8");
732
- if (!data)
733
- return void 0;
734
- return 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);
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;
735
789
  } catch {
736
790
  return void 0;
737
791
  }
738
792
  }
739
793
  /** Overwrites the file contents */
740
794
  async writeFile(data) {
741
- var _a, _b, _c;
795
+ var _a, _b, _c, _d;
796
+ this.ensureDataStoreOptions();
742
797
  try {
743
798
  if (!fs)
744
- fs = (await import("node:fs/promises")).default;
799
+ fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
800
+ if (!fs)
801
+ throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
745
802
  const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
746
803
  await fs.mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
747
- 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");
748
805
  } catch (err) {
749
806
  console.error("Error writing file:", err);
750
807
  }
@@ -778,6 +835,21 @@ var FileStorageEngine = class extends DataStoreEngine {
778
835
  delete data[name];
779
836
  await this.writeFile(data);
780
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
+ }
781
853
  };
782
854
 
783
855
  // lib/DataStoreSerializer.ts
@@ -805,14 +877,16 @@ var DataStoreSerializer = class _DataStoreSerializer {
805
877
  * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
806
878
  */
807
879
  async serializePartial(stores, useEncoding = true, stringified = true) {
880
+ var _a;
808
881
  const serData = [];
809
882
  for (const storeInst of this.stores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id))) {
810
- 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());
811
885
  serData.push({
812
886
  id: storeInst.id,
813
887
  data,
814
888
  formatVersion: storeInst.formatVersion,
815
- encoded: useEncoding && storeInst.encodingEnabled(),
889
+ encoded,
816
890
  checksum: this.options.addChecksum ? await this.calcChecksum(data) : void 0
817
891
  });
818
892
  }
@@ -936,6 +1010,7 @@ var NanoEmitter = class {
936
1010
  ...options
937
1011
  };
938
1012
  }
1013
+ //#region on
939
1014
  /**
940
1015
  * Subscribes to an event and calls the callback when it's emitted.
941
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 "_")
@@ -969,6 +1044,7 @@ var NanoEmitter = class {
969
1044
  this.eventUnsubscribes.push(unsub);
970
1045
  return unsubProxy;
971
1046
  }
1047
+ //#region once
972
1048
  /**
973
1049
  * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
974
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 "_")
@@ -999,9 +1075,90 @@ var NanoEmitter = class {
999
1075
  this.eventUnsubscribes.push(unsub);
1000
1076
  });
1001
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
1002
1159
  /**
1003
1160
  * Emits an event on this instance.
1004
- * ⚠️ 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!
1005
1162
  * @param event The event to emit
1006
1163
  * @param args The arguments to pass to the event listeners
1007
1164
  * @returns Returns true if `publicEmit` is true and the event was emitted successfully
@@ -1013,6 +1170,7 @@ var NanoEmitter = class {
1013
1170
  }
1014
1171
  return false;
1015
1172
  }
1173
+ //#region unsubscribeAll
1016
1174
  /** Unsubscribes all event listeners from this instance */
1017
1175
  unsubscribeAll() {
1018
1176
  for (const unsub of this.eventUnsubscribes)
@@ -1160,6 +1318,7 @@ export {
1160
1318
  joinArrayReadable,
1161
1319
  lightenColor,
1162
1320
  mapRange,
1321
+ overflowVal,
1163
1322
  pauseFor,
1164
1323
  pureObj,
1165
1324
  randRange,
@@ -1169,6 +1328,7 @@ export {
1169
1328
  randomizeArray,
1170
1329
  rgbToHex,
1171
1330
  roundFixed,
1331
+ scheduleExit,
1172
1332
  secsToTimeStr,
1173
1333
  setImmediateInterval,
1174
1334
  setImmediateTimeoutLoop,