@sv443-network/userutils 10.0.6 → 10.1.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 +18 -0
- package/README.md +38 -32
- package/dist/UserUtils.cjs +553 -395
- package/dist/UserUtils.mjs +555 -395
- package/dist/UserUtils.umd.js +439 -300
- package/dist/lib/consts.d.ts +7 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/translation.d.ts +80 -2
- package/package.json +4 -5
package/dist/UserUtils.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
9
|
var __export = (target, all) => {
|
|
9
10
|
for (var name in all)
|
|
10
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
27
|
mod
|
|
27
28
|
));
|
|
28
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
29
31
|
|
|
30
32
|
// lib/index.ts
|
|
31
33
|
var lib_exports = {};
|
|
@@ -110,11 +112,12 @@ __export(lib_exports, {
|
|
|
110
112
|
takeRandomItemIndex: () => takeRandomItemIndex,
|
|
111
113
|
tr: () => tr,
|
|
112
114
|
truncStr: () => truncStr,
|
|
113
|
-
valsWithin: () => valsWithin
|
|
115
|
+
valsWithin: () => valsWithin,
|
|
116
|
+
versions: () => versions
|
|
114
117
|
});
|
|
115
118
|
module.exports = __toCommonJS(lib_exports);
|
|
116
119
|
|
|
117
|
-
// node_modules/.pnpm/@sv443-network+coreutils@3.0
|
|
120
|
+
// node_modules/.pnpm/@sv443-network+coreutils@3.3.0/node_modules/@sv443-network/coreutils/dist/CoreUtils.mjs
|
|
118
121
|
function bitSetHas(bitSet, checkVal) {
|
|
119
122
|
return (bitSet & checkVal) === checkVal;
|
|
120
123
|
}
|
|
@@ -260,7 +263,7 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
260
263
|
if (isHexCol)
|
|
261
264
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
262
265
|
else if (color.startsWith("rgba"))
|
|
263
|
-
return `rgba(${r}, ${g}, ${b}, ${a
|
|
266
|
+
return `rgba(${r}, ${g}, ${b}, ${a != null ? a : NaN})`;
|
|
264
267
|
else
|
|
265
268
|
return `rgb(${r}, ${g}, ${b})`;
|
|
266
269
|
}
|
|
@@ -293,7 +296,8 @@ function atoab(str) {
|
|
|
293
296
|
return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
|
|
294
297
|
}
|
|
295
298
|
async function compress(input, compressionFormat, outputType = "string") {
|
|
296
|
-
|
|
299
|
+
var _a;
|
|
300
|
+
const byteArray = input instanceof Uint8Array ? input : new TextEncoder().encode((_a = input == null ? void 0 : input.toString()) != null ? _a : String(input));
|
|
297
301
|
const comp = new CompressionStream(compressionFormat);
|
|
298
302
|
const writer = comp.writable.getWriter();
|
|
299
303
|
writer.write(byteArray);
|
|
@@ -302,7 +306,8 @@ async function compress(input, compressionFormat, outputType = "string") {
|
|
|
302
306
|
return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
|
|
303
307
|
}
|
|
304
308
|
async function decompress(input, compressionFormat, outputType = "string") {
|
|
305
|
-
|
|
309
|
+
var _a;
|
|
310
|
+
const byteArray = input instanceof Uint8Array ? input : atoab((_a = input == null ? void 0 : input.toString()) != null ? _a : String(input));
|
|
306
311
|
const decomp = new DecompressionStream(compressionFormat);
|
|
307
312
|
const writer = decomp.writable.getWriter();
|
|
308
313
|
writer.write(byteArray);
|
|
@@ -347,9 +352,9 @@ function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase =
|
|
|
347
352
|
return arr.map((v) => caseArr[randRange(0, caseArr.length - 1, enhancedEntropy)] === 1 ? v.toUpperCase() : v).join("");
|
|
348
353
|
}
|
|
349
354
|
var DatedError = class extends Error {
|
|
350
|
-
date;
|
|
351
355
|
constructor(message, options) {
|
|
352
356
|
super(message, options);
|
|
357
|
+
__publicField(this, "date");
|
|
353
358
|
this.name = this.constructor.name;
|
|
354
359
|
this.date = /* @__PURE__ */ new Date();
|
|
355
360
|
}
|
|
@@ -432,7 +437,7 @@ function pauseFor(time, signal, rejectOnAbort = false) {
|
|
|
432
437
|
});
|
|
433
438
|
}
|
|
434
439
|
function pureObj(obj) {
|
|
435
|
-
return Object.assign(/* @__PURE__ */ Object.create(null), obj
|
|
440
|
+
return Object.assign(/* @__PURE__ */ Object.create(null), obj != null ? obj : {});
|
|
436
441
|
}
|
|
437
442
|
function setImmediateInterval(callback, interval, signal) {
|
|
438
443
|
let intervalId;
|
|
@@ -471,12 +476,13 @@ function scheduleExit(code = 0, timeout = 0) {
|
|
|
471
476
|
setTimeout(exit, timeout);
|
|
472
477
|
}
|
|
473
478
|
function getCallStack(asArray, lines = Infinity) {
|
|
479
|
+
var _a;
|
|
474
480
|
if (typeof lines !== "number" || isNaN(lines) || lines < 0)
|
|
475
481
|
throw new TypeError("lines parameter must be a non-negative number");
|
|
476
482
|
try {
|
|
477
483
|
throw new Error("This is to capture a stack trace with CoreUtils.getCallStack(). (If you see this somewhere, you can safely ignore it.)");
|
|
478
484
|
} catch (err) {
|
|
479
|
-
const stack = (err.stack
|
|
485
|
+
const stack = ((_a = err.stack) != null ? _a : "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
|
|
480
486
|
return asArray !== false ? stack : stack.join("\n");
|
|
481
487
|
}
|
|
482
488
|
}
|
|
@@ -529,9 +535,10 @@ function createProgressBar(percentage, barLength, chars = defaultPbChars) {
|
|
|
529
535
|
}
|
|
530
536
|
function insertValues(input, ...values) {
|
|
531
537
|
return input.replace(/%\d/gm, (match) => {
|
|
538
|
+
var _a2;
|
|
532
539
|
var _a;
|
|
533
540
|
const argIndex = Number(match.substring(1)) - 1;
|
|
534
|
-
return (_a = values[argIndex]
|
|
541
|
+
return (_a = (_a2 = values[argIndex]) != null ? _a2 : match) == null ? void 0 : _a.toString();
|
|
535
542
|
});
|
|
536
543
|
}
|
|
537
544
|
function joinArrayReadable(array, separators = ", ", lastSeparator = " and ") {
|
|
@@ -562,31 +569,209 @@ function secsToTimeStr(seconds) {
|
|
|
562
569
|
].join("");
|
|
563
570
|
}
|
|
564
571
|
function truncStr(input, length, endStr = "...") {
|
|
565
|
-
|
|
572
|
+
var _a;
|
|
573
|
+
const str = (_a = input == null ? void 0 : input.toString()) != null ? _a : String(input);
|
|
566
574
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
567
575
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
568
576
|
}
|
|
569
|
-
var
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
577
|
+
var createNanoEvents = () => ({
|
|
578
|
+
emit(event, ...args) {
|
|
579
|
+
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
580
|
+
callbacks[i](...args);
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
events: {},
|
|
584
|
+
on(event, cb) {
|
|
585
|
+
var _a;
|
|
586
|
+
;
|
|
587
|
+
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
588
|
+
return () => {
|
|
589
|
+
var _a2;
|
|
590
|
+
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
var NanoEmitter = class {
|
|
595
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
596
|
+
constructor(options = {}) {
|
|
597
|
+
__publicField(this, "events", createNanoEvents());
|
|
598
|
+
__publicField(this, "eventUnsubscribes", []);
|
|
599
|
+
__publicField(this, "emitterOptions");
|
|
600
|
+
this.emitterOptions = {
|
|
601
|
+
publicEmit: false,
|
|
602
|
+
...options
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
//#region on
|
|
606
|
+
/**
|
|
607
|
+
* Subscribes to an event and calls the callback when it's emitted.
|
|
608
|
+
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
609
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
610
|
+
* @example ```ts
|
|
611
|
+
* const emitter = new NanoEmitter<{
|
|
612
|
+
* foo: (bar: string) => void;
|
|
613
|
+
* }>({
|
|
614
|
+
* publicEmit: true,
|
|
615
|
+
* });
|
|
616
|
+
*
|
|
617
|
+
* let i = 0;
|
|
618
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
619
|
+
* // unsubscribe after 10 events:
|
|
620
|
+
* if(++i === 10) unsub();
|
|
621
|
+
* console.log(bar);
|
|
622
|
+
* });
|
|
623
|
+
*
|
|
624
|
+
* emitter.emit("foo", "bar");
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
on(event, cb) {
|
|
628
|
+
let unsub;
|
|
629
|
+
const unsubProxy = () => {
|
|
630
|
+
if (!unsub)
|
|
631
|
+
return;
|
|
632
|
+
unsub();
|
|
633
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
634
|
+
};
|
|
635
|
+
unsub = this.events.on(event, cb);
|
|
636
|
+
this.eventUnsubscribes.push(unsub);
|
|
637
|
+
return unsubProxy;
|
|
638
|
+
}
|
|
639
|
+
//#region once
|
|
640
|
+
/**
|
|
641
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
642
|
+
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
643
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
644
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
645
|
+
* @example ```ts
|
|
646
|
+
* const emitter = new NanoEmitter<{
|
|
647
|
+
* foo: (bar: string) => void;
|
|
648
|
+
* }>();
|
|
649
|
+
*
|
|
650
|
+
* // Promise syntax:
|
|
651
|
+
* const [bar] = await emitter.once("foo");
|
|
652
|
+
* console.log(bar);
|
|
653
|
+
*
|
|
654
|
+
* // Callback syntax:
|
|
655
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
656
|
+
* ```
|
|
657
|
+
*/
|
|
658
|
+
once(event, cb) {
|
|
659
|
+
return new Promise((resolve) => {
|
|
660
|
+
let unsub;
|
|
661
|
+
const onceProxy = ((...args) => {
|
|
662
|
+
cb == null ? void 0 : cb(...args);
|
|
663
|
+
unsub == null ? void 0 : unsub();
|
|
664
|
+
resolve(args);
|
|
665
|
+
});
|
|
666
|
+
unsub = this.events.on(event, onceProxy);
|
|
667
|
+
this.eventUnsubscribes.push(unsub);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
//#region onMulti
|
|
671
|
+
/**
|
|
672
|
+
* Allows subscribing to multiple events and calling the callback only when one of, all of, or a subset of the events are emitted, either continuously or only once.
|
|
673
|
+
* @param options An object or array of objects with the following properties:
|
|
674
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
675
|
+
*
|
|
676
|
+
* 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.
|
|
677
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
678
|
+
*
|
|
679
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
680
|
+
* 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.
|
|
681
|
+
* 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.
|
|
682
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
683
|
+
*
|
|
684
|
+
* @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.
|
|
685
|
+
*/
|
|
686
|
+
onMulti(options) {
|
|
687
|
+
const allUnsubs = [];
|
|
688
|
+
const unsubAll = () => {
|
|
689
|
+
for (const unsub of allUnsubs)
|
|
690
|
+
unsub();
|
|
691
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
692
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
693
|
+
};
|
|
694
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
695
|
+
const optsWithDefaults = {
|
|
696
|
+
allOf: [],
|
|
697
|
+
oneOf: [],
|
|
698
|
+
once: false,
|
|
699
|
+
...opts
|
|
700
|
+
};
|
|
701
|
+
const {
|
|
702
|
+
oneOf,
|
|
703
|
+
allOf,
|
|
704
|
+
once,
|
|
705
|
+
signal,
|
|
706
|
+
callback
|
|
707
|
+
} = optsWithDefaults;
|
|
708
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
709
|
+
return unsubAll;
|
|
710
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
711
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
712
|
+
const curEvtUnsubs = [];
|
|
713
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
714
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
715
|
+
return;
|
|
716
|
+
for (const unsub of curEvtUnsubs)
|
|
717
|
+
unsub();
|
|
718
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
719
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
720
|
+
};
|
|
721
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
722
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
723
|
+
for (const event of oneOf) {
|
|
724
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
725
|
+
checkUnsubAllEvt();
|
|
726
|
+
if (allOfConditionMet()) {
|
|
727
|
+
callback(event, ...args);
|
|
728
|
+
if (once)
|
|
729
|
+
checkUnsubAllEvt(true);
|
|
730
|
+
}
|
|
731
|
+
}));
|
|
732
|
+
curEvtUnsubs.push(unsub);
|
|
733
|
+
}
|
|
734
|
+
for (const event of allOf) {
|
|
735
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
736
|
+
checkUnsubAllEvt();
|
|
737
|
+
allOfEmitted.add(event);
|
|
738
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
739
|
+
callback(event, ...args);
|
|
740
|
+
if (once)
|
|
741
|
+
checkUnsubAllEvt(true);
|
|
742
|
+
}
|
|
743
|
+
}));
|
|
744
|
+
curEvtUnsubs.push(unsub);
|
|
745
|
+
}
|
|
746
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
747
|
+
}
|
|
748
|
+
return unsubAll;
|
|
749
|
+
}
|
|
750
|
+
//#region emit
|
|
580
751
|
/**
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
752
|
+
* Emits an event on this instance.
|
|
753
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
754
|
+
* @param event The event to emit
|
|
755
|
+
* @param args The arguments to pass to the event listeners
|
|
756
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
584
757
|
*/
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
758
|
+
emit(event, ...args) {
|
|
759
|
+
if (this.emitterOptions.publicEmit) {
|
|
760
|
+
this.events.emit(event, ...args);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
//#region unsubscribeAll
|
|
766
|
+
/** Unsubscribes all event listeners from this instance */
|
|
767
|
+
unsubscribeAll() {
|
|
768
|
+
for (const unsub of this.eventUnsubscribes)
|
|
769
|
+
unsub();
|
|
770
|
+
this.eventUnsubscribes = [];
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var dsFmtVer = 1;
|
|
774
|
+
var DataStore = class extends NanoEmitter {
|
|
590
775
|
//#region constructor
|
|
591
776
|
/**
|
|
592
777
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -598,20 +783,43 @@ var DataStore = class {
|
|
|
598
783
|
* @param opts The options for this DataStore instance
|
|
599
784
|
*/
|
|
600
785
|
constructor(opts) {
|
|
786
|
+
var _a, _b, _c;
|
|
787
|
+
super(opts.nanoEmitterOptions);
|
|
788
|
+
__publicField(this, "id");
|
|
789
|
+
__publicField(this, "formatVersion");
|
|
790
|
+
__publicField(this, "defaultData");
|
|
791
|
+
__publicField(this, "encodeData");
|
|
792
|
+
__publicField(this, "decodeData");
|
|
793
|
+
__publicField(this, "compressionFormat", "deflate-raw");
|
|
794
|
+
__publicField(this, "memoryCache");
|
|
795
|
+
__publicField(this, "engine");
|
|
796
|
+
__publicField(this, "keyPrefix");
|
|
797
|
+
__publicField(this, "options");
|
|
798
|
+
/**
|
|
799
|
+
* Whether all first-init checks should be done.
|
|
800
|
+
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
801
|
+
* 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.
|
|
802
|
+
*/
|
|
803
|
+
__publicField(this, "firstInit", true);
|
|
804
|
+
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
805
|
+
__publicField(this, "cachedData");
|
|
806
|
+
__publicField(this, "migrations");
|
|
807
|
+
__publicField(this, "migrateIds", []);
|
|
601
808
|
this.id = opts.id;
|
|
602
809
|
this.formatVersion = opts.formatVersion;
|
|
603
810
|
this.defaultData = opts.defaultData;
|
|
604
|
-
this.memoryCache = opts.memoryCache
|
|
811
|
+
this.memoryCache = (_a = opts.memoryCache) != null ? _a : true;
|
|
605
812
|
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
606
813
|
this.migrations = opts.migrations;
|
|
607
814
|
if (opts.migrateIds)
|
|
608
815
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
609
816
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
817
|
+
this.keyPrefix = (_b = opts.keyPrefix) != null ? _b : "__ds-";
|
|
610
818
|
this.options = opts;
|
|
611
819
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
612
820
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
613
821
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
614
|
-
this.compressionFormat = opts.encodeData[0]
|
|
822
|
+
this.compressionFormat = (_c = opts.encodeData[0]) != null ? _c : null;
|
|
615
823
|
} else if (opts.compressionFormat === null) {
|
|
616
824
|
this.encodeData = void 0;
|
|
617
825
|
this.decodeData = void 0;
|
|
@@ -635,6 +843,7 @@ var DataStore = class {
|
|
|
635
843
|
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
636
844
|
*/
|
|
637
845
|
async loadData() {
|
|
846
|
+
var _a;
|
|
638
847
|
try {
|
|
639
848
|
if (this.firstInit) {
|
|
640
849
|
this.firstInit = false;
|
|
@@ -648,13 +857,13 @@ var DataStore = class {
|
|
|
648
857
|
promises.push(this.engine.setValue(newKey, value));
|
|
649
858
|
promises.push(this.engine.deleteValue(oldKey));
|
|
650
859
|
};
|
|
651
|
-
migrateFmt(`_uucfg-${this.id}`,
|
|
860
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
652
861
|
if (!isNaN(oldVer))
|
|
653
|
-
migrateFmt(`_uucfgver-${this.id}`,
|
|
862
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
654
863
|
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
655
|
-
migrateFmt(`_uucfgenc-${this.id}`,
|
|
864
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? (_a = this.compressionFormat) != null ? _a : null : null);
|
|
656
865
|
else {
|
|
657
|
-
promises.push(this.engine.setValue(
|
|
866
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
658
867
|
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
659
868
|
}
|
|
660
869
|
await Promise.allSettled(promises);
|
|
@@ -666,31 +875,27 @@ var DataStore = class {
|
|
|
666
875
|
await this.migrateId(this.migrateIds);
|
|
667
876
|
this.migrateIds = [];
|
|
668
877
|
}
|
|
669
|
-
const storedDataRaw = await this.engine.getValue(
|
|
670
|
-
|
|
671
|
-
if (typeof storedDataRaw !== "string") {
|
|
672
|
-
await this.saveDefaultData();
|
|
673
|
-
|
|
878
|
+
const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
879
|
+
const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
880
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
881
|
+
await this.saveDefaultData(false);
|
|
882
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
883
|
+
this.events.emit("loadData", data);
|
|
884
|
+
return data;
|
|
674
885
|
}
|
|
675
|
-
const storedData = storedDataRaw
|
|
676
|
-
const encodingFmt = String(await this.engine.getValue(
|
|
886
|
+
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
887
|
+
const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
677
888
|
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
|
|
678
|
-
let
|
|
679
|
-
if (isNaN(storedFmtVer)) {
|
|
680
|
-
await this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
|
|
681
|
-
saveData = true;
|
|
682
|
-
}
|
|
683
|
-
let parsed = await this.engine.deserializeData(storedData, isEncoded);
|
|
889
|
+
let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
684
890
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
685
891
|
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
return this.cachedData = this.engine.deepCopy(parsed);
|
|
690
|
-
else
|
|
691
|
-
return this.engine.deepCopy(parsed);
|
|
892
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
893
|
+
this.events.emit("loadData", result);
|
|
894
|
+
return result;
|
|
692
895
|
} catch (err) {
|
|
896
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
693
897
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
898
|
+
this.events.emit("error", error);
|
|
694
899
|
await this.saveDefaultData();
|
|
695
900
|
return this.defaultData;
|
|
696
901
|
}
|
|
@@ -709,27 +914,47 @@ var DataStore = class {
|
|
|
709
914
|
//#region setData
|
|
710
915
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
711
916
|
setData(data) {
|
|
712
|
-
|
|
917
|
+
const dataCopy = this.engine.deepCopy(data);
|
|
918
|
+
if (this.memoryCache) {
|
|
713
919
|
this.cachedData = data;
|
|
920
|
+
this.events.emit("updateDataSync", dataCopy);
|
|
921
|
+
}
|
|
714
922
|
return new Promise(async (resolve) => {
|
|
715
|
-
await Promise.allSettled([
|
|
716
|
-
this.engine.setValue(
|
|
717
|
-
this.engine.setValue(
|
|
718
|
-
this.engine.setValue(
|
|
923
|
+
const results = await Promise.allSettled([
|
|
924
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
925
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
926
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
719
927
|
]);
|
|
928
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
929
|
+
this.events.emit("updateData", dataCopy);
|
|
930
|
+
else {
|
|
931
|
+
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
932
|
+
console.error(error);
|
|
933
|
+
this.events.emit("error", error);
|
|
934
|
+
}
|
|
720
935
|
resolve();
|
|
721
936
|
});
|
|
722
937
|
}
|
|
723
938
|
//#region saveDefaultData
|
|
724
|
-
/**
|
|
725
|
-
|
|
939
|
+
/**
|
|
940
|
+
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
941
|
+
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
942
|
+
*/
|
|
943
|
+
async saveDefaultData(emitEvent = true) {
|
|
726
944
|
if (this.memoryCache)
|
|
727
945
|
this.cachedData = this.defaultData;
|
|
728
|
-
await Promise.allSettled([
|
|
729
|
-
this.engine.setValue(
|
|
730
|
-
this.engine.setValue(
|
|
731
|
-
this.engine.setValue(
|
|
946
|
+
const results = await Promise.allSettled([
|
|
947
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
948
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
949
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
732
950
|
]);
|
|
951
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
952
|
+
emitEvent && this.events.emit("setDefaultData", this.defaultData);
|
|
953
|
+
else {
|
|
954
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
955
|
+
console.error(error);
|
|
956
|
+
this.events.emit("error", error);
|
|
957
|
+
}
|
|
733
958
|
}
|
|
734
959
|
//#region deleteData
|
|
735
960
|
/**
|
|
@@ -740,11 +965,12 @@ var DataStore = class {
|
|
|
740
965
|
async deleteData() {
|
|
741
966
|
var _a, _b;
|
|
742
967
|
await Promise.allSettled([
|
|
743
|
-
this.engine.deleteValue(
|
|
744
|
-
this.engine.deleteValue(
|
|
745
|
-
this.engine.deleteValue(
|
|
968
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
969
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
970
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
746
971
|
]);
|
|
747
972
|
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
973
|
+
this.events.emit("deleteData");
|
|
748
974
|
}
|
|
749
975
|
//#region encodingEnabled
|
|
750
976
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
@@ -765,30 +991,35 @@ var DataStore = class {
|
|
|
765
991
|
let newData = oldData;
|
|
766
992
|
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
767
993
|
let lastFmtVer = oldFmtVer;
|
|
768
|
-
for (
|
|
994
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
995
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
769
996
|
const ver = Number(fmtVer);
|
|
770
997
|
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
771
998
|
try {
|
|
772
999
|
const migRes = migrationFunc(newData);
|
|
773
1000
|
newData = migRes instanceof Promise ? await migRes : migRes;
|
|
774
1001
|
lastFmtVer = oldFmtVer = ver;
|
|
1002
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
1003
|
+
this.events.emit("migrateData", ver, newData, isFinal);
|
|
775
1004
|
} catch (err) {
|
|
1005
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
1006
|
+
this.events.emit("migrationError", ver, migError);
|
|
1007
|
+
this.events.emit("error", migError);
|
|
776
1008
|
if (!resetOnError)
|
|
777
|
-
throw
|
|
1009
|
+
throw migError;
|
|
778
1010
|
await this.saveDefaultData();
|
|
779
1011
|
return this.engine.deepCopy(this.defaultData);
|
|
780
1012
|
}
|
|
781
1013
|
}
|
|
782
1014
|
}
|
|
783
1015
|
await Promise.allSettled([
|
|
784
|
-
this.engine.setValue(
|
|
785
|
-
this.engine.setValue(
|
|
786
|
-
this.engine.setValue(
|
|
1016
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
|
|
1017
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
1018
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
787
1019
|
]);
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
return this.engine.deepCopy(newData);
|
|
1020
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
1021
|
+
this.events.emit("updateData", result);
|
|
1022
|
+
return result;
|
|
792
1023
|
}
|
|
793
1024
|
//#region migrateId
|
|
794
1025
|
/**
|
|
@@ -800,9 +1031,9 @@ var DataStore = class {
|
|
|
800
1031
|
await Promise.all(ids.map(async (id) => {
|
|
801
1032
|
const [data, fmtVer, isEncoded] = await (async () => {
|
|
802
1033
|
const [d, f, e] = await Promise.all([
|
|
803
|
-
this.engine.getValue(
|
|
804
|
-
this.engine.getValue(
|
|
805
|
-
this.engine.getValue(
|
|
1034
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
1035
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
1036
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
806
1037
|
]);
|
|
807
1038
|
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
808
1039
|
})();
|
|
@@ -810,20 +1041,21 @@ var DataStore = class {
|
|
|
810
1041
|
return;
|
|
811
1042
|
const parsed = await this.engine.deserializeData(data, isEncoded);
|
|
812
1043
|
await Promise.allSettled([
|
|
813
|
-
this.engine.setValue(
|
|
814
|
-
this.engine.setValue(
|
|
815
|
-
this.engine.setValue(
|
|
816
|
-
this.engine.deleteValue(
|
|
817
|
-
this.engine.deleteValue(
|
|
818
|
-
this.engine.deleteValue(
|
|
1044
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
1045
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
1046
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
1047
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
1048
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1049
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
819
1050
|
]);
|
|
1051
|
+
this.events.emit("migrateId", id, this.id);
|
|
820
1052
|
}));
|
|
821
1053
|
}
|
|
822
1054
|
};
|
|
823
1055
|
var DataStoreEngine = class {
|
|
824
|
-
dataStoreOptions;
|
|
825
1056
|
// setDataStoreOptions() is called from inside the DataStore constructor to set this value
|
|
826
1057
|
constructor(options) {
|
|
1058
|
+
__publicField(this, "dataStoreOptions");
|
|
827
1059
|
if (options)
|
|
828
1060
|
this.dataStoreOptions = options;
|
|
829
1061
|
}
|
|
@@ -851,7 +1083,7 @@ var DataStoreEngine = class {
|
|
|
851
1083
|
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;
|
|
852
1084
|
if (decRes instanceof Promise)
|
|
853
1085
|
decRes = await decRes;
|
|
854
|
-
return JSON.parse(decRes
|
|
1086
|
+
return JSON.parse(decRes != null ? decRes : data);
|
|
855
1087
|
}
|
|
856
1088
|
//#region misc api
|
|
857
1089
|
/** Throws an error if the DataStoreOptions are not set or invalid */
|
|
@@ -869,13 +1101,12 @@ var DataStoreEngine = class {
|
|
|
869
1101
|
try {
|
|
870
1102
|
if ("structuredClone" in globalThis)
|
|
871
1103
|
return structuredClone(obj);
|
|
872
|
-
} catch {
|
|
1104
|
+
} catch (e) {
|
|
873
1105
|
}
|
|
874
1106
|
return JSON.parse(JSON.stringify(obj));
|
|
875
1107
|
}
|
|
876
1108
|
};
|
|
877
1109
|
var BrowserStorageEngine = class extends DataStoreEngine {
|
|
878
|
-
options;
|
|
879
1110
|
/**
|
|
880
1111
|
* Creates an instance of `BrowserStorageEngine`.
|
|
881
1112
|
*
|
|
@@ -884,6 +1115,7 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
884
1115
|
*/
|
|
885
1116
|
constructor(options) {
|
|
886
1117
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1118
|
+
__publicField(this, "options");
|
|
887
1119
|
this.options = {
|
|
888
1120
|
type: "localStorage",
|
|
889
1121
|
...options
|
|
@@ -912,8 +1144,6 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
912
1144
|
};
|
|
913
1145
|
var fs;
|
|
914
1146
|
var FileStorageEngine = class extends DataStoreEngine {
|
|
915
|
-
options;
|
|
916
|
-
fileAccessQueue = Promise.resolve();
|
|
917
1147
|
/**
|
|
918
1148
|
* Creates an instance of `FileStorageEngine`.
|
|
919
1149
|
*
|
|
@@ -922,6 +1152,8 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
922
1152
|
*/
|
|
923
1153
|
constructor(options) {
|
|
924
1154
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1155
|
+
__publicField(this, "options");
|
|
1156
|
+
__publicField(this, "fileAccessQueue", Promise.resolve());
|
|
925
1157
|
this.options = {
|
|
926
1158
|
filePath: (id) => `.ds-${id}`,
|
|
927
1159
|
...options
|
|
@@ -930,6 +1162,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
930
1162
|
//#region json file
|
|
931
1163
|
/** Reads the file contents */
|
|
932
1164
|
async readFile() {
|
|
1165
|
+
var _a2;
|
|
933
1166
|
var _a, _b, _c, _d;
|
|
934
1167
|
this.ensureDataStoreOptions();
|
|
935
1168
|
try {
|
|
@@ -937,15 +1170,16 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
937
1170
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
938
1171
|
if (!fs)
|
|
939
1172
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
940
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1173
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
941
1174
|
const data = await fs.readFile(path, "utf-8");
|
|
942
|
-
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))
|
|
943
|
-
} catch {
|
|
1175
|
+
return data ? JSON.parse((_a2 = await ((_d = (_c = (_b = this.dataStoreOptions) == null ? void 0 : _b.decodeData) == null ? void 0 : _c[1]) == null ? void 0 : _d.call(_c, data))) != null ? _a2 : data) : void 0;
|
|
1176
|
+
} catch (e) {
|
|
944
1177
|
return void 0;
|
|
945
1178
|
}
|
|
946
1179
|
}
|
|
947
1180
|
/** Overwrites the file contents */
|
|
948
1181
|
async writeFile(data) {
|
|
1182
|
+
var _a2;
|
|
949
1183
|
var _a, _b, _c, _d;
|
|
950
1184
|
this.ensureDataStoreOptions();
|
|
951
1185
|
try {
|
|
@@ -953,9 +1187,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
953
1187
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
954
1188
|
if (!fs)
|
|
955
1189
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
956
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1190
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
957
1191
|
await fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
958
|
-
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)))
|
|
1192
|
+
await fs.writeFile(path, (_a2 = 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)))) != null ? _a2 : JSON.stringify(data, void 0, 2), "utf-8");
|
|
959
1193
|
} catch (err) {
|
|
960
1194
|
console.error("Error writing file:", err);
|
|
961
1195
|
}
|
|
@@ -969,8 +1203,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
969
1203
|
const value = data == null ? void 0 : data[name];
|
|
970
1204
|
if (typeof value === "undefined")
|
|
971
1205
|
return defaultValue;
|
|
972
|
-
if (typeof
|
|
973
|
-
|
|
1206
|
+
if (typeof defaultValue === "string") {
|
|
1207
|
+
if (typeof value === "object" && value !== null)
|
|
1208
|
+
return JSON.stringify(value);
|
|
1209
|
+
if (typeof value === "string")
|
|
1210
|
+
return value;
|
|
1211
|
+
return String(value);
|
|
1212
|
+
}
|
|
1213
|
+
if (typeof value === "string") {
|
|
1214
|
+
try {
|
|
1215
|
+
const parsed = JSON.parse(value);
|
|
1216
|
+
return parsed;
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
return defaultValue;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
974
1221
|
return value;
|
|
975
1222
|
}
|
|
976
1223
|
/** Sets a value in persistent storage */
|
|
@@ -979,7 +1226,18 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
979
1226
|
let data = await this.readFile();
|
|
980
1227
|
if (!data)
|
|
981
1228
|
data = {};
|
|
982
|
-
|
|
1229
|
+
let storeVal = value;
|
|
1230
|
+
if (typeof value === "string") {
|
|
1231
|
+
try {
|
|
1232
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1233
|
+
const parsed = JSON.parse(value);
|
|
1234
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1235
|
+
storeVal = parsed;
|
|
1236
|
+
}
|
|
1237
|
+
} catch (e) {
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
data[name] = storeVal;
|
|
983
1241
|
await this.writeFile(data);
|
|
984
1242
|
}).catch((err) => {
|
|
985
1243
|
console.error("Error in setValue:", err);
|
|
@@ -1012,7 +1270,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1012
1270
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
1013
1271
|
if (!fs)
|
|
1014
1272
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1015
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1273
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1016
1274
|
return await fs.unlink(path);
|
|
1017
1275
|
} catch (err) {
|
|
1018
1276
|
console.error("Error deleting file:", err);
|
|
@@ -1020,9 +1278,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1020
1278
|
}
|
|
1021
1279
|
};
|
|
1022
1280
|
var DataStoreSerializer = class _DataStoreSerializer {
|
|
1023
|
-
stores;
|
|
1024
|
-
options;
|
|
1025
1281
|
constructor(stores, options = {}) {
|
|
1282
|
+
__publicField(this, "stores");
|
|
1283
|
+
__publicField(this, "options");
|
|
1026
1284
|
if (!crypto || !crypto.subtle)
|
|
1027
1285
|
throw new ScriptContextError("DataStoreSerializer has to run in a secure context (HTTPS) or in another environment that implements the subtleCrypto API!");
|
|
1028
1286
|
this.stores = stores;
|
|
@@ -1078,8 +1336,9 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1078
1336
|
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
1079
1337
|
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
1080
1338
|
const resolveStoreId = (id) => {
|
|
1339
|
+
var _a2;
|
|
1081
1340
|
var _a;
|
|
1082
|
-
return ((_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0])
|
|
1341
|
+
return (_a2 = (_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0]) != null ? _a2 : id;
|
|
1083
1342
|
};
|
|
1084
1343
|
const matchesFilter = (id) => typeof stores === "function" ? stores(id) : stores.includes(id);
|
|
1085
1344
|
for (const storeData of deserStores) {
|
|
@@ -1156,201 +1415,6 @@ Has: ${checksum}`);
|
|
|
1156
1415
|
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1157
1416
|
}
|
|
1158
1417
|
};
|
|
1159
|
-
var createNanoEvents = () => ({
|
|
1160
|
-
emit(event, ...args) {
|
|
1161
|
-
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
1162
|
-
callbacks[i](...args);
|
|
1163
|
-
}
|
|
1164
|
-
},
|
|
1165
|
-
events: {},
|
|
1166
|
-
on(event, cb) {
|
|
1167
|
-
;
|
|
1168
|
-
(this.events[event] ||= []).push(cb);
|
|
1169
|
-
return () => {
|
|
1170
|
-
var _a;
|
|
1171
|
-
this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
});
|
|
1175
|
-
var NanoEmitter = class {
|
|
1176
|
-
events = createNanoEvents();
|
|
1177
|
-
eventUnsubscribes = [];
|
|
1178
|
-
emitterOptions;
|
|
1179
|
-
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
1180
|
-
constructor(options = {}) {
|
|
1181
|
-
this.emitterOptions = {
|
|
1182
|
-
publicEmit: false,
|
|
1183
|
-
...options
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
//#region on
|
|
1187
|
-
/**
|
|
1188
|
-
* Subscribes to an event and calls the callback when it's emitted.
|
|
1189
|
-
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
1190
|
-
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
1191
|
-
* @example ```ts
|
|
1192
|
-
* const emitter = new NanoEmitter<{
|
|
1193
|
-
* foo: (bar: string) => void;
|
|
1194
|
-
* }>({
|
|
1195
|
-
* publicEmit: true,
|
|
1196
|
-
* });
|
|
1197
|
-
*
|
|
1198
|
-
* let i = 0;
|
|
1199
|
-
* const unsub = emitter.on("foo", (bar) => {
|
|
1200
|
-
* // unsubscribe after 10 events:
|
|
1201
|
-
* if(++i === 10) unsub();
|
|
1202
|
-
* console.log(bar);
|
|
1203
|
-
* });
|
|
1204
|
-
*
|
|
1205
|
-
* emitter.emit("foo", "bar");
|
|
1206
|
-
* ```
|
|
1207
|
-
*/
|
|
1208
|
-
on(event, cb) {
|
|
1209
|
-
let unsub;
|
|
1210
|
-
const unsubProxy = () => {
|
|
1211
|
-
if (!unsub)
|
|
1212
|
-
return;
|
|
1213
|
-
unsub();
|
|
1214
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
1215
|
-
};
|
|
1216
|
-
unsub = this.events.on(event, cb);
|
|
1217
|
-
this.eventUnsubscribes.push(unsub);
|
|
1218
|
-
return unsubProxy;
|
|
1219
|
-
}
|
|
1220
|
-
//#region once
|
|
1221
|
-
/**
|
|
1222
|
-
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
1223
|
-
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
1224
|
-
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
1225
|
-
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
1226
|
-
* @example ```ts
|
|
1227
|
-
* const emitter = new NanoEmitter<{
|
|
1228
|
-
* foo: (bar: string) => void;
|
|
1229
|
-
* }>();
|
|
1230
|
-
*
|
|
1231
|
-
* // Promise syntax:
|
|
1232
|
-
* const [bar] = await emitter.once("foo");
|
|
1233
|
-
* console.log(bar);
|
|
1234
|
-
*
|
|
1235
|
-
* // Callback syntax:
|
|
1236
|
-
* emitter.once("foo", (bar) => console.log(bar));
|
|
1237
|
-
* ```
|
|
1238
|
-
*/
|
|
1239
|
-
once(event, cb) {
|
|
1240
|
-
return new Promise((resolve) => {
|
|
1241
|
-
let unsub;
|
|
1242
|
-
const onceProxy = ((...args) => {
|
|
1243
|
-
cb == null ? void 0 : cb(...args);
|
|
1244
|
-
unsub == null ? void 0 : unsub();
|
|
1245
|
-
resolve(args);
|
|
1246
|
-
});
|
|
1247
|
-
unsub = this.events.on(event, onceProxy);
|
|
1248
|
-
this.eventUnsubscribes.push(unsub);
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
//#region onMulti
|
|
1252
|
-
/**
|
|
1253
|
-
* Allows subscribing to multiple events and calling the callback only when one of, all of, or a subset of the events are emitted, either continuously or only once.
|
|
1254
|
-
* @param options An object or array of objects with the following properties:
|
|
1255
|
-
* `callback` (required) is the function that will be called when the conditions are met.
|
|
1256
|
-
*
|
|
1257
|
-
* 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.
|
|
1258
|
-
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
1259
|
-
*
|
|
1260
|
-
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
1261
|
-
* 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.
|
|
1262
|
-
* 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.
|
|
1263
|
-
* At least one of `oneOf` or `allOf` must be provided.
|
|
1264
|
-
*
|
|
1265
|
-
* @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.
|
|
1266
|
-
*/
|
|
1267
|
-
onMulti(options) {
|
|
1268
|
-
const allUnsubs = [];
|
|
1269
|
-
const unsubAll = () => {
|
|
1270
|
-
for (const unsub of allUnsubs)
|
|
1271
|
-
unsub();
|
|
1272
|
-
allUnsubs.splice(0, allUnsubs.length);
|
|
1273
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
1274
|
-
};
|
|
1275
|
-
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
1276
|
-
const optsWithDefaults = {
|
|
1277
|
-
allOf: [],
|
|
1278
|
-
oneOf: [],
|
|
1279
|
-
once: false,
|
|
1280
|
-
...opts
|
|
1281
|
-
};
|
|
1282
|
-
const {
|
|
1283
|
-
oneOf,
|
|
1284
|
-
allOf,
|
|
1285
|
-
once,
|
|
1286
|
-
signal,
|
|
1287
|
-
callback
|
|
1288
|
-
} = optsWithDefaults;
|
|
1289
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
1290
|
-
return unsubAll;
|
|
1291
|
-
if (oneOf.length === 0 && allOf.length === 0)
|
|
1292
|
-
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1293
|
-
const curEvtUnsubs = [];
|
|
1294
|
-
const checkUnsubAllEvt = (force = false) => {
|
|
1295
|
-
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
1296
|
-
return;
|
|
1297
|
-
for (const unsub of curEvtUnsubs)
|
|
1298
|
-
unsub();
|
|
1299
|
-
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
1300
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
1301
|
-
};
|
|
1302
|
-
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1303
|
-
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
1304
|
-
for (const event of oneOf) {
|
|
1305
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1306
|
-
checkUnsubAllEvt();
|
|
1307
|
-
if (allOfConditionMet()) {
|
|
1308
|
-
callback(event, ...args);
|
|
1309
|
-
if (once)
|
|
1310
|
-
checkUnsubAllEvt(true);
|
|
1311
|
-
}
|
|
1312
|
-
}));
|
|
1313
|
-
curEvtUnsubs.push(unsub);
|
|
1314
|
-
}
|
|
1315
|
-
for (const event of allOf) {
|
|
1316
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1317
|
-
checkUnsubAllEvt();
|
|
1318
|
-
allOfEmitted.add(event);
|
|
1319
|
-
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
1320
|
-
callback(event, ...args);
|
|
1321
|
-
if (once)
|
|
1322
|
-
checkUnsubAllEvt(true);
|
|
1323
|
-
}
|
|
1324
|
-
}));
|
|
1325
|
-
curEvtUnsubs.push(unsub);
|
|
1326
|
-
}
|
|
1327
|
-
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
1328
|
-
}
|
|
1329
|
-
return unsubAll;
|
|
1330
|
-
}
|
|
1331
|
-
//#region emit
|
|
1332
|
-
/**
|
|
1333
|
-
* Emits an event on this instance.
|
|
1334
|
-
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
1335
|
-
* @param event The event to emit
|
|
1336
|
-
* @param args The arguments to pass to the event listeners
|
|
1337
|
-
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
1338
|
-
*/
|
|
1339
|
-
emit(event, ...args) {
|
|
1340
|
-
if (this.emitterOptions.publicEmit) {
|
|
1341
|
-
this.events.emit(event, ...args);
|
|
1342
|
-
return true;
|
|
1343
|
-
}
|
|
1344
|
-
return false;
|
|
1345
|
-
}
|
|
1346
|
-
//#region unsubscribeAll
|
|
1347
|
-
/** Unsubscribes all event listeners from this instance */
|
|
1348
|
-
unsubscribeAll() {
|
|
1349
|
-
for (const unsub of this.eventUnsubscribes)
|
|
1350
|
-
unsub();
|
|
1351
|
-
this.eventUnsubscribes = [];
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
1418
|
var Debouncer = class extends NanoEmitter {
|
|
1355
1419
|
/**
|
|
1356
1420
|
* Creates a new debouncer with the specified timeout and edge type.
|
|
@@ -1359,15 +1423,15 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1359
1423
|
*/
|
|
1360
1424
|
constructor(timeout = 200, type = "immediate") {
|
|
1361
1425
|
super();
|
|
1426
|
+
/** All registered listener functions and the time they were attached */
|
|
1427
|
+
__publicField(this, "listeners", []);
|
|
1428
|
+
/** The currently active timeout */
|
|
1429
|
+
__publicField(this, "activeTimeout");
|
|
1430
|
+
/** The latest queued call */
|
|
1431
|
+
__publicField(this, "queuedCall");
|
|
1362
1432
|
this.timeout = timeout;
|
|
1363
1433
|
this.type = type;
|
|
1364
1434
|
}
|
|
1365
|
-
/** All registered listener functions and the time they were attached */
|
|
1366
|
-
listeners = [];
|
|
1367
|
-
/** The currently active timeout */
|
|
1368
|
-
activeTimeout;
|
|
1369
|
-
/** The latest queued call */
|
|
1370
|
-
queuedCall;
|
|
1371
1435
|
//#region listeners
|
|
1372
1436
|
/** Adds a listener function that will be called on timeout */
|
|
1373
1437
|
addListener(fn) {
|
|
@@ -1454,6 +1518,22 @@ function debounce(fn, timeout = 200, type = "immediate") {
|
|
|
1454
1518
|
return func;
|
|
1455
1519
|
}
|
|
1456
1520
|
|
|
1521
|
+
// lib/consts.ts
|
|
1522
|
+
var rawConsts = {
|
|
1523
|
+
coreUtilsVersion: "3.3.0",
|
|
1524
|
+
userUtilsVersion: "10.1.0"
|
|
1525
|
+
};
|
|
1526
|
+
function getConst(constKey, defaultVal) {
|
|
1527
|
+
const val = rawConsts[constKey];
|
|
1528
|
+
return val.match(/^#\{\{.+\}\}$/) ? defaultVal : val;
|
|
1529
|
+
}
|
|
1530
|
+
var versions = {
|
|
1531
|
+
/** Semver version string of the bundled library CoreUtils. */
|
|
1532
|
+
CoreUtils: getConst("coreUtilsVersion", "ERR:unknown"),
|
|
1533
|
+
/** Semver version string of UserUtils. */
|
|
1534
|
+
UserUtils: getConst("userUtilsVersion", "ERR:unknown")
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1457
1537
|
// lib/Errors.ts
|
|
1458
1538
|
var PlatformError = class extends DatedError {
|
|
1459
1539
|
constructor(message, options) {
|
|
@@ -1466,7 +1546,7 @@ var PlatformError = class extends DatedError {
|
|
|
1466
1546
|
function getUnsafeWindow() {
|
|
1467
1547
|
try {
|
|
1468
1548
|
return unsafeWindow;
|
|
1469
|
-
} catch {
|
|
1549
|
+
} catch (e) {
|
|
1470
1550
|
return window;
|
|
1471
1551
|
}
|
|
1472
1552
|
}
|
|
@@ -1497,7 +1577,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1497
1577
|
try {
|
|
1498
1578
|
if (typeof window.GM === "object")
|
|
1499
1579
|
GM.openInTab(href, background);
|
|
1500
|
-
} catch {
|
|
1580
|
+
} catch (e) {
|
|
1501
1581
|
const openElem = document.createElement("a");
|
|
1502
1582
|
Object.assign(openElem, {
|
|
1503
1583
|
className: "userutils-open-in-new-tab",
|
|
@@ -1517,7 +1597,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1517
1597
|
setTimeout(() => {
|
|
1518
1598
|
try {
|
|
1519
1599
|
openElem.remove();
|
|
1520
|
-
} catch {
|
|
1600
|
+
} catch (e2) {
|
|
1521
1601
|
}
|
|
1522
1602
|
}, 0);
|
|
1523
1603
|
}
|
|
@@ -1533,8 +1613,8 @@ function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
|
1533
1613
|
}
|
|
1534
1614
|
(function(original) {
|
|
1535
1615
|
eventObject.__proto__.addEventListener = function(...args) {
|
|
1536
|
-
var _a2;
|
|
1537
|
-
const origListener = typeof args[1] === "function" ? args[1] : ((_a2 = args[1]) == null ? void 0 : _a2.handleEvent)
|
|
1616
|
+
var _a2, _b;
|
|
1617
|
+
const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? void 0 : _a2.handleEvent) != null ? _b : (() => void 0);
|
|
1538
1618
|
args[1] = function(...a) {
|
|
1539
1619
|
if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
|
|
1540
1620
|
return;
|
|
@@ -1577,8 +1657,8 @@ function observeElementProp(element, property, callback) {
|
|
|
1577
1657
|
}
|
|
1578
1658
|
}
|
|
1579
1659
|
function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) {
|
|
1580
|
-
var _a;
|
|
1581
|
-
const siblings = [...((_a = refElement.parentNode) == null ? void 0 : _a.childNodes)
|
|
1660
|
+
var _a, _b;
|
|
1661
|
+
const siblings = [...(_b = (_a = refElement.parentNode) == null ? void 0 : _a.childNodes) != null ? _b : []];
|
|
1582
1662
|
const elemSiblIdx = siblings.indexOf(refElement);
|
|
1583
1663
|
if (elemSiblIdx === -1)
|
|
1584
1664
|
throw new Error("Element doesn't have a parent node");
|
|
@@ -1599,13 +1679,13 @@ function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "cent
|
|
|
1599
1679
|
}
|
|
1600
1680
|
var ttPolicy;
|
|
1601
1681
|
function setInnerHtmlUnsafe(element, html) {
|
|
1602
|
-
var _a, _b;
|
|
1682
|
+
var _a, _b, _c;
|
|
1603
1683
|
if (!ttPolicy && typeof ((_a = window == null ? void 0 : window.trustedTypes) == null ? void 0 : _a.createPolicy) === "function") {
|
|
1604
1684
|
ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", {
|
|
1605
1685
|
createHTML: (unsafeHtml) => unsafeHtml
|
|
1606
1686
|
});
|
|
1607
1687
|
}
|
|
1608
|
-
element.innerHTML = ((_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html))
|
|
1688
|
+
element.innerHTML = (_c = (_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
1609
1689
|
return element;
|
|
1610
1690
|
}
|
|
1611
1691
|
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
@@ -1798,20 +1878,20 @@ var defaultStrings = {
|
|
|
1798
1878
|
closeDialogTooltip: "Click to close the dialog"
|
|
1799
1879
|
};
|
|
1800
1880
|
var Dialog = class _Dialog extends NanoEmitter {
|
|
1801
|
-
/** Options passed to the dialog in the constructor */
|
|
1802
|
-
options;
|
|
1803
|
-
/** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */
|
|
1804
|
-
id;
|
|
1805
|
-
/** Strings used in the dialog (used for translations) */
|
|
1806
|
-
strings;
|
|
1807
|
-
dialogOpen = false;
|
|
1808
|
-
dialogMounted = false;
|
|
1809
1881
|
constructor(options) {
|
|
1810
1882
|
super();
|
|
1883
|
+
/** Options passed to the dialog in the constructor */
|
|
1884
|
+
__publicField(this, "options");
|
|
1885
|
+
/** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */
|
|
1886
|
+
__publicField(this, "id");
|
|
1887
|
+
/** Strings used in the dialog (used for translations) */
|
|
1888
|
+
__publicField(this, "strings");
|
|
1889
|
+
__publicField(this, "dialogOpen", false);
|
|
1890
|
+
__publicField(this, "dialogMounted", false);
|
|
1811
1891
|
const { strings, ...opts } = options;
|
|
1812
1892
|
this.strings = {
|
|
1813
1893
|
...defaultStrings,
|
|
1814
|
-
...strings
|
|
1894
|
+
...strings != null ? strings : {}
|
|
1815
1895
|
};
|
|
1816
1896
|
this.options = {
|
|
1817
1897
|
closeOnBgClick: true,
|
|
@@ -1828,11 +1908,12 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1828
1908
|
//#region public
|
|
1829
1909
|
/** Call after DOMContentLoaded to pre-render the dialog and invisibly mount it in the DOM */
|
|
1830
1910
|
async mount() {
|
|
1911
|
+
var _a;
|
|
1831
1912
|
if (this.dialogMounted)
|
|
1832
1913
|
return;
|
|
1833
1914
|
this.dialogMounted = true;
|
|
1834
1915
|
if (!document.querySelector("style.uu-dialog-css"))
|
|
1835
|
-
addGlobalStyle(this.options.dialogCss
|
|
1916
|
+
addGlobalStyle((_a = this.options.dialogCss) != null ? _a : defaultDialogCss).classList.add("uu-dialog-css");
|
|
1836
1917
|
const bgElem = document.createElement("div");
|
|
1837
1918
|
bgElem.id = `uu-${this.id}-dialog-bg`;
|
|
1838
1919
|
bgElem.classList.add("uu-dialog-bg");
|
|
@@ -1900,7 +1981,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1900
1981
|
}
|
|
1901
1982
|
/** Closes the dialog - prevents default action and immediate propagation of the passed event */
|
|
1902
1983
|
close(e) {
|
|
1903
|
-
var _a;
|
|
1984
|
+
var _a, _b;
|
|
1904
1985
|
e == null ? void 0 : e.preventDefault();
|
|
1905
1986
|
e == null ? void 0 : e.stopImmediatePropagation();
|
|
1906
1987
|
if (!this.isOpen())
|
|
@@ -1913,9 +1994,9 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1913
1994
|
dialogBg.style.display = "none";
|
|
1914
1995
|
dialogBg.inert = true;
|
|
1915
1996
|
openDialogs.splice(openDialogs.indexOf(this.id), 1);
|
|
1916
|
-
currentDialogId = openDialogs[0]
|
|
1997
|
+
currentDialogId = (_a = openDialogs[0]) != null ? _a : null;
|
|
1917
1998
|
if (currentDialogId)
|
|
1918
|
-
(
|
|
1999
|
+
(_b = document.querySelector(`#uu-${currentDialogId}-dialog-bg`)) == null ? void 0 : _b.removeAttribute("inert");
|
|
1919
2000
|
if (openDialogs.length === 0) {
|
|
1920
2001
|
document.body.classList.add("uu-no-select");
|
|
1921
2002
|
document.body.removeAttribute("inert");
|
|
@@ -1951,7 +2032,8 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1951
2032
|
}
|
|
1952
2033
|
//#region protected
|
|
1953
2034
|
getString(key) {
|
|
1954
|
-
|
|
2035
|
+
var _a;
|
|
2036
|
+
return (_a = this.strings[key]) != null ? _a : defaultStrings[key];
|
|
1955
2037
|
}
|
|
1956
2038
|
/** Called once to attach all generic event listeners */
|
|
1957
2039
|
attachListeners(bgElem) {
|
|
@@ -1976,7 +2058,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1976
2058
|
* @param listenerOptions Provide a {@linkcode listenerOptions} object to configure the listeners
|
|
1977
2059
|
*/
|
|
1978
2060
|
onInteraction(elem, listener, listenerOptions) {
|
|
1979
|
-
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions
|
|
2061
|
+
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions != null ? listenerOptions : {};
|
|
1980
2062
|
const interactionKeys = ["Enter", " ", "Space"];
|
|
1981
2063
|
const proxListener = (e) => {
|
|
1982
2064
|
if (e instanceof KeyboardEvent) {
|
|
@@ -2056,7 +2138,6 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
2056
2138
|
|
|
2057
2139
|
// lib/GMStorageEngine.ts
|
|
2058
2140
|
var GMStorageEngine = class extends DataStoreEngine {
|
|
2059
|
-
options;
|
|
2060
2141
|
/**
|
|
2061
2142
|
* Creates an instance of `GMStorageEngine`.
|
|
2062
2143
|
*
|
|
@@ -2065,6 +2146,7 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
2065
2146
|
*/
|
|
2066
2147
|
constructor(options) {
|
|
2067
2148
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
2149
|
+
__publicField(this, "options");
|
|
2068
2150
|
this.options = {
|
|
2069
2151
|
...options
|
|
2070
2152
|
};
|
|
@@ -2120,25 +2202,26 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
2120
2202
|
|
|
2121
2203
|
// lib/Mixins.ts
|
|
2122
2204
|
var Mixins = class {
|
|
2123
|
-
/** List of all registered mixins */
|
|
2124
|
-
mixins = [];
|
|
2125
|
-
/** Default configuration object for mixins */
|
|
2126
|
-
defaultMixinCfg;
|
|
2127
|
-
/** Whether the priorities should auto-increment if not specified */
|
|
2128
|
-
autoIncPrioEnabled;
|
|
2129
|
-
/** The current auto-increment priority counter */
|
|
2130
|
-
autoIncPrioCounter = /* @__PURE__ */ new Map();
|
|
2131
2205
|
/**
|
|
2132
2206
|
* Creates a new Mixins instance.
|
|
2133
2207
|
* @param config Configuration object to customize the behavior.
|
|
2134
2208
|
*/
|
|
2135
2209
|
constructor(config = {}) {
|
|
2210
|
+
/** List of all registered mixins */
|
|
2211
|
+
__publicField(this, "mixins", []);
|
|
2212
|
+
/** Default configuration object for mixins */
|
|
2213
|
+
__publicField(this, "defaultMixinCfg");
|
|
2214
|
+
/** Whether the priorities should auto-increment if not specified */
|
|
2215
|
+
__publicField(this, "autoIncPrioEnabled");
|
|
2216
|
+
/** The current auto-increment priority counter */
|
|
2217
|
+
__publicField(this, "autoIncPrioCounter", /* @__PURE__ */ new Map());
|
|
2218
|
+
var _a, _b, _c;
|
|
2136
2219
|
this.defaultMixinCfg = pureObj({
|
|
2137
|
-
priority: config.defaultPriority
|
|
2138
|
-
stopPropagation: config.defaultStopPropagation
|
|
2220
|
+
priority: (_a = config.defaultPriority) != null ? _a : 0,
|
|
2221
|
+
stopPropagation: (_b = config.defaultStopPropagation) != null ? _b : false,
|
|
2139
2222
|
signal: config.defaultSignal
|
|
2140
2223
|
});
|
|
2141
|
-
this.autoIncPrioEnabled = config.autoIncrementPriority
|
|
2224
|
+
this.autoIncPrioEnabled = (_c = config.autoIncrementPriority) != null ? _c : false;
|
|
2142
2225
|
}
|
|
2143
2226
|
//#region public
|
|
2144
2227
|
/**
|
|
@@ -2203,10 +2286,11 @@ var Mixins = class {
|
|
|
2203
2286
|
//#region protected
|
|
2204
2287
|
/** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
|
|
2205
2288
|
calcPriority(mixinKey, config) {
|
|
2289
|
+
var _a;
|
|
2206
2290
|
if (config.priority !== void 0)
|
|
2207
2291
|
return void 0;
|
|
2208
2292
|
if (!this.autoIncPrioEnabled)
|
|
2209
|
-
return config.priority
|
|
2293
|
+
return (_a = config.priority) != null ? _a : this.defaultMixinCfg.priority;
|
|
2210
2294
|
if (!this.autoIncPrioCounter.has(mixinKey))
|
|
2211
2295
|
this.autoIncPrioCounter.set(mixinKey, this.defaultMixinCfg.priority);
|
|
2212
2296
|
let prio = this.autoIncPrioCounter.get(mixinKey);
|
|
@@ -2227,13 +2311,13 @@ var Mixins = class {
|
|
|
2227
2311
|
|
|
2228
2312
|
// lib/SelectorObserver.ts
|
|
2229
2313
|
var SelectorObserver = class {
|
|
2230
|
-
enabled = false;
|
|
2231
|
-
baseElement;
|
|
2232
|
-
observer;
|
|
2233
|
-
observerOptions;
|
|
2234
|
-
customOptions;
|
|
2235
|
-
listenerMap;
|
|
2236
2314
|
constructor(baseElement, options = {}) {
|
|
2315
|
+
__publicField(this, "enabled", false);
|
|
2316
|
+
__publicField(this, "baseElement");
|
|
2317
|
+
__publicField(this, "observer");
|
|
2318
|
+
__publicField(this, "observerOptions");
|
|
2319
|
+
__publicField(this, "customOptions");
|
|
2320
|
+
__publicField(this, "listenerMap");
|
|
2237
2321
|
this.baseElement = baseElement;
|
|
2238
2322
|
this.listenerMap = /* @__PURE__ */ new Map();
|
|
2239
2323
|
const {
|
|
@@ -2249,10 +2333,10 @@ var SelectorObserver = class {
|
|
|
2249
2333
|
...observerOptions
|
|
2250
2334
|
};
|
|
2251
2335
|
this.customOptions = {
|
|
2252
|
-
defaultDebounce: defaultDebounce
|
|
2253
|
-
defaultDebounceType: defaultDebounceType
|
|
2254
|
-
disableOnNoListeners: disableOnNoListeners
|
|
2255
|
-
enableOnAddListener: enableOnAddListener
|
|
2336
|
+
defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
|
|
2337
|
+
defaultDebounceType: defaultDebounceType != null ? defaultDebounceType : "immediate",
|
|
2338
|
+
disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
|
|
2339
|
+
enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
|
|
2256
2340
|
};
|
|
2257
2341
|
if (typeof this.customOptions.checkInterval !== "number") {
|
|
2258
2342
|
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
@@ -2403,9 +2487,9 @@ var valTransforms = [];
|
|
|
2403
2487
|
var fallbackLang;
|
|
2404
2488
|
function translate(language, key, ...trArgs) {
|
|
2405
2489
|
if (typeof language !== "string")
|
|
2406
|
-
language = fallbackLang
|
|
2490
|
+
language = fallbackLang != null ? fallbackLang : "";
|
|
2407
2491
|
const trObj = trans[language];
|
|
2408
|
-
if (typeof language !== "string" ||
|
|
2492
|
+
if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
|
|
2409
2493
|
return fallbackLang && language !== fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
2410
2494
|
const transformTrVal = (trKey, trValue) => {
|
|
2411
2495
|
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
@@ -2457,15 +2541,18 @@ function trFor(language, key, ...args) {
|
|
|
2457
2541
|
function useTr(language) {
|
|
2458
2542
|
return (key, ...args) => translate(language, key, ...args);
|
|
2459
2543
|
}
|
|
2460
|
-
function hasKey(language = fallbackLang
|
|
2544
|
+
function hasKey(language = fallbackLang != null ? fallbackLang : "", key) {
|
|
2461
2545
|
return tr.for(language, key) !== key;
|
|
2462
2546
|
}
|
|
2463
2547
|
function addTranslations(language, translations) {
|
|
2464
2548
|
trans[language] = JSON.parse(JSON.stringify(translations));
|
|
2465
2549
|
}
|
|
2466
|
-
function getTranslations(language = fallbackLang
|
|
2550
|
+
function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
|
|
2467
2551
|
return trans[language];
|
|
2468
2552
|
}
|
|
2553
|
+
function getAllTranslations(asCopy = true) {
|
|
2554
|
+
return asCopy ? JSON.parse(JSON.stringify(trans)) : trans;
|
|
2555
|
+
}
|
|
2469
2556
|
var deleteTranslations = (language) => {
|
|
2470
2557
|
if (language in trans) {
|
|
2471
2558
|
delete trans[language];
|
|
@@ -2496,51 +2583,55 @@ function deleteTransform(patternOrFn) {
|
|
|
2496
2583
|
}
|
|
2497
2584
|
return false;
|
|
2498
2585
|
}
|
|
2499
|
-
var
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2510
|
-
if (typeof repl !== "undefined")
|
|
2511
|
-
str = str.replace(match[0], String(repl));
|
|
2512
|
-
}
|
|
2513
|
-
};
|
|
2514
|
-
const positionalMapping = () => {
|
|
2515
|
-
if (!patternRegex.test(str) || !trArgs[0])
|
|
2516
|
-
return;
|
|
2517
|
-
let matchNum = -1;
|
|
2518
|
-
for (const match of matches) {
|
|
2519
|
-
matchNum++;
|
|
2520
|
-
if (typeof trArgs[matchNum] !== "undefined")
|
|
2521
|
-
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2522
|
-
}
|
|
2523
|
-
};
|
|
2524
|
-
let notStringifiable = false;
|
|
2525
|
-
try {
|
|
2526
|
-
String(trArgs[0]);
|
|
2527
|
-
} catch {
|
|
2528
|
-
notStringifiable = true;
|
|
2586
|
+
var commonKeyedTransform = ({ matches, trArgs, trValue }, patternRegex, quickMatchPattern) => {
|
|
2587
|
+
let str = String(trValue);
|
|
2588
|
+
const someMatchKeyInArgs = (obj) => matches.some((match) => match[1] !== void 0 && match[1] in obj);
|
|
2589
|
+
const namedMapping = () => {
|
|
2590
|
+
if (!str.includes(quickMatchPattern) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !someMatchKeyInArgs(trArgs[0]))
|
|
2591
|
+
return;
|
|
2592
|
+
for (const match of matches) {
|
|
2593
|
+
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2594
|
+
if (typeof repl !== "undefined")
|
|
2595
|
+
str = str.replace(match[0], String(repl));
|
|
2529
2596
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2597
|
+
};
|
|
2598
|
+
const positionalMapping = () => {
|
|
2599
|
+
if (!patternRegex.test(str) || typeof trArgs[0] === "undefined")
|
|
2600
|
+
return;
|
|
2601
|
+
let matchNum = -1;
|
|
2602
|
+
for (const match of matches) {
|
|
2603
|
+
matchNum++;
|
|
2604
|
+
if (typeof trArgs[matchNum] !== "undefined")
|
|
2605
|
+
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
let notStringifiable = false;
|
|
2609
|
+
try {
|
|
2610
|
+
void `${trArgs[0]}`;
|
|
2611
|
+
} catch (e) {
|
|
2612
|
+
notStringifiable = true;
|
|
2536
2613
|
}
|
|
2614
|
+
const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && (notStringifiable || String(trArgs[0]).startsWith("[object"));
|
|
2615
|
+
if (isArgsObject && someMatchKeyInArgs(trArgs[0]))
|
|
2616
|
+
namedMapping();
|
|
2617
|
+
else
|
|
2618
|
+
positionalMapping();
|
|
2619
|
+
return str;
|
|
2620
|
+
};
|
|
2621
|
+
var templateLiteralTransform = [
|
|
2622
|
+
/\$\{([a-zA-Z0-9$_-]+)\}/gm,
|
|
2623
|
+
(tfProps) => commonKeyedTransform(tfProps, /\$\{.+\}/m, "${")
|
|
2624
|
+
];
|
|
2625
|
+
var i18nTransform = [
|
|
2626
|
+
/\{\{([a-zA-Z0-9$_-]+)\}\}/gm,
|
|
2627
|
+
(tfProps) => commonKeyedTransform(tfProps, /\{\{.+\}\}/m, "{{")
|
|
2537
2628
|
];
|
|
2538
2629
|
var percentTransform = [
|
|
2539
2630
|
/%(\d+)/gm,
|
|
2540
2631
|
({ matches, trArgs, trValue }) => {
|
|
2541
2632
|
let str = String(trValue);
|
|
2542
2633
|
for (const match of matches) {
|
|
2543
|
-
const repl =
|
|
2634
|
+
const repl = trArgs == null ? void 0 : trArgs[Number(match[1]) - 1];
|
|
2544
2635
|
if (typeof repl !== "undefined")
|
|
2545
2636
|
str = str.replace(match[0], String(repl));
|
|
2546
2637
|
}
|
|
@@ -2550,17 +2641,84 @@ var percentTransform = [
|
|
|
2550
2641
|
var tr = {
|
|
2551
2642
|
for: (...params) => trFor(...params),
|
|
2552
2643
|
use: (...params) => useTr(...params),
|
|
2553
|
-
hasKey: (language = fallbackLang
|
|
2644
|
+
hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
|
|
2554
2645
|
addTranslations,
|
|
2555
2646
|
getTranslations,
|
|
2647
|
+
getAllTranslations,
|
|
2556
2648
|
deleteTranslations,
|
|
2557
2649
|
setFallbackLanguage,
|
|
2558
2650
|
getFallbackLanguage,
|
|
2559
2651
|
addTransform,
|
|
2560
2652
|
deleteTransform,
|
|
2653
|
+
/** Collection of predefined transform functions that can be added via {@linkcode tr.addTransform()} */
|
|
2561
2654
|
transforms: {
|
|
2655
|
+
/**
|
|
2656
|
+
* This transform will replace placeholders matching `${key}` with the value of the passed argument(s).
|
|
2657
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2658
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2659
|
+
* - Positional: If the first argument is not an object or has a `toString()` method that returns something that doesn't start with `[object`, the values will be positionally inserted in the order they were passed.
|
|
2660
|
+
*
|
|
2661
|
+
* @example ```ts
|
|
2662
|
+
* tr.addTranslations("en", {
|
|
2663
|
+
* "greeting": "Hello, ${user}!\nYou have ${notifs} notifications.",
|
|
2664
|
+
* });
|
|
2665
|
+
* tr.addTransform(tr.transforms.templateLiteral);
|
|
2666
|
+
*
|
|
2667
|
+
* const t = tr.use("en");
|
|
2668
|
+
*
|
|
2669
|
+
* // both calls return the same result:
|
|
2670
|
+
* t("greeting", { user: "John", notifs: 5 }); // "Hello, John!\nYou have 5 notifications."
|
|
2671
|
+
* t("greeting", "John", 5); // "Hello, John!\nYou have 5 notifications."
|
|
2672
|
+
*
|
|
2673
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2674
|
+
* t("greeting", { user: "John" }); // "Hello, John!\nYou have ${notifs} notifications."
|
|
2675
|
+
* ```
|
|
2676
|
+
*/
|
|
2562
2677
|
templateLiteral: templateLiteralTransform,
|
|
2678
|
+
/**
|
|
2679
|
+
* This transform will replace placeholders matching `{{key}}` with the value of the passed argument(s).
|
|
2680
|
+
* This format is commonly used in i18n libraries. Note that advanced syntax is not supported, only simple key replacement.
|
|
2681
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2682
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2683
|
+
* - Positional: If the first argument is not an object or has a `toString()` method that returns something that doesn't start with `[object`, the values will be positionally inserted in the order they were passed.
|
|
2684
|
+
*
|
|
2685
|
+
* @example ```ts
|
|
2686
|
+
* tr.addTranslations("en", {
|
|
2687
|
+
* "greeting": "Hello, {{user}}!\nYou have {{notifs}} notifications.",
|
|
2688
|
+
* });
|
|
2689
|
+
* tr.addTransform(tr.transforms.i18n);
|
|
2690
|
+
*
|
|
2691
|
+
* const t = tr.use("en");
|
|
2692
|
+
*
|
|
2693
|
+
* // both calls return the same result:
|
|
2694
|
+
* t("greeting", { user: "Alice", notifs: 5 }); // "Hello, Alice!\nYou have 5 notifications."
|
|
2695
|
+
* t("greeting", "Alice", 5); // "Hello, Alice!\nYou have 5 notifications."
|
|
2696
|
+
*
|
|
2697
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2698
|
+
* t("greeting", { user: "Alice" }); // "Hello, Alice!\nYou have {{notifs}} notifications."
|
|
2699
|
+
* ```
|
|
2700
|
+
*/
|
|
2701
|
+
i18n: i18nTransform,
|
|
2702
|
+
/**
|
|
2703
|
+
* This transform will replace `%n` placeholders with the value of the passed arguments.
|
|
2704
|
+
* The `%n` placeholders are 1-indexed, meaning `%1` will be replaced by the first argument (index 0), `%2` by the second (index 1), and so on.
|
|
2705
|
+
* Objects will be stringified via `String()` before being inserted.
|
|
2706
|
+
*
|
|
2707
|
+
* @example ```ts
|
|
2708
|
+
* tr.addTranslations("en", {
|
|
2709
|
+
* "greeting": "Hello, %1!\nYou have %2 notifications.",
|
|
2710
|
+
* });
|
|
2711
|
+
* tr.addTransform(tr.transforms.percent);
|
|
2712
|
+
*
|
|
2713
|
+
* const t = tr.use("en");
|
|
2714
|
+
*
|
|
2715
|
+
* // arguments are inserted in the order they're passed:
|
|
2716
|
+
* t("greeting", "Bob", 5); // "Hello, Bob!\nYou have 5 notifications."
|
|
2717
|
+
*
|
|
2718
|
+
* // when a value isn't found, the placeholder will be left as-is:
|
|
2719
|
+
* t("greeting", "Bob"); // "Hello, Bob!\nYou have %2 notifications."
|
|
2720
|
+
* ```
|
|
2721
|
+
*/
|
|
2563
2722
|
percent: percentTransform
|
|
2564
2723
|
}
|
|
2565
2724
|
};
|
|
2566
|
-
//# sourceMappingURL=UserUtils.cjs.map
|