@sv443-network/userutils 10.0.5 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +38 -32
- package/dist/UserUtils.cjs +555 -397
- package/dist/UserUtils.mjs +557 -397
- package/dist/UserUtils.umd.js +443 -304
- package/dist/lib/consts.d.ts +7 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/translation.d.ts +80 -2
- package/package.json +4 -5
package/dist/UserUtils.umd.js
CHANGED
|
@@ -179,11 +179,12 @@ __export(lib_exports, {
|
|
|
179
179
|
takeRandomItemIndex: () => takeRandomItemIndex,
|
|
180
180
|
tr: () => tr,
|
|
181
181
|
truncStr: () => truncStr,
|
|
182
|
-
valsWithin: () => valsWithin
|
|
182
|
+
valsWithin: () => valsWithin,
|
|
183
|
+
versions: () => versions
|
|
183
184
|
});
|
|
184
185
|
module.exports = __toCommonJS(lib_exports);
|
|
185
186
|
|
|
186
|
-
// node_modules/.pnpm/@sv443-network+coreutils@3.0
|
|
187
|
+
// node_modules/.pnpm/@sv443-network+coreutils@3.3.0/node_modules/@sv443-network/coreutils/dist/CoreUtils.mjs
|
|
187
188
|
function bitSetHas(bitSet, checkVal) {
|
|
188
189
|
return (bitSet & checkVal) === checkVal;
|
|
189
190
|
}
|
|
@@ -649,8 +650,202 @@ function truncStr(input, length, endStr = "...") {
|
|
|
649
650
|
const finalStr = str.length > length ? str.substring(0, length - endStr.length) + endStr : str;
|
|
650
651
|
return finalStr.length > length ? finalStr.substring(0, length) : finalStr;
|
|
651
652
|
}
|
|
653
|
+
var createNanoEvents = () => ({
|
|
654
|
+
emit(event, ...args) {
|
|
655
|
+
for (let callbacks = this.events[event] || [], i = 0, length = callbacks.length; i < length; i++) {
|
|
656
|
+
callbacks[i](...args);
|
|
657
|
+
}
|
|
658
|
+
},
|
|
659
|
+
events: {},
|
|
660
|
+
on(event, cb) {
|
|
661
|
+
var _a;
|
|
662
|
+
;
|
|
663
|
+
((_a = this.events)[event] || (_a[event] = [])).push(cb);
|
|
664
|
+
return () => {
|
|
665
|
+
var _a2;
|
|
666
|
+
this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i);
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
var NanoEmitter = class {
|
|
671
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
672
|
+
constructor(options = {}) {
|
|
673
|
+
__publicField(this, "events", createNanoEvents());
|
|
674
|
+
__publicField(this, "eventUnsubscribes", []);
|
|
675
|
+
__publicField(this, "emitterOptions");
|
|
676
|
+
this.emitterOptions = __spreadValues({
|
|
677
|
+
publicEmit: false
|
|
678
|
+
}, options);
|
|
679
|
+
}
|
|
680
|
+
//#region on
|
|
681
|
+
/**
|
|
682
|
+
* Subscribes to an event and calls the callback when it's emitted.
|
|
683
|
+
* @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 "_")
|
|
684
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
685
|
+
* @example ```ts
|
|
686
|
+
* const emitter = new NanoEmitter<{
|
|
687
|
+
* foo: (bar: string) => void;
|
|
688
|
+
* }>({
|
|
689
|
+
* publicEmit: true,
|
|
690
|
+
* });
|
|
691
|
+
*
|
|
692
|
+
* let i = 0;
|
|
693
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
694
|
+
* // unsubscribe after 10 events:
|
|
695
|
+
* if(++i === 10) unsub();
|
|
696
|
+
* console.log(bar);
|
|
697
|
+
* });
|
|
698
|
+
*
|
|
699
|
+
* emitter.emit("foo", "bar");
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
on(event, cb) {
|
|
703
|
+
let unsub;
|
|
704
|
+
const unsubProxy = () => {
|
|
705
|
+
if (!unsub)
|
|
706
|
+
return;
|
|
707
|
+
unsub();
|
|
708
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub);
|
|
709
|
+
};
|
|
710
|
+
unsub = this.events.on(event, cb);
|
|
711
|
+
this.eventUnsubscribes.push(unsub);
|
|
712
|
+
return unsubProxy;
|
|
713
|
+
}
|
|
714
|
+
//#region once
|
|
715
|
+
/**
|
|
716
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
717
|
+
* @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 "_")
|
|
718
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
719
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
720
|
+
* @example ```ts
|
|
721
|
+
* const emitter = new NanoEmitter<{
|
|
722
|
+
* foo: (bar: string) => void;
|
|
723
|
+
* }>();
|
|
724
|
+
*
|
|
725
|
+
* // Promise syntax:
|
|
726
|
+
* const [bar] = await emitter.once("foo");
|
|
727
|
+
* console.log(bar);
|
|
728
|
+
*
|
|
729
|
+
* // Callback syntax:
|
|
730
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
731
|
+
* ```
|
|
732
|
+
*/
|
|
733
|
+
once(event, cb) {
|
|
734
|
+
return new Promise((resolve) => {
|
|
735
|
+
let unsub;
|
|
736
|
+
const onceProxy = ((...args) => {
|
|
737
|
+
cb == null ? void 0 : cb(...args);
|
|
738
|
+
unsub == null ? void 0 : unsub();
|
|
739
|
+
resolve(args);
|
|
740
|
+
});
|
|
741
|
+
unsub = this.events.on(event, onceProxy);
|
|
742
|
+
this.eventUnsubscribes.push(unsub);
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
//#region onMulti
|
|
746
|
+
/**
|
|
747
|
+
* 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.
|
|
748
|
+
* @param options An object or array of objects with the following properties:
|
|
749
|
+
* `callback` (required) is the function that will be called when the conditions are met.
|
|
750
|
+
*
|
|
751
|
+
* 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.
|
|
752
|
+
* If `signal` is provided, the subscription will be canceled when the given signal is aborted.
|
|
753
|
+
*
|
|
754
|
+
* If `oneOf` is used, the callback will be called when any of the matching events are emitted.
|
|
755
|
+
* 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.
|
|
756
|
+
* 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.
|
|
757
|
+
* At least one of `oneOf` or `allOf` must be provided.
|
|
758
|
+
*
|
|
759
|
+
* @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.
|
|
760
|
+
*/
|
|
761
|
+
onMulti(options) {
|
|
762
|
+
const allUnsubs = [];
|
|
763
|
+
const unsubAll = () => {
|
|
764
|
+
for (const unsub of allUnsubs)
|
|
765
|
+
unsub();
|
|
766
|
+
allUnsubs.splice(0, allUnsubs.length);
|
|
767
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !allUnsubs.includes(u));
|
|
768
|
+
};
|
|
769
|
+
for (const opts of Array.isArray(options) ? options : [options]) {
|
|
770
|
+
const optsWithDefaults = __spreadValues({
|
|
771
|
+
allOf: [],
|
|
772
|
+
oneOf: [],
|
|
773
|
+
once: false
|
|
774
|
+
}, opts);
|
|
775
|
+
const {
|
|
776
|
+
oneOf,
|
|
777
|
+
allOf,
|
|
778
|
+
once,
|
|
779
|
+
signal,
|
|
780
|
+
callback
|
|
781
|
+
} = optsWithDefaults;
|
|
782
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
783
|
+
return unsubAll;
|
|
784
|
+
if (oneOf.length === 0 && allOf.length === 0)
|
|
785
|
+
throw new TypeError("NanoEmitter.onMulti(): Either `oneOf` or `allOf` or both must be provided in the options");
|
|
786
|
+
const curEvtUnsubs = [];
|
|
787
|
+
const checkUnsubAllEvt = (force = false) => {
|
|
788
|
+
if (!(signal == null ? void 0 : signal.aborted) && !force)
|
|
789
|
+
return;
|
|
790
|
+
for (const unsub of curEvtUnsubs)
|
|
791
|
+
unsub();
|
|
792
|
+
curEvtUnsubs.splice(0, curEvtUnsubs.length);
|
|
793
|
+
this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => !curEvtUnsubs.includes(u));
|
|
794
|
+
};
|
|
795
|
+
const allOfEmitted = /* @__PURE__ */ new Set();
|
|
796
|
+
const allOfConditionMet = () => allOf.length === 0 || allOfEmitted.size === allOf.length;
|
|
797
|
+
for (const event of oneOf) {
|
|
798
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
799
|
+
checkUnsubAllEvt();
|
|
800
|
+
if (allOfConditionMet()) {
|
|
801
|
+
callback(event, ...args);
|
|
802
|
+
if (once)
|
|
803
|
+
checkUnsubAllEvt(true);
|
|
804
|
+
}
|
|
805
|
+
}));
|
|
806
|
+
curEvtUnsubs.push(unsub);
|
|
807
|
+
}
|
|
808
|
+
for (const event of allOf) {
|
|
809
|
+
const unsub = this.events.on(event, ((...args) => {
|
|
810
|
+
checkUnsubAllEvt();
|
|
811
|
+
allOfEmitted.add(event);
|
|
812
|
+
if (allOfConditionMet() && (oneOf.length === 0 || oneOf.includes(event))) {
|
|
813
|
+
callback(event, ...args);
|
|
814
|
+
if (once)
|
|
815
|
+
checkUnsubAllEvt(true);
|
|
816
|
+
}
|
|
817
|
+
}));
|
|
818
|
+
curEvtUnsubs.push(unsub);
|
|
819
|
+
}
|
|
820
|
+
allUnsubs.push(() => checkUnsubAllEvt(true));
|
|
821
|
+
}
|
|
822
|
+
return unsubAll;
|
|
823
|
+
}
|
|
824
|
+
//#region emit
|
|
825
|
+
/**
|
|
826
|
+
* Emits an event on this instance.
|
|
827
|
+
* - ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
828
|
+
* @param event The event to emit
|
|
829
|
+
* @param args The arguments to pass to the event listeners
|
|
830
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
831
|
+
*/
|
|
832
|
+
emit(event, ...args) {
|
|
833
|
+
if (this.emitterOptions.publicEmit) {
|
|
834
|
+
this.events.emit(event, ...args);
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
//#region unsubscribeAll
|
|
840
|
+
/** Unsubscribes all event listeners from this instance */
|
|
841
|
+
unsubscribeAll() {
|
|
842
|
+
for (const unsub of this.eventUnsubscribes)
|
|
843
|
+
unsub();
|
|
844
|
+
this.eventUnsubscribes = [];
|
|
845
|
+
}
|
|
846
|
+
};
|
|
652
847
|
var dsFmtVer = 1;
|
|
653
|
-
var DataStore = class {
|
|
848
|
+
var DataStore = class extends NanoEmitter {
|
|
654
849
|
//#region constructor
|
|
655
850
|
/**
|
|
656
851
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
@@ -662,14 +857,17 @@ var DataStore = class {
|
|
|
662
857
|
* @param opts The options for this DataStore instance
|
|
663
858
|
*/
|
|
664
859
|
constructor(opts) {
|
|
860
|
+
var _a, _b, _c;
|
|
861
|
+
super(opts.nanoEmitterOptions);
|
|
665
862
|
__publicField(this, "id");
|
|
666
863
|
__publicField(this, "formatVersion");
|
|
667
864
|
__publicField(this, "defaultData");
|
|
668
865
|
__publicField(this, "encodeData");
|
|
669
866
|
__publicField(this, "decodeData");
|
|
670
867
|
__publicField(this, "compressionFormat", "deflate-raw");
|
|
671
|
-
__publicField(this, "memoryCache"
|
|
868
|
+
__publicField(this, "memoryCache");
|
|
672
869
|
__publicField(this, "engine");
|
|
870
|
+
__publicField(this, "keyPrefix");
|
|
673
871
|
__publicField(this, "options");
|
|
674
872
|
/**
|
|
675
873
|
* Whether all first-init checks should be done.
|
|
@@ -681,21 +879,21 @@ var DataStore = class {
|
|
|
681
879
|
__publicField(this, "cachedData");
|
|
682
880
|
__publicField(this, "migrations");
|
|
683
881
|
__publicField(this, "migrateIds", []);
|
|
684
|
-
var _a, _b;
|
|
685
882
|
this.id = opts.id;
|
|
686
883
|
this.formatVersion = opts.formatVersion;
|
|
687
884
|
this.defaultData = opts.defaultData;
|
|
688
|
-
this.memoryCache =
|
|
885
|
+
this.memoryCache = (_a = opts.memoryCache) != null ? _a : true;
|
|
689
886
|
this.cachedData = this.memoryCache ? opts.defaultData : {};
|
|
690
887
|
this.migrations = opts.migrations;
|
|
691
888
|
if (opts.migrateIds)
|
|
692
889
|
this.migrateIds = Array.isArray(opts.migrateIds) ? opts.migrateIds : [opts.migrateIds];
|
|
693
890
|
this.engine = typeof opts.engine === "function" ? opts.engine() : opts.engine;
|
|
891
|
+
this.keyPrefix = (_b = opts.keyPrefix) != null ? _b : "__ds-";
|
|
694
892
|
this.options = opts;
|
|
695
893
|
if ("encodeData" in opts && "decodeData" in opts && Array.isArray(opts.encodeData) && Array.isArray(opts.decodeData)) {
|
|
696
894
|
this.encodeData = [opts.encodeData[0], opts.encodeData[1]];
|
|
697
895
|
this.decodeData = [opts.decodeData[0], opts.decodeData[1]];
|
|
698
|
-
this.compressionFormat = (
|
|
896
|
+
this.compressionFormat = (_c = opts.encodeData[0]) != null ? _c : null;
|
|
699
897
|
} else if (opts.compressionFormat === null) {
|
|
700
898
|
this.encodeData = void 0;
|
|
701
899
|
this.decodeData = void 0;
|
|
@@ -738,13 +936,13 @@ var DataStore = class {
|
|
|
738
936
|
promises.push(this.engine.setValue(newKey, value));
|
|
739
937
|
promises.push(this.engine.deleteValue(oldKey));
|
|
740
938
|
};
|
|
741
|
-
migrateFmt(`_uucfg-${this.id}`,
|
|
939
|
+
migrateFmt(`_uucfg-${this.id}`, `${this.keyPrefix}${this.id}-dat`, oldData);
|
|
742
940
|
if (!isNaN(oldVer))
|
|
743
|
-
migrateFmt(`_uucfgver-${this.id}`,
|
|
941
|
+
migrateFmt(`_uucfgver-${this.id}`, `${this.keyPrefix}${this.id}-ver`, oldVer);
|
|
744
942
|
if (typeof oldEnc === "boolean" || oldEnc === "true" || oldEnc === "false" || typeof oldEnc === "number" || oldEnc === "0" || oldEnc === "1")
|
|
745
|
-
migrateFmt(`_uucfgenc-${this.id}`,
|
|
943
|
+
migrateFmt(`_uucfgenc-${this.id}`, `${this.keyPrefix}${this.id}-enf`, [0, "0", true, "true"].includes(oldEnc) ? (_a = this.compressionFormat) != null ? _a : null : null);
|
|
746
944
|
else {
|
|
747
|
-
promises.push(this.engine.setValue(
|
|
945
|
+
promises.push(this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat));
|
|
748
946
|
promises.push(this.engine.deleteValue(`_uucfgenc-${this.id}`));
|
|
749
947
|
}
|
|
750
948
|
yield Promise.allSettled(promises);
|
|
@@ -756,31 +954,27 @@ var DataStore = class {
|
|
|
756
954
|
yield this.migrateId(this.migrateIds);
|
|
757
955
|
this.migrateIds = [];
|
|
758
956
|
}
|
|
759
|
-
const storedDataRaw = yield this.engine.getValue(
|
|
760
|
-
|
|
761
|
-
if (typeof storedDataRaw !== "string") {
|
|
762
|
-
yield this.saveDefaultData();
|
|
763
|
-
|
|
957
|
+
const storedDataRaw = yield this.engine.getValue(`${this.keyPrefix}${this.id}-dat`, null);
|
|
958
|
+
const storedFmtVer = Number(yield this.engine.getValue(`${this.keyPrefix}${this.id}-ver`, NaN));
|
|
959
|
+
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
960
|
+
yield this.saveDefaultData(false);
|
|
961
|
+
const data = this.engine.deepCopy(this.defaultData);
|
|
962
|
+
this.events.emit("loadData", data);
|
|
963
|
+
return data;
|
|
764
964
|
}
|
|
765
965
|
const storedData = storedDataRaw != null ? storedDataRaw : JSON.stringify(this.defaultData);
|
|
766
|
-
const encodingFmt = String(yield this.engine.getValue(
|
|
966
|
+
const encodingFmt = String(yield this.engine.getValue(`${this.keyPrefix}${this.id}-enf`, null));
|
|
767
967
|
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);
|
|
968
|
+
let parsed = typeof storedData === "string" ? yield this.engine.deserializeData(storedData, isEncoded) : storedData;
|
|
774
969
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
775
970
|
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);
|
|
971
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
972
|
+
this.events.emit("loadData", result);
|
|
973
|
+
return result;
|
|
782
974
|
} catch (err) {
|
|
975
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
783
976
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
977
|
+
this.events.emit("error", error);
|
|
784
978
|
yield this.saveDefaultData();
|
|
785
979
|
return this.defaultData;
|
|
786
980
|
}
|
|
@@ -790,7 +984,7 @@ var DataStore = class {
|
|
|
790
984
|
/**
|
|
791
985
|
* Returns a copy of the data from the in-memory cache.
|
|
792
986
|
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
793
|
-
* ⚠️
|
|
987
|
+
* ⚠️ Only available when `memoryCache` is `true` (default). When set to `false`, this produces a type and runtime error - use {@linkcode loadData()} instead.
|
|
794
988
|
*/
|
|
795
989
|
getData() {
|
|
796
990
|
if (!this.memoryCache)
|
|
@@ -800,28 +994,48 @@ var DataStore = class {
|
|
|
800
994
|
//#region setData
|
|
801
995
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
802
996
|
setData(data) {
|
|
803
|
-
|
|
997
|
+
const dataCopy = this.engine.deepCopy(data);
|
|
998
|
+
if (this.memoryCache) {
|
|
804
999
|
this.cachedData = data;
|
|
1000
|
+
this.events.emit("updateDataSync", dataCopy);
|
|
1001
|
+
}
|
|
805
1002
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
806
|
-
yield Promise.allSettled([
|
|
807
|
-
this.engine.setValue(
|
|
808
|
-
this.engine.setValue(
|
|
809
|
-
this.engine.setValue(
|
|
1003
|
+
const results = yield Promise.allSettled([
|
|
1004
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(data, this.encodingEnabled())),
|
|
1005
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1006
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
810
1007
|
]);
|
|
1008
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
1009
|
+
this.events.emit("updateData", dataCopy);
|
|
1010
|
+
else {
|
|
1011
|
+
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1012
|
+
console.error(error);
|
|
1013
|
+
this.events.emit("error", error);
|
|
1014
|
+
}
|
|
811
1015
|
resolve();
|
|
812
1016
|
}));
|
|
813
1017
|
}
|
|
814
1018
|
//#region saveDefaultData
|
|
815
|
-
/**
|
|
816
|
-
|
|
1019
|
+
/**
|
|
1020
|
+
* Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage.
|
|
1021
|
+
* @param emitEvent Whether to emit the `setDefaultData` event - set to `false` to prevent event emission (used internally during initial population in {@linkcode loadData()})
|
|
1022
|
+
*/
|
|
1023
|
+
saveDefaultData(emitEvent = true) {
|
|
817
1024
|
return __async(this, null, function* () {
|
|
818
1025
|
if (this.memoryCache)
|
|
819
1026
|
this.cachedData = this.defaultData;
|
|
820
|
-
yield Promise.allSettled([
|
|
821
|
-
this.engine.setValue(
|
|
822
|
-
this.engine.setValue(
|
|
823
|
-
this.engine.setValue(
|
|
1027
|
+
const results = yield Promise.allSettled([
|
|
1028
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(this.defaultData, this.encodingEnabled())),
|
|
1029
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, this.formatVersion),
|
|
1030
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
824
1031
|
]);
|
|
1032
|
+
if (results.every((r) => r.status === "fulfilled"))
|
|
1033
|
+
emitEvent && this.events.emit("setDefaultData", this.defaultData);
|
|
1034
|
+
else {
|
|
1035
|
+
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1036
|
+
console.error(error);
|
|
1037
|
+
this.events.emit("error", error);
|
|
1038
|
+
}
|
|
825
1039
|
});
|
|
826
1040
|
}
|
|
827
1041
|
//#region deleteData
|
|
@@ -834,11 +1048,12 @@ var DataStore = class {
|
|
|
834
1048
|
return __async(this, null, function* () {
|
|
835
1049
|
var _a, _b;
|
|
836
1050
|
yield Promise.allSettled([
|
|
837
|
-
this.engine.deleteValue(
|
|
838
|
-
this.engine.deleteValue(
|
|
839
|
-
this.engine.deleteValue(
|
|
1051
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-dat`),
|
|
1052
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-ver`),
|
|
1053
|
+
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
840
1054
|
]);
|
|
841
1055
|
yield (_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a);
|
|
1056
|
+
this.events.emit("deleteData");
|
|
842
1057
|
});
|
|
843
1058
|
}
|
|
844
1059
|
//#region encodingEnabled
|
|
@@ -861,30 +1076,35 @@ var DataStore = class {
|
|
|
861
1076
|
let newData = oldData;
|
|
862
1077
|
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
863
1078
|
let lastFmtVer = oldFmtVer;
|
|
864
|
-
for (
|
|
1079
|
+
for (let i = 0; i < sortedMigrations.length; i++) {
|
|
1080
|
+
const [fmtVer, migrationFunc] = sortedMigrations[i];
|
|
865
1081
|
const ver = Number(fmtVer);
|
|
866
1082
|
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
867
1083
|
try {
|
|
868
1084
|
const migRes = migrationFunc(newData);
|
|
869
1085
|
newData = migRes instanceof Promise ? yield migRes : migRes;
|
|
870
1086
|
lastFmtVer = oldFmtVer = ver;
|
|
1087
|
+
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
1088
|
+
this.events.emit("migrateData", ver, newData, isFinal);
|
|
871
1089
|
} catch (err) {
|
|
1090
|
+
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
1091
|
+
this.events.emit("migrationError", ver, migError);
|
|
1092
|
+
this.events.emit("error", migError);
|
|
872
1093
|
if (!resetOnError)
|
|
873
|
-
throw
|
|
1094
|
+
throw migError;
|
|
874
1095
|
yield this.saveDefaultData();
|
|
875
|
-
return this.
|
|
1096
|
+
return this.engine.deepCopy(this.defaultData);
|
|
876
1097
|
}
|
|
877
1098
|
}
|
|
878
1099
|
}
|
|
879
1100
|
yield Promise.allSettled([
|
|
880
|
-
this.engine.setValue(
|
|
881
|
-
this.engine.setValue(
|
|
882
|
-
this.engine.setValue(
|
|
1101
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(newData, this.encodingEnabled())),
|
|
1102
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, lastFmtVer),
|
|
1103
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
883
1104
|
]);
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
return this.engine.deepCopy(newData);
|
|
1105
|
+
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
1106
|
+
this.events.emit("updateData", result);
|
|
1107
|
+
return result;
|
|
888
1108
|
});
|
|
889
1109
|
}
|
|
890
1110
|
//#region migrateId
|
|
@@ -898,9 +1118,9 @@ var DataStore = class {
|
|
|
898
1118
|
yield Promise.all(ids.map((id) => __async(this, null, function* () {
|
|
899
1119
|
const [data, fmtVer, isEncoded] = yield (() => __async(this, null, function* () {
|
|
900
1120
|
const [d, f, e] = yield Promise.all([
|
|
901
|
-
this.engine.getValue(
|
|
902
|
-
this.engine.getValue(
|
|
903
|
-
this.engine.getValue(
|
|
1121
|
+
this.engine.getValue(`${this.keyPrefix}${id}-dat`, JSON.stringify(this.defaultData)),
|
|
1122
|
+
this.engine.getValue(`${this.keyPrefix}${id}-ver`, NaN),
|
|
1123
|
+
this.engine.getValue(`${this.keyPrefix}${id}-enf`, null)
|
|
904
1124
|
]);
|
|
905
1125
|
return [d, Number(f), Boolean(e) && String(e) !== "null"];
|
|
906
1126
|
}))();
|
|
@@ -908,13 +1128,14 @@ var DataStore = class {
|
|
|
908
1128
|
return;
|
|
909
1129
|
const parsed = yield this.engine.deserializeData(data, isEncoded);
|
|
910
1130
|
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(
|
|
1131
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-dat`, yield this.engine.serializeData(parsed, this.encodingEnabled())),
|
|
1132
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-ver`, fmtVer),
|
|
1133
|
+
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat),
|
|
1134
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-dat`),
|
|
1135
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1136
|
+
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
917
1137
|
]);
|
|
1138
|
+
this.events.emit("migrateId", id, this.id);
|
|
918
1139
|
})));
|
|
919
1140
|
});
|
|
920
1141
|
}
|
|
@@ -1046,7 +1267,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1046
1267
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1047
1268
|
if (!fs)
|
|
1048
1269
|
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);
|
|
1270
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1050
1271
|
const data = yield fs.readFile(path, "utf-8");
|
|
1051
1272
|
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
1273
|
} catch (e) {
|
|
@@ -1065,7 +1286,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1065
1286
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1066
1287
|
if (!fs)
|
|
1067
1288
|
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);
|
|
1289
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1069
1290
|
yield fs.mkdir(path.slice(0, path.lastIndexOf(path.includes("/") ? "/" : "\\")), { recursive: true });
|
|
1070
1291
|
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
1292
|
} catch (err) {
|
|
@@ -1083,8 +1304,21 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1083
1304
|
const value = data == null ? void 0 : data[name];
|
|
1084
1305
|
if (typeof value === "undefined")
|
|
1085
1306
|
return defaultValue;
|
|
1086
|
-
if (typeof
|
|
1087
|
-
|
|
1307
|
+
if (typeof defaultValue === "string") {
|
|
1308
|
+
if (typeof value === "object" && value !== null)
|
|
1309
|
+
return JSON.stringify(value);
|
|
1310
|
+
if (typeof value === "string")
|
|
1311
|
+
return value;
|
|
1312
|
+
return String(value);
|
|
1313
|
+
}
|
|
1314
|
+
if (typeof value === "string") {
|
|
1315
|
+
try {
|
|
1316
|
+
const parsed = JSON.parse(value);
|
|
1317
|
+
return parsed;
|
|
1318
|
+
} catch (e) {
|
|
1319
|
+
return defaultValue;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1088
1322
|
return value;
|
|
1089
1323
|
});
|
|
1090
1324
|
}
|
|
@@ -1095,7 +1329,18 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1095
1329
|
let data = yield this.readFile();
|
|
1096
1330
|
if (!data)
|
|
1097
1331
|
data = {};
|
|
1098
|
-
|
|
1332
|
+
let storeVal = value;
|
|
1333
|
+
if (typeof value === "string") {
|
|
1334
|
+
try {
|
|
1335
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
1336
|
+
const parsed = JSON.parse(value);
|
|
1337
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
1338
|
+
storeVal = parsed;
|
|
1339
|
+
}
|
|
1340
|
+
} catch (e) {
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
data[name] = storeVal;
|
|
1099
1344
|
yield this.writeFile(data);
|
|
1100
1345
|
})).catch((err) => {
|
|
1101
1346
|
console.error("Error in setValue:", err);
|
|
@@ -1132,7 +1377,7 @@ var FileStorageEngine = class extends DataStoreEngine {
|
|
|
1132
1377
|
fs = (_a = yield import("fs/promises")) == null ? void 0 : _a.default;
|
|
1133
1378
|
if (!fs)
|
|
1134
1379
|
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);
|
|
1380
|
+
const path = typeof this.options.filePath === "string" ? this.options.filePath : this.options.filePath(this.dataStoreOptions.id, this.dataStoreOptions);
|
|
1136
1381
|
return yield fs.unlink(path);
|
|
1137
1382
|
} catch (err) {
|
|
1138
1383
|
console.error("Error deleting file:", err);
|
|
@@ -1295,200 +1540,6 @@ Has: ${checksum}`);
|
|
|
1295
1540
|
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1296
1541
|
}
|
|
1297
1542
|
};
|
|
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
1543
|
var Debouncer = class extends NanoEmitter {
|
|
1493
1544
|
/**
|
|
1494
1545
|
* Creates a new debouncer with the specified timeout and edge type.
|
|
@@ -1592,6 +1643,22 @@ function debounce(fn, timeout = 200, type = "immediate") {
|
|
|
1592
1643
|
return func;
|
|
1593
1644
|
}
|
|
1594
1645
|
|
|
1646
|
+
// lib/consts.ts
|
|
1647
|
+
var rawConsts = {
|
|
1648
|
+
coreUtilsVersion: "3.3.0",
|
|
1649
|
+
userUtilsVersion: "10.1.0"
|
|
1650
|
+
};
|
|
1651
|
+
function getConst(constKey, defaultVal) {
|
|
1652
|
+
const val = rawConsts[constKey];
|
|
1653
|
+
return val.match(/^#\{\{.+\}\}$/) ? defaultVal : val;
|
|
1654
|
+
}
|
|
1655
|
+
var versions = {
|
|
1656
|
+
/** Semver version string of the bundled library CoreUtils. */
|
|
1657
|
+
CoreUtils: getConst("coreUtilsVersion", "ERR:unknown"),
|
|
1658
|
+
/** Semver version string of UserUtils. */
|
|
1659
|
+
UserUtils: getConst("userUtilsVersion", "ERR:unknown")
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1595
1662
|
// lib/Errors.ts
|
|
1596
1663
|
var PlatformError = class extends DatedError {
|
|
1597
1664
|
constructor(message, options) {
|
|
@@ -2558,7 +2625,7 @@ function translate(language, key, ...trArgs) {
|
|
|
2558
2625
|
if (typeof language !== "string")
|
|
2559
2626
|
language = fallbackLang != null ? fallbackLang : "";
|
|
2560
2627
|
const trObj = trans[language];
|
|
2561
|
-
if (typeof language !== "string" ||
|
|
2628
|
+
if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
|
|
2562
2629
|
return fallbackLang && language !== fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
2563
2630
|
const transformTrVal = (trKey, trValue) => {
|
|
2564
2631
|
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
@@ -2619,6 +2686,9 @@ function addTranslations(language, translations) {
|
|
|
2619
2686
|
function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
|
|
2620
2687
|
return trans[language];
|
|
2621
2688
|
}
|
|
2689
|
+
function getAllTranslations(asCopy = true) {
|
|
2690
|
+
return asCopy ? JSON.parse(JSON.stringify(trans)) : trans;
|
|
2691
|
+
}
|
|
2622
2692
|
var deleteTranslations = (language) => {
|
|
2623
2693
|
if (language in trans) {
|
|
2624
2694
|
delete trans[language];
|
|
@@ -2649,52 +2719,55 @@ function deleteTransform(patternOrFn) {
|
|
|
2649
2719
|
}
|
|
2650
2720
|
return false;
|
|
2651
2721
|
}
|
|
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;
|
|
2722
|
+
var commonKeyedTransform = ({ matches, trArgs, trValue }, patternRegex, quickMatchPattern) => {
|
|
2723
|
+
let str = String(trValue);
|
|
2724
|
+
const someMatchKeyInArgs = (obj) => matches.some((match) => match[1] !== void 0 && match[1] in obj);
|
|
2725
|
+
const namedMapping = () => {
|
|
2726
|
+
if (!str.includes(quickMatchPattern) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !someMatchKeyInArgs(trArgs[0]))
|
|
2727
|
+
return;
|
|
2728
|
+
for (const match of matches) {
|
|
2729
|
+
const repl = match[1] !== void 0 ? trArgs[0][match[1]] : void 0;
|
|
2730
|
+
if (typeof repl !== "undefined")
|
|
2731
|
+
str = str.replace(match[0], String(repl));
|
|
2683
2732
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2733
|
+
};
|
|
2734
|
+
const positionalMapping = () => {
|
|
2735
|
+
if (!patternRegex.test(str) || typeof trArgs[0] === "undefined")
|
|
2736
|
+
return;
|
|
2737
|
+
let matchNum = -1;
|
|
2738
|
+
for (const match of matches) {
|
|
2739
|
+
matchNum++;
|
|
2740
|
+
if (typeof trArgs[matchNum] !== "undefined")
|
|
2741
|
+
str = str.replace(match[0], String(trArgs[matchNum]));
|
|
2742
|
+
}
|
|
2743
|
+
};
|
|
2744
|
+
let notStringifiable = false;
|
|
2745
|
+
try {
|
|
2746
|
+
void `${trArgs[0]}`;
|
|
2747
|
+
} catch (e) {
|
|
2748
|
+
notStringifiable = true;
|
|
2690
2749
|
}
|
|
2750
|
+
const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && (notStringifiable || String(trArgs[0]).startsWith("[object"));
|
|
2751
|
+
if (isArgsObject && someMatchKeyInArgs(trArgs[0]))
|
|
2752
|
+
namedMapping();
|
|
2753
|
+
else
|
|
2754
|
+
positionalMapping();
|
|
2755
|
+
return str;
|
|
2756
|
+
};
|
|
2757
|
+
var templateLiteralTransform = [
|
|
2758
|
+
/\$\{([a-zA-Z0-9$_-]+)\}/gm,
|
|
2759
|
+
(tfProps) => commonKeyedTransform(tfProps, /\$\{.+\}/m, "${")
|
|
2760
|
+
];
|
|
2761
|
+
var i18nTransform = [
|
|
2762
|
+
/\{\{([a-zA-Z0-9$_-]+)\}\}/gm,
|
|
2763
|
+
(tfProps) => commonKeyedTransform(tfProps, /\{\{.+\}\}/m, "{{")
|
|
2691
2764
|
];
|
|
2692
2765
|
var percentTransform = [
|
|
2693
2766
|
/%(\d+)/gm,
|
|
2694
2767
|
({ matches, trArgs, trValue }) => {
|
|
2695
2768
|
let str = String(trValue);
|
|
2696
2769
|
for (const match of matches) {
|
|
2697
|
-
const repl =
|
|
2770
|
+
const repl = trArgs == null ? void 0 : trArgs[Number(match[1]) - 1];
|
|
2698
2771
|
if (typeof repl !== "undefined")
|
|
2699
2772
|
str = str.replace(match[0], String(repl));
|
|
2700
2773
|
}
|
|
@@ -2707,13 +2780,81 @@ var tr = {
|
|
|
2707
2780
|
hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
|
|
2708
2781
|
addTranslations,
|
|
2709
2782
|
getTranslations,
|
|
2783
|
+
getAllTranslations,
|
|
2710
2784
|
deleteTranslations,
|
|
2711
2785
|
setFallbackLanguage,
|
|
2712
2786
|
getFallbackLanguage,
|
|
2713
2787
|
addTransform,
|
|
2714
2788
|
deleteTransform,
|
|
2789
|
+
/** Collection of predefined transform functions that can be added via {@linkcode tr.addTransform()} */
|
|
2715
2790
|
transforms: {
|
|
2791
|
+
/**
|
|
2792
|
+
* This transform will replace placeholders matching `${key}` with the value of the passed argument(s).
|
|
2793
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2794
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2795
|
+
* - 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.
|
|
2796
|
+
*
|
|
2797
|
+
* @example ```ts
|
|
2798
|
+
* tr.addTranslations("en", {
|
|
2799
|
+
* "greeting": "Hello, ${user}!\nYou have ${notifs} notifications.",
|
|
2800
|
+
* });
|
|
2801
|
+
* tr.addTransform(tr.transforms.templateLiteral);
|
|
2802
|
+
*
|
|
2803
|
+
* const t = tr.use("en");
|
|
2804
|
+
*
|
|
2805
|
+
* // both calls return the same result:
|
|
2806
|
+
* t("greeting", { user: "John", notifs: 5 }); // "Hello, John!\nYou have 5 notifications."
|
|
2807
|
+
* t("greeting", "John", 5); // "Hello, John!\nYou have 5 notifications."
|
|
2808
|
+
*
|
|
2809
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2810
|
+
* t("greeting", { user: "John" }); // "Hello, John!\nYou have ${notifs} notifications."
|
|
2811
|
+
* ```
|
|
2812
|
+
*/
|
|
2716
2813
|
templateLiteral: templateLiteralTransform,
|
|
2814
|
+
/**
|
|
2815
|
+
* This transform will replace placeholders matching `{{key}}` with the value of the passed argument(s).
|
|
2816
|
+
* This format is commonly used in i18n libraries. Note that advanced syntax is not supported, only simple key replacement.
|
|
2817
|
+
* The arguments can be passed in keyed object form or positionally via the spread operator:
|
|
2818
|
+
* - Keyed: If the first argument is an object and `key` is found in it, the value will be used for the replacement.
|
|
2819
|
+
* - 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.
|
|
2820
|
+
*
|
|
2821
|
+
* @example ```ts
|
|
2822
|
+
* tr.addTranslations("en", {
|
|
2823
|
+
* "greeting": "Hello, {{user}}!\nYou have {{notifs}} notifications.",
|
|
2824
|
+
* });
|
|
2825
|
+
* tr.addTransform(tr.transforms.i18n);
|
|
2826
|
+
*
|
|
2827
|
+
* const t = tr.use("en");
|
|
2828
|
+
*
|
|
2829
|
+
* // both calls return the same result:
|
|
2830
|
+
* t("greeting", { user: "Alice", notifs: 5 }); // "Hello, Alice!\nYou have 5 notifications."
|
|
2831
|
+
* t("greeting", "Alice", 5); // "Hello, Alice!\nYou have 5 notifications."
|
|
2832
|
+
*
|
|
2833
|
+
* // when a key isn't found in the object, it will be left as-is:
|
|
2834
|
+
* t("greeting", { user: "Alice" }); // "Hello, Alice!\nYou have {{notifs}} notifications."
|
|
2835
|
+
* ```
|
|
2836
|
+
*/
|
|
2837
|
+
i18n: i18nTransform,
|
|
2838
|
+
/**
|
|
2839
|
+
* This transform will replace `%n` placeholders with the value of the passed arguments.
|
|
2840
|
+
* 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.
|
|
2841
|
+
* Objects will be stringified via `String()` before being inserted.
|
|
2842
|
+
*
|
|
2843
|
+
* @example ```ts
|
|
2844
|
+
* tr.addTranslations("en", {
|
|
2845
|
+
* "greeting": "Hello, %1!\nYou have %2 notifications.",
|
|
2846
|
+
* });
|
|
2847
|
+
* tr.addTransform(tr.transforms.percent);
|
|
2848
|
+
*
|
|
2849
|
+
* const t = tr.use("en");
|
|
2850
|
+
*
|
|
2851
|
+
* // arguments are inserted in the order they're passed:
|
|
2852
|
+
* t("greeting", "Bob", 5); // "Hello, Bob!\nYou have 5 notifications."
|
|
2853
|
+
*
|
|
2854
|
+
* // when a value isn't found, the placeholder will be left as-is:
|
|
2855
|
+
* t("greeting", "Bob"); // "Hello, Bob!\nYou have %2 notifications."
|
|
2856
|
+
* ```
|
|
2857
|
+
*/
|
|
2717
2858
|
percent: percentTransform
|
|
2718
2859
|
}
|
|
2719
2860
|
};
|
|
@@ -2740,5 +2881,3 @@ if (typeof module.exports == "object" && typeof exports == "object") {
|
|
|
2740
2881
|
|
|
2741
2882
|
|
|
2742
2883
|
|
|
2743
|
-
|
|
2744
|
-
//# sourceMappingURL=UserUtils.umd.js.map
|