@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.
- package/CHANGELOG.md +19 -2
- package/README.md +24 -8
- package/dist/CoreUtils.cjs +202 -57
- package/dist/CoreUtils.min.cjs +2 -2
- package/dist/CoreUtils.min.mjs +2 -2
- package/dist/CoreUtils.min.umd.js +2 -2
- package/dist/CoreUtils.mjs +202 -57
- package/dist/CoreUtils.umd.js +510 -480
- package/dist/lib/DataStore.d.ts +19 -11
- package/dist/lib/DataStoreEngine.d.ts +35 -14
- package/dist/lib/NanoEmitter.d.ts +33 -1
- package/dist/lib/TieredCache.d.ts +1 -1
- package/dist/lib/crypto.d.ts +15 -15
- package/dist/lib/math.d.ts +4 -0
- package/package.json +2 -1
package/dist/CoreUtils.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
181
|
-
return outputType === "arrayBuffer" ?
|
|
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
|
|
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
|
|
190
|
-
return outputType === "arrayBuffer" ?
|
|
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
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
|
510
|
-
let
|
|
511
|
-
if (typeof
|
|
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
|
|
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(
|
|
518
|
-
await this.engine.setValue(`__ds-${this.id}-ver`,
|
|
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(
|
|
522
|
-
if (
|
|
523
|
-
parsed = await this.runMigrations(parsed,
|
|
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,
|
|
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}-
|
|
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,
|
|
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}-
|
|
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}-
|
|
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}-
|
|
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
|
|
625
|
-
|
|
626
|
-
|
|
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}-
|
|
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}-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ((
|
|
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 ((
|
|
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
|
|
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
|
|
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,
|