@sv443-network/userutils 9.0.4 → 9.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 9.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - eb20132: Added the type `ListWithLength` to represent an array or object with a numeric `length`, `count` or `size` property.
8
+ - eb20132: Added `autoPlural()` support for generic objects with a numeric `length`, `count` or `size` property.
9
+ - c9b13d8: Added `signal: AbortSignal` and `rejectOnAbort: boolean` params to `pauseFor()` to allow for cutting the pause short
10
+ - c66324b: Support for words with `-y`/`-ies` extension in `autoPlural()`
11
+ - c9b13d8: Added `getListLength()` function to resolve a value of the new `ListWithLength` type
12
+ - c13e890: `autoPlural()` now defaults `pluralType` to `"auto"` and `num` to 2 if `pluralType` is invalid or `num` resolves to NaN
13
+
14
+ ### Patch Changes
15
+
16
+ - 3f86215: Fixed fetchAdvanced error "'abort' called on an object that does not implement interface AbortController"
17
+
3
18
  ## 9.0.4
4
19
 
5
20
  ### Patch Changes
package/README-summary.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ## UserUtils
2
- Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more.
2
+ General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more.
3
3
  Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
4
- The library also works in any DOM environment without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
4
+ The library works in any DOM environment with or without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
5
5
 
6
6
  You may want to check out my [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
7
7
 
@@ -66,22 +66,27 @@ View the documentation of previous major releases:
66
66
  - [`decompress()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#decompress) - decompress a previously compressed string
67
67
  - [`computeHash()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#computehash) - compute the hash / checksum of a string or ArrayBuffer
68
68
  - [`randomId()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomid) - generate a random ID of a given length and radix
69
+ - [`consumeGen()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#consumegen) - consumes a ValueGen and returns the value
70
+ - [`consumeStringGen()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#consumestringgen) - consumes a StringGen and returns the string
71
+ - [`getListLength()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#getlistlength) - get the length of any object with a numeric `length`, `count` or `size` property
69
72
  - **Arrays:**
70
73
  - [`randomItem()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomitem) - returns a random item from an array
71
74
  - [`randomItemIndex()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomitemindex) - returns a tuple of a random item and its index from an array
72
75
  - [`takeRandomItem()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#takerandomitem) - returns a random item from an array and mutates it to remove the item
73
76
  - [`randomizeArray()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomizearray) - returns a copy of the array with its items in a random order
74
77
  - **Translation:**
75
- - [`tr()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#tr) - simple JSON-based translation system with placeholder and nesting support
76
- - [`tr.forLang()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trforlang) - translate with the specified language instead of the currently active one
77
- - [`tr.setLanguage()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trsetlanguage) - set the currently active language for translations
78
- - [`tr.getLanguage()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgetlanguage) - returns the currently active language
79
- - [`tr.addTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#traddtranslations) - add a language and its translations
80
- - [`tr.getTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgettranslations) - returns the translations for the given language or the currently active one
81
- - [`tr.deleteTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetranslations) - delete the translations for the given language or the active one
82
- - [`tr.hasKey()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trhaskey) - check if a translation key exists for the given or active language
83
- - [`tr.addTransform()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#traddtransform) - add a transformation function to dynamically modify the translation value
84
- - [`tr.deleteTransform()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetransform) - delete a transformation function that was previously added
78
+ - [`tr.for()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trfor) - translates a key for the specified language
79
+ - [`tr.use()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#truse) - creates a translation function for the specified language
80
+ - [`tr.hasKey()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trhaskey) - checks if a key exists in the given language
81
+ - [`tr.addTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#traddtranslations) - add a flat or recursive translation object for a language
82
+ - [`tr.getTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgettranslations) - returns the translation object for a language
83
+ - [`tr.deleteTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetranslations) - delete the translation object for a language
84
+ - [`tr.setFallbackLanguage()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trsetfallbacklanguage) - set the fallback language used when a key is not found in the given language
85
+ - [`tr.getFallbackLanguage()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgetfallbacklanguage) - returns the fallback language
86
+ - [`tr.addTransform()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#traddtransform) - adds a transform function to the translation system for custom argument insertion and much more
87
+ - [`tr.deleteTransform()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetransform) - removes a transform function
88
+ - [`tr.transforms`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trtransforms) - predefined transform functions for quickly adding custom argument insertion
89
+ - [`TrKeys`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trkeys) - generic type that extracts all keys from a flat or recursive translation object into a union
85
90
  - **Colors:**
86
91
  - [`hexToRgb()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#hextorgb) - convert a hex color string to an RGB or RGBA value tuple
87
92
  - [`rgbToHex()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#rgbtohex) - convert RGB or RGBA values to a hex color string
@@ -95,6 +100,7 @@ View the documentation of previous major releases:
95
100
  - [`Prettify`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#prettify) - expands a complex type into a more readable format while keeping functionality the same
96
101
  - [`ValueGen`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#valuegen) - a "generator" value that allows for super flexible value typing and declaration
97
102
  - [`StringGen`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#stringgen) - a "generator" string that allows for super flexible string typing and declaration, including enhanced support for unions
103
+ - [`ListWithLength`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#listwithlength) - represents an array or object with a numeric `length`, `count` or `size` property
98
104
 
99
105
  <br><br>
100
106
 
@@ -138,9 +144,15 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
138
144
  // @require https://update.greasyfork.org/scripts/472956/UserUtils.js
139
145
  // @require https://openuserjs.org/src/libs/Sv443/UserUtils.js
140
146
  ```
141
-
142
- > **Note:**
143
- > In order for your userscript not to break on a major library update, use one the versioned URLs above after replacing `INSERT_VERSION` with the desired version (e.g. `8.3.2`) or the versioned URL that's shown at the top of the [GreasyFork page.](https://greasyfork.org/scripts/472956-userutils)
147
+
148
+ - If you are using this library in a generic DOM environment without access to the GreaseMonkey API, either download the latest release from the [releases page](https://github.com/Sv443-Network/UserUtils/releases) to include in your project or add one of the following tags to the &lt;head&gt;:
149
+ ```html
150
+ <script src="https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
151
+ <script src="https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
152
+ ```
153
+
154
+ > **Note:**
155
+ > In order for your userscript not to break on a major library update, use one the versioned URLs above after replacing `INSERT_VERSION` with the desired version (e.g. `8.3.2`) or the versioned URL that's shown [at the top of the GreasyFork page.](https://greasyfork.org/scripts/472956-userutils)
144
156
 
145
157
  <br>
146
158
 
@@ -156,7 +168,7 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
156
168
 
157
169
  <br>
158
170
 
159
- - If you're using TypeScript and it complains about the missing global variable `UserUtils`, install the library using the package manager of your choice and add the following inside a `.d.ts` file somewhere in the directory (or a subdirectory) defined in your `tsconfig.json`'s `baseUrl` option or `include` array:
171
+ - If you're using TypeScript and it complains about the missing global variable `UserUtils`, install the library using the package manager of your choice and add the following inside any `.ts` file that is included in the final build:
160
172
  ```ts
161
173
  declare const UserUtils: typeof import("@sv443-network/userutils");
162
174
 
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  <!-- #region Description -->
4
4
  ## UserUtils
5
- Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more.
5
+ General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more.
6
6
  Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
7
- The library also works in any DOM environment without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
7
+ The library works in any DOM environment with or without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
8
8
 
9
9
  You may want to check out my [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
10
10
  If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
@@ -71,6 +71,7 @@ View the documentation of previous major releases:
71
71
  - [`randomId()`](./docs.md#randomid) - generate a random ID of a given length and radix
72
72
  - [`consumeGen()`](./docs.md#consumegen) - consumes a ValueGen and returns the value
73
73
  - [`consumeStringGen()`](./docs.md#consumestringgen) - consumes a StringGen and returns the string
74
+ - [`getListLength()`](./docs.md#getlistlength) - get the length of any object with a numeric `length`, `count` or `size` property
74
75
  - [**Arrays:**](./docs.md#arrays)
75
76
  - [`randomItem()`](./docs.md#randomitem) - returns a random item from an array
76
77
  - [`randomItemIndex()`](./docs.md#randomitemindex) - returns a tuple of a random item and its index from an array
@@ -102,6 +103,7 @@ View the documentation of previous major releases:
102
103
  - [`Prettify`](./docs.md#prettify) - expands a complex type into a more readable format while keeping functionality the same
103
104
  - [`ValueGen`](./docs.md#valuegen) - a "generator" value that allows for super flexible value typing and declaration
104
105
  - [`StringGen`](./docs.md#stringgen) - a "generator" string that allows for super flexible string typing and declaration, including enhanced support for unions
106
+ - [`ListWithLength`](./docs.md#listwithlength) - represents an array or object with a numeric `length`, `count` or `size` property
105
107
 
106
108
  <br><br>
107
109
 
@@ -132,21 +134,27 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
132
134
 
133
135
  <br>
134
136
 
135
- - If you are not using a bundler, want to reduce the size of your userscript, or declared the package as external in your bundler, you can include the latest release by adding one of these directives to the userscript header, depending on your preferred CDN:
137
+ - If you are not using a bundler, want to reduce the size of your script, or declared the package as external in your bundler, you can include the latest release by adding one of these directives to the userscript header, depending on your preferred CDN:
136
138
 
137
139
  Versioned (recommended):
138
140
  ```
139
141
  // @require https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
140
142
  // @require https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
141
143
  ```
142
- Non-versioned (not recommended because auto-updating):
144
+ Non-versioned (not recommended because it auto-updates):
143
145
  ```
144
146
  // @require https://update.greasyfork.org/scripts/472956/UserUtils.js
145
147
  // @require https://openuserjs.org/src/libs/Sv443/UserUtils.js
146
148
  ```
147
-
149
+
150
+ - If you are using this library in a generic DOM environment without access to the GreaseMonkey API, either download the latest release from the [releases page](https://github.com/Sv443-Network/UserUtils/releases) to include in your project or add one of the following tags to the &lt;head&gt;:
151
+ ```html
152
+ <script src="https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
153
+ <script src="https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
154
+ ```
155
+
148
156
  > [!NOTE]
149
- > In order for your userscript not to break on a major library update, use one the versioned URLs above after replacing `INSERT_VERSION` with the desired version (e.g. `8.3.2`) or the versioned URL that's shown at the top of the [GreasyFork page.](https://greasyfork.org/scripts/472956-userutils)
157
+ > In order for your script not to break on a major library update, use one the versioned URLs above after replacing `INSERT_VERSION` with the desired version (e.g. `8.3.2`) or the versioned URL that's shown [at the top of the GreasyFork page.](https://greasyfork.org/scripts/472956-userutils)
150
158
 
151
159
  <br>
152
160
 
@@ -163,7 +171,7 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
163
171
 
164
172
  <br>
165
173
 
166
- - If you're using TypeScript and it complains about the missing global variable `UserUtils`, install the library using the package manager of your choice and add the following inside a `.d.ts` file somewhere in the directory (or a subdirectory) defined in your `tsconfig.json`'s `baseUrl` option or `include` array:
174
+ - If you're using TypeScript and it complains about the missing global variable `UserUtils`, install the library using the package manager of your choice and add the following inside any `.ts` file that is included in the final build:
167
175
 
168
176
  ```ts
169
177
  declare const UserUtils: typeof import("@sv443-network/userutils");
package/dist/index.cjs CHANGED
@@ -1331,10 +1331,23 @@ var Dialog = class _Dialog extends NanoEmitter {
1331
1331
  };
1332
1332
 
1333
1333
  // lib/misc.ts
1334
- function autoPlural(word, num) {
1335
- if (Array.isArray(num) || num instanceof NodeList)
1336
- num = num.length;
1337
- return `${word}${num === 1 ? "" : "s"}`;
1334
+ function autoPlural(term, num, pluralType = "auto") {
1335
+ let n = num;
1336
+ if (typeof n !== "number")
1337
+ n = getListLength(n, false);
1338
+ if (!["-s", "-ies"].includes(pluralType))
1339
+ pluralType = "auto";
1340
+ if (isNaN(n))
1341
+ n = 2;
1342
+ const pType = pluralType === "auto" ? String(term).endsWith("y") ? "-ies" : "-s" : pluralType;
1343
+ switch (pType) {
1344
+ case "-s":
1345
+ return `${term}${n === 1 ? "" : "s"}`;
1346
+ case "-ies":
1347
+ return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
1348
+ default:
1349
+ return String(term);
1350
+ }
1338
1351
  }
1339
1352
  function insertValues(input, ...values) {
1340
1353
  return input.replace(/%\d/gm, (match) => {
@@ -1343,29 +1356,33 @@ function insertValues(input, ...values) {
1343
1356
  return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
1344
1357
  });
1345
1358
  }
1346
- function pauseFor(time) {
1347
- return new Promise((res) => {
1348
- setTimeout(() => res(), time);
1359
+ function pauseFor(time, signal, rejectOnAbort = false) {
1360
+ return new Promise((res, rej) => {
1361
+ const timeout = setTimeout(() => res(), time);
1362
+ signal == null ? undefined : signal.addEventListener("abort", () => {
1363
+ clearTimeout(timeout);
1364
+ rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
1365
+ });
1349
1366
  });
1350
1367
  }
1351
1368
  function fetchAdvanced(_0) {
1352
1369
  return __async(this, arguments, function* (input, options = {}) {
1353
- var _a;
1354
1370
  const { timeout = 1e4 } = options;
1355
- const { signal, abort } = new AbortController();
1356
- (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1357
- let signalOpts = {}, id = undefined;
1371
+ const ctl = new AbortController();
1372
+ const _a = options, { signal } = _a, restOpts = __objRest(_a, ["signal"]);
1373
+ signal == null ? undefined : signal.addEventListener("abort", () => ctl.abort());
1374
+ let sigOpts = {}, id = undefined;
1358
1375
  if (timeout >= 0) {
1359
- id = setTimeout(() => abort(), timeout);
1360
- signalOpts = { signal };
1376
+ id = setTimeout(() => ctl.abort(), timeout);
1377
+ sigOpts = { signal: ctl.signal };
1361
1378
  }
1362
1379
  try {
1363
- const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
1364
- id && clearTimeout(id);
1380
+ const res = yield fetch(input, __spreadValues(__spreadValues({}, restOpts), sigOpts));
1381
+ typeof id !== "undefined" && clearTimeout(id);
1365
1382
  return res;
1366
1383
  } catch (err) {
1367
- id && clearTimeout(id);
1368
- throw err;
1384
+ typeof id !== "undefined" && clearTimeout(id);
1385
+ throw new Error("Error while calling fetch", { cause: err });
1369
1386
  }
1370
1387
  });
1371
1388
  }
@@ -1381,6 +1398,9 @@ function consumeStringGen(strGen) {
1381
1398
  );
1382
1399
  });
1383
1400
  }
1401
+ function getListLength(obj, zeroOnInvalid = true) {
1402
+ return "length" in obj ? obj.length : "size" in obj ? obj.size : "count" in obj ? obj.count : zeroOnInvalid ? 0 : NaN;
1403
+ }
1384
1404
 
1385
1405
  // lib/SelectorObserver.ts
1386
1406
  var domLoaded = false;
@@ -1741,6 +1761,7 @@ exports.defaultDialogCss = defaultDialogCss;
1741
1761
  exports.defaultStrings = defaultStrings;
1742
1762
  exports.digitCount = digitCount;
1743
1763
  exports.fetchAdvanced = fetchAdvanced;
1764
+ exports.getListLength = getListLength;
1744
1765
  exports.getSiblingsFrame = getSiblingsFrame;
1745
1766
  exports.getUnsafeWindow = getUnsafeWindow;
1746
1767
  exports.hexToRgb = hexToRgb;
@@ -8,7 +8,7 @@
8
8
  // ==UserLibrary==
9
9
  // @name UserUtils
10
10
  // @description Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more
11
- // @version 9.0.4
11
+ // @version 9.1.0
12
12
  // @license MIT
13
13
  // @copyright Sv443 (https://github.com/Sv443)
14
14
 
@@ -1369,10 +1369,23 @@ Has: ${checksum}`);
1369
1369
  };
1370
1370
 
1371
1371
  // lib/misc.ts
1372
- function autoPlural(word, num) {
1373
- if (Array.isArray(num) || num instanceof NodeList)
1374
- num = num.length;
1375
- return `${word}${num === 1 ? "" : "s"}`;
1372
+ function autoPlural(term, num, pluralType = "auto") {
1373
+ let n = num;
1374
+ if (typeof n !== "number")
1375
+ n = getListLength(n, false);
1376
+ if (!["-s", "-ies"].includes(pluralType))
1377
+ pluralType = "auto";
1378
+ if (isNaN(n))
1379
+ n = 2;
1380
+ const pType = pluralType === "auto" ? String(term).endsWith("y") ? "-ies" : "-s" : pluralType;
1381
+ switch (pType) {
1382
+ case "-s":
1383
+ return `${term}${n === 1 ? "" : "s"}`;
1384
+ case "-ies":
1385
+ return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
1386
+ default:
1387
+ return String(term);
1388
+ }
1376
1389
  }
1377
1390
  function insertValues(input, ...values) {
1378
1391
  return input.replace(/%\d/gm, (match) => {
@@ -1381,29 +1394,33 @@ Has: ${checksum}`);
1381
1394
  return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
1382
1395
  });
1383
1396
  }
1384
- function pauseFor(time) {
1385
- return new Promise((res) => {
1386
- setTimeout(() => res(), time);
1397
+ function pauseFor(time, signal, rejectOnAbort = false) {
1398
+ return new Promise((res, rej) => {
1399
+ const timeout = setTimeout(() => res(), time);
1400
+ signal == null ? undefined : signal.addEventListener("abort", () => {
1401
+ clearTimeout(timeout);
1402
+ rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
1403
+ });
1387
1404
  });
1388
1405
  }
1389
1406
  function fetchAdvanced(_0) {
1390
1407
  return __async(this, arguments, function* (input, options = {}) {
1391
- var _a;
1392
1408
  const { timeout = 1e4 } = options;
1393
- const { signal, abort } = new AbortController();
1394
- (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1395
- let signalOpts = {}, id = undefined;
1409
+ const ctl = new AbortController();
1410
+ const _a = options, { signal } = _a, restOpts = __objRest(_a, ["signal"]);
1411
+ signal == null ? undefined : signal.addEventListener("abort", () => ctl.abort());
1412
+ let sigOpts = {}, id = undefined;
1396
1413
  if (timeout >= 0) {
1397
- id = setTimeout(() => abort(), timeout);
1398
- signalOpts = { signal };
1414
+ id = setTimeout(() => ctl.abort(), timeout);
1415
+ sigOpts = { signal: ctl.signal };
1399
1416
  }
1400
1417
  try {
1401
- const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
1402
- id && clearTimeout(id);
1418
+ const res = yield fetch(input, __spreadValues(__spreadValues({}, restOpts), sigOpts));
1419
+ typeof id !== "undefined" && clearTimeout(id);
1403
1420
  return res;
1404
1421
  } catch (err) {
1405
- id && clearTimeout(id);
1406
- throw err;
1422
+ typeof id !== "undefined" && clearTimeout(id);
1423
+ throw new Error("Error while calling fetch", { cause: err });
1407
1424
  }
1408
1425
  });
1409
1426
  }
@@ -1419,6 +1436,9 @@ Has: ${checksum}`);
1419
1436
  );
1420
1437
  });
1421
1438
  }
1439
+ function getListLength(obj, zeroOnInvalid = true) {
1440
+ return "length" in obj ? obj.length : "size" in obj ? obj.size : "count" in obj ? obj.count : zeroOnInvalid ? 0 : NaN;
1441
+ }
1422
1442
 
1423
1443
  // lib/SelectorObserver.ts
1424
1444
  var domLoaded = false;
@@ -1779,6 +1799,7 @@ Has: ${checksum}`);
1779
1799
  exports.defaultStrings = defaultStrings;
1780
1800
  exports.digitCount = digitCount;
1781
1801
  exports.fetchAdvanced = fetchAdvanced;
1802
+ exports.getListLength = getListLength;
1782
1803
  exports.getSiblingsFrame = getSiblingsFrame;
1783
1804
  exports.getUnsafeWindow = getUnsafeWindow;
1784
1805
  exports.hexToRgb = hexToRgb;
package/dist/index.js CHANGED
@@ -1329,10 +1329,23 @@ var Dialog = class _Dialog extends NanoEmitter {
1329
1329
  };
1330
1330
 
1331
1331
  // lib/misc.ts
1332
- function autoPlural(word, num) {
1333
- if (Array.isArray(num) || num instanceof NodeList)
1334
- num = num.length;
1335
- return `${word}${num === 1 ? "" : "s"}`;
1332
+ function autoPlural(term, num, pluralType = "auto") {
1333
+ let n = num;
1334
+ if (typeof n !== "number")
1335
+ n = getListLength(n, false);
1336
+ if (!["-s", "-ies"].includes(pluralType))
1337
+ pluralType = "auto";
1338
+ if (isNaN(n))
1339
+ n = 2;
1340
+ const pType = pluralType === "auto" ? String(term).endsWith("y") ? "-ies" : "-s" : pluralType;
1341
+ switch (pType) {
1342
+ case "-s":
1343
+ return `${term}${n === 1 ? "" : "s"}`;
1344
+ case "-ies":
1345
+ return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
1346
+ default:
1347
+ return String(term);
1348
+ }
1336
1349
  }
1337
1350
  function insertValues(input, ...values) {
1338
1351
  return input.replace(/%\d/gm, (match) => {
@@ -1341,29 +1354,33 @@ function insertValues(input, ...values) {
1341
1354
  return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
1342
1355
  });
1343
1356
  }
1344
- function pauseFor(time) {
1345
- return new Promise((res) => {
1346
- setTimeout(() => res(), time);
1357
+ function pauseFor(time, signal, rejectOnAbort = false) {
1358
+ return new Promise((res, rej) => {
1359
+ const timeout = setTimeout(() => res(), time);
1360
+ signal == null ? undefined : signal.addEventListener("abort", () => {
1361
+ clearTimeout(timeout);
1362
+ rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
1363
+ });
1347
1364
  });
1348
1365
  }
1349
1366
  function fetchAdvanced(_0) {
1350
1367
  return __async(this, arguments, function* (input, options = {}) {
1351
- var _a;
1352
1368
  const { timeout = 1e4 } = options;
1353
- const { signal, abort } = new AbortController();
1354
- (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1355
- let signalOpts = {}, id = undefined;
1369
+ const ctl = new AbortController();
1370
+ const _a = options, { signal } = _a, restOpts = __objRest(_a, ["signal"]);
1371
+ signal == null ? undefined : signal.addEventListener("abort", () => ctl.abort());
1372
+ let sigOpts = {}, id = undefined;
1356
1373
  if (timeout >= 0) {
1357
- id = setTimeout(() => abort(), timeout);
1358
- signalOpts = { signal };
1374
+ id = setTimeout(() => ctl.abort(), timeout);
1375
+ sigOpts = { signal: ctl.signal };
1359
1376
  }
1360
1377
  try {
1361
- const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
1362
- id && clearTimeout(id);
1378
+ const res = yield fetch(input, __spreadValues(__spreadValues({}, restOpts), sigOpts));
1379
+ typeof id !== "undefined" && clearTimeout(id);
1363
1380
  return res;
1364
1381
  } catch (err) {
1365
- id && clearTimeout(id);
1366
- throw err;
1382
+ typeof id !== "undefined" && clearTimeout(id);
1383
+ throw new Error("Error while calling fetch", { cause: err });
1367
1384
  }
1368
1385
  });
1369
1386
  }
@@ -1379,6 +1396,9 @@ function consumeStringGen(strGen) {
1379
1396
  );
1380
1397
  });
1381
1398
  }
1399
+ function getListLength(obj, zeroOnInvalid = true) {
1400
+ return "length" in obj ? obj.length : "size" in obj ? obj.size : "count" in obj ? obj.count : zeroOnInvalid ? 0 : NaN;
1401
+ }
1382
1402
 
1383
1403
  // lib/SelectorObserver.ts
1384
1404
  var domLoaded = false;
@@ -1718,4 +1738,4 @@ var tr = {
1718
1738
  }
1719
1739
  };
1720
1740
 
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 };
1741
+ export { DataStore, DataStoreSerializer, Debouncer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getListLength, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isScrollable, lightenColor, mapRange, observeElementProp, openDialogs, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, setInnerHtmlUnsafe, takeRandomItem, tr };
@@ -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 appends an `s` to the passed {@linkcode word}, if {@linkcode num} is not equal to 1
8
- * @param word A word in singular form, to auto-convert to plural
9
- * @param num If this is an array or NodeList, the amount of items is used
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(word: Stringifiable, num: number | unknown[] | NodeList): string;
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
- /** Pauses async execution for the specified time in ms */
20
- export declare function pauseFor(time: number): Promise<void>;
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;
@@ -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
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sv443-network/userutils",
3
3
  "libName": "UserUtils",
4
- "version": "9.0.4",
4
+ "version": "9.1.0",
5
5
  "description": "Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",