@sv443-network/userutils 9.2.1 → 9.4.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.
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @module lib/Mixins
3
+ * Allows for defining and applying mixin functions to allow multiple sources to modify a value in a controlled way.
4
+ */
5
+ import type { Prettify } from "./types.js";
6
+ /** Full mixin object (either sync or async), as it is stored in the instance's mixin array. */
7
+ export type MixinObj<TArg, TCtx> = Prettify<MixinObjSync<TArg, TCtx> | MixinObjAsync<TArg, TCtx>>;
8
+ /** Asynchronous mixin object, as it is stored in the instance's mixin array. */
9
+ export type MixinObjSync<TArg, TCtx> = Prettify<{
10
+ /** The mixin function */
11
+ fn: (arg: TArg, ctx?: TCtx) => TArg;
12
+ } & MixinObjBase>;
13
+ /** Synchronous mixin object, as it is stored in the instance's mixin array. */
14
+ export type MixinObjAsync<TArg, TCtx> = Prettify<{
15
+ /** The mixin function */
16
+ fn: (arg: TArg, ctx?: TCtx) => TArg | Promise<TArg>;
17
+ } & MixinObjBase>;
18
+ /** Base type for mixin objects */
19
+ type MixinObjBase = Prettify<{
20
+ /** The public identifier key (purpose) of the mixin */
21
+ key: string;
22
+ } & MixinConfig>;
23
+ /** Configuration object for a mixin function */
24
+ export type MixinConfig = {
25
+ /** The higher, the earlier the mixin will be applied. Supports floating-point and negative numbers too. 0 by default. */
26
+ priority: number;
27
+ /** If true, no further mixins will be applied after this one. */
28
+ stopPropagation: boolean;
29
+ /** If set, the mixin will only be applied if the given signal is not aborted. */
30
+ signal?: AbortSignal;
31
+ };
32
+ /** Configuration object for the Mixins class */
33
+ export type MixinsConstructorConfig = {
34
+ /**
35
+ * If true, when no priority is specified, an auto-incrementing integer priority will be used, starting at `defaultPriority` or 0 (unique per mixin key). Defaults to false.
36
+ * If a priority value is already used, it will be incremented until a unique value is found.
37
+ * This is useful to ensure that mixins are applied in the order they were added, even if they don't specify a priority.
38
+ * It also allows for a finer level of interjection when the priority is a floating point number.
39
+ */
40
+ autoIncrementPriority: boolean;
41
+ /** The default priority for mixins that do not specify one. Defaults to 0. */
42
+ defaultPriority: number;
43
+ /** The default stopPropagation value for mixins that do not specify one. Defaults to false. */
44
+ defaultStopPropagation: boolean;
45
+ /** The default AbortSignal for mixins that do not specify one. Defaults to undefined. */
46
+ defaultSignal?: AbortSignal;
47
+ };
48
+ /**
49
+ * The mixin class allows for defining and applying mixin functions to allow multiple sources to modify values in a controlled way.
50
+ * Mixins are identified via their string key and can be added with {@linkcode add()}
51
+ * When calling {@linkcode resolve()}, all registered mixin functions with the same key will be applied to the input value in the order of their priority.
52
+ * If a mixin has the stopPropagation flag set to true, no further mixins will be applied after it.
53
+ * @template TMixinMap A map of mixin keys to their respective function signatures. The first argument of the function is the input value, the second argument is an optional context object. If it is defined here, it must be passed as the third argument in {@linkcode resolve()}.
54
+ * @example ```ts
55
+ * const ac = new AbortController();
56
+ * const { abort: removeAllMixins } = ac;
57
+ *
58
+ * const mathMixins = new Mixins<{
59
+ * // supports sync and async functions:
60
+ * foo: (val: number, ctx: { baz: string }) => Promise<number>;
61
+ * // first argument and return value have to be of the same type:
62
+ * bar: (val: bigint) => bigint;
63
+ * // ...
64
+ * }>({
65
+ * autoIncrementPriority: true,
66
+ * defaultPriority: 0,
67
+ * defaultSignal: ac.signal,
68
+ * });
69
+ *
70
+ * // will be applied last due to base priority of 0:
71
+ * mathMixins.add("foo", (val, ctx) => Promise.resolve(val * 2 + ctx.baz.length));
72
+ * // will be applied second due to manually set priority of 1:
73
+ * mathMixins.add("foo", (val) => val + 1, { priority: 1 });
74
+ * // will be applied first, even though the above ones were called first, because of the auto-incrementing priority of 2:
75
+ * mathMixins.add("foo", (val) => val / 2);
76
+ *
77
+ * const result = await mathMixins.resolve("foo", 10, { baz: "this has a length of 23" });
78
+ * // order of application:
79
+ * // input value: 10
80
+ * // 10 / 2 = 5
81
+ * // 5 + 1 = 6
82
+ * // 6 * 2 + 23 = 35
83
+ * // result = 35
84
+ *
85
+ * // removes all mixins added without a `signal` property:
86
+ * removeAllMixins();
87
+ * ```
88
+ */
89
+ export declare class Mixins<TMixinMap extends Record<string, (arg: any, ctx?: any) => any>, TMixinKey extends Extract<keyof TMixinMap, string> = Extract<keyof TMixinMap, string>> {
90
+ /** List of all registered mixins */
91
+ protected mixins: MixinObj<any, any>[];
92
+ /** Default configuration object for mixins */
93
+ protected readonly defaultMixinCfg: MixinConfig;
94
+ /** Whether the priorities should auto-increment if not specified */
95
+ protected readonly autoIncPrioEnabled: boolean;
96
+ /** The current auto-increment priority counter */
97
+ protected autoIncPrioCounter: Map<TMixinKey, number>;
98
+ /**
99
+ * Creates a new Mixins instance.
100
+ * @param config Configuration object to customize the behavior.
101
+ */
102
+ constructor(config?: Partial<MixinsConstructorConfig>);
103
+ /**
104
+ * Adds a mixin function to the given {@linkcode mixinKey}.
105
+ * If no priority is specified, it will be calculated via the protected method {@linkcode calcPriority()} based on the constructor configuration, or fall back to the default priority.
106
+ * @param mixinKey The key to identify the mixin function.
107
+ * @param mixinFn The function to be called to apply the mixin. The first argument is the input value, the second argument is the context object (if any).
108
+ * @param config Configuration object to customize the mixin behavior, or just the priority if a number is passed.
109
+ * @returns Returns a cleanup function, to be called when this mixin is no longer needed.
110
+ */
111
+ add<TKey extends TMixinKey, TArg extends Parameters<TMixinMap[TKey]>[0], TCtx extends Parameters<TMixinMap[TKey]>[1]>(mixinKey: TKey, mixinFn: (arg: TArg, ...ctx: TCtx extends undefined ? [void] : [TCtx]) => ReturnType<TMixinMap[TKey]> extends Promise<any> ? ReturnType<TMixinMap[TKey]> | Awaited<ReturnType<TMixinMap[TKey]>> : ReturnType<TMixinMap[TKey]>, config?: Partial<MixinConfig> | number): () => void;
112
+ /** Returns a list of all added mixins with their keys and configuration objects, but not their functions */
113
+ list(): ({
114
+ key: string;
115
+ } & MixinConfig)[];
116
+ /**
117
+ * Applies all mixins with the given key to the input value, respecting the priority and stopPropagation settings.
118
+ * If additional context is set in the MixinMap, it will need to be passed as the third argument.
119
+ * @returns The modified value after all mixins have been applied.
120
+ */
121
+ resolve<TKey extends TMixinKey, TArg extends Parameters<TMixinMap[TKey]>[0], TCtx extends Parameters<TMixinMap[TKey]>[1]>(mixinKey: TKey, inputValue: TArg, ...inputCtx: TCtx extends undefined ? [void] : [TCtx]): ReturnType<TMixinMap[TKey]> extends Promise<any> ? ReturnType<TMixinMap[TKey]> : ReturnType<TMixinMap[TKey]>;
122
+ /** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
123
+ protected calcPriority(mixinKey: TMixinKey, config: Partial<MixinConfig>): number | undefined;
124
+ /** Removes all mixins with the given key */
125
+ protected removeAll(mixinKey: TMixinKey): void;
126
+ }
127
+ export {};
@@ -7,18 +7,65 @@ export interface NanoEmitterOptions {
7
7
  /** If set to true, allows emitting events through the public method emit() */
8
8
  publicEmit: boolean;
9
9
  }
