@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.
- package/CHANGELOG.md +39 -10
- package/README.md +52 -9
- package/dist/CoreUtils.cjs +219 -59
- 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 +219 -59
- package/dist/CoreUtils.umd.js +219 -59
- 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/dist/lib/misc.d.ts +8 -0
- package/package.json +35 -28
package/dist/CoreUtils.cjs
CHANGED
|
@@ -65,6 +65,7 @@ __export(lib_exports, {
|
|
|
65
65
|
joinArrayReadable: () => joinArrayReadable,
|
|
66
66
|
lightenColor: () => lightenColor,
|
|
67
67
|
mapRange: () => mapRange,
|
|
68
|
+
overflowVal: () => overflowVal,
|
|
68
69
|
pauseFor: () => pauseFor,
|
|
69
70
|
pureObj: () => pureObj,
|
|
70
71
|
randRange: () => randRange,
|
|
@@ -74,6 +75,7 @@ __export(lib_exports, {
|
|
|
74
75
|
randomizeArray: () => randomizeArray,
|
|
75
76
|
rgbToHex: () => rgbToHex,
|
|
76
77
|
roundFixed: () => roundFixed,
|
|
78
|
+
scheduleExit: () => scheduleExit,
|
|
77
79
|
secsToTimeStr: () => secsToTimeStr,
|
|
78
80
|
setImmediateInterval: () => setImmediateInterval,
|
|
79
81
|
setImmediateTimeoutLoop: () => setImmediateTimeoutLoop,
|
|
@@ -127,6 +129,19 @@ function mapRange(value, range1min, range1max, range2min, range2max) {
|
|
|
127
129
|
return value * (range2max / range1max);
|
|
128
130
|
return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
|
|
129
131
|
}
|
|
132
|
+
function overflowVal(value, minOrMax, max) {
|
|
133
|
+
const min = typeof max === "number" ? minOrMax : 0;
|
|
134
|
+
max = typeof max === "number" ? max : minOrMax;
|
|
135
|
+
if (min > max)
|
|
136
|
+
throw new RangeError(`Parameter "min" can't be bigger than "max"`);
|
|
137
|
+
if (isNaN(value) || isNaN(min) || isNaN(max) || !isFinite(value) || !isFinite(min) || !isFinite(max))
|
|
138
|
+
return NaN;
|
|
139
|
+
if (value >= min && value <= max)
|
|
140
|
+
return value;
|
|
141
|
+
const range = max - min + 1;
|
|
142
|
+
const wrappedValue = ((value - min) % range + range) % range + min;
|
|
143
|
+
return wrappedValue;
|
|
144
|
+
}
|
|
130
145
|
function randRange(...args) {
|
|
131
146
|
let min, max, enhancedEntropy = false;
|
|
132
147
|
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
@@ -222,10 +237,8 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
222
237
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
223
238
|
else if (color.startsWith("rgba"))
|
|
224
239
|
return `rgba(${r}, ${g}, ${b}, ${a ?? NaN})`;
|
|
225
|
-
else if (color.startsWith("rgb"))
|
|
226
|
-
return `rgb(${r}, ${g}, ${b})`;
|
|
227
240
|
else
|
|
228
|
-
|
|
241
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
229
242
|
}
|
|
230
243
|
function hexToRgb(hex) {
|
|
231
244
|
hex = (hex.startsWith("#") ? hex.slice(1) : hex).trim();
|
|
@@ -258,22 +271,22 @@ function atoab(str) {
|
|
|
258
271
|
return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
|
|
259
272
|
}
|
|
260
273
|
async function compress(input, compressionFormat, outputType = "string") {
|
|
261
|
-
const byteArray = input instanceof
|
|
274
|
+
const byteArray = input instanceof Uint8Array ? input : new TextEncoder().encode((input == null ? void 0 : input.toString()) ?? String(input));
|
|
262
275
|
const comp = new CompressionStream(compressionFormat);
|
|
263
276
|
const writer = comp.writable.getWriter();
|
|
264
277
|
writer.write(byteArray);
|
|
265
278
|
writer.close();
|
|
266
|
-
const
|
|
267
|
-
return outputType === "arrayBuffer" ?
|
|
279
|
+
const uintArr = new Uint8Array(await new Response(comp.readable).arrayBuffer());
|
|
280
|
+
return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
|
|
268
281
|
}
|
|
269
282
|
async function decompress(input, compressionFormat, outputType = "string") {
|
|
270
|
-
const byteArray = input instanceof
|
|
283
|
+
const byteArray = input instanceof Uint8Array ? input : atoab((input == null ? void 0 : input.toString()) ?? String(input));
|
|
271
284
|
const decomp = new DecompressionStream(compressionFormat);
|
|
272
285
|
const writer = decomp.writable.getWriter();
|
|
273
286
|
writer.write(byteArray);
|
|
274
287
|
writer.close();
|
|
275
|
-
const
|
|
276
|
-
return outputType === "arrayBuffer" ?
|
|
288
|
+
const uintArr = new Uint8Array(await new Response(decomp.readable).arrayBuffer());
|
|
289
|
+
return outputType === "arrayBuffer" ? uintArr : new TextDecoder().decode(uintArr);
|
|
277
290
|
}
|
|
278
291
|
async function computeHash(input, algorithm = "SHA-256") {
|
|
279
292
|
let data;
|
|
@@ -382,6 +395,18 @@ function setImmediateTimeoutLoop(callback, interval, signal) {
|
|
|
382
395
|
signal == null ? void 0 : signal.addEventListener("abort", cleanup);
|
|
383
396
|
loop();
|
|
384
397
|
}
|
|
398
|
+
function scheduleExit(code = 0, timeout = 0) {
|
|
399
|
+
if (timeout < 0)
|
|
400
|
+
throw new TypeError("Timeout must be a non-negative number");
|
|
401
|
+
let exit;
|
|
402
|
+
if (typeof process !== "undefined" && "exit" in process)
|
|
403
|
+
exit = () => process.exit(code);
|
|
404
|
+
else if (typeof Deno !== "undefined" && "exit" in Deno)
|
|
405
|
+
exit = () => Deno.exit(code);
|
|
406
|
+
else
|
|
407
|
+
throw new Error("Cannot exit the process, no exit method available");
|
|
408
|
+
setTimeout(exit, timeout);
|
|
409
|
+
}
|
|
385
410
|
|
|
386
411
|
// lib/text.ts
|
|
387
412
|
function autoPlural(term, num, pluralType = "auto") {
|
|
@@ -507,7 +532,14 @@ var DataStore = class {
|
|
|
507
532
|
decodeData;
|
|
508
533
|
compressionFormat = "deflate-raw";
|
|
509
534
|
engine;
|
|
535
|
+
options;
|
|
536
|
+
/**
|
|
537
|
+
* Whether all first-init checks should be done.
|
|
538
|
+
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
539
|
+
* 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.
|
|
540
|
+
*/
|
|
510
541
|
firstInit = true;
|
|
542
|
+
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
511
543
|
cachedData;
|
|
512
544
|
migrations;
|
|
513
545
|
migrateIds = [];
|
|
@@ -515,13 +547,13 @@ var DataStore = class {
|
|
|
515
547
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
516
548
|
* Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
|
|
517
549
|
*
|
|
518
|
-
* - ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
|
|
519
550
|
* - ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
|
|
520
551
|
*
|
|
521
552
|
* @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.)
|
|
522
553
|
* @param opts The options for this DataStore instance
|
|
523
554
|
*/
|
|
524
555
|
constructor(opts) {
|
|
556
|
+
var _a;
|
|
525
557
|
this.id = opts.id;
|
|
526
558
|
this.formatVersion = opts.formatVersion;
|
|
527
559
|
this.defaultData = opts.defaultData;
|
|
@@ -532,8 +564,9 @@ var DataStore = class {
|
|
|
532
564
|
this.encodeData = opts.encodeData;
|
|
533
565
|
this.decodeData = opts.decodeData;
|
|
534
566
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
567
|
+
this.options = opts;
|
|
535
568
|
if (typeof opts.compressionFormat === "undefined")
|
|
536
|
-
opts.compressionFormat = "deflate-raw";
|
|
569
|
+
opts.compressionFormat = ((_a = opts.encodeData) == null ? void 0 : _a[0]) ?? "deflate-raw";
|
|
537
570
|
if (typeof opts.compressionFormat === "string") {
|
|
538
571
|
this.encodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
|
|
539
572
|
this.decodeData = [opts.compressionFormat, async (data) => await compress(data, opts.compressionFormat, "string")];
|
|
@@ -568,9 +601,14 @@ var DataStore = class {
|
|
|
568
601
|
promises.push(this.engine.setValue(newKey, value));
|
|
569
602
|
promises.push(this.engine.deleteValue(oldKey));
|
|
570
603
|
};
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
604
|
+
if (oldData)
|
|
605
|
+
migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
|
|
606
|
+
if (!isNaN(oldVer))
|
|
607
|
+
migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
|
|
608
|
+
if (typeof oldEnc === "boolean")
|
|
609
|
+
migrateFmt(`_uucfgenc-${this.id}`, `__ds-${this.id}-enf`, oldEnc === true ? Boolean(this.compressionFormat) || null : null);
|
|
610
|
+
else
|
|
611
|
+
promises.push(this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat));
|
|
574
612
|
await Promise.allSettled(promises);
|
|
575
613
|
}
|
|
576
614
|
await this.engine.setValue("__ds_fmt_ver", dsFmtVer);
|
|
@@ -580,21 +618,22 @@ var DataStore = class {
|
|
|
580
618
|
await this.migrateId(this.migrateIds);
|
|
581
619
|
this.migrateIds = [];
|
|
582
620
|
}
|
|
583
|
-
const
|
|
584
|
-
let
|
|
585
|
-
if (typeof
|
|
621
|
+
const storedData = await this.engine.getValue(`__ds-${this.id}-dat`, JSON.stringify(this.defaultData));
|
|
622
|
+
let storedFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
|
|
623
|
+
if (typeof storedData !== "string") {
|
|
586
624
|
await this.saveDefaultData();
|
|
587
625
|
return { ...this.defaultData };
|
|
588
626
|
}
|
|
589
|
-
const
|
|
627
|
+
const encodingFmt = String(await this.engine.getValue(`__ds-${this.id}-enf`, null));
|
|
628
|
+
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false";
|
|
590
629
|
let saveData = false;
|
|
591
|
-
if (isNaN(
|
|
592
|
-
await this.engine.setValue(`__ds-${this.id}-ver`,
|
|
630
|
+
if (isNaN(storedFmtVer)) {
|
|
631
|
+
await this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
|
|
593
632
|
saveData = true;
|
|
594
633
|
}
|
|
595
|
-
let parsed = await this.engine.deserializeData(
|
|
596
|
-
if (
|
|
597
|
-
parsed = await this.runMigrations(parsed,
|
|
634
|
+
let parsed = await this.engine.deserializeData(storedData, isEncoded);
|
|
635
|
+
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
636
|
+
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
598
637
|
if (saveData)
|
|
599
638
|
await this.setData(parsed);
|
|
600
639
|
return this.cachedData = this.engine.deepCopy(parsed);
|
|
@@ -614,12 +653,11 @@ var DataStore = class {
|
|
|
614
653
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
615
654
|
setData(data) {
|
|
616
655
|
this.cachedData = data;
|
|
617
|
-
const useEncoding = this.encodingEnabled();
|
|
618
656
|
return new Promise(async (resolve) => {
|
|
619
657
|
await Promise.allSettled([
|
|
620
|
-
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data,
|
|
658
|
+
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
621
659
|
this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
|
|
622
|
-
this.engine.setValue(`__ds-${this.id}-
|
|
660
|
+
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
|
|
623
661
|
]);
|
|
624
662
|
resolve();
|
|
625
663
|
});
|
|
@@ -627,30 +665,29 @@ var DataStore = class {
|
|
|
627
665
|
/** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
|
|
628
666
|
async saveDefaultData() {
|
|
629
667
|
this.cachedData = this.defaultData;
|
|
630
|
-
const useEncoding = this.encodingEnabled();
|
|
631
668
|
await Promise.allSettled([
|
|
632
|
-
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData,
|
|
669
|
+
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
633
670
|
this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
|
|
634
|
-
this.engine.setValue(`__ds-${this.id}-
|
|
671
|
+
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
|
|
635
672
|
]);
|
|
636
673
|
}
|
|
637
674
|
/**
|
|
638
|
-
* Call this method to clear all persistently stored data associated with this DataStore instance.
|
|
675
|
+
* Call this method to clear all persistently stored data associated with this DataStore instance, including the storage container (if supported by the DataStoreEngine).
|
|
639
676
|
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
640
|
-
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
641
|
-
*
|
|
642
|
-
* - ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"`
|
|
677
|
+
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
643
678
|
*/
|
|
644
679
|
async deleteData() {
|
|
680
|
+
var _a, _b;
|
|
645
681
|
await Promise.allSettled([
|
|
646
682
|
this.engine.deleteValue(`__ds-${this.id}-dat`),
|
|
647
683
|
this.engine.deleteValue(`__ds-${this.id}-ver`),
|
|
648
|
-
this.engine.deleteValue(`__ds-${this.id}-
|
|
684
|
+
this.engine.deleteValue(`__ds-${this.id}-enf`)
|
|
649
685
|
]);
|
|
686
|
+
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
650
687
|
}
|
|
651
688
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
652
689
|
encodingEnabled() {
|
|
653
|
-
return Boolean(this.encodeData && this.decodeData);
|
|
690
|
+
return Boolean(this.encodeData && this.decodeData) && this.compressionFormat !== null || Boolean(this.compressionFormat);
|
|
654
691
|
}
|
|
655
692
|
//#region migrations
|
|
656
693
|
/**
|
|
@@ -684,7 +721,7 @@ var DataStore = class {
|
|
|
684
721
|
await Promise.allSettled([
|
|
685
722
|
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(newData)),
|
|
686
723
|
this.engine.setValue(`__ds-${this.id}-ver`, lastFmtVer),
|
|
687
|
-
this.engine.setValue(`__ds-${this.id}-
|
|
724
|
+
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
|
|
688
725
|
]);
|
|
689
726
|
return this.cachedData = { ...newData };
|
|
690
727
|
}
|
|
@@ -695,19 +732,24 @@ var DataStore = class {
|
|
|
695
732
|
async migrateId(oldIds) {
|
|
696
733
|
const ids = Array.isArray(oldIds) ? oldIds : [oldIds];
|
|
697
734
|
await Promise.all(ids.map(async (id) => {
|
|
698
|
-
const data = await
|
|
699
|
-
|
|
700
|
-
|
|
735
|
+
const [data, fmtVer, isEncoded] = await (async () => {
|
|
736
|
+
const [d, f, e] = await Promise.all([
|
|
737
|
+
this.engine.getValue(`__ds-${id}-dat`, JSON.stringify(this.defaultData)),
|
|
738
|
+
this.engine.getValue(`__ds-${id}-ver`, NaN),
|
|
739
|
+
this.engine.getValue(`__ds-${id}-enf`, null)
|
|
740
|
+
]);
|
|
741
|
+
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
742
|
+
})();
|
|
701
743
|
if (data === void 0 || isNaN(fmtVer))
|
|
702
744
|
return;
|
|
703
745
|
const parsed = await this.engine.deserializeData(data, isEncoded);
|
|
704
746
|
await Promise.allSettled([
|
|
705
747
|
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(parsed)),
|
|
706
748
|
this.engine.setValue(`__ds-${this.id}-ver`, fmtVer),
|
|
707
|
-
this.engine.setValue(`__ds-${this.id}-
|
|
749
|
+
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat),
|
|
708
750
|
this.engine.deleteValue(`__ds-${id}-dat`),
|
|
709
751
|
this.engine.deleteValue(`__ds-${id}-ver`),
|
|
710
|
-
this.engine.deleteValue(`__ds-${id}-
|
|
752
|
+
this.engine.deleteValue(`__ds-${id}-enf`)
|
|
711
753
|
]);
|
|
712
754
|
}));
|
|
713
755
|
}
|
|
@@ -717,7 +759,11 @@ var DataStore = class {
|
|
|
717
759
|
var DataStoreEngine = class {
|
|
718
760
|
dataStoreOptions;
|
|
719
761
|
// setDataStoreOptions() is called from inside the DataStore constructor to set this value
|
|
720
|
-
|
|
762
|
+
constructor(options) {
|
|
763
|
+
if (options)
|
|
764
|
+
this.dataStoreOptions = options;
|
|
765
|
+
}
|
|
766
|
+
/** Called by DataStore on creation, to pass its options. Only call this if you are using this instance standalone! */
|
|
721
767
|
setDataStoreOptions(dataStoreOptions) {
|
|
722
768
|
this.dataStoreOptions = dataStoreOptions;
|
|
723
769
|
}
|
|
@@ -725,6 +771,7 @@ var DataStoreEngine = class {
|
|
|
725
771
|
/** Serializes the given object to a string, optionally encoded with `options.encodeData` if {@linkcode useEncoding} is set to true */
|
|
726
772
|
async serializeData(data, useEncoding) {
|
|
727
773
|
var _a, _b, _c, _d, _e;
|
|
774
|
+
this.ensureDataStoreOptions();
|
|
728
775
|
const stringData = JSON.stringify(data);
|
|
729
776
|
if (!useEncoding || !((_a = this.dataStoreOptions) == null ? void 0 : _a.encodeData) || !((_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData))
|
|
730
777
|
return stringData;
|
|
@@ -736,12 +783,20 @@ var DataStoreEngine = class {
|
|
|
736
783
|
/** Deserializes the given string to a JSON object, optionally decoded with `options.decodeData` if {@linkcode useEncoding} is set to true */
|
|
737
784
|
async deserializeData(data, useEncoding) {
|
|
738
785
|
var _a, _b, _c;
|
|
786
|
+
this.ensureDataStoreOptions();
|
|
739
787
|
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;
|
|
740
788
|
if (decRes instanceof Promise)
|
|
741
789
|
decRes = await decRes;
|
|
742
790
|
return JSON.parse(decRes ?? data);
|
|
743
791
|
}
|
|
744
792
|
//#region misc api
|
|
793
|
+
/** Throws an error if the DataStoreOptions are not set or invalid */
|
|
794
|
+
ensureDataStoreOptions() {
|
|
795
|
+
if (!this.dataStoreOptions)
|
|
796
|
+
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.");
|
|
797
|
+
if (!this.dataStoreOptions.id)
|
|
798
|
+
throw new DatedError("DataStoreEngine must be initialized with a valid DataStore ID");
|
|
799
|
+
}
|
|
745
800
|
/**
|
|
746
801
|
* Copies a JSON-compatible object and loses all its internal references in the process.
|
|
747
802
|
* Uses [`structuredClone()`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) if available, otherwise falls back to `JSON.parse(JSON.stringify(obj))`.
|
|
@@ -760,11 +815,11 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
760
815
|
/**
|
|
761
816
|
* Creates an instance of `BrowserStorageEngine`.
|
|
762
817
|
*
|
|
763
|
-
* ⚠️ Requires a DOM environment
|
|
764
|
-
* ⚠️ Don't reuse
|
|
818
|
+
* - ⚠️ Requires a DOM environment
|
|
819
|
+
* - ⚠️ Don't reuse engines across multiple {@linkcode DataStore} instances
|
|
765
820
|
*/
|
|
766
821
|
constructor(options) {
|
|
767
|
-
super();
|
|
822
|
+
super(options == null ? void 0 : options.dataStoreOptions);
|
|
768
823
|
this.options = {
|
|
769
824
|
type: "localStorage",
|
|
770
825
|
...options
|
|
@@ -796,11 +851,11 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
796
851
|
/**
|
|
797
852
|
* Creates an instance of `FileStorageEngine`.
|
|
798
853
|
*
|
|
799
|
-
* ⚠️ Requires Node.js or Deno with Node compatibility
|
|
800
|
-
* ⚠️ Don't reuse
|
|
854
|
+
* - ⚠️ Requires Node.js or Deno with Node compatibility (v1.31+)
|
|
855
|
+
* - ⚠️ Don't reuse engines across multiple {@linkcode DataStore} instances
|
|
801
856
|
*/
|
|
802
857
|
constructor(options) {
|
|
803
|
-
super();
|
|
858
|
+
super(options == null ? void 0 : options.dataStoreOptions);
|
|
804
859
|
this.options = {
|
|
805
860
|
filePath: (id) => `.ds-${id}`,
|
|
806
861
|
...options
|
|
@@ -809,28 +864,32 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
809
864
|
//#region json file
|
|
810
865
|
/** Reads the file contents */
|
|
811
866
|
async readFile() {
|
|
812
|
-
var _a, _b, _c;
|
|
867
|
+
var _a, _b, _c, _d;
|
|
868
|
+
this.ensureDataStoreOptions();
|
|
813
869
|
try {
|
|
814
870
|
if (!fs)
|
|
815
|
-
fs = (await import("fs/promises")).default;
|
|
871
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
872
|
+
if (!fs)
|
|
873
|
+
throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
|
|
816
874
|
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
817
875
|
const data = await fs.readFile(path, "utf-8");
|
|
818
|
-
|
|
819
|
-
return void 0;
|
|
820
|
-
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);
|
|
876
|
+
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;
|
|
821
877
|
} catch {
|
|
822
878
|
return void 0;
|
|
823
879
|
}
|
|
824
880
|
}
|
|
825
881
|
/** Overwrites the file contents */
|
|
826
882
|
async writeFile(data) {
|
|
827
|
-
var _a, _b, _c;
|
|
883
|
+
var _a, _b, _c, _d;
|
|
884
|
+
this.ensureDataStoreOptions();
|
|
828
885
|
try {
|
|
829
886
|
if (!fs)
|
|
830
|
-
fs = (await import("fs/promises")).default;
|
|
887
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
888
|
+
if (!fs)
|
|
889
|
+
throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
|
|
831
890
|
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
832
891
|
await fs.mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
833
|
-
await fs.writeFile(path, await ((
|
|
892
|
+
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");
|
|
834
893
|
} catch (err) {
|
|
835
894
|
console.error("Error writing file:", err);
|
|
836
895
|
}
|
|
@@ -864,6 +923,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
864
923
|
delete data[name];
|
|
865
924
|
await this.writeFile(data);
|
|
866
925
|
}
|
|
926
|
+
/** Deletes the file that contains the data of this DataStore. */
|
|
927
|
+
async deleteStorage() {
|
|
928
|
+
var _a;
|
|
929
|
+
this.ensureDataStoreOptions();
|
|
930
|
+
try {
|
|
931
|
+
if (!fs)
|
|
932
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
933
|
+
if (!fs)
|
|
934
|
+
throw new DatedError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new Error("'node:fs/promises' module not available") });
|
|
935
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
936
|
+
await fs.unlink(path);
|
|
937
|
+
} catch (err) {
|
|
938
|
+
console.error("Error deleting file:", err);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
867
941
|
};
|
|
868
942
|
|
|
869
943
|
// lib/DataStoreSerializer.ts
|
|
@@ -891,14 +965,16 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
891
965
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
892
966
|
*/
|
|
893
967
|
async serializePartial(stores, useEncoding = true, stringified = true) {
|
|
968
|
+
var _a;
|
|
894
969
|
const serData = [];
|
|
895
970
|
for (const storeInst of this.stores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id))) {
|
|
896
|
-
const
|
|
971
|
+
const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
|
|
972
|
+
const data = encoded ? await storeInst.encodeData[1](JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
897
973
|
serData.push({
|
|
898
974
|
id: storeInst.id,
|
|
899
975
|
data,
|
|
900
976
|
formatVersion: storeInst.formatVersion,
|
|
901
|
-
encoded
|
|
977
|
+
encoded,
|
|
902
978
|
checksum: this.options.addChecksum ? await this.calcChecksum(data) : void 0
|
|
903
979
|
});
|
|
904
980
|
}
|
|
@@ -1022,6 +1098,7 @@ var NanoEmitter = class {
|
|
|
1022
1098
|
...options
|
|
1023
1099
|
};
|
|
1024
1100
|
}
|
|
1101
|
+
//#region on
|
|
1025
1102
|
/**
|
|
1026
1103
|
* Subscribes to an event and calls the callback when it's emitted.
|
|
1027
1104
|
* @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 "_")
|
|
@@ -1055,6 +1132,7 @@ var NanoEmitter = class {
|
|
|
1055
1132
|
this.eventUnsubscribes.push(unsub);
|
|
1056
1133
|
return unsubProxy;
|
|
1057
1134
|
}
|
|
1135
|
+
//#region once
|
|
1058
1136
|
/**
|
|
1059
1137
|
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
1060
1138
|
* @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 "_")
|
|
@@ -1085,9 +1163,90 @@ var NanoEmitter = class {
|
|
|
1085
1163
|
this.eventUnsubscribes.push(unsub);
|
|
1086
1164
|
});
|
|
1087
1165
|
}
|
|
1166
|
+
//#region onMulti
|
|
1167
|
+
/**
|
|
1168
|
+
* 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.
|
|
1169
|
+
* @param options An object or array of objects with the following properties:
|
|
1170
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
1171
|
+
*
|
|
1172
|
+
* 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.
|
|
1173
|
+
* If `signal` is provided, the subscription will be aborted when the given signal is aborted.
|
|
1174
|
+
*
|
|
1175
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
1176
|
+
* 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.
|
|
1177
|
+
* You may use a combination of the above two options, but at least one of them must be provided.
|
|
1178
|
+
*
|
|
1179
|
+
* @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.
|
|
1180
|
+
*/
|
|
1181
|
+
onMulti(options) {
|
|
1182
|
+
const allUnsubs = [];
|
|
1183
|
+
const unsubAll = () => {
|
|
1184
|
+
for (const unsub of allUnsubs)
|
|
1185
|
+
unsub();
|
|
1186
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
1187
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
1188
|
+
};
|
|
1189
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
1190
|
+
const optsWithDefaults = {
|
|
1191
|
+
allOf: [],
|
|
1192
|
+
oneOf: [],
|
|
1193
|
+
once: false,
|
|
1194
|
+
...opts
|
|
1195
|
+
};
|
|
1196
|
+
const {
|
|
1197
|
+
oneOf,
|
|
1198
|
+
allOf,
|
|
1199
|
+
once,
|
|
1200
|
+
signal,
|
|
1201
|
+
callback
|
|
1202
|
+
} = optsWithDefaults;
|
|
1203
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
1204
|
+
return unsubAll;
|
|
1205
|
+
const curEvtUnsubs = [];
|
|
1206
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
1207
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
1208
|
+
return;
|
|
1209
|
+
for (const unsub of curEvtUnsubs)
|
|
1210
|
+
unsub();
|
|
1211
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
1212
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
1213
|
+
};
|
|
1214
|
+
for (const event of oneOf) {
|
|
1215
|
+
const unsub = this.events.on(event, (...args) => {
|
|
1216
|
+
checkUnsubAllEvt();
|
|
1217
|
+
callback(event, ...args);
|
|
1218
|
+
if (once)
|
|
1219
|
+
checkUnsubAllEvt(true);
|
|
1220
|
+
});
|
|
1221
|
+
curEvtUnsubs.push(unsub);
|
|
1222
|
+
}
|
|
1223
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1224
|
+
const checkAllOf = (event, ...args) => {
|
|
1225
|
+
checkUnsubAllEvt();
|
|
1226
|
+
allOfEmitted.add(event);
|
|
1227
|
+
if (allOfEmitted.size === allOf.length) {
|
|
1228
|
+
callback(event, ...args);
|
|
1229
|
+
if (once)
|
|
1230
|
+
checkUnsubAllEvt(true);
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
for (const event of allOf) {
|
|
1234
|
+
const unsub = this.events.on(event, (...args) => {
|
|
1235
|
+
checkUnsubAllEvt();
|
|
1236
|
+
checkAllOf(event, ...args);
|
|
1237
|
+
});
|
|
1238
|
+
curEvtUnsubs.push(unsub);
|
|
1239
|
+
}
|
|
1240
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
1241
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1242
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
1243
|
+
}
|
|
1244
|
+
return unsubAll;
|
|
1245
|
+
}
|
|
1246
|
+
//#region emit
|
|
1088
1247
|
/**
|
|
1089
1248
|
* Emits an event on this instance.
|
|
1090
|
-
* ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
1249
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
1091
1250
|
* @param event The event to emit
|
|
1092
1251
|
* @param args The arguments to pass to the event listeners
|
|
1093
1252
|
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
@@ -1099,6 +1258,7 @@ var NanoEmitter = class {
|
|
|
1099
1258
|
}
|
|
1100
1259
|
return false;
|
|
1101
1260
|
}
|
|
1261
|
+
//#region unsubscribeAll
|
|
1102
1262
|
/** Unsubscribes all event listeners from this instance */
|
|
1103
1263
|
unsubscribeAll() {
|
|
1104
1264
|
for (const unsub of this.eventUnsubscribes)
|