@sv443-network/userutils 10.0.5 → 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 +24 -0
- package/README.md +38 -32
- package/dist/UserUtils.cjs +555 -397
- package/dist/UserUtils.mjs +557 -397
- package/dist/UserUtils.umd.js +443 -304
- 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.mjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// node_modules/.pnpm/@sv443-network+coreutils@3.3.0/node_modules/@sv443-network/coreutils/dist/CoreUtils.mjs
|
|
2
6
|
function bitSetHas(bitSet, checkVal) {
|
|
3
7
|
return (bitSet & checkVal) === checkVal;
|
|
4
8
|
}
|
|
@@ -144,7 +148,7 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
144
148
|
if (isHexCol)
|
|
145
149
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
146
150
|
else if (color.startsWith("rgba"))
|
|
147
|
-
return `rgba(${r}, ${g}, ${b}, ${a
|
|
151
|
+
return `rgba(${r}, ${g}, ${b}, ${a != null ? a : NaN})`;
|
|
148
152
|
else
|
|
149
153
|
return `rgb(${r}, ${g}, ${b})`;
|
|
150
154
|
}
|
|
@@ -177,7 +181,8 @@ function atoab(str) {
|
|
|
177
181
|
return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
|
|
178
182
|
}
|
|
179
183
|
async function compress(input, compressionFormat, outputType = "string") {
|
|
180
|
-
|
|
184
|
+
var _a;
|
|
185
|
+
const byteArray = input instanceof Uint8Array ? input : new TextEncoder().encode((_a = input == null ? void 0 : input.toString()) != null ? _a : String(input));
|
|
181
186
|
const comp = new CompressionStream(compressionFormat);
|
|
182
187
|
const writer = comp.writable.getWriter();
|
|
183
188
|
writer.write(byteArray);
|
|
@@ -186,7 +191,8 @@ async function compress(input, compressionFormat, outputType = "string") {
|
|
|
186
191
|
return outputType === "arrayBuffer" ? uintArr : abtoa(uintArr);
|
|
187
192
|
}
|
|
188
193
|
async function decompress(input, compressionFormat, outputType = "string") {
|
|
189
|
-
|
|
194
|
+
var _a;
|
|
195
|
+
const byteArray = input instanceof Uint8Array ? input : atoab((_a = input == null ? void 0 : input.toString()) != null ? _a : String(input));
|
|
190
196
|
const decomp = new DecompressionStream(compressionFormat);
|
|
191
197
|
const writer = decomp.writable.getWriter();
|
|
192
198
|
writer.write(byteArray);
|
|
@@ -231,9 +237,9 @@ function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase =
|
|
|
231
237
|
return arr.map((v) => caseArr[randRange(0, caseArr.length - 1, enhancedEntropy)] === 1 ? v.toUpperCase() : v).join("");
|
|
232
238
|
}
|
|
233
239
|
var DatedError = class extends Error {
|
|
234
|
-
date;
|
|
235
240
|
constructor(message, options) {
|
|
236
241
|
super(message, options);
|
|
242
|
+
__publicField(this, "date");
|
|
237
243
|
this.name = this.constructor.name;
|
|
238
244
|
this.date = /* @__PURE__ */ new Date();
|
|
239
245
|
}
|
|
@@ -316,7 +322,7 @@ function pauseFor(time, signal, rejectOnAbort = false) {
|
|
|
316
322
|
});
|
|
317
323
|
}
|
|
318
324
|
function pureObj(obj) {
|
|
319
|
-
return Object.assign(/* @__PURE__ */ Object.create(null), obj
|
|
325
|
+
return Object.assign(/* @__PURE__ */ Object.create(null), obj != null ? obj : {});
|
|
320
326
|
}
|
|
321
327
|
function setImmediateInterval(callback, interval, signal) {
|
|
322
328
|
let intervalId;
|
|
@@ -355,12 +361,13 @@ function scheduleExit(code = 0, timeout = 0) {
|
|
|
355
361
|
setTimeout(exit, timeout);
|
|
356
362
|
}
|
|
357
363
|
function getCallStack(asArray, lines = Infinity) {
|
|
364
|
+
var _a;
|
|
358
365
|
if (typeof lines !== "number" || isNaN(lines) || lines < 0)
|
|
359
366
|
throw new TypeError("lines parameter must be a non-negative number");
|
|
360
367
|
try {
|
|
361
368
|
throw new Error("This is to capture a stack trace with CoreUtils.getCallStack(). (If you see this somewhere, you can safely ignore it.)");
|
|
362
369
|
} catch (err) {
|
|
363
|
-
const stack = (err.stack
|
|
370
|
+
const stack = ((_a = err.stack) != null ? _a : "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
|
|
364
371
|
return asArray !== false ? stack : stack.join("\n");
|
|
365
372
|
}
|
|
366
373
|
}
|
|
@@ -413,9 +420,10 @@ function createProgressBar(percentage, barLength, chars = defaultPbChars) {
|
|
|
413
420
|
}
|
|
414
421
|
function insertValues(input, ...values) {
|
|
415
422
|
return input.replace(/%\d/gm, (match) => {
|
|
423
|
+
var _a2;
|
|
416
424
|
var _a;
|
|
417
425
|
const argIndex = Number(match.substring(1)) - 1;
|
|
418
|
-
return (_a = values[argIndex]
|
|
426
|
+
return (_a = (_a2 = values[argIndex]) != null ? _a2 : match) == null ? void 0 : _a.toString();
|
|
419
427
|
});
|
|
420
428
|
}
|
|
421
429
|
function joinArrayReadable(array, separators = ", ", lastSeparator = " and ") {
|
|
@@ -446,31 +454,209 @@ function secsToTimeStr(seconds) {
|
|
|
446
454
|
].join("");
|
|
447
455
|
}
|
|
448
456
|
function truncStr(input, length, endStr = "...") {
|
|
449
|
-
|
|
457
|
+
var _a;
|
|
458
|
+
const str = (_a = input == null ? void 0 : input.toString()) != null ? _a : String(input);
|
|
450
459
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
451
460
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
452
461
|
}
|
|
453
|
-
var
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
462
|
+
var createNanoEvents = () => ({
|
|
463
|
+
emit(event, ...args) {
|
|
464
|
+
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
465
|
+
callbacks[i](...args);
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
events: {},
|
|
469
|
+
on(event, cb) {
|
|
470
|
+
var _a;
|
|
471
|
+
;
|
|
472
|
+
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
473
|
+
return () => {
|
|
474
|
+
var _a2;
|
|
475
|
+
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
var NanoEmitter = class {
|
|
480
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
481
|
+
constructor(options = {}) {
|
|
482
|
+
__publicField(this, "events", createNanoEvents());
|
|
483
|
+
__publicField(this, "eventUnsubscribes", []);
|
|
484
|
+
__publicField(this, "emitterOptions");
|
|
485
|
+
this.emitterOptions = {
|
|
486
|
+
publicEmit: false,
|
|
487
|
+
...options
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
//#region on
|
|
491
|
+
/**
|
|
492
|
+
* Subscribes to an event and calls the callback when it's emitted.
|
|
493
|
+
* @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 "_")
|
|
494
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
495
|
+
* @example ```ts
|
|
496
|
+
* const emitter = new NanoEmitter<{
|
|
497
|
+
* foo: (bar: string) => void;
|
|
498
|
+
* }>({
|
|
499
|
+
* publicEmit: true,
|
|
500
|
+
* });
|
|
501
|
+
*
|
|
502
|
+
* let i = 0;
|
|
503
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
504
|
+
* // unsubscribe after 10 events:
|
|
505
|
+
* if(++i === 10) unsub();
|
|
506
|
+
* console.log(bar);
|
|
507
|
+
* });
|
|
508
|
+
*
|
|
509
|
+
* emitter.emit("foo", "bar");
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
on(event, cb) {
|
|
513
|
+
let unsub;
|
|
514
|
+
const unsubProxy = () => {
|
|
515
|
+
if (!unsub)
|
|
516
|
+
return;
|
|
517
|
+
unsub();
|
|
518
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
519
|
+
};
|
|
520
|
+
unsub = this.events.on(event, cb);
|
|
521
|
+
this.eventUnsubscribes.push(unsub);
|
|
522
|
+
return unsubProxy;
|
|
523
|
+
}
|
|
524
|
+
//#region once
|
|
525
|
+
/**
|
|
526
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
527
|
+
* @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 "_")
|
|
528
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
529
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
530
|
+
* @example ```ts
|
|
531
|
+
* const emitter = new NanoEmitter<{
|
|
532
|
+
* foo: (bar: string) => void;
|
|
533
|
+
* }>();
|
|
534
|
+
*
|
|
535
|
+
* // Promise syntax:
|
|
536
|
+
* const [bar] = await emitter.once("foo");
|
|
537
|
+
* console.log(bar);
|
|
538
|
+
*
|
|
539
|
+
* // Callback syntax:
|
|
540
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
once(event, cb) {
|
|
544
|
+
return new Promise((resolve) => {
|
|
545
|
+
let unsub;
|
|
546
|
+
const onceProxy = ((...args) => {
|
|
547
|
+
cb == null ? void 0 : cb(...args);
|
|
548
|
+
unsub == null ? void 0 : unsub();
|
|
549
|
+
resolve(args);
|
|
550
|
+
});
|
|
551
|
+
unsub = this.events.on(event, onceProxy);
|
|
552
|
+
this.eventUnsubscribes.push(unsub);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
//#region onMulti
|
|
556
|
+
/**
|
|
557
|
+
* 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.
|
|
558
|
+
* @param options An object or array of objects with the following properties:
|
|
559
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
560
|
+
*
|
|
561
|
+
* 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.
|
|
562
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
563
|
+
*
|
|
564
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
565
|
+
* 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.
|
|
566
|
+
* 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.
|
|
567
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
568
|
+
*
|
|
569
|
+
* @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.
|
|
570
|
+
*/
|
|
571
|
+
onMulti(options) {
|
|
572
|
+
const allUnsubs = [];
|
|
573
|
+
const unsubAll = () => {
|
|
574
|
+
for (const unsub of allUnsubs)
|
|
575
|
+
unsub();
|
|
576
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
577
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
578
|
+
};
|
|
579
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
580
|
+
const optsWithDefaults = {
|
|
581
|
+
allOf: [],
|
|
582
|
+
oneOf: [],
|
|
583
|
+
once: false,
|
|
584
|
+
...opts
|
|
585
|
+
};
|
|
586
|
+
const {
|
|
587
|
+
oneOf,
|
|
588
|
+
allOf,
|
|
589
|
+
once,
|
|
590
|
+
signal,
|
|
591
|
+
callback
|
|
592
|
+
} = optsWithDefaults;
|
|
593
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
594
|
+
return unsubAll;
|
|
595
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
596
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
597
|
+
const curEvtUnsubs = [];
|
|
598
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
599
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
600
|
+
return;
|
|
601
|
+
for (const unsub of curEvtUnsubs)
|
|
602
|
+
unsub();
|
|
603
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
604
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
605
|
+
};
|
|
606
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
607
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
608
|
+
for (const event of oneOf) {
|
|
609
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
610
|
+
checkUnsubAllEvt();
|
|
611
|
+
if (allOfConditionMet()) {
|
|
612
|
+
callback(event, ...args);
|
|
613
|
+
if (once)
|
|
614
|
+
checkUnsubAllEvt(true);
|
|
615
|
+
}
|
|
616
|
+
}));
|
|
617
|
+
curEvtUnsubs.push(unsub);
|
|
618
|
+
}
|
|
619
|
+
for (const event of allOf) {
|
|
620
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
621
|
+
checkUnsubAllEvt();
|
|
622
|
+
allOfEmitted.add(event);
|
|
623
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
624
|
+
callback(event, ...args);
|
|
625
|
+
if (once)
|
|
626
|
+
checkUnsubAllEvt(true);
|
|
627
|
+
}
|
|
628
|
+
}));
|
|
629
|
+
curEvtUnsubs.push(unsub);
|
|
630
|
+
}
|
|
631
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
632
|
+
}
|
|
633
|
+
return unsubAll;
|
|
634
|
+
}
|
|
635
|
+
//#region emit
|
|
464
636
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
637
|
+
* Emits an event on this instance.
|
|
638
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
639
|
+
* @param event The event to emit
|
|
640
|
+
* @param args The arguments to pass to the event listeners
|
|
641
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
468
642
|
*/
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
643
|
+
emit(event, ...args) {
|
|
644
|
+
if (this.emitterOptions.publicEmit) {
|
|
645
|
+
this.events.emit(event, ...args);
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
//#region unsubscribeAll
|
|
651
|
+
/** Unsubscribes all event listeners from this instance */
|
|
652
|
+
unsubscribeAll() {
|
|
653
|
+
for (const unsub of this.eventUnsubscribes)
|
|
654
|
+
unsub();
|
|
655
|
+
this.eventUnsubscribes = [];
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var dsFmtVer = 1;
|
|
659
|
+
var DataStore = class extends NanoEmitter {
|
|
474
660
|
//#region constructor
|
|
475
661
|
/**
|
|
476
662
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -482,20 +668,43 @@ var DataStore = class {
|
|
|
482
668
|
* @param opts The options for this DataStore instance
|
|
483
669
|
*/
|
|
484
670
|
constructor(opts) {
|
|
671
|
+
var _a, _b, _c;
|
|
672
|
+
super(opts.nanoEmitterOptions);
|
|
673
|
+
__publicField(this, "id");
|
|
674
|
+
__publicField(this, "formatVersion");
|
|
675
|
+
__publicField(this, "defaultData");
|
|
676
|
+
__publicField(this, "encodeData");
|
|
677
|
+
__publicField(this, "decodeData");
|
|
678
|
+
__publicField(this, "compressionFormat", "deflate-raw");
|
|
679
|
+
__publicField(this, "memoryCache");
|
|
680
|
+
__publicField(this, "engine");
|
|
681
|
+
__publicField(this, "keyPrefix");
|
|
682
|
+
__publicField(this, "options");
|
|
683
|
+
/**
|
|
684
|
+
* Whether all first-init checks should be done.
|
|
685
|
+
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
686
|
+
* 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.
|
|
687
|
+
*/
|
|
688
|
+
__publicField(this, "firstInit", true);
|
|
689
|
+
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
690
|
+
__publicField(this, "cachedData");
|
|
691
|
+
__publicField(this, "migrations");
|
|
692
|
+
__publicField(this, "migrateIds", []);
|
|
485
693
|
this.id = opts.id;
|
|
486
694
|
this.formatVersion = opts.formatVersion;
|
|
487
695
|
this.defaultData = opts.defaultData;
|
|
488
|
-
this.memoryCache =
|
|
696
|
+
this.memoryCache = (_a = opts.memoryCache) != null ? _a : true;
|
|
489
697
|
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
490
698
|
this.migrations = opts.migrations;
|
|
491
699
|
if (opts.migrateIds)
|
|
492
700
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
493
701
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
702
|
+
this.keyPrefix = (_b = opts.keyPrefix) != null ? _b : "__ds-";
|
|
494
703
|
this.options = opts;
|
|
495
704
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
496
705
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
497
706
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
498
|
-
this.compressionFormat = opts.encodeData[0]
|
|
707
|
+
this.compressionFormat = (_c = opts.encodeData[0]) != null ? _c : null;
|
|
499
708
|
} else if (opts.compressionFormat === null) {
|
|
500
709
|
this.encodeData = void 0;
|
|
501
710
|
this.decodeData = void 0;
|
|
@@ -519,6 +728,7 @@ var DataStore = class {
|
|
|
519
728
|
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
520
729
|
*/
|
|
521
730
|
async loadData() {
|
|
731
|
+
var _a;
|
|
522
732
|
try {
|
|
523
733
|
if (this.firstInit) {
|
|
524
734
|
this.firstInit = false;
|
|
@@ -532,13 +742,13 @@ var DataStore = class {
|
|
|
532
742
|
promises.push(this.engine.setValue(newKey, value));
|
|
533
743
|
promises.push(this.engine.deleteValue(oldKey));
|
|
534
744
|
};
|
|
535
|
-
migrateFmt(`_uucfg-${this.id}`,
|
|
745
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
536
746
|
if (!isNaN(oldVer))
|
|
537
|
-
migrateFmt(`_uucfgver-${this.id}`,
|
|
747
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
538
748
|
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
539
|
-
migrateFmt(`_uucfgenc-${this.id}`,
|
|
749
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? (_a = this.compressionFormat) != null ? _a : null : null);
|
|
540
750
|
else {
|
|
541
|
-
promises.push(this.engine.setValue(
|
|
751
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
542
752
|
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
543
753
|
}
|
|
544
754
|
await Promise.allSettled(promises);
|
|
@@ -550,31 +760,27 @@ var DataStore = class {
|
|
|
550
760
|
await this.migrateId(this.migrateIds);
|
|
551
761
|
this.migrateIds = [];
|
|
552
762
|
}
|
|
553
|
-
const storedDataRaw = await this.engine.getValue(
|
|
554
|
-
|
|
555
|
-
if (typeof storedDataRaw !== "string") {
|
|
556
|
-
await this.saveDefaultData();
|
|
557
|
-
|
|
763
|
+
const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
764
|
+
const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
765
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
766
|
+
await this.saveDefaultData(false);
|
|
767
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
768
|
+
this.events.emit("loadData", data);
|
|
769
|
+
return data;
|
|
558
770
|
}
|
|
559
|
-
const storedData = storedDataRaw
|
|
560
|
-
const encodingFmt = String(await this.engine.getValue(
|
|
771
|
+
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
772
|
+
const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
561
773
|
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
|
|
562
|
-
let
|
|
563
|
-
if (isNaN(storedFmtVer)) {
|
|
564
|
-
await this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
|
|
565
|
-
saveData = true;
|
|
566
|
-
}
|
|
567
|
-
let parsed = await this.engine.deserializeData(storedData, isEncoded);
|
|
774
|
+
let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
568
775
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
569
776
|
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
return this.cachedData = this.engine.deepCopy(parsed);
|
|
574
|
-
else
|
|
575
|
-
return this.engine.deepCopy(parsed);
|
|
777
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
778
|
+
this.events.emit("loadData", result);
|
|
779
|
+
return result;
|
|
576
780
|
} catch (err) {
|
|
781
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
577
782
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
783
|
+
this.events.emit("error", error);
|
|
578
784
|
await this.saveDefaultData();
|
|
579
785
|
return this.defaultData;
|
|
580
786
|
}
|
|
@@ -583,7 +789,7 @@ var DataStore = class {
|
|
|
583
789
|
/**
|
|
584
790
|
* Returns a copy of the data from the in-memory cache.
|
|
585
791
|
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
586
|
-
* ⚠️
|
|
792
|
+
* ⚠️ Only available when `memoryCache` is `true` (default). When set to `false`, this produces a type and runtime error - use {@linkcode loadData()} instead.
|
|
587
793
|
*/
|
|
588
794
|
getData() {
|
|
589
795
|
if (!this.memoryCache)
|
|
@@ -593,27 +799,47 @@ var DataStore = class {
|
|
|
593
799
|
//#region setData
|
|
594
800
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
595
801
|
setData(data) {
|
|
596
|
-
|
|
802
|
+
const dataCopy = this.engine.deepCopy(data);
|
|
803
|
+
if (this.memoryCache) {
|
|
597
804
|
this.cachedData = data;
|
|
805
|
+
this.events.emit("updateDataSync", dataCopy);
|
|
806
|
+
}
|
|
598
807
|
return new Promise(async (resolve) => {
|
|
599
|
-
await Promise.allSettled([
|
|
600
|
-
this.engine.setValue(
|
|
601
|
-
this.engine.setValue(
|
|
602
|
-
this.engine.setValue(
|
|
808
|
+
const results = await Promise.allSettled([
|
|
809
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
810
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
811
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
603
812
|
]);
|
|
813
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
814
|
+
this.events.emit("updateData", dataCopy);
|
|
815
|
+
else {
|
|
816
|
+
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
817
|
+
console.error(error);
|
|
818
|
+
this.events.emit("error", error);
|
|
819
|
+
}
|
|
604
820
|
resolve();
|
|
605
821
|
});
|
|
606
822
|
}
|
|
607
823
|
//#region saveDefaultData
|
|
608
|
-
/**
|
|
609
|
-
|
|
824
|
+
/**
|
|
825
|
+
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
826
|
+
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
827
|
+
*/
|
|
828
|
+
async saveDefaultData(emitEvent = true) {
|
|
610
829
|
if (this.memoryCache)
|
|
611
830
|
this.cachedData = this.defaultData;
|
|
612
|
-
await Promise.allSettled([
|
|
613
|
-
this.engine.setValue(
|
|
614
|
-
this.engine.setValue(
|
|
615
|
-
this.engine.setValue(
|
|
831
|
+
const results = await Promise.allSettled([
|
|
832
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
833
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
834
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
616
835
|
]);
|
|
836
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
837
|
+
emitEvent && this.events.emit("setDefaultData", this.defaultData);
|
|
838
|
+
else {
|
|
839
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
840
|
+
console.error(error);
|
|
841
|
+
this.events.emit("error", error);
|
|
842
|
+
}
|
|
617
843
|
}
|
|
618
844
|
//#region deleteData
|
|
619
845
|
/**
|
|
@@ -624,11 +850,12 @@ var DataStore = class {
|
|
|
624
850
|
async deleteData() {
|
|
625
851
|
var _a, _b;
|
|
626
852
|
await Promise.allSettled([
|
|
627
|
-
this.engine.deleteValue(
|
|
628
|
-
this.engine.deleteValue(
|
|
629
|
-
this.engine.deleteValue(
|
|
853
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
854
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
855
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
630
856
|
]);
|
|
631
857
|
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
858
|
+
this.events.emit("deleteData");
|
|
632
859
|
}
|
|
633
860
|
//#region encodingEnabled
|
|
634
861
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
@@ -649,30 +876,35 @@ var DataStore = class {
|
|
|
649
876
|
let newData = oldData;
|
|
650
877
|
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
651
878
|
let lastFmtVer = oldFmtVer;
|
|
652
|
-
for (
|
|
879
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
880
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
653
881
|
const ver = Number(fmtVer);
|
|
654
882
|
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
655
883
|
try {
|
|
656
884
|
const migRes = migrationFunc(newData);
|
|
657
885
|
newData = migRes instanceof Promise ? await migRes : migRes;
|
|
658
886
|
lastFmtVer = oldFmtVer = ver;
|
|
887
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
888
|
+
this.events.emit("migrateData", ver, newData, isFinal);
|
|
659
889
|
} catch (err) {
|
|
890
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
891
|
+
this.events.emit("migrationError", ver, migError);
|
|
892
|
+
this.events.emit("error", migError);
|
|
660
893
|
if (!resetOnError)
|
|
661
|
-
throw
|
|
894
|
+
throw migError;
|
|
662
895
|
await this.saveDefaultData();
|
|
663
|
-
return this.
|
|
896
|
+
return this.engine.deepCopy(this.defaultData);
|
|
664
897
|
}
|
|
665
898
|
}
|
|
666
899
|
}
|
|
667
900
|
await Promise.allSettled([
|
|
668
|
-
this.engine.setValue(
|
|
669
|
-
this.engine.setValue(
|
|
670
|
-
this.engine.setValue(
|
|
901
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
|
|
902
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
903
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
671
904
|
]);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return this.engine.deepCopy(newData);
|
|
905
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
906
|
+
this.events.emit("updateData", result);
|
|
907
|
+
return result;
|
|
676
908
|
}
|
|
677
909
|
//#region migrateId
|
|
678
910
|
/**
|
|
@@ -684,9 +916,9 @@ var DataStore = class {
|
|
|
684
916
|
await Promise.all(ids.map(async (id) => {
|
|
685
917
|
const [data, fmtVer, isEncoded] = await (async () => {
|
|
686
918
|
const [d, f, e] = await Promise.all([
|
|
687
|
-
this.engine.getValue(
|
|
688
|
-
this.engine.getValue(
|
|
689
|
-
this.engine.getValue(
|
|
919
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
920
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
921
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
690
922
|
]);
|
|
691
923
|
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
692
924
|
})();
|
|
@@ -694,20 +926,21 @@ var DataStore = class {
|
|
|
694
926
|
return;
|
|
695
927
|
const parsed = await this.engine.deserializeData(data, isEncoded);
|
|
696
928
|
await Promise.allSettled([
|
|
697
|
-
this.engine.setValue(
|
|
698
|
-
this.engine.setValue(
|
|
699
|
-
this.engine.setValue(
|
|
700
|
-
this.engine.deleteValue(
|
|
701
|
-
this.engine.deleteValue(
|
|
702
|
-
this.engine.deleteValue(
|
|
929
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
930
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
931
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
932
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
933
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
934
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
703
935
|
]);
|
|
936
|
+
this.events.emit("migrateId", id, this.id);
|
|
704
937
|
}));
|
|
705
938
|
}
|
|
706
939
|
};
|
|
707
940
|
var DataStoreEngine = class {
|
|
708
|
-
dataStoreOptions;
|
|
709
941
|
// setDataStoreOptions() is called from inside the DataStore constructor to set this value
|
|
710
942
|
constructor(options) {
|
|
943
|
+
__publicField(this, "dataStoreOptions");
|
|
711
944
|
if (options)
|
|
712
945
|
this.dataStoreOptions = options;
|
|
713
946
|
}
|
|
@@ -735,7 +968,7 @@ var DataStoreEngine = class {
|
|
|
735
968
|
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;
|
|
736
969
|
if (decRes instanceof Promise)
|
|
737
970
|
decRes = await decRes;
|
|
738
|
-
return JSON.parse(decRes
|
|
971
|
+
return JSON.parse(decRes != null ? decRes : data);
|
|
739
972
|
}
|
|
740
973
|
//#region misc api
|
|
741
974
|
/** Throws an error if the DataStoreOptions are not set or invalid */
|
|
@@ -753,13 +986,12 @@ var DataStoreEngine = class {
|
|
|
753
986
|
try {
|
|
754
987
|
if ("structuredClone" in globalThis)
|
|
755
988
|
return structuredClone(obj);
|
|
756
|
-
} catch {
|
|
989
|
+
} catch (e) {
|
|
757
990
|
}
|
|
758
991
|
return JSON.parse(JSON.stringify(obj));
|
|
759
992
|
}
|
|
760
993
|
};
|
|
761
994
|
var BrowserStorageEngine = class extends DataStoreEngine {
|
|
762
|
-
options;
|
|
763
995
|
/**
|
|
764
996
|
* Creates an instance of `BrowserStorageEngine`.
|
|
765
997
|
*
|
|
@@ -768,6 +1000,7 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
768
1000
|
*/
|
|
769
1001
|
constructor(options) {
|
|
770
1002
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1003
|
+
__publicField(this, "options");
|
|
771
1004
|
this.options = {
|
|
772
1005
|
type: "localStorage",
|
|
773
1006
|
...options
|
|
@@ -796,8 +1029,6 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
796
1029
|
};
|
|
797
1030
|
var fs;
|
|
798
1031
|
var FileStorageEngine = class extends DataStoreEngine {
|
|
799
|
-
options;
|
|
800
|
-
fileAccessQueue = Promise.resolve();
|
|
801
1032
|
/**
|
|
802
1033
|
* Creates an instance of `FileStorageEngine`.
|
|
803
1034
|
*
|
|
@@ -806,6 +1037,8 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
806
1037
|
*/
|
|
807
1038
|
constructor(options) {
|
|
808
1039
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1040
|
+
__publicField(this, "options");
|
|
1041
|
+
__publicField(this, "fileAccessQueue", Promise.resolve());
|
|
809
1042
|
this.options = {
|
|
810
1043
|
filePath: (id) => `.ds-${id}`,
|
|
811
1044
|
...options
|
|
@@ -814,6 +1047,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
814
1047
|
//#region json file
|
|
815
1048
|
/** Reads the file contents */
|
|
816
1049
|
async readFile() {
|
|
1050
|
+
var _a2;
|
|
817
1051
|
var _a, _b, _c, _d;
|
|
818
1052
|
this.ensureDataStoreOptions();
|
|
819
1053
|
try {
|
|
@@ -821,15 +1055,16 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
821
1055
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
822
1056
|
if (!fs)
|
|
823
1057
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
824
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1058
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
825
1059
|
const data = await fs.readFile(path, "utf-8");
|
|
826
|
-
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))
|
|
827
|
-
} catch {
|
|
1060
|
+
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;
|
|
1061
|
+
} catch (e) {
|
|
828
1062
|
return void 0;
|
|
829
1063
|
}
|
|
830
1064
|
}
|
|
831
1065
|
/** Overwrites the file contents */
|
|
832
1066
|
async writeFile(data) {
|
|
1067
|
+
var _a2;
|
|
833
1068
|
var _a, _b, _c, _d;
|
|
834
1069
|
this.ensureDataStoreOptions();
|
|
835
1070
|
try {
|
|
@@ -837,9 +1072,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
837
1072
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
838
1073
|
if (!fs)
|
|
839
1074
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
840
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1075
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
841
1076
|
await fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
842
|
-
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)))
|
|
1077
|
+
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");
|
|
843
1078
|
} catch (err) {
|
|
844
1079
|
console.error("Error writing file:", err);
|
|
845
1080
|
}
|
|
@@ -853,8 +1088,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
853
1088
|
const value = data == null ? void 0 : data[name];
|
|
854
1089
|
if (typeof value === "undefined")
|
|
855
1090
|
return defaultValue;
|
|
856
|
-
if (typeof
|
|
857
|
-
|
|
1091
|
+
if (typeof defaultValue === "string") {
|
|
1092
|
+
if (typeof value === "object" && value !== null)
|
|
1093
|
+
return JSON.stringify(value);
|
|
1094
|
+
if (typeof value === "string")
|
|
1095
|
+
return value;
|
|
1096
|
+
return String(value);
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof value === "string") {
|
|
1099
|
+
try {
|
|
1100
|
+
const parsed = JSON.parse(value);
|
|
1101
|
+
return parsed;
|
|
1102
|
+
} catch (e) {
|
|
1103
|
+
return defaultValue;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
858
1106
|
return value;
|
|
859
1107
|
}
|
|
860
1108
|
/** Sets a value in persistent storage */
|
|
@@ -863,7 +1111,18 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
863
1111
|
let data = await this.readFile();
|
|
864
1112
|
if (!data)
|
|
865
1113
|
data = {};
|
|
866
|
-
|
|
1114
|
+
let storeVal = value;
|
|
1115
|
+
if (typeof value === "string") {
|
|
1116
|
+
try {
|
|
1117
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1118
|
+
const parsed = JSON.parse(value);
|
|
1119
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1120
|
+
storeVal = parsed;
|
|
1121
|
+
}
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
data[name] = storeVal;
|
|
867
1126
|
await this.writeFile(data);
|
|
868
1127
|
}).catch((err) => {
|
|
869
1128
|
console.error("Error in setValue:", err);
|
|
@@ -896,7 +1155,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
896
1155
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
897
1156
|
if (!fs)
|
|
898
1157
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
899
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1158
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
900
1159
|
return await fs.unlink(path);
|
|
901
1160
|
} catch (err) {
|
|
902
1161
|
console.error("Error deleting file:", err);
|
|
@@ -904,9 +1163,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
904
1163
|
}
|
|
905
1164
|
};
|
|
906
1165
|
var DataStoreSerializer = class _DataStoreSerializer {
|
|
907
|
-
stores;
|
|
908
|
-
options;
|
|
909
1166
|
constructor(stores, options = {}) {
|
|
1167
|
+
__publicField(this, "stores");
|
|
1168
|
+
__publicField(this, "options");
|
|
910
1169
|
if (!crypto || !crypto.subtle)
|
|
911
1170
|
throw new ScriptContextError("DataStoreSerializer has to run in a secure context (HTTPS) or in another environment that implements the subtleCrypto API!");
|
|
912
1171
|
this.stores = stores;
|
|
@@ -962,8 +1221,9 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
962
1221
|
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
963
1222
|
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
964
1223
|
const resolveStoreId = (id) => {
|
|
1224
|
+
var _a2;
|
|
965
1225
|
var _a;
|
|
966
|
-
return ((_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0])
|
|
1226
|
+
return (_a2 = (_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0]) != null ? _a2 : id;
|
|
967
1227
|
};
|
|
968
1228
|
const matchesFilter = (id) => typeof stores === "function" ? stores(id) : stores.includes(id);
|
|
969
1229
|
for (const storeData of deserStores) {
|
|
@@ -1040,201 +1300,6 @@ Has: ${checksum}`);
|
|
|
1040
1300
|
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1041
1301
|
}
|
|
1042
1302
|
};
|
|
1043
|
-
var createNanoEvents = () => ({
|
|
1044
|
-
emit(event, ...args) {
|
|
1045
|
-
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
1046
|
-
callbacks[i](...args);
|
|
1047
|
-
}
|
|
1048
|
-
},
|
|
1049
|
-
events: {},
|
|
1050
|
-
on(event, cb) {
|
|
1051
|
-
;
|
|
1052
|
-
(this.events[event] ||= []).push(cb);
|
|
1053
|
-
return () => {
|
|
1054
|
-
var _a;
|
|
1055
|
-
this.events[event] = (_a = this.events[event]) == null ? void 0 : _a.filter((i) => cb !== i);
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
var NanoEmitter = class {
|
|
1060
|
-
events = createNanoEvents();
|
|
1061
|
-
eventUnsubscribes = [];
|
|
1062
|
-
emitterOptions;
|
|
1063
|
-
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
1064
|
-
constructor(options = {}) {
|
|
1065
|
-
this.emitterOptions = {
|
|
1066
|
-
publicEmit: false,
|
|
1067
|
-
...options
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
//#region on
|
|
1071
|
-
/**
|
|
1072
|
-
* Subscribes to an event and calls the callback when it's emitted.
|
|
1073
|
-
* @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 "_")
|
|
1074
|
-
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
1075
|
-
* @example ```ts
|
|
1076
|
-
* const emitter = new NanoEmitter<{
|
|
1077
|
-
* foo: (bar: string) => void;
|
|
1078
|
-
* }>({
|
|
1079
|
-
* publicEmit: true,
|
|
1080
|
-
* });
|
|
1081
|
-
*
|
|
1082
|
-
* let i = 0;
|
|
1083
|
-
* const unsub = emitter.on("foo", (bar) => {
|
|
1084
|
-
* // unsubscribe after 10 events:
|
|
1085
|
-
* if(++i === 10) unsub();
|
|
1086
|
-
* console.log(bar);
|
|
1087
|
-
* });
|
|
1088
|
-
*
|
|
1089
|
-
* emitter.emit("foo", "bar");
|
|
1090
|
-
* ```
|
|
1091
|
-
*/
|
|
1092
|
-
on(event, cb) {
|
|
1093
|
-
let unsub;
|
|
1094
|
-
const unsubProxy = () => {
|
|
1095
|
-
if (!unsub)
|
|
1096
|
-
return;
|
|
1097
|
-
unsub();
|
|
1098
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
1099
|
-
};
|
|
1100
|
-
unsub = this.events.on(event, cb);
|
|
1101
|
-
this.eventUnsubscribes.push(unsub);
|
|
1102
|
-
return unsubProxy;
|
|
1103
|
-
}
|
|
1104
|
-
//#region once
|
|
1105
|
-
/**
|
|
1106
|
-
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
1107
|
-
* @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 "_")
|
|
1108
|
-
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
1109
|
-
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
1110
|
-
* @example ```ts
|
|
1111
|
-
* const emitter = new NanoEmitter<{
|
|
1112
|
-
* foo: (bar: string) => void;
|
|
1113
|
-
* }>();
|
|
1114
|
-
*
|
|
1115
|
-
* // Promise syntax:
|
|
1116
|
-
* const [bar] = await emitter.once("foo");
|
|
1117
|
-
* console.log(bar);
|
|
1118
|
-
*
|
|
1119
|
-
* // Callback syntax:
|
|
1120
|
-
* emitter.once("foo", (bar) => console.log(bar));
|
|
1121
|
-
* ```
|
|
1122
|
-
*/
|
|
1123
|
-
once(event, cb) {
|
|
1124
|
-
return new Promise((resolve) => {
|
|
1125
|
-
let unsub;
|
|
1126
|
-
const onceProxy = ((...args) => {
|
|
1127
|
-
cb == null ? void 0 : cb(...args);
|
|
1128
|
-
unsub == null ? void 0 : unsub();
|
|
1129
|
-
resolve(args);
|
|
1130
|
-
});
|
|
1131
|
-
unsub = this.events.on(event, onceProxy);
|
|
1132
|
-
this.eventUnsubscribes.push(unsub);
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
//#region onMulti
|
|
1136
|
-
/**
|
|
1137
|
-
* 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.
|
|
1138
|
-
* @param options An object or array of objects with the following properties:
|
|
1139
|
-
* `callback` (required) is the function that will be called when the conditions are met.
|
|
1140
|
-
*
|
|
1141
|
-
* 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.
|
|
1142
|
-
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
1143
|
-
*
|
|
1144
|
-
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
1145
|
-
* 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.
|
|
1146
|
-
* 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.
|
|
1147
|
-
* At least one of `oneOf` or `allOf` must be provided.
|
|
1148
|
-
*
|
|
1149
|
-
* @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.
|
|
1150
|
-
*/
|
|
1151
|
-
onMulti(options) {
|
|
1152
|
-
const allUnsubs = [];
|
|
1153
|
-
const unsubAll = () => {
|
|
1154
|
-
for (const unsub of allUnsubs)
|
|
1155
|
-
unsub();
|
|
1156
|
-
allUnsubs.splice(0, allUnsubs.length);
|
|
1157
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
1158
|
-
};
|
|
1159
|
-
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
1160
|
-
const optsWithDefaults = {
|
|
1161
|
-
allOf: [],
|
|
1162
|
-
oneOf: [],
|
|
1163
|
-
once: false,
|
|
1164
|
-
...opts
|
|
1165
|
-
};
|
|
1166
|
-
const {
|
|
1167
|
-
oneOf,
|
|
1168
|
-
allOf,
|
|
1169
|
-
once,
|
|
1170
|
-
signal,
|
|
1171
|
-
callback
|
|
1172
|
-
} = optsWithDefaults;
|
|
1173
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
1174
|
-
return unsubAll;
|
|
1175
|
-
if (oneOf.length === 0 && allOf.length === 0)
|
|
1176
|
-
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1177
|
-
const curEvtUnsubs = [];
|
|
1178
|
-
const checkUnsubAllEvt = (force = false) => {
|
|
1179
|
-
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
1180
|
-
return;
|
|
1181
|
-
for (const unsub of curEvtUnsubs)
|
|
1182
|
-
unsub();
|
|
1183
|
-
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
1184
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
1185
|
-
};
|
|
1186
|
-
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1187
|
-
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
1188
|
-
for (const event of oneOf) {
|
|
1189
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1190
|
-
checkUnsubAllEvt();
|
|
1191
|
-
if (allOfConditionMet()) {
|
|
1192
|
-
callback(event, ...args);
|
|
1193
|
-
if (once)
|
|
1194
|
-
checkUnsubAllEvt(true);
|
|
1195
|
-
}
|
|
1196
|
-
}));
|
|
1197
|
-
curEvtUnsubs.push(unsub);
|
|
1198
|
-
}
|
|
1199
|
-
for (const event of allOf) {
|
|
1200
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1201
|
-
checkUnsubAllEvt();
|
|
1202
|
-
allOfEmitted.add(event);
|
|
1203
|
-
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
1204
|
-
callback(event, ...args);
|
|
1205
|
-
if (once)
|
|
1206
|
-
checkUnsubAllEvt(true);
|
|
1207
|
-
}
|
|
1208
|
-
}));
|
|
1209
|
-
curEvtUnsubs.push(unsub);
|
|
1210
|
-
}
|
|
1211
|
-
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
1212
|
-
}
|
|
1213
|
-
return unsubAll;
|
|
1214
|
-
}
|
|
1215
|
-
//#region emit
|
|
1216
|
-
/**
|
|
1217
|
-
* Emits an event on this instance.
|
|
1218
|
-
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
1219
|
-
* @param event The event to emit
|
|
1220
|
-
* @param args The arguments to pass to the event listeners
|
|
1221
|
-
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
1222
|
-
*/
|
|
1223
|
-
emit(event, ...args) {
|
|
1224
|
-
if (this.emitterOptions.publicEmit) {
|
|
1225
|
-
this.events.emit(event, ...args);
|
|
1226
|
-
return true;
|
|
1227
|
-
}
|
|
1228
|
-
return false;
|
|
1229
|
-
}
|
|
1230
|
-
//#region unsubscribeAll
|
|
1231
|
-
/** Unsubscribes all event listeners from this instance */
|
|
1232
|
-
unsubscribeAll() {
|
|
1233
|
-
for (const unsub of this.eventUnsubscribes)
|
|
1234
|
-
unsub();
|
|
1235
|
-
this.eventUnsubscribes = [];
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
1303
|
var Debouncer = class extends NanoEmitter {
|
|
1239
1304
|
/**
|
|
1240
1305
|
* Creates a new debouncer with the specified timeout and edge type.
|
|
@@ -1243,15 +1308,15 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1243
1308
|
*/
|
|
1244
1309
|
constructor(timeout = 200, type = "immediate") {
|
|
1245
1310
|
super();
|
|
1311
|
+
/** All registered listener functions and the time they were attached */
|
|
1312
|
+
__publicField(this, "listeners", []);
|
|
1313
|
+
/** The currently active timeout */
|
|
1314
|
+
__publicField(this, "activeTimeout");
|
|
1315
|
+
/** The latest queued call */
|
|
1316
|
+
__publicField(this, "queuedCall");
|
|
1246
1317
|
this.timeout = timeout;
|
|
1247
1318
|
this.type = type;
|
|
1248
1319
|
}
|
|
1249
|
-
/** All registered listener functions and the time they were attached */
|
|
1250
|
-
listeners = [];
|
|
1251
|
-
/** The currently active timeout */
|
|
1252
|
-
activeTimeout;
|
|
1253
|
-
/** The latest queued call */
|
|
1254
|
-
queuedCall;
|
|
1255
1320
|
//#region listeners
|
|
1256
1321
|
/** Adds a listener function that will be called on timeout */
|
|
1257
1322
|
addListener(fn) {
|
|
@@ -1338,6 +1403,22 @@ function debounce(fn, timeout = 200, type = "immediate") {
|
|
|
1338
1403
|
return func;
|
|
1339
1404
|
}
|
|
1340
1405
|
|
|
1406
|
+
// lib/consts.ts
|
|
1407
|
+
var rawConsts = {
|
|
1408
|
+
coreUtilsVersion: "3.3.0",
|
|
1409
|
+
userUtilsVersion: "10.1.0"
|
|
1410
|
+
};
|
|
1411
|
+
function getConst(constKey, defaultVal) {
|
|
1412
|
+
const val = rawConsts[constKey];
|
|
1413
|
+
return val.match(/^#\{\{.+\}\}$/) ? defaultVal : val;
|
|
1414
|
+
}
|
|
1415
|
+
var versions = {
|
|
1416
|
+
/** Semver version string of the bundled library CoreUtils. */
|
|
1417
|
+
CoreUtils: getConst("coreUtilsVersion", "ERR:unknown"),
|
|
1418
|
+
/** Semver version string of UserUtils. */
|
|
1419
|
+
UserUtils: getConst("userUtilsVersion", "ERR:unknown")
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1341
1422
|
// lib/Errors.ts
|
|
1342
1423
|
var PlatformError = class extends DatedError {
|
|
1343
1424
|
constructor(message, options) {
|
|
@@ -1350,7 +1431,7 @@ var PlatformError = class extends DatedError {
|
|
|
1350
1431
|
function getUnsafeWindow() {
|
|
1351
1432
|
try {
|
|
1352
1433
|
return unsafeWindow;
|
|
1353
|
-
} catch {
|
|
1434
|
+
} catch (e) {
|
|
1354
1435
|
return window;
|
|
1355
1436
|
}
|
|
1356
1437
|
}
|
|
@@ -1381,7 +1462,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1381
1462
|
try {
|
|
1382
1463
|
if (typeof window.GM === "object")
|
|
1383
1464
|
GM.openInTab(href, background);
|
|
1384
|
-
} catch {
|
|
1465
|
+
} catch (e) {
|
|
1385
1466
|
const openElem = document.createElement("a");
|
|
1386
1467
|
Object.assign(openElem, {
|
|
1387
1468
|
className: "userutils-open-in-new-tab",
|
|
@@ -1401,7 +1482,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1401
1482
|
setTimeout(() => {
|
|
1402
1483
|
try {
|
|
1403
1484
|
openElem.remove();
|
|
1404
|
-
} catch {
|
|
1485
|
+
} catch (e2) {
|
|
1405
1486
|
}
|
|
1406
1487
|
}, 0);
|
|
1407
1488
|
}
|
|
@@ -1417,8 +1498,8 @@ function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
|
1417
1498
|
}
|
|
1418
1499
|
(function(original) {
|
|
1419
1500
|
eventObject.__proto__.addEventListener = function(...args) {
|
|
1420
|
-
var _a2;
|
|
1421
|
-
const origListener = typeof args[1] === "function" ? args[1] : ((_a2 = args[1]) == null ? void 0 : _a2.handleEvent)
|
|
1501
|
+
var _a2, _b;
|
|
1502
|
+
const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? void 0 : _a2.handleEvent) != null ? _b : (() => void 0);
|
|
1422
1503
|
args[1] = function(...a) {
|
|
1423
1504
|
if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
|
|
1424
1505
|
return;
|
|
@@ -1461,8 +1542,8 @@ function observeElementProp(element, property, callback) {
|
|
|
1461
1542
|
}
|
|
1462
1543
|
}
|
|
1463
1544
|
function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) {
|
|
1464
|
-
var _a;
|
|
1465
|
-
const siblings = [...((_a = refElement.parentNode) == null ? void 0 : _a.childNodes)
|
|
1545
|
+
var _a, _b;
|
|
1546
|
+
const siblings = [...(_b = (_a = refElement.parentNode) == null ? void 0 : _a.childNodes) != null ? _b : []];
|
|
1466
1547
|
const elemSiblIdx = siblings.indexOf(refElement);
|
|
1467
1548
|
if (elemSiblIdx === -1)
|
|
1468
1549
|
throw new Error("Element doesn't have a parent node");
|
|
@@ -1483,13 +1564,13 @@ function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "cent
|
|
|
1483
1564
|
}
|
|
1484
1565
|
var ttPolicy;
|
|
1485
1566
|
function setInnerHtmlUnsafe(element, html) {
|
|
1486
|
-
var _a, _b;
|
|
1567
|
+
var _a, _b, _c;
|
|
1487
1568
|
if (!ttPolicy && typeof ((_a = window == null ? void 0 : window.trustedTypes) == null ? void 0 : _a.createPolicy) === "function") {
|
|
1488
1569
|
ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", {
|
|
1489
1570
|
createHTML: (unsafeHtml) => unsafeHtml
|
|
1490
1571
|
});
|
|
1491
1572
|
}
|
|
1492
|
-
element.innerHTML = ((_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html))
|
|
1573
|
+
element.innerHTML = (_c = (_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
1493
1574
|
return element;
|
|
1494
1575
|
}
|
|
1495
1576
|
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
@@ -1682,20 +1763,20 @@ var defaultStrings = {
|
|
|
1682
1763
|
closeDialogTooltip: "Click to close the dialog"
|
|
1683
1764
|
};
|
|
1684
1765
|
var Dialog = class _Dialog extends NanoEmitter {
|
|
1685
|
-
/** Options passed to the dialog in the constructor */
|
|
1686
|
-
options;
|
|
1687
|
-
/** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */
|
|
1688
|
-
id;
|
|
1689
|
-
/** Strings used in the dialog (used for translations) */
|
|
1690
|
-
strings;
|
|
1691
|
-
dialogOpen = false;
|
|
1692
|
-
dialogMounted = false;
|
|
1693
1766
|
constructor(options) {
|
|
1694
1767
|
super();
|
|
1768
|
+
/** Options passed to the dialog in the constructor */
|
|
1769
|
+
__publicField(this, "options");
|
|
1770
|
+
/** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */
|
|
1771
|
+
__publicField(this, "id");
|
|
1772
|
+
/** Strings used in the dialog (used for translations) */
|
|
1773
|
+
__publicField(this, "strings");
|
|
1774
|
+
__publicField(this, "dialogOpen", false);
|
|
1775
|
+
__publicField(this, "dialogMounted", false);
|
|
1695
1776
|
const { strings, ...opts } = options;
|
|
1696
1777
|
this.strings = {
|
|
1697
1778
|
...defaultStrings,
|
|
1698
|
-
...strings
|
|
1779
|
+
...strings != null ? strings : {}
|
|
1699
1780
|
};
|
|
1700
1781
|
this.options = {
|
|
1701
1782
|
closeOnBgClick: true,
|
|
@@ -1712,11 +1793,12 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1712
1793
|
//#region public
|
|
1713
1794
|
/** Call after DOMContentLoaded to pre-render the dialog and invisibly mount it in the DOM */
|
|
1714
1795
|
async mount() {
|
|
1796
|
+
var _a;
|
|
1715
1797
|
if (this.dialogMounted)
|
|
1716
1798
|
return;
|
|
1717
1799
|
this.dialogMounted = true;
|
|
1718
1800
|
if (!document.querySelector("style.uu-dialog-css"))
|
|
1719
|
-
addGlobalStyle(this.options.dialogCss
|
|
1801
|
+
addGlobalStyle((_a = this.options.dialogCss) != null ? _a : defaultDialogCss).classList.add("uu-dialog-css");
|
|
1720
1802
|
const bgElem = document.createElement("div");
|
|
1721
1803
|
bgElem.id = `uu-${this.id}-dialog-bg`;
|
|
1722
1804
|
bgElem.classList.add("uu-dialog-bg");
|
|
@@ -1784,7 +1866,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1784
1866
|
}
|
|
1785
1867
|
/** Closes the dialog - prevents default action and immediate propagation of the passed event */
|
|
1786
1868
|
close(e) {
|
|
1787
|
-
var _a;
|
|
1869
|
+
var _a, _b;
|
|
1788
1870
|
e == null ? void 0 : e.preventDefault();
|
|
1789
1871
|
e == null ? void 0 : e.stopImmediatePropagation();
|
|
1790
1872
|
if (!this.isOpen())
|
|
@@ -1797,9 +1879,9 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1797
1879
|
dialogBg.style.display = "none";
|
|
1798
1880
|
dialogBg.inert = true;
|
|
1799
1881
|
openDialogs.splice(openDialogs.indexOf(this.id), 1);
|
|
1800
|
-
currentDialogId = openDialogs[0]
|
|
1882
|
+
currentDialogId = (_a = openDialogs[0]) != null ? _a : null;
|
|
1801
1883
|
if (currentDialogId)
|
|
1802
|
-
(
|
|
1884
|
+
(_b = document.querySelector(`#uu-${currentDialogId}-dialog-bg`)) == null ? void 0 : _b.removeAttribute("inert");
|
|
1803
1885
|
if (openDialogs.length === 0) {
|
|
1804
1886
|
document.body.classList.add("uu-no-select");
|
|
1805
1887
|
document.body.removeAttribute("inert");
|
|
@@ -1835,7 +1917,8 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1835
1917
|
}
|
|
1836
1918
|
//#region protected
|
|
1837
1919
|
getString(key) {
|
|
1838
|
-
|
|
1920
|
+
var _a;
|
|
1921
|
+
return (_a = this.strings[key]) != null ? _a : defaultStrings[key];
|
|
1839
1922
|
}
|
|
1840
1923
|
/** Called once to attach all generic event listeners */
|
|
1841
1924
|
attachListeners(bgElem) {
|
|
@@ -1860,7 +1943,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1860
1943
|
* @param listenerOptions Provide a {@linkcode listenerOptions} object to configure the listeners
|
|
1861
1944
|
*/
|
|
1862
1945
|
onInteraction(elem, listener, listenerOptions) {
|
|
1863
|
-
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions
|
|
1946
|
+
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions != null ? listenerOptions : {};
|
|
1864
1947
|
const interactionKeys = ["Enter", " ", "Space"];
|
|
1865
1948
|
const proxListener = (e) => {
|
|
1866
1949
|
if (e instanceof KeyboardEvent) {
|
|
@@ -1940,7 +2023,6 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1940
2023
|
|
|
1941
2024
|
// lib/GMStorageEngine.ts
|
|
1942
2025
|
var GMStorageEngine = class extends DataStoreEngine {
|
|
1943
|
-
options;
|
|
1944
2026
|
/**
|
|
1945
2027
|
* Creates an instance of `GMStorageEngine`.
|
|
1946
2028
|
*
|
|
@@ -1949,6 +2031,7 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
1949
2031
|
*/
|
|
1950
2032
|
constructor(options) {
|
|
1951
2033
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
2034
|
+
__publicField(this, "options");
|
|
1952
2035
|
this.options = {
|
|
1953
2036
|
...options
|
|
1954
2037
|
};
|
|
@@ -2004,25 +2087,26 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
2004
2087
|
|
|
2005
2088
|
// lib/Mixins.ts
|
|
2006
2089
|
var Mixins = class {
|
|
2007
|
-
/** List of all registered mixins */
|
|
2008
|
-
mixins = [];
|
|
2009
|
-
/** Default configuration object for mixins */
|
|
2010
|
-
defaultMixinCfg;
|
|
2011
|
-
/** Whether the priorities should auto-increment if not specified */
|
|
2012
|
-
autoIncPrioEnabled;
|
|
2013
|
-
/** The current auto-increment priority counter */
|
|
2014
|
-
autoIncPrioCounter = /* @__PURE__ */ new Map();
|
|
2015
2090
|
/**
|
|
2016
2091
|
* Creates a new Mixins instance.
|
|
2017
2092
|
* @param config Configuration object to customize the behavior.
|
|
2018
2093
|
*/
|
|
2019
2094
|
constructor(config = {}) {
|
|
2095
|
+
/** List of all registered mixins */
|
|
2096
|
+
__publicField(this, "mixins", []);
|
|
2097
|
+
/** Default configuration object for mixins */
|
|
2098
|
+
__publicField(this, "defaultMixinCfg");
|
|
2099
|
+
/** Whether the priorities should auto-increment if not specified */
|
|
2100
|
+
__publicField(this, "autoIncPrioEnabled");
|
|
2101
|
+
/** The current auto-increment priority counter */
|
|
2102
|
+
__publicField(this, "autoIncPrioCounter", /* @__PURE__ */ new Map());
|
|
2103
|
+
var _a, _b, _c;
|
|
2020
2104
|
this.defaultMixinCfg = pureObj({
|
|
2021
|
-
priority: config.defaultPriority
|
|
2022
|
-
stopPropagation: config.defaultStopPropagation
|
|
2105
|
+
priority: (_a = config.defaultPriority) != null ? _a : 0,
|
|
2106
|
+
stopPropagation: (_b = config.defaultStopPropagation) != null ? _b : false,
|
|
2023
2107
|
signal: config.defaultSignal
|
|
2024
2108
|
});
|
|
2025
|
-
this.autoIncPrioEnabled = config.autoIncrementPriority
|
|
2109
|
+
this.autoIncPrioEnabled = (_c = config.autoIncrementPriority) != null ? _c : false;
|
|
2026
2110
|
}
|
|
2027
2111
|
//#region public
|
|
2028
2112
|
/**
|
|
@@ -2087,10 +2171,11 @@ var Mixins = class {
|
|
|
2087
2171
|
//#region protected
|
|
2088
2172
|
/** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
|
|
2089
2173
|
calcPriority(mixinKey, config) {
|
|
2174
|
+
var _a;
|
|
2090
2175
|
if (config.priority !== void 0)
|
|
2091
2176
|
return void 0;
|
|
2092
2177
|
if (!this.autoIncPrioEnabled)
|
|
2093
|
-
return config.priority
|
|
2178
|
+
return (_a = config.priority) != null ? _a : this.defaultMixinCfg.priority;
|
|
2094
2179
|
if (!this.autoIncPrioCounter.has(mixinKey))
|
|
2095
2180
|
this.autoIncPrioCounter.set(mixinKey, this.defaultMixinCfg.priority);
|
|
2096
2181
|
let prio = this.autoIncPrioCounter.get(mixinKey);
|
|
@@ -2111,13 +2196,13 @@ var Mixins = class {
|
|
|
2111
2196
|
|
|
2112
2197
|
// lib/SelectorObserver.ts
|
|
2113
2198
|
var SelectorObserver = class {
|
|
2114
|
-
enabled = false;
|
|
2115
|
-
baseElement;
|
|
2116
|
-
observer;
|
|
2117
|
-
observerOptions;
|
|
2118
|
-
customOptions;
|
|
2119
|
-
listenerMap;
|
|
2120
2199
|
constructor(baseElement, options = {}) {
|
|
2200
|
+
__publicField(this, "enabled", false);
|
|
2201
|
+
__publicField(this, "baseElement");
|
|
2202
|
+
__publicField(this, "observer");
|
|
2203
|
+
__publicField(this, "observerOptions");
|
|
2204
|
+
__publicField(this, "customOptions");
|
|
2205
|
+
__publicField(this, "listenerMap");
|
|
2121
2206
|
this.baseElement = baseElement;
|
|
2122
2207
|
this.listenerMap = /* @__PURE__ */ new Map();
|
|
2123
2208
|
const {
|
|
@@ -2133,10 +2218,10 @@ var SelectorObserver = class {
|
|
|
2133
2218
|
...observerOptions
|
|
2134
2219
|
};
|
|
2135
2220
|
this.customOptions = {
|
|
2136
|
-
defaultDebounce: defaultDebounce
|
|
2137
|
-
defaultDebounceType: defaultDebounceType
|
|
2138
|
-
disableOnNoListeners: disableOnNoListeners
|
|
2139
|
-
enableOnAddListener: enableOnAddListener
|
|
2221
|
+
defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
|
|
2222
|
+
defaultDebounceType: defaultDebounceType != null ? defaultDebounceType : "immediate",
|
|
2223
|
+
disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
|
|
2224
|
+
enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
|
|
2140
2225
|
};
|
|
2141
2226
|
if (typeof this.customOptions.checkInterval !== "number") {
|
|
2142
2227
|
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
@@ -2287,9 +2372,9 @@ var valTransforms = [];
|
|
|
2287
2372
|
var fallbackLang;
|
|
2288
2373
|
function translate(language, key, ...trArgs) {
|
|
2289
2374
|
if (typeof language !== "string")
|
|
2290
|
-
language = fallbackLang
|
|
2375
|
+
language = fallbackLang != null ? fallbackLang : "";
|
|
2291
2376
|
const trObj = trans[language];
|
|
2292
|
-
if (typeof language !== "string" ||
|
|
2377
|
+
if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
|
|
2293
2378
|
return fallbackLang && language !== fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
2294
2379
|
const transformTrVal = (trKey, trValue) => {
|
|
2295
2380
|
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
@@ -2341,15 +2426,18 @@ function trFor(language, key, ...args) {
|
|
|
2341
2426
|
function useTr(language) {
|
|
2342
2427
|
return (key, ...args) => translate(language, key, ...args);
|
|
2343
2428
|
}
|
|
2344
|
-
function hasKey(language = fallbackLang
|
|
2429
|
+
function hasKey(language = fallbackLang != null ? fallbackLang : "", key) {
|
|
2345
2430
|
return tr.for(language, key) !== key;
|
|
2346
2431
|
}
|
|
2347
2432
|
function addTranslations(language, translations) {
|
|
2348
2433
|
trans[language] = JSON.parse(JSON.stringify(translations));
|
|
2349
2434
|
}
|
|
2350
|
-
function getTranslations(language = fallbackLang
|
|
2435
|
+
function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
|
|
2351
2436
|
return trans[language];
|
|
2352
2437
|
}
|
|
2438
|
+
function getAllTranslations(asCopy = true) {
|
|
2439
|
+
return asCopy ? JSON.parse(JSON.stringify(trans)) : trans;
|
|
2440
|
+
}
|
|
2353
2441
|
var deleteTranslations = (language) => {
|
|
2354
2442
|
if (language in trans) {
|
|
2355
2443
|
delete trans[language];
|
|
@@ -2380,51 +2468,55 @@ function deleteTransform(patternOrFn) {
|
|
|
2380
2468
|
}
|
|
2381
2469
|
return false;
|
|
2382
2470
|
}
|
|
2383
|
-
var
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
const
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2394
|
-
if (typeof repl !== "undefined")
|
|
2395
|
-
str = str.replace(match[0], String(repl));
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2398
|
-
const positionalMapping = () => {
|
|
2399
|
-
if (!patternRegex.test(str) || !trArgs[0])
|
|
2400
|
-
return;
|
|
2401
|
-
let matchNum = -1;
|
|
2402
|
-
for (const match of matches) {
|
|
2403
|
-
matchNum++;
|
|
2404
|
-
if (typeof trArgs[matchNum] !== "undefined")
|
|
2405
|
-
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2406
|
-
}
|
|
2407
|
-
};
|
|
2408
|
-
let notStringifiable = false;
|
|
2409
|
-
try {
|
|
2410
|
-
String(trArgs[0]);
|
|
2411
|
-
} catch {
|
|
2412
|
-
notStringifiable = true;
|
|
2471
|
+
var commonKeyedTransform = ({ matches, trArgs, trValue }, patternRegex, quickMatchPattern) => {
|
|
2472
|
+
let str = String(trValue);
|
|
2473
|
+
const someMatchKeyInArgs = (obj) => matches.some((match) => match[1] !== void 0 && match[1] in obj);
|
|
2474
|
+
const namedMapping = () => {
|
|
2475
|
+
if (!str.includes(quickMatchPattern) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !someMatchKeyInArgs(trArgs[0]))
|
|
2476
|
+
return;
|
|
2477
|
+
for (const match of matches) {
|
|
2478
|
+
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2479
|
+
if (typeof repl !== "undefined")
|
|
2480
|
+
str = str.replace(match[0], String(repl));
|
|
2413
2481
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2482
|
+
};
|
|
2483
|
+
const positionalMapping = () => {
|
|
2484
|
+
if (!patternRegex.test(str) || typeof trArgs[0] === "undefined")
|
|
2485
|
+
return;
|
|
2486
|
+
let matchNum = -1;
|
|
2487
|
+
for (const match of matches) {
|
|
2488
|
+
matchNum++;
|
|
2489
|
+
if (typeof trArgs[matchNum] !== "undefined")
|
|
2490
|
+
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2493
|
+
let notStringifiable = false;
|
|
2494
|
+
try {
|
|
2495
|
+
void `${trArgs[0]}`;
|
|
2496
|
+
} catch (e) {
|
|
2497
|
+
notStringifiable = true;
|
|
2420
2498
|
}
|
|
2499
|
+
const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && (notStringifiable || String(trArgs[0]).startsWith("[object"));
|
|
2500
|
+
if (isArgsObject && someMatchKeyInArgs(trArgs[0]))
|
|
2501
|
+
namedMapping();
|
|
2502
|
+
else
|
|
2503
|
+
positionalMapping();
|
|
2504
|
+
return str;
|
|
2505
|
+
};
|
|
2506
|
+
var templateLiteralTransform = [
|
|
2507
|
+
/\$\{([a-zA-Z0-9$_-]+)\}/gm,
|
|
2508
|
+
(tfProps) => commonKeyedTransform(tfProps, /\$\{.+\}/m, "${")
|
|
2509
|
+
];
|
|
2510
|
+
var i18nTransform = [
|
|
2511
|
+
/\{\{([a-zA-Z0-9$_-]+)\}\}/gm,
|
|
2512
|
+
(tfProps) => commonKeyedTransform(tfProps, /\{\{.+\}\}/m, "{{")
|
|
2421
2513
|
];
|
|
2422
2514
|
var percentTransform = [
|
|
2423
2515
|
/%(\d+)/gm,
|
|
2424
2516
|
({ matches, trArgs, trValue }) => {
|
|
2425
2517
|
let str = String(trValue);
|
|
2426
2518
|
for (const match of matches) {
|
|
2427
|
-
const repl =
|
|
2519
|
+
const repl = trArgs == null ? void 0 : trArgs[Number(match[1]) - 1];
|
|
2428
2520
|
if (typeof repl !== "undefined")
|
|
2429
2521
|
str = str.replace(match[0], String(repl));
|
|
2430
2522
|
}
|
|
@@ -2434,16 +2526,84 @@ var percentTransform = [
|
|
|
2434
2526
|
var tr = {
|
|
2435
2527
|
for: (...params) => trFor(...params),
|
|
2436
2528
|
use: (...params) => useTr(...params),
|
|
2437
|
-
hasKey: (language = fallbackLang
|
|
2529
|
+
hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
|
|
2438
2530
|
addTranslations,
|
|
2439
2531
|
getTranslations,
|
|
2532
|
+
getAllTranslations,
|
|
2440
2533
|
deleteTranslations,
|
|
2441
2534
|
setFallbackLanguage,
|
|
2442
2535
|
getFallbackLanguage,
|
|
2443
2536
|
addTransform,
|
|
2444
2537
|
deleteTransform,
|
|
2538
|
+
/** Collection of predefined transform functions that can be added via {@linkcode tr.addTransform()} */
|
|
2445
2539
|
transforms: {
|
|
2540
|
+
/**
|
|
2541
|
+
* This transform will replace placeholders matching `${key}` with the value of the passed argument(s).
|
|
2542
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2543
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2544
|
+
* - 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.
|
|
2545
|
+
*
|
|
2546
|
+
* @example ```ts
|
|
2547
|
+
* tr.addTranslations("en", {
|
|
2548
|
+
* "greeting": "Hello, ${user}!\nYou have ${notifs} notifications.",
|
|
2549
|
+
* });
|
|
2550
|
+
* tr.addTransform(tr.transforms.templateLiteral);
|
|
2551
|
+
*
|
|
2552
|
+
* const t = tr.use("en");
|
|
2553
|
+
*
|
|
2554
|
+
* // both calls return the same result:
|
|
2555
|
+
* t("greeting", { user: "John", notifs: 5 }); // "Hello, John!\nYou have 5 notifications."
|
|
2556
|
+
* t("greeting", "John", 5); // "Hello, John!\nYou have 5 notifications."
|
|
2557
|
+
*
|
|
2558
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2559
|
+
* t("greeting", { user: "John" }); // "Hello, John!\nYou have ${notifs} notifications."
|
|
2560
|
+
* ```
|
|
2561
|
+
*/
|
|
2446
2562
|
templateLiteral: templateLiteralTransform,
|
|
2563
|
+
/**
|
|
2564
|
+
* This transform will replace placeholders matching `{{key}}` with the value of the passed argument(s).
|
|
2565
|
+
* This format is commonly used in i18n libraries. Note that advanced syntax is not supported, only simple key replacement.
|
|
2566
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2567
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2568
|
+
* - 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.
|
|
2569
|
+
*
|
|
2570
|
+
* @example ```ts
|
|
2571
|
+
* tr.addTranslations("en", {
|
|
2572
|
+
* "greeting": "Hello, {{user}}!\nYou have {{notifs}} notifications.",
|
|
2573
|
+
* });
|
|
2574
|
+
* tr.addTransform(tr.transforms.i18n);
|
|
2575
|
+
*
|
|
2576
|
+
* const t = tr.use("en");
|
|
2577
|
+
*
|
|
2578
|
+
* // both calls return the same result:
|
|
2579
|
+
* t("greeting", { user: "Alice", notifs: 5 }); // "Hello, Alice!\nYou have 5 notifications."
|
|
2580
|
+
* t("greeting", "Alice", 5); // "Hello, Alice!\nYou have 5 notifications."
|
|
2581
|
+
*
|
|
2582
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2583
|
+
* t("greeting", { user: "Alice" }); // "Hello, Alice!\nYou have {{notifs}} notifications."
|
|
2584
|
+
* ```
|
|
2585
|
+
*/
|
|
2586
|
+
i18n: i18nTransform,
|
|
2587
|
+
/**
|
|
2588
|
+
* This transform will replace `%n` placeholders with the value of the passed arguments.
|
|
2589
|
+
* 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.
|
|
2590
|
+
* Objects will be stringified via `String()` before being inserted.
|
|
2591
|
+
*
|
|
2592
|
+
* @example ```ts
|
|
2593
|
+
* tr.addTranslations("en", {
|
|
2594
|
+
* "greeting": "Hello, %1!\nYou have %2 notifications.",
|
|
2595
|
+
* });
|
|
2596
|
+
* tr.addTransform(tr.transforms.percent);
|
|
2597
|
+
*
|
|
2598
|
+
* const t = tr.use("en");
|
|
2599
|
+
*
|
|
2600
|
+
* // arguments are inserted in the order they're passed:
|
|
2601
|
+
* t("greeting", "Bob", 5); // "Hello, Bob!\nYou have 5 notifications."
|
|
2602
|
+
*
|
|
2603
|
+
* // when a value isn't found, the placeholder will be left as-is:
|
|
2604
|
+
* t("greeting", "Bob"); // "Hello, Bob!\nYou have %2 notifications."
|
|
2605
|
+
* ```
|
|
2606
|
+
*/
|
|
2447
2607
|
percent: percentTransform
|
|
2448
2608
|
}
|
|
2449
2609
|
};
|
|
@@ -2528,6 +2688,6 @@ export {
|
|
|
2528
2688
|
takeRandomItemIndex,
|
|
2529
2689
|
tr,
|
|
2530
2690
|
truncStr,
|
|
2531
|
-
valsWithin
|
|
2691
|
+
valsWithin,
|
|
2692
|
+
versions
|
|
2532
2693
|
};
|
|
2533
|
-
//# sourceMappingURL=UserUtils.mjs.map
|