10
- /** Class that can be extended or instantiated by itself to create an event emitter with helper methods and a strongly typed event map */
10
+ /**
11
+ * Class that can be extended or instantiated by itself to create a lightweight event emitter with helper methods and a strongly typed event map.
12
+ * If extended from, you can use `this.events.emit()` to emit events, even if the `emit()` method doesn't work because `publicEmit` is not set to true in the constructor.
13
+ */
11
14
  export declare class NanoEmitter<TEvtMap extends EventsMap = DefaultEvents> {
12
15
  protected readonly events: Emitter<TEvtMap>;
13
16
  protected eventUnsubscribes: Unsubscribe[];
14
17
  protected emitterOptions: NanoEmitterOptions;
18
+ /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
15
19
  constructor(options?: Partial<NanoEmitterOptions>);
16
- /** Subscribes to an event - returns a function that unsubscribes the event listener */
20
+ /**
21
+ * Subscribes to an event and calls the callback when it's emitted.
22
+ * @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 "_")
23
+ * @returns Returns a function that can be called to unsubscribe the event listener
24
+ * @example ```ts
25
+ * const emitter = new NanoEmitter<{
26
+ * foo: (bar: string) => void;
27
+ * }>({
28
+ * publicEmit: true,
29
+ * });
30
+ *
31
+ * let i = 0;
32
+ * const unsub = emitter.on("foo", (bar) => {
33
+ * // unsubscribe after 10 events:
34
+ * if(++i === 10) unsub();
35
+ * console.log(bar);
36
+ * });
37
+ *
38
+ * emitter.emit("foo", "bar");
39
+ * ```
40
+ */
17
41
  on<TKey extends keyof TEvtMap>(event: TKey | "_", cb: TEvtMap[TKey]): () => void;
18
- /** Subscribes to an event and calls the callback or resolves the Promise only once */
42
+ /**
43
+ * Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
44
+ * @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 "_")
45
+ * @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
46
+ * @returns Returns a Promise that resolves with the event arguments when the event is emitted
47
+ * @example ```ts
48
+ * const emitter = new NanoEmitter<{
49
+ * foo: (bar: string) => void;
50
+ * }>();
51
+ *
52
+ * // Promise syntax:
53
+ * const [bar] = await emitter.once("foo");
54
+ * console.log(bar);
55
+ *
56
+ * // Callback syntax:
57
+ * emitter.once("foo", (bar) => console.log(bar));
58
+ * ```
59
+ */
19
60
  once<TKey extends keyof TEvtMap>(event: TKey | "_", cb?: TEvtMap[TKey]): Promise<Parameters<TEvtMap[TKey]>>;
20
- /** Emits an event on this instance - Needs `publicEmit` to be set to true in the constructor! */
61
+ /**
62
+ * Emits an event on this instance.
63
+ * ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
64
+ * @param event The event to emit
65
+ * @param args The arguments to pass to the event listeners
66
+ * @returns Returns true if `publicEmit` is true and the event was emitted successfully
67
+ */
21
68
  emit<TKey extends keyof TEvtMap>(event: TKey, ...args: Parameters<TEvtMap[TKey]>): boolean;
22
- /** Unsubscribes all event listeners */
69
+ /** Unsubscribes all event listeners from this instance */
23
70
  unsubscribeAll(): void;
24
71
  }
