@sv443-network/coreutils 3.5.1 → 3.6.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 +10 -0
- package/README.md +1 -0
- package/dist/CoreUtils.cjs +70 -28
- package/dist/CoreUtils.min.cjs +4 -4
- package/dist/CoreUtils.min.mjs +4 -4
- package/dist/CoreUtils.min.umd.js +4 -4
- package/dist/CoreUtils.mjs +70 -28
- package/dist/CoreUtils.umd.js +528 -606
- package/dist/lib/DataStoreSerializer.d.ts +13 -8
- package/dist/lib/NanoEmitter.d.ts +16 -1
- package/dist/lib/misc.d.ts +17 -4
- package/dist/lib/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/CoreUtils.umd.js
CHANGED
|
@@ -16,41 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("CoreUtils",f)}else {g["CoreUtils"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports};
|
|
20
19
|
"use strict";
|
|
21
20
|
var __create = Object.create;
|
|
22
21
|
var __defProp = Object.defineProperty;
|
|
23
22
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
24
23
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
25
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
26
24
|
var __getProtoOf = Object.getPrototypeOf;
|
|
27
25
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
28
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
29
|
-
var __pow = Math.pow;
|
|
30
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
31
|
-
var __spreadValues = (a, b) => {
|
|
32
|
-
for (var prop in b || (b = {}))
|
|
33
|
-
if (__hasOwnProp.call(b, prop))
|
|
34
|
-
__defNormalProp(a, prop, b[prop]);
|
|
35
|
-
if (__getOwnPropSymbols)
|
|
36
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
37
|
-
if (__propIsEnum.call(b, prop))
|
|
38
|
-
__defNormalProp(a, prop, b[prop]);
|
|
39
|
-
}
|
|
40
|
-
return a;
|
|
41
|
-
};
|
|
42
|
-
var __objRest = (source, exclude) => {
|
|
43
|
-
var target = {};
|
|
44
|
-
for (var prop in source)
|
|
45
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
46
|
-
target[prop] = source[prop];
|
|
47
|
-
if (source != null && __getOwnPropSymbols)
|
|
48
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
49
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
50
|
-
target[prop] = source[prop];
|
|
51
|
-
}
|
|
52
|
-
return target;
|
|
53
|
-
};
|
|
54
26
|
var __export = (target, all) => {
|
|
55
27
|
for (var name in all)
|
|
56
28
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -72,27 +44,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
72
44
|
mod
|
|
73
45
|
));
|
|
74
46
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
75
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
76
|
-
var __async = (__this, __arguments, generator) => {
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
var fulfilled = (value) => {
|
|
79
|
-
try {
|
|
80
|
-
step(generator.next(value));
|
|
81
|
-
} catch (e) {
|
|
82
|
-
reject(e);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
var rejected = (value) => {
|
|
86
|
-
try {
|
|
87
|
-
step(generator.throw(value));
|
|
88
|
-
} catch (e) {
|
|
89
|
-
reject(e);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
93
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
94
|
-
});
|
|
95
|
-
};
|
|
96
47
|
|
|
97
48
|
// lib/index.ts
|
|
98
49
|
var lib_exports = {};
|
|
@@ -134,6 +85,7 @@ __export(lib_exports, {
|
|
|
134
85
|
formatNumber: () => formatNumber,
|
|
135
86
|
getCallStack: () => getCallStack,
|
|
136
87
|
getListLength: () => getListLength,
|
|
88
|
+
getterifyObj: () => getterifyObj,
|
|
137
89
|
hexToRgb: () => hexToRgb,
|
|
138
90
|
insertValues: () => insertValues,
|
|
139
91
|
joinArrayReadable: () => joinArrayReadable,
|
|
@@ -246,7 +198,7 @@ function randRange(...args) {
|
|
|
246
198
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
247
199
|
}
|
|
248
200
|
function roundFixed(num, fractionDigits) {
|
|
249
|
-
const scale =
|
|
201
|
+
const scale = 10 ** fractionDigits;
|
|
250
202
|
return Math.round(num * scale) / scale;
|
|
251
203
|
}
|
|
252
204
|
function valsWithin(a, b, dec = 1, withinRange = 0.5) {
|
|
@@ -310,7 +262,7 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
310
262
|
if (isHexCol)
|
|
311
263
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
312
264
|
else if (color.startsWith("rgba"))
|
|
313
|
-
return `rgba(${r}, ${g}, ${b}, ${a
|
|
265
|
+
return `rgba(${r}, ${g}, ${b}, ${a ?? NaN})`;
|
|
314
266
|
else
|
|
315
267
|
return `rgb(${r}, ${g}, ${b})`;
|
|
316
268
|
}
|
|
@@ -344,43 +296,35 @@ function abtoa(buf) {
|
|
|
344
296
|
function atoab(str) {
|
|
345
297
|
return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
|
|
346
298
|
}
|
|
347
|
-
function compress(input, compressionFormat, outputType = "string") {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const uintArr = new Uint8Array(yield new Response(comp.readable).arrayBuffer());
|
|
356
|
-
return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
|
|
357
|
-
});
|
|
299
|
+
async function compress(input, compressionFormat, outputType = "string") {
|
|
300
|
+
const byteArray = input instanceof Uint8Array ? input : new TextEncoder().encode((input == null ? void 0 : input.toString()) ?? String(input));
|
|
301
|
+
const comp = new CompressionStream(compressionFormat);
|
|
302
|
+
const writer = comp.writable.getWriter();
|
|
303
|
+
writer.write(byteArray);
|
|
304
|
+
writer.close();
|
|
305
|
+
const uintArr = new Uint8Array(await new Response(comp.readable).arrayBuffer());
|
|
306
|
+
return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
|
|
358
307
|
}
|
|
359
|
-
function decompress(input, compressionFormat, outputType = "string") {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const uintArr = new Uint8Array(yield new Response(decomp.readable).arrayBuffer());
|
|
368
|
-
return outputType === "arrayBuffer" ? uintArr : new TextDecoder().decode(uintArr);
|
|
369
|
-
});
|
|
308
|
+
async function decompress(input, compressionFormat, outputType = "string") {
|
|
309
|
+
const byteArray = input instanceof Uint8Array ? input : atoab((input == null ? void 0 : input.toString()) ?? String(input));
|
|
310
|
+
const decomp = new DecompressionStream(compressionFormat);
|
|
311
|
+
const writer = decomp.writable.getWriter();
|
|
312
|
+
writer.write(byteArray);
|
|
313
|
+
writer.close();
|
|
314
|
+
const uintArr = new Uint8Array(await new Response(decomp.readable).arrayBuffer());
|
|
315
|
+
return outputType === "arrayBuffer" ? uintArr : new TextDecoder().decode(uintArr);
|
|
370
316
|
}
|
|
371
|
-
function computeHash(input, algorithm = "SHA-256") {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
return hashHex;
|
|
383
|
-
});
|
|
317
|
+
async function computeHash(input, algorithm = "SHA-256") {
|
|
318
|
+
let data;
|
|
319
|
+
if (typeof input === "string") {
|
|
320
|
+
const encoder = new TextEncoder();
|
|
321
|
+
data = encoder.encode(input);
|
|
322
|
+
} else
|
|
323
|
+
data = input;
|
|
324
|
+
const hashBuffer = await crypto.subtle.digest(algorithm, data);
|
|
325
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
326
|
+
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
327
|
+
return hashHex;
|
|
384
328
|
}
|
|
385
329
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
386
330
|
if (length < 1)
|
|
@@ -409,9 +353,9 @@ function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase =
|
|
|
409
353
|
|
|
410
354
|
// lib/Errors.ts
|
|
411
355
|
var DatedError = class extends Error {
|
|
356
|
+
date;
|
|
412
357
|
constructor(message, options) {
|
|
413
358
|
super(message, options);
|
|
414
|
-
__publicField(this, "date");
|
|
415
359
|
this.name = this.constructor.name;
|
|
416
360
|
this.date = /* @__PURE__ */ new Date();
|
|
417
361
|
}
|
|
@@ -454,37 +398,34 @@ var NetworkError = class extends DatedError {
|
|
|
454
398
|
};
|
|
455
399
|
|
|
456
400
|
// lib/misc.ts
|
|
457
|
-
function consumeGen(valGen) {
|
|
458
|
-
return
|
|
459
|
-
return yield typeof valGen === "function" ? valGen() : valGen;
|
|
460
|
-
});
|
|
401
|
+
async function consumeGen(valGen, ...args) {
|
|
402
|
+
return await (typeof valGen === "function" ? valGen(...args) : valGen);
|
|
461
403
|
}
|
|
462
|
-
function consumeStringGen(strGen) {
|
|
463
|
-
return
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
);
|
|
467
|
-
});
|
|
404
|
+
async function consumeStringGen(strGen, ...args) {
|
|
405
|
+
return typeof strGen === "string" ? strGen : String(
|
|
406
|
+
typeof strGen === "function" ? await strGen(...args) : strGen
|
|
407
|
+
);
|
|
468
408
|
}
|
|
469
|
-
function fetchAdvanced(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
409
|
+
async function fetchAdvanced(input, options = {}) {
|
|
410
|
+
const { timeout = 1e4, signal, ...restOpts } = options;
|
|
411
|
+
const ctl = new AbortController();
|
|
412
|
+
signal == null ? void 0 : signal.addEventListener("abort", () => ctl.abort());
|
|
413
|
+
let sigOpts = {}, id = void 0;
|
|
414
|
+
if (timeout >= 0) {
|
|
415
|
+
id = setTimeout(() => ctl.abort(), timeout);
|
|
416
|
+
sigOpts = { signal: ctl.signal };
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const res = await fetch(input, {
|
|
420
|
+
...restOpts,
|
|
421
|
+
...sigOpts
|
|
422
|
+
});
|
|
423
|
+
typeof id !== "undefined" && clearTimeout(id);
|
|
424
|
+
return res;
|
|
425
|
+
} catch (err) {
|
|
426
|
+
typeof id !== "undefined" && clearTimeout(id);
|
|
427
|
+
throw new NetworkError("Error while calling fetch", { cause: err });
|
|
428
|
+
}
|
|
488
429
|
}
|
|
489
430
|
function getListLength(listLike, zeroOnInvalid = true) {
|
|
490
431
|
return "length" in listLike ? listLike.length : "size" in listLike ? listLike.size : "count" in listLike ? listLike.count : zeroOnInvalid ? 0 : NaN;
|
|
@@ -499,7 +440,18 @@ function pauseFor(time, signal, rejectOnAbort = false) {
|
|
|
499
440
|
});
|
|
500
441
|
}
|
|
501
442
|
function pureObj(obj) {
|
|
502
|
-
return Object.assign(/* @__PURE__ */ Object.create(null), obj
|
|
443
|
+
return Object.assign(/* @__PURE__ */ Object.create(null), obj ?? {});
|
|
444
|
+
}
|
|
445
|
+
function getterifyObj(obj, asCopy = false) {
|
|
446
|
+
const newObj = {};
|
|
447
|
+
for (const key in obj) {
|
|
448
|
+
Object.defineProperty(newObj, key, {
|
|
449
|
+
get: () => obj[key],
|
|
450
|
+
enumerable: true,
|
|
451
|
+
configurable: true
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return asCopy ? structuredClone(newObj) : newObj;
|
|
503
455
|
}
|
|
504
456
|
function setImmediateInterval(callback, interval, signal) {
|
|
505
457
|
let intervalId;
|
|
@@ -516,12 +468,12 @@ function setImmediateInterval(callback, interval, signal) {
|
|
|
516
468
|
function setImmediateTimeoutLoop(callback, interval, signal) {
|
|
517
469
|
let timeout;
|
|
518
470
|
const cleanup = () => clearTimeout(timeout);
|
|
519
|
-
const loop = () =>
|
|
471
|
+
const loop = async () => {
|
|
520
472
|
if (signal == null ? void 0 : signal.aborted)
|
|
521
473
|
return cleanup();
|
|
522
|
-
|
|
474
|
+
await callback();
|
|
523
475
|
timeout = setTimeout(loop, interval);
|
|
524
|
-
}
|
|
476
|
+
};
|
|
525
477
|
signal == null ? void 0 : signal.addEventListener("abort", cleanup);
|
|
526
478
|
loop();
|
|
527
479
|
}
|
|
@@ -538,13 +490,12 @@ function scheduleExit(code = 0, timeout = 0) {
|
|
|
538
490
|
setTimeout(exit, timeout);
|
|
539
491
|
}
|
|
540
492
|
function getCallStack(asArray, lines = Infinity) {
|
|
541
|
-
var _a;
|
|
542
493
|
if (typeof lines !== "number" || isNaN(lines) || lines < 0)
|
|
543
494
|
throw new TypeError("lines parameter must be a non-negative number");
|
|
544
495
|
try {
|
|
545
496
|
throw new CustomError("GetCallStack", "Capturing a stack trace with CoreUtils.getCallStack(). If you see this anywhere, you can safely ignore it.");
|
|
546
497
|
} catch (err) {
|
|
547
|
-
const stack = (
|
|
498
|
+
const stack = (err.stack ?? "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
|
|
548
499
|
return asArray !== false ? stack : stack.join("\n");
|
|
549
500
|
}
|
|
550
501
|
}
|
|
@@ -555,22 +506,22 @@ function createRecurringTask(options) {
|
|
|
555
506
|
(_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
|
|
556
507
|
aborted = true;
|
|
557
508
|
}, { once: true });
|
|
558
|
-
const runRecurringTask = (initial = false) =>
|
|
559
|
-
var _a2
|
|
509
|
+
const runRecurringTask = async (initial = false) => {
|
|
510
|
+
var _a2;
|
|
560
511
|
if (aborted)
|
|
561
512
|
return;
|
|
562
513
|
try {
|
|
563
|
-
if ((
|
|
514
|
+
if ((options.immediate ?? true) || !initial) {
|
|
564
515
|
iterations++;
|
|
565
|
-
if (
|
|
566
|
-
const val =
|
|
516
|
+
if (await ((_a2 = options.condition) == null ? void 0 : _a2.call(options, iterations - 1)) ?? true) {
|
|
517
|
+
const val = await options.task(iterations - 1);
|
|
567
518
|
if (options.onSuccess)
|
|
568
|
-
|
|
519
|
+
await options.onSuccess(val, iterations - 1);
|
|
569
520
|
}
|
|
570
521
|
}
|
|
571
522
|
} catch (err) {
|
|
572
523
|
if (options.onError)
|
|
573
|
-
|
|
524
|
+
await options.onError(err, iterations - 1);
|
|
574
525
|
if (options.abortOnError)
|
|
575
526
|
aborted = true;
|
|
576
527
|
if (!options.onError && !options.abortOnError)
|
|
@@ -578,7 +529,7 @@ function createRecurringTask(options) {
|
|
|
578
529
|
}
|
|
579
530
|
if (!aborted && (typeof options.maxIterations !== "number" || iterations < options.maxIterations))
|
|
580
531
|
setTimeout(runRecurringTask, options.timeout);
|
|
581
|
-
}
|
|
532
|
+
};
|
|
582
533
|
return runRecurringTask(true);
|
|
583
534
|
}
|
|
584
535
|
|
|
@@ -632,9 +583,9 @@ function createProgressBar(percentage, barLength, chars = defaultPbChars) {
|
|
|
632
583
|
}
|
|
633
584
|
function insertValues(input, ...values) {
|
|
634
585
|
return input.replace(/%\d/gm, (match) => {
|
|
635
|
-
var _a
|
|
586
|
+
var _a;
|
|
636
587
|
const argIndex = Number(match.substring(1)) - 1;
|
|
637
|
-
return (
|
|
588
|
+
return (_a = values[argIndex] ?? match) == null ? void 0 : _a.toString();
|
|
638
589
|
});
|
|
639
590
|
}
|
|
640
591
|
function joinArrayReadable(array, separators = ", ", lastSeparator = " and ") {
|
|
@@ -665,8 +616,7 @@ function secsToTimeStr(seconds) {
|
|
|
665
616
|
].join("");
|
|
666
617
|
}
|
|
667
618
|
function truncStr(input, length, endStr = "...") {
|
|
668
|
-
|
|
669
|
-
const str = (_a = input == null ? void 0 : input.toString()) != null ? _a : String(input);
|
|
619
|
+
const str = (input == null ? void 0 : input.toString()) ?? String(input);
|
|
670
620
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
671
621
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
672
622
|
}
|
|
@@ -712,8 +662,8 @@ var defaultTableLineCharset = {
|
|
|
712
662
|
}
|
|
713
663
|
};
|
|
714
664
|
function createTable(rows, options) {
|
|
715
|
-
var _a
|
|
716
|
-
const opts =
|
|
665
|
+
var _a;
|
|
666
|
+
const opts = {
|
|
717
667
|
columnAlign: "left",
|
|
718
668
|
truncateAbove: Infinity,
|
|
719
669
|
truncEndStr: "\u2026",
|
|
@@ -721,15 +671,16 @@ function createTable(rows, options) {
|
|
|
721
671
|
lineStyle: "single",
|
|
722
672
|
applyCellStyle: () => void 0,
|
|
723
673
|
applyLineStyle: () => void 0,
|
|
724
|
-
lineCharset: defaultTableLineCharset
|
|
725
|
-
|
|
674
|
+
lineCharset: defaultTableLineCharset,
|
|
675
|
+
...options ?? {}
|
|
676
|
+
};
|
|
726
677
|
const defRange = (val, min, max) => clamp(typeof val !== "number" || isNaN(Number(val)) ? min : val, min, max);
|
|
727
678
|
opts.truncateAbove = defRange(opts.truncateAbove, 0, Infinity);
|
|
728
679
|
opts.minPadding = defRange(opts.minPadding, 0, Infinity);
|
|
729
680
|
const lnCh = opts.lineCharset[opts.lineStyle];
|
|
730
681
|
const stripAnsi = (str) => str.replace(/\u001b\[[0-9;]*m/g, "");
|
|
731
682
|
const stringRows = rows.map((row) => row.map((cell) => String(cell)));
|
|
732
|
-
const colCount = (
|
|
683
|
+
const colCount = ((_a = rows[0]) == null ? void 0 : _a.length) ?? 0;
|
|
733
684
|
if (colCount === 0 || stringRows.length === 0)
|
|
734
685
|
return "";
|
|
735
686
|
if (isFinite(opts.truncateAbove)) {
|
|
@@ -765,28 +716,23 @@ function createTable(rows, options) {
|
|
|
765
716
|
};
|
|
766
717
|
for (const row of stringRows)
|
|
767
718
|
for (let j = 0; j < row.length; j++)
|
|
768
|
-
if (stripAnsi(
|
|
769
|
-
row[j] = truncAnsi(
|
|
719
|
+
if (stripAnsi(row[j] ?? "").length > opts.truncateAbove)
|
|
720
|
+
row[j] = truncAnsi(row[j] ?? "", opts.truncateAbove, opts.truncEndStr);
|
|
770
721
|
}
|
|
771
722
|
const colWidths = Array.from(
|
|
772
723
|
{ length: colCount },
|
|
773
|
-
(_, j) => Math.max(0, ...stringRows.map((row) =>
|
|
774
|
-
var _a2;
|
|
775
|
-
return stripAnsi((_a2 = row[j]) != null ? _a2 : "").length;
|
|
776
|
-
}))
|
|
724
|
+
(_, j) => Math.max(0, ...stringRows.map((row) => stripAnsi(row[j] ?? "").length))
|
|
777
725
|
);
|
|
778
726
|
const applyLn = (i, j, ch) => {
|
|
779
|
-
|
|
780
|
-
const [before = "", after = ""] = (_a2 = opts.applyLineStyle(i, j)) != null ? _a2 : [];
|
|
727
|
+
const [before = "", after = ""] = opts.applyLineStyle(i, j) ?? [];
|
|
781
728
|
return `${before}${ch}${after}`;
|
|
782
729
|
};
|
|
783
730
|
const buildBorderRow = (lineIdx, leftCh, midCh, rightCh) => {
|
|
784
|
-
var _a2;
|
|
785
731
|
let result = "";
|
|
786
732
|
let j = 0;
|
|
787
733
|
result += applyLn(lineIdx, j++, leftCh);
|
|
788
734
|
for (let col = 0; col < colCount; col++) {
|
|
789
|
-
const cellWidth = (
|
|
735
|
+
const cellWidth = (colWidths[col] ?? 0) + opts.minPadding * 2;
|
|
790
736
|
for (let ci = 0; ci < cellWidth; ci++)
|
|
791
737
|
result += applyLn(lineIdx, j++, lnCh.horizontal);
|
|
792
738
|
if (col < colCount - 1)
|
|
@@ -797,7 +743,7 @@ function createTable(rows, options) {
|
|
|
797
743
|
};
|
|
798
744
|
const lines = [];
|
|
799
745
|
for (let rowIdx = 0; rowIdx < stringRows.length; rowIdx++) {
|
|
800
|
-
const row =
|
|
746
|
+
const row = stringRows[rowIdx] ?? [];
|
|
801
747
|
const lineIdxBase = rowIdx * 3;
|
|
802
748
|
if (opts.lineStyle !== "none") {
|
|
803
749
|
lines.push(
|
|
@@ -808,10 +754,10 @@ function createTable(rows, options) {
|
|
|
808
754
|
let j = 0;
|
|
809
755
|
contentLine += applyLn(lineIdxBase + 1, j++, lnCh.vertical);
|
|
810
756
|
for (let colIdx = 0; colIdx < colCount; colIdx++) {
|
|
811
|
-
const cell =
|
|
757
|
+
const cell = row[colIdx] ?? "";
|
|
812
758
|
const visLen = stripAnsi(cell).length;
|
|
813
|
-
const extra = (
|
|
814
|
-
const align = (
|
|
759
|
+
const extra = (colWidths[colIdx] ?? 0) - visLen;
|
|
760
|
+
const align = (Array.isArray(opts.columnAlign) ? opts.columnAlign[colIdx] : opts.columnAlign) ?? "left";
|
|
815
761
|
let leftPad;
|
|
816
762
|
let rightPad;
|
|
817
763
|
switch (align) {
|
|
@@ -831,7 +777,7 @@ function createTable(rows, options) {
|
|
|
831
777
|
leftPad = opts.minPadding;
|
|
832
778
|
rightPad = opts.minPadding + extra;
|
|
833
779
|
}
|
|
834
|
-
const [cellBefore = "", cellAfter = ""] =
|
|
780
|
+
const [cellBefore = "", cellAfter = ""] = opts.applyCellStyle(rowIdx, colIdx) ?? [];
|
|
835
781
|
contentLine += " ".repeat(leftPad) + cellBefore + cell + cellAfter + " ".repeat(rightPad);
|
|
836
782
|
contentLine += applyLn(lineIdxBase + 1, j++, lnCh.vertical);
|
|
837
783
|
}
|
|
@@ -851,26 +797,40 @@ var createNanoEvents = () => ({
|
|
|
851
797
|
},
|
|
852
798
|
events: {},
|
|
853
799
|
on(event, cb) {
|
|
854
|
-
var _a;
|
|
855
800
|
;
|
|
856
|
-
(
|
|
801
|
+
(this.events[event] ||= []).push(cb);
|
|
857
802
|
return () => {
|
|
858
|
-
var
|
|
859
|
-
this.events[event] = (
|
|
803
|
+
var _a;
|
|
804
|
+
this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
|
|
860
805
|
};
|
|
861
806
|
}
|
|
862
807
|
});
|
|
863
808
|
|
|
864
809
|
// lib/NanoEmitter.ts
|
|
865
810
|
var NanoEmitter = class {
|
|
811
|
+
events = createNanoEvents();
|
|
812
|
+
eventUnsubscribes = [];
|
|
813
|
+
emitterOptions;
|
|
814
|
+
/** Stores the last arguments for each event listed in `catchUpEvents` */
|
|
815
|
+
catchUpMemory = /* @__PURE__ */ new Map();
|
|
866
816
|
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
867
817
|
constructor(options = {}) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
818
|
+
this.emitterOptions = {
|
|
819
|
+
publicEmit: false,
|
|
820
|
+
...options
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
//#region emitEvent
|
|
824
|
+
/**
|
|
825
|
+
* Emits an event on this instance, bypassing the `publicEmit` guard.
|
|
826
|
+
* Prefer this over `this.events.emit()` in subclasses — it updates catch-up memory
|
|
827
|
+
* for any event listed in `catchUpEvents` so late listeners can still receive the last value.
|
|
828
|
+
*/
|
|
829
|
+
emitEvent(event, ...args) {
|
|
830
|
+
var _a;
|
|
831
|
+
if ((_a = this.emitterOptions.catchUpEvents) == null ? void 0 : _a.includes(event))
|
|
832
|
+
this.catchUpMemory.set(event, args);
|
|
833
|
+
this.events.emit(event, ...args);
|
|
874
834
|
}
|
|
875
835
|
//#region on
|
|
876
836
|
/**
|
|
@@ -904,6 +864,9 @@ var NanoEmitter = class {
|
|
|
904
864
|
};
|
|
905
865
|
unsub = this.events.on(event, cb);
|
|
906
866
|
this.eventUnsubscribes.push(unsub);
|
|
867
|
+
const memory = this.catchUpMemory.get(event);
|
|
868
|
+
if (memory)
|
|
869
|
+
cb(...memory);
|
|
907
870
|
return unsubProxy;
|
|
908
871
|
}
|
|
909
872
|
//#region once
|
|
@@ -926,6 +889,12 @@ var NanoEmitter = class {
|
|
|
926
889
|
* ```
|
|
927
890
|
*/
|
|
928
891
|
once(event, cb) {
|
|
892
|
+
const memory = this.catchUpMemory.get(event);
|
|
893
|
+
if (memory) {
|
|
894
|
+
const args = memory;
|
|
895
|
+
cb == null ? void 0 : cb(...args);
|
|
896
|
+
return Promise.resolve(args);
|
|
897
|
+
}
|
|
929
898
|
return new Promise((resolve) => {
|
|
930
899
|
let unsub;
|
|
931
900
|
const onceProxy = ((...args) => {
|
|
@@ -962,11 +931,12 @@ var NanoEmitter = class {
|
|
|
962
931
|
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
963
932
|
};
|
|
964
933
|
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
965
|
-
const optsWithDefaults =
|
|
934
|
+
const optsWithDefaults = {
|
|
966
935
|
allOf: [],
|
|
967
936
|
oneOf: [],
|
|
968
|
-
once: false
|
|
969
|
-
|
|
937
|
+
once: false,
|
|
938
|
+
...opts
|
|
939
|
+
};
|
|
970
940
|
const {
|
|
971
941
|
oneOf,
|
|
972
942
|
allOf,
|
|
@@ -1026,7 +996,7 @@ var NanoEmitter = class {
|
|
|
1026
996
|
*/
|
|
1027
997
|
emit(event, ...args) {
|
|
1028
998
|
if (this.emitterOptions.publicEmit) {
|
|
1029
|
-
this.
|
|
999
|
+
this.emitEvent(event, ...args);
|
|
1030
1000
|
return true;
|
|
1031
1001
|
}
|
|
1032
1002
|
return false;
|
|
@@ -1043,6 +1013,26 @@ var NanoEmitter = class {
|
|
|
1043
1013
|
// lib/DataStore.ts
|
|
1044
1014
|
var dsFmtVer = 1;
|
|
1045
1015
|
var DataStore = class extends NanoEmitter {
|
|
1016
|
+
id;
|
|
1017
|
+
formatVersion;
|
|
1018
|
+
defaultData;
|
|
1019
|
+
encodeData;
|
|
1020
|
+
decodeData;
|
|
1021
|
+
compressionFormat = "deflate-raw";
|
|
1022
|
+
memoryCache;
|
|
1023
|
+
engine;
|
|
1024
|
+
keyPrefix;
|
|
1025
|
+
options;
|
|
1026
|
+
/**
|
|
1027
|
+
* Whether all first-init checks should be done.
|
|
1028
|
+
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
1029
|
+
* 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.
|
|
1030
|
+
*/
|
|
1031
|
+
firstInit = true;
|
|
1032
|
+
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
1033
|
+
cachedData;
|
|
1034
|
+
migrations;
|
|
1035
|
+
migrateIds = [];
|
|
1046
1036
|
//#region constructor
|
|
1047
1037
|
/**
|
|
1048
1038
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -1054,43 +1044,22 @@ var DataStore = class extends NanoEmitter {
|
|
|
1054
1044
|
* @param opts The options for this DataStore instance
|
|
1055
1045
|
*/
|
|
1056
1046
|
constructor(opts) {
|
|
1057
|
-
var _a, _b, _c;
|
|
1058
1047
|
super(opts.nanoEmitterOptions);
|
|
1059
|
-
__publicField(this, "id");
|
|
1060
|
-
__publicField(this, "formatVersion");
|
|
1061
|
-
__publicField(this, "defaultData");
|
|
1062
|
-
__publicField(this, "encodeData");
|
|
1063
|
-
__publicField(this, "decodeData");
|
|
1064
|
-
__publicField(this, "compressionFormat", "deflate-raw");
|
|
1065
|
-
__publicField(this, "memoryCache");
|
|
1066
|
-
__publicField(this, "engine");
|
|
1067
|
-
__publicField(this, "keyPrefix");
|
|
1068
|
-
__publicField(this, "options");
|
|
1069
|
-
/**
|
|
1070
|
-
* Whether all first-init checks should be done.
|
|
1071
|
-
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
1072
|
-
* 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.
|
|
1073
|
-
*/
|
|
1074
|
-
__publicField(this, "firstInit", true);
|
|
1075
|
-
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
1076
|
-
__publicField(this, "cachedData");
|
|
1077
|
-
__publicField(this, "migrations");
|
|
1078
|
-
__publicField(this, "migrateIds", []);
|
|
1079
1048
|
this.id = opts.id;
|
|
1080
1049
|
this.formatVersion = opts.formatVersion;
|
|
1081
1050
|
this.defaultData = opts.defaultData;
|
|
1082
|
-
this.memoryCache =
|
|
1051
|
+
this.memoryCache = opts.memoryCache ?? true;
|
|
1083
1052
|
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
1084
1053
|
this.migrations = opts.migrations;
|
|
1085
1054
|
if (opts.migrateIds)
|
|
1086
1055
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
1087
1056
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
1088
|
-
this.keyPrefix =
|
|
1057
|
+
this.keyPrefix = opts.keyPrefix ?? "__ds-";
|
|
1089
1058
|
this.options = opts;
|
|
1090
1059
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
1091
1060
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
1092
1061
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
1093
|
-
this.compressionFormat =
|
|
1062
|
+
this.compressionFormat = opts.encodeData[0] ?? null;
|
|
1094
1063
|
} else if (opts.compressionFormat === null) {
|
|
1095
1064
|
this.encodeData = void 0;
|
|
1096
1065
|
this.decodeData = void 0;
|
|
@@ -1098,12 +1067,8 @@ var DataStore = class extends NanoEmitter {
|
|
|
1098
1067
|
} else {
|
|
1099
1068
|
const fmt = typeof opts.compressionFormat === "string" ? opts.compressionFormat : "deflate-raw";
|
|
1100
1069
|
this.compressionFormat = fmt;
|
|
1101
|
-
this.encodeData = [fmt, (data) =>
|
|
1102
|
-
|
|
1103
|
-
})];
|
|
1104
|
-
this.decodeData = [fmt, (data) => __async(this, null, function* () {
|
|
1105
|
-
return yield decompress(data, fmt, "string");
|
|
1106
|
-
})];
|
|
1070
|
+
this.encodeData = [fmt, async (data) => await compress(data, fmt, "string")];
|
|
1071
|
+
this.decodeData = [fmt, async (data) => await decompress(data, fmt, "string")];
|
|
1107
1072
|
}
|
|
1108
1073
|
this.engine.setDataStoreOptions({
|
|
1109
1074
|
id: this.id,
|
|
@@ -1117,65 +1082,62 @@ var DataStore = class extends NanoEmitter {
|
|
|
1117
1082
|
* Automatically populates persistent storage with default data if it doesn't contain any data yet.
|
|
1118
1083
|
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
1119
1084
|
*/
|
|
1120
|
-
loadData() {
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
migrateFmt(`
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
1143
|
-
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
1144
|
-
}
|
|
1145
|
-
yield Promise.allSettled(promises);
|
|
1085
|
+
async loadData() {
|
|
1086
|
+
try {
|
|
1087
|
+
if (this.firstInit) {
|
|
1088
|
+
this.firstInit = false;
|
|
1089
|
+
const dsVer = Number(await this.engine.getValue("__ds_fmt_ver", 0));
|
|
1090
|
+
const oldData = await this.engine.getValue(`_uucfg-${this.id}`, null);
|
|
1091
|
+
if (oldData) {
|
|
1092
|
+
const oldVer = Number(await this.engine.getValue(`_uucfgver-${this.id}`, NaN));
|
|
1093
|
+
const oldEnc = await this.engine.getValue(`_uucfgenc-${this.id}`, null);
|
|
1094
|
+
const promises = [];
|
|
1095
|
+
const migrateFmt = (oldKey, newKey, value) => {
|
|
1096
|
+
promises.push(this.engine.setValue(newKey, value));
|
|
1097
|
+
promises.push(this.engine.deleteValue(oldKey));
|
|
1098
|
+
};
|
|
1099
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
1100
|
+
if (!isNaN(oldVer))
|
|
1101
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
1102
|
+
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
1103
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? this.compressionFormat ?? null : null);
|
|
1104
|
+
else {
|
|
1105
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
1106
|
+
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
1146
1107
|
}
|
|
1147
|
-
|
|
1148
|
-
yield this.engine.setValue("__ds_fmt_ver", dsFmtVer);
|
|
1108
|
+
await Promise.allSettled(promises);
|
|
1149
1109
|
}
|
|
1150
|
-
if (
|
|
1151
|
-
|
|
1152
|
-
this.migrateIds = [];
|
|
1153
|
-
}
|
|
1154
|
-
const storedDataRaw = yield this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
1155
|
-
const storedFmtVer = Number(yield this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
1156
|
-
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
1157
|
-
yield this.saveDefaultData(false);
|
|
1158
|
-
const data = this.engine.deepCopy(this.defaultData);
|
|
1159
|
-
this.events.emit("loadData", data);
|
|
1160
|
-
return data;
|
|
1161
|
-
}
|
|
1162
|
-
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
1163
|
-
const encodingFmt = String(yield this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
1164
|
-
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
|
|
1165
|
-
let parsed = typeof storedData === "string" ? yield this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
1166
|
-
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
1167
|
-
parsed = yield this.runMigrations(parsed, storedFmtVer);
|
|
1168
|
-
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
1169
|
-
this.events.emit("loadData", result);
|
|
1170
|
-
return result;
|
|
1171
|
-
} catch (err) {
|
|
1172
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1173
|
-
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
1174
|
-
this.events.emit("error", error);
|
|
1175
|
-
yield this.saveDefaultData();
|
|
1176
|
-
return this.defaultData;
|
|
1110
|
+
if (isNaN(dsVer) || dsVer < dsFmtVer)
|
|
1111
|
+
await this.engine.setValue("__ds_fmt_ver", dsFmtVer);
|
|
1177
1112
|
}
|
|
1178
|
-
|
|
1113
|
+
if (this.migrateIds.length > 0) {
|
|
1114
|
+
await this.migrateId(this.migrateIds);
|
|
1115
|
+
this.migrateIds = [];
|
|
1116
|
+
}
|
|
1117
|
+
const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
1118
|
+
const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
1119
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
1120
|
+
await this.saveDefaultData(false);
|
|
1121
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
1122
|
+
this.emitEvent("loadData", data);
|
|
1123
|
+
return data;
|
|
1124
|
+
}
|
|
1125
|
+
const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
|
|
1126
|
+
const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
1127
|
+
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
|
|
1128
|
+
let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
1129
|
+
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
1130
|
+
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
1131
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
1132
|
+
this.emitEvent("loadData", result);
|
|
1133
|
+
return result;
|
|
1134
|
+
} catch (err) {
|
|
1135
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1136
|
+
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
1137
|
+
this.emitEvent("error", error);
|
|
1138
|
+
await this.saveDefaultData();
|
|
1139
|
+
return this.defaultData;
|
|
1140
|
+
}
|
|
1179
1141
|
}
|
|
1180
1142
|
//#region getData
|
|
1181
1143
|
/**
|
|
@@ -1194,46 +1156,44 @@ var DataStore = class extends NanoEmitter {
|
|
|
1194
1156
|
const dataCopy = this.engine.deepCopy(data);
|
|
1195
1157
|
if (this.memoryCache) {
|
|
1196
1158
|
this.cachedData = data;
|
|
1197
|
-
this.
|
|
1159
|
+
this.emitEvent("updateDataSync", dataCopy);
|
|
1198
1160
|
}
|
|
1199
|
-
return new Promise((resolve) =>
|
|
1200
|
-
const results =
|
|
1201
|
-
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`,
|
|
1161
|
+
return new Promise(async (resolve) => {
|
|
1162
|
+
const results = await Promise.allSettled([
|
|
1163
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
1202
1164
|
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1203
1165
|
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1204
1166
|
]);
|
|
1205
1167
|
if (results.every((r) => r.status === "fulfilled"))
|
|
1206
|
-
this.
|
|
1168
|
+
this.emitEvent("updateData", dataCopy);
|
|
1207
1169
|
else {
|
|
1208
1170
|
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1209
1171
|
console.error(error);
|
|
1210
|
-
this.
|
|
1172
|
+
this.emitEvent("error", error);
|
|
1211
1173
|
}
|
|
1212
1174
|
resolve();
|
|
1213
|
-
})
|
|
1175
|
+
});
|
|
1214
1176
|
}
|
|
1215
1177
|
//#region saveDefaultData
|
|
1216
1178
|
/**
|
|
1217
1179
|
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
1218
1180
|
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
1219
1181
|
*/
|
|
1220
|
-
saveDefaultData(emitEvent = true) {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
});
|
|
1182
|
+
async saveDefaultData(emitEvent = true) {
|
|
1183
|
+
if (this.memoryCache)
|
|
1184
|
+
this.cachedData = this.defaultData;
|
|
1185
|
+
const results = await Promise.allSettled([
|
|
1186
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
1187
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1188
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1189
|
+
]);
|
|
1190
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
1191
|
+
emitEvent && this.emitEvent("setDefaultData", this.defaultData);
|
|
1192
|
+
else {
|
|
1193
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1194
|
+
console.error(error);
|
|
1195
|
+
this.emitEvent("error", error);
|
|
1196
|
+
}
|
|
1237
1197
|
}
|
|
1238
1198
|
//#region deleteData
|
|
1239
1199
|
/**
|
|
@@ -1241,17 +1201,15 @@ var DataStore = class extends NanoEmitter {
|
|
|
1241
1201
|
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
1242
1202
|
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
1243
1203
|
*/
|
|
1244
|
-
deleteData() {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
this.events.emit("deleteData");
|
|
1254
|
-
});
|
|
1204
|
+
async deleteData() {
|
|
1205
|
+
var _a, _b;
|
|
1206
|
+
await Promise.allSettled([
|
|
1207
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
1208
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
1209
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
1210
|
+
]);
|
|
1211
|
+
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
1212
|
+
this.emitEvent("deleteData");
|
|
1255
1213
|
}
|
|
1256
1214
|
//#region encodingEnabled
|
|
1257
1215
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
@@ -1266,83 +1224,79 @@ var DataStore = class extends NanoEmitter {
|
|
|
1266
1224
|
*
|
|
1267
1225
|
* If one of the migrations fails, the data will be reset to the default value if `resetOnError` is set to `true` (default). Otherwise, an error will be thrown and no data will be saved.
|
|
1268
1226
|
*/
|
|
1269
|
-
runMigrations(oldData, oldFmtVer, resetOnError = true) {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
return this.engine.deepCopy(this.defaultData);
|
|
1294
|
-
}
|
|
1227
|
+
async runMigrations(oldData, oldFmtVer, resetOnError = true) {
|
|
1228
|
+
if (!this.migrations)
|
|
1229
|
+
return oldData;
|
|
1230
|
+
let newData = oldData;
|
|
1231
|
+
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
1232
|
+
let lastFmtVer = oldFmtVer;
|
|
1233
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
1234
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
1235
|
+
const ver = Number(fmtVer);
|
|
1236
|
+
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
1237
|
+
try {
|
|
1238
|
+
const migRes = migrationFunc(newData);
|
|
1239
|
+
newData = migRes instanceof Promise ? await migRes : migRes;
|
|
1240
|
+
lastFmtVer = oldFmtVer = ver;
|
|
1241
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
1242
|
+
this.emitEvent("migrateData", ver, newData, isFinal);
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
1245
|
+
this.emitEvent("migrationError", ver, migError);
|
|
1246
|
+
this.emitEvent("error", migError);
|
|
1247
|
+
if (!resetOnError)
|
|
1248
|
+
throw migError;
|
|
1249
|
+
await this.saveDefaultData();
|
|
1250
|
+
return this.engine.deepCopy(this.defaultData);
|
|
1295
1251
|
}
|
|
1296
1252
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1253
|
+
}
|
|
1254
|
+
await Promise.allSettled([
|
|
1255
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
|
|
1256
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
1257
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1258
|
+
]);
|
|
1259
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
1260
|
+
this.emitEvent("updateData", result);
|
|
1261
|
+
return result;
|
|
1306
1262
|
}
|
|
1307
1263
|
//#region migrateId
|
|
1308
1264
|
/**
|
|
1309
1265
|
* Tries to migrate the currently saved persistent data from one or more old IDs to the ID set in the constructor.
|
|
1310
1266
|
* 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.
|
|
1311
1267
|
*/
|
|
1312
|
-
migrateId(oldIds) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
const [
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
1321
|
-
]);
|
|
1322
|
-
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
1323
|
-
}))();
|
|
1324
|
-
if (data === void 0 || isNaN(fmtVer))
|
|
1325
|
-
return;
|
|
1326
|
-
const parsed = yield this.engine.deserializeData(data, isEncoded);
|
|
1327
|
-
yield Promise.allSettled([
|
|
1328
|
-
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
1329
|
-
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
1330
|
-
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
1331
|
-
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
1332
|
-
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1333
|
-
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
1268
|
+
async migrateId(oldIds) {
|
|
1269
|
+
const ids = Array.isArray(oldIds) ? oldIds : [oldIds];
|
|
1270
|
+
await Promise.all(ids.map(async (id) => {
|
|
1271
|
+
const [data, fmtVer, isEncoded] = await (async () => {
|
|
1272
|
+
const [d, f, e] = await Promise.all([
|
|
1273
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
1274
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
1275
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
1334
1276
|
]);
|
|
1335
|
-
|
|
1336
|
-
}))
|
|
1337
|
-
|
|
1277
|
+
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
1278
|
+
})();
|
|
1279
|
+
if (data === void 0 || isNaN(fmtVer))
|
|
1280
|
+
return;
|
|
1281
|
+
const parsed = await this.engine.deserializeData(data, isEncoded);
|
|
1282
|
+
await Promise.allSettled([
|
|
1283
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
1284
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
1285
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
1286
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
1287
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1288
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
1289
|
+
]);
|
|
1290
|
+
this.emitEvent("migrateId", id, this.id);
|
|
1291
|
+
}));
|
|
1338
1292
|
}
|
|
1339
1293
|
};
|
|
1340
1294
|
|
|
1341
1295
|
// lib/DataStoreEngine.ts
|
|
1342
1296
|
var DataStoreEngine = class {
|
|
1297
|
+
dataStoreOptions;
|
|
1343
1298
|
// setDataStoreOptions() is called from inside the DataStore constructor to set this value
|
|
1344
1299
|
constructor(options) {
|
|
1345
|
-
__publicField(this, "dataStoreOptions");
|
|
1346
1300
|
if (options)
|
|
1347
1301
|
this.dataStoreOptions = options;
|
|
1348
1302
|
}
|
|
@@ -1352,29 +1306,25 @@ var DataStoreEngine = class {
|
|
|
1352
1306
|
}
|
|
1353
1307
|
//#region serialization api
|
|
1354
1308
|
/** 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 */
|
|
1355
|
-
serializeData(data, useEncoding) {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
return encRes;
|
|
1366
|
-
});
|
|
1309
|
+
async serializeData(data, useEncoding) {
|
|
1310
|
+
var _a, _b, _c, _d, _e;
|
|
1311
|
+
this.ensureDataStoreOptions();
|
|
1312
|
+
const stringData = JSON.stringify(data);
|
|
1313
|
+
if (!useEncoding || !((_a = this.dataStoreOptions) == null ? void 0 : _a.encodeData) || !((_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData))
|
|
1314
|
+
return stringData;
|
|
1315
|
+
const encRes = (_e = (_d = (_c = this.dataStoreOptions) == null ? void 0 : _c.encodeData) == null ? void 0 : _d[1]) == null ? void 0 : _e.call(_d, stringData);
|
|
1316
|
+
if (encRes instanceof Promise)
|
|
1317
|
+
return await encRes;
|
|
1318
|
+
return encRes;
|
|
1367
1319
|
}
|
|
1368
1320
|
/** Deserializes the given string to a JSON object, optionally decoded with `options.decodeData` if {@linkcode useEncoding} is set to true */
|
|
1369
|
-
deserializeData(data, useEncoding) {
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
return JSON.parse(decRes != null ? decRes : data);
|
|
1377
|
-
});
|
|
1321
|
+
async deserializeData(data, useEncoding) {
|
|
1322
|
+
var _a, _b, _c;
|
|
1323
|
+
this.ensureDataStoreOptions();
|
|
1324
|
+
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;
|
|
1325
|
+
if (decRes instanceof Promise)
|
|
1326
|
+
decRes = await decRes;
|
|
1327
|
+
return JSON.parse(decRes ?? data);
|
|
1378
1328
|
}
|
|
1379
1329
|
//#region misc api
|
|
1380
1330
|
/** Throws an error if the DataStoreOptions are not set or invalid */
|
|
@@ -1392,12 +1342,13 @@ var DataStoreEngine = class {
|
|
|
1392
1342
|
try {
|
|
1393
1343
|
if ("structuredClone" in globalThis)
|
|
1394
1344
|
return structuredClone(obj);
|
|
1395
|
-
} catch
|
|
1345
|
+
} catch {
|
|
1396
1346
|
}
|
|
1397
1347
|
return JSON.parse(JSON.stringify(obj));
|
|
1398
1348
|
}
|
|
1399
1349
|
};
|
|
1400
1350
|
var BrowserStorageEngine = class extends DataStoreEngine {
|
|
1351
|
+
options;
|
|
1401
1352
|
/**
|
|
1402
1353
|
* Creates an instance of `BrowserStorageEngine`.
|
|
1403
1354
|
*
|
|
@@ -1406,40 +1357,36 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
1406
1357
|
*/
|
|
1407
1358
|
constructor(options) {
|
|
1408
1359
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
}
|
|
1360
|
+
this.options = {
|
|
1361
|
+
type: "localStorage",
|
|
1362
|
+
...options
|
|
1363
|
+
};
|
|
1413
1364
|
}
|
|
1414
1365
|
//#region storage api
|
|
1415
1366
|
/** Fetches a value from persistent storage */
|
|
1416
|
-
getValue(name, defaultValue) {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
return typeof val === "undefined" ? defaultValue : val;
|
|
1420
|
-
});
|
|
1367
|
+
async getValue(name, defaultValue) {
|
|
1368
|
+
const val = this.options.type === "localStorage" ? globalThis.localStorage.getItem(name) : globalThis.sessionStorage.getItem(name);
|
|
1369
|
+
return typeof val === "undefined" ? defaultValue : val;
|
|
1421
1370
|
}
|
|
1422
1371
|
/** Sets a value in persistent storage */
|
|
1423
|
-
setValue(name, value) {
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
globalThis.sessionStorage.setItem(name, String(value));
|
|
1429
|
-
});
|
|
1372
|
+
async setValue(name, value) {
|
|
1373
|
+
if (this.options.type === "localStorage")
|
|
1374
|
+
globalThis.localStorage.setItem(name, String(value));
|
|
1375
|
+
else
|
|
1376
|
+
globalThis.sessionStorage.setItem(name, String(value));
|
|
1430
1377
|
}
|
|
1431
1378
|
/** Deletes a value from persistent storage */
|
|
1432
|
-
deleteValue(name) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
globalThis.sessionStorage.removeItem(name);
|
|
1438
|
-
});
|
|
1379
|
+
async deleteValue(name) {
|
|
1380
|
+
if (this.options.type === "localStorage")
|
|
1381
|
+
globalThis.localStorage.removeItem(name);
|
|
1382
|
+
else
|
|
1383
|
+
globalThis.sessionStorage.removeItem(name);
|
|
1439
1384
|
}
|
|
1440
1385
|
};
|
|
1441
1386
|
var fs;
|
|
1442
1387
|
var FileStorageEngine = class extends DataStoreEngine {
|
|
1388
|
+
options;
|
|
1389
|
+
fileAccessQueue = Promise.resolve();
|
|
1443
1390
|
/**
|
|
1444
1391
|
* Creates an instance of `FileStorageEngine`.
|
|
1445
1392
|
*
|
|
@@ -1448,164 +1395,155 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1448
1395
|
*/
|
|
1449
1396
|
constructor(options) {
|
|
1450
1397
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
}, options);
|
|
1398
|
+
this.options = {
|
|
1399
|
+
filePath: (id) => `.ds-${id}`,
|
|
1400
|
+
...options
|
|
1401
|
+
};
|
|
1456
1402
|
}
|
|
1457
1403
|
//#region json file
|
|
1458
1404
|
/** Reads the file contents */
|
|
1459
|
-
readFile() {
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1474
|
-
});
|
|
1405
|
+
async readFile() {
|
|
1406
|
+
var _a, _b, _c, _d;
|
|
1407
|
+
this.ensureDataStoreOptions();
|
|
1408
|
+
try {
|
|
1409
|
+
if (!fs)
|
|
1410
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
1411
|
+
if (!fs)
|
|
1412
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1413
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1414
|
+
const data = await fs.readFile(path, "utf-8");
|
|
1415
|
+
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;
|
|
1416
|
+
} catch {
|
|
1417
|
+
return void 0;
|
|
1418
|
+
}
|
|
1475
1419
|
}
|
|
1476
1420
|
/** Overwrites the file contents */
|
|
1477
|
-
writeFile(data) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
}
|
|
1492
|
-
});
|
|
1421
|
+
async writeFile(data) {
|
|
1422
|
+
var _a, _b, _c, _d;
|
|
1423
|
+
this.ensureDataStoreOptions();
|
|
1424
|
+
try {
|
|
1425
|
+
if (!fs)
|
|
1426
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
1427
|
+
if (!fs)
|
|
1428
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1429
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1430
|
+
await fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
1431
|
+
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");
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
console.error("Error writing file:", err);
|
|
1434
|
+
}
|
|
1493
1435
|
}
|
|
1494
1436
|
//#region storage api
|
|
1495
1437
|
/** Fetches a value from persistent storage */
|
|
1496
|
-
getValue(name, defaultValue) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1438
|
+
async getValue(name, defaultValue) {
|
|
1439
|
+
const data = await this.readFile();
|
|
1440
|
+
if (!data)
|
|
1441
|
+
return defaultValue;
|
|
1442
|
+
const value = data == null ? void 0 : data[name];
|
|
1443
|
+
if (typeof value === "undefined")
|
|
1444
|
+
return defaultValue;
|
|
1445
|
+
if (typeof defaultValue === "string") {
|
|
1446
|
+
if (typeof value === "object" && value !== null)
|
|
1447
|
+
return JSON.stringify(value);
|
|
1448
|
+
if (typeof value === "string")
|
|
1449
|
+
return value;
|
|
1450
|
+
return String(value);
|
|
1451
|
+
}
|
|
1452
|
+
if (typeof value === "string") {
|
|
1453
|
+
try {
|
|
1454
|
+
const parsed = JSON.parse(value);
|
|
1455
|
+
return parsed;
|
|
1456
|
+
} catch {
|
|
1503
1457
|
return defaultValue;
|
|
1504
|
-
if (typeof defaultValue === "string") {
|
|
1505
|
-
if (typeof value === "object" && value !== null)
|
|
1506
|
-
return JSON.stringify(value);
|
|
1507
|
-
if (typeof value === "string")
|
|
1508
|
-
return value;
|
|
1509
|
-
return String(value);
|
|
1510
1458
|
}
|
|
1459
|
+
}
|
|
1460
|
+
return value;
|
|
1461
|
+
}
|
|
1462
|
+
/** Sets a value in persistent storage */
|
|
1463
|
+
async setValue(name, value) {
|
|
1464
|
+
this.fileAccessQueue = this.fileAccessQueue.then(async () => {
|
|
1465
|
+
let data = await this.readFile();
|
|
1466
|
+
if (!data)
|
|
1467
|
+
data = {};
|
|
1468
|
+
let storeVal = value;
|
|
1511
1469
|
if (typeof value === "string") {
|
|
1512
1470
|
try {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1471
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1472
|
+
const parsed = JSON.parse(value);
|
|
1473
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1474
|
+
storeVal = parsed;
|
|
1475
|
+
}
|
|
1476
|
+
} catch {
|
|
1517
1477
|
}
|
|
1518
1478
|
}
|
|
1519
|
-
|
|
1479
|
+
data[name] = storeVal;
|
|
1480
|
+
await this.writeFile(data);
|
|
1481
|
+
}).catch((err) => {
|
|
1482
|
+
console.error("Error in setValue:", err);
|
|
1483
|
+
throw err;
|
|
1520
1484
|
});
|
|
1521
|
-
|
|
1522
|
-
/** Sets a value in persistent storage */
|
|
1523
|
-
setValue(name, value) {
|
|
1524
|
-
return __async(this, null, function* () {
|
|
1525
|
-
this.fileAccessQueue = this.fileAccessQueue.then(() => __async(this, null, function* () {
|
|
1526
|
-
let data = yield this.readFile();
|
|
1527
|
-
if (!data)
|
|
1528
|
-
data = {};
|
|
1529
|
-
let storeVal = value;
|
|
1530
|
-
if (typeof value === "string") {
|
|
1531
|
-
try {
|
|
1532
|
-
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1533
|
-
const parsed = JSON.parse(value);
|
|
1534
|
-
if (typeof parsed === "object" && parsed !== null)
|
|
1535
|
-
storeVal = parsed;
|
|
1536
|
-
}
|
|
1537
|
-
} catch (e) {
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
data[name] = storeVal;
|
|
1541
|
-
yield this.writeFile(data);
|
|
1542
|
-
})).catch((err) => {
|
|
1543
|
-
console.error("Error in setValue:", err);
|
|
1544
|
-
throw err;
|
|
1545
|
-
});
|
|
1546
|
-
yield this.fileAccessQueue.catch(() => {
|
|
1547
|
-
});
|
|
1485
|
+
await this.fileAccessQueue.catch(() => {
|
|
1548
1486
|
});
|
|
1549
1487
|
}
|
|
1550
1488
|
/** Deletes a value from persistent storage */
|
|
1551
|
-
deleteValue(name) {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
yield this.fileAccessQueue.catch(() => {
|
|
1564
|
-
});
|
|
1489
|
+
async deleteValue(name) {
|
|
1490
|
+
this.fileAccessQueue = this.fileAccessQueue.then(async () => {
|
|
1491
|
+
const data = await this.readFile();
|
|
1492
|
+
if (!data)
|
|
1493
|
+
return;
|
|
1494
|
+
delete data[name];
|
|
1495
|
+
await this.writeFile(data);
|
|
1496
|
+
}).catch((err) => {
|
|
1497
|
+
console.error("Error in deleteValue:", err);
|
|
1498
|
+
throw err;
|
|
1499
|
+
});
|
|
1500
|
+
await this.fileAccessQueue.catch(() => {
|
|
1565
1501
|
});
|
|
1566
1502
|
}
|
|
1567
1503
|
/** Deletes the file that contains the data of this DataStore. */
|
|
1568
|
-
deleteStorage() {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
}
|
|
1582
|
-
});
|
|
1504
|
+
async deleteStorage() {
|
|
1505
|
+
var _a;
|
|
1506
|
+
this.ensureDataStoreOptions();
|
|
1507
|
+
try {
|
|
1508
|
+
if (!fs)
|
|
1509
|
+
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
1510
|
+
if (!fs)
|
|
1511
|
+
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1512
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1513
|
+
return await fs.unlink(path);
|
|
1514
|
+
} catch (err) {
|
|
1515
|
+
console.error("Error deleting file:", err);
|
|
1516
|
+
}
|
|
1583
1517
|
}
|
|
1584
1518
|
};
|
|
1585
1519
|
|
|
1586
1520
|
// lib/DataStoreSerializer.ts
|
|
1587
1521
|
var DataStoreSerializer = class _DataStoreSerializer {
|
|
1522
|
+
stores;
|
|
1523
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
1524
|
+
options;
|
|
1588
1525
|
constructor(stores, options = {}) {
|
|
1589
|
-
__publicField(this, "stores");
|
|
1590
|
-
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
1591
|
-
__publicField(this, "options");
|
|
1592
1526
|
if (!crypto || !crypto.subtle)
|
|
1593
1527
|
throw new ScriptContextError("DataStoreSerializer has to run in a secure context (HTTPS) or in another environment that implements the subtleCrypto API!");
|
|
1594
1528
|
this.stores = stores;
|
|
1595
|
-
this.options =
|
|
1529
|
+
this.options = {
|
|
1596
1530
|
addChecksum: true,
|
|
1597
1531
|
ensureIntegrity: true,
|
|
1598
|
-
remapIds: {}
|
|
1599
|
-
|
|
1532
|
+
remapIds: {},
|
|
1533
|
+
stringifyData: true,
|
|
1534
|
+
...options
|
|
1535
|
+
};
|
|
1600
1536
|
}
|
|
1601
1537
|
/**
|
|
1602
|
-
* Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1538
|
+
* Calculates the checksum of a string or {@linkcode DataStoreData} object. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1603
1539
|
* Override this in a subclass if a custom checksum method is needed.
|
|
1604
1540
|
*/
|
|
1605
|
-
calcChecksum(input) {
|
|
1606
|
-
|
|
1607
|
-
return computeHash(input, "SHA-256");
|
|
1608
|
-
})
|
|
1541
|
+
async calcChecksum(input) {
|
|
1542
|
+
try {
|
|
1543
|
+
return computeHash(typeof input === "string" ? input : JSON.stringify(input), "SHA-256");
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
throw new Error(`Failed to calculate checksum: ${err.message}`, { cause: err });
|
|
1546
|
+
}
|
|
1609
1547
|
}
|
|
1610
1548
|
/**
|
|
1611
1549
|
* Serializes only a subset of the data stores into a string.
|
|
@@ -1613,80 +1551,72 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1613
1551
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
1614
1552
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
1615
1553
|
*/
|
|
1616
|
-
serializePartial(stores, useEncoding = true, stringified = true) {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
return stringified ? JSON.stringify(serData) : serData;
|
|
1634
|
-
});
|
|
1554
|
+
async serializePartial(stores, useEncoding = true, stringified = true) {
|
|
1555
|
+
var _a;
|
|
1556
|
+
const serData = [];
|
|
1557
|
+
const filteredStores = this.stores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id));
|
|
1558
|
+
for (const storeInst of filteredStores) {
|
|
1559
|
+
const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
|
|
1560
|
+
const rawData = storeInst.memoryCache ? storeInst.getData() : await storeInst.loadData();
|
|
1561
|
+
const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : this.options.stringifyData ? JSON.stringify(rawData) : rawData;
|
|
1562
|
+
serData.push({
|
|
1563
|
+
id: storeInst.id,
|
|
1564
|
+
data,
|
|
1565
|
+
formatVersion: storeInst.formatVersion,
|
|
1566
|
+
encoded,
|
|
1567
|
+
checksum: this.options.addChecksum ? await this.calcChecksum(data) : void 0
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
return stringified ? JSON.stringify(serData) : serData;
|
|
1635
1571
|
}
|
|
1636
1572
|
/**
|
|
1637
1573
|
* Serializes the data stores into a string.
|
|
1638
1574
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
1639
1575
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
1640
1576
|
*/
|
|
1641
|
-
serialize(useEncoding = true, stringified = true) {
|
|
1642
|
-
return
|
|
1643
|
-
return this.serializePartial(this.stores.map((s) => s.id), useEncoding, stringified);
|
|
1644
|
-
});
|
|
1577
|
+
async serialize(useEncoding = true, stringified = true) {
|
|
1578
|
+
return this.serializePartial(this.stores.map((s) => s.id), useEncoding, stringified);
|
|
1645
1579
|
}
|
|
1646
1580
|
/**
|
|
1647
1581
|
* Deserializes the data exported via {@linkcode serialize()} and imports only a subset into the DataStore instances.
|
|
1648
1582
|
* Also triggers the migration process if the data format has changed.
|
|
1649
1583
|
*/
|
|
1650
|
-
deserializePartial(stores, data) {
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
1584
|
+
async deserializePartial(stores, data) {
|
|
1585
|
+
const deserStores = typeof data === "string" ? JSON.parse(data) : data;
|
|
1586
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
1587
|
+
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
1588
|
+
const resolveStoreId = (id) => {
|
|
1589
|
+
var _a;
|
|
1590
|
+
return ((_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0]) ?? id;
|
|
1591
|
+
};
|
|
1592
|
+
const matchesFilter = (id) => typeof stores === "function" ? stores(id) : stores.includes(id);
|
|
1593
|
+
for (const storeData of deserStores) {
|
|
1594
|
+
const effectiveId = resolveStoreId(storeData.id);
|
|
1595
|
+
if (!matchesFilter(effectiveId))
|
|
1596
|
+
continue;
|
|
1597
|
+
const storeInst = this.stores.find((s) => s.id === effectiveId);
|
|
1598
|
+
if (!storeInst)
|
|
1599
|
+
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.`);
|
|
1600
|
+
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
1601
|
+
const checksum = await this.calcChecksum(storeData.data);
|
|
1602
|
+
if (checksum !== storeData.checksum)
|
|
1603
|
+
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
1671
1604
|
Expected: ${storeData.checksum}
|
|
1672
1605
|
Has: ${checksum}`);
|
|
1673
|
-
}
|
|
1674
|
-
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? yield storeInst.decodeData[1](storeData.data) : storeData.data;
|
|
1675
|
-
if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
|
|
1676
|
-
yield storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false);
|
|
1677
|
-
else
|
|
1678
|
-
yield storeInst.setData(JSON.parse(decodedData));
|
|
1679
1606
|
}
|
|
1680
|
-
|
|
1607
|
+
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? await storeInst.decodeData[1](typeof storeData.data === "string" ? storeData.data : JSON.stringify(storeData.data)) : storeData.data;
|
|
1608
|
+
if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
|
|
1609
|
+
await storeInst.runMigrations(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData, Number(storeData.formatVersion), false);
|
|
1610
|
+
else
|
|
1611
|
+
await storeInst.setData(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData);
|
|
1612
|
+
}
|
|
1681
1613
|
}
|
|
1682
1614
|
/**
|
|
1683
1615
|
* Deserializes the data exported via {@linkcode serialize()} and imports the data into all matching DataStore instances.
|
|
1684
1616
|
* Also triggers the migration process if the data format has changed.
|
|
1685
1617
|
*/
|
|
1686
|
-
deserialize(data) {
|
|
1687
|
-
return
|
|
1688
|
-
return this.deserializePartial(this.stores.map((s) => s.id), data);
|
|
1689
|
-
});
|
|
1618
|
+
async deserialize(data) {
|
|
1619
|
+
return this.deserializePartial(this.stores.map((s) => s.id), data);
|
|
1690
1620
|
}
|
|
1691
1621
|
/**
|
|
1692
1622
|
* Loads the persistent data of the DataStore instances into the in-memory cache.
|
|
@@ -1694,40 +1624,32 @@ Has: ${checksum}`);
|
|
|
1694
1624
|
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be loaded
|
|
1695
1625
|
* @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }`
|
|
1696
1626
|
*/
|
|
1697
|
-
loadStoresData(stores) {
|
|
1698
|
-
return
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
};
|
|
1705
|
-
}))
|
|
1706
|
-
);
|
|
1707
|
-
});
|
|
1627
|
+
async loadStoresData(stores) {
|
|
1628
|
+
return Promise.allSettled(
|
|
1629
|
+
this.getStoresFiltered(stores).map(async (store) => ({
|
|
1630
|
+
id: store.id,
|
|
1631
|
+
data: await store.loadData()
|
|
1632
|
+
}))
|
|
1633
|
+
);
|
|
1708
1634
|
}
|
|
1709
1635
|
/**
|
|
1710
1636
|
* Resets the persistent and in-memory data of the DataStore instances to their default values.
|
|
1711
1637
|
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
1712
1638
|
*/
|
|
1713
|
-
resetStoresData(stores) {
|
|
1714
|
-
return
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
);
|
|
1718
|
-
});
|
|
1639
|
+
async resetStoresData(stores) {
|
|
1640
|
+
return Promise.allSettled(
|
|
1641
|
+
this.getStoresFiltered(stores).map((store) => store.saveDefaultData())
|
|
1642
|
+
);
|
|
1719
1643
|
}
|
|
1720
1644
|
/**
|
|
1721
1645
|
* Deletes the persistent data of the DataStore instances.
|
|
1722
1646
|
* Leaves the in-memory data untouched.
|
|
1723
1647
|
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
1724
1648
|
*/
|
|
1725
|
-
deleteStoresData(stores) {
|
|
1726
|
-
return
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
);
|
|
1730
|
-
});
|
|
1649
|
+
async deleteStoresData(stores) {
|
|
1650
|
+
return Promise.allSettled(
|
|
1651
|
+
this.getStoresFiltered(stores).map((store) => store.deleteData())
|
|
1652
|
+
);
|
|
1731
1653
|
}
|
|
1732
1654
|
/** Checks if a given value is an array of SerializedDataStore objects */
|
|
1733
1655
|
static isSerializedDataStoreObjArray(obj) {
|
|
@@ -1754,13 +1676,15 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1754
1676
|
super(nanoEmitterOptions);
|
|
1755
1677
|
this.timeout = timeout;
|
|
1756
1678
|
this.type = type;
|
|
1757
|
-
/** All registered listener functions and the time they were attached */
|
|
1758
|
-
__publicField(this, "listeners", []);
|
|
1759
|
-
/** The currently active timeout */
|
|
1760
|
-
__publicField(this, "activeTimeout");
|
|
1761
|
-
/** The latest queued call */
|
|
1762
|
-
__publicField(this, "queuedCall");
|
|
1763
1679
|
}
|
|
1680
|
+
timeout;
|
|
1681
|
+
type;
|
|
1682
|
+
/** All registered listener functions and the time they were attached */
|
|
1683
|
+
listeners = [];
|
|
1684
|
+
/** The currently active timeout */
|
|
1685
|
+
activeTimeout;
|
|
1686
|
+
/** The latest queued call */
|
|
1687
|
+
queuedCall;
|
|
1764
1688
|
//#region listeners
|
|
1765
1689
|
/** Adds a listener function that will be called on timeout */
|
|
1766
1690
|
addListener(fn) {
|
|
@@ -1782,7 +1706,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1782
1706
|
//#region timeout
|
|
1783
1707
|
/** Sets the timeout for the debouncer */
|
|
1784
1708
|
setTimeout(timeout) {
|
|
1785
|
-
this.
|
|
1709
|
+
this.emitEvent("change", this.timeout = timeout, this.type);
|
|
1786
1710
|
}
|
|
1787
1711
|
/** Returns the current timeout */
|
|
1788
1712
|
getTimeout() {
|
|
@@ -1795,7 +1719,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1795
1719
|
//#region type
|
|
1796
1720
|
/** Sets the edge type for the debouncer */
|
|
1797
1721
|
setType(type) {
|
|
1798
|
-
this.
|
|
1722
|
+
this.emitEvent("change", this.timeout, this.type = type);
|
|
1799
1723
|
}
|
|
1800
1724
|
/** Returns the current edge type */
|
|
1801
1725
|
getType() {
|
|
@@ -1806,7 +1730,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1806
1730
|
call(...args) {
|
|
1807
1731
|
const cl = (...a) => {
|
|
1808
1732
|
this.queuedCall = void 0;
|
|
1809
|
-
this.
|
|
1733
|
+
this.emitEvent("call", ...a);
|
|
1810
1734
|
this.listeners.forEach((l) => l.call(this, ...a));
|
|
1811
1735
|
};
|
|
1812
1736
|
const setRepeatTimeout = () => {
|
|
@@ -1847,8 +1771,6 @@ function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
|
1847
1771
|
return func;
|
|
1848
1772
|
}
|
|
1849
1773
|
|
|
1850
|
-
if(__exports != exports)module.exports = exports;return module.exports}));
|
|
1851
|
-
|
|
1852
1774
|
|
|
1853
1775
|
|
|
1854
1776
|
if (typeof module.exports == "object" && typeof exports == "object") {
|