@sv443-network/coreutils 2.0.3 → 3.0.1
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 -0
- package/README.md +4 -3
- package/dist/CoreUtils.cjs +169 -105
- package/dist/CoreUtils.min.cjs +5 -3
- package/dist/CoreUtils.min.mjs +5 -3
- package/dist/CoreUtils.min.umd.js +5 -3
- package/dist/CoreUtils.mjs +169 -105
- package/dist/CoreUtils.umd.js +537 -586
- package/dist/lib/DataStore.d.ts +30 -12
- package/dist/lib/DataStoreEngine.d.ts +13 -13
- package/dist/lib/DataStoreSerializer.d.ts +2 -0
- package/dist/lib/Errors.d.ts +15 -3
- package/dist/lib/NanoEmitter.d.ts +6 -5
- package/dist/lib/math.d.ts +1 -1
- package/dist/lib/misc.d.ts +6 -0
- package/dist/lib/{TestDataStore.d.ts → test/DirectAccessDataStore.d.ts} +4 -3
- package/dist/lib/test/softExpect.d.ts +11 -0
- package/dist/lib/types.d.ts +7 -0
- package/package.json +4 -1
package/dist/CoreUtils.mjs
CHANGED
|
@@ -87,7 +87,7 @@ function roundFixed(num, fractionDigits) {
|
|
|
87
87
|
const scale = 10 ** fractionDigits;
|
|
88
88
|
return Math.round(num * scale) / scale;
|
|
89
89
|
}
|
|
90
|
-
function valsWithin(a, b, dec =
|
|
90
|
+
function valsWithin(a, b, dec = 1, withinRange = 0.5) {
|
|
91
91
|
return Math.abs(roundFixed(a, dec) - roundFixed(b, dec)) <= withinRange;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -237,6 +237,52 @@ function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase =
|
|
|
237
237
|
return arr.map((v) => caseArr[randRange(0, caseArr.length - 1, enhancedEntropy)] === 1 ? v.toUpperCase() : v).join("");
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
// lib/Errors.ts
|
|
241
|
+
var DatedError = class extends Error {
|
|
242
|
+
date;
|
|
243
|
+
constructor(message, options) {
|
|
244
|
+
super(message, options);
|
|
245
|
+
this.name = this.constructor.name;
|
|
246
|
+
this.date = /* @__PURE__ */ new Date();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
var ChecksumMismatchError = class extends DatedError {
|
|
250
|
+
constructor(message, options) {
|
|
251
|
+
super(message, options);
|
|
252
|
+
this.name = "ChecksumMismatchError";
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var CustomError = class extends DatedError {
|
|
256
|
+
constructor(name, message, options) {
|
|
257
|
+
super(message, options);
|
|
258
|
+
this.name = name;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
var MigrationError = class extends DatedError {
|
|
262
|
+
constructor(message, options) {
|
|
263
|
+
super(message, options);
|
|
264
|
+
this.name = "MigrationError";
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var ValidationError = class extends DatedError {
|
|
268
|
+
constructor(message, options) {
|
|
269
|
+
super(message, options);
|
|
270
|
+
this.name = "ValidationError";
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var ScriptContextError = class extends DatedError {
|
|
274
|
+
constructor(message, options) {
|
|
275
|
+
super(message, options);
|
|
276
|
+
this.name = "ScriptContextError";
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
var NetworkError = class extends DatedError {
|
|
280
|
+
constructor(message, options) {
|
|
281
|
+
super(message, options);
|
|
282
|
+
this.name = "NetworkError";
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
240
286
|
// lib/misc.ts
|
|
241
287
|
async function consumeGen(valGen) {
|
|
242
288
|
return await (typeof valGen === "function" ? valGen() : valGen);
|
|
@@ -247,9 +293,8 @@ async function consumeStringGen(strGen) {
|
|
|
247
293
|
);
|
|
248
294
|
}
|
|
249
295
|
async function fetchAdvanced(input, options = {}) {
|
|
250
|
-
const { timeout = 1e4 } = options;
|
|
296
|
+
const { timeout = 1e4, signal, ...restOpts } = options;
|
|
251
297
|
const ctl = new AbortController();
|
|
252
|
-
const { signal, ...restOpts } = options;
|
|
253
298
|
signal == null ? void 0 : signal.addEventListener("abort", () => ctl.abort());
|
|
254
299
|
let sigOpts = {}, id = void 0;
|
|
255
300
|
if (timeout >= 0) {
|
|
@@ -265,7 +310,7 @@ async function fetchAdvanced(input, options = {}) {
|
|
|
265
310
|
return res;
|
|
266
311
|
} catch (err) {
|
|
267
312
|
typeof id !== "undefined" && clearTimeout(id);
|
|
268
|
-
throw new
|
|
313
|
+
throw new NetworkError("Error while calling fetch", { cause: err });
|
|
269
314
|
}
|
|
270
315
|
}
|
|
271
316
|
function getListLength(listLike, zeroOnInvalid = true) {
|
|
@@ -276,7 +321,7 @@ function pauseFor(time, signal, rejectOnAbort = false) {
|
|
|
276
321
|
const timeout = setTimeout(() => res(), time);
|
|
277
322
|
signal == null ? void 0 : signal.addEventListener("abort", () => {
|
|
278
323
|
clearTimeout(timeout);
|
|
279
|
-
rejectOnAbort ? rej(new
|
|
324
|
+
rejectOnAbort ? rej(new CustomError("AbortError", "The pause was aborted")) : res();
|
|
280
325
|
});
|
|
281
326
|
});
|
|
282
327
|
}
|
|
@@ -311,14 +356,24 @@ function scheduleExit(code = 0, timeout = 0) {
|
|
|
311
356
|
if (timeout < 0)
|
|
312
357
|
throw new TypeError("Timeout must be a non-negative number");
|
|
313
358
|
let exit;
|
|
314
|
-
if (typeof process !== "undefined" && "exit" in process)
|
|
359
|
+
if (typeof process !== "undefined" && "exit" in process && typeof process.exit === "function")
|
|
315
360
|
exit = () => process.exit(code);
|
|
316
|
-
else if (typeof Deno !== "undefined" && "exit" in Deno)
|
|
361
|
+
else if (typeof Deno !== "undefined" && "exit" in Deno && typeof Deno.exit === "function")
|
|
317
362
|
exit = () => Deno.exit(code);
|
|
318
363
|
else
|
|
319
|
-
throw new
|
|
364
|
+
throw new ScriptContextError("Cannot exit the process, no exit method available");
|
|
320
365
|
setTimeout(exit, timeout);
|
|
321
366
|
}
|
|
367
|
+
function getCallStack(asArray, lines = Infinity) {
|
|
368
|
+
if (typeof lines !== "number" || isNaN(lines) || lines < 0)
|
|
369
|
+
throw new TypeError("lines parameter must be a non-negative number");
|
|
370
|
+
try {
|
|
371
|
+
throw new Error("This is to capture a stack trace with CoreUtils.getCallStack(). (If you see this somewhere, you can safely ignore it.)");
|
|
372
|
+
} catch (err) {
|
|
373
|
+
const stack = (err.stack ?? "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
|
|
374
|
+
return asArray !== false ? stack : stack.join("\n");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
322
377
|
|
|
323
378
|
// lib/text.ts
|
|
324
379
|
function autoPlural(term, num, pluralType = "auto") {
|
|
@@ -388,16 +443,18 @@ function joinArrayReadable(array, separators = ", ", lastSeparator = " and ") {
|
|
|
388
443
|
return arr.join(separators) + lastItm;
|
|
389
444
|
}
|
|
390
445
|
function secsToTimeStr(seconds) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
446
|
+
const isNegative = seconds < 0;
|
|
447
|
+
const s = Math.abs(seconds);
|
|
448
|
+
if (isNaN(s) || !isFinite(s))
|
|
449
|
+
throw new TypeError("The seconds argument must be a valid number");
|
|
450
|
+
const hrs = Math.floor(s / 3600);
|
|
451
|
+
const mins = Math.floor(s % 3600 / 60);
|
|
452
|
+
const secs = Math.floor(s % 60);
|
|
453
|
+
return (isNegative ? "-" : "") + [
|
|
454
|
+
hrs ? hrs + ":" : "",
|
|
455
|
+
String(mins).padStart(mins > 0 || hrs > 0 ? 2 : 1, "0"),
|
|
399
456
|
":",
|
|
400
|
-
String(secs).padStart(secs > 0 ||
|
|
457
|
+
String(secs).padStart(secs > 0 || mins > 0 || hrs > 0 || seconds === 0 ? 2 : 1, "0")
|
|
401
458
|
].join("");
|
|
402
459
|
}
|
|
403
460
|
function truncStr(input, length, endStr = "...") {
|
|
@@ -406,34 +463,6 @@ function truncStr(input, length, endStr = "...") {
|
|
|
406
463
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
407
464
|
}
|
|
408
465
|
|
|
409
|
-
// lib/Errors.ts
|
|
410
|
-
var DatedError = class extends Error {
|
|
411
|
-
date;
|
|
412
|
-
constructor(message, options) {
|
|
413
|
-
super(message, options);
|
|
414
|
-
this.name = this.constructor.name;
|
|
415
|
-
this.date = /* @__PURE__ */ new Date();
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
var ChecksumMismatchError = class extends DatedError {
|
|
419
|
-
constructor(message, options) {
|
|
420
|
-
super(message, options);
|
|
421
|
-
this.name = "ChecksumMismatchError";
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
var MigrationError = class extends DatedError {
|
|
425
|
-
constructor(message, options) {
|
|
426
|
-
super(message, options);
|
|
427
|
-
this.name = "MigrationError";
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
var ValidationError = class extends DatedError {
|
|
431
|
-
constructor(message, options) {
|
|
432
|
-
super(message, options);
|
|
433
|
-
this.name = "ValidationError";
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
|
|
437
466
|
// lib/DataStore.ts
|
|
438
467
|
var dsFmtVer = 1;
|
|
439
468
|
var DataStore = class {
|
|
@@ -443,6 +472,7 @@ var DataStore = class {
|
|
|
443
472
|
encodeData;
|
|
444
473
|
decodeData;
|
|
445
474
|
compressionFormat = "deflate-raw";
|
|
475
|
+
memoryCache = true;
|
|
446
476
|
engine;
|
|
447
477
|
options;
|
|
448
478
|
/**
|
|
@@ -455,6 +485,7 @@ var DataStore = class {
|
|
|
455
485
|
cachedData;
|
|
456
486
|
migrations;
|
|
457
487
|
migrateIds = [];
|
|
488
|
+
//#region constructor
|
|
458
489
|
/**
|
|
459
490
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
460
491
|
* Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
|
|
@@ -469,7 +500,8 @@ var DataStore = class {
|
|
|
469
500
|
this.id = opts.id;
|
|
470
501
|
this.formatVersion = opts.formatVersion;
|
|
471
502
|
this.defaultData = opts.defaultData;
|
|
472
|
-
this.
|
|
503
|
+
this.memoryCache = Boolean(opts.memoryCache ?? true);
|
|
504
|
+
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
473
505
|
this.migrations = opts.migrations;
|
|
474
506
|
if (opts.migrateIds)
|
|
475
507
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
@@ -494,7 +526,7 @@ var DataStore = class {
|
|
|
494
526
|
throw new TypeError("Either `compressionFormat` or `encodeData` and `decodeData` have to be set and valid, but not all three at a time. Please refer to the documentation for more info.");
|
|
495
527
|
this.engine.setDataStoreOptions(opts);
|
|
496
528
|
}
|
|
497
|
-
//#region
|
|
529
|
+
//#region loadData
|
|
498
530
|
/**
|
|
499
531
|
* Loads the data saved in persistent storage into the in-memory cache and also returns a copy of it.
|
|
500
532
|
* Automatically populates persistent storage with default data if it doesn't contain any data yet.
|
|
@@ -505,28 +537,28 @@ var DataStore = class {
|
|
|
505
537
|
if (this.firstInit) {
|
|
506
538
|
this.firstInit = false;
|
|
507
539
|
const dsVer = Number(await this.engine.getValue("__ds_fmt_ver", 0));
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
promises.push(this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat));
|
|
526
|
-
await Promise.allSettled(promises);
|
|
540
|
+
const oldData = await this.engine.getValue(`_uucfg-${this.id}`, null);
|
|
541
|
+
if (oldData) {
|
|
542
|
+
const oldVer = Number(await this.engine.getValue(`_uucfgver-${this.id}`, NaN));
|
|
543
|
+
const oldEnc = await this.engine.getValue(`_uucfgenc-${this.id}`, null);
|
|
544
|
+
const promises = [];
|
|
545
|
+
const migrateFmt = (oldKey, newKey, value) => {
|
|
546
|
+
promises.push(this.engine.setValue(newKey, value));
|
|
547
|
+
promises.push(this.engine.deleteValue(oldKey));
|
|
548
|
+
};
|
|
549
|
+
migrateFmt(`_uucfg-${this.id}`, `__ds-${this.id}-dat`, oldData);
|
|
550
|
+
if (!isNaN(oldVer))
|
|
551
|
+
migrateFmt(`_uucfgver-${this.id}`, `__ds-${this.id}-ver`, oldVer);
|
|
552
|
+
if (typeof oldEnc === "boolean")
|
|
553
|
+
migrateFmt(`_uucfgenc-${this.id}`, `__ds-${this.id}-enf`, oldEnc === true ? this.compressionFormat ?? null : null);
|
|
554
|
+
else {
|
|
555
|
+
promises.push(this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat));
|
|
556
|
+
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
527
557
|
}
|
|
528
|
-
await
|
|
558
|
+
await Promise.allSettled(promises);
|
|
529
559
|
}
|
|
560
|
+
if (isNaN(dsVer) || dsVer < dsFmtVer)
|
|
561
|
+
await this.engine.setValue("__ds_fmt_ver", dsFmtVer);
|
|
530
562
|
}
|
|
531
563
|
if (this.migrateIds.length > 0) {
|
|
532
564
|
await this.migrateId(this.migrateIds);
|
|
@@ -536,7 +568,7 @@ var DataStore = class {
|
|
|
536
568
|
let storedFmtVer = Number(await this.engine.getValue(`__ds-${this.id}-ver`, NaN));
|
|
537
569
|
if (typeof storedDataRaw !== "string") {
|
|
538
570
|
await this.saveDefaultData();
|
|
539
|
-
return
|
|
571
|
+
return this.engine.deepCopy(this.defaultData);
|
|
540
572
|
}
|
|
541
573
|
const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
|
|
542
574
|
const encodingFmt = String(await this.engine.getValue(`__ds-${this.id}-enf`, null));
|
|
@@ -551,23 +583,32 @@ var DataStore = class {
|
|
|
551
583
|
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
552
584
|
if (saveData)
|
|
553
585
|
await this.setData(parsed);
|
|
554
|
-
|
|
586
|
+
if (this.memoryCache)
|
|
587
|
+
return this.cachedData = this.engine.deepCopy(parsed);
|
|
588
|
+
else
|
|
589
|
+
return this.engine.deepCopy(parsed);
|
|
555
590
|
} catch (err) {
|
|
556
591
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
557
592
|
await this.saveDefaultData();
|
|
558
593
|
return this.defaultData;
|
|
559
594
|
}
|
|
560
595
|
}
|
|
596
|
+
//#region getData
|
|
561
597
|
/**
|
|
562
598
|
* Returns a copy of the data from the in-memory cache.
|
|
563
|
-
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
599
|
+
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
600
|
+
* ⚠️ If `memoryCache` was set to `false` in the constructor options, this method will throw an error.
|
|
564
601
|
*/
|
|
565
602
|
getData() {
|
|
603
|
+
if (!this.memoryCache)
|
|
604
|
+
throw new DatedError("In-memory cache is disabled for this DataStore instance, so getData() can't be used. Please use loadData() instead.");
|
|
566
605
|
return this.engine.deepCopy(this.cachedData);
|
|
567
606
|
}
|
|
607
|
+
//#region setData
|
|
568
608
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
569
609
|
setData(data) {
|
|
570
|
-
this.
|
|
610
|
+
if (this.memoryCache)
|
|
611
|
+
this.cachedData = data;
|
|
571
612
|
return new Promise(async (resolve) => {
|
|
572
613
|
await Promise.allSettled([
|
|
573
614
|
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
@@ -577,15 +618,18 @@ var DataStore = class {
|
|
|
577
618
|
resolve();
|
|
578
619
|
});
|
|
579
620
|
}
|
|
621
|
+
//#region saveDefaultData
|
|
580
622
|
/** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
|
|
581
623
|
async saveDefaultData() {
|
|
582
|
-
|
|
624
|
+
if (this.memoryCache)
|
|
625
|
+
this.cachedData = this.defaultData;
|
|
583
626
|
await Promise.allSettled([
|
|
584
627
|
this.engine.setValue(`__ds-${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
585
628
|
this.engine.setValue(`__ds-${this.id}-ver`, this.formatVersion),
|
|
586
629
|
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
|
|
587
630
|
]);
|
|
588
631
|
}
|
|
632
|
+
//#region deleteData
|
|
589
633
|
/**
|
|
590
634
|
* Call this method to clear all persistently stored data associated with this DataStore instance, including the storage container (if supported by the DataStoreEngine).
|
|
591
635
|
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
@@ -600,11 +644,12 @@ var DataStore = class {
|
|
|
600
644
|
]);
|
|
601
645
|
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
602
646
|
}
|
|
647
|
+
//#region encodingEnabled
|
|
603
648
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
604
649
|
encodingEnabled() {
|
|
605
650
|
return Boolean(this.encodeData && this.decodeData) && this.compressionFormat !== null || Boolean(this.compressionFormat);
|
|
606
651
|
}
|
|
607
|
-
//#region
|
|
652
|
+
//#region runMigrations
|
|
608
653
|
/**
|
|
609
654
|
* Runs all necessary migration functions consecutively and saves the result to the in-memory cache and persistent storage and also returns it.
|
|
610
655
|
* This method is automatically called by {@linkcode loadData()} if the data format has changed since the last time the data was saved.
|
|
@@ -638,8 +683,12 @@ var DataStore = class {
|
|
|
638
683
|
this.engine.setValue(`__ds-${this.id}-ver`, lastFmtVer),
|
|
639
684
|
this.engine.setValue(`__ds-${this.id}-enf`, this.compressionFormat)
|
|
640
685
|
]);
|
|
641
|
-
|
|
686
|
+
if (this.memoryCache)
|
|
687
|
+
return this.cachedData = this.engine.deepCopy(newData);
|
|
688
|
+
else
|
|
689
|
+
return this.engine.deepCopy(newData);
|
|
642
690
|
}
|
|
691
|
+
//#region migrateId
|
|
643
692
|
/**
|
|
644
693
|
* Tries to migrate the currently saved persistent data from one or more old IDs to the ID set in the constructor.
|
|
645
694
|
* If no data exist for the old ID(s), nothing will be done, but some time may still pass trying to fetch the non-existent data.
|
|
@@ -683,7 +732,7 @@ var DataStoreEngine = class {
|
|
|
683
732
|
this.dataStoreOptions = dataStoreOptions;
|
|
684
733
|
}
|
|
685
734
|
//#region serialization api
|
|
686
|
-
/** Serializes the given object to a string, optionally encoded with `options.encodeData` if {@linkcode useEncoding} is set to
|
|
735
|
+
/** Serializes the given object to a string, optionally encoded with `options.encodeData` if {@linkcode useEncoding} is not set to false and the `encodeData` and `decodeData` options are set */
|
|
687
736
|
async serializeData(data, useEncoding) {
|
|
688
737
|
var _a, _b, _c, _d, _e;
|
|
689
738
|
this.ensureDataStoreOptions();
|
|
@@ -731,7 +780,7 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
731
780
|
* Creates an instance of `BrowserStorageEngine`.
|
|
732
781
|
*
|
|
733
782
|
* - ⚠️ Requires a DOM environment
|
|
734
|
-
* - ⚠️ Don't reuse
|
|
783
|
+
* - ⚠️ Don't reuse engine instances, always create a new one for each {@linkcode DataStore} instance
|
|
735
784
|
*/
|
|
736
785
|
constructor(options) {
|
|
737
786
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
@@ -769,7 +818,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
769
818
|
* Creates an instance of `FileStorageEngine`.
|
|
770
819
|
*
|
|
771
820
|
* - ⚠️ Requires Node.js or Deno with Node compatibility (v1.31+)
|
|
772
|
-
* - ⚠️ Don't reuse
|
|
821
|
+
* - ⚠️ Don't reuse engine instances, always create a new one for each {@linkcode DataStore} instance
|
|
773
822
|
*/
|
|
774
823
|
constructor(options) {
|
|
775
824
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
@@ -787,7 +836,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
787
836
|
if (!fs)
|
|
788
837
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
789
838
|
if (!fs)
|
|
790
|
-
throw new
|
|
839
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
791
840
|
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
792
841
|
const data = await fs.readFile(path, "utf-8");
|
|
793
842
|
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;
|
|
@@ -803,7 +852,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
803
852
|
if (!fs)
|
|
804
853
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
805
854
|
if (!fs)
|
|
806
|
-
throw new
|
|
855
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
807
856
|
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
808
857
|
await fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
809
858
|
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");
|
|
@@ -862,9 +911,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
862
911
|
if (!fs)
|
|
863
912
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
864
913
|
if (!fs)
|
|
865
|
-
throw new
|
|
914
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
866
915
|
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
867
|
-
await fs.unlink(path);
|
|
916
|
+
return await fs.unlink(path);
|
|
868
917
|
} catch (err) {
|
|
869
918
|
console.error("Error deleting file:", err);
|
|
870
919
|
}
|
|
@@ -877,11 +926,12 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
877
926
|
options;
|
|
878
927
|
constructor(stores, options = {}) {
|
|
879
928
|
if (!crypto || !crypto.subtle)
|
|
880
|
-
throw new
|
|
929
|
+
throw new ScriptContextError("DataStoreSerializer has to run in a secure context (HTTPS) or in another environment that implements the subtleCrypto API!");
|
|
881
930
|
this.stores = stores;
|
|
882
931
|
this.options = {
|
|
883
932
|
addChecksum: true,
|
|
884
933
|
ensureIntegrity: true,
|
|
934
|
+
remapIds: {},
|
|
885
935
|
...options
|
|
886
936
|
};
|
|
887
937
|
}
|
|
@@ -898,9 +948,11 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
898
948
|
async serializePartial(stores, useEncoding = true, stringified = true) {
|
|
899
949
|
var _a;
|
|
900
950
|
const serData = [];
|
|
901
|
-
|
|
951
|
+
const filteredStores = this.stores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id));
|
|
952
|
+
for (const storeInst of filteredStores) {
|
|
902
953
|
const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
|
|
903
|
-
const
|
|
954
|
+
const rawData = storeInst.memoryCache ? storeInst.getData() : await storeInst.loadData();
|
|
955
|
+
const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : JSON.stringify(rawData);
|
|
904
956
|
serData.push({
|
|
905
957
|
id: storeInst.id,
|
|
906
958
|
data,
|
|
@@ -927,10 +979,18 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
927
979
|
const deserStores = typeof data === "string" ? JSON.parse(data) : data;
|
|
928
980
|
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
929
981
|
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
930
|
-
|
|
931
|
-
|
|
982
|
+
const resolveStoreId = (id) => {
|
|
983
|
+
var _a;
|
|
984
|
+
return ((_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0]) ?? id;
|
|
985
|
+
};
|
|
986
|
+
const matchesFilter = (id) => typeof stores === "function" ? stores(id) : stores.includes(id);
|
|
987
|
+
for (const storeData of deserStores) {
|
|
988
|
+
const effectiveId = resolveStoreId(storeData.id);
|
|
989
|
+
if (!matchesFilter(effectiveId))
|
|
990
|
+
continue;
|
|
991
|
+
const storeInst = this.stores.find((s) => s.id === effectiveId);
|
|
932
992
|
if (!storeInst)
|
|
933
|
-
throw new
|
|
993
|
+
throw new DatedError(`Can't deserialize data because no DataStore instance with the ID "${effectiveId}" was found! Make sure to provide it in the DataStoreSerializer constructor.`);
|
|
934
994
|
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
935
995
|
const checksum = await this.calcChecksum(storeData.data);
|
|
936
996
|
if (checksum !== storeData.checksum)
|
|
@@ -1101,11 +1161,12 @@ var NanoEmitter = class {
|
|
|
1101
1161
|
* `callback` (required) is the function that will be called when the conditions are met.
|
|
1102
1162
|
*
|
|
1103
1163
|
* 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.
|
|
1104
|
-
* If `signal` is provided, the subscription will be
|
|
1164
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
1105
1165
|
*
|
|
1106
1166
|
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
1107
1167
|
* 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.
|
|
1108
|
-
*
|
|
1168
|
+
* If both `oneOf` and `allOf` are used together, the callback will be called when any of the `oneOf` events are emitted AND all of the `allOf` events have been emitted at least once.
|
|
1169
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
1109
1170
|
*
|
|
1110
1171
|
* @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.
|
|
1111
1172
|
*/
|
|
@@ -1133,6 +1194,8 @@ var NanoEmitter = class {
|
|
|
1133
1194
|
} = optsWithDefaults;
|
|
1134
1195
|
if (signal == null ? void 0 : signal.aborted)
|
|
1135
1196
|
return unsubAll;
|
|
1197
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
1198
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1136
1199
|
const curEvtUnsubs = [];
|
|
1137
1200
|
const checkUnsubAllEvt = (force = false) => {
|
|
1138
1201
|
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
@@ -1142,34 +1205,31 @@ var NanoEmitter = class {
|
|
|
1142
1205
|
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
1143
1206
|
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
1144
1207
|
};
|
|
1208
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1209
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
1145
1210
|
for (const event of oneOf) {
|
|
1146
1211
|
const unsub = this.events.on(event, ((...args) => {
|
|
1147
1212
|
checkUnsubAllEvt();
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1213
|
+
if (allOfConditionMet()) {
|
|
1214
|
+
callback(event, ...args);
|
|
1215
|
+
if (once)
|
|
1216
|
+
checkUnsubAllEvt(true);
|
|
1217
|
+
}
|
|
1151
1218
|
}));
|
|
1152
1219
|
curEvtUnsubs.push(unsub);
|
|
1153
1220
|
}
|
|
1154
|
-
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1155
|
-
const checkAllOf = (event, ...args) => {
|
|
1156
|
-
checkUnsubAllEvt();
|
|
1157
|
-
allOfEmitted.add(event);
|
|
1158
|
-
if (allOfEmitted.size === allOf.length) {
|
|
1159
|
-
callback(event, ...args);
|
|
1160
|
-
if (once)
|
|
1161
|
-
checkUnsubAllEvt(true);
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
1221
|
for (const event of allOf) {
|
|
1165
1222
|
const unsub = this.events.on(event, ((...args) => {
|
|
1166
1223
|
checkUnsubAllEvt();
|
|
1167
|
-
|
|
1224
|
+
allOfEmitted.add(event);
|
|
1225
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
1226
|
+
callback(event, ...args);
|
|
1227
|
+
if (once)
|
|
1228
|
+
checkUnsubAllEvt(true);
|
|
1229
|
+
}
|
|
1168
1230
|
}));
|
|
1169
1231
|
curEvtUnsubs.push(unsub);
|
|
1170
1232
|
}
|
|
1171
|
-
if (oneOf.length === 0 && allOf.length === 0)
|
|
1172
|
-
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1173
1233
|
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
1174
1234
|
}
|
|
1175
1235
|
return unsubAll;
|
|
@@ -1304,6 +1364,7 @@ function debounce(fn, timeout = 200, type = "immediate") {
|
|
|
1304
1364
|
export {
|
|
1305
1365
|
BrowserStorageEngine,
|
|
1306
1366
|
ChecksumMismatchError,
|
|
1367
|
+
CustomError,
|
|
1307
1368
|
DataStore,
|
|
1308
1369
|
DataStoreEngine,
|
|
1309
1370
|
DataStoreSerializer,
|
|
@@ -1312,6 +1373,8 @@ export {
|
|
|
1312
1373
|
FileStorageEngine,
|
|
1313
1374
|
MigrationError,
|
|
1314
1375
|
NanoEmitter,
|
|
1376
|
+
NetworkError,
|
|
1377
|
+
ScriptContextError,
|
|
1315
1378
|
ValidationError,
|
|
1316
1379
|
abtoa,
|
|
1317
1380
|
atoab,
|
|
@@ -1331,6 +1394,7 @@ export {
|
|
|
1331
1394
|
digitCount,
|
|
1332
1395
|
fetchAdvanced,
|
|
1333
1396
|
formatNumber,
|
|
1397
|
+
getCallStack,
|
|
1334
1398
|
getListLength,
|
|
1335
1399
|
hexToRgb,
|
|
1336
1400
|
insertValues,
|