package/dist/lib/dom.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function addParent<TElem extends Element, TParentElem extends Ele
20
20
  export declare function addGlobalStyle(style: string): HTMLStyleElement;
21
21
  /**
22
22
  * Preloads an array of image URLs so they can be loaded instantly from the browser cache later on
23
- * @param rejects If set to `true`, the returned PromiseSettledResults will contain rejections for any of the images that failed to load
23
+ * @param rejects If set to `true`, the returned PromiseSettledResults will contain rejections for any of the images that failed to load. Is set to `false` by default.
24
24
  * @returns Returns an array of `PromiseSettledResult` - each resolved result will contain the loaded image element, while each rejected result will contain an `ErrorEvent`
25
25
  */
26
26
  export declare function preloadImages(srcUrls: string[], rejects?: boolean): Promise<PromiseSettledResult<HTMLImageElement>[]>;
@@ -13,6 +13,7 @@ export * from "./dom.js";
13
13
  export * from "./errors.js";
14
14
  export * from "./math.js";
15
15
  export * from "./misc.js";
16
+ export * from "./Mixins.js";
16
17
  export * from "./NanoEmitter.js";
17
18
  export * from "./SelectorObserver.js";
18
19
  export * from "./translation.js";
@@ -61,3 +61,9 @@ export declare function consumeStringGen<TStrUnion extends string>(strGen: Strin
61
61
  * Set {@linkcode zeroOnInvalid} to false to return NaN instead of 0 if the object doesn't have any of the properties.
62
62
  */
63
63
  export declare function getListLength(obj: ListWithLength, zeroOnInvalid?: boolean): number;
64
+ /**
65
+ * Turns the passed object into a "pure" object without a prototype chain, meaning it won't have any default properties like `toString`, `__proto__`, `__defineGetter__`, etc.
66
+ * This makes the object immune to prototype pollution attacks and allows for cleaner object literals, at the cost of being harder to work with in some cases.
67
+ * It also effectively transforms a `Stringifiable` value into one that will throw a TypeError when stringified instead of defaulting to `[object Object]`
68
+ */
69
+ export declare function purifyObj<TObj extends object>(obj: TObj): TObj;
@@ -165,7 +165,7 @@ declare function addTransform<TTrKey extends string = string>(transform: Transfo
165
165
  * @param patternOrFn A reference to the regular expression of the transform function, a string matching the original pattern, or a reference to the transform function to delete
166
166
  * @returns Returns true if the transform function was found and deleted, false if it wasn't found
167
167
  */
168
- declare function deleteTransform(patternOrFn: RegExp | string | TransformFn): boolean;
168
+ declare function deleteTransform(patternOrFn: RegExp | TransformFn): boolean;
169
169
  declare const tr: {
170
170
  for: <TTrKey extends string = string>(language: string, key: TTrKey, ...args: (Stringifiable | Record<string, Stringifiable>)[]) => ReturnType<typeof trFor<TTrKey>>;
171
171
  use: <TTrKey extends string = string>(language: string) => ReturnType<typeof useTr<TTrKey>>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sv443-network/userutils",
3
3
  "libName": "UserUtils",
4
- "version": "9.2.1",
4
+ "version": "9.4.0",
5
5
  "description": "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
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -14,6 +14,11 @@
14
14
  "import": "./dist/index.js"
15
15
  }
16
16
  },
17
+ "engines": {
18
+ "pnpm": ">=9",
19
+ "npm": "please-use-pnpm",
20
+ "yarn": "please-use-pnpm"
21
+ },
17
22
  "type": "module",
18
23
  "repository": {
19
24
  "type": "git",
@@ -38,6 +43,8 @@
38
43
  "devDependencies": {
39
44
  "@changesets/cli": "^2.27.11",
40
45
  "@eslint/eslintrc": "^3.2.0",
46
+ "@eslint/js": "^9.23.0",
47
+ "@testing-library/dom": "^10.4.0",
41
48
  "@types/express": "^4.17.21",
42
49
  "@types/greasemonkey": "^4.0.7",
43
50
  "@types/node": "^22.10.5",
@@ -45,15 +52,18 @@
45
52
  "@typescript-eslint/eslint-plugin": "^8.21.0",
46
53
  "@typescript-eslint/parser": "^8.21.0",
47
54
  "@typescript-eslint/utils": "^8.21.0",
55
+ "@vitest/coverage-v8": "^3.0.9",
48
56
  "concurrently": "^8.2.2",
49
57
  "eslint": "^9.18.0",
50
58
  "express": "^4.21.2",
51
59
  "globals": "^15.14.0",
60
+ "jsdom": "^26.0.0",
52
61
  "kleur": "^4.1.5",
53
62
  "tslib": "^2.8.1",
54
63
  "tsup": "^8.3.5",
55
64
  "tsx": "^4.19.2",
56
- "typescript": "^5.7.3"
65
+ "typescript": "^5.7.3",
66
+ "vitest": "^3.0.9"
57
67
  },
58
68
  "files": [
59
69
  "/dist/index.js",
@@ -63,7 +73,7 @@
63
73
  "/dist/index.umd.js",
64
74
  "/dist/lib/**.d.ts",
65
75
  "/package.json",
66
- "/README-summary.md",
76
+ "/README.md",
67
77
  "/CHANGELOG.md",
68
78
  "/LICENSE.txt"
69
79
  ],
@@ -81,8 +91,10 @@
81
91
  "publish-package-jsr": "pnpm update-jsr-version && npx jsr publish --allow-dirty",
82
92
  "check-jsr": "npx jsr publish --allow-dirty --dry-run",
83
93
  "change": "changeset",
84
- "test-serve": "node --import tsx ./test/TestPage/server.mts",
85
- "test-dev": "cd test/TestScript && pnpm dev",
86
- "test": "concurrently \"pnpm test-serve\" \"pnpm test-dev\""
94
+ "test-gm-serve": "node --import tsx ./test/TestPage/server.mts",
95
+ "test-gm-dev": "cd test/TestScript && pnpm dev",
96
+ "test-gm": "concurrently \"pnpm test-gm-serve\" \"pnpm test-gm-dev\"",
97
+ "test": "vitest --passWithNoTests",
98
+ "test-coverage": "vitest --passWithNoTests --coverage"
87
99
  }
88
100
  }
