@nemigo/helpers 0.0.10 → 0.0.19

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,42 @@
1
+ import type { Timestamp } from "./types.js";
2
+ /**
3
+ * Создаёт задержку на указанное количество миллисекунд
4
+ *
5
+ * @param ms - Количество миллисекунд для задержки
6
+ */
7
+ export declare const delay: (ms: Timestamp) => Promise<void>;
8
+ /**
9
+ * Создаёт debounce-функцию, которая откладывает выполнение до тех пор, пока не пройдёт указанное время без новых вызовов
10
+ *
11
+ * @param call - Функция для debounce
12
+ * @param delay - Задержка в миллисекундах
13
+ *
14
+ * @returns Функция с debounce
15
+ */
16
+ export declare const debounce: <T extends (...args: any[]) => any>(call: T, delay: Timestamp) => ((...args: Parameters<T>) => void);
17
+ /**
18
+ * Создаёт функцию-замыкание, которое гарантирует (старается), что переданная функция `call`
19
+ * будет вызвана не чаще одного раза в заданный интервал времени.
20
+ *
21
+ * mode = `"delay"`
22
+ * - первый вызов откладывается на `ms`
23
+ * - повторные вызовы до старта игнорируются, аргументы обновляются
24
+ * - все ждут одного промисса
25
+ *
26
+ * mode = `"urgent"`:
27
+ * - первый вызов исполняется сразу
28
+ * - повторные вызовы возвращают тот же промиcс в течение `ms` (даже после завершения)
29
+ * - после `ms` можно снова вызвать
30
+ */
31
+ export declare const trottling: <F extends (...args: any[]) => any>(call: F, mode: "urgent" | "delay", ms: number) => ((...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>>);
32
+ /**
33
+ * Создаёт функцию-замыкание, которая гарантирует однократное выполнение асинхронной операции
34
+ *
35
+ * При первом вызове запускает переданный `call` и сохраняет его результат.
36
+ * Все последующие вызовы возвращают сохранённый результат без повторного выполнения операции.
37
+ * Если вызов происходит во время выполнения, возвращает тот же промисс, вместо создания нового
38
+ *
39
+ * @param call - Асинхронная функция, результат которой нужно кешировать
40
+ * @returns Функция, возвращающая закешированный результат после первого успешного выполнения
41
+ */
42
+ export declare const createPromiseTrap: <T>(call: () => Promise<T>) => () => Promise<T>;
package/dist/async.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Создаёт задержку на указанное количество миллисекунд
3
+ *
4
+ * @param ms - Количество миллисекунд для задержки
5
+ */
6
+ export const delay = (ms) => new Promise((ready) => setTimeout(ready, ms));
7
+ //...
8
+ /**
9
+ * Создаёт debounce-функцию, которая откладывает выполнение до тех пор, пока не пройдёт указанное время без новых вызовов
10
+ *
11
+ * @param call - Функция для debounce
12
+ * @param delay - Задержка в миллисекундах
13
+ *
14
+ * @returns Функция с debounce
15
+ */
16
+ export const debounce = (call, delay) => {
17
+ let timeout;
18
+ return (...args) => {
19
+ if (timeout)
20
+ clearTimeout(timeout);
21
+ timeout = setTimeout(() => call(...args), delay);
22
+ };
23
+ };
24
+ /**
25
+ * Создаёт функцию-замыкание, которое гарантирует (старается), что переданная функция `call`
26
+ * будет вызвана не чаще одного раза в заданный интервал времени.
27
+ *
28
+ * mode = `"delay"`
29
+ * - первый вызов откладывается на `ms`
30
+ * - повторные вызовы до старта игнорируются, аргументы обновляются
31
+ * - все ждут одного промисса
32
+ *
33
+ * mode = `"urgent"`:
34
+ * - первый вызов исполняется сразу
35
+ * - повторные вызовы возвращают тот же промиcс в течение `ms` (даже после завершения)
36
+ * - после `ms` можно снова вызвать
37
+ */
38
+ export const trottling = (call, mode, ms) => {
39
+ let _promise = null;
40
+ let _args = [];
41
+ return (...args) => {
42
+ _args = args;
43
+ if (_promise)
44
+ return _promise;
45
+ let resolver;
46
+ let rejecter;
47
+ _promise = new Promise((resolve, reject) => {
48
+ resolver = resolve;
49
+ rejecter = reject;
50
+ });
51
+ const reset = () => (_promise = null);
52
+ if (mode === "delay") {
53
+ setTimeout(() => {
54
+ call(..._args)
55
+ .then(resolver)
56
+ .catch(rejecter)
57
+ .finally(reset);
58
+ }, ms);
59
+ }
60
+ else {
61
+ call(..._args)
62
+ .then(resolver)
63
+ .catch(rejecter)
64
+ .finally(delay(ms).then(reset));
65
+ }
66
+ return _promise;
67
+ };
68
+ };
69
+ //...
70
+ /**
71
+ * Создаёт функцию-замыкание, которая гарантирует однократное выполнение асинхронной операции
72
+ *
73
+ * При первом вызове запускает переданный `call` и сохраняет его результат.
74
+ * Все последующие вызовы возвращают сохранённый результат без повторного выполнения операции.
75
+ * Если вызов происходит во время выполнения, возвращает тот же промисс, вместо создания нового
76
+ *
77
+ * @param call - Асинхронная функция, результат которой нужно кешировать
78
+ * @returns Функция, возвращающая закешированный результат после первого успешного выполнения
79
+ */
80
+ export const createPromiseTrap = (call) => {
81
+ const mark = Symbol();
82
+ let value = mark;
83
+ let promise = null;
84
+ return () => {
85
+ if (value !== mark)
86
+ return value;
87
+ if (!promise) {
88
+ promise = call().then((result) => {
89
+ value = result;
90
+ return result;
91
+ });
92
+ }
93
+ return promise;
94
+ };
95
+ };
@@ -0,0 +1,44 @@
1
+ import type { Timestamp } from "./types.js";
2
+ /**
3
+ * Русифицированные названия месяцев в родительском падеже
4
+ */
5
+ export declare const months: string[];
6
+ /**
7
+ * Русифицированные сокращённые названия месяцев в родительском падеже
8
+ */
9
+ export declare const abbs: string[];
10
+ /**
11
+ * Шаблонизатор вывода даты и времени из {@link Timestamp}
12
+ *
13
+ * TS - секунды
14
+ *
15
+ * TM - минуты
16
+ *
17
+ * DD - дни
18
+ *
19
+ * DM - месяц числом
20
+ *
21
+ * DFM - месяц полным названием
22
+ *
23
+ * DAM - месяц сокращённым названием
24
+ *
25
+ * DY - год
26
+ *
27
+ * @example
28
+ * const formatted = dateTimeFormatter(new Date(), 'DD.DM.DY TH:TM:TS');
29
+ * console.log(formatted); // "09.10.2023 14:30:59"
30
+ */
31
+ export declare const formatDateTime: (datetime?: Date | Timestamp | string, format?: string) => string;
32
+ /**
33
+ * Конвертирует строковые представления даты и времени в {@link Timestamp}
34
+ *
35
+ * @param date - Строка даты в формате "DD.MM.YYYY"
36
+ * @param [time="00:00"] - Строка времени в формате "HH:MM"
37
+ *
38
+ * @returns TimeStamp соответствующий указанной дате и времени или 0 при неверном формате
39
+ *
40
+ * @example
41
+ * const timestamp = toTimeStamp("11.02.2012", "13:45");
42
+ * console.log(timestamp); // Выведет {@link Timestamp} для "11.02.2012 13:45"
43
+ */
44
+ export declare const toTimeStamp: (date: string, time?: string) => Timestamp | 0;
@@ -0,0 +1,109 @@
1
+ import { pad } from "./index.js";
2
+ /**
3
+ * Русифицированные названия месяцев в родительском падеже
4
+ */
5
+ export const months = [
6
+ "января",
7
+ "февраля",
8
+ "марта",
9
+ "апреля",
10
+ "мая",
11
+ "июня",
12
+ "июля",
13
+ "августа",
14
+ "сентября",
15
+ "октября",
16
+ "ноября",
17
+ "декабря",
18
+ ];
19
+ /**
20
+ * Русифицированные сокращённые названия месяцев в родительском падеже
21
+ */
22
+ export const abbs = [
23
+ "янв",
24
+ "фев",
25
+ "марта",
26
+ "апр",
27
+ "мая",
28
+ "июня",
29
+ "июля",
30
+ "авг",
31
+ "сен",
32
+ "окт",
33
+ "ноя",
34
+ "дек",
35
+ ];
36
+ /**
37
+ * Шаблонизатор вывода даты и времени из {@link Timestamp}
38
+ *
39
+ * TS - секунды
40
+ *
41
+ * TM - минуты
42
+ *
43
+ * DD - дни
44
+ *
45
+ * DM - месяц числом
46
+ *
47
+ * DFM - месяц полным названием
48
+ *
49
+ * DAM - месяц сокращённым названием
50
+ *
51
+ * DY - год
52
+ *
53
+ * @example
54
+ * const formatted = dateTimeFormatter(new Date(), 'DD.DM.DY TH:TM:TS');
55
+ * console.log(formatted); // "09.10.2023 14:30:59"
56
+ */
57
+ export const formatDateTime = (datetime = Date.now(), format = "DD.DM.DY TH:TM") => {
58
+ const date = new Date(datetime);
59
+ const replacements = {
60
+ TS: pad(date.getSeconds()),
61
+ TM: pad(date.getMinutes()),
62
+ TH: pad(date.getHours()),
63
+ //...
64
+ DD: pad(date.getDate()),
65
+ DM: pad(date.getMonth() + 1),
66
+ DFM: months[date.getMonth()],
67
+ DAM: abbs[date.getMonth()],
68
+ DY: date.getFullYear().toString(),
69
+ };
70
+ const result = [];
71
+ let i = 0;
72
+ while (i < format.length) {
73
+ let found = false;
74
+ for (const key in replacements) {
75
+ if (format.startsWith(key, i)) {
76
+ result.push(replacements[key]);
77
+ i += key.length;
78
+ found = true;
79
+ break;
80
+ }
81
+ }
82
+ if (!found) {
83
+ result.push(format[i]);
84
+ i++;
85
+ }
86
+ }
87
+ return result.join("");
88
+ };
89
+ /**
90
+ * Конвертирует строковые представления даты и времени в {@link Timestamp}
91
+ *
92
+ * @param date - Строка даты в формате "DD.MM.YYYY"
93
+ * @param [time="00:00"] - Строка времени в формате "HH:MM"
94
+ *
95
+ * @returns TimeStamp соответствующий указанной дате и времени или 0 при неверном формате
96
+ *
97
+ * @example
98
+ * const timestamp = toTimeStamp("11.02.2012", "13:45");
99
+ * console.log(timestamp); // Выведет {@link Timestamp} для "11.02.2012 13:45"
100
+ */
101
+ export const toTimeStamp = (date, time = "00:00") => {
102
+ const [day, month, year] = date.split(".").map(Number);
103
+ const [hours, minutes] = time.split(":").map(Number);
104
+ // Проверка на корректность ввода (если хотя бы одно значение не число)
105
+ // prettier-ignore
106
+ if ([day, month, year, hours, minutes].some(isNaN))
107
+ return 0;
108
+ return new Date(year, month - 1, day, hours, minutes).valueOf();
109
+ };
@@ -0,0 +1 @@
1
+ export declare const fileToBase64: (file: File) => Promise<string>;
package/dist/files.js ADDED
@@ -0,0 +1,6 @@
1
+ export const fileToBase64 = (file) => new Promise((resolve, reject) => {
2
+ const reader = new FileReader();
3
+ reader.onload = () => resolve(reader.result);
4
+ reader.onerror = reject;
5
+ reader.readAsDataURL(file);
6
+ });
@@ -0,0 +1,31 @@
1
+ import type { CanBeNullable } from "./types.js";
2
+ interface FormatNumberConfig {
3
+ /**
4
+ * @default 2
5
+ */
6
+ fraction?: number;
7
+ /**
8
+ * @default "---"
9
+ */
10
+ fallback?: string;
11
+ /**
12
+ * @example "₽"
13
+ */
14
+ postfix?: string;
15
+ /**
16
+ * - "always" - "0" для всех исключительных ситуаций
17
+ * - "value" - "0" только при значении равном нулю, иначе `fallback`
18
+ * - "never" - `fallback` всегда
19
+ *
20
+ * @default value
21
+ */
22
+ zero?: "always" | "value" | "never";
23
+ }
24
+ export declare const FormatNumberRegExp: RegExp;
25
+ /**
26
+ * Форматирование числа по тысячам
27
+ *
28
+ * @example "1 234" или "1 234,56"
29
+ */
30
+ export declare const formatNumber: (amount: CanBeNullable<number | string>, { fraction, fallback, zero, postfix }?: FormatNumberConfig) => string;
31
+ export {};
package/dist/format.js ADDED
@@ -0,0 +1,20 @@
1
+ import { caseNumber } from "./cases.js";
2
+ import { toRound } from "./phymath.js";
3
+ export const FormatNumberRegExp = /\B(?=(\d{3})+(?!\d))/g;
4
+ /**
5
+ * Форматирование числа по тысячам
6
+ *
7
+ * @example "1 234" или "1 234,56"
8
+ */
9
+ export const formatNumber = (amount, { fraction = 2, fallback = "---", zero = "value", postfix } = {}) => caseNumber(amount, (v) => {
10
+ if (v === undefined)
11
+ return zero === "always" ? (postfix ? "0 " + postfix : "0") : fallback;
12
+ if (v === 0)
13
+ return zero === "never" ? fallback : postfix ? "0 " + postfix : "0";
14
+ if (fraction === 0)
15
+ return Math.round(v).toString().replace(FormatNumberRegExp, " ");
16
+ const { 0: integer, 1: fractional = "" } = toRound(v, fraction).toString().split(".");
17
+ const result = integer.replace(FormatNumberRegExp, " ");
18
+ const formatted = fractional ? `${result},${fractional}` : result;
19
+ return postfix ? formatted + " " + postfix : formatted;
20
+ });
package/dist/html.d.ts ADDED
@@ -0,0 +1,58 @@
1
+ import type { CanBeNullable, CanBePromise, KEYS } from "./types";
2
+ /**
3
+ * Проверка по глобальному `window`
4
+ */
5
+ export declare const isSSR: boolean;
6
+ /**
7
+ * Предотвращает дефолтное поведение события и останавливает всплытие
8
+ */
9
+ export declare const preventStop: (e: Event) => void;
10
+ /**
11
+ * Создаёт обработчик события с {@link preventStop}, который выполняется всегда.
12
+ * Коллбэк выполняется только, если load вернёт `false` (при его наличии)
13
+ */
14
+ export declare const preventStopHook: <T extends Event = Event>(call?: CanBeNullable<(e: T) => CanBePromise>, load?: () => boolean | undefined) => ((e: T) => void);
15
+ /**
16
+ * Возвращает обработчик клавиатурных событий, который:
17
+ *
18
+ * 1. Если `usePreventStop === true`, вызывает {@link preventStop}
19
+ * 2. Если load вернёт `false` (при его наличии), то выполнение остановится
20
+ * 3. Вызывает переданный **prepare-коллбэк** (при его наличии), если он вернёт `false`, то выполнение остановится
21
+ * 4. Вызывает основной коллбэк только при нажатии `Enter`.
22
+ */
23
+ export declare const enterKeyHook: <T extends KeyboardEvent = KeyboardEvent>(call?: CanBeNullable<(e: T) => CanBePromise>, config?: {
24
+ usePreventStop?: "enter" | boolean | ((e: T, isEnter: boolean) => boolean);
25
+ prepare?: (e: T) => CanBePromise<boolean>;
26
+ load?: () => boolean | undefined;
27
+ }) => (e: T) => void;
28
+ /**
29
+ * Создаёт обработчик событий для указанного элемента или окна
30
+ *
31
+ * @template T - Тип события из `WindowEventMap`
32
+ * @template E - Тип объекта события (по умолчанию `WindowEventMap[T]`)
33
+ *
34
+ * @param {T} event - Ключ события
35
+ * @param {(e: E) => CanBePromise} call - Функция-обработчик события
36
+ * @param {AddEventListenerOptions} [options] - Дополнительные параметры
37
+ * @param {HTMLElement | Window} [target=window] - Целевой элемент для привязки события
38
+ *
39
+ * @returns Функция для удаления обработчика события
40
+ */
41
+ export declare const createEventListener: <const M = WindowEventMap, const T extends KEYS<M> = KEYS<M>, const E = M[T]>(event: T, call: (e: E) => CanBePromise, options?: AddEventListenerOptions, target?: HTMLElement | Window | Document) => (() => void);
42
+ /**
43
+ * Убирает все выделения текста на странице
44
+ */
45
+ export declare const unselect: () => void;
46
+ /**
47
+ * Создаёт once-обработчик события "mousedown" с {@link unselect}
48
+ *
49
+ * @returns Функция для удаления обработчика события
50
+ */
51
+ export declare const unselectByMousedown: () => (() => void);
52
+ /**
53
+ * Преобразование `{@link FormData}` в объект
54
+ *
55
+ * @param form - данные формы
56
+ * @param multi - поддержка массивов значений
57
+ */
58
+ export declare const formon: <D = any>(form: FormData, multi?: boolean) => D;
package/dist/html.js ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Проверка по глобальному `window`
3
+ */
4
+ export const isSSR = typeof window === "undefined";
5
+ /**
6
+ * Предотвращает дефолтное поведение события и останавливает всплытие
7
+ */
8
+ export const preventStop = (e) => (e.preventDefault(), e.stopPropagation());
9
+ /**
10
+ * Создаёт обработчик события с {@link preventStop}, который выполняется всегда.
11
+ * Коллбэк выполняется только, если load вернёт `false` (при его наличии)
12
+ */
13
+ export const preventStopHook = (call, load) => (e) => {
14
+ preventStop(e);
15
+ if (!load || !load())
16
+ call?.(e);
17
+ };
18
+ //...
19
+ /**
20
+ * Возвращает обработчик клавиатурных событий, который:
21
+ *
22
+ * 1. Если `usePreventStop === true`, вызывает {@link preventStop}
23
+ * 2. Если load вернёт `false` (при его наличии), то выполнение остановится
24
+ * 3. Вызывает переданный **prepare-коллбэк** (при его наличии), если он вернёт `false`, то выполнение остановится
25
+ * 4. Вызывает основной коллбэк только при нажатии `Enter`.
26
+ */
27
+ export const enterKeyHook = (call, config = { usePreventStop: "enter" }) => (e) => {
28
+ const isEnter = e.key === "Enter";
29
+ if (config?.usePreventStop === "enter") {
30
+ if (isEnter)
31
+ preventStop(e);
32
+ }
33
+ else {
34
+ if (config?.usePreventStop !== undefined) {
35
+ if (typeof config?.usePreventStop === "boolean") {
36
+ if (config?.usePreventStop)
37
+ preventStop(e);
38
+ }
39
+ else {
40
+ if (config?.usePreventStop(e, isEnter))
41
+ preventStop(e);
42
+ }
43
+ }
44
+ }
45
+ if (config?.load?.())
46
+ return;
47
+ if (config?.prepare) {
48
+ Promise.resolve(config?.prepare?.(e)).then((next) => {
49
+ if (next && isEnter)
50
+ call?.(e);
51
+ });
52
+ }
53
+ else {
54
+ if (isEnter)
55
+ call?.(e);
56
+ }
57
+ };
58
+ //...
59
+ /**
60
+ * Создаёт обработчик событий для указанного элемента или окна
61
+ *
62
+ * @template T - Тип события из `WindowEventMap`
63
+ * @template E - Тип объекта события (по умолчанию `WindowEventMap[T]`)
64
+ *
65
+ * @param {T} event - Ключ события
66
+ * @param {(e: E) => CanBePromise} call - Функция-обработчик события
67
+ * @param {AddEventListenerOptions} [options] - Дополнительные параметры
68
+ * @param {HTMLElement | Window} [target=window] - Целевой элемент для привязки события
69
+ *
70
+ * @returns Функция для удаления обработчика события
71
+ */
72
+ export const createEventListener = (event, call, options = {}, target = window) => {
73
+ target.addEventListener(event, call, options);
74
+ return () => target.removeEventListener(event, call);
75
+ };
76
+ //...
77
+ /**
78
+ * Убирает все выделения текста на странице
79
+ */
80
+ export const unselect = () => document.getSelection()?.removeAllRanges();
81
+ /**
82
+ * Создаёт once-обработчик события "mousedown" с {@link unselect}
83
+ *
84
+ * @returns Функция для удаления обработчика события
85
+ */
86
+ export const unselectByMousedown = () => createEventListener("mousedown", unselect, { once: true });
87
+ //...
88
+ /**
89
+ * Преобразование `{@link FormData}` в объект
90
+ *
91
+ * @param form - данные формы
92
+ * @param multi - поддержка массивов значений
93
+ */
94
+ export const formon = (form, multi = false) => {
95
+ if (multi) {
96
+ const result = {};
97
+ for (const { 0: key, 1: value } of form.entries()) {
98
+ if (result[key] !== undefined) {
99
+ if (!Array.isArray(result[key])) {
100
+ result[key] = [result[key]];
101
+ }
102
+ result[key].push(value);
103
+ }
104
+ else {
105
+ result[key] = value;
106
+ }
107
+ }
108
+ return result;
109
+ }
110
+ else {
111
+ return Object.fromEntries(form.entries());
112
+ }
113
+ };
@@ -0,0 +1,5 @@
1
+ export type RU_CASES = "nominative" | "genitive" | "dative" | "accusative" | "ablative";
2
+ /**
3
+ * Форма слова 'рубль' в правильном падеже и числе в зависимости от количества
4
+ */
5
+ export declare const Rubles: (value: number, form?: RU_CASES) => string;
@@ -0,0 +1,46 @@
1
+ //...
2
+ /**
3
+ * Основные формы слова 'рубль'
4
+ */
5
+ const RublesMainSwitch = (form) => {
6
+ switch (form) {
7
+ case "dative":
8
+ return "рублям";
9
+ case "ablative":
10
+ return "рублями";
11
+ default:
12
+ return "рублей";
13
+ }
14
+ };
15
+ /**
16
+ * Форма слова 'рубль' в правильном падеже и числе в зависимости от количества
17
+ */
18
+ export const Rubles = (value, form = "nominative") => {
19
+ const lastTwoDigits = Math.abs(value % 100);
20
+ if (lastTwoDigits > 4 && lastTwoDigits < 21)
21
+ return RublesMainSwitch(form);
22
+ const lastDigit = Math.abs(value % 10);
23
+ if (lastDigit >= 2 && lastDigit <= 4) {
24
+ switch (form) {
25
+ case "nominative":
26
+ case "accusative":
27
+ return "рубля";
28
+ default:
29
+ return RublesMainSwitch(form);
30
+ }
31
+ }
32
+ if (lastDigit === 1) {
33
+ switch (form) {
34
+ case "nominative":
35
+ case "accusative":
36
+ return "рубль";
37
+ case "genitive":
38
+ return "рубля";
39
+ case "dative":
40
+ return "рублю";
41
+ case "ablative":
42
+ return "рублём";
43
+ }
44
+ }
45
+ return RublesMainSwitch(form);
46
+ };
@@ -0,0 +1,33 @@
1
+ import type { CanBeArray } from "./types.js";
2
+ /**
3
+ * Добавляет ведущие нули к числу для достижения указанной длины
4
+ *
5
+ * @example pad(123, 5) => "00123"
6
+ */
7
+ export declare const pad: (v: number, l?: number) => string;
8
+ /**
9
+ * JSON-клонирование объектов посредством `JSON.parse(JSON.stringify(v))`
10
+ * Если значение не `object` вернётся как есть
11
+ */
12
+ export declare const parsify: <T>(v: T) => T;
13
+ /**
14
+ * Приведение значения к массиву
15
+ */
16
+ export declare const arrayble: <T>(v: CanBeArray<T>) => T[];
17
+ /**
18
+ * Тип разделения для функции {@link partition}
19
+ */
20
+ export interface Partition<T> {
21
+ /**
22
+ * Элементы, удовлетворяющие условию
23
+ */
24
+ result: T[];
25
+ /**
26
+ * Исключённые элементы
27
+ */
28
+ excluded: T[];
29
+ }
30
+ /**
31
+ * Разделяет массив на два по условию
32
+ */
33
+ export declare const partition: <T>(arr: T[], filter: (item: T) => boolean, initial?: Partition<T>) => Partition<T>;
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Добавляет ведущие нули к числу для достижения указанной длины
3
+ *
4
+ * @example pad(123, 5) => "00123"
5
+ */
6
+ export const pad = (v, l = 2) => String(v).padStart(l, "0");
7
+ /**
8
+ * JSON-клонирование объектов посредством `JSON.parse(JSON.stringify(v))`
9
+ * Если значение не `object` вернётся как есть
10
+ */
11
+ export const parsify = (v) => {
12
+ if (typeof v !== "object")
13
+ return v;
14
+ return JSON.parse(JSON.stringify(v));
15
+ };
16
+ /**
17
+ * Приведение значения к массиву
18
+ */
19
+ export const arrayble = (v) => (Array.isArray(v) ? v : [v]);
20
+ /**
21
+ * Разделяет массив на два по условию
22
+ */
23
+ export const partition = (arr, filter, initial = { result: [], excluded: [] }) => arr.reduce((acc, item) => (acc[filter(item) ? "result" : "excluded"].push(item), acc), initial);
@@ -0,0 +1,35 @@
1
+ import type { Timestamp } from "./types.js";
2
+ /**
3
+ * Абстрактный класс очереди
4
+ *
5
+ * @template T - Тип элементов в очереди
6
+ */
7
+ export declare abstract class Queue<T> {
8
+ _queue: T[];
9
+ _delay: number;
10
+ private timeout;
11
+ /**
12
+ * @param [delay=25] - Задержка (в миллисекундах) между обработкой элементов очереди
13
+ */
14
+ constructor(delay?: Timestamp);
15
+ /**
16
+ * Добавляет элемент в очередь и запускает обработку
17
+ *
18
+ * @param item - Элемент для добавления в очередь
19
+ */
20
+ protected push(item: T): void;
21
+ /**
22
+ * Запускает обработку очереди с задержкой
23
+ */
24
+ private run;
25
+ /**
26
+ * Метод для обработки элемента очереди
27
+ *
28
+ * @param item - Элемент для обработки
29
+ */
30
+ protected abstract handle(item: T): void;
31
+ /**
32
+ * Сброс очереди
33
+ */
34
+ reset(): void;
35
+ }
package/dist/queue.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Абстрактный класс очереди
3
+ *
4
+ * @template T - Тип элементов в очереди
5
+ */
6
+ export class Queue {
7
+ _queue = [];
8
+ _delay;
9
+ timeout = 0;
10
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
11
+ /**
12
+ * @param [delay=25] - Задержка (в миллисекундах) между обработкой элементов очереди
13
+ */
14
+ constructor(delay = 25) {
15
+ this._delay = delay;
16
+ }
17
+ /**
18
+ * Добавляет элемент в очередь и запускает обработку
19
+ *
20
+ * @param item - Элемент для добавления в очередь
21
+ */
22
+ push(item) {
23
+ this._queue.push(item);
24
+ this.run();
25
+ }
26
+ /**
27
+ * Запускает обработку очереди с задержкой
28
+ */
29
+ run() {
30
+ this.reset();
31
+ this.timeout = setTimeout(() => {
32
+ if (this._queue.length === 0)
33
+ return this.reset();
34
+ const item = this._queue.shift();
35
+ this.handle(item);
36
+ this._queue.length > 0 ? this.run() : this.reset();
37
+ }, this._delay);
38
+ }
39
+ /**
40
+ * Сброс очереди
41
+ */
42
+ reset() {
43
+ if (this.timeout)
44
+ clearTimeout(this.timeout);
45
+ this.timeout = 0;
46
+ this._queue.length = 0;
47
+ }
48
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Регулярное выражение для захвата всех нецифровых символов из строки
3
+ */
4
+ export declare const oDigitRegExp: RegExp;
5
+ /**
6
+ * Чистка строки от всего, кроме цифр
7
+ */
8
+ export declare const oDigitClean: (v?: string) => string;
9
+ /**
10
+ * Регулярное выражение для захвата пробелов
11
+ */
12
+ export declare const spaceRegExp: RegExp;
13
+ /**
14
+ * Заменяет все двойные, тройные и т. д. пробелы на один
15
+ */
16
+ export declare const spaceClean: (v?: string) => string;
17
+ /**
18
+ * Очистка от плейсхолдеров
19
+ */
20
+ export declare const clearPH: (str?: string, placeholder?: string | RegExp) => string;
21
+ /**
22
+ * Приводит первый символ слова к верхнему регистру
23
+ */
24
+ export declare const toCapital: (word?: string) => string;
25
+ /**
26
+ * Приводит первый символ каждого слова к верхнему регистру
27
+ */
28
+ export declare const toCapitalMap: (str?: string) => string;
29
+ /**
30
+ * Повторяет строку указанное количество раз с разделителем
31
+ *
32
+ * @param {string} str - Исходная строка
33
+ * @param {number} length - Количество повторений
34
+ * @param {string} [separator=""] - Разделитель между повторениями (по умолчанию пустая строка)
35
+ */
36
+ export declare const repeat: (str: string, length: number, separator?: string) => string;
37
+ /**
38
+ * Приводит всё к нижнему регистру и корректирует пробелы
39
+ */
40
+ export declare const toFlat: (str?: string) => string;
41
+ /**
42
+ * Готовая {@link Map} для перевода кириллицы нижнего регистра в латиницу
43
+ */
44
+ export declare const cyrillicToLatinMap: Map<string, string>;
45
+ /**
46
+ * Функция для создания *slug* (фрагмента URL) сущности
47
+ */
48
+ export declare const toSlug: (text: string, separator?: string) => string;
package/dist/string.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Регулярное выражение для захвата всех нецифровых символов из строки
3
+ */
4
+ export const oDigitRegExp = /\D/g;
5
+ /**
6
+ * Чистка строки от всего, кроме цифр
7
+ */
8
+ export const oDigitClean = (v = "") => v.replace(oDigitRegExp, "");
9
+ //...
10
+ /**
11
+ * Регулярное выражение для захвата пробелов
12
+ */
13
+ export const spaceRegExp = /\s+/g;
14
+ /**
15
+ * Заменяет все двойные, тройные и т. д. пробелы на один
16
+ */
17
+ export const spaceClean = (v = "") => v.replace(spaceRegExp, " ").trim();
18
+ /**
19
+ * Очистка от плейсхолдеров
20
+ */
21
+ export const clearPH = (str = "", placeholder = "_") => str.replaceAll(placeholder, "");
22
+ //...
23
+ /**
24
+ * Приводит первый символ слова к верхнему регистру
25
+ */
26
+ export const toCapital = (word = "") => word.charAt(0).toUpperCase() + word.slice(1);
27
+ /**
28
+ * Приводит первый символ каждого слова к верхнему регистру
29
+ */
30
+ export const toCapitalMap = (str = "") => str.split(" ").map(toCapital).join(" ");
31
+ //...
32
+ /**
33
+ * Повторяет строку указанное количество раз с разделителем
34
+ *
35
+ * @param {string} str - Исходная строка
36
+ * @param {number} length - Количество повторений
37
+ * @param {string} [separator=""] - Разделитель между повторениями (по умолчанию пустая строка)
38
+ */
39
+ export const repeat = (str, length, separator = "") => Array.from({ length }).fill(str).join(separator);
40
+ //...
41
+ /**
42
+ * Приводит всё к нижнему регистру и корректирует пробелы
43
+ */
44
+ export const toFlat = (str = "") => spaceClean(str.toLowerCase());
45
+ /**
46
+ * Готовая {@link Map} для перевода кириллицы нижнего регистра в латиницу
47
+ */
48
+ export const cyrillicToLatinMap = new Map([
49
+ ["а", "a"],
50
+ ["б", "b"],
51
+ ["в", "v"],
52
+ ["г", "g"],
53
+ ["д", "d"],
54
+ ["е", "e"],
55
+ ["ё", "yo"],
56
+ ["ж", "zh"],
57
+ ["з", "z"],
58
+ ["и", "i"],
59
+ ["й", "y"],
60
+ ["к", "k"],
61
+ ["л", "l"],
62
+ ["м", "m"],
63
+ ["н", "n"],
64
+ ["о", "o"],
65
+ ["п", "p"],
66
+ ["р", "r"],
67
+ ["с", "s"],
68
+ ["т", "t"],
69
+ ["у", "u"],
70
+ ["ф", "f"],
71
+ ["х", "h"],
72
+ ["ц", "ts"],
73
+ ["ч", "ch"],
74
+ ["ш", "sh"],
75
+ ["щ", "sch"],
76
+ ["ъ", ""],
77
+ ["ы", "y"],
78
+ ["ь", ""],
79
+ ["э", "e"],
80
+ ["ю", "yu"],
81
+ ["я", "ya"],
82
+ ]);
83
+ /**
84
+ * Функция для создания *slug* (фрагмента URL) сущности
85
+ */
86
+ export const toSlug = (text, separator = "-") => {
87
+ let acc = "";
88
+ let wasSpace = false;
89
+ for (const _char of text.split("")) {
90
+ const char = _char.toLowerCase();
91
+ const code = char.charCodeAt(0);
92
+ // Кириллица (а-я, ё)
93
+ if ((code >= 1072 && code <= 1103) || code === 1105) {
94
+ acc += cyrillicToLatinMap.get(char) || char;
95
+ wasSpace = false;
96
+ }
97
+ // Латиница (a-z)
98
+ else if (code >= 97 && code <= 122) {
99
+ acc += char;
100
+ wasSpace = false;
101
+ }
102
+ // Цифры (0-9)
103
+ else if (code >= 48 && code <= 57) {
104
+ acc += char;
105
+ wasSpace = false;
106
+ }
107
+ // Пробел или другие символы, которые нужно заменить на separator
108
+ else {
109
+ if (!wasSpace && acc.length > 0) {
110
+ acc += separator;
111
+ wasSpace = true;
112
+ }
113
+ }
114
+ }
115
+ // Убираем завершающий separator, если он есть
116
+ return acc.endsWith(separator) ? acc.slice(0, -1) : acc;
117
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Дата и время в формате UNIX
3
+ */
4
+ export type Timestamp = number;
5
+ /**
6
+ * Идентификатор чего-либо для рантайма (например, для кэша)
7
+ */
8
+ export type RID = string | number | symbol;
9
+ /**
10
+ * Обобщённый тип таймеров
11
+ */
12
+ export type TimeoutType = ReturnType<typeof setTimeout>;
13
+ /**
14
+ * Обобщённый тип интервалов
15
+ */
16
+ export type IntervalType = ReturnType<typeof setInterval>;
17
+ /**
18
+ * Массив в массиве
19
+ */
20
+ export type Matrix<T = unknown> = T[][];
21
+ /**
22
+ * Значение может быть `null` или `undefined`
23
+ */
24
+ export type CanBeNullable<T = unknown> = T | null | undefined;
25
+ /**
26
+ * Значение может быть массивом или одиночным элементом
27
+ */
28
+ export type CanBeArray<T = unknown> = T | T[];
29
+ /**
30
+ * Значение может быть промиссом
31
+ */
32
+ export type CanBePromise<T = unknown> = T | Promise<T>;
33
+ /**
34
+ * Исключение `undefined`
35
+ */
36
+ export type Exist<T> = Exclude<T, undefined>;
37
+ /**
38
+ * Только строковые ключи объектов
39
+ */
40
+ export type KEYS<O> = keyof O extends infer Key ? (Key extends string ? Key : never) : never;
41
+ /**
42
+ * Рекурсивный {@link Partial}
43
+ */
44
+ export type DeepPartial<O> = {
45
+ [K in keyof O]?: O[K] extends object | undefined ? DeepPartial<O[K]> : O[K];
46
+ };
47
+ /**
48
+ * Рекурсивный {@link Required}
49
+ */
50
+ export type DeepRequired<O> = {
51
+ [K in keyof O]-?: O[K] extends object | undefined ? DeepRequired<NonNullable<O[K]>> : O[K];
52
+ };
53
+ /**
54
+ * Получение тип элемента массива
55
+ */
56
+ export type ArrayElement<T> = T extends (infer U)[] ? U : never;
57
+ /**
58
+ * - ASC - по возрастанию
59
+ * - DESC - по убыванию
60
+ */
61
+ export type SortDirection = "ASC" | "DESC";
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/url.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import type { CanBeNullable } from "./types.js";
2
+ /**
3
+ * Возможные значения параметра для {@link pushParams} и {@link clearParams}
4
+ */
5
+ export type ParamValue = CanBeNullable<string | number | boolean>;
6
+ /**
7
+ * KV-параметры для URL для {@link pushParams} и {@link clearParams}
8
+ */
9
+ export type ParamKV<V extends ParamValue = ParamValue> = {
10
+ key: string;
11
+ value?: V;
12
+ };
13
+ /**
14
+ * Параметры для URL для {@link pushParams} и {@link clearParams}
15
+ */
16
+ export type Params<V extends ParamValue = ParamValue> = CanBeNullable<string | ParamKV<V>>[];
17
+ /**
18
+ * Добавляет параметры в URL
19
+ *
20
+ * Для каждого параметра из массива:
21
+ * - Если параметр равен `null` или `undefined`, он пропускается
22
+ * - Если параметр имеет тип `string`, то в строке запроса добавляется параметр с ключом равным значению строки и значением `"true"`
23
+ * - Если параметр является объектом с ключом и значением, то, если значение не `undefined` и не является объектом, оно приводится к строке и устанавливается в качестве значения
24
+ *
25
+ * **Небезопасное поведение:**
26
+ * - При передаче параметра типа `string` он добавляется как `?param1&param2` (без значения, то есть без `true`)
27
+ * - При передаче параметра, значение которого равно `true` или пустой строке, параметр добавляется без значения
28
+ *
29
+ * @param url - URL, в который будут добавлены параметры
30
+ * @param params - Массив параметров
31
+ * @param [unsafe=false] - Небезопасное поведение
32
+ */
33
+ export declare const pushParams: (url: URL, params: Params, unsafe?: boolean) => URL;
34
+ export type StrictPushParams = Required<ParamKV<string | boolean | number>>[];
35
+ /**
36
+ * Объединяет массивы параметров для {@link pushParams} в один массив, исключая дубликаты
37
+ *
38
+ * @param args - Массивы параметров для объединения
39
+ * @returns Массив параметров без дубликатов
40
+ */
41
+ export declare const mergePushParams: (...args: Params[]) => StrictPushParams;
42
+ /**
43
+ * Удаляет из URL параметры
44
+ *
45
+ * Для каждого элемента массива:
46
+ * - Если параметр отсутствует или равен `null` или `undefined`, он пропускается
47
+ * - Если параметр имеет тип `string`, из URL удаляется параметр с таким именем
48
+ * - Если параметр является объектом с ключом (`key`), из URL удаляется параметр с этим ключом
49
+ *
50
+ * @param url - URL, из которого будут удалены параметры
51
+ * @param params - Массив параметров для удаления
52
+ */
53
+ export declare const clearParams: (url: URL, params: Params) => URL;
54
+ export type StrictClearParams = string[];
55
+ /**
56
+ * Объединяет массивы параметров для {@link clearParams} в один массив, исключая дубликаты
57
+ *
58
+ * @param args - Массивы параметров для объединения
59
+ * @returns Массив параметров без дубликатов
60
+ */
61
+ export declare const mergeClearParams: (...args: Params[]) => StrictClearParams;
package/dist/url.js ADDED
@@ -0,0 +1,137 @@
1
+ //...
2
+ /**
3
+ * Добавляет параметры в URL
4
+ *
5
+ * Для каждого параметра из массива:
6
+ * - Если параметр равен `null` или `undefined`, он пропускается
7
+ * - Если параметр имеет тип `string`, то в строке запроса добавляется параметр с ключом равным значению строки и значением `"true"`
8
+ * - Если параметр является объектом с ключом и значением, то, если значение не `undefined` и не является объектом, оно приводится к строке и устанавливается в качестве значения
9
+ *
10
+ * **Небезопасное поведение:**
11
+ * - При передаче параметра типа `string` он добавляется как `?param1&param2` (без значения, то есть без `true`)
12
+ * - При передаче параметра, значение которого равно `true` или пустой строке, параметр добавляется без значения
13
+ *
14
+ * @param url - URL, в который будут добавлены параметры
15
+ * @param params - Массив параметров
16
+ * @param [unsafe=false] - Небезопасное поведение
17
+ */
18
+ export const pushParams = (url, params, unsafe = false) => {
19
+ for (const param of params) {
20
+ if (!param)
21
+ continue;
22
+ if (typeof param === "string") {
23
+ // Если unsafe === true, добавляем параметр без значения, иначе - как "true"
24
+ url.searchParams.set(param, unsafe ? "" : "true");
25
+ continue;
26
+ }
27
+ if (!param?.key)
28
+ continue;
29
+ if (typeof param.value !== "object") {
30
+ if (unsafe && (param?.value === undefined || param.value === true || param.value === "")) {
31
+ url.searchParams.set(param.key, "");
32
+ }
33
+ else {
34
+ url.searchParams.set(param.key, String(param.value));
35
+ }
36
+ }
37
+ }
38
+ if (unsafe) {
39
+ const splitted = url.href.split("?");
40
+ if (splitted[1]) {
41
+ splitted[1] = splitted[1].replaceAll("=&", "&");
42
+ if (splitted[1].endsWith("="))
43
+ splitted[1] = splitted[1].slice(0, splitted[1].length - 1);
44
+ }
45
+ url.href = splitted.join("?");
46
+ }
47
+ return url;
48
+ };
49
+ /**
50
+ * Объединяет массивы параметров для {@link pushParams} в один массив, исключая дубликаты
51
+ *
52
+ * @param args - Массивы параметров для объединения
53
+ * @returns Массив параметров без дубликатов
54
+ */
55
+ export const mergePushParams = (...args) => {
56
+ const acc = [];
57
+ for (const params of args) {
58
+ for (const param of params) {
59
+ if (!param)
60
+ continue;
61
+ if (typeof param === "string") {
62
+ const existed = acc.find((v) => v.key === param);
63
+ if (existed)
64
+ existed.value = true;
65
+ else
66
+ acc.push({ key: param, value: true });
67
+ continue;
68
+ }
69
+ if (!param?.key)
70
+ continue;
71
+ if (typeof param?.value !== "object") {
72
+ const existed = acc.find((v) => v.key === param.key);
73
+ if (param.value === undefined || param.value === true || param.value === "") {
74
+ if (existed)
75
+ existed.value = true;
76
+ else
77
+ acc.push({ key: param.key, value: true });
78
+ }
79
+ else {
80
+ if (existed)
81
+ existed.value = String(param.value);
82
+ else
83
+ acc.push({ key: param.key, value: param.value });
84
+ }
85
+ }
86
+ }
87
+ }
88
+ return acc;
89
+ };
90
+ //...
91
+ /**
92
+ * Удаляет из URL параметры
93
+ *
94
+ * Для каждого элемента массива:
95
+ * - Если параметр отсутствует или равен `null` или `undefined`, он пропускается
96
+ * - Если параметр имеет тип `string`, из URL удаляется параметр с таким именем
97
+ * - Если параметр является объектом с ключом (`key`), из URL удаляется параметр с этим ключом
98
+ *
99
+ * @param url - URL, из которого будут удалены параметры
100
+ * @param params - Массив параметров для удаления
101
+ */
102
+ export const clearParams = (url, params) => {
103
+ for (const param of params) {
104
+ if (!param)
105
+ continue;
106
+ if (typeof param === "string") {
107
+ url.searchParams.delete(param);
108
+ continue;
109
+ }
110
+ if (param?.key)
111
+ url.searchParams.delete(param.key);
112
+ }
113
+ return url;
114
+ };
115
+ /**
116
+ * Объединяет массивы параметров для {@link clearParams} в один массив, исключая дубликаты
117
+ *
118
+ * @param args - Массивы параметров для объединения
119
+ * @returns Массив параметров без дубликатов
120
+ */
121
+ export const mergeClearParams = (...args) => {
122
+ const acc = [];
123
+ for (const params of args) {
124
+ for (const param of params) {
125
+ if (!param)
126
+ continue;
127
+ if (typeof param === "string") {
128
+ if (!acc.includes(param))
129
+ acc.push(param);
130
+ continue;
131
+ }
132
+ if (param.key && !acc.includes(param.key))
133
+ acc.push(param.key);
134
+ }
135
+ }
136
+ return acc;
137
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nemigo/helpers",
3
- "version": "0.0.10",
3
+ "version": "0.0.19",
4
4
  "private": false,
5
5
  "author": {
6
6
  "name": "Vlad Logvin",
@@ -15,6 +15,14 @@
15
15
  "format": "prettier --write ./"
16
16
  },
17
17
  "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./async": {
23
+ "types": "./dist/async.d.ts",
24
+ "default": "./dist/async.js"
25
+ },
18
26
  "./cases": {
19
27
  "types": "./dist/cases.d.ts",
20
28
  "default": "./dist/cases.js"
@@ -23,10 +31,30 @@
23
31
  "types": "./dist/clean.d.ts",
24
32
  "default": "./dist/clean.js"
25
33
  },
34
+ "./datetime": {
35
+ "types": "./dist/datetime.d.ts",
36
+ "default": "./dist/datetime.js"
37
+ },
26
38
  "./explorer": {
27
39
  "types": "./dist/explorer.d.ts",
28
40
  "default": "./dist/explorer.js"
29
41
  },
42
+ "./files": {
43
+ "types": "./dist/files.d.ts",
44
+ "default": "./dist/files.js"
45
+ },
46
+ "./format": {
47
+ "types": "./dist/format.d.ts",
48
+ "default": "./dist/format.js"
49
+ },
50
+ "./html": {
51
+ "types": "./dist/html.d.ts",
52
+ "default": "./dist/html.js"
53
+ },
54
+ "./humanly": {
55
+ "types": "./dist/humanly.d.ts",
56
+ "default": "./dist/humanly.js"
57
+ },
30
58
  "./lens": {
31
59
  "types": "./dist/lens.d.ts",
32
60
  "default": "./dist/lens.js"
@@ -35,23 +63,48 @@
35
63
  "types": "./dist/msgpack.d.ts",
36
64
  "default": "./dist/msgpack.js"
37
65
  },
38
- "./random": {
39
- "types": "./dist/random.d.ts",
40
- "default": "./dist/random.js"
41
- },
42
66
  "./phymath": {
43
67
  "types": "./dist/phymath.d.ts",
44
68
  "default": "./dist/phymath.js"
45
69
  },
70
+ "./queue": {
71
+ "types": "./dist/queue.d.ts",
72
+ "default": "./dist/queue.js"
73
+ },
74
+ "./random": {
75
+ "types": "./dist/random.d.ts",
76
+ "default": "./dist/random.js"
77
+ },
46
78
  "./script": {
47
79
  "types": "./dist/script.d.ts",
48
80
  "default": "./dist/script.js"
81
+ },
82
+ "./string": {
83
+ "types": "./dist/string.d.ts",
84
+ "default": "./dist/string.js"
85
+ },
86
+ "./types": {
87
+ "types": "./dist/types.d.ts",
88
+ "default": "./dist/types.js"
89
+ },
90
+ "./url": {
91
+ "types": "./dist/url.d.ts",
92
+ "default": "./dist/url.js"
93
+ }
94
+ },
95
+ "peerDependencies": {
96
+ "zod": "^4.0.0"
97
+ },
98
+ "peerDependenciesMeta": {
99
+ "zod": {
100
+ "optional": true
49
101
  }
50
102
  },
51
103
  "dependencies": {
52
104
  "@std/msgpack": "jsr:^1.0.0"
53
105
  },
54
106
  "devDependencies": {
55
- "@nemigo/configs": "workspace:*"
107
+ "@nemigo/configs": "workspace:*",
108
+ "zod": "4.1.5"
56
109
  }
57
110
  }