@sv443-network/userutils 10.0.6 → 10.2.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 +27 -0
- package/README.md +40 -32
- package/dist/UserUtils.cjs +600 -404
- package/dist/UserUtils.mjs +602 -404
- package/dist/UserUtils.umd.js +486 -309
- package/dist/lib/consts.d.ts +2 -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.4.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,15 +361,50 @@ 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
|
-
throw new
|
|
368
|
+
throw new CustomError("GetCallStack", "Capturing a stack trace with CoreUtils.getCallStack(). If you see this anywhere, 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
|
}
|
|
374
|
+
function createRecurringTask(options) {
|
|
375
|
+
var _a;
|
|
376
|
+
let iterations = 0;
|
|
377
|
+
let aborted = false;
|
|
378
|
+
(_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
|
|
379
|
+
aborted = true;
|
|
380
|
+
}, { once: true });
|
|
381
|
+
const runRecurringTask = async (initial = false) => {
|
|
382
|
+
var _a3, _b;
|
|
383
|
+
var _a2;
|
|
384
|
+
if (aborted)
|
|
385
|
+
return;
|
|
386
|
+
try {
|
|
387
|
+
if (((_a3 = options.immediate) != null ? _a3 : true) || !initial) {
|
|
388
|
+
iterations++;
|
|
389
|
+
if ((_b = await ((_a2 = options.condition) == null ? void 0 : _a2.call(options, iterations - 1))) != null ? _b : true) {
|
|
390
|
+
const val = await options.task(iterations - 1);
|
|
391
|
+
if (options.onSuccess)
|
|
392
|
+
await options.onSuccess(val, iterations - 1);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
if (options.onError)
|
|
397
|
+
await options.onError(err, iterations - 1);
|
|
398
|
+
if (options.abortOnError)
|
|
399
|
+
aborted = true;
|
|
400
|
+
if (!options.onError && !options.abortOnError)
|
|
401
|
+
throw err;
|
|
402
|
+
}
|
|
403
|
+
if (!aborted && (typeof options.maxIterations !== "number" || iterations < options.maxIterations))
|
|
404
|
+
setTimeout(runRecurringTask, options.timeout);
|
|
405
|
+
};
|
|
406
|
+
return runRecurringTask(true);
|
|
407
|
+
}
|
|
367
408
|
function autoPlural(term, num, pluralType = "auto") {
|
|
368
409
|
if (typeof num !== "number") {
|
|
369
410
|
if ("length" in num)
|
|
@@ -413,9 +454,10 @@ function createProgressBar(percentage, barLength, chars = defaultPbChars) {
|
|
|
413
454
|
}
|
|
414
455
|
function insertValues(input, ...values) {
|
|
415
456
|
return input.replace(/%\d/gm, (match) => {
|
|
457
|
+
var _a2;
|
|
416
458
|
var _a;
|
|
417
459
|
const argIndex = Number(match.substring(1)) - 1;
|
|
418
|
-
return (_a = values[argIndex]
|
|
460
|
+
return (_a = (_a2 = values[argIndex]) != null ? _a2 : match) == null ? void 0 : _a.toString();
|
|
419
461
|
});
|
|
420
462
|
}
|
|
421
463
|
function joinArrayReadable(array, separators = ", ", lastSeparator = " and ") {
|
|
@@ -446,31 +488,209 @@ function secsToTimeStr(seconds) {
|
|
|
446
488
|
].join("");
|
|
447
489
|
}
|
|
448
490
|
function truncStr(input, length, endStr = "...") {
|
|
449
|
-
|
|
491
|
+
var _a;
|
|
492
|
+
const str = (_a = input == null ? void 0 : input.toString()) != null ? _a : String(input);
|
|
450
493
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
451
494
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
452
495
|
}
|
|
453
|
-
var
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
496
|
+
var createNanoEvents = () => ({
|
|
497
|
+
emit(event, ...args) {
|
|
498
|
+
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
499
|
+
callbacks[i](...args);
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
events: {},
|
|
503
|
+
on(event, cb) {
|
|
504
|
+
var _a;
|
|
505
|
+
;
|
|
506
|
+
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
507
|
+
return () => {
|
|
508
|
+
var _a2;
|
|
509
|
+
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
var NanoEmitter = class {
|
|
514
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
515
|
+
constructor(options = {}) {
|
|
516
|
+
__publicField(this, "events", createNanoEvents());
|
|
517
|
+
__publicField(this, "eventUnsubscribes", []);
|
|
518
|
+
__publicField(this, "emitterOptions");
|
|
519
|
+
this.emitterOptions = {
|
|
520
|
+
publicEmit: false,
|
|
521
|
+
...options
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
//#region on
|
|
525
|
+
/**
|
|
526
|
+
* Subscribes to an event and calls the callback 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
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
529
|
+
* @example ```ts
|
|
530
|
+
* const emitter = new NanoEmitter<{
|
|
531
|
+
* foo: (bar: string) => void;
|
|
532
|
+
* }>({
|
|
533
|
+
* publicEmit: true,
|
|
534
|
+
* });
|
|
535
|
+
*
|
|
536
|
+
* let i = 0;
|
|
537
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
538
|
+
* // unsubscribe after 10 events:
|
|
539
|
+
* if(++i === 10) unsub();
|
|
540
|
+
* console.log(bar);
|
|
541
|
+
* });
|
|
542
|
+
*
|
|
543
|
+
* emitter.emit("foo", "bar");
|
|
544
|
+
* ```
|
|
545
|
+
*/
|
|
546
|
+
on(event, cb) {
|
|
547
|
+
let unsub;
|
|
548
|
+
const unsubProxy = () => {
|
|
549
|
+
if (!unsub)
|
|
550
|
+
return;
|
|
551
|
+
unsub();
|
|
552
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
553
|
+
};
|
|
554
|
+
unsub = this.events.on(event, cb);
|
|
555
|
+
this.eventUnsubscribes.push(unsub);
|
|
556
|
+
return unsubProxy;
|
|
557
|
+
}
|
|
558
|
+
//#region once
|
|
559
|
+
/**
|
|
560
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
561
|
+
* @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 "_")
|
|
562
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
563
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
564
|
+
* @example ```ts
|
|
565
|
+
* const emitter = new NanoEmitter<{
|
|
566
|
+
* foo: (bar: string) => void;
|
|
567
|
+
* }>();
|
|
568
|
+
*
|
|
569
|
+
* // Promise syntax:
|
|
570
|
+
* const [bar] = await emitter.once("foo");
|
|
571
|
+
* console.log(bar);
|
|
572
|
+
*
|
|
573
|
+
* // Callback syntax:
|
|
574
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
once(event, cb) {
|
|
578
|
+
return new Promise((resolve) => {
|
|
579
|
+
let unsub;
|
|
580
|
+
const onceProxy = ((...args) => {
|
|
581
|
+
cb == null ? void 0 : cb(...args);
|
|
582
|
+
unsub == null ? void 0 : unsub();
|
|
583
|
+
resolve(args);
|
|
584
|
+
});
|
|
585
|
+
unsub = this.events.on(event, onceProxy);
|
|
586
|
+
this.eventUnsubscribes.push(unsub);
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
//#region onMulti
|
|
590
|
+
/**
|
|
591
|
+
* 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.
|
|
592
|
+
* @param options An object or array of objects with the following properties:
|
|
593
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
594
|
+
*
|
|
595
|
+
* 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.
|
|
596
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
597
|
+
*
|
|
598
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
599
|
+
* 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.
|
|
600
|
+
* 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.
|
|
601
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
602
|
+
*
|
|
603
|
+
* @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.
|
|
604
|
+
*/
|
|
605
|
+
onMulti(options) {
|
|
606
|
+
const allUnsubs = [];
|
|
607
|
+
const unsubAll = () => {
|
|
608
|
+
for (const unsub of allUnsubs)
|
|
609
|
+
unsub();
|
|
610
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
611
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
612
|
+
};
|
|
613
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
614
|
+
const optsWithDefaults = {
|
|
615
|
+
allOf: [],
|
|
616
|
+
oneOf: [],
|
|
617
|
+
once: false,
|
|
618
|
+
...opts
|
|
619
|
+
};
|
|
620
|
+
const {
|
|
621
|
+
oneOf,
|
|
622
|
+
allOf,
|
|
623
|
+
once,
|
|
624
|
+
signal,
|
|
625
|
+
callback
|
|
626
|
+
} = optsWithDefaults;
|
|
627
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
628
|
+
return unsubAll;
|
|
629
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
630
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
631
|
+
const curEvtUnsubs = [];
|
|
632
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
633
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
634
|
+
return;
|
|
635
|
+
for (const unsub of curEvtUnsubs)
|
|
636
|
+
unsub();
|
|
637
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
638
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
639
|
+
};
|
|
640
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
641
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
642
|
+
for (const event of oneOf) {
|
|
643
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
644
|
+
checkUnsubAllEvt();
|
|
645
|
+
if (allOfConditionMet()) {
|
|
646
|
+
callback(event, ...args);
|
|
647
|
+
if (once)
|
|
648
|
+
checkUnsubAllEvt(true);
|
|
649
|
+
}
|
|
650
|
+
}));
|
|
651
|
+
curEvtUnsubs.push(unsub);
|
|
652
|
+
}
|
|
653
|
+
for (const event of allOf) {
|
|
654
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
655
|
+
checkUnsubAllEvt();
|
|
656
|
+
allOfEmitted.add(event);
|
|
657
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
658
|
+
callback(event, ...args);
|
|
659
|
+
if (once)
|
|
660
|
+
checkUnsubAllEvt(true);
|
|
661
|
+
}
|
|
662
|
+
}));
|
|
663
|
+
curEvtUnsubs.push(unsub);
|
|
664
|
+
}
|
|
665
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
666
|
+
}
|
|
667
|
+
return unsubAll;
|
|
668
|
+
}
|
|
669
|
+
//#region emit
|
|
464
670
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
671
|
+
* Emits an event on this instance.
|
|
672
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
673
|
+
* @param event The event to emit
|
|
674
|
+
* @param args The arguments to pass to the event listeners
|
|
675
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
468
676
|
*/
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
677
|
+
emit(event, ...args) {
|
|
678
|
+
if (this.emitterOptions.publicEmit) {
|
|
679
|
+
this.events.emit(event, ...args);
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
//#region unsubscribeAll
|
|
685
|
+
/** Unsubscribes all event listeners from this instance */
|
|
686
|
+
unsubscribeAll() {
|
|
687
|
+
for (const unsub of this.eventUnsubscribes)
|
|
688
|
+
unsub();
|
|
689
|
+
this.eventUnsubscribes = [];
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
var dsFmtVer = 1;
|
|
693
|
+
var DataStore = class extends NanoEmitter {
|
|
474
694
|
//#region constructor
|
|
475
695
|
/**
|
|
476
696
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -482,20 +702,43 @@ var DataStore = class {
|
|
|
482
702
|
* @param opts The options for this DataStore instance
|
|
483
703
|
*/
|
|
484
704
|
constructor(opts) {
|
|
705
|
+
var _a, _b, _c;
|
|
706
|
+
super(opts.nanoEmitterOptions);
|
|
707
|
+
__publicField(this, "id");
|
|
708
|
+
__publicField(this, "formatVersion");
|
|
709
|
+
__publicField(this, "defaultData");
|
|
710
|
+
__publicField(this, "encodeData");
|
|
711
|
+
__publicField(this, "decodeData");
|
|
712
|
+
__publicField(this, "compressionFormat", "deflate-raw");
|
|
713
|
+
__publicField(this, "memoryCache");
|
|
714
|
+
__publicField(this, "engine");
|
|
715
|
+
__publicField(this, "keyPrefix");
|
|
716
|
+
__publicField(this, "options");
|
|
717
|
+
/**
|
|
718
|
+
* Whether all first-init checks should be done.
|
|
719
|
+
* This includes migrating the internal DataStore format, migrating data from the UserUtils format, and anything similar.
|
|
720
|
+
* 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.
|
|
721
|
+
*/
|
|
722
|
+
__publicField(this, "firstInit", true);
|
|
723
|
+
/** In-memory cached copy of the data that is saved in persistent storage used for synchronous read access. */
|
|
724
|
+
__publicField(this, "cachedData");
|
|
725
|
+
__publicField(this, "migrations");
|
|
726
|
+
__publicField(this, "migrateIds", []);
|
|
485
727
|
this.id = opts.id;
|
|
486
728
|
this.formatVersion = opts.formatVersion;
|
|
487
729
|
this.defaultData = opts.defaultData;
|
|
488
|
-
this.memoryCache = opts.memoryCache
|
|
730
|
+
this.memoryCache = (_a = opts.memoryCache) != null ? _a : true;
|
|
489
731
|
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
490
732
|
this.migrations = opts.migrations;
|
|
491
733
|
if (opts.migrateIds)
|
|
492
734
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
493
735
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
736
|
+
this.keyPrefix = (_b = opts.keyPrefix) != null ? _b : "__ds-";
|
|
494
737
|
this.options = opts;
|
|
495
738
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
496
739
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
497
740
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
498
|
-
this.compressionFormat = opts.encodeData[0]
|
|
741
|
+
this.compressionFormat = (_c = opts.encodeData[0]) != null ? _c : null;
|
|
499
742
|
} else if (opts.compressionFormat === null) {
|
|
500
743
|
this.encodeData = void 0;
|
|
501
744
|
this.decodeData = void 0;
|
|
@@ -519,6 +762,7 @@ var DataStore = class {
|
|
|
519
762
|
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
520
763
|
*/
|
|
521
764
|
async loadData() {
|
|
765
|
+
var _a;
|
|
522
766
|
try {
|
|
523
767
|
if (this.firstInit) {
|
|
524
768
|
this.firstInit = false;
|
|
@@ -532,13 +776,13 @@ var DataStore = class {
|
|
|
532
776
|
promises.push(this.engine.setValue(newKey, value));
|
|
533
777
|
promises.push(this.engine.deleteValue(oldKey));
|
|
534
778
|
};
|
|
535
|
-
migrateFmt(`_uucfg-${this.id}`,
|
|
779
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
536
780
|
if (!isNaN(oldVer))
|
|
537
|
-
migrateFmt(`_uucfgver-${this.id}`,
|
|
781
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
538
782
|
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
539
|
-
migrateFmt(`_uucfgenc-${this.id}`,
|
|
783
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? (_a = this.compressionFormat) != null ? _a : null : null);
|
|
540
784
|
else {
|
|
541
|
-
promises.push(this.engine.setValue(
|
|
785
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
542
786
|
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
543
787
|
}
|
|
544
788
|
await Promise.allSettled(promises);
|
|
@@ -550,31 +794,27 @@ var DataStore = class {
|
|
|
550
794
|
await this.migrateId(this.migrateIds);
|
|
551
795
|
this.migrateIds = [];
|
|
552
796
|
}
|
|
553
|
-
const storedDataRaw = await this.engine.getValue(
|
|
554
|
-
|
|
555
|
-
if (typeof storedDataRaw !== "string") {
|
|
556
|
-
await this.saveDefaultData();
|
|
557
|
-
|
|
797
|
+
const storedDataRaw = await this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
798
|
+
const storedFmtVer = Number(await this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
799
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
800
|
+
await this.saveDefaultData(false);
|
|
801
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
802
|
+
this.events.emit("loadData", data);
|
|
803
|
+
return data;
|
|
558
804
|
}
|
|
559
|
-
const storedData = storedDataRaw
|
|
560
|
-
const encodingFmt = String(await this.engine.getValue(
|
|
805
|
+
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
806
|
+
const encodingFmt = String(await this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
561
807
|
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);
|
|
808
|
+
let parsed = typeof storedData === "string" ? await this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
568
809
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
569
810
|
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);
|
|
811
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
812
|
+
this.events.emit("loadData", result);
|
|
813
|
+
return result;
|
|
576
814
|
} catch (err) {
|
|
815
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
577
816
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
817
|
+
this.events.emit("error", error);
|
|
578
818
|
await this.saveDefaultData();
|
|
579
819
|
return this.defaultData;
|
|
580
820
|
}
|
|
@@ -593,27 +833,47 @@ var DataStore = class {
|
|
|
593
833
|
//#region setData
|
|
594
834
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
595
835
|
setData(data) {
|
|
596
|
-
|
|
836
|
+
const dataCopy = this.engine.deepCopy(data);
|
|
837
|
+
if (this.memoryCache) {
|
|
597
838
|
this.cachedData = data;
|
|
839
|
+
this.events.emit("updateDataSync", dataCopy);
|
|
840
|
+
}
|
|
598
841
|
return new Promise(async (resolve) => {
|
|
599
|
-
await Promise.allSettled([
|
|
600
|
-
this.engine.setValue(
|
|
601
|
-
this.engine.setValue(
|
|
602
|
-
this.engine.setValue(
|
|
842
|
+
const results = await Promise.allSettled([
|
|
843
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(data, this.encodingEnabled())),
|
|
844
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
845
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
603
846
|
]);
|
|
847
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
848
|
+
this.events.emit("updateData", dataCopy);
|
|
849
|
+
else {
|
|
850
|
+
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
851
|
+
console.error(error);
|
|
852
|
+
this.events.emit("error", error);
|
|
853
|
+
}
|
|
604
854
|
resolve();
|
|
605
855
|
});
|
|
606
856
|
}
|
|
607
857
|
//#region saveDefaultData
|
|
608
|
-
/**
|
|
609
|
-
|
|
858
|
+
/**
|
|
859
|
+
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
860
|
+
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
861
|
+
*/
|
|
862
|
+
async saveDefaultData(emitEvent = true) {
|
|
610
863
|
if (this.memoryCache)
|
|
611
864
|
this.cachedData = this.defaultData;
|
|
612
|
-
await Promise.allSettled([
|
|
613
|
-
this.engine.setValue(
|
|
614
|
-
this.engine.setValue(
|
|
615
|
-
this.engine.setValue(
|
|
865
|
+
const results = await Promise.allSettled([
|
|
866
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
867
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
868
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
616
869
|
]);
|
|
870
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
871
|
+
emitEvent && this.events.emit("setDefaultData", this.defaultData);
|
|
872
|
+
else {
|
|
873
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
874
|
+
console.error(error);
|
|
875
|
+
this.events.emit("error", error);
|
|
876
|
+
}
|
|
617
877
|
}
|
|
618
878
|
//#region deleteData
|
|
619
879
|
/**
|
|
@@ -624,11 +884,12 @@ var DataStore = class {
|
|
|
624
884
|
async deleteData() {
|
|
625
885
|
var _a, _b;
|
|
626
886
|
await Promise.allSettled([
|
|
627
|
-
this.engine.deleteValue(
|
|
628
|
-
this.engine.deleteValue(
|
|
629
|
-
this.engine.deleteValue(
|
|
887
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
888
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
889
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
630
890
|
]);
|
|
631
891
|
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
892
|
+
this.events.emit("deleteData");
|
|
632
893
|
}
|
|
633
894
|
//#region encodingEnabled
|
|
634
895
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
@@ -649,30 +910,35 @@ var DataStore = class {
|
|
|
649
910
|
let newData = oldData;
|
|
650
911
|
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
651
912
|
let lastFmtVer = oldFmtVer;
|
|
652
|
-
for (
|
|
913
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
914
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
653
915
|
const ver = Number(fmtVer);
|
|
654
916
|
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
655
917
|
try {
|
|
656
918
|
const migRes = migrationFunc(newData);
|
|
657
919
|
newData = migRes instanceof Promise ? await migRes : migRes;
|
|
658
920
|
lastFmtVer = oldFmtVer = ver;
|
|
921
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
922
|
+
this.events.emit("migrateData", ver, newData, isFinal);
|
|
659
923
|
} catch (err) {
|
|
924
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
925
|
+
this.events.emit("migrationError", ver, migError);
|
|
926
|
+
this.events.emit("error", migError);
|
|
660
927
|
if (!resetOnError)
|
|
661
|
-
throw
|
|
928
|
+
throw migError;
|
|
662
929
|
await this.saveDefaultData();
|
|
663
930
|
return this.engine.deepCopy(this.defaultData);
|
|
664
931
|
}
|
|
665
932
|
}
|
|
666
933
|
}
|
|
667
934
|
await Promise.allSettled([
|
|
668
|
-
this.engine.setValue(
|
|
669
|
-
this.engine.setValue(
|
|
670
|
-
this.engine.setValue(
|
|
935
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(newData, this.encodingEnabled())),
|
|
936
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
937
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
671
938
|
]);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return this.engine.deepCopy(newData);
|
|
939
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
940
|
+
this.events.emit("updateData", result);
|
|
941
|
+
return result;
|
|
676
942
|
}
|
|
677
943
|
//#region migrateId
|
|
678
944
|
/**
|
|
@@ -684,9 +950,9 @@ var DataStore = class {
|
|
|
684
950
|
await Promise.all(ids.map(async (id) => {
|
|
685
951
|
const [data, fmtVer, isEncoded] = await (async () => {
|
|
686
952
|
const [d, f, e] = await Promise.all([
|
|
687
|
-
this.engine.getValue(
|
|
688
|
-
this.engine.getValue(
|
|
689
|
-
this.engine.getValue(
|
|
953
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
954
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
955
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
690
956
|
]);
|
|
691
957
|
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
692
958
|
})();
|
|
@@ -694,20 +960,21 @@ var DataStore = class {
|
|
|
694
960
|
return;
|
|
695
961
|
const parsed = await this.engine.deserializeData(data, isEncoded);
|
|
696
962
|
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(
|
|
963
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, await this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
964
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
965
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
966
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
967
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
968
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
703
969
|
]);
|
|
970
|
+
this.events.emit("migrateId", id, this.id);
|
|
704
971
|
}));
|
|
705
972
|
}
|
|
706
973
|
};
|
|
707
974
|
var DataStoreEngine = class {
|
|
708
|
-
dataStoreOptions;
|
|
709
975
|
// setDataStoreOptions() is called from inside the DataStore constructor to set this value
|
|
710
976
|
constructor(options) {
|
|
977
|
+
__publicField(this, "dataStoreOptions");
|
|
711
978
|
if (options)
|
|
712
979
|
this.dataStoreOptions = options;
|
|
713
980
|
}
|
|
@@ -735,7 +1002,7 @@ var DataStoreEngine = class {
|
|
|
735
1002
|
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
1003
|
if (decRes instanceof Promise)
|
|
737
1004
|
decRes = await decRes;
|
|
738
|
-
return JSON.parse(decRes
|
|
1005
|
+
return JSON.parse(decRes != null ? decRes : data);
|
|
739
1006
|
}
|
|
740
1007
|
//#region misc api
|
|
741
1008
|
/** Throws an error if the DataStoreOptions are not set or invalid */
|
|
@@ -753,13 +1020,12 @@ var DataStoreEngine = class {
|
|
|
753
1020
|
try {
|
|
754
1021
|
if ("structuredClone" in globalThis)
|
|
755
1022
|
return structuredClone(obj);
|
|
756
|
-
} catch {
|
|
1023
|
+
} catch (e) {
|
|
757
1024
|
}
|
|
758
1025
|
return JSON.parse(JSON.stringify(obj));
|
|
759
1026
|
}
|
|
760
1027
|
};
|
|
761
1028
|
var BrowserStorageEngine = class extends DataStoreEngine {
|
|
762
|
-
options;
|
|
763
1029
|
/**
|
|
764
1030
|
* Creates an instance of `BrowserStorageEngine`.
|
|
765
1031
|
*
|
|
@@ -768,6 +1034,7 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
768
1034
|
*/
|
|
769
1035
|
constructor(options) {
|
|
770
1036
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1037
|
+
__publicField(this, "options");
|
|
771
1038
|
this.options = {
|
|
772
1039
|
type: "localStorage",
|
|
773
1040
|
...options
|
|
@@ -796,8 +1063,6 @@ var BrowserStorageEngine = class extends DataStoreEngine {
|
|
|
796
1063
|
};
|
|
797
1064
|
var fs;
|
|
798
1065
|
var FileStorageEngine = class extends DataStoreEngine {
|
|
799
|
-
options;
|
|
800
|
-
fileAccessQueue = Promise.resolve();
|
|
801
1066
|
/**
|
|
802
1067
|
* Creates an instance of `FileStorageEngine`.
|
|
803
1068
|
*
|
|
@@ -806,6 +1071,8 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
806
1071
|
*/
|
|
807
1072
|
constructor(options) {
|
|
808
1073
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
1074
|
+
__publicField(this, "options");
|
|
1075
|
+
__publicField(this, "fileAccessQueue", Promise.resolve());
|
|
809
1076
|
this.options = {
|
|
810
1077
|
filePath: (id) => `.ds-${id}`,
|
|
811
1078
|
...options
|
|
@@ -814,6 +1081,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
814
1081
|
//#region json file
|
|
815
1082
|
/** Reads the file contents */
|
|
816
1083
|
async readFile() {
|
|
1084
|
+
var _a2;
|
|
817
1085
|
var _a, _b, _c, _d;
|
|
818
1086
|
this.ensureDataStoreOptions();
|
|
819
1087
|
try {
|
|
@@ -821,15 +1089,16 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
821
1089
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
822
1090
|
if (!fs)
|
|
823
1091
|
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);
|
|
1092
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
825
1093
|
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 {
|
|
1094
|
+
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;
|
|
1095
|
+
} catch (e) {
|
|
828
1096
|
return void 0;
|
|
829
1097
|
}
|
|
830
1098
|
}
|
|
831
1099
|
/** Overwrites the file contents */
|
|
832
1100
|
async writeFile(data) {
|
|
1101
|
+
var _a2;
|
|
833
1102
|
var _a, _b, _c, _d;
|
|
834
1103
|
this.ensureDataStoreOptions();
|
|
835
1104
|
try {
|
|
@@ -837,9 +1106,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
837
1106
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
838
1107
|
if (!fs)
|
|
839
1108
|
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);
|
|
1109
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
841
1110
|
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)))
|
|
1111
|
+
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
1112
|
} catch (err) {
|
|
844
1113
|
console.error("Error writing file:", err);
|
|
845
1114
|
}
|
|
@@ -853,8 +1122,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
853
1122
|
const value = data == null ? void 0 : data[name];
|
|
854
1123
|
if (typeof value === "undefined")
|
|
855
1124
|
return defaultValue;
|
|
856
|
-
if (typeof
|
|
857
|
-
|
|
1125
|
+
if (typeof defaultValue === "string") {
|
|
1126
|
+
if (typeof value === "object" && value !== null)
|
|
1127
|
+
return JSON.stringify(value);
|
|
1128
|
+
if (typeof value === "string")
|
|
1129
|
+
return value;
|
|
1130
|
+
return String(value);
|
|
1131
|
+
}
|
|
1132
|
+
if (typeof value === "string") {
|
|
1133
|
+
try {
|
|
1134
|
+
const parsed = JSON.parse(value);
|
|
1135
|
+
return parsed;
|
|
1136
|
+
} catch (e) {
|
|
1137
|
+
return defaultValue;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
858
1140
|
return value;
|
|
859
1141
|
}
|
|
860
1142
|
/** Sets a value in persistent storage */
|
|
@@ -863,7 +1145,18 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
863
1145
|
let data = await this.readFile();
|
|
864
1146
|
if (!data)
|
|
865
1147
|
data = {};
|
|
866
|
-
|
|
1148
|
+
let storeVal = value;
|
|
1149
|
+
if (typeof value === "string") {
|
|
1150
|
+
try {
|
|
1151
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1152
|
+
const parsed = JSON.parse(value);
|
|
1153
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1154
|
+
storeVal = parsed;
|
|
1155
|
+
}
|
|
1156
|
+
} catch (e) {
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
data[name] = storeVal;
|
|
867
1160
|
await this.writeFile(data);
|
|
868
1161
|
}).catch((err) => {
|
|
869
1162
|
console.error("Error in setValue:", err);
|
|
@@ -896,7 +1189,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
896
1189
|
fs = (_a = await import("fs/promises")) == null ? void 0 : _a.default;
|
|
897
1190
|
if (!fs)
|
|
898
1191
|
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);
|
|
1192
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
900
1193
|
return await fs.unlink(path);
|
|
901
1194
|
} catch (err) {
|
|
902
1195
|
console.error("Error deleting file:", err);
|
|
@@ -904,9 +1197,9 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
904
1197
|
}
|
|
905
1198
|
};
|
|
906
1199
|
var DataStoreSerializer = class _DataStoreSerializer {
|
|
907
|
-
stores;
|
|
908
|
-
options;
|
|
909
1200
|
constructor(stores, options = {}) {
|
|
1201
|
+
__publicField(this, "stores");
|
|
1202
|
+
__publicField(this, "options");
|
|
910
1203
|
if (!crypto || !crypto.subtle)
|
|
911
1204
|
throw new ScriptContextError("DataStoreSerializer has to run in a secure context (HTTPS) or in another environment that implements the subtleCrypto API!");
|
|
912
1205
|
this.stores = stores;
|
|
@@ -917,7 +1210,10 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
917
1210
|
...options
|
|
918
1211
|
};
|
|
919
1212
|
}
|
|
920
|
-
/**
|
|
1213
|
+
/**
|
|
1214
|
+
* Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1215
|
+
* Override this in a subclass if a custom checksum method is needed.
|
|
1216
|
+
*/
|
|
921
1217
|
async calcChecksum(input) {
|
|
922
1218
|
return computeHash(input, "SHA-256");
|
|
923
1219
|
}
|
|
@@ -962,8 +1258,9 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
962
1258
|
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
963
1259
|
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
964
1260
|
const resolveStoreId = (id) => {
|
|
1261
|
+
var _a2;
|
|
965
1262
|
var _a;
|
|
966
|
-
return ((_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0])
|
|
1263
|
+
return (_a2 = (_a = Object.entries(this.options.remapIds).find(([, v]) => v.includes(id))) == null ? void 0 : _a[0]) != null ? _a2 : id;
|
|
967
1264
|
};
|
|
968
1265
|
const matchesFilter = (id) => typeof stores === "function" ? stores(id) : stores.includes(id);
|
|
969
1266
|
for (const storeData of deserStores) {
|
|
@@ -1040,218 +1337,23 @@ Has: ${checksum}`);
|
|
|
1040
1337
|
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1041
1338
|
}
|
|
1042
1339
|
};
|
|
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
1340
|
var Debouncer = class extends NanoEmitter {
|
|
1239
1341
|
/**
|
|
1240
1342
|
* Creates a new debouncer with the specified timeout and edge type.
|
|
1241
1343
|
* @param timeout Timeout in milliseconds between letting through calls - defaults to 200
|
|
1242
1344
|
* @param type The edge type to use for the debouncer - see {@linkcode DebouncerType} for details or [the documentation for an explanation and diagram](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debouncer) - defaults to "immediate"
|
|
1243
1345
|
*/
|
|
1244
|
-
constructor(timeout = 200, type = "immediate") {
|
|
1245
|
-
super();
|
|
1346
|
+
constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1347
|
+
super(nanoEmitterOptions);
|
|
1348
|
+
/** All registered listener functions and the time they were attached */
|
|
1349
|
+
__publicField(this, "listeners", []);
|
|
1350
|
+
/** The currently active timeout */
|
|
1351
|
+
__publicField(this, "activeTimeout");
|
|
1352
|
+
/** The latest queued call */
|
|
1353
|
+
__publicField(this, "queuedCall");
|
|
1246
1354
|
this.timeout = timeout;
|
|
1247
1355
|
this.type = type;
|
|
1248
1356
|
}
|
|
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
1357
|
//#region listeners
|
|
1256
1358
|
/** Adds a listener function that will be called on timeout */
|
|
1257
1359
|
addListener(fn) {
|
|
@@ -1273,7 +1375,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1273
1375
|
//#region timeout
|
|
1274
1376
|
/** Sets the timeout for the debouncer */
|
|
1275
1377
|
setTimeout(timeout) {
|
|
1276
|
-
this.emit("change", this.timeout = timeout, this.type);
|
|
1378
|
+
this.events.emit("change", this.timeout = timeout, this.type);
|
|
1277
1379
|
}
|
|
1278
1380
|
/** Returns the current timeout */
|
|
1279
1381
|
getTimeout() {
|
|
@@ -1286,7 +1388,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1286
1388
|
//#region type
|
|
1287
1389
|
/** Sets the edge type for the debouncer */
|
|
1288
1390
|
setType(type) {
|
|
1289
|
-
this.emit("change", this.timeout, this.type = type);
|
|
1391
|
+
this.events.emit("change", this.timeout, this.type = type);
|
|
1290
1392
|
}
|
|
1291
1393
|
/** Returns the current edge type */
|
|
1292
1394
|
getType() {
|
|
@@ -1297,7 +1399,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1297
1399
|
call(...args) {
|
|
1298
1400
|
const cl = (...a) => {
|
|
1299
1401
|
this.queuedCall = void 0;
|
|
1300
|
-
this.emit("call", ...a);
|
|
1402
|
+
this.events.emit("call", ...a);
|
|
1301
1403
|
this.listeners.forEach((l) => l.call(this, ...a));
|
|
1302
1404
|
};
|
|
1303
1405
|
const setRepeatTimeout = () => {
|
|
@@ -1330,14 +1432,30 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1330
1432
|
}
|
|
1331
1433
|
}
|
|
1332
1434
|
};
|
|
1333
|
-
function debounce(fn, timeout = 200, type = "immediate") {
|
|
1334
|
-
const debouncer = new Debouncer(timeout, type);
|
|
1435
|
+
function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1436
|
+
const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
|
|
1335
1437
|
debouncer.addListener(fn);
|
|
1336
1438
|
const func = ((...args) => debouncer.call(...args));
|
|
1337
1439
|
func.debouncer = debouncer;
|
|
1338
1440
|
return func;
|
|
1339
1441
|
}
|
|
1340
1442
|
|
|
1443
|
+
// lib/consts.ts
|
|
1444
|
+
var rawConsts = {
|
|
1445
|
+
coreUtilsVersion: "3.4.0",
|
|
1446
|
+
userUtilsVersion: "10.2.0"
|
|
1447
|
+
};
|
|
1448
|
+
function getConst(constKey, defaultVal) {
|
|
1449
|
+
const val = rawConsts[constKey];
|
|
1450
|
+
return val.match(/^#\{\{.+\}\}$/) ? defaultVal : val;
|
|
1451
|
+
}
|
|
1452
|
+
var versions = {
|
|
1453
|
+
/** Semver version string of the bundled library CoreUtils. */
|
|
1454
|
+
CoreUtils: getConst("coreUtilsVersion", "ERR:unknown"),
|
|
1455
|
+
/** Semver version string of UserUtils. */
|
|
1456
|
+
UserUtils: getConst("userUtilsVersion", "ERR:unknown")
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1341
1459
|
// lib/Errors.ts
|
|
1342
1460
|
var PlatformError = class extends DatedError {
|
|
1343
1461
|
constructor(message, options) {
|
|
@@ -1350,7 +1468,7 @@ var PlatformError = class extends DatedError {
|
|
|
1350
1468
|
function getUnsafeWindow() {
|
|
1351
1469
|
try {
|
|
1352
1470
|
return unsafeWindow;
|
|
1353
|
-
} catch {
|
|
1471
|
+
} catch (e) {
|
|
1354
1472
|
return window;
|
|
1355
1473
|
}
|
|
1356
1474
|
}
|
|
@@ -1381,7 +1499,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1381
1499
|
try {
|
|
1382
1500
|
if (typeof window.GM === "object")
|
|
1383
1501
|
GM.openInTab(href, background);
|
|
1384
|
-
} catch {
|
|
1502
|
+
} catch (e) {
|
|
1385
1503
|
const openElem = document.createElement("a");
|
|
1386
1504
|
Object.assign(openElem, {
|
|
1387
1505
|
className: "userutils-open-in-new-tab",
|
|
@@ -1401,7 +1519,7 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
1401
1519
|
setTimeout(() => {
|
|
1402
1520
|
try {
|
|
1403
1521
|
openElem.remove();
|
|
1404
|
-
} catch {
|
|
1522
|
+
} catch (e2) {
|
|
1405
1523
|
}
|
|
1406
1524
|
}, 0);
|
|
1407
1525
|
}
|
|
@@ -1417,8 +1535,8 @@ function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
|
1417
1535
|
}
|
|
1418
1536
|
(function(original) {
|
|
1419
1537
|
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)
|
|
1538
|
+
var _a2, _b;
|
|
1539
|
+
const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? void 0 : _a2.handleEvent) != null ? _b : (() => void 0);
|
|
1422
1540
|
args[1] = function(...a) {
|
|
1423
1541
|
if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
|
|
1424
1542
|
return;
|
|
@@ -1461,8 +1579,8 @@ function observeElementProp(element, property, callback) {
|
|
|
1461
1579
|
}
|
|
1462
1580
|
}
|
|
1463
1581
|
function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) {
|
|
1464
|
-
var _a;
|
|
1465
|
-
const siblings = [...((_a = refElement.parentNode) == null ? void 0 : _a.childNodes)
|
|
1582
|
+
var _a, _b;
|
|
1583
|
+
const siblings = [...(_b = (_a = refElement.parentNode) == null ? void 0 : _a.childNodes) != null ? _b : []];
|
|
1466
1584
|
const elemSiblIdx = siblings.indexOf(refElement);
|
|
1467
1585
|
if (elemSiblIdx === -1)
|
|
1468
1586
|
throw new Error("Element doesn't have a parent node");
|
|
@@ -1483,13 +1601,13 @@ function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "cent
|
|
|
1483
1601
|
}
|
|
1484
1602
|
var ttPolicy;
|
|
1485
1603
|
function setInnerHtmlUnsafe(element, html) {
|
|
1486
|
-
var _a, _b;
|
|
1604
|
+
var _a, _b, _c;
|
|
1487
1605
|
if (!ttPolicy && typeof ((_a = window == null ? void 0 : window.trustedTypes) == null ? void 0 : _a.createPolicy) === "function") {
|
|
1488
1606
|
ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", {
|
|
1489
1607
|
createHTML: (unsafeHtml) => unsafeHtml
|
|
1490
1608
|
});
|
|
1491
1609
|
}
|
|
1492
|
-
element.innerHTML = ((_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html))
|
|
1610
|
+
element.innerHTML = (_c = (_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
1493
1611
|
return element;
|
|
1494
1612
|
}
|
|
1495
1613
|
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
@@ -1682,20 +1800,20 @@ var defaultStrings = {
|
|
|
1682
1800
|
closeDialogTooltip: "Click to close the dialog"
|
|
1683
1801
|
};
|
|
1684
1802
|
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
1803
|
constructor(options) {
|
|
1694
1804
|
super();
|
|
1805
|
+
/** Options passed to the dialog in the constructor */
|
|
1806
|
+
__publicField(this, "options");
|
|
1807
|
+
/** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */
|
|
1808
|
+
__publicField(this, "id");
|
|
1809
|
+
/** Strings used in the dialog (used for translations) */
|
|
1810
|
+
__publicField(this, "strings");
|
|
1811
|
+
__publicField(this, "dialogOpen", false);
|
|
1812
|
+
__publicField(this, "dialogMounted", false);
|
|
1695
1813
|
const { strings, ...opts } = options;
|
|
1696
1814
|
this.strings = {
|
|
1697
1815
|
...defaultStrings,
|
|
1698
|
-
...strings
|
|
1816
|
+
...strings != null ? strings : {}
|
|
1699
1817
|
};
|
|
1700
1818
|
this.options = {
|
|
1701
1819
|
closeOnBgClick: true,
|
|
@@ -1712,11 +1830,12 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1712
1830
|
//#region public
|
|
1713
1831
|
/** Call after DOMContentLoaded to pre-render the dialog and invisibly mount it in the DOM */
|
|
1714
1832
|
async mount() {
|
|
1833
|
+
var _a;
|
|
1715
1834
|
if (this.dialogMounted)
|
|
1716
1835
|
return;
|
|
1717
1836
|
this.dialogMounted = true;
|
|
1718
1837
|
if (!document.querySelector("style.uu-dialog-css"))
|
|
1719
|
-
addGlobalStyle(this.options.dialogCss
|
|
1838
|
+
addGlobalStyle((_a = this.options.dialogCss) != null ? _a : defaultDialogCss).classList.add("uu-dialog-css");
|
|
1720
1839
|
const bgElem = document.createElement("div");
|
|
1721
1840
|
bgElem.id = `uu-${this.id}-dialog-bg`;
|
|
1722
1841
|
bgElem.classList.add("uu-dialog-bg");
|
|
@@ -1784,7 +1903,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1784
1903
|
}
|
|
1785
1904
|
/** Closes the dialog - prevents default action and immediate propagation of the passed event */
|
|
1786
1905
|
close(e) {
|
|
1787
|
-
var _a;
|
|
1906
|
+
var _a, _b;
|
|
1788
1907
|
e == null ? void 0 : e.preventDefault();
|
|
1789
1908
|
e == null ? void 0 : e.stopImmediatePropagation();
|
|
1790
1909
|
if (!this.isOpen())
|
|
@@ -1797,9 +1916,9 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1797
1916
|
dialogBg.style.display = "none";
|
|
1798
1917
|
dialogBg.inert = true;
|
|
1799
1918
|
openDialogs.splice(openDialogs.indexOf(this.id), 1);
|
|
1800
|
-
currentDialogId = openDialogs[0]
|
|
1919
|
+
currentDialogId = (_a = openDialogs[0]) != null ? _a : null;
|
|
1801
1920
|
if (currentDialogId)
|
|
1802
|
-
(
|
|
1921
|
+
(_b = document.querySelector(`#uu-${currentDialogId}-dialog-bg`)) == null ? void 0 : _b.removeAttribute("inert");
|
|
1803
1922
|
if (openDialogs.length === 0) {
|
|
1804
1923
|
document.body.classList.add("uu-no-select");
|
|
1805
1924
|
document.body.removeAttribute("inert");
|
|
@@ -1835,7 +1954,8 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1835
1954
|
}
|
|
1836
1955
|
//#region protected
|
|
1837
1956
|
getString(key) {
|
|
1838
|
-
|
|
1957
|
+
var _a;
|
|
1958
|
+
return (_a = this.strings[key]) != null ? _a : defaultStrings[key];
|
|
1839
1959
|
}
|
|
1840
1960
|
/** Called once to attach all generic event listeners */
|
|
1841
1961
|
attachListeners(bgElem) {
|
|
@@ -1860,7 +1980,7 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1860
1980
|
* @param listenerOptions Provide a {@linkcode listenerOptions} object to configure the listeners
|
|
1861
1981
|
*/
|
|
1862
1982
|
onInteraction(elem, listener, listenerOptions) {
|
|
1863
|
-
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions
|
|
1983
|
+
const { preventDefault = true, stopPropagation = true, ...listenerOpts } = listenerOptions != null ? listenerOptions : {};
|
|
1864
1984
|
const interactionKeys = ["Enter", " ", "Space"];
|
|
1865
1985
|
const proxListener = (e) => {
|
|
1866
1986
|
if (e instanceof KeyboardEvent) {
|
|
@@ -1940,7 +2060,6 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1940
2060
|
|
|
1941
2061
|
// lib/GMStorageEngine.ts
|
|
1942
2062
|
var GMStorageEngine = class extends DataStoreEngine {
|
|
1943
|
-
options;
|
|
1944
2063
|
/**
|
|
1945
2064
|
* Creates an instance of `GMStorageEngine`.
|
|
1946
2065
|
*
|
|
@@ -1949,6 +2068,7 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
1949
2068
|
*/
|
|
1950
2069
|
constructor(options) {
|
|
1951
2070
|
super(options == null ? void 0 : options.dataStoreOptions);
|
|
2071
|
+
__publicField(this, "options");
|
|
1952
2072
|
this.options = {
|
|
1953
2073
|
...options
|
|
1954
2074
|
};
|
|
@@ -2004,25 +2124,26 @@ var GMStorageEngine = class extends DataStoreEngine {
|
|
|
2004
2124
|
|
|
2005
2125
|
// lib/Mixins.ts
|
|
2006
2126
|
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
2127
|
/**
|
|
2016
2128
|
* Creates a new Mixins instance.
|
|
2017
2129
|
* @param config Configuration object to customize the behavior.
|
|
2018
2130
|
*/
|
|
2019
2131
|
constructor(config = {}) {
|
|
2132
|
+
/** List of all registered mixins */
|
|
2133
|
+
__publicField(this, "mixins", []);
|
|
2134
|
+
/** Default configuration object for mixins */
|
|
2135
|
+
__publicField(this, "defaultMixinCfg");
|
|
2136
|
+
/** Whether the priorities should auto-increment if not specified */
|
|
2137
|
+
__publicField(this, "autoIncPrioEnabled");
|
|
2138
|
+
/** The current auto-increment priority counter */
|
|
2139
|
+
__publicField(this, "autoIncPrioCounter", /* @__PURE__ */ new Map());
|
|
2140
|
+
var _a, _b, _c;
|
|
2020
2141
|
this.defaultMixinCfg = pureObj({
|
|
2021
|
-
priority: config.defaultPriority
|
|
2022
|
-
stopPropagation: config.defaultStopPropagation
|
|
2142
|
+
priority: (_a = config.defaultPriority) != null ? _a : 0,
|
|
2143
|
+
stopPropagation: (_b = config.defaultStopPropagation) != null ? _b : false,
|
|
2023
2144
|
signal: config.defaultSignal
|
|
2024
2145
|
});
|
|
2025
|
-
this.autoIncPrioEnabled = config.autoIncrementPriority
|
|
2146
|
+
this.autoIncPrioEnabled = (_c = config.autoIncrementPriority) != null ? _c : false;
|
|
2026
2147
|
}
|
|
2027
2148
|
//#region public
|
|
2028
2149
|
/**
|
|
@@ -2087,10 +2208,11 @@ var Mixins = class {
|
|
|
2087
2208
|
//#region protected
|
|
2088
2209
|
/** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
|
|
2089
2210
|
calcPriority(mixinKey, config) {
|
|
2211
|
+
var _a;
|
|
2090
2212
|
if (config.priority !== void 0)
|
|
2091
2213
|
return void 0;
|
|
2092
2214
|
if (!this.autoIncPrioEnabled)
|
|
2093
|
-
return config.priority
|
|
2215
|
+
return (_a = config.priority) != null ? _a : this.defaultMixinCfg.priority;
|
|
2094
2216
|
if (!this.autoIncPrioCounter.has(mixinKey))
|
|
2095
2217
|
this.autoIncPrioCounter.set(mixinKey, this.defaultMixinCfg.priority);
|
|
2096
2218
|
let prio = this.autoIncPrioCounter.get(mixinKey);
|
|
@@ -2111,13 +2233,13 @@ var Mixins = class {
|
|
|
2111
2233
|
|
|
2112
2234
|
// lib/SelectorObserver.ts
|
|
2113
2235
|
var SelectorObserver = class {
|
|
2114
|
-
enabled = false;
|
|
2115
|
-
baseElement;
|
|
2116
|
-
observer;
|
|
2117
|
-
observerOptions;
|
|
2118
|
-
customOptions;
|
|
2119
|
-
listenerMap;
|
|
2120
2236
|
constructor(baseElement, options = {}) {
|
|
2237
|
+
__publicField(this, "enabled", false);
|
|
2238
|
+
__publicField(this, "baseElement");
|
|
2239
|
+
__publicField(this, "observer");
|
|
2240
|
+
__publicField(this, "observerOptions");
|
|
2241
|
+
__publicField(this, "customOptions");
|
|
2242
|
+
__publicField(this, "listenerMap");
|
|
2121
2243
|
this.baseElement = baseElement;
|
|
2122
2244
|
this.listenerMap = /* @__PURE__ */ new Map();
|
|
2123
2245
|
const {
|
|
@@ -2133,10 +2255,10 @@ var SelectorObserver = class {
|
|
|
2133
2255
|
...observerOptions
|
|
2134
2256
|
};
|
|
2135
2257
|
this.customOptions = {
|
|
2136
|
-
defaultDebounce: defaultDebounce
|
|
2137
|
-
defaultDebounceType: defaultDebounceType
|
|
2138
|
-
disableOnNoListeners: disableOnNoListeners
|
|
2139
|
-
enableOnAddListener: enableOnAddListener
|
|
2258
|
+
defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
|
|
2259
|
+
defaultDebounceType: defaultDebounceType != null ? defaultDebounceType : "immediate",
|
|
2260
|
+
disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
|
|
2261
|
+
enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
|
|
2140
2262
|
};
|
|
2141
2263
|
if (typeof this.customOptions.checkInterval !== "number") {
|
|
2142
2264
|
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
@@ -2287,9 +2409,9 @@ var valTransforms = [];
|
|
|
2287
2409
|
var fallbackLang;
|
|
2288
2410
|
function translate(language, key, ...trArgs) {
|
|
2289
2411
|
if (typeof language !== "string")
|
|
2290
|
-
language = fallbackLang
|
|
2412
|
+
language = fallbackLang != null ? fallbackLang : "";
|
|
2291
2413
|
const trObj = trans[language];
|
|
2292
|
-
if (typeof language !== "string" ||
|
|
2414
|
+
if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
|
|
2293
2415
|
return fallbackLang && language !== fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
2294
2416
|
const transformTrVal = (trKey, trValue) => {
|
|
2295
2417
|
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
@@ -2341,15 +2463,18 @@ function trFor(language, key, ...args) {
|
|
|
2341
2463
|
function useTr(language) {
|
|
2342
2464
|
return (key, ...args) => translate(language, key, ...args);
|
|
2343
2465
|
}
|
|
2344
|
-
function hasKey(language = fallbackLang
|
|
2466
|
+
function hasKey(language = fallbackLang != null ? fallbackLang : "", key) {
|
|
2345
2467
|
return tr.for(language, key) !== key;
|
|
2346
2468
|
}
|
|
2347
2469
|
function addTranslations(language, translations) {
|
|
2348
2470
|
trans[language] = JSON.parse(JSON.stringify(translations));
|
|
2349
2471
|
}
|
|
2350
|
-
function getTranslations(language = fallbackLang
|
|
2472
|
+
function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
|
|
2351
2473
|
return trans[language];
|
|
2352
2474
|
}
|
|
2475
|
+
function getAllTranslations(asCopy = true) {
|
|
2476
|
+
return asCopy ? JSON.parse(JSON.stringify(trans)) : trans;
|
|
2477
|
+
}
|
|
2353
2478
|
var deleteTranslations = (language) => {
|
|
2354
2479
|
if (language in trans) {
|
|
2355
2480
|
delete trans[language];
|
|
@@ -2380,51 +2505,55 @@ function deleteTransform(patternOrFn) {
|
|
|
2380
2505
|
}
|
|
2381
2506
|
return false;
|
|
2382
2507
|
}
|
|
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;
|
|
2508
|
+
var commonKeyedTransform = ({ matches, trArgs, trValue }, patternRegex, quickMatchPattern) => {
|
|
2509
|
+
let str = String(trValue);
|
|
2510
|
+
const someMatchKeyInArgs = (obj) => matches.some((match) => match[1] !== void 0 && match[1] in obj);
|
|
2511
|
+
const namedMapping = () => {
|
|
2512
|
+
if (!str.includes(quickMatchPattern) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !someMatchKeyInArgs(trArgs[0]))
|
|
2513
|
+
return;
|
|
2514
|
+
for (const match of matches) {
|
|
2515
|
+
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2516
|
+
if (typeof repl !== "undefined")
|
|
2517
|
+
str = str.replace(match[0], String(repl));
|
|
2413
2518
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2519
|
+
};
|
|
2520
|
+
const positionalMapping = () => {
|
|
2521
|
+
if (!patternRegex.test(str) || typeof trArgs[0] === "undefined")
|
|
2522
|
+
return;
|
|
2523
|
+
let matchNum = -1;
|
|
2524
|
+
for (const match of matches) {
|
|
2525
|
+
matchNum++;
|
|
2526
|
+
if (typeof trArgs[matchNum] !== "undefined")
|
|
2527
|
+
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2528
|
+
}
|
|
2529
|
+
};
|
|
2530
|
+
let notStringifiable = false;
|
|
2531
|
+
try {
|
|
2532
|
+
void `${trArgs[0]}`;
|
|
2533
|
+
} catch (e) {
|
|
2534
|
+
notStringifiable = true;
|
|
2420
2535
|
}
|
|
2536
|
+
const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && (notStringifiable || String(trArgs[0]).startsWith("[object"));
|
|
2537
|
+
if (isArgsObject && someMatchKeyInArgs(trArgs[0]))
|
|
2538
|
+
namedMapping();
|
|
2539
|
+
else
|
|
2540
|
+
positionalMapping();
|
|
2541
|
+
return str;
|
|
2542
|
+
};
|
|
2543
|
+
var templateLiteralTransform = [
|
|
2544
|
+
/\$\{([a-zA-Z0-9$_-]+)\}/gm,
|
|
2545
|
+
(tfProps) => commonKeyedTransform(tfProps, /\$\{.+\}/m, "${")
|
|
2546
|
+
];
|
|
2547
|
+
var i18nTransform = [
|
|
2548
|
+
/\{\{([a-zA-Z0-9$_-]+)\}\}/gm,
|
|
2549
|
+
(tfProps) => commonKeyedTransform(tfProps, /\{\{.+\}\}/m, "{{")
|
|
2421
2550
|
];
|
|
2422
2551
|
var percentTransform = [
|
|
2423
2552
|
/%(\d+)/gm,
|
|
2424
2553
|
({ matches, trArgs, trValue }) => {
|
|
2425
2554
|
let str = String(trValue);
|
|
2426
2555
|
for (const match of matches) {
|
|
2427
|
-
const repl =
|
|
2556
|
+
const repl = trArgs == null ? void 0 : trArgs[Number(match[1]) - 1];
|
|
2428
2557
|
if (typeof repl !== "undefined")
|
|
2429
2558
|
str = str.replace(match[0], String(repl));
|
|
2430
2559
|
}
|
|
@@ -2434,16 +2563,84 @@ var percentTransform = [
|
|
|
2434
2563
|
var tr = {
|
|
2435
2564
|
for: (...params) => trFor(...params),
|
|
2436
2565
|
use: (...params) => useTr(...params),
|
|
2437
|
-
hasKey: (language = fallbackLang
|
|
2566
|
+
hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
|
|
2438
2567
|
addTranslations,
|
|
2439
2568
|
getTranslations,
|
|
2569
|
+
getAllTranslations,
|
|
2440
2570
|
deleteTranslations,
|
|
2441
2571
|
setFallbackLanguage,
|
|
2442
2572
|
getFallbackLanguage,
|
|
2443
2573
|
addTransform,
|
|
2444
2574
|
deleteTransform,
|
|
2575
|
+
/** Collection of predefined transform functions that can be added via {@linkcode tr.addTransform()} */
|
|
2445
2576
|
transforms: {
|
|
2577
|
+
/**
|
|
2578
|
+
* This transform will replace placeholders matching `${key}` with the value of the passed argument(s).
|
|
2579
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2580
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2581
|
+
* - 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.
|
|
2582
|
+
*
|
|
2583
|
+
* @example ```ts
|
|
2584
|
+
* tr.addTranslations("en", {
|
|
2585
|
+
* "greeting": "Hello, ${user}!\nYou have ${notifs} notifications.",
|
|
2586
|
+
* });
|
|
2587
|
+
* tr.addTransform(tr.transforms.templateLiteral);
|
|
2588
|
+
*
|
|
2589
|
+
* const t = tr.use("en");
|
|
2590
|
+
*
|
|
2591
|
+
* // both calls return the same result:
|
|
2592
|
+
* t("greeting", { user: "John", notifs: 5 }); // "Hello, John!\nYou have 5 notifications."
|
|
2593
|
+
* t("greeting", "John", 5); // "Hello, John!\nYou have 5 notifications."
|
|
2594
|
+
*
|
|
2595
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2596
|
+
* t("greeting", { user: "John" }); // "Hello, John!\nYou have ${notifs} notifications."
|
|
2597
|
+
* ```
|
|
2598
|
+
*/
|
|
2446
2599
|
templateLiteral: templateLiteralTransform,
|
|
2600
|
+
/**
|
|
2601
|
+
* This transform will replace placeholders matching `{{key}}` with the value of the passed argument(s).
|
|
2602
|
+
* This format is commonly used in i18n libraries. Note that advanced syntax is not supported, only simple key replacement.
|
|
2603
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2604
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2605
|
+
* - 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.
|
|
2606
|
+
*
|
|
2607
|
+
* @example ```ts
|
|
2608
|
+
* tr.addTranslations("en", {
|
|
2609
|
+
* "greeting": "Hello, {{user}}!\nYou have {{notifs}} notifications.",
|
|
2610
|
+
* });
|
|
2611
|
+
* tr.addTransform(tr.transforms.i18n);
|
|
2612
|
+
*
|
|
2613
|
+
* const t = tr.use("en");
|
|
2614
|
+
*
|
|
2615
|
+
* // both calls return the same result:
|
|
2616
|
+
* t("greeting", { user: "Alice", notifs: 5 }); // "Hello, Alice!\nYou have 5 notifications."
|
|
2617
|
+
* t("greeting", "Alice", 5); // "Hello, Alice!\nYou have 5 notifications."
|
|
2618
|
+
*
|
|
2619
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2620
|
+
* t("greeting", { user: "Alice" }); // "Hello, Alice!\nYou have {{notifs}} notifications."
|
|
2621
|
+
* ```
|
|
2622
|
+
*/
|
|
2623
|
+
i18n: i18nTransform,
|
|
2624
|
+
/**
|
|
2625
|
+
* This transform will replace `%n` placeholders with the value of the passed arguments.
|
|
2626
|
+
* 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.
|
|
2627
|
+
* Objects will be stringified via `String()` before being inserted.
|
|
2628
|
+
*
|
|
2629
|
+
* @example ```ts
|
|
2630
|
+
* tr.addTranslations("en", {
|
|
2631
|
+
* "greeting": "Hello, %1!\nYou have %2 notifications.",
|
|
2632
|
+
* });
|
|
2633
|
+
* tr.addTransform(tr.transforms.percent);
|
|
2634
|
+
*
|
|
2635
|
+
* const t = tr.use("en");
|
|
2636
|
+
*
|
|
2637
|
+
* // arguments are inserted in the order they're passed:
|
|
2638
|
+
* t("greeting", "Bob", 5); // "Hello, Bob!\nYou have 5 notifications."
|
|
2639
|
+
*
|
|
2640
|
+
* // when a value isn't found, the placeholder will be left as-is:
|
|
2641
|
+
* t("greeting", "Bob"); // "Hello, Bob!\nYou have %2 notifications."
|
|
2642
|
+
* ```
|
|
2643
|
+
*/
|
|
2447
2644
|
percent: percentTransform
|
|
2448
2645
|
}
|
|
2449
2646
|
};
|
|
@@ -2480,6 +2677,7 @@ export {
|
|
|
2480
2677
|
consumeGen,
|
|
2481
2678
|
consumeStringGen,
|
|
2482
2679
|
createProgressBar,
|
|
2680
|
+
createRecurringTask,
|
|
2483
2681
|
currentDialogId,
|
|
2484
2682
|
darkenColor,
|
|
2485
2683
|
debounce,
|
|
@@ -2528,6 +2726,6 @@ export {
|
|
|
2528
2726
|
takeRandomItemIndex,
|
|
2529
2727
|
tr,
|
|
2530
2728
|
truncStr,
|
|
2531
|
-
valsWithin
|
|
2729
|
+
valsWithin,
|
|
2730
|
+
versions
|
|
2532
2731
|
};
|
|
2533
|
-
//# sourceMappingURL=UserUtils.mjs.map
|