package/README-summary.md DELETED
@@ -1,213 +0,0 @@
1
- ## UserUtils
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
- Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
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
-
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
-
8
- If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
9
-
10
- <br>
11
-
12
- [![minified bundle size badge](https://badgen.net/bundlephobia/min/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
13
- [![minified and gzipped bundle size badge](https://badgen.net/bundlephobia/minzip/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
14
- [![tree shaking support badge](https://badgen.net/bundlephobia/tree-shaking/@sv443-network/userutils)](https://bundlephobia.com/package/@sv443-network/userutils)
15
-
16
- [![github stargazers badge](https://badgen.net/github/stars/Sv443-Network/UserUtils?icon=github)](https://github.com/Sv443-Network/UserUtils/stargazers)
17
- [![discord server badge](https://badgen.net/discord/online-members/aBH4uRG?icon=discord)](https://dc.sv443.net/)
18
-
19
- <br>
20
-
21
- ## &gt; [Full documentation on GitHub](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#readme) &lt;
22
-
23
- <br>
24
-
25
- <span style="font-size: 0.8em;">
26
-
27
- View the documentation of previous major releases:
28
- <a href="https://github.com/Sv443-Network/UserUtils/blob/v8.0.0/README.md" rel="noopener noreferrer">8.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v7.0.0/README.md" rel="noopener noreferrer">7.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v6.0.0/README.md" rel="noopener noreferrer">6.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.0/README.md" rel="noopener noreferrer">5.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.0.0/README.md" rel="noopener noreferrer">4.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" rel="noopener noreferrer">3.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.0/README.md" rel="noopener noreferrer">2.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.0.0/README.md" rel="noopener noreferrer">1.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" rel="noopener noreferrer">0.5.3</a>
29
- <!-- <a href="https://github.com/Sv443-Network/UserUtils/blob/vX.0.0/docs.md" rel="noopener noreferrer">X.0.0</a>, -->
30
- </span>
31
-
32
- <br>
33
-
34
- <!-- https://github.com/Sv443-Network/UserUtils < #foo -->
35
- ## Feature Summary:
36
- - **DOM:**
37
- - [`SelectorObserver`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#selectorobserver) - class that manages listeners that are called when selectors are found in the DOM
38
- - [`getUnsafeWindow()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#getunsafewindow) - get the unsafeWindow object or fall back to the regular window object
39
- - [`isDomLoaded()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#isdomloaded) - check if the DOM has finished loading and can be queried and modified
40
- - [`onDomLoad()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#ondomload) - run a function or pause async execution until the DOM has finished loading (or immediately if DOM is already loaded)
41
- - [`addParent()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#addparent) - add a parent element around another element
42
- - [`addGlobalStyle()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#addglobalstyle) - add a global style to the page
43
- - [`preloadImages()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#preloadimages) - preload images into the browser cache for faster loading later on
44
- - [`openInNewTab()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#openinnewtab) - open a link in a new tab
45
- - [`interceptEvent()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#interceptevent) - conditionally intercepts events registered by `addEventListener()` on any given EventTarget object
46
- - [`interceptWindowEvent()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#interceptwindowevent) - conditionally intercepts events registered by `addEventListener()` on the window object
47
- - [`isScrollable()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#isscrollable) - check if an element has a horizontal or vertical scroll bar
48
- - [`observeElementProp()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#observeelementprop) - observe changes to an element's property that can't be observed with MutationObserver
49
- - [`getSiblingsFrame()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#getsiblingsframe) - returns a frame of an element's siblings, with a given alignment and size
50
- - [`setInnerHtmlUnsafe()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#setinnerhtmlunsafe) - set the innerHTML of an element using a [Trusted Types policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) without sanitizing or escaping it
51
- - [`probeElementStyle()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#probeelementstyle) - probe the computed style of a temporary element (get default font size, resolve CSS variables, etc.)
52
- - **Math:**
53
- - [`clamp()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#clamp) - constrain a number between a min and max value
54
- - [`mapRange()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#maprange) - map a number from one range to the same spot in another range
55
- - [`randRange()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randrange) - generate a random number between a min and max boundary
56
- - [`digitCount()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#digitcount) - calculate the amount of digits in a number
57
- - [`roundFixed()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#roundfixed) - round a floating-point number at the given amount of decimals, or to the given power of 10
58
- - [`bitSetHas()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#bitsethas) - check if a bit is set in a [bitset](https://www.geeksforgeeks.org/cpp-bitset-and-its-application/)
59
- - **Misc:**
60
- - [`DataStore`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
61
- - [`DataStoreSerializer`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
62
- - [`Dialog`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#dialog) - class for creating custom modal dialogs with a promise-based API and a generic, default style
63
- - [`NanoEmitter`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#nanoemitter) - tiny event emitter class with a focus on performance and simplicity (based on [nanoevents](https://npmjs.com/package/nanoevents))
64
- - [`Debouncer`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debouncer) - class for debouncing function calls with a given timeout
65
- - [`debounce()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debounce) - function wrapper for the Debouncer class for easier usage
66
- - [`autoPlural()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#autoplural) - automatically pluralize a string
67
- - [`pauseFor()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#pausefor) - pause the execution of a function for a given amount of time
68
- - [`fetchAdvanced()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#fetchadvanced) - wrapper around the fetch API with a timeout option
69
- - [`insertValues()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#insertvalues) - insert values into a string at specified placeholders
70
- - [`compress()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#compress) - compress a string with Gzip or Deflate
71
- - [`decompress()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#decompress) - decompress a previously compressed string
72
- - [`computeHash()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#computehash) - compute the hash / checksum of a string or ArrayBuffer
73
- - [`randomId()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomid) - generate a random ID of a given length and radix
74
- - [`consumeGen()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#consumegen) - consumes a ValueGen and returns the value
75
- - [`consumeStringGen()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#consumestringgen) - consumes a StringGen and returns the string
76
- - [`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
77
- - **Arrays:**
78
- - [`randomItem()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randomitem) - returns a random item from an array
79
- - [`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
80
- - [`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
81
- - [`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
82
- - **Translation:**
83
- - [`tr.for()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trfor) - translates a key for the specified language
84
- - [`tr.use()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#truse) - creates a translation function for the specified language
85
- - [`tr.hasKey()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trhaskey) - checks if a key exists in the given language
86
- - [`tr.addTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#traddtranslations) - add a flat or recursive translation object for a language
87
- - [`tr.getTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgettranslations) - returns the translation object for a language
88
- - [`tr.deleteTranslations()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetranslations) - delete the translation object for a language
89
- - [`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
90
- - [`tr.getFallbackLanguage()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trgetfallbacklanguage) - returns the fallback language
91
- - [`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
92
- - [`tr.deleteTransform()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trdeletetransform) - removes a transform function
93
- - [`tr.transforms`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#trtransforms) - predefined transform functions for quickly adding custom argument insertion
94
- - [`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
95
- - **Colors:**
96
- - [`hexToRgb()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#hextorgb) - convert a hex color string to an RGB or RGBA value tuple
97
- - [`rgbToHex()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#rgbtohex) - convert RGB or RGBA values to a hex color string
98
- - [`lightenColor()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#lightencolor) - lighten a CSS color string (hex, rgb or rgba) by a given percentage
99
- - [`darkenColor()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#darkencolor) - darken a CSS color string (hex, rgb or rgba) by a given percentage
100
- - **Utility types for TypeScript:**
101
- - [`Stringifiable`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#stringifiable) - any value that is a string or can be converted to one (implicitly or explicitly)
102
- - [`NonEmptyArray`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#nonemptyarray) - any array that should have at least one item
103
- - [`NonEmptyString`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#nonemptystring) - any string that should have at least one character
104
- - [`LooseUnion`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#looseunion) - a union that gives autocomplete in the IDE but also allows any other value of the same type
105
- - [`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
106
- - [`ValueGen`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#valuegen) - a "generator" value that allows for super flexible value typing and declaration
107
- - [`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
108
- - [`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
109
- - **Custom Error classes:**
110
- - [`UUError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#uuerror) - base class for all custom UserUtils errors - has a custom `date` prop set to the time of creation
111
- - [`ChecksumMismatchError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#checksummismatcherror) - thrown when a string of data doesn't match its checksum
112
- - [`MigrationError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#migrationerror) - thrown when a data migration fails
113
- - [`PlatformError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#platformerror) - thrown when a function is called in an unsupported environment
114
-
115
- <br><br>
116
-
117
- ## Installation:
118
- Shameless plug: I made a [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.
119
-
120
- - If you are using a bundler (like webpack, rollup, vite, etc.), you can install this package in one of the following ways:
121
- ```
122
- npm i @sv443-network/userutils
123
- pnpm i @sv443-network/userutils
124
- yarn add @sv443-network/userutils
125
- npx jsr install @sv443-network/userutils
126
- deno add jsr:@sv443-network/userutils
127
- ```
128
- Then import it in your script as usual:
129
-
130
- ```ts
131
- // on Node:
132
- import { addGlobalStyle } from "@sv443-network/userutils";
133
-
134
- // on Deno:
135
- import { addGlobalStyle } from "jsr:@sv443-network/userutils";
136
-
137
- // you can also import the entire library as an object (not recommended because of worse treeshaking support):
138
- import * as UserUtils from "@sv443-network/userutils";
139
- ```
140
-
141
-
142
- <br>
143
-
144
- - 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:
145
- Versioned (recommended):
146
-
147
- ```
148
- // @require https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
149
- // @require https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
150
- ```
151
- Non-versioned (not recommended because auto-updating):
152
-
153
- ```
154
- // @require https://update.greasyfork.org/scripts/472956/UserUtils.js
155
- // @require https://openuserjs.org/src/libs/Sv443/UserUtils.js
156
- ```
157
-
158
- - 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;:
159
- ```html
160
- <script src="https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
161
- <script src="https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js"></script>
162
- ```
163
-
164
- > **Note:**
165
- > 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)
166
-
167
- <br>
168
-
169
- - Then, access the functions on the global variable `UserUtils`:
170
- ```ts
171
- UserUtils.addGlobalStyle("body { background-color: red; }");
172
-
173
- // or using object destructuring:
174
-
175
- const { clamp } = UserUtils;
176
- console.log(clamp(1, 5, 10)); // 5
177
- ```
178
-
179
- <br>
180
-
181
- - 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:
182
- ```ts
183
- declare const UserUtils: typeof import("@sv443-network/userutils");
184
-
185
- declare global {
186
- interface Window {
187
- UserUtils: typeof UserUtils;
188
- }
189
- }
190
- ```
191
-
192
- <br>
193
-
194
- - If you're using a linter like ESLint, it might complain about the global variable `UserUtils` not being defined. To fix this, add the following to your ESLint configuration file:
195
- ```json
196
- "globals": {
197
- "UserUtils": "readonly"
198
- }
199
- ```
200
-
201
- <br><br>
202
-
203
- <!-- #region License -->
204
- ## License:
205
- This library is licensed under the MIT License.
206
- See the [license file](./LICENSE.txt) for details.
207
-
208
- <br><br>
209
-
210
- ---
211
-
212
- Made with ❤️ by [Sv443](https://github.com/Sv443)
213
- If you like this library, please consider [supporting the development](https://github.com/sponsors/Sv443)