@brandup/ui-helpers 1.0.44 → 2.0.2

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/README.md CHANGED
@@ -10,36 +10,143 @@ Install NPM package [@brandup/ui-helpers](https://www.npmjs.com/package/@brandup
10
10
  npm i @brandup/ui-helpers@latest
11
11
  ```
12
12
 
13
+ ## Helper groups
14
+
15
+ The package exposes the following named helper groups:
16
+
17
+ | Import | Module |
18
+ | --- | --- |
19
+ | `ObjectHelper` | object property access by path |
20
+ | `TypeHelper` | runtime type checks |
21
+ | `FuncHelper` | timing / async helpers |
22
+ | `WordHelper` | word pluralization |
23
+ | `Guid` | GUID generation |
24
+ | `formatText` | string template formatting |
25
+
26
+ ```TypeScript
27
+ import { ObjectHelper, TypeHelper, FuncHelper, WordHelper, Guid, formatText } from "@brandup/ui-helpers";
28
+ ```
29
+
13
30
  ## String helpers
14
31
 
15
- ### Format text
32
+ `formatText` substitutes `{...}` placeholders in a template.
16
33
 
17
- Format with model values:
34
+ Format with model values (placeholders are dot-separated property paths):
18
35
 
19
36
  ```TypeScript
20
- const text = "Hello, {name}";
21
- const result = text.format({ name: "Dmitry" }); // Hello, Dmitry
37
+ import { formatText } from "@brandup/ui-helpers";
38
+
39
+ const result = formatText("Hello, {name}", { name: "Dmitry" }); // Hello, Dmitry
22
40
  ```
23
41
 
24
- Format with arguments:
42
+ Format with positional arguments (placeholders are zero-based indexes):
25
43
 
26
44
  ```TypeScript
27
- const text = "Hello, {0}";
28
- const result = text.format("Dmitry"); // Hello, Dmitry
45
+ const result = formatText("Hello, {0}", "Dmitry"); // Hello, Dmitry
46
+ ```
47
+
48
+ ## Format text
49
+
50
+ `formatText` substitutes `{...}` placeholders in a template — by name from a model object, or by zero-based index from positional arguments.
51
+
52
+ ```TypeScript
53
+ import { formatText } from "@brandup/ui-helpers";
54
+
55
+ formatText("Hello, {name}", { name: "Dmitry" }); // "Hello, Dmitry"
56
+ formatText("Hello, {0}", "Dmitry"); // "Hello, Dmitry"
29
57
  ```
30
58
 
31
59
  ## Object helpers
32
60
 
33
- ### Get value by property path
61
+ `ObjectHelper` reads nested values by a dot-separated path.
34
62
 
35
63
  ```TypeScript
36
- const model = {
37
- header: {
38
- value: "Item"
39
- }
40
- }
64
+ import { ObjectHelper } from "@brandup/ui-helpers";
65
+
66
+ const model = { header: { value: "Item" } };
67
+
68
+ ObjectHelper.getProperty(model, "header.value"); // "Item"
69
+ ObjectHelper.hasProperty(model, "header.value"); // true
70
+ ```
71
+
72
+ - `getProperty(obj, path)` — returns the resolved value; `null` when `obj` is falsy, `undefined` when any path segment is missing or a mid-path value is `null`/primitive.
73
+ - `hasProperty(obj, path)` — returns `true` if every segment of the path exists; safely returns `false` when a mid-path value is `null` or a primitive.
74
+
75
+ ## Type helpers
76
+
77
+ `TypeHelper` provides runtime type checks.
78
+
79
+ ```TypeScript
80
+ import { TypeHelper } from "@brandup/ui-helpers";
81
+
82
+ TypeHelper.isFunction(() => {}); // true
83
+ TypeHelper.isString("text"); // true
84
+ ```
85
+
86
+ - `isFunction(value)` — `true` if the value is a function.
87
+ - `isString(value)` — `true` for string primitives and `String` instances.
88
+
89
+ ## Word helpers
41
90
 
42
- const value = Object.prop(model, "header.value"); // return "Item"
91
+ `WordHelper.getWordEnd` picks a grammatical ending that agrees with a count (Russian-style pluralization).
92
+
93
+ ```TypeScript
94
+ import { WordHelper } from "@brandup/ui-helpers";
95
+
96
+ WordHelper.getWordEnd(1, "товар", "", "а", "ов"); // товар
97
+ WordHelper.getWordEnd(3, "товар", "", "а", "ов"); // товара
98
+ WordHelper.getWordEnd(5, "товар", "", "а", "ов"); // товаров
99
+ ```
100
+
101
+ - `getWordEnd(count, word, one?, two?, five?)` — appends `one` for counts ending in 1, `two` for 2–4, and `five` for 0/5–9 and 11–20.
102
+
103
+ ## Guid helpers
104
+
105
+ `Guid` generates and exposes UUID values.
106
+
107
+ ```TypeScript
108
+ import { Guid } from "@brandup/ui-helpers";
109
+
110
+ const id = Guid.createGuid(); // e.g. "3f2a1b4c-9d8e-4a23-b123-456789abcdef"
111
+ Guid.empty; // "00000000-0000-0000-0000-000000000000"
112
+ ```
113
+
114
+ - `createGuid()` — a new RFC 4122 UUID v4 string (lowercase, uses `crypto.randomUUID()`).
115
+ - `empty` — the all-zero UUID constant.
116
+
117
+ ## Func helpers
118
+
119
+ `FuncHelper` contains timing and async utilities.
120
+
121
+ ```TypeScript
122
+ import { FuncHelper } from "@brandup/ui-helpers";
123
+
124
+ // Resolve after 500ms (cancellable via AbortSignal)
125
+ await FuncHelper.delay(500);
126
+
127
+ // Keep a loading state visible for at least 1000ms
128
+ const data = await FuncHelper.minWaitAsync(() => loadData(), 1000);
129
+
130
+ // Reject with TimeoutError if the request takes longer than 5000ms
131
+ const result = await FuncHelper.timeout(fetch("/api"), 5000);
132
+ ```
133
+
134
+ Detect a timeout by checking the error type:
135
+
136
+ ```TypeScript
137
+ import { FuncHelper } from "@brandup/ui-helpers";
138
+
139
+ try {
140
+ const result = await FuncHelper.timeout(fetch("/api"), 5000);
141
+ } catch (e) {
142
+ if (e instanceof FuncHelper.TimeoutError) {
143
+ console.log("Request timed out");
144
+ }
145
+ }
146
+ ```
43
147
 
44
- const hasValue = Object.hasProp(model, "header.value"); // return true
45
- ```
148
+ - `minWait(func, minTime?)` wraps a callback so it runs no sooner than `minTime` ms after wrapping.
149
+ - `minWaitAsync(func, minTime?, abort?)` — awaits an async operation, padding so it settles no sooner than `minTime` ms.
150
+ - `delay(time, abort?)` — a promise resolved after `time` ms; rejects on abort.
151
+ - `timeout(promise, timeout, abort?)` — races `promise` against `timeout` ms; rejects with a `TimeoutError` on timeout. Throws synchronously if `timeout ≤ 0`.
152
+ - `TimeoutError` — error class thrown by `timeout` when the time limit is exceeded.
@@ -0,0 +1,130 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Wraps a callback so that, when invoked, it runs no sooner than `minTime` milliseconds
5
+ * after this wrapper was created.
6
+ *
7
+ * The deadline is measured from the moment `minWait` is called. If `minTime` is omitted
8
+ * or falsy, the original function is returned unchanged.
9
+ *
10
+ * @param func Callback to defer.
11
+ * @param minTime Minimum delay in milliseconds before the callback may run.
12
+ * @returns A wrapped function with the same arguments as `func`.
13
+ */
14
+ const minWait = (func, minTime) => {
15
+ if (!minTime)
16
+ return func;
17
+ const beginTime = Date.now();
18
+ const ret = (...args) => {
19
+ const rightTime = getRightTime(beginTime, minTime);
20
+ if (rightTime)
21
+ setTimeout(() => func(...args), rightTime);
22
+ else
23
+ func(...args);
24
+ };
25
+ return ret;
26
+ };
27
+ /**
28
+ * Awaits an async operation and guarantees the returned promise settles no sooner than
29
+ * `minTime` milliseconds after invocation, padding with an extra delay when needed.
30
+ *
31
+ * Useful for keeping spinners/loading states visible for a minimum duration. If `minTime`
32
+ * is omitted or falsy, `func` is awaited and its result returned without padding.
33
+ *
34
+ * @typeParam TResult Result type produced by `func`.
35
+ * @param func Factory returning the promise to await.
36
+ * @param minTime Minimum total duration in milliseconds.
37
+ * @param abort Optional signal used to cancel the padding delay.
38
+ * @returns The result produced by `func`.
39
+ */
40
+ async function minWaitAsync(func, minTime, abort) {
41
+ if (!minTime)
42
+ return func();
43
+ const beginTime = Date.now();
44
+ const result = await func();
45
+ const rightTime = getRightTime(beginTime, minTime);
46
+ if (rightTime)
47
+ await delay(rightTime, abort);
48
+ return result;
49
+ }
50
+ /** @internal */
51
+ const getRightTime = (start, minTime) => {
52
+ const finishTime = Date.now();
53
+ const w = minTime - (finishTime - start);
54
+ return w > minTime * 0.1 ? w : 0;
55
+ };
56
+ /**
57
+ * Returns a promise that resolves after the given number of milliseconds.
58
+ *
59
+ * If an already-aborted signal is supplied the promise rejects immediately; otherwise
60
+ * aborting before the delay elapses clears the timer and rejects with the abort reason.
61
+ *
62
+ * @param time Delay in milliseconds.
63
+ * @param abort Optional signal used to cancel the delay.
64
+ * @returns A promise that resolves when the delay elapses.
65
+ */
66
+ function delay(time, abort) {
67
+ return new Promise((resolve, reject) => {
68
+ abort?.throwIfAborted();
69
+ const onAbort = () => {
70
+ clearTimeout(timer);
71
+ reject(abort?.reason);
72
+ };
73
+ const timer = setTimeout(() => {
74
+ abort?.removeEventListener("abort", onAbort);
75
+ resolve();
76
+ }, time);
77
+ abort?.addEventListener("abort", onAbort, { once: true });
78
+ });
79
+ }
80
+ /**
81
+ * Races a promise against a timeout.
82
+ *
83
+ * Resolves/rejects with the original promise if it settles in time. If the timeout elapses
84
+ * first the returned promise rejects with a {@link TimeoutError}. Aborting via `abort`
85
+ * rejects with the signal's reason.
86
+ *
87
+ * @typeParam T Resolved value type of the wrapped promise.
88
+ * @param promise Promise to guard with a timeout.
89
+ * @param timeout Timeout in milliseconds; must be greater than `0`.
90
+ * @param abort Optional signal used to cancel the wait.
91
+ * @returns A promise mirroring `promise` unless the timeout or abort fires first.
92
+ * @throws {Error} When `timeout` is not greater than `0`.
93
+ */
94
+ function timeout(promise, timeout, abort) {
95
+ if (timeout <= 0)
96
+ throw new Error("Invalid timeout value.");
97
+ return new Promise((resolve, reject) => {
98
+ abort?.throwIfAborted();
99
+ const onAbort = () => {
100
+ clearTimeout(timer);
101
+ reject(abort?.reason);
102
+ };
103
+ const timer = setTimeout(() => {
104
+ abort?.removeEventListener("abort", onAbort);
105
+ reject(new TimeoutError());
106
+ }, timeout);
107
+ abort?.addEventListener("abort", onAbort, { once: true });
108
+ promise
109
+ .then(result => resolve(result))
110
+ .catch(reason => reject(reason))
111
+ .finally(() => {
112
+ clearTimeout(timer);
113
+ abort?.removeEventListener("abort", onAbort);
114
+ });
115
+ });
116
+ }
117
+ /** Thrown by {@link timeout} when the time limit is exceeded. */
118
+ class TimeoutError extends Error {
119
+ constructor() {
120
+ super("Timeout");
121
+ this.name = "TimeoutError";
122
+ }
123
+ }
124
+
125
+ exports.TimeoutError = TimeoutError;
126
+ exports.delay = delay;
127
+ exports.minWait = minWait;
128
+ exports.minWaitAsync = minWaitAsync;
129
+ exports.timeout = timeout;
130
+ //# sourceMappingURL=func.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"func.js","sources":["../../../../source/helpers/func.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Generates a RFC 4122 UUID v4 string using `crypto.randomUUID()`.
5
+ *
6
+ * @returns A newly generated UUID string in `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` form.
7
+ * @example
8
+ * createGuid(); // e.g. "3f2a1b4c-9d8e-4a23-b123-456789abcdef"
9
+ */
10
+ const createGuid = () => crypto.randomUUID();
11
+ /** The empty (all-zero) GUID value `"00000000-0000-0000-0000-000000000000"`. */
12
+ const empty = "00000000-0000-0000-0000-000000000000";
13
+
14
+ exports.createGuid = createGuid;
15
+ exports.empty = empty;
16
+ //# sourceMappingURL=guid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guid.js","sources":["../../../../source/helpers/guid.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;;;;;;AAMG;AACH,MAAM,UAAU,GAAG,MAAc,MAAM,CAAC,UAAU;AAElD;AACA,MAAM,KAAK,GAAG;;;;;"}
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Reads a nested property value from an object by a dot-separated path.
5
+ *
6
+ * @param obj Source object to read from.
7
+ * @param path Dot-separated property path, e.g. `"header.value"`.
8
+ * @returns The resolved value; `null` when `obj` is falsy, or `undefined` when
9
+ * any segment of the path does not exist.
10
+ * @example
11
+ * getProperty({ header: { value: "Item" } }, "header.value"); // "Item"
12
+ */
13
+ function getProperty(obj, path) {
14
+ if (!obj)
15
+ return null;
16
+ const props = path.split('.');
17
+ for (let i = 0; i < props.length; i++) {
18
+ const name = props[i];
19
+ if (obj == null || (typeof obj !== "object" && typeof obj !== "function"))
20
+ return undefined;
21
+ if (!(name in obj))
22
+ return undefined;
23
+ obj = obj[name];
24
+ }
25
+ return obj;
26
+ }
27
+ /**
28
+ * Determines whether an object has a nested property at the given dot-separated path.
29
+ *
30
+ * @param obj Source object to inspect.
31
+ * @param path Dot-separated property path, e.g. `"header.value"`.
32
+ * @returns `true` if every segment of the path exists, otherwise `false`.
33
+ * @example
34
+ * hasProperty({ header: { value: "Item" } }, "header.value"); // true
35
+ */
36
+ function hasProperty(obj, path) {
37
+ if (!obj)
38
+ return false;
39
+ const props = path.split('.');
40
+ for (let i = 0; i < props.length; i++) {
41
+ const name = props[i];
42
+ if (obj == null || (typeof obj !== "object" && typeof obj !== "function"))
43
+ return false;
44
+ if (!(name in obj))
45
+ return false;
46
+ obj = obj[name];
47
+ }
48
+ return true;
49
+ }
50
+
51
+ exports.getProperty = getProperty;
52
+ exports.hasProperty = hasProperty;
53
+ //# sourceMappingURL=object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.js","sources":["../../../../source/helpers/object.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;;;;;;;;;AASG;AACH,SAAS,WAAW,CAAC,GAAQ,EAAE,IAAY,EAAA;AAC1C,IAAA,IAAI,CAAC,GAAG;AACP,QAAA,OAAO,IAAI;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AAE7B,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,QAAA,IAAI,GAAG,IAAI,IAAI,KAAK,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,UAAU,CAAC;AACxE,YAAA,OAAO,SAAS;AAEjB,QAAA,IAAI,EAAE,IAAI,IAAI,GAAG,CAAC;AACjB,YAAA,OAAO,SAAS;AAEjB,QAAA,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC;IAChB;AAEA,IAAA,OAAO,GAAG;AACX;AAEA;;;;;;;;AAQG;AACH,SAAS,WAAW,CAAC,GAAQ,EAAE,IAAY,EAAA;AAC1C,IAAA,IAAI,CAAC,GAAG;AACP,QAAA,OAAO,KAAK;IAEb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AAE7B,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,QAAA,IAAI,GAAG,IAAI,IAAI,KAAK,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,UAAU,CAAC;AACxE,YAAA,OAAO,KAAK;AAEb,QAAA,IAAI,EAAE,IAAI,IAAI,GAAG,CAAC;AACjB,YAAA,OAAO,KAAK;AAEb,QAAA,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC;IAChB;AAEA,IAAA,OAAO,IAAI;AACZ;;;;;"}
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var object = require('./object.js');
4
+
5
+ /**
6
+ * Formats a template string by substituting `{...}` placeholders.
7
+ *
8
+ * When the first argument is an object, placeholders are treated as dot-separated
9
+ * property paths resolved against that object (see {@link getProperty}). Otherwise
10
+ * placeholders are treated as zero-based indexes into the remaining arguments.
11
+ * Unresolved placeholders are replaced with an empty string.
12
+ *
13
+ * @param template Template containing `{name}` or `{0}` placeholders.
14
+ * @param args Either a single model object, or positional arguments.
15
+ * @returns The formatted string.
16
+ * @example
17
+ * formatText("Hello, {name}", { name: "Dmitry" }); // "Hello, Dmitry"
18
+ * formatText("Hello, {0}", "Dmitry"); // "Hello, Dmitry"
19
+ */
20
+ function formatText(template, ...args) {
21
+ if (!args.length)
22
+ return template;
23
+ const obj = typeof args[0] === "object" ? args[0] : null;
24
+ return template.replace(/\{([^}]+)\}/g, (_match, key) => {
25
+ if (obj)
26
+ return object.getProperty(obj, key) ?? "";
27
+ else {
28
+ const paramIndex = parseInt(key);
29
+ if (!isNaN(paramIndex) && paramIndex < args.length)
30
+ return args[paramIndex] ?? "";
31
+ }
32
+ return "";
33
+ });
34
+ }
35
+
36
+ exports.formatText = formatText;
37
+ //# sourceMappingURL=string.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.js","sources":["../../../../source/helpers/string.ts"],"sourcesContent":[null],"names":["getProperty"],"mappings":";;;;AAEA;;;;;;;;;;;;;;AAcG;AACH,SAAS,UAAU,CAAC,QAAgB,EAAE,GAAG,IAAW,EAAA;IACnD,IAAI,CAAC,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,QAAQ;IAEjC,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI;IAExD,OAAO,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,KAAI;AACvD,QAAA,IAAI,GAAG;YAAE,OAAOA,kBAAW,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE;aACtC;AACJ,YAAA,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,IAAI,CAAC,MAAM;AACjD,gBAAA,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;QAC/B;AAEA,QAAA,OAAO,EAAE;AACV,IAAA,CAAC,CAAC;AACH;;;;"}
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Determines whether the given value is a function.
5
+ *
6
+ * @param value Value to test.
7
+ * @returns `true` if the value is a function, otherwise `false`.
8
+ */
9
+ function isFunction(value) {
10
+ return (typeof value === "function");
11
+ }
12
+ /**
13
+ * Determines whether the given value is a string.
14
+ *
15
+ * Returns `true` for both string primitives and `String` object instances.
16
+ *
17
+ * @param value Value to test.
18
+ * @returns `true` if the value is a string, otherwise `false`.
19
+ */
20
+ function isString(value) {
21
+ return (typeof value === "string" || value instanceof String);
22
+ }
23
+
24
+ exports.isFunction = isFunction;
25
+ exports.isString = isString;
26
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sources":["../../../../source/helpers/type.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;;;;;AAKG;AACH,SAAS,UAAU,CAAC,KAAU,EAAA;AAC7B,IAAA,QAAQ,OAAO,KAAK,KAAK,UAAU;AACpC;AAEA;;;;;;;AAOG;AACH,SAAS,QAAQ,CAAC,KAAU,EAAA;IAC3B,QAAQ,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM;AAC7D;;;;;"}
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Returns a word combined with the grammatical ending that agrees with the given count.
5
+ *
6
+ * Implements Russian-style pluralization rules: the ending is chosen depending on
7
+ * whether the count ends in 1, in 2–4, or in 0/5–9 (with 11–14 treated as the "five" form).
8
+ *
9
+ * @param count Quantity that the word refers to.
10
+ * @param word Word stem to which the ending is appended.
11
+ * @param one Ending used for counts ending in 1 (but not 11).
12
+ * @param two Ending used for counts ending in 2–4 (but not 12–14).
13
+ * @param five Ending used for counts ending in 0, 5–9 and 11–20.
14
+ * @returns The word with the appropriate ending appended.
15
+ * @example
16
+ * getWordEnd(1, "товар", "", "а", "ов"); // "товар"
17
+ * getWordEnd(3, "товар", "", "а", "ов"); // "товара"
18
+ * getWordEnd(5, "товар", "", "а", "ов"); // "товаров"
19
+ */
20
+ function getWordEnd(count, word, one, two, five) {
21
+ const tt = count % 100;
22
+ if (tt >= 5 && tt <= 20)
23
+ return word + (five || "");
24
+ const t = count % 10;
25
+ return (t === 1 ?
26
+ (word + (one || "")) : ((t >= 2 && t <= 4) ? (word + (two || "")) : (word + (five || ""))));
27
+ }
28
+
29
+ exports.getWordEnd = getWordEnd;
30
+ //# sourceMappingURL=word.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"word.js","sources":["../../../../source/helpers/word.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;AAgBG;AACH,SAAS,UAAU,CAAC,KAAa,EAAE,IAAY,EAAE,GAAY,EAAE,GAAY,EAAE,IAAa,EAAA;AACzF,IAAA,MAAM,EAAE,GAAG,KAAK,GAAG,GAAG;AACtB,IAAA,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;AACtB,QAAA,OAAO,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;AAE3B,IAAA,MAAM,CAAC,GAAG,KAAK,GAAG,EAAE;AAEpB,IAAA,QAAQ,CAAC,KAAK,CAAC;SACb,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AAE5F;;;;"}