@sv443-network/coreutils 3.5.0 → 3.6.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 +16 -0
- package/README.md +1 -0
- package/dist/CoreUtils.cjs +70 -28
- package/dist/CoreUtils.min.cjs +4 -4
- package/dist/CoreUtils.min.mjs +4 -4
- package/dist/CoreUtils.min.umd.js +4 -4
- package/dist/CoreUtils.mjs +70 -28
- package/dist/CoreUtils.umd.js +70 -187
- package/dist/lib/DataStoreSerializer.d.ts +13 -8
- package/dist/lib/NanoEmitter.d.ts +16 -1
- package/dist/lib/misc.d.ts +17 -4
- package/dist/lib/text.d.ts +1 -1
- package/dist/lib/types.d.ts +1 -1
- package/package.json +1 -1
package/dist/CoreUtils.umd.js
CHANGED
|
@@ -85,6 +85,7 @@ __export(lib_exports, {
|
|
|
85
85
|
formatNumber: () => formatNumber,
|
|
86
86
|
getCallStack: () => getCallStack,
|
|
87
87
|
getListLength: () => getListLength,
|
|
88
|
+
getterifyObj: () => getterifyObj,
|
|
88
89
|
hexToRgb: () => hexToRgb,
|
|
89
90
|
insertValues: () => insertValues,
|
|
90
91
|
joinArrayReadable: () => joinArrayReadable,
|
|
@@ -397,12 +398,12 @@ var NetworkError = class extends DatedError {
|
|
|
397
398
|
};
|
|
398
399
|
|
|
399
400
|
// lib/misc.ts
|
|
400
|
-
async function consumeGen(valGen) {
|
|
401
|
-
return await (typeof valGen === "function" ? valGen() : valGen);
|
|
401
|
+
async function consumeGen(valGen, ...args) {
|
|
402
|
+
return await (typeof valGen === "function" ? valGen(...args) : valGen);
|
|
402
403
|
}
|
|
403
|
-
async function consumeStringGen(strGen) {
|
|
404
|
+
async function consumeStringGen(strGen, ...args) {
|
|
404
405
|
return typeof strGen === "string" ? strGen : String(
|
|
405
|
-
typeof strGen === "function" ? await strGen() : strGen
|
|
406
|
+
typeof strGen === "function" ? await strGen(...args) : strGen
|
|
406
407
|
);
|
|
407
408
|
}
|
|
408
409
|
async function fetchAdvanced(input, options = {}) {
|
|
@@ -441,6 +442,17 @@ function pauseFor(time, signal, rejectOnAbort = false) {
|
|
|
441
442
|
function pureObj(obj) {
|
|
442
443
|
return Object.assign(/* @__PURE__ */ Object.create(null), obj ?? {});
|
|
443
444
|
}
|
|
445
|
+
function getterifyObj(obj, asCopy = false) {
|
|
446
|
+
const newObj = {};
|
|
447
|
+
for (const key in obj) {
|
|
448
|
+
Object.defineProperty(newObj, key, {
|
|
449
|
+
get: () => obj[key],
|
|
450
|
+
enumerable: true,
|
|
451
|
+
configurable: true
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return asCopy ? structuredClone(newObj) : newObj;
|
|
455
|
+
}
|
|
444
456
|
function setImmediateInterval(callback, interval, signal) {
|
|
445
457
|
let intervalId;
|
|
446
458
|
const cleanup = () => clearInterval(intervalId);
|
|
@@ -799,6 +811,8 @@ var NanoEmitter = class {
|
|
|
799
811
|
events = createNanoEvents();
|
|
800
812
|
eventUnsubscribes = [];
|
|
801
813
|
emitterOptions;
|
|
814
|
+
/** Stores the last arguments for each event listed in `catchUpEvents` */
|
|
815
|
+
catchUpMemory = /* @__PURE__ */ new Map();
|
|
802
816
|
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
803
817
|
constructor(options = {}) {
|
|
804
818
|
this.emitterOptions = {
|
|
@@ -806,6 +820,18 @@ var NanoEmitter = class {
|
|
|
806
820
|
...options
|
|
807
821
|
};
|
|
808
822
|
}
|
|
823
|
+
//#region emitEvent
|
|
824
|
+
/**
|
|
825
|
+
* Emits an event on this instance, bypassing the `publicEmit` guard.
|
|
826
|
+
* Prefer this over `this.events.emit()` in subclasses — it updates catch-up memory
|
|
827
|
+
* for any event listed in `catchUpEvents` so late listeners can still receive the last value.
|
|
828
|
+
*/
|
|
829
|
+
emitEvent(event, ...args) {
|
|
830
|
+
var _a;
|
|
831
|
+
if ((_a = this.emitterOptions.catchUpEvents) == null ? void 0 : _a.includes(event))
|
|
832
|
+
this.catchUpMemory.set(event, args);
|
|
833
|
+
this.events.emit(event, ...args);
|
|
834
|
+
}
|
|
809
835
|
//#region on
|
|
810
836
|
/**
|
|
811
837
|
* Subscribes to an event and calls the callback when it's emitted.
|
|
@@ -838,6 +864,9 @@ var NanoEmitter = class {
|
|
|
838
864
|
};
|
|
839
865
|
unsub = this.events.on(event, cb);
|
|
840
866
|
this.eventUnsubscribes.push(unsub);
|
|
867
|
+
const memory = this.catchUpMemory.get(event);
|
|
868
|
+
if (memory)
|
|
869
|
+
cb(...memory);
|
|
841
870
|
return unsubProxy;
|
|
842
871
|
}
|
|
843
872
|
//#region once
|
|
@@ -860,6 +889,12 @@ var NanoEmitter = class {
|
|
|
860
889
|
* ```
|
|
861
890
|
*/
|
|
862
891
|
once(event, cb) {
|
|
892
|
+
const memory = this.catchUpMemory.get(event);
|
|
893
|
+
if (memory) {
|
|
894
|
+
const args = memory;
|
|
895
|
+
cb == null ? void 0 : cb(...args);
|
|
896
|
+
return Promise.resolve(args);
|
|
897
|
+
}
|
|
863
898
|
return new Promise((resolve) => {
|
|
864
899
|
let unsub;
|
|
865
900
|
const onceProxy = ((...args) => {
|
|
@@ -961,7 +996,7 @@ var NanoEmitter = class {
|
|
|
961
996
|
*/
|
|
962
997
|
emit(event, ...args) {
|
|
963
998
|
if (this.emitterOptions.publicEmit) {
|
|
964
|
-
this.
|
|
999
|
+
this.emitEvent(event, ...args);
|
|
965
1000
|
return true;
|
|
966
1001
|
}
|
|
967
1002
|
return false;
|
|
@@ -1084,7 +1119,7 @@ var DataStore = class extends NanoEmitter {
|
|
|
1084
1119
|
if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
|
|
1085
1120
|
await this.saveDefaultData(false);
|
|
1086
1121
|
const data = this.engine.deepCopy(this.defaultData);
|
|
1087
|
-
this.
|
|
1122
|
+
this.emitEvent("loadData", data);
|
|
1088
1123
|
return data;
|
|
1089
1124
|
}
|
|
1090
1125
|
const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
|
|
@@ -1094,12 +1129,12 @@ var DataStore = class extends NanoEmitter {
|
|
|
1094
1129
|
if (storedFmtVer < this.formatVersion && this.migrations)
|
|
1095
1130
|
parsed = await this.runMigrations(parsed, storedFmtVer);
|
|
1096
1131
|
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
|
|
1097
|
-
this.
|
|
1132
|
+
this.emitEvent("loadData", result);
|
|
1098
1133
|
return result;
|
|
1099
1134
|
} catch (err) {
|
|
1100
1135
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1101
1136
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
1102
|
-
this.
|
|
1137
|
+
this.emitEvent("error", error);
|
|
1103
1138
|
await this.saveDefaultData();
|
|
1104
1139
|
return this.defaultData;
|
|
1105
1140
|
}
|
|
@@ -1121,7 +1156,7 @@ var DataStore = class extends NanoEmitter {
|
|
|
1121
1156
|
const dataCopy = this.engine.deepCopy(data);
|
|
1122
1157
|
if (this.memoryCache) {
|
|
1123
1158
|
this.cachedData = data;
|
|
1124
|
-
this.
|
|
1159
|
+
this.emitEvent("updateDataSync", dataCopy);
|
|
1125
1160
|
}
|
|
1126
1161
|
return new Promise(async (resolve) => {
|
|
1127
1162
|
const results = await Promise.allSettled([
|
|
@@ -1130,11 +1165,11 @@ var DataStore = class extends NanoEmitter {
|
|
|
1130
1165
|
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1131
1166
|
]);
|
|
1132
1167
|
if (results.every((r) => r.status === "fulfilled"))
|
|
1133
|
-
this.
|
|
1168
|
+
this.emitEvent("updateData", dataCopy);
|
|
1134
1169
|
else {
|
|
1135
1170
|
const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1136
1171
|
console.error(error);
|
|
1137
|
-
this.
|
|
1172
|
+
this.emitEvent("error", error);
|
|
1138
1173
|
}
|
|
1139
1174
|
resolve();
|
|
1140
1175
|
});
|
|
@@ -1153,11 +1188,11 @@ var DataStore = class extends NanoEmitter {
|
|
|
1153
1188
|
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1154
1189
|
]);
|
|
1155
1190
|
if (results.every((r) => r.status === "fulfilled"))
|
|
1156
|
-
emitEvent && this.
|
|
1191
|
+
emitEvent && this.emitEvent("setDefaultData", this.defaultData);
|
|
1157
1192
|
else {
|
|
1158
1193
|
const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
|
|
1159
1194
|
console.error(error);
|
|
1160
|
-
this.
|
|
1195
|
+
this.emitEvent("error", error);
|
|
1161
1196
|
}
|
|
1162
1197
|
}
|
|
1163
1198
|
//#region deleteData
|
|
@@ -1174,7 +1209,7 @@ var DataStore = class extends NanoEmitter {
|
|
|
1174
1209
|
this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
|
|
1175
1210
|
]);
|
|
1176
1211
|
await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
|
|
1177
|
-
this.
|
|
1212
|
+
this.emitEvent("deleteData");
|
|
1178
1213
|
}
|
|
1179
1214
|
//#region encodingEnabled
|
|
1180
1215
|
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
@@ -1204,11 +1239,11 @@ var DataStore = class extends NanoEmitter {
|
|
|
1204
1239
|
newData = migRes instanceof Promise ? await migRes : migRes;
|
|
1205
1240
|
lastFmtVer = oldFmtVer = ver;
|
|
1206
1241
|
const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
|
|
1207
|
-
this.
|
|
1242
|
+
this.emitEvent("migrateData", ver, newData, isFinal);
|
|
1208
1243
|
} catch (err) {
|
|
1209
1244
|
const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
1210
|
-
this.
|
|
1211
|
-
this.
|
|
1245
|
+
this.emitEvent("migrationError", ver, migError);
|
|
1246
|
+
this.emitEvent("error", migError);
|
|
1212
1247
|
if (!resetOnError)
|
|
1213
1248
|
throw migError;
|
|
1214
1249
|
await this.saveDefaultData();
|
|
@@ -1222,7 +1257,7 @@ var DataStore = class extends NanoEmitter {
|
|
|
1222
1257
|
this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
|
|
1223
1258
|
]);
|
|
1224
1259
|
const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
|
|
1225
|
-
this.
|
|
1260
|
+
this.emitEvent("updateData", result);
|
|
1226
1261
|
return result;
|
|
1227
1262
|
}
|
|
1228
1263
|
//#region migrateId
|
|
@@ -1252,7 +1287,7 @@ var DataStore = class extends NanoEmitter {
|
|
|
1252
1287
|
this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
|
|
1253
1288
|
this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
|
|
1254
1289
|
]);
|
|
1255
|
-
this.
|
|
1290
|
+
this.emitEvent("migrateId", id, this.id);
|
|
1256
1291
|
}));
|
|
1257
1292
|
}
|
|
1258
1293
|
};
|
|
@@ -1495,15 +1530,20 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1495
1530
|
addChecksum: true,
|
|
1496
1531
|
ensureIntegrity: true,
|
|
1497
1532
|
remapIds: {},
|
|
1533
|
+
stringifyData: true,
|
|
1498
1534
|
...options
|
|
1499
1535
|
};
|
|
1500
1536
|
}
|
|
1501
1537
|
/**
|
|
1502
|
-
* Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1538
|
+
* Calculates the checksum of a string or {@linkcode DataStoreData} object. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
1503
1539
|
* Override this in a subclass if a custom checksum method is needed.
|
|
1504
1540
|
*/
|
|
1505
1541
|
async calcChecksum(input) {
|
|
1506
|
-
|
|
1542
|
+
try {
|
|
1543
|
+
return computeHash(typeof input === "string" ? input : JSON.stringify(input), "SHA-256");
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
throw new Error(`Failed to calculate checksum: ${err.message}`, { cause: err });
|
|
1546
|
+
}
|
|
1507
1547
|
}
|
|
1508
1548
|
/**
|
|
1509
1549
|
* Serializes only a subset of the data stores into a string.
|
|
@@ -1518,7 +1558,7 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1518
1558
|
for (const storeInst of filteredStores) {
|
|
1519
1559
|
const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
|
|
1520
1560
|
const rawData = storeInst.memoryCache ? storeInst.getData() : await storeInst.loadData();
|
|
1521
|
-
const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : JSON.stringify(rawData);
|
|
1561
|
+
const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : this.options.stringifyData ? JSON.stringify(rawData) : rawData;
|
|
1522
1562
|
serData.push({
|
|
1523
1563
|
id: storeInst.id,
|
|
1524
1564
|
data,
|
|
@@ -1564,11 +1604,11 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
1564
1604
|
Expected: ${storeData.checksum}
|
|
1565
1605
|
Has: ${checksum}`);
|
|
1566
1606
|
}
|
|
1567
|
-
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? await storeInst.decodeData[1](storeData.data) : storeData.data;
|
|
1607
|
+
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? await storeInst.decodeData[1](typeof storeData.data === "string" ? storeData.data : JSON.stringify(storeData.data)) : storeData.data;
|
|
1568
1608
|
if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
|
|
1569
|
-
await storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false);
|
|
1609
|
+
await storeInst.runMigrations(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData, Number(storeData.formatVersion), false);
|
|
1570
1610
|
else
|
|
1571
|
-
await storeInst.setData(JSON.parse(decodedData));
|
|
1611
|
+
await storeInst.setData(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData);
|
|
1572
1612
|
}
|
|
1573
1613
|
}
|
|
1574
1614
|
/**
|
|
@@ -1637,6 +1677,8 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1637
1677
|
this.timeout = timeout;
|
|
1638
1678
|
this.type = type;
|
|
1639
1679
|
}
|
|
1680
|
+
timeout;
|
|
1681
|
+
type;
|
|
1640
1682
|
/** All registered listener functions and the time they were attached */
|
|
1641
1683
|
listeners = [];
|
|
1642
1684
|
/** The currently active timeout */
|
|
@@ -1664,7 +1706,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1664
1706
|
//#region timeout
|
|
1665
1707
|
/** Sets the timeout for the debouncer */
|
|
1666
1708
|
setTimeout(timeout) {
|
|
1667
|
-
this.
|
|
1709
|
+
this.emitEvent("change", this.timeout = timeout, this.type);
|
|
1668
1710
|
}
|
|
1669
1711
|
/** Returns the current timeout */
|
|
1670
1712
|
getTimeout() {
|
|
@@ -1677,7 +1719,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1677
1719
|
//#region type
|
|
1678
1720
|
/** Sets the edge type for the debouncer */
|
|
1679
1721
|
setType(type) {
|
|
1680
|
-
this.
|
|
1722
|
+
this.emitEvent("change", this.timeout, this.type = type);
|
|
1681
1723
|
}
|
|
1682
1724
|
/** Returns the current edge type */
|
|
1683
1725
|
getType() {
|
|
@@ -1688,7 +1730,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
1688
1730
|
call(...args) {
|
|
1689
1731
|
const cl = (...a) => {
|
|
1690
1732
|
this.queuedCall = void 0;
|
|
1691
|
-
this.
|
|
1733
|
+
this.emitEvent("call", ...a);
|
|
1692
1734
|
this.listeners.forEach((l) => l.call(this, ...a));
|
|
1693
1735
|
};
|
|
1694
1736
|
const setRepeatTimeout = () => {
|
|
@@ -1728,166 +1770,7 @@ function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
|
1728
1770
|
func.debouncer = debouncer;
|
|
1729
1771
|
return func;
|
|
1730
1772
|
}
|
|
1731
|
-
sistent data of the DataStore instances into the in-memory cache.
|
|
1732
|
-
* Also triggers the migration process if the data format has changed.
|
|
1733
|
-
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be loaded
|
|
1734
|
-
* @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }`
|
|
1735
|
-
*/
|
|
1736
|
-
loadStoresData(stores) {
|
|
1737
|
-
return __async(this, null, function* () {
|
|
1738
|
-
return Promise.allSettled(
|
|
1739
|
-
this.getStoresFiltered(stores).map((store) => __async(this, null, function* () {
|
|
1740
|
-
return {
|
|
1741
|
-
id: store.id,
|
|
1742
|
-
data: yield store.loadData()
|
|
1743
|
-
};
|
|
1744
|
-
}))
|
|
1745
|
-
);
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1748
|
-
/**
|
|
1749
|
-
* Resets the persistent and in-memory data of the DataStore instances to their default values.
|
|
1750
|
-
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
1751
|
-
*/
|
|
1752
|
-
resetStoresData(stores) {
|
|
1753
|
-
return __async(this, null, function* () {
|
|
1754
|
-
return Promise.allSettled(
|
|
1755
|
-
this.getStoresFiltered(stores).map((store) => store.saveDefaultData())
|
|
1756
|
-
);
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Deletes the persistent data of the DataStore instances.
|
|
1761
|
-
* Leaves the in-memory data untouched.
|
|
1762
|
-
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
1763
|
-
*/
|
|
1764
|
-
deleteStoresData(stores) {
|
|
1765
|
-
return __async(this, null, function* () {
|
|
1766
|
-
return Promise.allSettled(
|
|
1767
|
-
this.getStoresFiltered(stores).map((store) => store.deleteData())
|
|
1768
|
-
);
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
/** Checks if a given value is an array of SerializedDataStore objects */
|
|
1772
|
-
static isSerializedDataStoreObjArray(obj) {
|
|
1773
|
-
return Array.isArray(obj) && obj.every((o) => typeof o === "object" && o !== null && "id" in o && "data" in o && "formatVersion" in o && "encoded" in o);
|
|
1774
|
-
}
|
|
1775
|
-
/** Checks if a given value is a SerializedDataStore object */
|
|
1776
|
-
static isSerializedDataStoreObj(obj) {
|
|
1777
|
-
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
1778
|
-
}
|
|
1779
|
-
/** Returns the DataStore instances whose IDs match the provided array or function */
|
|
1780
|
-
getStoresFiltered(stores) {
|
|
1781
|
-
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
1782
|
-
}
|
|
1783
|
-
};
|
|
1784
1773
|
|
|
1785
|
-
// lib/Debouncer.ts
|
|
1786
|
-
var Debouncer = class extends NanoEmitter {
|
|
1787
|
-
/**
|
|
1788
|
-
* Creates a new debouncer with the specified timeout and edge type.
|
|
1789
|
-
* @param timeout Timeout in milliseconds between letting through calls - defaults to 200
|
|
1790
|
-
* @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"
|
|
1791
|
-
*/
|
|
1792
|
-
constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1793
|
-
super(nanoEmitterOptions);
|
|
1794
|
-
this.timeout = timeout;
|
|
1795
|
-
this.type = type;
|
|
1796
|
-
/** All registered listener functions and the time they were attached */
|
|
1797
|
-
__publicField(this, "listeners", []);
|
|
1798
|
-
/** The currently active timeout */
|
|
1799
|
-
__publicField(this, "activeTimeout");
|
|
1800
|
-
/** The latest queued call */
|
|
1801
|
-
__publicField(this, "queuedCall");
|
|
1802
|
-
}
|
|
1803
|
-
//#region listeners
|
|
1804
|
-
/** Adds a listener function that will be called on timeout */
|
|
1805
|
-
addListener(fn) {
|
|
1806
|
-
this.listeners.push(fn);
|
|
1807
|
-
}
|
|
1808
|
-
/** Removes the listener with the specified function reference */
|
|
1809
|
-
removeListener(fn) {
|
|
1810
|
-
const idx = this.listeners.findIndex((l) => l === fn);
|
|
1811
|
-
idx !== -1 && this.listeners.splice(idx, 1);
|
|
1812
|
-
}
|
|
1813
|
-
/** Removes all listeners */
|
|
1814
|
-
removeAllListeners() {
|
|
1815
|
-
this.listeners = [];
|
|
1816
|
-
}
|
|
1817
|
-
/** Returns all registered listeners */
|
|
1818
|
-
getListeners() {
|
|
1819
|
-
return this.listeners;
|
|
1820
|
-
}
|
|
1821
|
-
//#region timeout
|
|
1822
|
-
/** Sets the timeout for the debouncer */
|
|
1823
|
-
setTimeout(timeout) {
|
|
1824
|
-
this.events.emit("change", this.timeout = timeout, this.type);
|
|
1825
|
-
}
|
|
1826
|
-
/** Returns the current timeout */
|
|
1827
|
-
getTimeout() {
|
|
1828
|
-
return this.timeout;
|
|
1829
|
-
}
|
|
1830
|
-
/** Whether the timeout is currently active, meaning any latest call to the {@linkcode call()} method will be queued */
|
|
1831
|
-
isTimeoutActive() {
|
|
1832
|
-
return typeof this.activeTimeout !== "undefined";
|
|
1833
|
-
}
|
|
1834
|
-
//#region type
|
|
1835
|
-
/** Sets the edge type for the debouncer */
|
|
1836
|
-
setType(type) {
|
|
1837
|
-
this.events.emit("change", this.timeout, this.type = type);
|
|
1838
|
-
}
|
|
1839
|
-
/** Returns the current edge type */
|
|
1840
|
-
getType() {
|
|
1841
|
-
return this.type;
|
|
1842
|
-
}
|
|
1843
|
-
//#region call
|
|
1844
|
-
/** Use this to call the debouncer with the specified arguments that will be passed to all listener functions registered with {@linkcode addListener()} */
|
|
1845
|
-
call(...args) {
|
|
1846
|
-
const cl = (...a) => {
|
|
1847
|
-
this.queuedCall = void 0;
|
|
1848
|
-
this.events.emit("call", ...a);
|
|
1849
|
-
this.listeners.forEach((l) => l.call(this, ...a));
|
|
1850
|
-
};
|
|
1851
|
-
const setRepeatTimeout = () => {
|
|
1852
|
-
this.activeTimeout = setTimeout(() => {
|
|
1853
|
-
if (this.queuedCall) {
|
|
1854
|
-
this.queuedCall();
|
|
1855
|
-
setRepeatTimeout();
|
|
1856
|
-
} else
|
|
1857
|
-
this.activeTimeout = void 0;
|
|
1858
|
-
}, this.timeout);
|
|
1859
|
-
};
|
|
1860
|
-
switch (this.type) {
|
|
1861
|
-
case "immediate":
|
|
1862
|
-
if (typeof this.activeTimeout === "undefined") {
|
|
1863
|
-
cl(...args);
|
|
1864
|
-
setRepeatTimeout();
|
|
1865
|
-
} else
|
|
1866
|
-
this.queuedCall = () => cl(...args);
|
|
1867
|
-
break;
|
|
1868
|
-
case "idle":
|
|
1869
|
-
if (this.activeTimeout)
|
|
1870
|
-
clearTimeout(this.activeTimeout);
|
|
1871
|
-
this.activeTimeout = setTimeout(() => {
|
|
1872
|
-
cl(...args);
|
|
1873
|
-
this.activeTimeout = void 0;
|
|
1874
|
-
}, this.timeout);
|
|
1875
|
-
break;
|
|
1876
|
-
default:
|
|
1877
|
-
throw new TypeError(`Invalid debouncer type: ${this.type}`);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
};
|
|
1881
|
-
function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
|
|
1882
|
-
const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
|
|
1883
|
-
debouncer.addListener(fn);
|
|
1884
|
-
const func = ((...args) => debouncer.call(...args));
|
|
1885
|
-
func.debouncer = debouncer;
|
|
1886
|
-
return func;
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
if(__exports != exports)module.exports = exports;return module.exports}));
|
|
1890
|
-
//# sourceMappingURL=CoreUtils.umd.js.map
|
|
1891
1774
|
|
|
1892
1775
|
|
|
1893
1776
|
if (typeof module.exports == "object" && typeof exports == "object") {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module DataStoreSerializer
|
|
3
3
|
* This module contains the DataStoreSerializer class, which allows you to import and export serialized DataStore data - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastoreserializer)
|
|
4
4
|
*/
|
|
5
|
-
import type { DataStore } from "./DataStore.ts";
|
|
5
|
+
import type { DataStore, DataStoreData } from "./DataStore.ts";
|
|
6
6
|
/** Options for the DataStoreSerializer class */
|
|
7
7
|
export type DataStoreSerializerOptions = {
|
|
8
8
|
/** Whether to add a checksum to the exported data. Defaults to `true` */
|
|
@@ -11,13 +11,18 @@ export type DataStoreSerializerOptions = {
|
|
|
11
11
|
ensureIntegrity?: boolean;
|
|
12
12
|
/** If provided, all stores with an ID in the value's array will be remapped to the key's ID when deserialization is called. If they don't match a DataStore instance's ID, nothing will happen. */
|
|
13
13
|
remapIds?: Record<string, string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Controls the type of the `data` property on {@linkcode SerializedDataStore} objects.
|
|
16
|
+
* When this is set to `false`, `data` will be the raw data object instead of a string, regardless of whether {@linkcode DataStoreSerializer.serialize()} or {@linkcode DataStoreSerializer.serializePartial()} are called with their `stringify` / `stringified` parameter set to `true` or `false`.
|
|
17
|
+
*/
|
|
18
|
+
stringifyData?: boolean;
|
|
14
19
|
};
|
|
15
20
|
/** Meta object and serialized data of a DataStore instance */
|
|
16
|
-
export type SerializedDataStore = {
|
|
21
|
+
export type SerializedDataStore<TData extends string | DataStoreData = string> = {
|
|
17
22
|
/** The ID of the DataStore instance */
|
|
18
23
|
id: string;
|
|
19
24
|
/** The serialized data */
|
|
20
|
-
data:
|
|
25
|
+
data: TData;
|
|
21
26
|
/** The format version of the data */
|
|
22
27
|
formatVersion: number;
|
|
23
28
|
/** Whether the data is encoded */
|
|
@@ -48,10 +53,10 @@ export declare class DataStoreSerializer {
|
|
|
48
53
|
protected options: Required<DataStoreSerializerOptions>;
|
|
49
54
|
constructor(stores: DataStore<any, boolean>[], options?: DataStoreSerializerOptions);
|
|
50
55
|
/**
|
|
51
|
-
* Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
56
|
+
* Calculates the checksum of a string or {@linkcode DataStoreData} object. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
|
|
52
57
|
* Override this in a subclass if a custom checksum method is needed.
|
|
53
58
|
*/
|
|
54
|
-
protected calcChecksum(input: string): Promise<string>;
|
|
59
|
+
protected calcChecksum(input: string | DataStoreData): Promise<string>;
|
|
55
60
|
/**
|
|
56
61
|
* Serializes only a subset of the data stores into a string.
|
|
57
62
|
* @param stores An array of store IDs or functions that take a store ID and return a boolean
|
|
@@ -65,14 +70,14 @@ export declare class DataStoreSerializer {
|
|
|
65
70
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
66
71
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
67
72
|
*/
|
|
68
|
-
serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore[]>;
|
|
73
|
+
serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore<string | DataStoreData>[]>;
|
|
69
74
|
/**
|
|
70
75
|
* Serializes only a subset of the data stores into a string.
|
|
71
76
|
* @param stores An array of store IDs or functions that take a store ID and return a boolean
|
|
72
77
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
73
78
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
74
79
|
*/
|
|
75
|
-
serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: boolean): Promise<string | SerializedDataStore[]>;
|
|
80
|
+
serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: boolean): Promise<string | SerializedDataStore<string | DataStoreData>[]>;
|
|
76
81
|
/**
|
|
77
82
|
* Serializes the data stores into a string.
|
|
78
83
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
@@ -84,7 +89,7 @@ export declare class DataStoreSerializer {
|
|
|
84
89
|
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
85
90
|
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
86
91
|
*/
|
|
87
|
-
serialize(useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore[]>;
|
|
92
|
+
serialize(useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore<string | DataStoreData>[]>;
|
|
88
93
|
/**
|
|
89
94
|
* Deserializes the data exported via {@linkcode serialize()} and imports only a subset into the DataStore instances.
|
|
90
95
|
* Also triggers the migration process if the data format has changed.
|
|
@@ -7,6 +7,13 @@ import type { Prettify } from "./types.ts";
|
|
|
7
7
|
export interface NanoEmitterOptions {
|
|
8
8
|
/** If set to true, allows emitting events through the public method emit() */
|
|
9
9
|
publicEmit: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* When provided, the emitter will remember the last arguments of each listed event.
|
|
12
|
+
* Any listener attached via `on()` or `once()` after the event has already fired will
|
|
13
|
+
* be immediately called / resolved with the cached arguments (catch-up behaviour).
|
|
14
|
+
* Only works for emissions that go through the public `emit()` or the protected `emitEvent()` method.
|
|
15
|
+
*/
|
|
16
|
+
catchUpEvents?: PropertyKey[];
|
|
10
17
|
}
|
|
11
18
|
type NanoEmitterOnMultiTriggerOptions<TEvtMap extends EventsMap, TKey extends keyof TEvtMap = keyof TEvtMap> = {
|
|
12
19
|
/** Calls the callback when one of the given events is emitted. Either one of or both of `oneOf` and `allOf` need to be set. If both are set, they behave like an "AND" condition. */
|
|
@@ -25,14 +32,22 @@ export type NanoEmitterOnMultiOptions<TEvtMap extends EventsMap, TKey extends ke
|
|
|
25
32
|
} & NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey> & (Pick<Required<NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey>>, "oneOf"> | Pick<Required<NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey>>, "allOf">)>;
|
|
26
33
|
/**
|
|
27
34
|
* Class that can be extended or instantiated by itself to create a lightweight event emitter with helper methods and a strongly typed event map.
|
|
28
|
-
* If extended from,
|
|
35
|
+
* If extended from, prefer using `this.emitEvent()` over `this.events.emit()` — it updates the catch-up memory for any events listed in `catchUpEvents`.
|
|
29
36
|
*/
|
|
30
37
|
export declare class NanoEmitter<TEvtMap extends EventsMap = DefaultEvents> {
|
|
31
38
|
protected readonly events: Emitter<TEvtMap>;
|
|
32
39
|
protected eventUnsubscribes: Unsubscribe[];
|
|
33
40
|
protected emitterOptions: NanoEmitterOptions;
|
|
41
|
+
/** Stores the last arguments for each event listed in `catchUpEvents` */
|
|
42
|
+
protected catchUpMemory: Map<PropertyKey, unknown[]>;
|
|
34
43
|
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
35
44
|
constructor(options?: Partial<NanoEmitterOptions>);
|
|
45
|
+
/**
|
|
46
|
+
* Emits an event on this instance, bypassing the `publicEmit` guard.
|
|
47
|
+
* Prefer this over `this.events.emit()` in subclasses — it updates catch-up memory
|
|
48
|
+
* for any event listed in `catchUpEvents` so late listeners can still receive the last value.
|
|
49
|
+
*/
|
|
50
|
+
protected emitEvent<TKey extends keyof TEvtMap>(event: TKey, ...args: Parameters<TEvtMap[TKey]>): void;
|
|
36
51
|
/**
|
|
37
52
|
* Subscribes to an event and calls the callback when it's emitted.
|
|
38
53
|
* @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 "_")
|
package/dist/lib/misc.d.ts
CHANGED
|
@@ -6,24 +6,32 @@ import type { ListLike, Prettify, Stringifiable } from "./types.ts";
|
|
|
6
6
|
/**
|
|
7
7
|
* A ValueGen value is either its type, a promise that resolves to its type, or a function that returns its type, either synchronous or asynchronous.
|
|
8
8
|
* ValueGen allows for the utmost flexibility when applied to any type, as long as {@linkcode consumeGen()} is used to get the final value.
|
|
9
|
+
* The optional `TFn` parameter allows specifying the function variant's signature, enabling parametrized consumption via {@linkcode consumeGen()}.
|
|
9
10
|
* @template TValueType The type of the value that the ValueGen should yield
|
|
11
|
+
* @template TFn The function signature for the function variant - defaults to a no-arg function for backwards compatibility
|
|
10
12
|
*/
|
|
11
|
-
export type ValueGen<TValueType
|
|
13
|
+
export type ValueGen<TValueType, TFn extends (...args: any[]) => TValueType | Promise<TValueType> = () => TValueType | Promise<TValueType>> = TValueType | Promise<TValueType> | TFn;
|
|
12
14
|
/**
|
|
13
15
|
* Turns a {@linkcode ValueGen} into its final value.
|
|
16
|
+
* @param args Optional arguments forwarded to the function, if the ValueGen is a parametrized function
|
|
14
17
|
* @template TValueType The type of the value that the ValueGen should yield
|
|
18
|
+
* @template TFn The function signature for the function variant - inferred automatically
|
|
15
19
|
*/
|
|
16
|
-
export declare function consumeGen<TValueType>(valGen: ValueGen<TValueType>): Promise<TValueType>;
|
|
20
|
+
export declare function consumeGen<TValueType, TFn extends (...args: any[]) => TValueType | Promise<TValueType> = () => TValueType | Promise<TValueType>>(valGen: ValueGen<TValueType, TFn>, ...args: Parameters<TFn>): Promise<TValueType>;
|
|
17
21
|
/**
|
|
18
22
|
* A StringGen value is either a string, anything that can be converted to a string, or a function that returns one of the previous two, either synchronous or asynchronous, or a promise that returns a string.
|
|
19
23
|
* StringGen allows for the utmost flexibility when dealing with strings, as long as {@linkcode consumeStringGen()} is used to get the final string.
|
|
24
|
+
* The optional `TFn` parameter allows specifying the function variant's signature, enabling parametrized consumption via {@linkcode consumeStringGen()}.
|
|
25
|
+
* @template TFn The function signature for the function variant - defaults to a no-arg function for backwards compatibility
|
|
20
26
|
*/
|
|
21
|
-
export type StringGen = ValueGen<Stringifiable>;
|
|
27
|
+
export type StringGen<TFn extends (...args: any[]) => Stringifiable | Promise<Stringifiable> = () => Stringifiable | Promise<Stringifiable>> = ValueGen<Stringifiable, TFn>;
|
|
22
28
|
/**
|
|
23
29
|
* Turns a {@linkcode StringGen} into its final string value.
|
|
30
|
+
* @param args Optional arguments forwarded to the function, if the StringGen is a parametrized function
|
|
24
31
|
* @template TStrUnion The union of strings that the StringGen should yield - this allows for finer type control compared to {@linkcode consumeGen()}
|
|
32
|
+
* @template TFn The function signature for the function variant - inferred automatically
|
|
25
33
|
*/
|
|
26
|
-
export declare function consumeStringGen<TStrUnion extends string>(strGen: StringGen): Promise<TStrUnion>;
|
|
34
|
+
export declare function consumeStringGen<TStrUnion extends string, TFn extends (...args: any[]) => Stringifiable | Promise<Stringifiable> = () => Stringifiable | Promise<Stringifiable>>(strGen: StringGen<TFn>, ...args: Parameters<TFn>): Promise<TStrUnion>;
|
|
27
35
|
/** Options for the `fetchAdvanced()` function */
|
|
28
36
|
export type FetchAdvancedOpts = Prettify<Partial<{
|
|
29
37
|
/** Timeout in milliseconds after which the fetch call will be canceled with an AbortController signal */
|
|
@@ -50,6 +58,11 @@ export declare function pauseFor(time: number, signal?: AbortSignal, rejectOnAbo
|
|
|
50
58
|
* If no object is passed, it will return an empty object without prototype chain.
|
|
51
59
|
*/
|
|
52
60
|
export declare function pureObj<TObj extends object>(obj?: TObj): TObj;
|
|
61
|
+
/**
|
|
62
|
+
* Transforms an object's enumerable, own, string-keyed properties into getters that return the original values.
|
|
63
|
+
* Set `asCopy` to true to return a new object with the getterified properties instead of a live view of the original object.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getterifyObj<TObj extends object>(obj: TObj, asCopy?: boolean): TObj;
|
|
53
66
|
/**
|
|
54
67
|
* Works similarly to `setInterval()`, but the callback is also called immediately and can be aborted with an `AbortSignal`.
|
|
55
68
|
* Uses `setInterval()` internally, which might cause overlapping calls if the callback's synchronous execution takes longer than the given interval time.
|
package/dist/lib/text.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ export type TableLineCharset = Record<TableLineStyle, TableLineStyleChars>;
|
|
|
99
99
|
export declare const defaultTableLineCharset: TableLineCharset;
|
|
100
100
|
/**
|
|
101
101
|
* Creates an ASCII table string from the given rows and options.
|
|
102
|
-
* Supports
|
|
102
|
+
* Supports ANSI color codes in cell content: they are ignored for width calculation, included in the final output, and handled correctly during truncation (escape sequences are never split; any open color code is closed with a reset).
|
|
103
103
|
* @param rows Array of tuples, where each tuple represents a row and its values. The first tuple is used to determine the column count.
|
|
104
104
|
* @param options Object with options for customizing the table output, such as column alignment, truncation, padding and line styles.
|
|
105
105
|
*/
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type NonEmptyString<TString extends string> = TString extends "" ? never
|
|
|
35
35
|
/** String constant that decides which set of number formatting options to use */
|
|
36
36
|
export type NumberFormat = "short" | "long";
|
|
37
37
|
/**
|
|
38
|
-
* Makes the structure of a type more readable by expanding
|
|
38
|
+
* Makes the structure of a type more readable by expanding other types merged via intersections.
|
|
39
39
|
* This can be useful for debugging or for improving the readability of complex types.
|
|
40
40
|
*/
|
|
41
41
|
export type Prettify<T> = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sv443-network/coreutils",
|
|
3
3
|
"libName": "@sv443-network/coreutils",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.6.0",
|
|
5
5
|
"description": "Cross-platform, general-purpose, JavaScript core library for Node, Deno and the browser. Intended to be used in conjunction with `@sv443-network/userutils` and `@sv443-network/djsutils`, but can be used independently as well.",
|
|
6
6
|
"main": "dist/CoreUtils.cjs",
|
|
7
7
|
"module": "dist/CoreUtils.mjs",
|