@sv443-network/userutils 9.0.4 → 9.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 +34 -0
- package/README-summary.md +38 -16
- package/README.md +25 -7
- package/dist/index.cjs +157 -58
- package/dist/index.global.js +159 -60
- package/dist/index.js +148 -59
- package/dist/lib/DataStoreSerializer.d.ts +16 -6
- package/dist/lib/dom.d.ts +19 -0
- package/dist/lib/errors.d.ts +21 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/math.d.ts +52 -2
- package/dist/lib/misc.d.ts +22 -7
- package/dist/lib/translation.d.ts +23 -16
- package/dist/lib/types.d.ts +8 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -97,13 +97,21 @@ function randRange(...args) {
|
|
|
97
97
|
} else
|
|
98
98
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
99
99
|
}
|
|
100
|
-
function digitCount(num) {
|
|
100
|
+
function digitCount(num, withDecimals = true) {
|
|
101
101
|
num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
|
|
102
102
|
if (typeof num === "number" && isNaN(num))
|
|
103
103
|
return NaN;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
const [intPart, decPart] = num.toString().split(".");
|
|
105
|
+
const intDigits = intPart === "0" ? 1 : Math.floor(Math.log10(Math.abs(Number(intPart))) + 1);
|
|
106
|
+
const decDigits = withDecimals && decPart ? decPart.length : 0;
|
|
107
|
+
return intDigits + decDigits;
|
|
108
|
+
}
|
|
109
|
+
function roundFixed(num, fractionDigits) {
|
|
110
|
+
const scale = 10 ** fractionDigits;
|
|
111
|
+
return Math.round(num * scale) / scale;
|
|
112
|
+
}
|
|
113
|
+
function bitSetHas(bitSet, checkVal) {
|
|
114
|
+
return (bitSet & checkVal) === checkVal;
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
// lib/array.ts
|
|
@@ -128,7 +136,7 @@ function randomizeArray(array) {
|
|
|
128
136
|
if (array.length === 0)
|
|
129
137
|
return retArray;
|
|
130
138
|
for (let i = retArray.length - 1; i > 0; i--) {
|
|
131
|
-
const j = Math.floor(
|
|
139
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
132
140
|
[retArray[i], retArray[j]] = [retArray[j], retArray[i]];
|
|
133
141
|
}
|
|
134
142
|
return retArray;
|
|
@@ -171,10 +179,10 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
171
179
|
else if (color.startsWith("rgb")) {
|
|
172
180
|
const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
|
|
173
181
|
if (!rgbValues)
|
|
174
|
-
throw new
|
|
182
|
+
throw new TypeError("Invalid RGB/RGBA color format");
|
|
175
183
|
[r, g, b, a] = rgbValues;
|
|
176
184
|
} else
|
|
177
|
-
throw new
|
|
185
|
+
throw new TypeError("Unsupported color format");
|
|
178
186
|
[r, g, b] = darkenRgb(r, g, b, percent);
|
|
179
187
|
if (isHexCol)
|
|
180
188
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
@@ -183,10 +191,39 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
183
191
|
else if (color.startsWith("rgb"))
|
|
184
192
|
return `rgb(${r}, ${g}, ${b})`;
|
|
185
193
|
else
|
|
186
|
-
throw new
|
|
194
|
+
throw new TypeError("Unsupported color format");
|
|
187
195
|
}
|
|
188
196
|
|
|
197
|
+
// lib/errors.ts
|
|
198
|
+
var UUError = class extends Error {
|
|
199
|
+
constructor(message, options) {
|
|
200
|
+
super(message, options);
|
|
201
|
+
__publicField(this, "date");
|
|
202
|
+
this.date = /* @__PURE__ */ new Date();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var ChecksumMismatchError = class extends UUError {
|
|
206
|
+
constructor(message, options) {
|
|
207
|
+
super(message, options);
|
|
208
|
+
this.name = "ChecksumMismatchError";
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var MigrationError = class extends UUError {
|
|
212
|
+
constructor(message, options) {
|
|
213
|
+
super(message, options);
|
|
214
|
+
this.name = "MigrationError";
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var PlatformError = class extends UUError {
|
|
218
|
+
constructor(message, options) {
|
|
219
|
+
super(message, options);
|
|
220
|
+
this.name = "PlatformError";
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
189
224
|
// lib/dom.ts
|
|
225
|
+
var domReady = false;
|
|
226
|
+
document.addEventListener("DOMContentLoaded", () => domReady = true);
|
|
190
227
|
function getUnsafeWindow() {
|
|
191
228
|
try {
|
|
192
229
|
return unsafeWindow;
|
|
@@ -242,8 +279,8 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
242
279
|
}
|
|
243
280
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
244
281
|
var _a;
|
|
245
|
-
if ((
|
|
246
|
-
throw new
|
|
282
|
+
if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
283
|
+
throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
|
|
247
284
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
248
285
|
if (isNaN(Error.stackTraceLimit))
|
|
249
286
|
Error.stackTraceLimit = 100;
|
|
@@ -325,6 +362,36 @@ function setInnerHtmlUnsafe(element, html) {
|
|
|
325
362
|
element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
326
363
|
return element;
|
|
327
364
|
}
|
|
365
|
+
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
366
|
+
const el = element ? typeof element === "function" ? element() : element : document.createElement("span");
|
|
367
|
+
if (hideOffscreen) {
|
|
368
|
+
el.style.position = "absolute";
|
|
369
|
+
el.style.left = "-9999px";
|
|
370
|
+
el.style.top = "-9999px";
|
|
371
|
+
el.style.zIndex = "-9999";
|
|
372
|
+
}
|
|
373
|
+
el.classList.add("_uu_probe_element");
|
|
374
|
+
parentElement.appendChild(el);
|
|
375
|
+
const style = window.getComputedStyle(el);
|
|
376
|
+
const result = probeStyle(style, el);
|
|
377
|
+
setTimeout(() => el.remove(), 1);
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
function isDomLoaded() {
|
|
381
|
+
return domReady;
|
|
382
|
+
}
|
|
383
|
+
function onDomLoad(cb) {
|
|
384
|
+
return new Promise((res) => {
|
|
385
|
+
if (domReady) {
|
|
386
|
+
cb == null ? undefined : cb();
|
|
387
|
+
res();
|
|
388
|
+
} else
|
|
389
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
390
|
+
cb == null ? undefined : cb();
|
|
391
|
+
res();
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
}
|
|
328
395
|
|
|
329
396
|
// lib/crypto.ts
|
|
330
397
|
function compress(input, compressionFormat, outputType = "string") {
|
|
@@ -372,6 +439,8 @@ function computeHash(input, algorithm = "SHA-256") {
|
|
|
372
439
|
});
|
|
373
440
|
}
|
|
374
441
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
442
|
+
if (radix < 2 || radix > 36)
|
|
443
|
+
throw new RangeError("The radix argument must be between 2 and 36");
|
|
375
444
|
let arr = [];
|
|
376
445
|
const caseArr = randomCase ? [0, 1] : [0];
|
|
377
446
|
if (enhancedEntropy) {
|
|
@@ -545,8 +614,7 @@ var DataStore = class {
|
|
|
545
614
|
lastFmtVer = oldFmtVer = ver;
|
|
546
615
|
} catch (err) {
|
|
547
616
|
if (!resetOnError)
|
|
548
|
-
throw new
|
|
549
|
-
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
617
|
+
throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
550
618
|
yield this.saveDefaultData();
|
|
551
619
|
return this.getData();
|
|
552
620
|
}
|
|
@@ -607,7 +675,6 @@ var DataStore = class {
|
|
|
607
675
|
return JSON.parse(decRes != null ? decRes : data);
|
|
608
676
|
});
|
|
609
677
|
}
|
|
610
|
-
//#region misc
|
|
611
678
|
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
612
679
|
deepCopy(obj) {
|
|
613
680
|
return JSON.parse(JSON.stringify(obj));
|
|
@@ -659,7 +726,7 @@ var DataStore = class {
|
|
|
659
726
|
};
|
|
660
727
|
|
|
661
728
|
// lib/DataStoreSerializer.ts
|
|
662
|
-
var DataStoreSerializer = class {
|
|
729
|
+
var DataStoreSerializer = class _DataStoreSerializer {
|
|
663
730
|
constructor(stores, options = {}) {
|
|
664
731
|
__publicField(this, "stores");
|
|
665
732
|
__publicField(this, "options");
|
|
@@ -677,27 +744,25 @@ var DataStoreSerializer = class {
|
|
|
677
744
|
return computeHash(input, "SHA-256");
|
|
678
745
|
});
|
|
679
746
|
}
|
|
680
|
-
/**
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
id: storeInst.id,
|
|
687
|
-
data,
|
|
688
|
-
formatVersion: storeInst.formatVersion,
|
|
689
|
-
encoded: storeInst.encodingEnabled(),
|
|
690
|
-
checksum
|
|
691
|
-
};
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
/** Serializes the data stores into a string */
|
|
695
|
-
serialize() {
|
|
747
|
+
/**
|
|
748
|
+
* Serializes the data stores into a string.
|
|
749
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
750
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
751
|
+
*/
|
|
752
|
+
serialize(useEncoding = true, stringified = true) {
|
|
696
753
|
return __async(this, null, function* () {
|
|
697
754
|
const serData = [];
|
|
698
|
-
for (const
|
|
699
|
-
|
|
700
|
-
|
|
755
|
+
for (const storeInst of this.stores) {
|
|
756
|
+
const data = useEncoding && storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
757
|
+
serData.push({
|
|
758
|
+
id: storeInst.id,
|
|
759
|
+
data,
|
|
760
|
+
formatVersion: storeInst.formatVersion,
|
|
761
|
+
encoded: useEncoding && storeInst.encodingEnabled(),
|
|
762
|
+
checksum: this.options.addChecksum ? yield this.calcChecksum(data) : undefined
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return stringified ? JSON.stringify(serData) : serData;
|
|
701
766
|
});
|
|
702
767
|
}
|
|
703
768
|
/**
|
|
@@ -706,7 +771,9 @@ var DataStoreSerializer = class {
|
|
|
706
771
|
*/
|
|
707
772
|
deserialize(serializedData) {
|
|
708
773
|
return __async(this, null, function* () {
|
|
709
|
-
const deserStores = JSON.parse(serializedData);
|
|
774
|
+
const deserStores = typeof serializedData === "string" ? JSON.parse(serializedData) : serializedData;
|
|
775
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
|
|
776
|
+
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
710
777
|
for (const storeData of deserStores) {
|
|
711
778
|
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
712
779
|
if (!storeInst)
|
|
@@ -714,7 +781,7 @@ var DataStoreSerializer = class {
|
|
|
714
781
|
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
715
782
|
const checksum = yield this.calcChecksum(storeData.data);
|
|
716
783
|
if (checksum !== storeData.checksum)
|
|
717
|
-
throw new
|
|
784
|
+
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
718
785
|
Expected: ${storeData.checksum}
|
|
719
786
|
Has: ${checksum}`);
|
|
720
787
|
}
|
|
@@ -758,6 +825,10 @@ Has: ${checksum}`);
|
|
|
758
825
|
return Promise.allSettled(this.stores.map((store) => store.deleteData()));
|
|
759
826
|
});
|
|
760
827
|
}
|
|
828
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
829
|
+
static isSerializedDataStore(obj) {
|
|
830
|
+
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
831
|
+
}
|
|
761
832
|
};
|
|
762
833
|
var NanoEmitter = class {
|
|
763
834
|
constructor(options = {}) {
|
|
@@ -897,7 +968,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
897
968
|
}, this.timeout);
|
|
898
969
|
break;
|
|
899
970
|
default:
|
|
900
|
-
throw new
|
|
971
|
+
throw new TypeError(`Invalid debouncer type: ${this.type}`);
|
|
901
972
|
}
|
|
902
973
|
}
|
|
903
974
|
};
|
|
@@ -1329,10 +1400,23 @@ var Dialog = class _Dialog extends NanoEmitter {
|
|
|
1329
1400
|
};
|
|
1330
1401
|
|
|
1331
1402
|
// lib/misc.ts
|
|
1332
|
-
function autoPlural(
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1403
|
+
function autoPlural(term, num, pluralType = "auto") {
|
|
1404
|
+
let n = num;
|
|
1405
|
+
if (typeof n !== "number")
|
|
1406
|
+
n = getListLength(n, false);
|
|
1407
|
+
if (!["-s", "-ies"].includes(pluralType))
|
|
1408
|
+
pluralType = "auto";
|
|
1409
|
+
if (isNaN(n))
|
|
1410
|
+
n = 2;
|
|
1411
|
+
const pType = pluralType === "auto" ? String(term).endsWith("y") ? "-ies" : "-s" : pluralType;
|
|
1412
|
+
switch (pType) {
|
|
1413
|
+
case "-s":
|
|
1414
|
+
return `${term}${n === 1 ? "" : "s"}`;
|
|
1415
|
+
case "-ies":
|
|
1416
|
+
return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
|
|
1417
|
+
default:
|
|
1418
|
+
return String(term);
|
|
1419
|
+
}
|
|
1336
1420
|
}
|
|
1337
1421
|
function insertValues(input, ...values) {
|
|
1338
1422
|
return input.replace(/%\d/gm, (match) => {
|
|
@@ -1341,29 +1425,33 @@ function insertValues(input, ...values) {
|
|
|
1341
1425
|
return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
|
|
1342
1426
|
});
|
|
1343
1427
|
}
|
|
1344
|
-
function pauseFor(time) {
|
|
1345
|
-
return new Promise((res) => {
|
|
1346
|
-
setTimeout(() => res(), time);
|
|
1428
|
+
function pauseFor(time, signal, rejectOnAbort = false) {
|
|
1429
|
+
return new Promise((res, rej) => {
|
|
1430
|
+
const timeout = setTimeout(() => res(), time);
|
|
1431
|
+
signal == null ? undefined : signal.addEventListener("abort", () => {
|
|
1432
|
+
clearTimeout(timeout);
|
|
1433
|
+
rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
|
|
1434
|
+
});
|
|
1347
1435
|
});
|
|
1348
1436
|
}
|
|
1349
1437
|
function fetchAdvanced(_0) {
|
|
1350
1438
|
return __async(this, arguments, function* (input, options = {}) {
|
|
1351
|
-
var _a;
|
|
1352
1439
|
const { timeout = 1e4 } = options;
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1440
|
+
const ctl = new AbortController();
|
|
1441
|
+
const _a = options, { signal } = _a, restOpts = __objRest(_a, ["signal"]);
|
|
1442
|
+
signal == null ? undefined : signal.addEventListener("abort", () => ctl.abort());
|
|
1443
|
+
let sigOpts = {}, id = undefined;
|
|
1356
1444
|
if (timeout >= 0) {
|
|
1357
|
-
id = setTimeout(() => abort(), timeout);
|
|
1358
|
-
|
|
1445
|
+
id = setTimeout(() => ctl.abort(), timeout);
|
|
1446
|
+
sigOpts = { signal: ctl.signal };
|
|
1359
1447
|
}
|
|
1360
1448
|
try {
|
|
1361
|
-
const res = yield fetch(input, __spreadValues(__spreadValues({},
|
|
1362
|
-
id && clearTimeout(id);
|
|
1449
|
+
const res = yield fetch(input, __spreadValues(__spreadValues({}, restOpts), sigOpts));
|
|
1450
|
+
typeof id !== "undefined" && clearTimeout(id);
|
|
1363
1451
|
return res;
|
|
1364
1452
|
} catch (err) {
|
|
1365
|
-
id && clearTimeout(id);
|
|
1366
|
-
throw err;
|
|
1453
|
+
typeof id !== "undefined" && clearTimeout(id);
|
|
1454
|
+
throw new Error("Error while calling fetch", { cause: err });
|
|
1367
1455
|
}
|
|
1368
1456
|
});
|
|
1369
1457
|
}
|
|
@@ -1379,10 +1467,11 @@ function consumeStringGen(strGen) {
|
|
|
1379
1467
|
);
|
|
1380
1468
|
});
|
|
1381
1469
|
}
|
|
1470
|
+
function getListLength(obj, zeroOnInvalid = true) {
|
|
1471
|
+
return "length" in obj ? obj.length : "size" in obj ? obj.size : "count" in obj ? obj.count : zeroOnInvalid ? 0 : NaN;
|
|
1472
|
+
}
|
|
1382
1473
|
|
|
1383
1474
|
// lib/SelectorObserver.ts
|
|
1384
|
-
var domLoaded = false;
|
|
1385
|
-
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
1386
1475
|
var SelectorObserver = class {
|
|
1387
1476
|
constructor(baseElement, options = {}) {
|
|
1388
1477
|
__publicField(this, "enabled", false);
|
|
@@ -1423,7 +1512,7 @@ var SelectorObserver = class {
|
|
|
1423
1512
|
}
|
|
1424
1513
|
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
1425
1514
|
checkAllSelectors() {
|
|
1426
|
-
if (!this.enabled || !
|
|
1515
|
+
if (!this.enabled || !isDomLoaded())
|
|
1427
1516
|
return;
|
|
1428
1517
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
1429
1518
|
this.checkSelector(selector, listeners);
|
|
@@ -1567,9 +1656,9 @@ function translate(language, key, ...trArgs) {
|
|
|
1567
1656
|
if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
|
|
1568
1657
|
return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
1569
1658
|
const transformTrVal = (trKey, trValue) => {
|
|
1570
|
-
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
|
|
1659
|
+
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
1571
1660
|
if (tfs.length === 0)
|
|
1572
|
-
return trValue;
|
|
1661
|
+
return String(trValue);
|
|
1573
1662
|
let retStr = String(trValue);
|
|
1574
1663
|
for (const tf of tfs) {
|
|
1575
1664
|
const re = new RegExp(tf.regex);
|
|
@@ -1718,4 +1807,4 @@ var tr = {
|
|
|
1718
1807
|
}
|
|
1719
1808
|
};
|
|
1720
1809
|
|
|
1721
|
-
export { DataStore, DataStoreSerializer, Debouncer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isScrollable, lightenColor, mapRange, observeElementProp, openDialogs, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, setInnerHtmlUnsafe, takeRandomItem, tr };
|
|
1810
|
+
export { ChecksumMismatchError, DataStore, DataStoreSerializer, Debouncer, Dialog, MigrationError, NanoEmitter, PlatformError, SelectorObserver, UUError, addGlobalStyle, addParent, autoPlural, bitSetHas, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getListLength, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isDomLoaded, isScrollable, lightenColor, mapRange, observeElementProp, onDomLoad, openDialogs, openInNewTab, pauseFor, preloadImages, probeElementStyle, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, roundFixed, setInnerHtmlUnsafe, takeRandomItem, tr };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module lib/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 {
|
|
5
|
+
import type { DataStore } from "./DataStore.js";
|
|
6
6
|
export type DataStoreSerializerOptions = {
|
|
7
7
|
/** Whether to add a checksum to the exported data */
|
|
8
8
|
addChecksum?: boolean;
|
|
@@ -43,15 +43,23 @@ export declare class DataStoreSerializer {
|
|
|
43
43
|
constructor(stores: DataStore[], options?: DataStoreSerializerOptions);
|
|
44
44
|
/** Calculates the checksum of a string */
|
|
45
45
|
protected calcChecksum(input: string): Promise<string>;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Serializes the data stores into a string.
|
|
48
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
49
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
50
|
+
*/
|
|
51
|
+
serialize(useEncoding: boolean, stringified: true): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Serializes the data stores into a string.
|
|
54
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
55
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
56
|
+
*/
|
|
57
|
+
serialize(useEncoding: boolean, stringified: false): Promise<SerializedDataStore[]>;
|
|
50
58
|
/**
|
|
51
59
|
* Deserializes the data exported via {@linkcode serialize()} and imports it into the DataStore instances.
|
|
52
60
|
* Also triggers the migration process if the data format has changed.
|
|
53
61
|
*/
|
|
54
|
-
deserialize(serializedData: string): Promise<void>;
|
|
62
|
+
deserialize(serializedData: string | SerializedDataStore[]): Promise<void>;
|
|
55
63
|
/**
|
|
56
64
|
* Loads the persistent data of the DataStore instances into the in-memory cache.
|
|
57
65
|
* Also triggers the migration process if the data format has changed.
|
|
@@ -65,4 +73,6 @@ export declare class DataStoreSerializer {
|
|
|
65
73
|
* Leaves the in-memory data untouched.
|
|
66
74
|
*/
|
|
67
75
|
deleteStoresData(): Promise<PromiseSettledResult<void>[]>;
|
|
76
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
77
|
+
static isSerializedDataStore(obj: unknown): obj is SerializedDataStore;
|
|
68
78
|
}
|
package/dist/lib/dom.d.ts
CHANGED
|
@@ -76,3 +76,22 @@ export declare function getSiblingsFrame<TSibling extends Element = HTMLElement>
|
|
|
76
76
|
* - ⚠️ This function does not perform any sanitization and should thus be used with utmost caution, as it can easily lead to XSS vulnerabilities!
|
|
77
77
|
*/
|
|
78
78
|
export declare function setInnerHtmlUnsafe<TElement extends Element = HTMLElement>(element: TElement, html: string): TElement;
|
|
79
|
+
/**
|
|
80
|
+
* Creates an invisible temporary element to probe its rendered style.
|
|
81
|
+
* Has to be run after the `DOMContentLoaded` event has fired on the document object.
|
|
82
|
+
* @param probeStyle Function to probe the element's style. First argument is the element's style object from [`window.getComputedStyle()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle), second argument is the element itself
|
|
83
|
+
* @param element The element to probe, or a function that creates and returns the element - should not be added to the DOM prior to calling this function! - all probe elements will have the class `_uu_probe_element` added to them
|
|
84
|
+
* @param hideOffscreen Whether to hide the element offscreen, enabled by default - disable if you want to probe the position style properties of the element
|
|
85
|
+
* @param parentElement The parent element to append the probe element to, defaults to `document.body`
|
|
86
|
+
* @returns The value returned by the `probeElement` function
|
|
87
|
+
*/
|
|
88
|
+
export declare function probeElementStyle<TValue, TElem extends HTMLElement = HTMLSpanElement>(probeStyle: (style: CSSStyleDeclaration, element: TElem) => TValue, element?: TElem | (() => TElem), hideOffscreen?: boolean, parentElement?: HTMLElement): TValue;
|
|
89
|
+
/** Returns whether or not the DOM has finished loading */
|
|
90
|
+
export declare function isDomLoaded(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Executes a callback and/or resolves the returned Promise when the DOM has finished loading.
|
|
93
|
+
* Immediately executes/resolves if the DOM is already loaded.
|
|
94
|
+
* @param cb Callback to execute when the DOM has finished loading
|
|
95
|
+
* @returns Returns a Promise that resolves when the DOM has finished loading
|
|
96
|
+
*/
|
|
97
|
+
export declare function onDomLoad(cb?: () => void): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module lib/errors
|
|
3
|
+
* Contains custom error classes
|
|
4
|
+
*/
|
|
5
|
+
/** Base class for all UserUtils errors - adds a `date` prop set to the error throw time */
|
|
6
|
+
export declare class UUError extends Error {
|
|
7
|
+
readonly date: Date;
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
/** Error while validating checksum */
|
|
11
|
+
export declare class ChecksumMismatchError extends UUError {
|
|
12
|
+
constructor(message: string, options?: ErrorOptions);
|
|
13
|
+
}
|
|
14
|
+
/** Error while migrating data */
|
|
15
|
+
export declare class MigrationError extends UUError {
|
|
16
|
+
constructor(message: string, options?: ErrorOptions);
|
|
17
|
+
}
|
|
18
|
+
/** Error due to the platform, like using a feature that's not supported by the browser */
|
|
19
|
+
export declare class PlatformError extends UUError {
|
|
20
|
+
constructor(message: string, options?: ErrorOptions);
|
|
21
|
+
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./DataStoreSerializer.js";
|
|
|
10
10
|
export * from "./Debouncer.js";
|
|
11
11
|
export * from "./Dialog.js";
|
|
12
12
|
export * from "./dom.js";
|
|
13
|
+
export * from "./errors.js";
|
|
13
14
|
export * from "./math.js";
|
|
14
15
|
export * from "./misc.js";
|
|
15
16
|
export * from "./NanoEmitter.js";
|
package/dist/lib/math.d.ts
CHANGED
|
@@ -27,5 +27,55 @@ export declare function randRange(min: number, max: number, enhancedEntropy?: bo
|
|
|
27
27
|
* Set {@linkcode enhancedEntropy} to true to use `crypto.getRandomValues()` for better cryptographic randomness (this also makes it take longer to generate)
|
|
28
28
|
*/
|
|
29
29
|
export declare function randRange(max: number, enhancedEntropy?: boolean): number;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Calculates the amount of digits in the given number - the given number or string will be passed to the `Number()` constructor.
|
|
32
|
+
* Returns NaN if the number is invalid.
|
|
33
|
+
* @param num The number to count the digits of
|
|
34
|
+
* @param withDecimals Whether to count the decimal places as well (defaults to true)
|
|
35
|
+
* @example ```ts
|
|
36
|
+
* digitCount(); // NaN
|
|
37
|
+
* digitCount(0); // 1
|
|
38
|
+
* digitCount(123); // 3
|
|
39
|
+
* digitCount(123.456); // 6
|
|
40
|
+
* digitCount(Infinity); // Infinity
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function digitCount(num: number | Stringifiable, withDecimals?: boolean): number;
|
|
44
|
+
/**
|
|
45
|
+
* Rounds {@linkcode num} to a fixed amount of decimal places, specified by {@linkcode fractionDigits} (supports negative values to round to the nearest power of 10).
|
|
46
|
+
* @example ```ts
|
|
47
|
+
* roundFixed(234.567, -2); // 200
|
|
48
|
+
* roundFixed(234.567, -1); // 230
|
|
49
|
+
* roundFixed(234.567, 0); // 235
|
|
50
|
+
* roundFixed(234.567, 1); // 234.6
|
|
51
|
+
* roundFixed(234.567, 2); // 234.57
|
|
52
|
+
* roundFixed(234.567, 3); // 234.567
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function roundFixed(num: number, fractionDigits: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Checks if the {@linkcode bitSet} has the {@linkcode checkVal} set
|
|
58
|
+
* @example ```ts
|
|
59
|
+
* // the two vertically adjacent bits are tested for:
|
|
60
|
+
* bitSetHas(
|
|
61
|
+
* 0b1110,
|
|
62
|
+
* 0b0010,
|
|
63
|
+
* ); // true
|
|
64
|
+
*
|
|
65
|
+
* bitSetHas(
|
|
66
|
+
* 0b1110,
|
|
67
|
+
* 0b0001,
|
|
68
|
+
* ); // false
|
|
69
|
+
*
|
|
70
|
+
* // with TS enums (or JS maps):
|
|
71
|
+
* enum MyEnum {
|
|
72
|
+
* A = 1, B = 2, C = 4,
|
|
73
|
+
* D = 8, E = 16, F = 32,
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* const myBitSet = MyEnum.A | MyEnum.B;
|
|
77
|
+
* bitSetHas(myBitSet, MyEnum.B); // true
|
|
78
|
+
* bitSetHas(myBitSet, MyEnum.F); // false
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function bitSetHas<TType extends number | bigint>(bitSet: TType, checkVal: TType): boolean;
|
package/dist/lib/misc.d.ts
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
* @module lib/misc
|
|
3
3
|
* This module contains miscellaneous functions that don't fit in another category - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#misc)
|
|
4
4
|
*/
|
|
5
|
-
import type { Prettify, Stringifiable } from "./types.js";
|
|
5
|
+
import type { ListWithLength, Prettify, Stringifiable } from "./types.js";
|
|
6
|
+
/** Which plural form to use when auto-pluralizing */
|
|
7
|
+
export type PluralType = "auto" | "-s" | "-ies";
|
|
6
8
|
/**
|
|
7
|
-
* Automatically
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
9
|
+
* Automatically pluralizes the given string by adding an `-s` or `-ies` to the passed {@linkcode term}, if {@linkcode num} is not equal to 1.
|
|
10
|
+
* By default, words ending in `-y` will have it replaced with `-ies`, and all other words will simply have `-s` appended.
|
|
11
|
+
* {@linkcode pluralType} will default to `auto` if invalid and {@linkcode num} is set to 2 if it resolves to `NaN`.
|
|
12
|
+
* @param term The term, written in singular form, to auto-convert to plural form
|
|
13
|
+
* @param num A number, or list-like value that has either a `length`, `count` or `size` property, like an array, Map or NodeList - does not support iterables, they need to be converted to an array first
|
|
14
|
+
* @param pluralType Which plural form to use when auto-pluralizing. Defaults to `"auto"`, which removes the last char and uses `-ies` for words ending in `y` and simply appends `-s` for all other words
|
|
10
15
|
*/
|
|
11
|
-
export declare function autoPlural(
|
|
16
|
+
export declare function autoPlural(term: Stringifiable, num: number | ListWithLength, pluralType?: PluralType): string;
|
|
12
17
|
/**
|
|
13
18
|
* Inserts the passed values into a string at the respective placeholders.
|
|
14
19
|
* The placeholder format is `%n`, where `n` is the 1-indexed argument number.
|
|
@@ -16,8 +21,12 @@ export declare function autoPlural(word: Stringifiable, num: number | unknown[]
|
|
|
16
21
|
* @param values The values to insert, in order, starting at `%1`
|
|
17
22
|
*/
|
|
18
23
|
export declare function insertValues(input: string, ...values: Stringifiable[]): string;
|
|
19
|
-
/**
|
|
20
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Pauses async execution for the specified time in ms.
|
|
26
|
+
* If an `AbortSignal` is passed, the pause will be aborted when the signal is triggered.
|
|
27
|
+
* By default, this will resolve the promise, but you can set {@linkcode rejectOnAbort} to true to reject it instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare function pauseFor(time: number, signal?: AbortSignal, rejectOnAbort?: boolean): Promise<void>;
|
|
21
30
|
/** Options for the `fetchAdvanced()` function */
|
|
22
31
|
export type FetchAdvancedOpts = Prettify<Partial<{
|
|
23
32
|
/** Timeout in milliseconds after which the fetch call will be canceled with an AbortController signal */
|
|
@@ -46,3 +55,9 @@ export type StringGen = ValueGen<Stringifiable>;
|
|
|
46
55
|
* @template TStrUnion The union of strings that the StringGen should yield - this allows for finer type control compared to {@linkcode consumeGen()}
|
|
47
56
|
*/
|
|
48
57
|
export declare function consumeStringGen<TStrUnion extends string>(strGen: StringGen): Promise<TStrUnion>;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the length of the given list-like object (anything with a numeric `length`, `size` or `count` property, like an array, Map or NodeList).
|
|
60
|
+
* If the object doesn't have any of these properties, it will return 0 by default.
|
|
61
|
+
* Set {@linkcode zeroOnInvalid} to false to return NaN instead of 0 if the object doesn't have any of the properties.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getListLength(obj: ListWithLength, zeroOnInvalid?: boolean): number;
|
|
@@ -120,35 +120,42 @@ declare function getFallbackLanguage(): string | undefined;
|
|
|
120
120
|
* After all %n-formatted values have been injected, the transform functions will be called sequentially in the order they were added.
|
|
121
121
|
* @example
|
|
122
122
|
* ```ts
|
|
123
|
-
* tr
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
123
|
+
* import { tr, type TrKeys } from "@sv443-network/userutils";
|
|
124
|
+
*
|
|
125
|
+
* const transEn = {
|
|
126
|
+
* "headline": {
|
|
127
|
+
* "basic": "Hello, ${USERNAME}",
|
|
128
|
+
* "html": "Hello, ${USERNAME}<br><c=red>You have ${UNREAD_NOTIFS} unread notifications.</c>"
|
|
127
129
|
* }
|
|
128
|
-
* }
|
|
130
|
+
* } as const;
|
|
131
|
+
*
|
|
132
|
+
* tr.addTranslations("en", transEn);
|
|
129
133
|
*
|
|
130
|
-
* // replace ${PATTERN}
|
|
131
|
-
* tr.addTransform(
|
|
134
|
+
* // replace ${PATTERN} with predefined values
|
|
135
|
+
* tr.addTransform(/\$\{([A-Z_]+)\}/g, ({ matches }) => {
|
|
132
136
|
* switch(matches?.[1]) {
|
|
133
|
-
* default:
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* case "
|
|
137
|
+
* default:
|
|
138
|
+
* return `[UNKNOWN: ${matches?.[1]}]`;
|
|
139
|
+
* // these would be grabbed from elsewhere in the application, like a DataStore, global state or variable:
|
|
140
|
+
* case "USERNAME":
|
|
141
|
+
* return "JohnDoe45";
|
|
142
|
+
* case "UNREAD_NOTIFS":
|
|
143
|
+
* return 5;
|
|
137
144
|
* }
|
|
138
145
|
* });
|
|
139
146
|
*
|
|
140
|
-
* // replace <c=red>...</c> with <span
|
|
147
|
+
* // replace <c=red>...</c> with <span style="color: red;">...</span>
|
|
141
148
|
* tr.addTransform(/<c=([a-z]+)>(.*?)<\/c>/g, ({ matches }) => {
|
|
142
149
|
* const color = matches?.[1];
|
|
143
150
|
* const content = matches?.[2];
|
|
144
151
|
*
|
|
145
|
-
* return
|
|
152
|
+
* return `<span style="color: ${color};">${content}</span>`;
|
|
146
153
|
* });
|
|
147
154
|
*
|
|
148
|
-
* tr.
|
|
155
|
+
* const t = tr.use<TrKeys<typeof transEn>>("en");
|
|
149
156
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
157
|
+
* t("headline.basic"); // "Hello, JohnDoe45"
|
|
158
|
+
* t("headline.html"); // "Hello, JohnDoe45<br><span style="color: red;">You have 5 unread notifications.</span>"
|
|
152
159
|
* ```
|
|
153
160
|
* @param args A tuple containing the regular expression to match and the transform function to call if the pattern is found in a translation string
|
|
154
161
|
*/
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -28,3 +28,11 @@ export type NonEmptyString<TString extends string> = TString extends "" ? never
|
|
|
28
28
|
export type Prettify<T> = {
|
|
29
29
|
[K in keyof T]: T[K];
|
|
30
30
|
} & {};
|
|
31
|
+
/** Any value that is list-like, i.e. has a numeric length, count or size property */
|
|
32
|
+
export type ListWithLength = {
|
|
33
|
+
length: number;
|
|
34
|
+
} | {
|
|
35
|
+
count: number;
|
|
36
|
+
} | {
|
|
37
|
+
size: number;
|
|
38
|
+
};
|