@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.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;
|
|
@@ -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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
|
498
|
-
let
|
|
499
|
-
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") {
|
|
500
536
|
await this.saveDefaultData();
|
|
501
537
|
return { ...this.defaultData };
|
|
502
538
|
}
|
|
503
|
-
const
|
|
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(
|
|
506
|
-
await this.engine.setValue(`__ds-${this.id}-ver`,
|
|
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(
|
|
510
|
-
if (
|
|
511
|
-
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);
|
|
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,
|
|
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}-
|
|
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,
|
|
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}-
|
|
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}-
|
|
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}-
|
|
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
|
|
613
|
-
|
|
614
|
-
|
|
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}-
|
|
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}-
|
|
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
|
-
|
|
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
|
|
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
|
|
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("
|
|
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
|
-
|
|
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("
|
|
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 ((
|
|
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
|
|
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
|
|
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,
|