@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.umd.js
CHANGED
|
@@ -131,6 +131,7 @@ __export(lib_exports, {
|
|
|
131
131
|
consumeGen: () => consumeGen,
|
|
132
132
|
consumeStringGen: () => consumeStringGen,
|
|
133
133
|
createProgressBar: () => createProgressBar,
|
|
134
|
+
createRecurringTask: () => createRecurringTask,
|
|
134
135
|
currentDialogId: () => currentDialogId,
|
|
135
136
|
darkenColor: () => darkenColor,
|
|
136
137
|
debounce: () => debounce,
|
|
@@ -179,11 +180,12 @@ __export(lib_exports, {
|
|
|
179
180
|
takeRandomItemIndex: () => takeRandomItemIndex,
|
|
180
181
|
tr: () => tr,
|
|
181
182
|
truncStr: () => truncStr,
|
|
182
|
-
valsWithin: () => valsWithin
|
|
183
|
+
valsWithin: () => valsWithin,
|
|
184
|
+
versions: () => versions
|
|
183
185
|
});
|
|
184
186
|
module.exports = __toCommonJS(lib_exports);
|
|
185
187
|
|
|
186
|
-
// node_modules/.pnpm/@sv443-network+coreutils@3.0
|
|
188
|
+
// node_modules/.pnpm/@sv443-network+coreutils@3.4.0/node_modules/@sv443-network/coreutils/dist/CoreUtils.mjs
|
|
187
189
|
function bitSetHas(bitSet, checkVal) {
|
|
188
190
|
return (bitSet & checkVal) === checkVal;
|
|
189
191
|
}
|
|
@@ -555,12 +557,46 @@ function getCallStack(asArray, lines = Infinity) {
|
|
|
555
557
|
if (typeof lines !== "number" || isNaN(lines) || lines < 0)
|
|
556
558
|
throw new TypeError("lines parameter must be a non-negative number");
|
|
557
559
|
try {
|
|
558
|
-
throw new
|
|
560
|
+
throw new CustomError("GetCallStack", "Capturing a stack trace with CoreUtils.getCallStack(). If you see this anywhere, you can safely ignore it.");
|
|
559
561
|
} catch (err) {
|
|
560
562
|
const stack = ((_a = err.stack) != null ? _a : "").split("\n").map((line) => line.trim()).slice(2, lines + 2);
|
|
561
563
|
return asArray !== false ? stack : stack.join("\n");
|
|
562
564
|
}
|
|
563
565
|
}
|
|
566
|
+
function createRecurringTask(options) {
|
|
567
|
+
var _a;
|
|
568
|
+
let iterations = 0;
|
|
569
|
+
let aborted = false;
|
|
570
|
+
(_a = options.signal) == null ? void 0 : _a.addEventListener("abort", () => {
|
|
571
|
+
aborted = true;
|
|
572
|
+
}, { once: true });
|
|
573
|
+
const runRecurringTask = (initial = false) => __async(null, null, function* () {
|
|
574
|
+
var _a3, _b;
|
|
575
|
+
var _a2;
|
|
576
|
+
if (aborted)
|
|
577
|
+
return;
|
|
578
|
+
try {
|
|
579
|
+
if (((_a3 = options.immediate) != null ? _a3 : true) || !initial) {
|
|
580
|
+
iterations++;
|
|
581
|
+
if ((_b = yield (_a2 = options.condition) == null ? void 0 : _a2.call(options, iterations - 1)) != null ? _b : true) {
|
|
582
|
+
const val = yield options.task(iterations - 1);
|
|
583
|
+
if (options.onSuccess)
|
|
584
|
+
yield options.onSuccess(val, iterations - 1);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
} catch (err) {
|
|
588
|
+
if (options.onError)
|
|
589
|
+
yield options.onError(err, iterations - 1);
|
|
590
|
+
if (options.abortOnError)
|
|
591
|
+
aborted = true;
|
|
592
|
+
if (!options.onError && !options.abortOnError)
|
|
593
|
+
throw err;
|
|
594
|
+
}
|
|
595
|
+
if (!aborted && (typeof options.maxIterations !== "number" || iterations < options.maxIterations))
|
|
596
|
+
setTimeout(runRecurringTask, options.timeout);
|
|
597
|
+
});
|
|
598
|
+
return runRecurringTask(true);
|
|
599
|
+
}
|
|
564
600
|
function autoPlural(term, num, pluralType = "auto") {
|
|
565
601
|
if (typeof num !== "number") {
|
|
566
602
|
if ("length" in num)
|
|
@@ -649,8 +685,202 @@ function truncStr(input, length, endStr = "...") {
|
|
|
649
685
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
650
686
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
651
687
|
}
|
|
688
|
+
var createNanoEvents = () => ({
|
|
689
|
+
emit(event, ...args) {
|
|
690
|
+
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
691
|
+
callbacks[i](...args);
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
events: {},
|
|
695
|
+
on(event, cb) {
|
|
696
|
+
var _a;
|
|
697
|
+
;
|
|
698
|
+
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
699
|
+
return () => {
|
|
700
|
+
var _a2;
|
|
701
|
+
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
var NanoEmitter = class {
|
|
706
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
707
|
+
constructor(options = {}) {
|
|
708
|
+
__publicField(this, "events", createNanoEvents());
|
|
709
|
+
__publicField(this, "eventUnsubscribes", []);
|
|
710
|
+
__publicField(this, "emitterOptions");
|
|
711
|
+
this.emitterOptions = __spreadValues({
|
|
712
|
+
publicEmit: false
|
|
713
|
+
}, options);
|
|
714
|
+
}
|
|
715
|
+
//#region on
|
|
716
|
+
/**
|
|
717
|
+
* Subscribes to an event and calls the callback when it's emitted.
|
|
718
|
+
* @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 "_")
|
|
719
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
720
|
+
* @example ```ts
|
|
721
|
+
* const emitter = new NanoEmitter<{
|
|
722
|
+
* foo: (bar: string) => void;
|
|
723
|
+
* }>({
|
|
724
|
+
* publicEmit: true,
|
|
725
|
+
* });
|
|
726
|
+
*
|
|
727
|
+
* let i = 0;
|
|
728
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
729
|
+
* // unsubscribe after 10 events:
|
|
730
|
+
* if(++i === 10) unsub();
|
|
731
|
+
* console.log(bar);
|
|
732
|
+
* });
|
|
733
|
+
*
|
|
734
|
+
* emitter.emit("foo", "bar");
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
on(event, cb) {
|
|
738
|
+
let unsub;
|
|
739
|
+
const unsubProxy = () => {
|
|
740
|
+
if (!unsub)
|
|
741
|
+
return;
|
|
742
|
+
unsub();
|
|
743
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
744
|
+
};
|
|
745
|
+
unsub = this.events.on(event, cb);
|
|
746
|
+
this.eventUnsubscribes.push(unsub);
|
|
747
|
+
return unsubProxy;
|
|
748
|
+
}
|
|
749
|
+
//#region once
|
|
750
|
+
/**
|
|
751
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
752
|
+
* @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 "_")
|
|
753
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
754
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
755
|
+
* @example ```ts
|
|
756
|
+
* const emitter = new NanoEmitter<{
|
|
757
|
+
* foo: (bar: string) => void;
|
|
758
|
+
* }>();
|
|
759
|
+
*
|
|
760
|
+
* // Promise syntax:
|
|
761
|
+
* const [bar] = await emitter.once("foo");
|
|
762
|
+
* console.log(bar);
|
|
763
|
+
*
|
|
764
|
+
* // Callback syntax:
|
|
765
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
once(event, cb) {
|
|
769
|
+
return new Promise((resolve) => {
|
|
770
|
+
let unsub;
|
|
771
|
+
const onceProxy = ((...args) => {
|
|
772
|
+
cb == null ? void 0 : cb(...args);
|
|
773
|
+
unsub == null ? void 0 : unsub();
|
|
774
|
+
resolve(args);
|
|
775
|
+
});
|
|
776
|
+
unsub = this.events.on(event, onceProxy);
|
|
777
|
+
this.eventUnsubscribes.push(unsub);
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
//#region onMulti
|
|
781
|
+
/**
|
|
782
|
+
* 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.
|
|
783
|
+
* @param options An object or array of objects with the following properties:
|
|
784
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
785
|
+
*
|
|
786
|
+
* 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.
|
|
787
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
788
|
+
*
|
|
789
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
790
|
+
* 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.
|
|
791
|
+
* 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.
|
|
792
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
793
|
+
*
|
|
794
|
+
* @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.
|
|
795
|
+
*/
|
|
796
|
+
onMulti(options) {
|
|
797
|
+
const allUnsubs = [];
|
|
798
|
+
const unsubAll = () => {
|
|
799
|
+
for (const unsub of allUnsubs)
|
|
800
|
+
unsub();
|
|
801
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
802
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
803
|
+
};
|
|
804
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
805
|
+
const optsWithDefaults = __spreadValues({
|
|
806
|
+
allOf: [],
|
|
807
|
+
oneOf: [],
|
|
808
|
+
once: false
|
|
809
|
+
}, opts);
|
|
810
|
+
const {
|
|
811
|
+
oneOf,
|
|
812
|
+
allOf,
|
|
813
|
+
once,
|
|
814
|
+
signal,
|
|
815
|
+
callback
|
|
816
|
+
} = optsWithDefaults;
|
|
817
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
818
|
+
return unsubAll;
|
|
819
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
820
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
821
|
+
const curEvtUnsubs = [];
|
|
822
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
823
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
824
|
+
return;
|
|
825
|
+
for (const unsub of curEvtUnsubs)
|
|
826
|
+
unsub();
|
|
827
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
828
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
829
|
+
};
|
|
830
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
831
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
832
|
+
for (const event of oneOf) {
|
|
833
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
834
|
+
checkUnsubAllEvt();
|
|
835
|
+
if (allOfConditionMet()) {
|
|
836
|
+
callback(event, ...args);
|
|
837
|
+
if (once)
|
|
838
|
+
checkUnsubAllEvt(true);
|
|
839
|
+
}
|
|
840
|
+
}));
|
|
841
|
+
curEvtUnsubs.push(unsub);
|
|
842
|
+
}
|
|
843
|
+
for (const event of allOf) {
|
|
844
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
845
|
+
checkUnsubAllEvt();
|
|
846
|
+
allOfEmitted.add(event);
|
|
847
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
848
|
+
callback(event, ...args);
|
|
849
|
+
if (once)
|
|
850
|
+
checkUnsubAllEvt(true);
|
|
851
|
+
}
|
|
852
|
+
}));
|
|
853
|
+
curEvtUnsubs.push(unsub);
|
|
854
|
+
}
|
|
855
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
856
|
+
}
|
|
857
|
+
return unsubAll;
|
|
858
|
+
}
|
|
859
|
+
//#region emit
|
|
860
|
+
/**
|
|
861
|
+
* Emits an event on this instance.
|
|
862
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
863
|
+
* @param event The event to emit
|
|
864
|
+
* @param args The arguments to pass to the event listeners
|
|
865
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
866
|
+
*/
|
|
867
|
+
emit(event, ...args) {
|
|
868
|
+
if (this.emitterOptions.publicEmit) {
|
|
869
|
+
this.events.emit(event, ...args);
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
return false;
|
|
873
|
+
}
|
|
874
|
+
//#region unsubscribeAll
|
|
875
|
+
/** Unsubscribes all event listeners from this instance */
|
|
876
|
+
unsubscribeAll() {
|
|
877
|
+
for (const unsub of this.eventUnsubscribes)
|
|
878
|
+
unsub();
|
|
879
|
+
this.eventUnsubscribes = [];
|
|
880
|
+
}
|
|
881
|
+
};
|
|
652
882
|
var dsFmtVer = 1;
|
|
653
|
-
var DataStore = class {
|
|
883
|
+
var DataStore = class extends NanoEmitter {
|
|
654
884
|
//#region constructor
|
|
655
885
|
/**
|
|
656
886
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -662,6 +892,8 @@ var DataStore = class {
|
|
|
662
892
|
* @param opts The options for this DataStore instance
|
|
663
893
|
*/
|
|
664
894
|
constructor(opts) {
|
|
895
|
+
var _a, _b, _c;
|
|
896
|
+
super(opts.nanoEmitterOptions);
|
|
665
897
|
__publicField(this, "id");
|
|
666
898
|
__publicField(this, "formatVersion");
|
|
667
899
|
__publicField(this, "defaultData");
|
|
@@ -670,6 +902,7 @@ var DataStore = class {
|
|
|
670
902
|
__publicField(this, "compressionFormat", "deflate-raw");
|
|
671
903
|
__publicField(this, "memoryCache");
|
|
672
904
|
__publicField(this, "engine");
|
|
905
|
+
__publicField(this, "keyPrefix");
|
|
673
906
|
__publicField(this, "options");
|
|
674
907
|
/**
|
|
675
908
|
* Whether all first-init checks should be done.
|
|
@@ -681,7 +914,6 @@ var DataStore = class {
|
|
|
681
914
|
__publicField(this, "cachedData");
|
|
682
915
|
__publicField(this, "migrations");
|
|
683
916
|
__publicField(this, "migrateIds", []);
|
|
684
|
-
var _a, _b;
|
|
685
917
|
this.id = opts.id;
|
|
686
918
|
this.formatVersion = opts.formatVersion;
|
|
687
919
|
this.defaultData = opts.defaultData;
|
|
@@ -691,11 +923,12 @@ var DataStore = class {
|
|
|
691
923
|
if (opts.migrateIds)
|
|
692
924
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
693
925
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
926
|
+
this.keyPrefix = (_b = opts.keyPrefix) != null ? _b : "__ds-";
|
|
694
927
|
this.options = opts;
|
|
695
928
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
696
929
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
697
930
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
698
|
-
this.compressionFormat = (
|
|
931
|
+
this.compressionFormat = (_c = opts.encodeData[0]) != null ? _c : null;
|
|
699
932
|
} else if (opts.compressionFormat === null) {
|
|
700
933
|
this.encodeData = void 0;
|
|
701
934
|
this.decodeData = void 0;
|
|
@@ -738,13 +971,13 @@ var DataStore = class {
|
|
|
738
971
|
promises.push(this.engine.setValue(newKey, value));
|
|
739
972
|
promises.push(this.engine.deleteValue(oldKey));
|
|
740
973
|
};
|
|
741
|
-
migrateFmt(`_uucfg-${this.id}`,
|
|
974
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
742
975
|
if (!isNaN(oldVer))
|
|
743
|
-
migrateFmt(`_uucfgver-${this.id}`,
|
|
976
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
744
977
|
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
745
|
-
migrateFmt(`_uucfgenc-${this.id}`,
|
|
978
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? (_a = this.compressionFormat) != null ? _a : null : null);
|
|
746
979
|
else {
|
|
747
|
-
promises.push(this.engine.setValue(
|
|
980
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
748
981
|
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
749
982
|
}
|
|
750
983
|
yield Promise.allSettled(promises);
|
|
@@ -756,31 +989,27 @@ var DataStore = class {
|
|
|
756
989
|
yield this.migrateId(this.migrateIds);
|
|
757
990
|
this.migrateIds = [];
|
|
758
991
|
}
|
|
759
|
-
const storedDataRaw = yield this.engine.getValue(
|
|
760
|
-
|
|
761
|
-
if (typeof storedDataRaw !== "string") {
|
|
762
|
-
yield this.saveDefaultData();
|
|
763
|
-
|
|
992
|
+
const storedDataRaw = yield this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
993
|
+
const storedFmtVer = Number(yield this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
994
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
995
|
+
yield this.saveDefaultData(false);
|
|
996
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
997
|
+
this.events.emit("loadData", data);
|
|
998
|
+
return data;
|
|
764
999
|
}
|
|
765
1000
|
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
766
|
-
const encodingFmt = String(yield this.engine.getValue(
|
|
1001
|
+
const encodingFmt = String(yield this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
767
1002
|
const isEncoded = encodingFmt !== "null" && encodingFmt !== "false" && encodingFmt !== "0" && encodingFmt !== "" && encodingFmt !== null;
|
|
768
|
-
let
|
|
769
|
-
if (isNaN(storedFmtVer)) {
|
|
770
|
-
yield this.engine.setValue(`__ds-${this.id}-ver`, storedFmtVer = this.formatVersion);
|
|
771
|
-
saveData = true;
|
|
772
|
-
}
|
|
773
|
-
let parsed = yield this.engine.deserializeData(storedData, isEncoded);
|
|
1003
|
+
let parsed = typeof storedData === "string" ? yield this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
774
1004
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
775
1005
|
parsed = yield this.runMigrations(parsed, storedFmtVer);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
return this.cachedData = this.engine.deepCopy(parsed);
|
|
780
|
-
else
|
|
781
|
-
return this.engine.deepCopy(parsed);
|
|
1006
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
1007
|
+
this.events.emit("loadData", result);
|
|
1008
|
+
return result;
|
|
782
1009
|
} catch (err) {
|
|
1010
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
783
1011
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
1012
|
+
this.events.emit("error", error);
|
|
784
1013
|
yield this.saveDefaultData();
|
|
785
1014
|
return this.defaultData;
|
|
786
1015
|
}
|
|
@@ -800,28 +1029,48 @@ var DataStore = class {
|
|
|
800
1029
|
//#region setData
|
|
801
1030
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
802
1031
|
setData(data) {
|
|
803
|
-
|
|
1032
|
+
const dataCopy = this.engine.deepCopy(data);
|
|
1033
|
+
if (this.memoryCache) {
|
|
804
1034
|
this.cachedData = data;
|
|
1035
|
+
this.events.emit("updateDataSync", dataCopy);
|
|
1036
|
+
}
|
|
805
1037
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
806
|
-
yield Promise.allSettled([
|
|
807
|
-
this.engine.setValue(
|
|
808
|
-
this.engine.setValue(
|
|
809
|
-
this.engine.setValue(
|
|
1038
|
+
const results = yield Promise.allSettled([
|
|
1039
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(data, this.encodingEnabled())),
|
|
1040
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1041
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
810
1042
|
]);
|
|
1043
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
1044
|
+
this.events.emit("updateData", dataCopy);
|
|
1045
|
+
else {
|
|
1046
|
+
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1047
|
+
console.error(error);
|
|
1048
|
+
this.events.emit("error", error);
|
|
1049
|
+
}
|
|
811
1050
|
resolve();
|
|
812
1051
|
}));
|
|
813
1052
|
}
|
|
814
1053
|
//#region saveDefaultData
|
|
815
|
-
/**
|
|
816
|
-
|
|
1054
|
+
/**
|
|
1055
|
+
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
1056
|
+
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
1057
|
+
*/
|
|
1058
|
+
saveDefaultData(emitEvent = true) {
|
|
817
1059
|
return __async(this, null, function* () {
|
|
818
1060
|
if (this.memoryCache)
|
|
819
1061
|
this.cachedData = this.defaultData;
|
|
820
|
-
yield Promise.allSettled([
|
|
821
|
-
this.engine.setValue(
|
|
822
|
-
this.engine.setValue(
|
|
823
|
-
this.engine.setValue(
|
|
1062
|
+
const results = yield Promise.allSettled([
|
|
1063
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
1064
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1065
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
824
1066
|
]);
|
|
1067
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
1068
|
+
emitEvent && this.events.emit("setDefaultData", this.defaultData);
|
|
1069
|
+
else {
|
|
1070
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1071
|
+
console.error(error);
|
|
1072
|
+
this.events.emit("error", error);
|
|
1073
|
+
}
|
|
825
1074
|
});
|
|
826
1075
|
}
|
|
827
1076
|
//#region deleteData
|
|
@@ -834,11 +1083,12 @@ var DataStore = class {
|
|
|
834
1083
|
return __async(this, null, function* () {
|
|
835
1084
|
var _a, _b;
|
|
836
1085
|
yield Promise.allSettled([
|
|
837
|
-
this.engine.deleteValue(
|
|
838
|
-
this.engine.deleteValue(
|
|
839
|
-
this.engine.deleteValue(
|
|
1086
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
1087
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
1088
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
840
1089
|
]);
|
|
841
1090
|
yield (_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a);
|
|
1091
|
+
this.events.emit("deleteData");
|
|
842
1092
|
});
|
|
843
1093
|
}
|
|
844
1094
|
//#region encodingEnabled
|
|
@@ -861,30 +1111,35 @@ var DataStore = class {
|
|
|
861
1111
|
let newData = oldData;
|
|
862
1112
|
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
863
1113
|
let lastFmtVer = oldFmtVer;
|
|
864
|
-
for (
|
|
1114
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
1115
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
865
1116
|
const ver = Number(fmtVer);
|
|
866
1117
|
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
867
1118
|
try {
|
|
868
1119
|
const migRes = migrationFunc(newData);
|
|
869
1120
|
newData = migRes instanceof Promise ? yield migRes : migRes;
|
|
870
1121
|
lastFmtVer = oldFmtVer = ver;
|
|
1122
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
1123
|
+
this.events.emit("migrateData", ver, newData, isFinal);
|
|
871
1124
|
} catch (err) {
|
|
1125
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
1126
|
+
this.events.emit("migrationError", ver, migError);
|
|
1127
|
+
this.events.emit("error", migError);
|
|
872
1128
|
if (!resetOnError)
|
|
873
|
-
throw
|
|
1129
|
+
throw migError;
|
|
874
1130
|
yield this.saveDefaultData();
|
|
875
1131
|
return this.engine.deepCopy(this.defaultData);
|
|
876
1132
|
}
|
|
877
1133
|
}
|
|
878
1134
|
}
|
|
879
1135
|
yield Promise.allSettled([
|
|
880
|
-
this.engine.setValue(
|
|
881
|
-
this.engine.setValue(
|
|
882
|
-
this.engine.setValue(
|
|
1136
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(newData, this.encodingEnabled())),
|
|
1137
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
1138
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
883
1139
|
]);
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
return this.engine.deepCopy(newData);
|
|
1140
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
1141
|
+
this.events.emit("updateData", result);
|
|
1142
|
+
return result;
|
|
888
1143
|
});
|
|
889
1144
|
}
|
|
890
1145
|
//#region migrateId
|
|
@@ -898,9 +1153,9 @@ var DataStore = class {
|
|
|
898
1153
|
yield Promise.all(ids.map((id) => __async(this, null, function* () {
|
|
899
1154
|
const [data, fmtVer, isEncoded] = yield (() => __async(this, null, function* () {
|
|
900
1155
|
const [d, f, e] = yield Promise.all([
|
|
901
|
-
this.engine.getValue(
|
|
902
|
-
this.engine.getValue(
|
|
903
|
-
this.engine.getValue(
|
|
1156
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
1157
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
1158
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
904
1159
|
]);
|
|
905
1160
|
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
906
1161
|
}))();
|
|
@@ -908,13 +1163,14 @@ var DataStore = class {
|
|
|
908
1163
|
return;
|
|
909
1164
|
const parsed = yield this.engine.deserializeData(data, isEncoded);
|
|
910
1165
|
yield Promise.allSettled([
|
|
911
|
-
this.engine.setValue(
|
|
912
|
-
this.engine.setValue(
|
|
913
|
-
this.engine.setValue(
|
|
914
|
-
this.engine.deleteValue(
|
|
915
|
-
this.engine.deleteValue(
|
|
916
|
-
this.engine.deleteValue(
|
|
1166
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
1167
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
1168
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
1169
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
1170
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1171
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
917
1172
|
]);
|
|
1173
|
+
this.events.emit("migrateId", id, this.id);
|
|
918
1174
|
})));
|
|
919
1175
|
});
|
|
920
1176
|
}
|
|
@@ -1046,7 +1302,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1046
1302
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1047
1303
|
if (!fs)
|
|
1048
1304
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1049
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1305
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1050
1306
|
const data = yield fs.readFile(path, "utf-8");
|
|
1051
1307
|
return data ? JSON.parse((_a2 = yield (_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;
|
|
1052
1308
|
} catch (e) {
|
|
@@ -1065,7 +1321,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1065
1321
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1066
1322
|
if (!fs)
|
|
1067
1323
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1068
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1324
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1069
1325
|
yield fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
1070
1326
|
yield fs.writeFile(path, (_a2 = yield (_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");
|
|
1071
1327
|
} catch (err) {
|
|
@@ -1083,8 +1339,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1083
1339
|
const value = data == null ? void 0 : data[name];
|
|
1084
1340
|
if (typeof value === "undefined")
|
|
1085
1341
|
return defaultValue;
|
|
1086
|
-
if (typeof
|
|
1087
|
-
|
|
1342
|
+
if (typeof defaultValue === "string") {
|
|
1343
|
+
if (typeof value === "object" && value !== null)
|
|
1344
|
+
return JSON.stringify(value);
|
|
1345
|
+
if (typeof value === "string")
|
|
1346
|
+
return value;
|
|
1347
|
+
return String(value);
|
|
1348
|
+
}
|
|
1349
|
+
if (typeof value === "string") {
|
|
1350
|
+
try {
|
|
1351
|
+
const parsed = JSON.parse(value);
|
|
1352
|
+
return parsed;
|
|
1353
|
+
} catch (e) {
|
|
1354
|
+
return defaultValue;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1088
1357
|
return value;
|
|
1089
1358
|
});
|
|
1090
1359
|
}
|
|
@@ -1095,7 +1364,18 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1095
1364
|
let data = yield this.readFile();
|
|
1096
1365
|
if (!data)
|
|
1097
1366
|
data = {};
|
|
1098
|
-
|
|
1367
|
+
let storeVal = value;
|
|
1368
|
+
if (typeof value === "string") {
|
|
1369
|
+
try {
|
|
1370
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1371
|
+
const parsed = JSON.parse(value);
|
|
1372
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1373
|
+
storeVal = parsed;
|
|
1374
|
+
}
|
|
1375
|
+
} catch (e) {
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
data[name] = storeVal;
|
|
1099
1379
|
yield this.writeFile(data);
|
|
1100
1380
|
})).catch((err) => {
|
|
1101
1381
|
console.error("Error in setValue:", err);
|
|
@@ -1132,7 +1412,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1132
1412
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1133
1413
|
if (!fs)
|
|
1134
1414
|
throw new ScriptContextError("FileStorageEngine requires Node.js or Deno with Node compatibility (v1.31+)", { cause: new DatedError("'node:fs/promises' module not available") });
|
|
1135
|
-
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id);
|
|
1415
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1136
1416
|
return yield fs.unlink(path);
|
|
1137
1417
|
} catch (err) {
|
|
1138
1418
|
console.error("Error deleting file:", err);
|
|
@@ -1153,7 +1433,10 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1153
1433
|
remapIds: {}
|
|
1154
1434
|
}, options);
|
|
1155
1435
|
}
|
|
1156
|
-
/**
|
|
1436
|
+
/**
|
|
1437
|
+
* Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1438
|
+
* Override this in a subclass if a custom checksum method is needed.
|
|
1439
|
+
*/
|
|
1157
1440
|
calcChecksum(input) {
|
|
1158
1441
|
return __async(this, null, function* () {
|
|
1159
1442
|
return computeHash(input, "SHA-256");
|
|
@@ -1295,208 +1578,14 @@ Has: ${checksum}`);
|
|
|
1295
1578
|
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1296
1579
|
}
|
|
1297
1580
|
};
|
|
1298
|
-
var createNanoEvents = () => ({
|
|
1299
|
-
emit(event, ...args) {
|
|
1300
|
-
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
1301
|
-
callbacks[i](...args);
|
|
1302
|
-
}
|
|
1303
|
-
},
|
|
1304
|
-
events: {},
|
|
1305
|
-
on(event, cb) {
|
|
1306
|
-
var _a;
|
|
1307
|
-
;
|
|
1308
|
-
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
1309
|
-
return () => {
|
|
1310
|
-
var _a2;
|
|
1311
|
-
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
|
-
var NanoEmitter = class {
|
|
1316
|
-
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
1317
|
-
constructor(options = {}) {
|
|
1318
|
-
__publicField(this, "events", createNanoEvents());
|
|
1319
|
-
__publicField(this, "eventUnsubscribes", []);
|
|
1320
|
-
__publicField(this, "emitterOptions");
|
|
1321
|
-
this.emitterOptions = __spreadValues({
|
|
1322
|
-
publicEmit: false
|
|
1323
|
-
}, options);
|
|
1324
|
-
}
|
|
1325
|
-
//#region on
|
|
1326
|
-
/**
|
|
1327
|
-
* Subscribes to an event and calls the callback when it's emitted.
|
|
1328
|
-
* @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 "_")
|
|
1329
|
-
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
1330
|
-
* @example ```ts
|
|
1331
|
-
* const emitter = new NanoEmitter<{
|
|
1332
|
-
* foo: (bar: string) => void;
|
|
1333
|
-
* }>({
|
|
1334
|
-
* publicEmit: true,
|
|
1335
|
-
* });
|
|
1336
|
-
*
|
|
1337
|
-
* let i = 0;
|
|
1338
|
-
* const unsub = emitter.on("foo", (bar) => {
|
|
1339
|
-
* // unsubscribe after 10 events:
|
|
1340
|
-
* if(++i === 10) unsub();
|
|
1341
|
-
* console.log(bar);
|
|
1342
|
-
* });
|
|
1343
|
-
*
|
|
1344
|
-
* emitter.emit("foo", "bar");
|
|
1345
|
-
* ```
|
|
1346
|
-
*/
|
|
1347
|
-
on(event, cb) {
|
|
1348
|
-
let unsub;
|
|
1349
|
-
const unsubProxy = () => {
|
|
1350
|
-
if (!unsub)
|
|
1351
|
-
return;
|
|
1352
|
-
unsub();
|
|
1353
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
1354
|
-
};
|
|
1355
|
-
unsub = this.events.on(event, cb);
|
|
1356
|
-
this.eventUnsubscribes.push(unsub);
|
|
1357
|
-
return unsubProxy;
|
|
1358
|
-
}
|
|
1359
|
-
//#region once
|
|
1360
|
-
/**
|
|
1361
|
-
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
1362
|
-
* @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 "_")
|
|
1363
|
-
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
1364
|
-
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
1365
|
-
* @example ```ts
|
|
1366
|
-
* const emitter = new NanoEmitter<{
|
|
1367
|
-
* foo: (bar: string) => void;
|
|
1368
|
-
* }>();
|
|
1369
|
-
*
|
|
1370
|
-
* // Promise syntax:
|
|
1371
|
-
* const [bar] = await emitter.once("foo");
|
|
1372
|
-
* console.log(bar);
|
|
1373
|
-
*
|
|
1374
|
-
* // Callback syntax:
|
|
1375
|
-
* emitter.once("foo", (bar) => console.log(bar));
|
|
1376
|
-
* ```
|
|
1377
|
-
*/
|
|
1378
|
-
once(event, cb) {
|
|
1379
|
-
return new Promise((resolve) => {
|
|
1380
|
-
let unsub;
|
|
1381
|
-
const onceProxy = ((...args) => {
|
|
1382
|
-
cb == null ? void 0 : cb(...args);
|
|
1383
|
-
unsub == null ? void 0 : unsub();
|
|
1384
|
-
resolve(args);
|
|
1385
|
-
});
|
|
1386
|
-
unsub = this.events.on(event, onceProxy);
|
|
1387
|
-
this.eventUnsubscribes.push(unsub);
|
|
1388
|
-
});
|
|
1389
|
-
}
|
|
1390
|
-
//#region onMulti
|
|
1391
|
-
/**
|
|
1392
|
-
* 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.
|
|
1393
|
-
* @param options An object or array of objects with the following properties:
|
|
1394
|
-
* `callback` (required) is the function that will be called when the conditions are met.
|
|
1395
|
-
*
|
|
1396
|
-
* 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.
|
|
1397
|
-
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
1398
|
-
*
|
|
1399
|
-
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
1400
|
-
* 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.
|
|
1401
|
-
* 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.
|
|
1402
|
-
* At least one of `oneOf` or `allOf` must be provided.
|
|
1403
|
-
*
|
|
1404
|
-
* @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.
|
|
1405
|
-
*/
|
|
1406
|
-
onMulti(options) {
|
|
1407
|
-
const allUnsubs = [];
|
|
1408
|
-
const unsubAll = () => {
|
|
1409
|
-
for (const unsub of allUnsubs)
|
|
1410
|
-
unsub();
|
|
1411
|
-
allUnsubs.splice(0, allUnsubs.length);
|
|
1412
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
1413
|
-
};
|
|
1414
|
-
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
1415
|
-
const optsWithDefaults = __spreadValues({
|
|
1416
|
-
allOf: [],
|
|
1417
|
-
oneOf: [],
|
|
1418
|
-
once: false
|
|
1419
|
-
}, opts);
|
|
1420
|
-
const {
|
|
1421
|
-
oneOf,
|
|
1422
|
-
allOf,
|
|
1423
|
-
once,
|
|
1424
|
-
signal,
|
|
1425
|
-
callback
|
|
1426
|
-
} = optsWithDefaults;
|
|
1427
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
1428
|
-
return unsubAll;
|
|
1429
|
-
if (oneOf.length === 0 && allOf.length === 0)
|
|
1430
|
-
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
1431
|
-
const curEvtUnsubs = [];
|
|
1432
|
-
const checkUnsubAllEvt = (force = false) => {
|
|
1433
|
-
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
1434
|
-
return;
|
|
1435
|
-
for (const unsub of curEvtUnsubs)
|
|
1436
|
-
unsub();
|
|
1437
|
-
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
1438
|
-
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
1439
|
-
};
|
|
1440
|
-
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
1441
|
-
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
1442
|
-
for (const event of oneOf) {
|
|
1443
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1444
|
-
checkUnsubAllEvt();
|
|
1445
|
-
if (allOfConditionMet()) {
|
|
1446
|
-
callback(event, ...args);
|
|
1447
|
-
if (once)
|
|
1448
|
-
checkUnsubAllEvt(true);
|
|
1449
|
-
}
|
|
1450
|
-
}));
|
|
1451
|
-
curEvtUnsubs.push(unsub);
|
|
1452
|
-
}
|
|
1453
|
-
for (const event of allOf) {
|
|
1454
|
-
const unsub = this.events.on(event, ((...args) => {
|
|
1455
|
-
checkUnsubAllEvt();
|
|
1456
|
-
allOfEmitted.add(event);
|
|
1457
|
-
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
1458
|
-
callback(event, ...args);
|
|
1459
|
-
if (once)
|
|
1460
|
-
checkUnsubAllEvt(true);
|
|
1461
|
-
}
|
|
1462
|
-
}));
|
|
1463
|
-
curEvtUnsubs.push(unsub);
|
|
1464
|
-
}
|
|
1465
|
-
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
1466
|
-
}
|
|
1467
|
-
return unsubAll;
|
|
1468
|
-
}
|
|
1469
|
-
//#region emit
|
|
1470
|
-
/**
|
|
1471
|
-
* Emits an event on this instance.
|
|
1472
|
-
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
1473
|
-
* @param event The event to emit
|
|
1474
|
-
* @param args The arguments to pass to the event listeners
|
|
1475
|
-
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
1476
|
-
*/
|
|
1477
|
-
emit(event, ...args) {
|
|
1478
|
-
if (this.emitterOptions.publicEmit) {
|
|
1479
|
-
this.events.emit(event, ...args);
|
|
1480
|
-
return true;
|
|
1481
|
-
}
|
|
1482
|
-
return false;
|
|
1483
|
-
}
|
|
1484
|
-
//#region unsubscribeAll
|
|
1485
|
-
/** Unsubscribes all event listeners from this instance */
|
|
1486
|
-
unsubscribeAll() {
|
|
1487
|
-
for (const unsub of this.eventUnsubscribes)
|
|
1488
|
-
unsub();
|
|
1489
|
-
this.eventUnsubscribes = [];
|
|
1490
|
-
}
|
|
1491
|
-
};
|
|
1492
1581
|
var Debouncer = class extends NanoEmitter {
|
|
1493
1582
|
/**
|
|
1494
1583
|
* Creates a new debouncer with the specified timeout and edge type.
|
|
1495
1584
|
* @param timeout Timeout in milliseconds between letting through calls - defaults to 200
|
|
1496
1585
|
* @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"
|
|
1497
1586
|
*/
|
|
1498
|
-
constructor(timeout = 200, type = "immediate") {
|
|
1499
|
-
super();
|
|
1587
|
+
constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1588
|
+
super(nanoEmitterOptions);
|
|
1500
1589
|
/** All registered listener functions and the time they were attached */
|
|
1501
1590
|
__publicField(this, "listeners", []);
|
|
1502
1591
|
/** The currently active timeout */
|
|
@@ -1527,7 +1616,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1527
1616
|
//#region timeout
|
|
1528
1617
|
/** Sets the timeout for the debouncer */
|
|
1529
1618
|
setTimeout(timeout) {
|
|
1530
|
-
this.emit("change", this.timeout = timeout, this.type);
|
|
1619
|
+
this.events.emit("change", this.timeout = timeout, this.type);
|
|
1531
1620
|
}
|
|
1532
1621
|
/** Returns the current timeout */
|
|
1533
1622
|
getTimeout() {
|
|
@@ -1540,7 +1629,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1540
1629
|
//#region type
|
|
1541
1630
|
/** Sets the edge type for the debouncer */
|
|
1542
1631
|
setType(type) {
|
|
1543
|
-
this.emit("change", this.timeout, this.type = type);
|
|
1632
|
+
this.events.emit("change", this.timeout, this.type = type);
|
|
1544
1633
|
}
|
|
1545
1634
|
/** Returns the current edge type */
|
|
1546
1635
|
getType() {
|
|
@@ -1551,7 +1640,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1551
1640
|
call(...args) {
|
|
1552
1641
|
const cl = (...a) => {
|
|
1553
1642
|
this.queuedCall = void 0;
|
|
1554
|
-
this.emit("call", ...a);
|
|
1643
|
+
this.events.emit("call", ...a);
|
|
1555
1644
|
this.listeners.forEach((l) => l.call(this, ...a));
|
|
1556
1645
|
};
|
|
1557
1646
|
const setRepeatTimeout = () => {
|
|
@@ -1584,14 +1673,30 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1584
1673
|
}
|
|
1585
1674
|
}
|
|
1586
1675
|
};
|
|
1587
|
-
function debounce(fn, timeout = 200, type = "immediate") {
|
|
1588
|
-
const debouncer = new Debouncer(timeout, type);
|
|
1676
|
+
function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1677
|
+
const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
|
|
1589
1678
|
debouncer.addListener(fn);
|
|
1590
1679
|
const func = ((...args) => debouncer.call(...args));
|
|
1591
1680
|
func.debouncer = debouncer;
|
|
1592
1681
|
return func;
|
|
1593
1682
|
}
|
|
1594
1683
|
|
|
1684
|
+
// lib/consts.ts
|
|
1685
|
+
var rawConsts = {
|
|
1686
|
+
coreUtilsVersion: "3.4.0",
|
|
1687
|
+
userUtilsVersion: "10.2.0"
|
|
1688
|
+
};
|
|
1689
|
+
function getConst(constKey, defaultVal) {
|
|
1690
|
+
const val = rawConsts[constKey];
|
|
1691
|
+
return val.match(/^#\{\{.+\}\}$/) ? defaultVal : val;
|
|
1692
|
+
}
|
|
1693
|
+
var versions = {
|
|
1694
|
+
/** Semver version string of the bundled library CoreUtils. */
|
|
1695
|
+
CoreUtils: getConst("coreUtilsVersion", "ERR:unknown"),
|
|
1696
|
+
/** Semver version string of UserUtils. */
|
|
1697
|
+
UserUtils: getConst("userUtilsVersion", "ERR:unknown")
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1595
1700
|
// lib/Errors.ts
|
|
1596
1701
|
var PlatformError = class extends DatedError {
|
|
1597
1702
|
constructor(message, options) {
|
|
@@ -2558,7 +2663,7 @@ function translate(language, key, ...trArgs) {
|
|
|
2558
2663
|
if (typeof language !== "string")
|
|
2559
2664
|
language = fallbackLang != null ? fallbackLang : "";
|
|
2560
2665
|
const trObj = trans[language];
|
|
2561
|
-
if (typeof language !== "string" ||
|
|
2666
|
+
if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
|
|
2562
2667
|
return fallbackLang && language !== fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
2563
2668
|
const transformTrVal = (trKey, trValue) => {
|
|
2564
2669
|
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
@@ -2619,6 +2724,9 @@ function addTranslations(language, translations) {
|
|
|
2619
2724
|
function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
|
|
2620
2725
|
return trans[language];
|
|
2621
2726
|
}
|
|
2727
|
+
function getAllTranslations(asCopy = true) {
|
|
2728
|
+
return asCopy ? JSON.parse(JSON.stringify(trans)) : trans;
|
|
2729
|
+
}
|
|
2622
2730
|
var deleteTranslations = (language) => {
|
|
2623
2731
|
if (language in trans) {
|
|
2624
2732
|
delete trans[language];
|
|
@@ -2649,52 +2757,55 @@ function deleteTransform(patternOrFn) {
|
|
|
2649
2757
|
}
|
|
2650
2758
|
return false;
|
|
2651
2759
|
}
|
|
2652
|
-
var
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
const
|
|
2659
|
-
|
|
2660
|
-
if (
|
|
2661
|
-
|
|
2662
|
-
for (const match of matches) {
|
|
2663
|
-
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2664
|
-
if (typeof repl !== "undefined")
|
|
2665
|
-
str = str.replace(match[0], String(repl));
|
|
2666
|
-
}
|
|
2667
|
-
};
|
|
2668
|
-
const positionalMapping = () => {
|
|
2669
|
-
if (!patternRegex.test(str) || !trArgs[0])
|
|
2670
|
-
return;
|
|
2671
|
-
let matchNum = -1;
|
|
2672
|
-
for (const match of matches) {
|
|
2673
|
-
matchNum++;
|
|
2674
|
-
if (typeof trArgs[matchNum] !== "undefined")
|
|
2675
|
-
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2676
|
-
}
|
|
2677
|
-
};
|
|
2678
|
-
let notStringifiable = false;
|
|
2679
|
-
try {
|
|
2680
|
-
String(trArgs[0]);
|
|
2681
|
-
} catch (e) {
|
|
2682
|
-
notStringifiable = true;
|
|
2760
|
+
var commonKeyedTransform = ({ matches, trArgs, trValue }, patternRegex, quickMatchPattern) => {
|
|
2761
|
+
let str = String(trValue);
|
|
2762
|
+
const someMatchKeyInArgs = (obj) => matches.some((match) => match[1] !== void 0 && match[1] in obj);
|
|
2763
|
+
const namedMapping = () => {
|
|
2764
|
+
if (!str.includes(quickMatchPattern) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !someMatchKeyInArgs(trArgs[0]))
|
|
2765
|
+
return;
|
|
2766
|
+
for (const match of matches) {
|
|
2767
|
+
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2768
|
+
if (typeof repl !== "undefined")
|
|
2769
|
+
str = str.replace(match[0], String(repl));
|
|
2683
2770
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2771
|
+
};
|
|
2772
|
+
const positionalMapping = () => {
|
|
2773
|
+
if (!patternRegex.test(str) || typeof trArgs[0] === "undefined")
|
|
2774
|
+
return;
|
|
2775
|
+
let matchNum = -1;
|
|
2776
|
+
for (const match of matches) {
|
|
2777
|
+
matchNum++;
|
|
2778
|
+
if (typeof trArgs[matchNum] !== "undefined")
|
|
2779
|
+
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
let notStringifiable = false;
|
|
2783
|
+
try {
|
|
2784
|
+
void `${trArgs[0]}`;
|
|
2785
|
+
} catch (e) {
|
|
2786
|
+
notStringifiable = true;
|
|
2690
2787
|
}
|
|
2788
|
+
const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && (notStringifiable || String(trArgs[0]).startsWith("[object"));
|
|
2789
|
+
if (isArgsObject && someMatchKeyInArgs(trArgs[0]))
|
|
2790
|
+
namedMapping();
|
|
2791
|
+
else
|
|
2792
|
+
positionalMapping();
|
|
2793
|
+
return str;
|
|
2794
|
+
};
|
|
2795
|
+
var templateLiteralTransform = [
|
|
2796
|
+
/\$\{([a-zA-Z0-9$_-]+)\}/gm,
|
|
2797
|
+
(tfProps) => commonKeyedTransform(tfProps, /\$\{.+\}/m, "${")
|
|
2798
|
+
];
|
|
2799
|
+
var i18nTransform = [
|
|
2800
|
+
/\{\{([a-zA-Z0-9$_-]+)\}\}/gm,
|
|
2801
|
+
(tfProps) => commonKeyedTransform(tfProps, /\{\{.+\}\}/m, "{{")
|
|
2691
2802
|
];
|
|
2692
2803
|
var percentTransform = [
|
|
2693
2804
|
/%(\d+)/gm,
|
|
2694
2805
|
({ matches, trArgs, trValue }) => {
|
|
2695
2806
|
let str = String(trValue);
|
|
2696
2807
|
for (const match of matches) {
|
|
2697
|
-
const repl =
|
|
2808
|
+
const repl = trArgs == null ? void 0 : trArgs[Number(match[1]) - 1];
|
|
2698
2809
|
if (typeof repl !== "undefined")
|
|
2699
2810
|
str = str.replace(match[0], String(repl));
|
|
2700
2811
|
}
|
|
@@ -2707,13 +2818,81 @@ var tr = {
|
|
|
2707
2818
|
hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
|
|
2708
2819
|
addTranslations,
|
|
2709
2820
|
getTranslations,
|
|
2821
|
+
getAllTranslations,
|
|
2710
2822
|
deleteTranslations,
|
|
2711
2823
|
setFallbackLanguage,
|
|
2712
2824
|
getFallbackLanguage,
|
|
2713
2825
|
addTransform,
|
|
2714
2826
|
deleteTransform,
|
|
2827
|
+
/** Collection of predefined transform functions that can be added via {@linkcode tr.addTransform()} */
|
|
2715
2828
|
transforms: {
|
|
2829
|
+
/**
|
|
2830
|
+
* This transform will replace placeholders matching `${key}` with the value of the passed argument(s).
|
|
2831
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2832
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2833
|
+
* - 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.
|
|
2834
|
+
*
|
|
2835
|
+
* @example ```ts
|
|
2836
|
+
* tr.addTranslations("en", {
|
|
2837
|
+
* "greeting": "Hello, ${user}!\nYou have ${notifs} notifications.",
|
|
2838
|
+
* });
|
|
2839
|
+
* tr.addTransform(tr.transforms.templateLiteral);
|
|
2840
|
+
*
|
|
2841
|
+
* const t = tr.use("en");
|
|
2842
|
+
*
|
|
2843
|
+
* // both calls return the same result:
|
|
2844
|
+
* t("greeting", { user: "John", notifs: 5 }); // "Hello, John!\nYou have 5 notifications."
|
|
2845
|
+
* t("greeting", "John", 5); // "Hello, John!\nYou have 5 notifications."
|
|
2846
|
+
*
|
|
2847
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2848
|
+
* t("greeting", { user: "John" }); // "Hello, John!\nYou have ${notifs} notifications."
|
|
2849
|
+
* ```
|
|
2850
|
+
*/
|
|
2716
2851
|
templateLiteral: templateLiteralTransform,
|
|
2852
|
+
/**
|
|
2853
|
+
* This transform will replace placeholders matching `{{key}}` with the value of the passed argument(s).
|
|
2854
|
+
* This format is commonly used in i18n libraries. Note that advanced syntax is not supported, only simple key replacement.
|
|
2855
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2856
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2857
|
+
* - 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.
|
|
2858
|
+
*
|
|
2859
|
+
* @example ```ts
|
|
2860
|
+
* tr.addTranslations("en", {
|
|
2861
|
+
* "greeting": "Hello, {{user}}!\nYou have {{notifs}} notifications.",
|
|
2862
|
+
* });
|
|
2863
|
+
* tr.addTransform(tr.transforms.i18n);
|
|
2864
|
+
*
|
|
2865
|
+
* const t = tr.use("en");
|
|
2866
|
+
*
|
|
2867
|
+
* // both calls return the same result:
|
|
2868
|
+
* t("greeting", { user: "Alice", notifs: 5 }); // "Hello, Alice!\nYou have 5 notifications."
|
|
2869
|
+
* t("greeting", "Alice", 5); // "Hello, Alice!\nYou have 5 notifications."
|
|
2870
|
+
*
|
|
2871
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2872
|
+
* t("greeting", { user: "Alice" }); // "Hello, Alice!\nYou have {{notifs}} notifications."
|
|
2873
|
+
* ```
|
|
2874
|
+
*/
|
|
2875
|
+
i18n: i18nTransform,
|
|
2876
|
+
/**
|
|
2877
|
+
* This transform will replace `%n` placeholders with the value of the passed arguments.
|
|
2878
|
+
* 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.
|
|
2879
|
+
* Objects will be stringified via `String()` before being inserted.
|
|
2880
|
+
*
|
|
2881
|
+
* @example ```ts
|
|
2882
|
+
* tr.addTranslations("en", {
|
|
2883
|
+
* "greeting": "Hello, %1!\nYou have %2 notifications.",
|
|
2884
|
+
* });
|
|
2885
|
+
* tr.addTransform(tr.transforms.percent);
|
|
2886
|
+
*
|
|
2887
|
+
* const t = tr.use("en");
|
|
2888
|
+
*
|
|
2889
|
+
* // arguments are inserted in the order they're passed:
|
|
2890
|
+
* t("greeting", "Bob", 5); // "Hello, Bob!\nYou have 5 notifications."
|
|
2891
|
+
*
|
|
2892
|
+
* // when a value isn't found, the placeholder will be left as-is:
|
|
2893
|
+
* t("greeting", "Bob"); // "Hello, Bob!\nYou have %2 notifications."
|
|
2894
|
+
* ```
|
|
2895
|
+
*/
|
|
2717
2896
|
percent: percentTransform
|
|
2718
2897
|
}
|
|
2719
2898
|
};
|
|
@@ -2740,5 +2919,3 @@ if (typeof module.exports == "object" && typeof exports == "object") {
|
|
|
2740
2919
|
|
|
2741
2920
|
|
|
2742
2921
|
|
|
2743
|
-
|
|
2744
|
-
//# sourceMappingURL=UserUtils.umd.js.map
|