@primoui/utils 1.3.4 → 1.4.1
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/dist/http/http.d.ts +17 -18
- package/dist/http/http.d.ts.map +1 -1
- package/dist/index.js +154 -137
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +1 -1
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +3 -6
package/dist/http/http.d.ts
CHANGED
|
@@ -76,22 +76,21 @@ export declare const setQueryParams: (url: string, params: Record<string, string
|
|
|
76
76
|
* @returns URL without query parameters
|
|
77
77
|
*/
|
|
78
78
|
export declare const removeQueryParams: (url?: string) => string;
|
|
79
|
-
/**
|
|
80
|
-
export
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
export declare const addSearchParams: (url: string, params: Record<string, string | number | boolean>) => string;
|
|
79
|
+
/** Options for checkUrlAvailability */
|
|
80
|
+
export type CheckUrlAvailabilityOptions = {
|
|
81
|
+
/** Request timeout in milliseconds (default: 5000) */
|
|
82
|
+
timeout?: number;
|
|
83
|
+
/** HTTP status codes below this value are considered successful (default: 400) */
|
|
84
|
+
successStatusBelow?: number;
|
|
85
|
+
/** User-Agent header to send with requests */
|
|
86
|
+
userAgent?: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Checks if a URL is accessible by making an HTTP request.
|
|
90
|
+
* First tries a HEAD request, then falls back to GET if HEAD fails.
|
|
91
|
+
* @param url - The URL to check
|
|
92
|
+
* @param options - Configuration options for the request
|
|
93
|
+
* @returns True if the URL is accessible (status < 400), false otherwise
|
|
94
|
+
*/
|
|
95
|
+
export declare const checkUrlAvailability: (url: string, options?: CheckUrlAvailabilityOptions) => Promise<boolean>;
|
|
97
96
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAazC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,MAW5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAE7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MA4B3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAOxC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,MASvC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OAG5C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAU7C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,GAAG,OAAO,MAAM,EAAE,KAAG,MAe/D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAajE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,KAAK,MAAM,EACX,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAChD,MAiBF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAYhD,CAAA;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAazC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,MAW5D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAE7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MA4B3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAOxC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,MASvC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OAG5C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAU7C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,GAAG,OAAO,MAAM,EAAE,KAAG,MAe/D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAajE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,KAAK,MAAM,EACX,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAChD,MAiBF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAYhD,CAAA;AAED,uCAAuC;AACvC,MAAM,MAAM,2BAA2B,GAAG;IACxC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,KAAK,MAAM,EACX,UAAS,2BAAgC,KACxC,OAAO,CAAC,OAAO,CAwCjB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
1
|
import d from "@sindresorhus/slugify";
|
|
2
|
-
const
|
|
2
|
+
const F = (t, e) => {
|
|
3
3
|
const r = e - t + 1;
|
|
4
4
|
return Array.from({ length: r }, (n, s) => s + t);
|
|
5
|
-
}, y = async (t) => new Promise((e) => setTimeout(e, t)),
|
|
5
|
+
}, y = async (t) => new Promise((e) => setTimeout(e, t)), j = ({ key: t, metaKey: e }) => `${e ? "⌘" : ""}${t.toUpperCase()}`, w = (t) => t.replace(/<[^>]*>?/gm, ""), b = (t, e = " ") => t.replace(/\n+/g, e), z = (t, e = 250) => {
|
|
6
6
|
if (!t)
|
|
7
7
|
return null;
|
|
8
8
|
const r = b(w(t)), n = r.slice(0, e).trim();
|
|
9
9
|
return n.length < r.length ? `${n}...` : n;
|
|
10
|
-
},
|
|
10
|
+
}, W = (t, e = !1) => d(t, {
|
|
11
11
|
decamelize: e,
|
|
12
12
|
customReplacements: [
|
|
13
13
|
["#", "sharp"],
|
|
14
14
|
["+", "plus"]
|
|
15
15
|
]
|
|
16
|
-
}),
|
|
16
|
+
}), K = (t) => t.length === 25 && t[0] === "c", U = (t) => !!t, J = (t, e = 0) => {
|
|
17
17
|
const r = (t || "").trim();
|
|
18
18
|
if (r.length === 0 || r.length === 1 || r.length === 2)
|
|
19
19
|
return r.toUpperCase();
|
|
20
20
|
const s = r.split(" ").filter(U).map((o) => o.charAt(0).toUpperCase()).join("");
|
|
21
21
|
return e > 0 ? s.slice(0, e) : s;
|
|
22
|
-
},
|
|
22
|
+
}, V = (t) => new Promise((e, r) => {
|
|
23
23
|
const n = new FileReader();
|
|
24
24
|
n.readAsDataURL(t), n.onload = () => e(n.result), n.onerror = (s) => r(s);
|
|
25
|
-
}),
|
|
25
|
+
}), M = (t, e) => {
|
|
26
26
|
const r = [];
|
|
27
27
|
for (let n = 0; n < t.length; n += e)
|
|
28
28
|
r.push(t.slice(n, n + e));
|
|
29
29
|
return r;
|
|
30
|
-
},
|
|
30
|
+
}, H = (t, e, r = !1) => {
|
|
31
31
|
Object.getOwnPropertyDescriptor(
|
|
32
32
|
HTMLInputElement.prototype,
|
|
33
33
|
"value"
|
|
34
34
|
)?.set?.call(t, e), r && t?.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
35
|
-
},
|
|
35
|
+
}, Z = (t, e = 3, r = "and") => t.slice(0, e).join(", ").replace(/, ([^,]*)$/, ` ${r} $1`), G = async (t) => {
|
|
36
36
|
try {
|
|
37
37
|
return { data: await t, error: null };
|
|
38
38
|
} catch (e) {
|
|
39
39
|
return { data: null, error: e };
|
|
40
40
|
}
|
|
41
|
-
},
|
|
41
|
+
}, E = (t) => {
|
|
42
42
|
if (t !== null) {
|
|
43
43
|
if (t?.constructor.name === "Object")
|
|
44
44
|
for (const e in t)
|
|
45
|
-
t[e] =
|
|
45
|
+
t[e] = E(t[e]);
|
|
46
46
|
return t;
|
|
47
47
|
}
|
|
48
|
-
},
|
|
48
|
+
}, Q = (t, e) => e.some((r) => {
|
|
49
49
|
const [n, s] = t.split("/"), [o, c] = r.split("/");
|
|
50
50
|
return n !== o ? !1 : c === "*" ? !0 : s === c;
|
|
51
|
-
}),
|
|
51
|
+
}), S = async (t, e, r) => {
|
|
52
52
|
const { batchSize: n, concurrency: s = n, delay: o = 0 } = r;
|
|
53
53
|
if (t.length === 0) return [];
|
|
54
|
-
const c = [], a =
|
|
55
|
-
for (const [
|
|
56
|
-
console.log(`Processing batch ${
|
|
57
|
-
const
|
|
58
|
-
c.push(...
|
|
54
|
+
const c = [], a = M(t, n);
|
|
55
|
+
for (const [i, u] of a.entries()) {
|
|
56
|
+
console.log(`Processing batch ${i + 1}/${a.length} (${u.length} items)`);
|
|
57
|
+
const l = await R(u, e, s);
|
|
58
|
+
c.push(...l), o > 0 && i < a.length - 1 && (console.log(`Waiting ${o}ms before next batch...`), await y(o));
|
|
59
59
|
}
|
|
60
60
|
return c;
|
|
61
|
-
},
|
|
61
|
+
}, _ = async (t, e, r) => {
|
|
62
62
|
const { onError: n } = r;
|
|
63
|
-
return
|
|
63
|
+
return S(t, async (o) => {
|
|
64
64
|
try {
|
|
65
65
|
return await e(o);
|
|
66
66
|
} catch (c) {
|
|
@@ -68,7 +68,7 @@ const A = (t, e) => {
|
|
|
68
68
|
return n?.(a, o), a;
|
|
69
69
|
}
|
|
70
70
|
}, r);
|
|
71
|
-
},
|
|
71
|
+
}, R = async (t, e, r) => {
|
|
72
72
|
if (r >= t.length)
|
|
73
73
|
return Promise.all(t.map(e));
|
|
74
74
|
const n = [], s = [];
|
|
@@ -79,42 +79,42 @@ const A = (t, e) => {
|
|
|
79
79
|
s.push(c), s.length >= r && (await Promise.race(s), s.splice(0, s.length - s.filter((a) => a !== c).length));
|
|
80
80
|
}
|
|
81
81
|
return await Promise.all(s), n;
|
|
82
|
-
},
|
|
82
|
+
}, q = (t) => {
|
|
83
83
|
const e = t.replace("#", "").substring(0, 6), r = Number.parseInt(e.substring(0, 2), 16), n = Number.parseInt(e.substring(2, 4), 16), s = Number.parseInt(e.substring(4, 6), 16);
|
|
84
84
|
return r * 0.299 + n * 0.587 + s * 0.114 > 186;
|
|
85
|
-
},
|
|
85
|
+
}, Y = (t) => {
|
|
86
86
|
const e = document.getElementById(t || "");
|
|
87
87
|
if (!e) return;
|
|
88
88
|
const r = window.getComputedStyle(e), n = Number.parseFloat(r.scrollMarginTop), s = Math.floor(window.scrollY + e.getBoundingClientRect().top - n);
|
|
89
89
|
return { id: t, top: s };
|
|
90
|
-
},
|
|
91
|
-
if (
|
|
90
|
+
}, P = (t) => typeof t == "object" && t !== null && "message" in t && typeof t.message == "string", N = (t) => {
|
|
91
|
+
if (P(t)) return t;
|
|
92
92
|
try {
|
|
93
93
|
return new Error(JSON.stringify(t));
|
|
94
94
|
} catch {
|
|
95
95
|
return new Error(String(t));
|
|
96
96
|
}
|
|
97
|
-
},
|
|
97
|
+
}, X = (t) => N(t).message, tt = (t, e) => {
|
|
98
98
|
document.addEventListener(t, e);
|
|
99
|
-
},
|
|
99
|
+
}, et = (t, e) => {
|
|
100
100
|
document.removeEventListener(t, e);
|
|
101
|
-
},
|
|
101
|
+
}, rt = (t, e) => {
|
|
102
102
|
const r = new CustomEvent(t, { detail: e });
|
|
103
103
|
document.dispatchEvent(r);
|
|
104
|
-
},
|
|
104
|
+
}, nt = () => {
|
|
105
105
|
const t = new KeyboardEvent("keydown", { key: "Escape" });
|
|
106
106
|
document.dispatchEvent(t);
|
|
107
|
-
},
|
|
107
|
+
}, st = (t, e = "compact", r = "en-US") => new Intl.NumberFormat(r, { notation: e }).format(t), ot = (t, e = "USD") => new Intl.NumberFormat("en-US", {
|
|
108
108
|
style: "currency",
|
|
109
109
|
currency: e
|
|
110
|
-
}).format(t).replace(/\D00(?=\D*$)/, ""),
|
|
110
|
+
}).format(t).replace(/\D00(?=\D*$)/, ""), ct = (t, e = "month") => f(t / (e === "year" ? 12 : 1), 2), f = (t, e = 0) => t.toFixed(e < 0 ? 0 : e).replace(/\.0+$/, ""), at = (t, e = 0) => {
|
|
111
111
|
if (t < 1024) {
|
|
112
112
|
const c = 10 ** e;
|
|
113
113
|
return `${Math.floor(t / 1024 * c) / c} KB`;
|
|
114
114
|
}
|
|
115
115
|
const n = Math.floor(Math.log(t) / Math.log(1024));
|
|
116
|
-
return `${
|
|
117
|
-
},
|
|
116
|
+
return `${f(t / 1024 ** n, e)} ${["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][n]}`;
|
|
117
|
+
}, it = (t) => {
|
|
118
118
|
const [, e] = t.split("/");
|
|
119
119
|
let r;
|
|
120
120
|
switch (e) {
|
|
@@ -124,14 +124,14 @@ const A = (t, e) => {
|
|
|
124
124
|
r = e;
|
|
125
125
|
}
|
|
126
126
|
return r?.toUpperCase();
|
|
127
|
-
},
|
|
128
|
-
if (!t || typeof t != "string" || !
|
|
127
|
+
}, v = /^https?:\/\/(?:www\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b|localhost(?::\d+)?|127\.0\.0\.1(?::\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/, I = (t) => {
|
|
128
|
+
if (!t || typeof t != "string" || !v.test(t)) return !1;
|
|
129
129
|
try {
|
|
130
130
|
return new URL(t), !0;
|
|
131
131
|
} catch {
|
|
132
132
|
return !1;
|
|
133
133
|
}
|
|
134
|
-
},
|
|
134
|
+
}, T = (t, e) => t ? t.startsWith("http://") || t.startsWith("https://") ? t : `${e !== void 0 ? e ? "https" : "http" : D(t) ? "http" : "https"}://${t}` : "", ut = (t) => t?.replace(/^https?:\/\//, "") ?? "", h = (t) => {
|
|
135
135
|
if (!t) return "";
|
|
136
136
|
let e = t.trim();
|
|
137
137
|
if (e.includes("?") || e.includes("#"))
|
|
@@ -142,14 +142,14 @@ const A = (t, e) => {
|
|
|
142
142
|
return e.length > 1 && e.endsWith("/") ? e.slice(0, -1) : e;
|
|
143
143
|
}
|
|
144
144
|
return e.length > 1 && e.endsWith("/") && (e = e.slice(0, -1)), e;
|
|
145
|
-
},
|
|
145
|
+
}, lt = (t) => {
|
|
146
146
|
try {
|
|
147
147
|
const e = new URL(t);
|
|
148
148
|
return `${e.protocol}//${e.host}`;
|
|
149
149
|
} catch {
|
|
150
150
|
return t;
|
|
151
151
|
}
|
|
152
|
-
},
|
|
152
|
+
}, ht = (t) => {
|
|
153
153
|
try {
|
|
154
154
|
if (!I(t)) return t;
|
|
155
155
|
const e = new URL(t).hostname;
|
|
@@ -157,24 +157,24 @@ const A = (t, e) => {
|
|
|
157
157
|
} catch {
|
|
158
158
|
return t;
|
|
159
159
|
}
|
|
160
|
-
}, mt = (t) => t ? /^https?:\/\//.test(t) : !1,
|
|
160
|
+
}, mt = (t) => t ? /^https?:\/\//.test(t) : !1, D = (t) => {
|
|
161
161
|
if (!t) return !1;
|
|
162
162
|
try {
|
|
163
|
-
const r = new URL(
|
|
163
|
+
const r = new URL(T(t)).hostname;
|
|
164
164
|
return r === "localhost" || r === "127.0.0.1" || r.endsWith(".localhost");
|
|
165
165
|
} catch {
|
|
166
166
|
return t.includes("localhost") || t.includes("127.0.0.1");
|
|
167
167
|
}
|
|
168
|
-
},
|
|
168
|
+
}, ft = (t, ...e) => {
|
|
169
169
|
if (!t) return "";
|
|
170
|
-
let r =
|
|
170
|
+
let r = h(t);
|
|
171
171
|
for (const n of e) {
|
|
172
172
|
if (!n) continue;
|
|
173
173
|
const s = n.replace(/^\/+|\/+$/g, "");
|
|
174
174
|
s && (r += `/${s}`);
|
|
175
175
|
}
|
|
176
176
|
return r;
|
|
177
|
-
},
|
|
177
|
+
}, O = (t) => {
|
|
178
178
|
try {
|
|
179
179
|
const e = new URL(t), r = {};
|
|
180
180
|
return e.searchParams.forEach((n, s) => {
|
|
@@ -183,7 +183,7 @@ const A = (t, e) => {
|
|
|
183
183
|
} catch {
|
|
184
184
|
return {};
|
|
185
185
|
}
|
|
186
|
-
},
|
|
186
|
+
}, pt = (t, e) => {
|
|
187
187
|
try {
|
|
188
188
|
const r = new URL(t);
|
|
189
189
|
Object.entries(e).forEach(([s, o]) => {
|
|
@@ -194,140 +194,157 @@ const A = (t, e) => {
|
|
|
194
194
|
} catch {
|
|
195
195
|
return t;
|
|
196
196
|
}
|
|
197
|
-
},
|
|
197
|
+
}, gt = (t) => {
|
|
198
198
|
if (!t) return "";
|
|
199
199
|
try {
|
|
200
200
|
const e = new URL(t);
|
|
201
|
-
return e.search = "",
|
|
201
|
+
return e.search = "", h(e.toString());
|
|
202
202
|
} catch {
|
|
203
203
|
const e = t.indexOf("?");
|
|
204
204
|
return e !== -1 ? t.substring(0, e) : t;
|
|
205
205
|
}
|
|
206
|
-
},
|
|
206
|
+
}, dt = async (t, e = {}) => {
|
|
207
|
+
if (!t)
|
|
208
|
+
return !1;
|
|
209
|
+
const {
|
|
210
|
+
timeout: r = 5e3,
|
|
211
|
+
successStatusBelow: n = 400,
|
|
212
|
+
userAgent: s = "Mozilla/5.0 (compatible; URLChecker/1.0)"
|
|
213
|
+
} = e, o = h(t), c = async (u) => {
|
|
214
|
+
const l = new AbortController(), m = setTimeout(() => l.abort(), r);
|
|
215
|
+
try {
|
|
216
|
+
const g = await fetch(o, {
|
|
217
|
+
method: u,
|
|
218
|
+
signal: l.signal,
|
|
219
|
+
redirect: "follow",
|
|
220
|
+
headers: { "User-Agent": s }
|
|
221
|
+
});
|
|
222
|
+
return clearTimeout(m), g;
|
|
223
|
+
} catch {
|
|
224
|
+
return clearTimeout(m), null;
|
|
225
|
+
}
|
|
226
|
+
}, a = await c("HEAD");
|
|
227
|
+
if (a && a.status < n)
|
|
228
|
+
return !0;
|
|
229
|
+
const i = await c("GET");
|
|
230
|
+
return i !== null && i.status < n;
|
|
231
|
+
}, yt = (t, e, r) => e !== void 0 && r !== void 0 ? Math.min(Math.max(t, e), r) : e !== void 0 ? Math.max(t, e) : r !== void 0 ? Math.min(t, r) : t, wt = (t) => {
|
|
207
232
|
if (t == null) return;
|
|
208
233
|
const e = Number.parseFloat(t.toString());
|
|
209
234
|
return Number.isNaN(e) ? void 0 : e;
|
|
210
|
-
},
|
|
211
|
-
const r =
|
|
235
|
+
}, bt = (t, e = 2) => {
|
|
236
|
+
const r = 10 ** e;
|
|
212
237
|
return Math.round((t + Number.EPSILON) * r) / r;
|
|
213
|
-
},
|
|
238
|
+
}, Ut = (t = {}) => t.constructor === Object && !Object.entries(t).length, Mt = (t, e) => t in e, Et = (t) => (e, r) => {
|
|
214
239
|
const n = t.indexOf(Object.keys(e)[0] ?? ""), s = t.indexOf(Object.keys(r)[0] ?? "");
|
|
215
240
|
return n === -1 && s === -1 ? 0 : n === -1 ? 1 : s === -1 ? -1 : n - s;
|
|
216
|
-
},
|
|
241
|
+
}, St = (t, e) => Object.keys(t).sort(e).reduce((r, n) => ({ ...r, [n]: t[n] }), {}), Rt = (t, e) => {
|
|
217
242
|
const r = {};
|
|
218
243
|
for (const n of e)
|
|
219
244
|
n in t && (r[n] = t[n]);
|
|
220
245
|
return r;
|
|
221
|
-
},
|
|
222
|
-
const { page: r, ...n } =
|
|
246
|
+
}, $ = (t) => Math.max(t && !Number.isNaN(Number(t)) ? Number.parseInt(t || "1", 10) : 1, 1), Pt = (t, e) => {
|
|
247
|
+
const { page: r, ...n } = O(t), o = ($(r) - 1) * e;
|
|
223
248
|
return { take: e, skip: o, ...n };
|
|
224
|
-
},
|
|
249
|
+
}, Nt = (t, e, r) => (t.set("page", r.toString()), `${e}?${t.toString()}`), vt = (t) => {
|
|
225
250
|
try {
|
|
226
251
|
return JSON.parse(t);
|
|
227
252
|
} catch {
|
|
228
253
|
return t;
|
|
229
254
|
}
|
|
230
|
-
},
|
|
255
|
+
}, It = (t) => typeof t == "object" ? JSON.stringify(t) : t, Tt = () => Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0"), Dt = (t = 16) => {
|
|
231
256
|
const e = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
232
257
|
return Array.from({ length: t }, () => e[Math.floor(Math.random() * e.length)]).join("");
|
|
233
|
-
},
|
|
258
|
+
}, Ot = (t, e) => Math.floor(Math.random() * (e - t + 1)) + t, $t = (t) => Array.from({ length: t }, () => Math.floor(Math.random() * 10)).join(""), Lt = (t) => {
|
|
234
259
|
const e = Math.floor(Math.random() * t.length);
|
|
235
260
|
return t[e];
|
|
236
|
-
},
|
|
261
|
+
}, kt = (t) => {
|
|
237
262
|
const e = Object.keys(t), r = e[Math.floor(e.length * Math.random())];
|
|
238
263
|
return t[r];
|
|
239
|
-
},
|
|
264
|
+
}, Ct = (t) => typeof t != "string" ? "" : t.length === 0 ? t : t[0]?.toUpperCase() + t.slice(1), Bt = (t) => typeof t != "string" ? "" : t.length === 0 ? t : t[0]?.toLowerCase() + t.slice(1), p = (t, e) => new Intl.DateTimeFormat(t, { dateStyle: e }), L = (t, e) => new Intl.DateTimeFormat(t, { timeStyle: e }), k = (t, e, r) => new Intl.DateTimeFormat(t, { dateStyle: e, timeStyle: r }), C = (t, e = "medium", r = "en-US") => p(r, e).format(new Date(t)), B = (t, e = "short", r = "en-US") => L(r, e).format(new Date(t)), x = (t, e = "medium", r = "short", n = "en-US") => k(n, e, r).format(new Date(t)), xt = (t, e, r = "medium", n = "short", s = "en-US") => {
|
|
240
265
|
switch (e) {
|
|
241
266
|
case "date":
|
|
242
|
-
return
|
|
267
|
+
return C(t, r, s);
|
|
243
268
|
case "time":
|
|
244
|
-
return
|
|
269
|
+
return B(t, n, s);
|
|
245
270
|
default:
|
|
246
|
-
return
|
|
271
|
+
return x(t, r, n, s);
|
|
247
272
|
}
|
|
248
|
-
},
|
|
273
|
+
}, At = (t, e, r = "medium", n = "en-US") => p(n, r).formatRange(new Date(t), new Date(e)), Ft = (t, e = 265) => t ? Math.ceil(t.trim().split(/\s+/).length / e) : 0;
|
|
249
274
|
export {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
Mt as addSearchParams,
|
|
275
|
+
T as addProtocol,
|
|
276
|
+
dt as checkUrlAvailability,
|
|
253
277
|
b as convertNewlines,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
vt as isEmptyObject,
|
|
287
|
-
R as isErrorWithMessage,
|
|
278
|
+
at as formatBytes,
|
|
279
|
+
ot as formatCurrency,
|
|
280
|
+
C as formatDate,
|
|
281
|
+
xt as formatDateOrTime,
|
|
282
|
+
At as formatDateRange,
|
|
283
|
+
x as formatDateTime,
|
|
284
|
+
ct as formatIntervalAmount,
|
|
285
|
+
it as formatMimeType,
|
|
286
|
+
st as formatNumber,
|
|
287
|
+
B as formatTime,
|
|
288
|
+
f as formatToDecimals,
|
|
289
|
+
lt as getBaseUrl,
|
|
290
|
+
$ as getCurrentPage,
|
|
291
|
+
ht as getDomain,
|
|
292
|
+
Y as getElementPosition,
|
|
293
|
+
X as getErrorMessage,
|
|
294
|
+
z as getExcerpt,
|
|
295
|
+
J as getInitials,
|
|
296
|
+
Nt as getPageLink,
|
|
297
|
+
Pt as getPageParams,
|
|
298
|
+
O as getQueryParams,
|
|
299
|
+
Tt as getRandomColor,
|
|
300
|
+
$t as getRandomDigits,
|
|
301
|
+
Lt as getRandomElement,
|
|
302
|
+
Ot as getRandomNumber,
|
|
303
|
+
kt as getRandomProperty,
|
|
304
|
+
Dt as getRandomString,
|
|
305
|
+
Ft as getReadTime,
|
|
306
|
+
j as getShortcutLabel,
|
|
307
|
+
K as isCuid,
|
|
308
|
+
Ut as isEmptyObject,
|
|
309
|
+
P as isErrorWithMessage,
|
|
288
310
|
mt as isExternalUrl,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
311
|
+
Mt as isKeyInObject,
|
|
312
|
+
q as isLightColor,
|
|
313
|
+
D as isLocalhostUrl,
|
|
314
|
+
Q as isMimeTypeMatch,
|
|
293
315
|
U as isTruthy,
|
|
294
316
|
I as isValidUrl,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
dt as removeTrailingSlash,
|
|
316
|
-
_ as setInputValue,
|
|
317
|
-
L as setQueryParams,
|
|
317
|
+
Z as joinAsSentence,
|
|
318
|
+
ft as joinUrlPaths,
|
|
319
|
+
yt as keepNumberInRange,
|
|
320
|
+
Bt as lcFirst,
|
|
321
|
+
vt as maybeParseJson,
|
|
322
|
+
It as maybeStringifyJson,
|
|
323
|
+
h as normalizeUrl,
|
|
324
|
+
E as nullsToUndefined,
|
|
325
|
+
wt as parseNumericValue,
|
|
326
|
+
Rt as pickFromObject,
|
|
327
|
+
bt as preciseRound,
|
|
328
|
+
S as processBatch,
|
|
329
|
+
_ as processBatchWithErrorHandling,
|
|
330
|
+
rt as publish,
|
|
331
|
+
nt as publishEscape,
|
|
332
|
+
F as range,
|
|
333
|
+
ut as removeProtocol,
|
|
334
|
+
gt as removeQueryParams,
|
|
335
|
+
H as setInputValue,
|
|
336
|
+
pt as setQueryParams,
|
|
318
337
|
y as sleep,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
338
|
+
W as slugify,
|
|
339
|
+
St as sortObject,
|
|
340
|
+
Et as sortObjectKeys,
|
|
341
|
+
M as splitArrayIntoChunks,
|
|
323
342
|
w as stripHtml,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
zt as ucFirst,
|
|
331
|
-
st as unsubscribe
|
|
343
|
+
tt as subscribe,
|
|
344
|
+
V as toBase64,
|
|
345
|
+
N as toErrorWithMessage,
|
|
346
|
+
G as tryCatch,
|
|
347
|
+
Ct as ucFirst,
|
|
348
|
+
et as unsubscribe
|
|
332
349
|
};
|
|
333
350
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/helpers/helpers.ts","../src/batch/batch.ts","../src/colors/colors.ts","../src/dom/dom.ts","../src/errors/errors.ts","../src/events/events.ts","../src/format/format.ts","../src/http/http.ts","../src/numbers/numbers.ts","../src/objects/objects.ts","../src/pagination/pagination.ts","../src/parsers/parsers.ts","../src/random/random.ts","../src/string/string.ts","../src/time/time.ts"],"sourcesContent":["import slugifyString from \"@sindresorhus/slugify\"\nimport type { ReplaceNullWithUndefined } from \"..\"\n\n/**\n * A collection of helper functions used throughout the application.\n */\n\n/**\n * A utility function that generates an array of numbers within a specified range.\n * @param start - The starting number of the range.\n * @param end - The ending number of the range.\n * @returns An array of numbers within the specified range.\n */\nexport const range = (start: number, end: number) => {\n const length = end - start + 1\n\n return Array.from({ length }, (_, idx) => idx + start)\n}\n\n/**\n * Delays the execution of the function by the specified amount of time.\n * @param delay - The amount of time to delay the execution of the function, in milliseconds.\n */\nexport const sleep = async (delay: number) => {\n return new Promise(resolve => setTimeout(resolve, delay))\n}\n\n/**\n * Returns a label for the first search key shortcut found.\n * @returns The label for the shortcut.\n */\nexport const getShortcutLabel = ({ key, metaKey }: { key: string; metaKey?: boolean }) => {\n const label = `${metaKey ? \"⌘\" : \"\"}${key.toUpperCase()}`\n return label\n}\n\n/**\n * Strip html tags from a string\n * @param string - string to strip tags from\n * @returns string without html tags\n */\nexport const stripHtml = (string: string) => {\n return string.replace(/<[^>]*>?/gm, \"\")\n}\n\n/**\n * Convert newlines to specified element\n * @param string - string to convert\n * @param replacement - replacement to convert newlines to\n * @returns string with newlines converted to specified element\n */\nexport const convertNewlines = (string: string, replacement = \" \") => {\n return string.replace(/\\n+/g, replacement)\n}\n\n/**\n * Get an excerpt from a string\n * @param content - The string to get an excerpt from\n * @param length - The length of the excerpt\n * @returns An excerpt from the string\n */\nexport const getExcerpt = (content: string | undefined | null, length = 250) => {\n if (!content) {\n return null\n }\n\n const plainText = convertNewlines(stripHtml(content))\n const text = plainText.slice(0, length).trim()\n\n if (text.length < plainText.length) {\n return `${text}...`\n }\n\n return text\n}\n\n/**\n * Converts a string into a slugified version.\n *\n * @param input The string to be slugified.\n * @param decamelize Whether to decamelize the string. Defaults to false.\n * @returns The slugified string.\n */\nexport const slugify = (input: string, decamelize = false): string => {\n return slugifyString(input, {\n decamelize,\n customReplacements: [\n [\"#\", \"sharp\"],\n [\"+\", \"plus\"],\n ],\n })\n}\n\n/**\n * Check if a given string is a valid cuid\n * @param id A string to check\n * @returns A boolean indicating if the string is a cuid\n */\nexport const isCuid = (id: string) => {\n return id.length === 25 && id[0] === \"c\"\n}\n\n/**\n * Check if a value is truthy\n * @param value - The value to check\n * @returns A boolean indicating if the value is truthy\n */\nexport const isTruthy = <T>(value?: T | undefined | null | false): value is T => {\n return !!value\n}\n\n/**\n * Get the initials from a string\n * @param value A string to get the initials from\n * @param limit The maximum number of initials to return\n * @returns The initials from the string\n */\nexport const getInitials = (value?: string | null, limit = 0) => {\n const val = (value || \"\").trim()\n\n // If the value is empty, a single character, or two characters (already initials)\n if (val.length === 0 || val.length === 1 || val.length === 2) {\n return val.toUpperCase()\n }\n\n const values = val.split(\" \").filter(isTruthy)\n const initials = values.map(name => name.charAt(0).toUpperCase()).join(\"\")\n\n if (limit > 0) {\n return initials.slice(0, limit)\n }\n\n return initials\n}\n\n/**\n * Converts a File object to a Base64 encoded string.\n * @param file - The File object to be converted.\n * @returns A promise that resolves with the Base64 encoded string.\n */\nexport const toBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(file)\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = error => reject(error)\n })\n}\n\n/**\n * Splits an array into chunks of a specified size.\n * @param items - The array of items to split into chunks.\n * @param chunkSize - The size of each chunk.\n * @returns An array of arrays, each containing a chunk of the original array.\n */\nexport const splitArrayIntoChunks = <T>(items: T[], chunkSize: number) => {\n const chunks: T[][] = []\n\n for (let i = 0; i < items.length; i += chunkSize) {\n chunks.push(items.slice(i, i + chunkSize))\n }\n\n return chunks\n}\n\n/**\n * Set the value of an HTMLInputElement using its native value setter.\n *\n * @param input - The HTMLInputElement to set the value of.\n * @param value - The value to set on the input element.\n */\nexport const setInputValue = (\n input: HTMLInputElement | null | undefined,\n value: unknown,\n triggerChange = false,\n) => {\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n \"value\",\n )?.set\n\n nativeInputValueSetter?.call(input, value)\n\n // Trigger a change event if the value was changed\n triggerChange && input?.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/**\n * Joins an array of strings into a sentence, with a maximum of 3 items.\n *\n * @param items The array of strings to be joined.\n * @param maxItems The maximum number of items to include in the sentence.\n * @returns The joined sentence.\n */\nexport const joinAsSentence = (items: string[], maxItems = 3, lastItem = \"and\") => {\n return items\n .slice(0, maxItems)\n .join(\", \")\n .replace(/, ([^,]*)$/, ` ${lastItem} $1`)\n}\n\n/**\n * A type representing a successful result with data and no error.\n */\ntype Success<T> = {\n data: T\n error: null\n}\n\n/**\n * A type representing a failed result with no data and an error.\n */\ntype Failure<E> = {\n data: null\n error: E\n}\n\n/**\n * A type representing a result with either data or an error.\n */\ntype Result<T, E = Error> = Success<T> | Failure<E>\n\n/**\n * Wraps a promise and returns a result object with the data or error\n * @param promise - The promise to wrap\n * @returns A result object with the data or error\n */\nexport const tryCatch = async <T, E = Error>(promise: Promise<T>): Promise<Result<T, E>> => {\n try {\n const data = await promise\n return { data, error: null }\n } catch (error) {\n return { data: null, error: error as E }\n }\n}\n\n/**\n * Converts all null values in an object to undefined.\n * @param obj - The object to convert.\n * @returns The converted object.\n */\nexport const nullsToUndefined = <T>(obj: T): ReplaceNullWithUndefined<T> => {\n if (obj === null) {\n return undefined as any\n }\n\n // object check based on: https://stackoverflow.com/a/51458052/6489012\n if (obj?.constructor.name === \"Object\") {\n for (const key in obj) {\n obj[key] = nullsToUndefined(obj[key]) as any\n }\n }\n\n return obj as any\n}\n\n/**\n * Checks if a MIME type matches any of the provided patterns.\n * Supports wildcard matching for subtypes (e.g., \"image/*\").\n *\n * @param mimeType - The MIME type to check (e.g., \"image/jpeg\", \"text/plain\")\n * @param patterns - Array of MIME type patterns to match against (e.g., [\"image/*\", \"text/plain\"])\n * @returns True if the MIME type matches any of the patterns, false otherwise\n *\n * @example\n * ```typescript\n * isMimeTypeMatch(\"image/jpeg\", [\"image/*\"]) // returns true\n * isMimeTypeMatch(\"text/plain\", [\"image/*\", \"text/plain\"]) // returns true\n * isMimeTypeMatch(\"application/json\", [\"image/*\"]) // returns false\n * ```\n */\nexport const isMimeTypeMatch = (mimeType: string, patterns: string[]) => {\n return patterns.some(pattern => {\n // Split type/subtype for both mimeType and pattern\n const [type, subtype] = mimeType.split(\"/\")\n const [pType, pSubtype] = pattern.split(\"/\")\n\n // Type must match\n if (type !== pType) return false\n\n // Wildcard matches any subtype\n if (pSubtype === \"*\") return true\n\n // Exact subtype match\n return subtype === pSubtype\n })\n}\n","import { sleep, splitArrayIntoChunks } from \"../helpers/helpers\"\n\ntype ProcessBatchOptions = {\n batchSize: number\n concurrency?: number\n delay?: number\n}\n/**\n * Process items in batches with controlled concurrency and delays\n * Useful for handling external API rate limits\n */\nexport const processBatch = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions,\n): Promise<R[]> => {\n const { batchSize, concurrency = batchSize, delay = 0 } = options\n\n if (items.length === 0) return []\n\n const results: R[] = []\n const batches = splitArrayIntoChunks(items, batchSize)\n\n for (const [i, batch] of batches.entries()) {\n console.log(`Processing batch ${i + 1}/${batches.length} (${batch.length} items)`)\n\n // Process batch with controlled concurrency\n const batchResults = await processWithConcurrency(batch, processor, concurrency)\n results.push(...batchResults)\n\n // Add delay between batches (except for the last batch)\n if (delay > 0 && i < batches.length - 1) {\n console.log(`Waiting ${delay}ms before next batch...`)\n await sleep(delay)\n }\n }\n\n return results\n}\n\n/**\n * Batch processing with error handling - continues processing even if some items fail\n */\nexport const processBatchWithErrorHandling = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions & {\n onError?: (error: Error, item: T) => void\n },\n): Promise<Array<R | Error>> => {\n const { onError } = options\n\n const wrappedProcessor = async (item: T): Promise<R | Error> => {\n try {\n return await processor(item)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err, item)\n return err\n }\n }\n\n return processBatch(items, wrappedProcessor, options)\n}\n\n/**\n * Process items with controlled concurrency using a semaphore-like approach\n */\nconst processWithConcurrency = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n concurrency: number,\n): Promise<R[]> => {\n if (concurrency >= items.length) {\n // If concurrency is higher than items count, just process all at once\n return Promise.all(items.map(processor))\n }\n\n const results: R[] = []\n const executing: Promise<void>[] = []\n\n for (const item of items) {\n const promise = processor(item).then(result => {\n results.push(result)\n })\n\n executing.push(promise)\n\n // If we've reached the concurrency limit, wait for one to finish\n if (executing.length >= concurrency) {\n await Promise.race(executing)\n // Remove completed promises\n executing.splice(0, executing.length - executing.filter(p => p !== promise).length)\n }\n }\n\n // Wait for all remaining promises to complete\n await Promise.all(executing)\n\n return results\n}\n","/**\n * Check if a given color in hexadecimal format is a light color.\n * Only supports 6-digit hex colors (RGB). If longer string is provided, it will be trimmed.\n *\n * @param hexa - The hexadecimal color code to check (e.g. \"#FF0000\").\n * @returns A boolean indicating if the color is light.\n */\nexport const isLightColor = (hexa: string): boolean => {\n // Remove # if present and trim to 6 characters\n const hex = hexa.replace(\"#\", \"\").substring(0, 6)\n\n // Parse RGB values\n const r = Number.parseInt(hex.substring(0, 2), 16)\n const g = Number.parseInt(hex.substring(2, 4), 16)\n const b = Number.parseInt(hex.substring(4, 6), 16)\n\n // Calculate perceived brightness\n const brightness = r * 0.299 + g * 0.587 + b * 0.114\n\n return brightness > 186\n}\n","/**\n * Returns the position of an element with the given ID relative to the top of the viewport.\n * @param id - The ID of the element to get the position of.\n * @returns An object with the ID and top position of the element, or undefined if the element is not found.\n */\nexport const getElementPosition = (id?: string) => {\n const el = document.getElementById(id || \"\")\n if (!el) return\n\n const style = window.getComputedStyle(el)\n const scrollMt = Number.parseFloat(style.scrollMarginTop)\n const top = Math.floor(window.scrollY + el.getBoundingClientRect().top - scrollMt)\n\n return { id, top }\n}\n","/**\n * Utility functions for working with errors.\n */\n\n/**\n * An error object with a message property.\n */\nexport type ErrorWithMessage = {\n message: string\n}\n\n/**\n * Type guard function that checks if an object is an ErrorWithMessage.\n * @param error - The object to check.\n * @returns True if the object is an ErrorWithMessage, false otherwise.\n */\nexport const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n )\n}\n\n/**\n * Converts an unknown value to an ErrorWithMessage object.\n * If the value is already an ErrorWithMessage, it is returned as-is.\n * Otherwise, a new Error object is created with the value stringified as its message.\n * @param maybeError - The value to convert.\n * @returns An ErrorWithMessage object.\n */\nexport const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => {\n if (isErrorWithMessage(maybeError)) return maybeError\n\n try {\n return new Error(JSON.stringify(maybeError))\n } catch {\n // fallback in case there's an error stringifying the maybeError\n // like with circular references for example.\n return new Error(String(maybeError))\n }\n}\n\n/**\n * Gets the error message from an unknown value.\n * If the value is an ErrorWithMessage, its message property is returned.\n * Otherwise, the value is stringified and returned as the error message.\n * @param error - The value to get the error message from.\n * @returns The error message as a string.\n */\nexport const getErrorMessage = (error: unknown) => {\n return toErrorWithMessage(error).message\n}\n","/**\n * Utility functions for working with events.\n */\n\n/**\n * Subscribes to a custom event.\n * @param eventName - The name of the event to subscribe to.\n * @param listener - The function to be called when the event is triggered.\n */\nexport const subscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.addEventListener(eventName, listener)\n}\n\n/**\n * Unsubscribes from a custom event.\n * @param eventName - The name of the event to unsubscribe from.\n * @param listener - The function to be removed from the event listeners.\n */\nexport const unsubscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.removeEventListener(eventName, listener)\n}\n\n/**\n * Publishes a custom event.\n * @param eventName - The name of the event to publish.\n * @param data - The data to be passed along with the event.\n */\nexport const publish = (eventName: string, data: unknown) => {\n const event = new CustomEvent(eventName, { detail: data })\n document.dispatchEvent(event)\n}\n\n/**\n * Publishes a keyboard event for the Escape key.\n */\nexport const publishEscape = () => {\n const event = new KeyboardEvent(\"keydown\", { key: \"Escape\" })\n document.dispatchEvent(event)\n}\n","/**\n * Utility functions for formatting data.\n */\n\ntype Notation = Intl.NumberFormatOptions[\"notation\"]\ntype Currency = Intl.NumberFormatOptions[\"currency\"]\n\n/**\n * Formats a number with thousands separators.\n * @param number - The number to format.\n * @param notation - The notation to use for formatting. Defaults to 'compact'.\n * @param locale - The locale to use for formatting. Defaults to 'en-US'.\n * @returns The formatted number as a string.\n */\nexport const formatNumber = (number: number, notation: Notation = \"compact\", locale = \"en-US\") => {\n const formatter = new Intl.NumberFormat(locale, { notation })\n\n return formatter.format(number)\n}\n\n/**\n * Formats a given amount of money into a currency string.\n * @param amount The amount of money to format.\n * @param currency The currency to format the amount in. Defaults to 'USD'.\n * @returns The formatted currency string.\n */\nexport const formatCurrency = (amount: number, currency: Currency = \"USD\") => {\n const formatter = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency,\n })\n\n return formatter.format(amount).replace(/\\D00(?=\\D*$)/, \"\")\n}\n\n/**\n * Formats a given amount with an interval\n * @param amount The amount of money to format.\n * @param interval The interval, either 'month' or 'year'. Defaults to 'month'.\n * @returns The formatted amount per interval.\n */\nexport const formatIntervalAmount = (amount: number, interval: \"month\" | \"year\" = \"month\") => {\n return formatToDecimals(amount / (interval === \"year\" ? 12 : 1), 2)\n}\n\n/**\n * Formats a number to a specified number of decimal places.\n * @param number - The number to format.\n * @param precision - The number of decimal places to format to.\n * @returns The formatted number as a string.\n */\nexport const formatToDecimals = (number: number, precision = 0): string => {\n return number.toFixed(precision < 0 ? 0 : precision).replace(/\\.0+$/, \"\")\n}\n\n/**\n * Formats a number of bytes to a human-readable string.\n * @param bytes - The number of bytes to format.\n * @param precision - The number of decimal places to format the size to.\n * @returns The formatted size as a string.\n */\nexport const formatBytes = (bytes: number, precision = 0): string => {\n const k = 1024\n\n if (bytes < k) {\n const factor = 10 ** precision\n const size = Math.floor((bytes / k) * factor) / factor\n return `${size} KB`\n }\n\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\n return `${size} ${sizes[i]}`\n}\n\n/**\n * Formats a MIME type string to a more readable format.\n * @param mimeType - The MIME type string to format.\n * @returns The formatted MIME type string.\n */\nexport const formatMimeType = (mimeType: string): string | undefined => {\n const [, subtype] = mimeType.split(\"/\")\n let type: string | undefined\n\n switch (subtype) {\n case \"*\":\n return undefined\n default:\n type = subtype\n }\n\n return type?.toUpperCase()\n}\n","/**\n * Utility functions for URL manipulation and validation.\n */\n\n/**\n * Enhanced URL validation using regex pattern and URL constructor\n * Supports both regular domains and localhost/IP addresses\n */\nconst URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,63}\\b|localhost(?::\\d+)?|127\\.0\\.0\\.1(?::\\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/\n\n/**\n * Checks if a URL is valid using both regex and URL constructor validation\n * @param url - The URL to validate\n * @returns True if the URL is valid\n */\nexport const isValidUrl = (url?: string): boolean => {\n if (!url || typeof url !== \"string\") return false\n\n // First check with regex for basic format\n if (!URL_REGEX.test(url)) return false\n\n // Then validate with URL constructor\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Adds protocol to a URL string\n * @param url - The URL string without protocol\n * @param secure - Whether to use https (default: true, false for localhost)\n * @returns URL with protocol added\n */\nexport const addProtocol = (url?: string, secure?: boolean): string => {\n if (!url) return \"\"\n\n // Don't add protocol if already present\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) return url\n\n // Determine protocol based on secure flag and URL\n const protocol =\n secure !== undefined ? (secure ? \"https\" : \"http\") : isLocalhostUrl(url) ? \"http\" : \"https\"\n\n return `${protocol}://${url}`\n}\n\n/**\n * Removes protocol from a URL string\n * @param url - The URL string with protocol\n * @returns URL without protocol\n */\nexport const removeProtocol = (url?: string): string => {\n return url?.replace(/^https?:\\/\\//, \"\") ?? \"\"\n}\n\n/**\n * Normalizes a URL by removing trailing slashes and cleaning up format\n * @param url - The URL to normalize\n * @returns Normalized URL\n */\nexport const normalizeUrl = (url?: string): string => {\n if (!url) return \"\"\n\n let normalized = url.trim()\n\n // Handle URLs with query parameters or hash fragments\n if (normalized.includes(\"?\") || normalized.includes(\"#\")) {\n try {\n const parsedUrl = new URL(normalized)\n // Remove trailing slash from pathname only\n if (parsedUrl.pathname.length > 1 && parsedUrl.pathname.endsWith(\"/\")) {\n parsedUrl.pathname = parsedUrl.pathname.slice(0, -1)\n }\n return parsedUrl.toString()\n } catch {\n // Fallback for invalid URLs - just remove trailing slash if no query/hash\n return normalized.length > 1 && normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized\n }\n }\n\n // Simple case: remove trailing slash but keep root slash\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1)\n }\n\n return normalized\n}\n\n/**\n * Gets the base URL (protocol + hostname + port)\n * @param url - The URL string\n * @returns Base URL without path, search, or hash\n */\nexport const getBaseUrl = (url: string): string => {\n try {\n const parsedUrl = new URL(url)\n return `${parsedUrl.protocol}//${parsedUrl.host}`\n } catch {\n return url\n }\n}\n\n/**\n * Extracts the domain name from a URL\n * @param url - The URL string\n * @returns Domain name without www prefix\n */\nexport const getDomain = (url: string): string => {\n try {\n if (!isValidUrl(url)) return url\n\n const hostname = new URL(url).hostname\n return hostname.startsWith(\"www.\") ? hostname.slice(4) : hostname\n } catch {\n return url\n }\n}\n\n/**\n * Checks if a URL is external (has protocol)\n * @param url - The URL to check\n * @returns True if URL is external (contains protocol)\n */\nexport const isExternalUrl = (url?: string): boolean => {\n if (!url) return false\n return /^https?:\\/\\//.test(url)\n}\n\n/**\n * Checks if a URL is a localhost URL\n * @param url - The URL to check\n * @returns True if URL points to localhost\n */\nexport const isLocalhostUrl = (url?: string): boolean => {\n if (!url) return false\n\n try {\n const parsedUrl = new URL(addProtocol(url))\n const hostname = parsedUrl.hostname\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname.endsWith(\".localhost\")\n } catch {\n return url.includes(\"localhost\") || url.includes(\"127.0.0.1\")\n }\n}\n\n/**\n * Joins URL paths safely\n * @param base - Base URL\n * @param paths - Path segments to join\n * @returns Combined URL\n */\nexport const joinUrlPaths = (base: string, ...paths: string[]): string => {\n if (!base) return \"\"\n\n let result = normalizeUrl(base)\n\n for (const path of paths) {\n if (!path) continue\n\n const cleanPath = path.replace(/^\\/+|\\/+$/g, \"\") // Remove leading/trailing slashes\n if (cleanPath) {\n result += `/${cleanPath}`\n }\n }\n\n return result\n}\n\n/**\n * Extracts query parameters from a URL\n * @param url - The URL string\n * @returns Object containing query parameters\n */\nexport const getQueryParams = (url: string): Record<string, string> => {\n try {\n const parsedUrl = new URL(url)\n const params: Record<string, string> = {}\n\n parsedUrl.searchParams.forEach((value, key) => {\n params[key] = value\n })\n\n return params\n } catch {\n return {}\n }\n}\n\n/**\n * Adds or updates query parameters in a URL\n * @param url - The base URL\n * @param params - Parameters to add/update\n * @returns URL with updated parameters\n */\nexport const setQueryParams = (\n url: string,\n params: Record<string, string | number | boolean>,\n): string => {\n try {\n const parsedUrl = new URL(url)\n\n Object.entries(params).forEach(([key, value]) => {\n parsedUrl.searchParams.set(key, String(value))\n })\n\n let result = parsedUrl.toString()\n\n // Special case: remove trailing slash before query parameters for cleaner URLs\n result = result.replace(/\\/\\?/, \"?\")\n\n return result\n } catch {\n return url\n }\n}\n\n/**\n * Removes query parameters from a URL\n * @param url - The URL string\n * @returns URL without query parameters\n */\nexport const removeQueryParams = (url?: string): string => {\n if (!url) return \"\"\n\n try {\n const parsedUrl = new URL(url)\n parsedUrl.search = \"\"\n return normalizeUrl(parsedUrl.toString())\n } catch {\n // For invalid URLs, try simple string manipulation\n const questionIndex = url.indexOf(\"?\")\n return questionIndex !== -1 ? url.substring(0, questionIndex) : url\n }\n}\n\n// Legacy aliases for backward compatibility (deprecated)\n/** @deprecated Use addProtocol instead */\nexport const addHttp = addProtocol\n\n/** @deprecated Use removeProtocol instead */\nexport const removeHttp = removeProtocol\n\n/** @deprecated Use normalizeUrl instead */\nexport const removeTrailingSlash = normalizeUrl\n\n/** @deprecated Use normalizeUrl instead */\nexport const stripTrailingSlash = normalizeUrl\n\n/** @deprecated Use getBaseUrl instead */\nexport const stripURLSubpath = getBaseUrl\n\n/** @deprecated Use getDomain instead */\nexport const getUrlHostname = getDomain\n\n/** @deprecated Use removeQueryParams instead */\nexport const removeSearchParams = removeQueryParams\n\n/** @deprecated Use getQueryParams instead */\nexport const getSearchParams = getQueryParams\n\n/** @deprecated Use setQueryParams instead */\nexport const addSearchParams = setQueryParams\n","/**\n * Utility functions for working with numbers.\n */\n\n/**\n * Keep a number within a specified range.\n * @param value - The number to keep within the range.\n * @param min - The minimum value of the range.\n * @param max - The maximum value of the range.\n * @returns The number within the specified range.\n */\nexport const keepNumberInRange = (value: number, min?: number, max?: number) => {\n if (min !== undefined && max !== undefined) {\n return Math.min(Math.max(value, min), max)\n }\n if (min !== undefined) {\n return Math.max(value, min)\n }\n if (max !== undefined) {\n return Math.min(value, max)\n }\n\n return value\n}\n\n/**\n * Parse a string into a numeric value.\n * @param value - The value to parse into a numeric value.\n * @returns The parsed numeric value, or `undefined` if the value cannot be parsed.\n */\nexport const parseNumericValue = (value?: string | number | null) => {\n if (value === undefined || value === null) return undefined\n const parsed = Number.parseFloat(value.toString())\n\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\n/**\n * Rounds a number to a specified number of decimal places, with an adjustment for floating point precision.\n * @param value - The number to round.\n * @param decimals - The number of decimal places to round to. Defaults to 2.\n * @returns The rounded number.\n */\nexport const preciseRound = (value: number, decimals = 2) => {\n const factor = Math.pow(10, decimals)\n\n return Math.round((value + Number.EPSILON) * factor) / factor\n}\n","/**\n * Utility functions for working with objects.\n */\n\n/**\n * Checks if an object is empty (has no own properties).\n * @param obj - The object to check.\n * @returns `true` if the object is empty, `false` otherwise.\n */\nexport const isEmptyObject = (obj: Record<string, unknown> = {}) => {\n return obj.constructor === Object && !Object.entries(obj).length\n}\n\n/**\n * Checks if a key is present in an object.\n * @param key - The key to check.\n * @param obj - The object to check.\n * @returns `true` if the key is present in the object, `false` otherwise.\n */\nexport const isKeyInObject = <T extends object>(key: PropertyKey, obj: T): key is keyof T => {\n return key in obj\n}\n\n/**\n * Sorts two objects based on their keys' positions in an array of keys.\n * @param keys - An array of keys to sort the objects by.\n * @param a - The first object to compare.\n * @param b - The second object to compare.\n * @returns A number indicating the sort order of the two objects.\n */\nexport const sortObjectKeys = (keys: string[]) => {\n return (a: Record<string, unknown>, b: Record<string, unknown>) => {\n const aIndex = keys.indexOf(Object.keys(a)[0] ?? \"\")\n const bIndex = keys.indexOf(Object.keys(b)[0] ?? \"\")\n\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n\n return aIndex - bIndex\n }\n}\n\n/**\n * Sorts the keys of an object in alphabetical order and returns a new object with the sorted keys.\n * @param obj - The input object to sort.\n * @param comparator - An optional comparator function to use when sorting the keys.\n * @returns - A new object with the sorted keys.\n */\nexport const sortObject = <T extends Record<K, unknown>, K extends keyof T>(\n obj: T,\n comparator?: (a: unknown, b: unknown) => number,\n) => {\n return Object.keys(obj)\n .sort(comparator)\n .reduce((result, key) => {\n return { ...result, [key as K]: obj[key as K] }\n }, {} as T)\n}\n\n/**\n * Creates a new object with only the specified properties from the source object.\n * Provides full type safety and intellisense for the picked properties.\n *\n * @param obj - The source object to pick properties from\n * @param keys - Array of property keys to pick from the source object\n * @returns A new object containing only the specified properties\n *\n * @example\n * const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' }\n * const publicUser = pick(user, ['id', 'name', 'email'])\n * // Result: { id: 1, name: 'John', email: 'john@example.com' }\n */\nexport const pickFromObject = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key]\n }\n }\n return result\n}\n","/**\n * Utility functions for pagination and page management.\n */\n\nimport { getQueryParams } from \"../http/http\"\n\n/**\n * Represents the parameters for a paginated query.\n * @template T - The type of the query parameters.\n */\nexport type GetPageParams<T> = T & {\n take: number\n skip: number\n}\n\n/**\n * Returns the current page number from a string.\n * @param page - The page number as a string.\n * @returns The current page number as a number.\n */\nexport const getCurrentPage = (page?: string | null) => {\n return Math.max(page && !Number.isNaN(Number(page)) ? Number.parseInt(page || \"1\", 10) : 1, 1)\n}\n\n/**\n * Returns an object containing the parameters for a paginated query.\n * @template T - The type of the query parameters.\n * @param url - The URL to get the page parameters from.\n * @param take - The number of items to take per page.\n * @returns An object containing the parameters for a paginated query.\n */\nexport const getPageParams = <T extends object>(url: string, take: number) => {\n const { page, ...params } = getQueryParams(url)\n\n const currentPage = getCurrentPage(page)\n const skip = (currentPage - 1) * take\n\n return { take, skip, ...params } as GetPageParams<T>\n}\n\n/**\n * Returns a link to a specific page of a paginated query.\n * @param searchParams - The search parameters object.\n * @param pathname - The pathname of the URL.\n * @param page - The page number to link to.\n * @returns A link to the specified page of the paginated query.\n */\nexport const getPageLink = (searchParams: URLSearchParams, pathname: string, page: number) => {\n searchParams.set(\"page\", page.toString())\n\n return `${pathname}?${searchParams.toString()}`\n}\n","/**\n * Utility functions for parsing and stringifying JSON.\n */\n\n/**\n * Parses a string as JSON and returns the result. If the string cannot be parsed as JSON, returns the original string.\n * @param value - The string to parse as JSON.\n * @returns The parsed JSON object or the original string.\n * @template T - The type of the parsed JSON object.\n */\nexport const maybeParseJson = <T>(value: string) => {\n try {\n return JSON.parse(value) as T\n } catch {\n return value\n }\n}\n\n/**\n * Returns a JSON string representation of the given object, or the original string if it's not an object.\n * @param value - The value to stringify.\n * @returns The JSON string representation of the object, or the original string if it's not an object.\n */\nexport const maybeStringifyJson = (value?: object | string) => {\n if (typeof value === \"object\") {\n return JSON.stringify(value)\n }\n\n return value\n}\n","/**\n * Utility functions for generating random values.\n */\n\n/**\n * Returns a random hexadecimal color code.\n * @returns A string representing a random hexadecimal color code.\n */\nexport const getRandomColor = (): string => {\n return Math.floor(Math.random() * 16777215)\n .toString(16)\n .padStart(6, \"0\")\n}\n\n/**\n * Returns a random string of characters.\n * @param length - The desired length of the random string\n * @returns A string representing a random string of characters.\n */\nexport const getRandomString = (length = 16): string => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(\"\")\n}\n\n/**\n * Generates a random number between the specified minimum and maximum values (inclusive).\n *\n * @param min The minimum value for the random number.\n * @param max The maximum value for the random number.\n * @returns A random number between the specified minimum and maximum values.\n */\nexport const getRandomNumber = (min: number, max: number) => {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * Generate a random string of digits\n * @param length Length of the digits string\n * @returns Random digits string\n */\nexport const getRandomDigits = (length: number) => {\n return Array.from({ length }, () => Math.floor(Math.random() * 10)).join(\"\")\n}\n\n/**\n * Returns a random element from an array.\n *\n * @param array - The array to get a random element from.\n * @returns A random element from the array.\n */\nexport const getRandomElement = <T>(array: T[]): T => {\n const index = Math.floor(Math.random() * array.length)\n return array[index]!\n}\n\n/**\n * Returns a random property value from an object.\n * @param obj - The object to get a random property value from.\n * @returns A random property value from the object.\n */\nexport const getRandomProperty = <T>(obj: Record<string, T>): T => {\n const keys = Object.keys(obj)\n const randomKey = keys[Math.floor(keys.length * Math.random())]!\n\n return obj[randomKey]!\n}\n","/**\n * Utility functions for working with strings.\n */\n\n/**\n * Uppercases the first character in the `string`.\n * @param string - The string to uppercase the first character of.\n * @returns The string with the first character in uppercase.\n */\nexport const ucFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toUpperCase() + string.slice(1)\n}\n\n/**\n * Lowercases the first character in the `string`.\n * @param string - The string to lowercase the first character of.\n * @returns The string with the first character in lowercase.\n */\nexport const lcFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toLowerCase() + string.slice(1)\n}\n","/**\n * Utility functions related to time.\n */\ntype DateStyle = Intl.DateTimeFormatOptions[\"dateStyle\"]\ntype TimeStyle = Intl.DateTimeFormatOptions[\"timeStyle\"]\ntype Timestamp = string | number | Date\n\nconst getDateFormatter = (locale: string, dateStyle: DateStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle })\n}\n\nconst getTimeFormatter = (locale: string, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { timeStyle })\n}\n\nconst getDateTimeFormatter = (locale: string, dateStyle: DateStyle, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle, timeStyle })\n}\n\n/**\n * Formats a date according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date string.\n */\nexport const formatDate = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted time string.\n */\nexport const formatTime = (\n timestamp: Timestamp,\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getTimeFormatter(locale, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date and time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date and time string.\n */\nexport const formatDateTime = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getDateTimeFormatter(locale, dateStyle, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date or time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param type - The type of formatting to use. Can be 'date', 'time', or 'datetime'.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date or time string.\n */\nexport const formatDateOrTime = (\n timestamp: Timestamp,\n type: \"date\" | \"time\" | \"datetime\",\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n switch (type) {\n case \"date\":\n return formatDate(timestamp, dateStyle, locale)\n case \"time\":\n return formatTime(timestamp, timeStyle, locale)\n default:\n return formatDateTime(timestamp, dateStyle, timeStyle, locale)\n }\n}\n\n/**\n * Formats a date range.\n * @param start - The start date.\n * @param end - The end date.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date range string.\n */\nexport const formatDateRange = (\n start: Timestamp,\n end: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).formatRange(new Date(start), new Date(end))\n}\n\n/**\n * Calculates the estimated read time for a given content.\n * @param content - The content to calculate the read time for.\n * @param wpm - The average words per minute to use for the calculation. Defaults to 265.\n * @returns The estimated read time in minutes.\n */\nexport const getReadTime = (content: string | null, wpm = 265): number => {\n if (!content) {\n return 0\n }\n\n return Math.ceil(content.trim().split(/\\s+/).length / wpm)\n}\n"],"names":["range","start","end","length","_","idx","sleep","delay","resolve","getShortcutLabel","key","metaKey","stripHtml","string","convertNewlines","replacement","getExcerpt","content","plainText","text","slugify","input","decamelize","slugifyString","isCuid","id","isTruthy","value","getInitials","limit","val","initials","name","toBase64","file","reject","reader","error","splitArrayIntoChunks","items","chunkSize","chunks","i","setInputValue","triggerChange","joinAsSentence","maxItems","lastItem","tryCatch","promise","nullsToUndefined","obj","isMimeTypeMatch","mimeType","patterns","pattern","type","subtype","pType","pSubtype","processBatch","processor","options","batchSize","concurrency","results","batches","batch","batchResults","processWithConcurrency","processBatchWithErrorHandling","onError","item","err","executing","result","p","isLightColor","hexa","hex","g","b","getElementPosition","el","style","scrollMt","top","isErrorWithMessage","toErrorWithMessage","maybeError","getErrorMessage","subscribe","eventName","listener","unsubscribe","publish","data","event","publishEscape","formatNumber","number","notation","locale","formatCurrency","amount","currency","formatIntervalAmount","interval","formatToDecimals","precision","formatBytes","bytes","factor","formatMimeType","URL_REGEX","isValidUrl","url","addProtocol","secure","isLocalhostUrl","removeProtocol","normalizeUrl","normalized","parsedUrl","getBaseUrl","getDomain","hostname","isExternalUrl","joinUrlPaths","base","paths","path","cleanPath","getQueryParams","params","setQueryParams","removeQueryParams","questionIndex","addHttp","removeHttp","removeTrailingSlash","stripTrailingSlash","stripURLSubpath","getUrlHostname","removeSearchParams","getSearchParams","addSearchParams","keepNumberInRange","min","max","parseNumericValue","parsed","preciseRound","decimals","isEmptyObject","isKeyInObject","sortObjectKeys","keys","a","aIndex","bIndex","sortObject","comparator","pickFromObject","getCurrentPage","page","getPageParams","take","skip","getPageLink","searchParams","pathname","maybeParseJson","maybeStringifyJson","getRandomColor","getRandomString","chars","getRandomNumber","getRandomDigits","getRandomElement","array","index","getRandomProperty","randomKey","ucFirst","lcFirst","getDateFormatter","dateStyle","getTimeFormatter","timeStyle","getDateTimeFormatter","formatDate","timestamp","formatTime","formatDateTime","formatDateOrTime","formatDateRange","getReadTime","wpm"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/helpers/helpers.ts","../src/batch/batch.ts","../src/colors/colors.ts","../src/dom/dom.ts","../src/errors/errors.ts","../src/events/events.ts","../src/format/format.ts","../src/http/http.ts","../src/numbers/numbers.ts","../src/objects/objects.ts","../src/pagination/pagination.ts","../src/parsers/parsers.ts","../src/random/random.ts","../src/string/string.ts","../src/time/time.ts"],"sourcesContent":["import slugifyString from \"@sindresorhus/slugify\"\nimport type { ReplaceNullWithUndefined } from \"..\"\n\n/**\n * A collection of helper functions used throughout the application.\n */\n\n/**\n * A utility function that generates an array of numbers within a specified range.\n * @param start - The starting number of the range.\n * @param end - The ending number of the range.\n * @returns An array of numbers within the specified range.\n */\nexport const range = (start: number, end: number) => {\n const length = end - start + 1\n\n return Array.from({ length }, (_, idx) => idx + start)\n}\n\n/**\n * Delays the execution of the function by the specified amount of time.\n * @param delay - The amount of time to delay the execution of the function, in milliseconds.\n */\nexport const sleep = async (delay: number) => {\n return new Promise(resolve => setTimeout(resolve, delay))\n}\n\n/**\n * Returns a label for the first search key shortcut found.\n * @returns The label for the shortcut.\n */\nexport const getShortcutLabel = ({ key, metaKey }: { key: string; metaKey?: boolean }) => {\n const label = `${metaKey ? \"⌘\" : \"\"}${key.toUpperCase()}`\n return label\n}\n\n/**\n * Strip html tags from a string\n * @param string - string to strip tags from\n * @returns string without html tags\n */\nexport const stripHtml = (string: string) => {\n return string.replace(/<[^>]*>?/gm, \"\")\n}\n\n/**\n * Convert newlines to specified element\n * @param string - string to convert\n * @param replacement - replacement to convert newlines to\n * @returns string with newlines converted to specified element\n */\nexport const convertNewlines = (string: string, replacement = \" \") => {\n return string.replace(/\\n+/g, replacement)\n}\n\n/**\n * Get an excerpt from a string\n * @param content - The string to get an excerpt from\n * @param length - The length of the excerpt\n * @returns An excerpt from the string\n */\nexport const getExcerpt = (content: string | undefined | null, length = 250) => {\n if (!content) {\n return null\n }\n\n const plainText = convertNewlines(stripHtml(content))\n const text = plainText.slice(0, length).trim()\n\n if (text.length < plainText.length) {\n return `${text}...`\n }\n\n return text\n}\n\n/**\n * Converts a string into a slugified version.\n *\n * @param input The string to be slugified.\n * @param decamelize Whether to decamelize the string. Defaults to false.\n * @returns The slugified string.\n */\nexport const slugify = (input: string, decamelize = false): string => {\n return slugifyString(input, {\n decamelize,\n customReplacements: [\n [\"#\", \"sharp\"],\n [\"+\", \"plus\"],\n ],\n })\n}\n\n/**\n * Check if a given string is a valid cuid\n * @param id A string to check\n * @returns A boolean indicating if the string is a cuid\n */\nexport const isCuid = (id: string) => {\n return id.length === 25 && id[0] === \"c\"\n}\n\n/**\n * Check if a value is truthy\n * @param value - The value to check\n * @returns A boolean indicating if the value is truthy\n */\nexport const isTruthy = <T>(value?: T | undefined | null | false): value is T => {\n return !!value\n}\n\n/**\n * Get the initials from a string\n * @param value A string to get the initials from\n * @param limit The maximum number of initials to return\n * @returns The initials from the string\n */\nexport const getInitials = (value?: string | null, limit = 0) => {\n const val = (value || \"\").trim()\n\n // If the value is empty, a single character, or two characters (already initials)\n if (val.length === 0 || val.length === 1 || val.length === 2) {\n return val.toUpperCase()\n }\n\n const values = val.split(\" \").filter(isTruthy)\n const initials = values.map(name => name.charAt(0).toUpperCase()).join(\"\")\n\n if (limit > 0) {\n return initials.slice(0, limit)\n }\n\n return initials\n}\n\n/**\n * Converts a File object to a Base64 encoded string.\n * @param file - The File object to be converted.\n * @returns A promise that resolves with the Base64 encoded string.\n */\nexport const toBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(file)\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = error => reject(error)\n })\n}\n\n/**\n * Splits an array into chunks of a specified size.\n * @param items - The array of items to split into chunks.\n * @param chunkSize - The size of each chunk.\n * @returns An array of arrays, each containing a chunk of the original array.\n */\nexport const splitArrayIntoChunks = <T>(items: T[], chunkSize: number) => {\n const chunks: T[][] = []\n\n for (let i = 0; i < items.length; i += chunkSize) {\n chunks.push(items.slice(i, i + chunkSize))\n }\n\n return chunks\n}\n\n/**\n * Set the value of an HTMLInputElement using its native value setter.\n *\n * @param input - The HTMLInputElement to set the value of.\n * @param value - The value to set on the input element.\n */\nexport const setInputValue = (\n input: HTMLInputElement | null | undefined,\n value: unknown,\n triggerChange = false,\n) => {\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n \"value\",\n )?.set\n\n nativeInputValueSetter?.call(input, value)\n\n // Trigger a change event if the value was changed\n triggerChange && input?.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/**\n * Joins an array of strings into a sentence, with a maximum of 3 items.\n *\n * @param items The array of strings to be joined.\n * @param maxItems The maximum number of items to include in the sentence.\n * @returns The joined sentence.\n */\nexport const joinAsSentence = (items: string[], maxItems = 3, lastItem = \"and\") => {\n return items\n .slice(0, maxItems)\n .join(\", \")\n .replace(/, ([^,]*)$/, ` ${lastItem} $1`)\n}\n\n/**\n * A type representing a successful result with data and no error.\n */\ntype Success<T> = {\n data: T\n error: null\n}\n\n/**\n * A type representing a failed result with no data and an error.\n */\ntype Failure<E> = {\n data: null\n error: E\n}\n\n/**\n * A type representing a result with either data or an error.\n */\ntype Result<T, E = Error> = Success<T> | Failure<E>\n\n/**\n * Wraps a promise and returns a result object with the data or error\n * @param promise - The promise to wrap\n * @returns A result object with the data or error\n */\nexport const tryCatch = async <T, E = Error>(promise: Promise<T>): Promise<Result<T, E>> => {\n try {\n const data = await promise\n return { data, error: null }\n } catch (error) {\n return { data: null, error: error as E }\n }\n}\n\n/**\n * Converts all null values in an object to undefined.\n * @param obj - The object to convert.\n * @returns The converted object.\n */\nexport const nullsToUndefined = <T>(obj: T): ReplaceNullWithUndefined<T> => {\n if (obj === null) {\n return undefined as any\n }\n\n // object check based on: https://stackoverflow.com/a/51458052/6489012\n if (obj?.constructor.name === \"Object\") {\n for (const key in obj) {\n obj[key] = nullsToUndefined(obj[key]) as any\n }\n }\n\n return obj as any\n}\n\n/**\n * Checks if a MIME type matches any of the provided patterns.\n * Supports wildcard matching for subtypes (e.g., \"image/*\").\n *\n * @param mimeType - The MIME type to check (e.g., \"image/jpeg\", \"text/plain\")\n * @param patterns - Array of MIME type patterns to match against (e.g., [\"image/*\", \"text/plain\"])\n * @returns True if the MIME type matches any of the patterns, false otherwise\n *\n * @example\n * ```typescript\n * isMimeTypeMatch(\"image/jpeg\", [\"image/*\"]) // returns true\n * isMimeTypeMatch(\"text/plain\", [\"image/*\", \"text/plain\"]) // returns true\n * isMimeTypeMatch(\"application/json\", [\"image/*\"]) // returns false\n * ```\n */\nexport const isMimeTypeMatch = (mimeType: string, patterns: string[]) => {\n return patterns.some(pattern => {\n // Split type/subtype for both mimeType and pattern\n const [type, subtype] = mimeType.split(\"/\")\n const [pType, pSubtype] = pattern.split(\"/\")\n\n // Type must match\n if (type !== pType) return false\n\n // Wildcard matches any subtype\n if (pSubtype === \"*\") return true\n\n // Exact subtype match\n return subtype === pSubtype\n })\n}\n","import { sleep, splitArrayIntoChunks } from \"../helpers/helpers\"\n\ntype ProcessBatchOptions = {\n batchSize: number\n concurrency?: number\n delay?: number\n}\n/**\n * Process items in batches with controlled concurrency and delays\n * Useful for handling external API rate limits\n */\nexport const processBatch = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions,\n): Promise<R[]> => {\n const { batchSize, concurrency = batchSize, delay = 0 } = options\n\n if (items.length === 0) return []\n\n const results: R[] = []\n const batches = splitArrayIntoChunks(items, batchSize)\n\n for (const [i, batch] of batches.entries()) {\n console.log(`Processing batch ${i + 1}/${batches.length} (${batch.length} items)`)\n\n // Process batch with controlled concurrency\n const batchResults = await processWithConcurrency(batch, processor, concurrency)\n results.push(...batchResults)\n\n // Add delay between batches (except for the last batch)\n if (delay > 0 && i < batches.length - 1) {\n console.log(`Waiting ${delay}ms before next batch...`)\n await sleep(delay)\n }\n }\n\n return results\n}\n\n/**\n * Batch processing with error handling - continues processing even if some items fail\n */\nexport const processBatchWithErrorHandling = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions & {\n onError?: (error: Error, item: T) => void\n },\n): Promise<Array<R | Error>> => {\n const { onError } = options\n\n const wrappedProcessor = async (item: T): Promise<R | Error> => {\n try {\n return await processor(item)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err, item)\n return err\n }\n }\n\n return processBatch(items, wrappedProcessor, options)\n}\n\n/**\n * Process items with controlled concurrency using a semaphore-like approach\n */\nconst processWithConcurrency = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n concurrency: number,\n): Promise<R[]> => {\n if (concurrency >= items.length) {\n // If concurrency is higher than items count, just process all at once\n return Promise.all(items.map(processor))\n }\n\n const results: R[] = []\n const executing: Promise<void>[] = []\n\n for (const item of items) {\n const promise = processor(item).then(result => {\n results.push(result)\n })\n\n executing.push(promise)\n\n // If we've reached the concurrency limit, wait for one to finish\n if (executing.length >= concurrency) {\n await Promise.race(executing)\n // Remove completed promises\n executing.splice(0, executing.length - executing.filter(p => p !== promise).length)\n }\n }\n\n // Wait for all remaining promises to complete\n await Promise.all(executing)\n\n return results\n}\n","/**\n * Check if a given color in hexadecimal format is a light color.\n * Only supports 6-digit hex colors (RGB). If longer string is provided, it will be trimmed.\n *\n * @param hexa - The hexadecimal color code to check (e.g. \"#FF0000\").\n * @returns A boolean indicating if the color is light.\n */\nexport const isLightColor = (hexa: string): boolean => {\n // Remove # if present and trim to 6 characters\n const hex = hexa.replace(\"#\", \"\").substring(0, 6)\n\n // Parse RGB values\n const r = Number.parseInt(hex.substring(0, 2), 16)\n const g = Number.parseInt(hex.substring(2, 4), 16)\n const b = Number.parseInt(hex.substring(4, 6), 16)\n\n // Calculate perceived brightness\n const brightness = r * 0.299 + g * 0.587 + b * 0.114\n\n return brightness > 186\n}\n","/**\n * Returns the position of an element with the given ID relative to the top of the viewport.\n * @param id - The ID of the element to get the position of.\n * @returns An object with the ID and top position of the element, or undefined if the element is not found.\n */\nexport const getElementPosition = (id?: string) => {\n const el = document.getElementById(id || \"\")\n if (!el) return\n\n const style = window.getComputedStyle(el)\n const scrollMt = Number.parseFloat(style.scrollMarginTop)\n const top = Math.floor(window.scrollY + el.getBoundingClientRect().top - scrollMt)\n\n return { id, top }\n}\n","/**\n * Utility functions for working with errors.\n */\n\n/**\n * An error object with a message property.\n */\nexport type ErrorWithMessage = {\n message: string\n}\n\n/**\n * Type guard function that checks if an object is an ErrorWithMessage.\n * @param error - The object to check.\n * @returns True if the object is an ErrorWithMessage, false otherwise.\n */\nexport const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n )\n}\n\n/**\n * Converts an unknown value to an ErrorWithMessage object.\n * If the value is already an ErrorWithMessage, it is returned as-is.\n * Otherwise, a new Error object is created with the value stringified as its message.\n * @param maybeError - The value to convert.\n * @returns An ErrorWithMessage object.\n */\nexport const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => {\n if (isErrorWithMessage(maybeError)) return maybeError\n\n try {\n return new Error(JSON.stringify(maybeError))\n } catch {\n // fallback in case there's an error stringifying the maybeError\n // like with circular references for example.\n return new Error(String(maybeError))\n }\n}\n\n/**\n * Gets the error message from an unknown value.\n * If the value is an ErrorWithMessage, its message property is returned.\n * Otherwise, the value is stringified and returned as the error message.\n * @param error - The value to get the error message from.\n * @returns The error message as a string.\n */\nexport const getErrorMessage = (error: unknown) => {\n return toErrorWithMessage(error).message\n}\n","/**\n * Utility functions for working with events.\n */\n\n/**\n * Subscribes to a custom event.\n * @param eventName - The name of the event to subscribe to.\n * @param listener - The function to be called when the event is triggered.\n */\nexport const subscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.addEventListener(eventName, listener)\n}\n\n/**\n * Unsubscribes from a custom event.\n * @param eventName - The name of the event to unsubscribe from.\n * @param listener - The function to be removed from the event listeners.\n */\nexport const unsubscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.removeEventListener(eventName, listener)\n}\n\n/**\n * Publishes a custom event.\n * @param eventName - The name of the event to publish.\n * @param data - The data to be passed along with the event.\n */\nexport const publish = (eventName: string, data: unknown) => {\n const event = new CustomEvent(eventName, { detail: data })\n document.dispatchEvent(event)\n}\n\n/**\n * Publishes a keyboard event for the Escape key.\n */\nexport const publishEscape = () => {\n const event = new KeyboardEvent(\"keydown\", { key: \"Escape\" })\n document.dispatchEvent(event)\n}\n","/**\n * Utility functions for formatting data.\n */\n\ntype Notation = Intl.NumberFormatOptions[\"notation\"]\ntype Currency = Intl.NumberFormatOptions[\"currency\"]\n\n/**\n * Formats a number with thousands separators.\n * @param number - The number to format.\n * @param notation - The notation to use for formatting. Defaults to 'compact'.\n * @param locale - The locale to use for formatting. Defaults to 'en-US'.\n * @returns The formatted number as a string.\n */\nexport const formatNumber = (number: number, notation: Notation = \"compact\", locale = \"en-US\") => {\n const formatter = new Intl.NumberFormat(locale, { notation })\n\n return formatter.format(number)\n}\n\n/**\n * Formats a given amount of money into a currency string.\n * @param amount The amount of money to format.\n * @param currency The currency to format the amount in. Defaults to 'USD'.\n * @returns The formatted currency string.\n */\nexport const formatCurrency = (amount: number, currency: Currency = \"USD\") => {\n const formatter = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency,\n })\n\n return formatter.format(amount).replace(/\\D00(?=\\D*$)/, \"\")\n}\n\n/**\n * Formats a given amount with an interval\n * @param amount The amount of money to format.\n * @param interval The interval, either 'month' or 'year'. Defaults to 'month'.\n * @returns The formatted amount per interval.\n */\nexport const formatIntervalAmount = (amount: number, interval: \"month\" | \"year\" = \"month\") => {\n return formatToDecimals(amount / (interval === \"year\" ? 12 : 1), 2)\n}\n\n/**\n * Formats a number to a specified number of decimal places.\n * @param number - The number to format.\n * @param precision - The number of decimal places to format to.\n * @returns The formatted number as a string.\n */\nexport const formatToDecimals = (number: number, precision = 0): string => {\n return number.toFixed(precision < 0 ? 0 : precision).replace(/\\.0+$/, \"\")\n}\n\n/**\n * Formats a number of bytes to a human-readable string.\n * @param bytes - The number of bytes to format.\n * @param precision - The number of decimal places to format the size to.\n * @returns The formatted size as a string.\n */\nexport const formatBytes = (bytes: number, precision = 0): string => {\n const k = 1024\n\n if (bytes < k) {\n const factor = 10 ** precision\n const size = Math.floor((bytes / k) * factor) / factor\n return `${size} KB`\n }\n\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\n return `${size} ${sizes[i]}`\n}\n\n/**\n * Formats a MIME type string to a more readable format.\n * @param mimeType - The MIME type string to format.\n * @returns The formatted MIME type string.\n */\nexport const formatMimeType = (mimeType: string): string | undefined => {\n const [, subtype] = mimeType.split(\"/\")\n let type: string | undefined\n\n switch (subtype) {\n case \"*\":\n return undefined\n default:\n type = subtype\n }\n\n return type?.toUpperCase()\n}\n","/**\n * Utility functions for URL manipulation and validation.\n */\n\n/**\n * Enhanced URL validation using regex pattern and URL constructor\n * Supports both regular domains and localhost/IP addresses\n */\nconst URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,63}\\b|localhost(?::\\d+)?|127\\.0\\.0\\.1(?::\\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/\n\n/**\n * Checks if a URL is valid using both regex and URL constructor validation\n * @param url - The URL to validate\n * @returns True if the URL is valid\n */\nexport const isValidUrl = (url?: string): boolean => {\n if (!url || typeof url !== \"string\") return false\n\n // First check with regex for basic format\n if (!URL_REGEX.test(url)) return false\n\n // Then validate with URL constructor\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Adds protocol to a URL string\n * @param url - The URL string without protocol\n * @param secure - Whether to use https (default: true, false for localhost)\n * @returns URL with protocol added\n */\nexport const addProtocol = (url?: string, secure?: boolean): string => {\n if (!url) return \"\"\n\n // Don't add protocol if already present\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) return url\n\n // Determine protocol based on secure flag and URL\n const protocol =\n secure !== undefined ? (secure ? \"https\" : \"http\") : isLocalhostUrl(url) ? \"http\" : \"https\"\n\n return `${protocol}://${url}`\n}\n\n/**\n * Removes protocol from a URL string\n * @param url - The URL string with protocol\n * @returns URL without protocol\n */\nexport const removeProtocol = (url?: string): string => {\n return url?.replace(/^https?:\\/\\//, \"\") ?? \"\"\n}\n\n/**\n * Normalizes a URL by removing trailing slashes and cleaning up format\n * @param url - The URL to normalize\n * @returns Normalized URL\n */\nexport const normalizeUrl = (url?: string): string => {\n if (!url) return \"\"\n\n let normalized = url.trim()\n\n // Handle URLs with query parameters or hash fragments\n if (normalized.includes(\"?\") || normalized.includes(\"#\")) {\n try {\n const parsedUrl = new URL(normalized)\n // Remove trailing slash from pathname only\n if (parsedUrl.pathname.length > 1 && parsedUrl.pathname.endsWith(\"/\")) {\n parsedUrl.pathname = parsedUrl.pathname.slice(0, -1)\n }\n return parsedUrl.toString()\n } catch {\n // Fallback for invalid URLs - just remove trailing slash if no query/hash\n return normalized.length > 1 && normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized\n }\n }\n\n // Simple case: remove trailing slash but keep root slash\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1)\n }\n\n return normalized\n}\n\n/**\n * Gets the base URL (protocol + hostname + port)\n * @param url - The URL string\n * @returns Base URL without path, search, or hash\n */\nexport const getBaseUrl = (url: string): string => {\n try {\n const parsedUrl = new URL(url)\n return `${parsedUrl.protocol}//${parsedUrl.host}`\n } catch {\n return url\n }\n}\n\n/**\n * Extracts the domain name from a URL\n * @param url - The URL string\n * @returns Domain name without www prefix\n */\nexport const getDomain = (url: string): string => {\n try {\n if (!isValidUrl(url)) return url\n\n const hostname = new URL(url).hostname\n return hostname.startsWith(\"www.\") ? hostname.slice(4) : hostname\n } catch {\n return url\n }\n}\n\n/**\n * Checks if a URL is external (has protocol)\n * @param url - The URL to check\n * @returns True if URL is external (contains protocol)\n */\nexport const isExternalUrl = (url?: string): boolean => {\n if (!url) return false\n return /^https?:\\/\\//.test(url)\n}\n\n/**\n * Checks if a URL is a localhost URL\n * @param url - The URL to check\n * @returns True if URL points to localhost\n */\nexport const isLocalhostUrl = (url?: string): boolean => {\n if (!url) return false\n\n try {\n const parsedUrl = new URL(addProtocol(url))\n const hostname = parsedUrl.hostname\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname.endsWith(\".localhost\")\n } catch {\n return url.includes(\"localhost\") || url.includes(\"127.0.0.1\")\n }\n}\n\n/**\n * Joins URL paths safely\n * @param base - Base URL\n * @param paths - Path segments to join\n * @returns Combined URL\n */\nexport const joinUrlPaths = (base: string, ...paths: string[]): string => {\n if (!base) return \"\"\n\n let result = normalizeUrl(base)\n\n for (const path of paths) {\n if (!path) continue\n\n const cleanPath = path.replace(/^\\/+|\\/+$/g, \"\") // Remove leading/trailing slashes\n if (cleanPath) {\n result += `/${cleanPath}`\n }\n }\n\n return result\n}\n\n/**\n * Extracts query parameters from a URL\n * @param url - The URL string\n * @returns Object containing query parameters\n */\nexport const getQueryParams = (url: string): Record<string, string> => {\n try {\n const parsedUrl = new URL(url)\n const params: Record<string, string> = {}\n\n parsedUrl.searchParams.forEach((value, key) => {\n params[key] = value\n })\n\n return params\n } catch {\n return {}\n }\n}\n\n/**\n * Adds or updates query parameters in a URL\n * @param url - The base URL\n * @param params - Parameters to add/update\n * @returns URL with updated parameters\n */\nexport const setQueryParams = (\n url: string,\n params: Record<string, string | number | boolean>,\n): string => {\n try {\n const parsedUrl = new URL(url)\n\n Object.entries(params).forEach(([key, value]) => {\n parsedUrl.searchParams.set(key, String(value))\n })\n\n let result = parsedUrl.toString()\n\n // Special case: remove trailing slash before query parameters for cleaner URLs\n result = result.replace(/\\/\\?/, \"?\")\n\n return result\n } catch {\n return url\n }\n}\n\n/**\n * Removes query parameters from a URL\n * @param url - The URL string\n * @returns URL without query parameters\n */\nexport const removeQueryParams = (url?: string): string => {\n if (!url) return \"\"\n\n try {\n const parsedUrl = new URL(url)\n parsedUrl.search = \"\"\n return normalizeUrl(parsedUrl.toString())\n } catch {\n // For invalid URLs, try simple string manipulation\n const questionIndex = url.indexOf(\"?\")\n return questionIndex !== -1 ? url.substring(0, questionIndex) : url\n }\n}\n\n/** Options for checkUrlAvailability */\nexport type CheckUrlAvailabilityOptions = {\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number\n /** HTTP status codes below this value are considered successful (default: 400) */\n successStatusBelow?: number\n /** User-Agent header to send with requests */\n userAgent?: string\n}\n\n/**\n * Checks if a URL is accessible by making an HTTP request.\n * First tries a HEAD request, then falls back to GET if HEAD fails.\n * @param url - The URL to check\n * @param options - Configuration options for the request\n * @returns True if the URL is accessible (status < 400), false otherwise\n */\nexport const checkUrlAvailability = async (\n url: string,\n options: CheckUrlAvailabilityOptions = {},\n): Promise<boolean> => {\n if (!url) {\n return false\n }\n\n const {\n timeout = 5000,\n successStatusBelow = 400,\n userAgent = \"Mozilla/5.0 (compatible; URLChecker/1.0)\",\n } = options\n\n const normalizedUrl = normalizeUrl(url)\n\n const makeRequest = async (method: \"HEAD\" | \"GET\"): Promise<Response | null> => {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(normalizedUrl, {\n method,\n signal: controller.signal,\n redirect: \"follow\",\n headers: { \"User-Agent\": userAgent },\n })\n clearTimeout(timeoutId)\n return response\n } catch {\n clearTimeout(timeoutId)\n return null\n }\n }\n\n const headResponse = await makeRequest(\"HEAD\")\n\n if (headResponse && headResponse.status < successStatusBelow) {\n return true\n }\n\n const getResponse = await makeRequest(\"GET\")\n return getResponse !== null && getResponse.status < successStatusBelow\n}\n","/**\n * Utility functions for working with numbers.\n */\n\n/**\n * Keep a number within a specified range.\n * @param value - The number to keep within the range.\n * @param min - The minimum value of the range.\n * @param max - The maximum value of the range.\n * @returns The number within the specified range.\n */\nexport const keepNumberInRange = (value: number, min?: number, max?: number) => {\n if (min !== undefined && max !== undefined) {\n return Math.min(Math.max(value, min), max)\n }\n if (min !== undefined) {\n return Math.max(value, min)\n }\n if (max !== undefined) {\n return Math.min(value, max)\n }\n\n return value\n}\n\n/**\n * Parse a string into a numeric value.\n * @param value - The value to parse into a numeric value.\n * @returns The parsed numeric value, or `undefined` if the value cannot be parsed.\n */\nexport const parseNumericValue = (value?: string | number | null) => {\n if (value === undefined || value === null) return undefined\n const parsed = Number.parseFloat(value.toString())\n\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\n/**\n * Rounds a number to a specified number of decimal places, with an adjustment for floating point precision.\n * @param value - The number to round.\n * @param decimals - The number of decimal places to round to. Defaults to 2.\n * @returns The rounded number.\n */\nexport const preciseRound = (value: number, decimals = 2) => {\n const factor = 10 ** decimals\n\n return Math.round((value + Number.EPSILON) * factor) / factor\n}\n","/**\n * Utility functions for working with objects.\n */\n\n/**\n * Checks if an object is empty (has no own properties).\n * @param obj - The object to check.\n * @returns `true` if the object is empty, `false` otherwise.\n */\nexport const isEmptyObject = (obj: Record<string, unknown> = {}) => {\n return obj.constructor === Object && !Object.entries(obj).length\n}\n\n/**\n * Checks if a key is present in an object.\n * @param key - The key to check.\n * @param obj - The object to check.\n * @returns `true` if the key is present in the object, `false` otherwise.\n */\nexport const isKeyInObject = <T extends object>(key: PropertyKey, obj: T): key is keyof T => {\n return key in obj\n}\n\n/**\n * Sorts two objects based on their keys' positions in an array of keys.\n * @param keys - An array of keys to sort the objects by.\n * @param a - The first object to compare.\n * @param b - The second object to compare.\n * @returns A number indicating the sort order of the two objects.\n */\nexport const sortObjectKeys = (keys: string[]) => {\n return (a: Record<string, unknown>, b: Record<string, unknown>) => {\n const aIndex = keys.indexOf(Object.keys(a)[0] ?? \"\")\n const bIndex = keys.indexOf(Object.keys(b)[0] ?? \"\")\n\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n\n return aIndex - bIndex\n }\n}\n\n/**\n * Sorts the keys of an object in alphabetical order and returns a new object with the sorted keys.\n * @param obj - The input object to sort.\n * @param comparator - An optional comparator function to use when sorting the keys.\n * @returns - A new object with the sorted keys.\n */\nexport const sortObject = <T extends Record<K, unknown>, K extends keyof T>(\n obj: T,\n comparator?: (a: unknown, b: unknown) => number,\n) => {\n return Object.keys(obj)\n .sort(comparator)\n .reduce((result, key) => {\n return { ...result, [key as K]: obj[key as K] }\n }, {} as T)\n}\n\n/**\n * Creates a new object with only the specified properties from the source object.\n * Provides full type safety and intellisense for the picked properties.\n *\n * @param obj - The source object to pick properties from\n * @param keys - Array of property keys to pick from the source object\n * @returns A new object containing only the specified properties\n *\n * @example\n * const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' }\n * const publicUser = pick(user, ['id', 'name', 'email'])\n * // Result: { id: 1, name: 'John', email: 'john@example.com' }\n */\nexport const pickFromObject = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key]\n }\n }\n return result\n}\n","/**\n * Utility functions for pagination and page management.\n */\n\nimport { getQueryParams } from \"../http/http\"\n\n/**\n * Represents the parameters for a paginated query.\n * @template T - The type of the query parameters.\n */\nexport type GetPageParams<T> = T & {\n take: number\n skip: number\n}\n\n/**\n * Returns the current page number from a string.\n * @param page - The page number as a string.\n * @returns The current page number as a number.\n */\nexport const getCurrentPage = (page?: string | null) => {\n return Math.max(page && !Number.isNaN(Number(page)) ? Number.parseInt(page || \"1\", 10) : 1, 1)\n}\n\n/**\n * Returns an object containing the parameters for a paginated query.\n * @template T - The type of the query parameters.\n * @param url - The URL to get the page parameters from.\n * @param take - The number of items to take per page.\n * @returns An object containing the parameters for a paginated query.\n */\nexport const getPageParams = <T extends object>(url: string, take: number) => {\n const { page, ...params } = getQueryParams(url)\n\n const currentPage = getCurrentPage(page)\n const skip = (currentPage - 1) * take\n\n return { take, skip, ...params } as GetPageParams<T>\n}\n\n/**\n * Returns a link to a specific page of a paginated query.\n * @param searchParams - The search parameters object.\n * @param pathname - The pathname of the URL.\n * @param page - The page number to link to.\n * @returns A link to the specified page of the paginated query.\n */\nexport const getPageLink = (searchParams: URLSearchParams, pathname: string, page: number) => {\n searchParams.set(\"page\", page.toString())\n\n return `${pathname}?${searchParams.toString()}`\n}\n","/**\n * Utility functions for parsing and stringifying JSON.\n */\n\n/**\n * Parses a string as JSON and returns the result. If the string cannot be parsed as JSON, returns the original string.\n * @param value - The string to parse as JSON.\n * @returns The parsed JSON object or the original string.\n * @template T - The type of the parsed JSON object.\n */\nexport const maybeParseJson = <T>(value: string) => {\n try {\n return JSON.parse(value) as T\n } catch {\n return value\n }\n}\n\n/**\n * Returns a JSON string representation of the given object, or the original string if it's not an object.\n * @param value - The value to stringify.\n * @returns The JSON string representation of the object, or the original string if it's not an object.\n */\nexport const maybeStringifyJson = (value?: object | string) => {\n if (typeof value === \"object\") {\n return JSON.stringify(value)\n }\n\n return value\n}\n","/**\n * Utility functions for generating random values.\n */\n\n/**\n * Returns a random hexadecimal color code.\n * @returns A string representing a random hexadecimal color code.\n */\nexport const getRandomColor = (): string => {\n return Math.floor(Math.random() * 16777215)\n .toString(16)\n .padStart(6, \"0\")\n}\n\n/**\n * Returns a random string of characters.\n * @param length - The desired length of the random string\n * @returns A string representing a random string of characters.\n */\nexport const getRandomString = (length = 16): string => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(\"\")\n}\n\n/**\n * Generates a random number between the specified minimum and maximum values (inclusive).\n *\n * @param min The minimum value for the random number.\n * @param max The maximum value for the random number.\n * @returns A random number between the specified minimum and maximum values.\n */\nexport const getRandomNumber = (min: number, max: number) => {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * Generate a random string of digits\n * @param length Length of the digits string\n * @returns Random digits string\n */\nexport const getRandomDigits = (length: number) => {\n return Array.from({ length }, () => Math.floor(Math.random() * 10)).join(\"\")\n}\n\n/**\n * Returns a random element from an array.\n *\n * @param array - The array to get a random element from.\n * @returns A random element from the array.\n */\nexport const getRandomElement = <T>(array: T[]): T => {\n const index = Math.floor(Math.random() * array.length)\n return array[index]!\n}\n\n/**\n * Returns a random property value from an object.\n * @param obj - The object to get a random property value from.\n * @returns A random property value from the object.\n */\nexport const getRandomProperty = <T>(obj: Record<string, T>): T => {\n const keys = Object.keys(obj)\n const randomKey = keys[Math.floor(keys.length * Math.random())]!\n\n return obj[randomKey]!\n}\n","/**\n * Utility functions for working with strings.\n */\n\n/**\n * Uppercases the first character in the `string`.\n * @param string - The string to uppercase the first character of.\n * @returns The string with the first character in uppercase.\n */\nexport const ucFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toUpperCase() + string.slice(1)\n}\n\n/**\n * Lowercases the first character in the `string`.\n * @param string - The string to lowercase the first character of.\n * @returns The string with the first character in lowercase.\n */\nexport const lcFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toLowerCase() + string.slice(1)\n}\n","/**\n * Utility functions related to time.\n */\ntype DateStyle = Intl.DateTimeFormatOptions[\"dateStyle\"]\ntype TimeStyle = Intl.DateTimeFormatOptions[\"timeStyle\"]\ntype Timestamp = string | number | Date\n\nconst getDateFormatter = (locale: string, dateStyle: DateStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle })\n}\n\nconst getTimeFormatter = (locale: string, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { timeStyle })\n}\n\nconst getDateTimeFormatter = (locale: string, dateStyle: DateStyle, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle, timeStyle })\n}\n\n/**\n * Formats a date according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date string.\n */\nexport const formatDate = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted time string.\n */\nexport const formatTime = (\n timestamp: Timestamp,\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getTimeFormatter(locale, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date and time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date and time string.\n */\nexport const formatDateTime = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getDateTimeFormatter(locale, dateStyle, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date or time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param type - The type of formatting to use. Can be 'date', 'time', or 'datetime'.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date or time string.\n */\nexport const formatDateOrTime = (\n timestamp: Timestamp,\n type: \"date\" | \"time\" | \"datetime\",\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n switch (type) {\n case \"date\":\n return formatDate(timestamp, dateStyle, locale)\n case \"time\":\n return formatTime(timestamp, timeStyle, locale)\n default:\n return formatDateTime(timestamp, dateStyle, timeStyle, locale)\n }\n}\n\n/**\n * Formats a date range.\n * @param start - The start date.\n * @param end - The end date.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date range string.\n */\nexport const formatDateRange = (\n start: Timestamp,\n end: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).formatRange(new Date(start), new Date(end))\n}\n\n/**\n * Calculates the estimated read time for a given content.\n * @param content - The content to calculate the read time for.\n * @param wpm - The average words per minute to use for the calculation. Defaults to 265.\n * @returns The estimated read time in minutes.\n */\nexport const getReadTime = (content: string | null, wpm = 265): number => {\n if (!content) {\n return 0\n }\n\n return Math.ceil(content.trim().split(/\\s+/).length / wpm)\n}\n"],"names":["range","start","end","length","_","idx","sleep","delay","resolve","getShortcutLabel","key","metaKey","stripHtml","string","convertNewlines","replacement","getExcerpt","content","plainText","text","slugify","input","decamelize","slugifyString","isCuid","id","isTruthy","value","getInitials","limit","val","initials","name","toBase64","file","reject","reader","error","splitArrayIntoChunks","items","chunkSize","chunks","i","setInputValue","triggerChange","joinAsSentence","maxItems","lastItem","tryCatch","promise","nullsToUndefined","obj","isMimeTypeMatch","mimeType","patterns","pattern","type","subtype","pType","pSubtype","processBatch","processor","options","batchSize","concurrency","results","batches","batch","batchResults","processWithConcurrency","processBatchWithErrorHandling","onError","item","err","executing","result","p","isLightColor","hexa","hex","g","b","getElementPosition","el","style","scrollMt","top","isErrorWithMessage","toErrorWithMessage","maybeError","getErrorMessage","subscribe","eventName","listener","unsubscribe","publish","data","event","publishEscape","formatNumber","number","notation","locale","formatCurrency","amount","currency","formatIntervalAmount","interval","formatToDecimals","precision","formatBytes","bytes","factor","formatMimeType","URL_REGEX","isValidUrl","url","addProtocol","secure","isLocalhostUrl","removeProtocol","normalizeUrl","normalized","parsedUrl","getBaseUrl","getDomain","hostname","isExternalUrl","joinUrlPaths","base","paths","path","cleanPath","getQueryParams","params","setQueryParams","removeQueryParams","questionIndex","checkUrlAvailability","timeout","successStatusBelow","userAgent","normalizedUrl","makeRequest","method","controller","timeoutId","response","headResponse","getResponse","keepNumberInRange","min","max","parseNumericValue","parsed","preciseRound","decimals","isEmptyObject","isKeyInObject","sortObjectKeys","keys","a","aIndex","bIndex","sortObject","comparator","pickFromObject","getCurrentPage","page","getPageParams","take","skip","getPageLink","searchParams","pathname","maybeParseJson","maybeStringifyJson","getRandomColor","getRandomString","chars","getRandomNumber","getRandomDigits","getRandomElement","array","index","getRandomProperty","randomKey","ucFirst","lcFirst","getDateFormatter","dateStyle","getTimeFormatter","timeStyle","getDateTimeFormatter","formatDate","timestamp","formatTime","formatDateTime","formatDateOrTime","formatDateRange","getReadTime","wpm"],"mappings":";AAaO,MAAMA,IAAQ,CAACC,GAAeC,MAAgB;AACnD,QAAMC,IAASD,IAAMD,IAAQ;AAE7B,SAAO,MAAM,KAAK,EAAE,QAAAE,EAAA,GAAU,CAACC,GAAGC,MAAQA,IAAMJ,CAAK;AACvD,GAMaK,IAAQ,OAAOC,MACnB,IAAI,QAAQ,CAAAC,MAAW,WAAWA,GAASD,CAAK,CAAC,GAO7CE,IAAmB,CAAC,EAAE,KAAAC,GAAK,SAAAC,QACxB,GAAGA,IAAU,MAAM,EAAE,GAAGD,EAAI,aAAa,IAS5CE,IAAY,CAACC,MACjBA,EAAO,QAAQ,cAAc,EAAE,GAS3BC,IAAkB,CAACD,GAAgBE,IAAc,QACrDF,EAAO,QAAQ,QAAQE,CAAW,GAS9BC,IAAa,CAACC,GAAoCd,IAAS,QAAQ;AAC9E,MAAI,CAACc;AACH,WAAO;AAGT,QAAMC,IAAYJ,EAAgBF,EAAUK,CAAO,CAAC,GAC9CE,IAAOD,EAAU,MAAM,GAAGf,CAAM,EAAE,KAAA;AAExC,SAAIgB,EAAK,SAASD,EAAU,SACnB,GAAGC,CAAI,QAGTA;AACT,GASaC,IAAU,CAACC,GAAeC,IAAa,OAC3CC,EAAcF,GAAO;AAAA,EAC1B,YAAAC;AAAA,EACA,oBAAoB;AAAA,IAClB,CAAC,KAAK,OAAO;AAAA,IACb,CAAC,KAAK,MAAM;AAAA,EAAA;AACd,CACD,GAQUE,IAAS,CAACC,MACdA,EAAG,WAAW,MAAMA,EAAG,CAAC,MAAM,KAQ1BC,IAAW,CAAIC,MACnB,CAAC,CAACA,GASEC,IAAc,CAACD,GAAuBE,IAAQ,MAAM;AAC/D,QAAMC,KAAOH,KAAS,IAAI,KAAA;AAG1B,MAAIG,EAAI,WAAW,KAAKA,EAAI,WAAW,KAAKA,EAAI,WAAW;AACzD,WAAOA,EAAI,YAAA;AAIb,QAAMC,IADSD,EAAI,MAAM,GAAG,EAAE,OAAOJ,CAAQ,EACrB,IAAI,CAAAM,MAAQA,EAAK,OAAO,CAAC,EAAE,YAAA,CAAa,EAAE,KAAK,EAAE;AAEzE,SAAIH,IAAQ,IACHE,EAAS,MAAM,GAAGF,CAAK,IAGzBE;AACT,GAOaE,IAAW,CAACC,MAChB,IAAI,QAAQ,CAAC1B,GAAS2B,MAAW;AACtC,QAAMC,IAAS,IAAI,WAAA;AACnB,EAAAA,EAAO,cAAcF,CAAI,GACzBE,EAAO,SAAS,MAAM5B,EAAQ4B,EAAO,MAAgB,GACrDA,EAAO,UAAU,CAAAC,MAASF,EAAOE,CAAK;AACxC,CAAC,GASUC,IAAuB,CAAIC,GAAYC,MAAsB;AACxE,QAAMC,IAAgB,CAAA;AAEtB,WAASC,IAAI,GAAGA,IAAIH,EAAM,QAAQG,KAAKF;AACrC,IAAAC,EAAO,KAAKF,EAAM,MAAMG,GAAGA,IAAIF,CAAS,CAAC;AAG3C,SAAOC;AACT,GAQaE,IAAgB,CAC3BtB,GACAM,GACAiB,IAAgB,OACb;AAMH,EAL+B,OAAO;AAAA,IACpC,iBAAiB;AAAA,IACjB;AAAA,EAAA,GACC,KAEqB,KAAKvB,GAAOM,CAAK,GAGzCiB,KAAiBvB,GAAO,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,GAAA,CAAM,CAAC;AAC7E,GASawB,IAAiB,CAACN,GAAiBO,IAAW,GAAGC,IAAW,UAChER,EACJ,MAAM,GAAGO,CAAQ,EACjB,KAAK,IAAI,EACT,QAAQ,cAAc,IAAIC,CAAQ,KAAK,GA6B/BC,IAAW,OAAqBC,MAA+C;AAC1F,MAAI;AAEF,WAAO,EAAE,MADI,MAAMA,GACJ,OAAO,KAAA;AAAA,EACxB,SAASZ,GAAO;AACd,WAAO,EAAE,MAAM,MAAM,OAAAA,EAAA;AAAA,EACvB;AACF,GAOaa,IAAmB,CAAIC,MAAwC;AAC1E,MAAIA,MAAQ,MAKZ;AAAA,QAAIA,GAAK,YAAY,SAAS;AAC5B,iBAAWzC,KAAOyC;AAChB,QAAAA,EAAIzC,CAAG,IAAIwC,EAAiBC,EAAIzC,CAAG,CAAC;AAIxC,WAAOyC;AAAA;AACT,GAiBaC,IAAkB,CAACC,GAAkBC,MACzCA,EAAS,KAAK,CAAAC,MAAW;AAE9B,QAAM,CAACC,GAAMC,CAAO,IAAIJ,EAAS,MAAM,GAAG,GACpC,CAACK,GAAOC,CAAQ,IAAIJ,EAAQ,MAAM,GAAG;AAG3C,SAAIC,MAASE,IAAc,KAGvBC,MAAa,MAAY,KAGtBF,MAAYE;AACrB,CAAC,GClRUC,IAAe,OAC1BrB,GACAsB,GACAC,MACiB;AACjB,QAAM,EAAE,WAAAC,GAAW,aAAAC,IAAcD,GAAW,OAAAxD,IAAQ,MAAMuD;AAE1D,MAAIvB,EAAM,WAAW,EAAG,QAAO,CAAA;AAE/B,QAAM0B,IAAe,CAAA,GACfC,IAAU5B,EAAqBC,GAAOwB,CAAS;AAErD,aAAW,CAAC,GAAGI,CAAK,KAAKD,EAAQ,WAAW;AAC1C,YAAQ,IAAI,oBAAoB,IAAI,CAAC,IAAIA,EAAQ,MAAM,KAAKC,EAAM,MAAM,SAAS;AAGjF,UAAMC,IAAe,MAAMC,EAAuBF,GAAON,GAAWG,CAAW;AAC/E,IAAAC,EAAQ,KAAK,GAAGG,CAAY,GAGxB7D,IAAQ,KAAK,IAAI2D,EAAQ,SAAS,MACpC,QAAQ,IAAI,WAAW3D,CAAK,yBAAyB,GACrD,MAAMD,EAAMC,CAAK;AAAA,EAErB;AAEA,SAAO0D;AACT,GAKaK,IAAgC,OAC3C/B,GACAsB,GACAC,MAG8B;AAC9B,QAAM,EAAE,SAAAS,MAAYT;AAYpB,SAAOF,EAAarB,GAVK,OAAOiC,MAAgC;AAC9D,QAAI;AACF,aAAO,MAAMX,EAAUW,CAAI;AAAA,IAC7B,SAASnC,GAAO;AACd,YAAMoC,IAAMpC,aAAiB,QAAQA,IAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC;AACpE,aAAAkC,IAAUE,GAAKD,CAAI,GACZC;AAAA,IACT;AAAA,EACF,GAE6CX,CAAO;AACtD,GAKMO,IAAyB,OAC7B9B,GACAsB,GACAG,MACiB;AACjB,MAAIA,KAAezB,EAAM;AAEvB,WAAO,QAAQ,IAAIA,EAAM,IAAIsB,CAAS,CAAC;AAGzC,QAAMI,IAAe,CAAA,GACfS,IAA6B,CAAA;AAEnC,aAAWF,KAAQjC,GAAO;AACxB,UAAMU,IAAUY,EAAUW,CAAI,EAAE,KAAK,CAAAG,MAAU;AAC7C,MAAAV,EAAQ,KAAKU,CAAM;AAAA,IACrB,CAAC;AAED,IAAAD,EAAU,KAAKzB,CAAO,GAGlByB,EAAU,UAAUV,MACtB,MAAM,QAAQ,KAAKU,CAAS,GAE5BA,EAAU,OAAO,GAAGA,EAAU,SAASA,EAAU,OAAO,CAAAE,MAAKA,MAAM3B,CAAO,EAAE,MAAM;AAAA,EAEtF;AAGA,eAAM,QAAQ,IAAIyB,CAAS,GAEpBT;AACT,GC7FaY,IAAe,CAACC,MAA0B;AAErD,QAAMC,IAAMD,EAAK,QAAQ,KAAK,EAAE,EAAE,UAAU,GAAG,CAAC,GAG1C,IAAI,OAAO,SAASC,EAAI,UAAU,GAAG,CAAC,GAAG,EAAE,GAC3CC,IAAI,OAAO,SAASD,EAAI,UAAU,GAAG,CAAC,GAAG,EAAE,GAC3CE,IAAI,OAAO,SAASF,EAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAKjD,SAFmB,IAAI,QAAQC,IAAI,QAAQC,IAAI,QAE3B;AACtB,GCfaC,IAAqB,CAACzD,MAAgB;AACjD,QAAM0D,IAAK,SAAS,eAAe1D,KAAM,EAAE;AAC3C,MAAI,CAAC0D,EAAI;AAET,QAAMC,IAAQ,OAAO,iBAAiBD,CAAE,GAClCE,IAAW,OAAO,WAAWD,EAAM,eAAe,GAClDE,IAAM,KAAK,MAAM,OAAO,UAAUH,EAAG,sBAAA,EAAwB,MAAME,CAAQ;AAEjF,SAAO,EAAE,IAAA5D,GAAI,KAAA6D,EAAA;AACf,GCEaC,IAAqB,CAAClD,MAE/B,OAAOA,KAAU,YACjBA,MAAU,QACV,aAAaA,KACb,OAAQA,EAAkC,WAAY,UAW7CmD,IAAqB,CAACC,MAA0C;AAC3E,MAAIF,EAAmBE,CAAU,EAAG,QAAOA;AAE3C,MAAI;AACF,WAAO,IAAI,MAAM,KAAK,UAAUA,CAAU,CAAC;AAAA,EAC7C,QAAQ;AAGN,WAAO,IAAI,MAAM,OAAOA,CAAU,CAAC;AAAA,EACrC;AACF,GASaC,IAAkB,CAACrD,MACvBmD,EAAmBnD,CAAK,EAAE,SC3CtBsD,KAAY,CAACC,GAAmBC,MAAiD;AAC5F,WAAS,iBAAiBD,GAAWC,CAAQ;AAC/C,GAOaC,KAAc,CAACF,GAAmBC,MAAiD;AAC9F,WAAS,oBAAoBD,GAAWC,CAAQ;AAClD,GAOaE,KAAU,CAACH,GAAmBI,MAAkB;AAC3D,QAAMC,IAAQ,IAAI,YAAYL,GAAW,EAAE,QAAQI,GAAM;AACzD,WAAS,cAAcC,CAAK;AAC9B,GAKaC,KAAgB,MAAM;AACjC,QAAMD,IAAQ,IAAI,cAAc,WAAW,EAAE,KAAK,UAAU;AAC5D,WAAS,cAAcA,CAAK;AAC9B,GCxBaE,KAAe,CAACC,GAAgBC,IAAqB,WAAWC,IAAS,YAClE,IAAI,KAAK,aAAaA,GAAQ,EAAE,UAAAD,GAAU,EAE3C,OAAOD,CAAM,GASnBG,KAAiB,CAACC,GAAgBC,IAAqB,UAChD,IAAI,KAAK,aAAa,SAAS;AAAA,EAC/C,OAAO;AAAA,EACP,UAAAA;AAAA,CACD,EAEgB,OAAOD,CAAM,EAAE,QAAQ,gBAAgB,EAAE,GAS/CE,KAAuB,CAACF,GAAgBG,IAA6B,YACzEC,EAAiBJ,KAAUG,MAAa,SAAS,KAAK,IAAI,CAAC,GASvDC,IAAmB,CAACR,GAAgBS,IAAY,MACpDT,EAAO,QAAQS,IAAY,IAAI,IAAIA,CAAS,EAAE,QAAQ,SAAS,EAAE,GAS7DC,KAAc,CAACC,GAAeF,IAAY,MAAc;AAGnE,MAAIE,IAAQ,MAAG;AACb,UAAMC,IAAS,MAAMH;AAErB,WAAO,GADM,KAAK,MAAOE,IAAQ,OAAKC,CAAM,IAAIA,CAClC;AAAA,EAChB;AAEA,QAAMtE,IAAI,KAAK,MAAM,KAAK,IAAIqE,CAAK,IAAI,KAAK,IAAI,IAAC,CAAC;AAIlD,SAAO,GAHMH,EAAiBG,IAAQ,QAAKrE,GAAGmE,CAAS,CAGzC,IAFA,CAAC,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI,EAE1CnE,CAAC,CAAC;AAC5B,GAOauE,KAAiB,CAAC5D,MAAyC;AACtE,QAAM,CAAA,EAAGI,CAAO,IAAIJ,EAAS,MAAM,GAAG;AACtC,MAAIG;AAEJ,UAAQC,GAAA;AAAA,IACN,KAAK;AACH;AAAA,IACF;AACE,MAAAD,IAAOC;AAAA,EAAA;AAGX,SAAOD,GAAM,YAAA;AACf,GCtFM0D,IACJ,0JAOWC,IAAa,CAACC,MAA0B;AAInD,MAHI,CAACA,KAAO,OAAOA,KAAQ,YAGvB,CAACF,EAAU,KAAKE,CAAG,EAAG,QAAO;AAGjC,MAAI;AACF,eAAI,IAAIA,CAAG,GACJ;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF,GAQaC,IAAc,CAACD,GAAcE,MACnCF,IAGDA,EAAI,WAAW,SAAS,KAAKA,EAAI,WAAW,UAAU,IAAUA,IAM7D,GAFLE,MAAW,SAAaA,IAAS,UAAU,SAAUC,EAAeH,CAAG,IAAI,SAAS,OAEpE,MAAMA,CAAG,KATV,IAiBNI,KAAiB,CAACJ,MACtBA,GAAK,QAAQ,gBAAgB,EAAE,KAAK,IAQhCK,IAAe,CAACL,MAAyB;AACpD,MAAI,CAACA,EAAK,QAAO;AAEjB,MAAIM,IAAaN,EAAI,KAAA;AAGrB,MAAIM,EAAW,SAAS,GAAG,KAAKA,EAAW,SAAS,GAAG;AACrD,QAAI;AACF,YAAMC,IAAY,IAAI,IAAID,CAAU;AAEpC,aAAIC,EAAU,SAAS,SAAS,KAAKA,EAAU,SAAS,SAAS,GAAG,MAClEA,EAAU,WAAWA,EAAU,SAAS,MAAM,GAAG,EAAE,IAE9CA,EAAU,SAAA;AAAA,IACnB,QAAQ;AAEN,aAAOD,EAAW,SAAS,KAAKA,EAAW,SAAS,GAAG,IACnDA,EAAW,MAAM,GAAG,EAAE,IACtBA;AAAA,IACN;AAIF,SAAIA,EAAW,SAAS,KAAKA,EAAW,SAAS,GAAG,MAClDA,IAAaA,EAAW,MAAM,GAAG,EAAE,IAG9BA;AACT,GAOaE,KAAa,CAACR,MAAwB;AACjD,MAAI;AACF,UAAMO,IAAY,IAAI,IAAIP,CAAG;AAC7B,WAAO,GAAGO,EAAU,QAAQ,KAAKA,EAAU,IAAI;AAAA,EACjD,QAAQ;AACN,WAAOP;AAAA,EACT;AACF,GAOaS,KAAY,CAACT,MAAwB;AAChD,MAAI;AACF,QAAI,CAACD,EAAWC,CAAG,EAAG,QAAOA;AAE7B,UAAMU,IAAW,IAAI,IAAIV,CAAG,EAAE;AAC9B,WAAOU,EAAS,WAAW,MAAM,IAAIA,EAAS,MAAM,CAAC,IAAIA;AAAA,EAC3D,QAAQ;AACN,WAAOV;AAAA,EACT;AACF,GAOaW,KAAgB,CAACX,MACvBA,IACE,eAAe,KAAKA,CAAG,IADb,IASNG,IAAiB,CAACH,MAA0B;AACvD,MAAI,CAACA,EAAK,QAAO;AAEjB,MAAI;AAEF,UAAMU,IADY,IAAI,IAAIT,EAAYD,CAAG,CAAC,EACf;AAC3B,WAAOU,MAAa,eAAeA,MAAa,eAAeA,EAAS,SAAS,YAAY;AAAA,EAC/F,QAAQ;AACN,WAAOV,EAAI,SAAS,WAAW,KAAKA,EAAI,SAAS,WAAW;AAAA,EAC9D;AACF,GAQaY,KAAe,CAACC,MAAiBC,MAA4B;AACxE,MAAI,CAACD,EAAM,QAAO;AAElB,MAAItD,IAAS8C,EAAaQ,CAAI;AAE9B,aAAWE,KAAQD,GAAO;AACxB,QAAI,CAACC,EAAM;AAEX,UAAMC,IAAYD,EAAK,QAAQ,cAAc,EAAE;AAC/C,IAAIC,MACFzD,KAAU,IAAIyD,CAAS;AAAA,EAE3B;AAEA,SAAOzD;AACT,GAOa0D,IAAiB,CAACjB,MAAwC;AACrE,MAAI;AACF,UAAMO,IAAY,IAAI,IAAIP,CAAG,GACvBkB,IAAiC,CAAA;AAEvC,WAAAX,EAAU,aAAa,QAAQ,CAAChG,GAAOjB,MAAQ;AAC7C,MAAA4H,EAAO5H,CAAG,IAAIiB;AAAA,IAChB,CAAC,GAEM2G;AAAA,EACT,QAAQ;AACN,WAAO,CAAA;AAAA,EACT;AACF,GAQaC,KAAiB,CAC5BnB,GACAkB,MACW;AACX,MAAI;AACF,UAAMX,IAAY,IAAI,IAAIP,CAAG;AAE7B,WAAO,QAAQkB,CAAM,EAAE,QAAQ,CAAC,CAAC5H,GAAKiB,CAAK,MAAM;AAC/C,MAAAgG,EAAU,aAAa,IAAIjH,GAAK,OAAOiB,CAAK,CAAC;AAAA,IAC/C,CAAC;AAED,QAAIgD,IAASgD,EAAU,SAAA;AAGvB,WAAAhD,IAASA,EAAO,QAAQ,QAAQ,GAAG,GAE5BA;AAAA,EACT,QAAQ;AACN,WAAOyC;AAAA,EACT;AACF,GAOaoB,KAAoB,CAACpB,MAAyB;AACzD,MAAI,CAACA,EAAK,QAAO;AAEjB,MAAI;AACF,UAAMO,IAAY,IAAI,IAAIP,CAAG;AAC7B,WAAAO,EAAU,SAAS,IACZF,EAAaE,EAAU,UAAU;AAAA,EAC1C,QAAQ;AAEN,UAAMc,IAAgBrB,EAAI,QAAQ,GAAG;AACrC,WAAOqB,MAAkB,KAAKrB,EAAI,UAAU,GAAGqB,CAAa,IAAIrB;AAAA,EAClE;AACF,GAmBasB,KAAuB,OAClCtB,GACAtD,IAAuC,OAClB;AACrB,MAAI,CAACsD;AACH,WAAO;AAGT,QAAM;AAAA,IACJ,SAAAuB,IAAU;AAAA,IACV,oBAAAC,IAAqB;AAAA,IACrB,WAAAC,IAAY;AAAA,EAAA,IACV/E,GAEEgF,IAAgBrB,EAAaL,CAAG,GAEhC2B,IAAc,OAAOC,MAAqD;AAC9E,UAAMC,IAAa,IAAI,gBAAA,GACjBC,IAAY,WAAW,MAAMD,EAAW,MAAA,GAASN,CAAO;AAE9D,QAAI;AACF,YAAMQ,IAAW,MAAM,MAAML,GAAe;AAAA,QAC1C,QAAAE;AAAA,QACA,QAAQC,EAAW;AAAA,QACnB,UAAU;AAAA,QACV,SAAS,EAAE,cAAcJ,EAAA;AAAA,MAAU,CACpC;AACD,0BAAaK,CAAS,GACfC;AAAA,IACT,QAAQ;AACN,0BAAaD,CAAS,GACf;AAAA,IACT;AAAA,EACF,GAEME,IAAe,MAAML,EAAY,MAAM;AAE7C,MAAIK,KAAgBA,EAAa,SAASR;AACxC,WAAO;AAGT,QAAMS,IAAc,MAAMN,EAAY,KAAK;AAC3C,SAAOM,MAAgB,QAAQA,EAAY,SAAST;AACtD,GClSaU,KAAoB,CAAC3H,GAAe4H,GAAcC,MACzDD,MAAQ,UAAaC,MAAQ,SACxB,KAAK,IAAI,KAAK,IAAI7H,GAAO4H,CAAG,GAAGC,CAAG,IAEvCD,MAAQ,SACH,KAAK,IAAI5H,GAAO4H,CAAG,IAExBC,MAAQ,SACH,KAAK,IAAI7H,GAAO6H,CAAG,IAGrB7H,GAQI8H,KAAoB,CAAC9H,MAAmC;AACnE,MAA2BA,KAAU,KAAM;AAC3C,QAAM+H,IAAS,OAAO,WAAW/H,EAAM,UAAU;AAEjD,SAAO,OAAO,MAAM+H,CAAM,IAAI,SAAYA;AAC5C,GAQaC,KAAe,CAAChI,GAAeiI,IAAW,MAAM;AAC3D,QAAM5C,IAAS,MAAM4C;AAErB,SAAO,KAAK,OAAOjI,IAAQ,OAAO,WAAWqF,CAAM,IAAIA;AACzD,GCtCa6C,KAAgB,CAAC1G,IAA+B,OACpDA,EAAI,gBAAgB,UAAU,CAAC,OAAO,QAAQA,CAAG,EAAE,QAS/C2G,KAAgB,CAAmBpJ,GAAkByC,MACzDzC,KAAOyC,GAUH4G,KAAiB,CAACC,MACtB,CAACC,GAA4BhF,MAA+B;AACjE,QAAMiF,IAASF,EAAK,QAAQ,OAAO,KAAKC,CAAC,EAAE,CAAC,KAAK,EAAE,GAC7CE,IAASH,EAAK,QAAQ,OAAO,KAAK/E,CAAC,EAAE,CAAC,KAAK,EAAE;AAEnD,SAAIiF,MAAW,MAAMC,MAAW,KAAW,IACvCD,MAAW,KAAW,IACtBC,MAAW,KAAW,KAEnBD,IAASC;AAClB,GASWC,KAAa,CACxBjH,GACAkH,MAEO,OAAO,KAAKlH,CAAG,EACnB,KAAKkH,CAAU,EACf,OAAO,CAAC1F,GAAQjE,OACR,EAAE,GAAGiE,GAAQ,CAACjE,CAAQ,GAAGyC,EAAIzC,CAAQ,EAAA,IAC3C,CAAA,CAAO,GAgBD4J,KAAiB,CAC5BnH,GACA6G,MACe;AACf,QAAMrF,IAAS,CAAA;AACf,aAAWjE,KAAOsJ;AAChB,IAAItJ,KAAOyC,MACTwB,EAAOjE,CAAG,IAAIyC,EAAIzC,CAAG;AAGzB,SAAOiE;AACT,GChEa4F,IAAiB,CAACC,MACtB,KAAK,IAAIA,KAAQ,CAAC,OAAO,MAAM,OAAOA,CAAI,CAAC,IAAI,OAAO,SAASA,KAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,GAUlFC,KAAgB,CAAmBrD,GAAasD,MAAiB;AAC5E,QAAM,EAAE,MAAAF,GAAM,GAAGlC,EAAA,IAAWD,EAAejB,CAAG,GAGxCuD,KADcJ,EAAeC,CAAI,IACX,KAAKE;AAEjC,SAAO,EAAE,MAAAA,GAAM,MAAAC,GAAM,GAAGrC,EAAA;AAC1B,GASasC,KAAc,CAACC,GAA+BC,GAAkBN,OAC3EK,EAAa,IAAI,QAAQL,EAAK,SAAA,CAAU,GAEjC,GAAGM,CAAQ,IAAID,EAAa,UAAU,KCxClCE,KAAiB,CAAIpJ,MAAkB;AAClD,MAAI;AACF,WAAO,KAAK,MAAMA,CAAK;AAAA,EACzB,QAAQ;AACN,WAAOA;AAAA,EACT;AACF,GAOaqJ,KAAqB,CAACrJ,MAC7B,OAAOA,KAAU,WACZ,KAAK,UAAUA,CAAK,IAGtBA,GCpBIsJ,KAAiB,MACrB,KAAK,MAAM,KAAK,OAAA,IAAW,QAAQ,EACvC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG,GAQPC,KAAkB,CAAC/K,IAAS,OAAe;AACtD,QAAMgL,IAAQ;AACd,SAAO,MAAM,KAAK,EAAE,QAAAhL,KAAU,MAAMgL,EAAM,KAAK,MAAM,KAAK,OAAA,IAAWA,EAAM,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE;AAC9F,GASaC,KAAkB,CAAC7B,GAAaC,MACpC,KAAK,MAAM,KAAK,OAAA,KAAYA,IAAMD,IAAM,EAAE,IAAIA,GAQ1C8B,KAAkB,CAAClL,MACvB,MAAM,KAAK,EAAE,QAAAA,EAAA,GAAU,MAAM,KAAK,MAAM,KAAK,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,GAShEmL,KAAmB,CAAIC,MAAkB;AACpD,QAAMC,IAAQ,KAAK,MAAM,KAAK,OAAA,IAAWD,EAAM,MAAM;AACrD,SAAOA,EAAMC,CAAK;AACpB,GAOaC,KAAoB,CAAItI,MAA8B;AACjE,QAAM6G,IAAO,OAAO,KAAK7G,CAAG,GACtBuI,IAAY1B,EAAK,KAAK,MAAMA,EAAK,SAAS,KAAK,OAAA,CAAQ,CAAC;AAE9D,SAAO7G,EAAIuI,CAAS;AACtB,GCxDaC,KAAU,CAAC9K,MAClB,OAAOA,KAAW,WACb,KAGLA,EAAO,WAAW,IACbA,IAGFA,EAAO,CAAC,GAAG,gBAAgBA,EAAO,MAAM,CAAC,GAQrC+K,KAAU,CAAC/K,MAClB,OAAOA,KAAW,WACb,KAGLA,EAAO,WAAW,IACbA,IAGFA,EAAO,CAAC,GAAG,gBAAgBA,EAAO,MAAM,CAAC,GC5B5CgL,IAAmB,CAACvF,GAAgBwF,MACjC,IAAI,KAAK,eAAexF,GAAQ,EAAE,WAAAwF,GAAW,GAGhDC,IAAmB,CAACzF,GAAgB0F,MACjC,IAAI,KAAK,eAAe1F,GAAQ,EAAE,WAAA0F,GAAW,GAGhDC,IAAuB,CAAC3F,GAAgBwF,GAAsBE,MAC3D,IAAI,KAAK,eAAe1F,GAAQ,EAAE,WAAAwF,GAAW,WAAAE,GAAW,GAUpDE,IAAa,CACxBC,GACAL,IAAuB,UACvBxF,IAAS,YAEFuF,EAAiBvF,GAAQwF,CAAS,EAAE,OAAO,IAAI,KAAKK,CAAS,CAAC,GAU1DC,IAAa,CACxBD,GACAH,IAAuB,SACvB1F,IAAS,YAEFyF,EAAiBzF,GAAQ0F,CAAS,EAAE,OAAO,IAAI,KAAKG,CAAS,CAAC,GAW1DE,IAAiB,CAC5BF,GACAL,IAAuB,UACvBE,IAAuB,SACvB1F,IAAS,YAEF2F,EAAqB3F,GAAQwF,GAAWE,CAAS,EAAE,OAAO,IAAI,KAAKG,CAAS,CAAC,GAYzEG,KAAmB,CAC9BH,GACA3I,GACAsI,IAAuB,UACvBE,IAAuB,SACvB1F,IAAS,YACN;AACH,UAAQ9C,GAAA;AAAA,IACN,KAAK;AACH,aAAO0I,EAAWC,GAAWL,GAAWxF,CAAM;AAAA,IAChD,KAAK;AACH,aAAO8F,EAAWD,GAAWH,GAAW1F,CAAM;AAAA,IAChD;AACE,aAAO+F,EAAeF,GAAWL,GAAWE,GAAW1F,CAAM;AAAA,EAAA;AAEnE,GAUaiG,KAAkB,CAC7BtM,GACAC,GACA4L,IAAuB,UACvBxF,IAAS,YAEFuF,EAAiBvF,GAAQwF,CAAS,EAAE,YAAY,IAAI,KAAK7L,CAAK,GAAG,IAAI,KAAKC,CAAG,CAAC,GAS1EsM,KAAc,CAACvL,GAAwBwL,IAAM,QACnDxL,IAIE,KAAK,KAAKA,EAAQ,KAAA,EAAO,MAAM,KAAK,EAAE,SAASwL,CAAG,IAHhD;"}
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(r,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("@sindresorhus/slugify")):typeof define=="function"&&define.amd?define(["exports","@sindresorhus/slugify"],u):(r=typeof globalThis<"u"?globalThis:r||self,u(r["@primoui/utils"]={},r.slugifyString))})(this,function(r,u){"use strict";const
|
|
1
|
+
(function(r,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("@sindresorhus/slugify")):typeof define=="function"&&define.amd?define(["exports","@sindresorhus/slugify"],u):(r=typeof globalThis<"u"?globalThis:r||self,u(r["@primoui/utils"]={},r.slugifyString))})(this,function(r,u){"use strict";const L=(t,e)=>{const n=e-t+1;return Array.from({length:n},(s,o)=>o+t)},d=async t=>new Promise(e=>setTimeout(e,t)),B=({key:t,metaKey:e})=>`${e?"⌘":""}${t.toUpperCase()}`,y=t=>t.replace(/<[^>]*>?/gm,""),p=(t,e=" ")=>t.replace(/\n+/g,e),$=(t,e=250)=>{if(!t)return null;const n=p(y(t)),s=n.slice(0,e).trim();return s.length<n.length?`${s}...`:s},j=(t,e=!1)=>u(t,{decamelize:e,customReplacements:[["#","sharp"],["+","plus"]]}),A=t=>t.length===25&&t[0]==="c",b=t=>!!t,F=(t,e=0)=>{const n=(t||"").trim();if(n.length===0||n.length===1||n.length===2)return n.toUpperCase();const o=n.split(" ").filter(b).map(a=>a.charAt(0).toUpperCase()).join("");return e>0?o.slice(0,e):o},W=t=>new Promise((e,n)=>{const s=new FileReader;s.readAsDataURL(t),s.onload=()=>e(s.result),s.onerror=o=>n(o)}),w=(t,e)=>{const n=[];for(let s=0;s<t.length;s+=e)n.push(t.slice(s,s+e));return n},z=(t,e,n=!1)=>{Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"value")?.set?.call(t,e),n&&t?.dispatchEvent(new Event("input",{bubbles:!0}))},K=(t,e=3,n="and")=>t.slice(0,e).join(", ").replace(/, ([^,]*)$/,` ${n} $1`),V=async t=>{try{return{data:await t,error:null}}catch(e){return{data:null,error:e}}},U=t=>{if(t!==null){if(t?.constructor.name==="Object")for(const e in t)t[e]=U(t[e]);return t}},J=(t,e)=>e.some(n=>{const[s,o]=t.split("/"),[a,c]=n.split("/");return s!==a?!1:c==="*"?!0:o===c}),M=async(t,e,n)=>{const{batchSize:s,concurrency:o=s,delay:a=0}=n;if(t.length===0)return[];const c=[],i=w(t,s);for(const[l,h]of i.entries()){console.log(`Processing batch ${l+1}/${i.length} (${h.length} items)`);const f=await Q(h,e,o);c.push(...f),a>0&&l<i.length-1&&(console.log(`Waiting ${a}ms before next batch...`),await d(a))}return c},H=async(t,e,n)=>{const{onError:s}=n;return M(t,async a=>{try{return await e(a)}catch(c){const i=c instanceof Error?c:new Error(String(c));return s?.(i,a),i}},n)},Q=async(t,e,n)=>{if(n>=t.length)return Promise.all(t.map(e));const s=[],o=[];for(const a of t){const c=e(a).then(i=>{s.push(i)});o.push(c),o.length>=n&&(await Promise.race(o),o.splice(0,o.length-o.filter(i=>i!==c).length))}return await Promise.all(o),s},Z=t=>{const e=t.replace("#","").substring(0,6),n=Number.parseInt(e.substring(0,2),16),s=Number.parseInt(e.substring(2,4),16),o=Number.parseInt(e.substring(4,6),16);return n*.299+s*.587+o*.114>186},q=t=>{const e=document.getElementById(t||"");if(!e)return;const n=window.getComputedStyle(e),s=Number.parseFloat(n.scrollMarginTop),o=Math.floor(window.scrollY+e.getBoundingClientRect().top-s);return{id:t,top:o}},P=t=>typeof t=="object"&&t!==null&&"message"in t&&typeof t.message=="string",E=t=>{if(P(t))return t;try{return new Error(JSON.stringify(t))}catch{return new Error(String(t))}},G=t=>E(t).message,_=(t,e)=>{document.addEventListener(t,e)},Y=(t,e)=>{document.removeEventListener(t,e)},X=(t,e)=>{const n=new CustomEvent(t,{detail:e});document.dispatchEvent(n)},x=()=>{const t=new KeyboardEvent("keydown",{key:"Escape"});document.dispatchEvent(t)},tt=(t,e="compact",n="en-US")=>new Intl.NumberFormat(n,{notation:e}).format(t),et=(t,e="USD")=>new Intl.NumberFormat("en-US",{style:"currency",currency:e}).format(t).replace(/\D00(?=\D*$)/,""),nt=(t,e="month")=>g(t/(e==="year"?12:1),2),g=(t,e=0)=>t.toFixed(e<0?0:e).replace(/\.0+$/,""),rt=(t,e=0)=>{if(t<1024){const c=10**e;return`${Math.floor(t/1024*c)/c} KB`}const s=Math.floor(Math.log(t)/Math.log(1024));return`${g(t/1024**s,e)} ${["B","KB","MB","GB","TB","PB","EB","ZB","YB"][s]}`},st=t=>{const[,e]=t.split("/");let n;switch(e){case"*":return;default:n=e}return n?.toUpperCase()},ot=/^https?:\/\/(?:www\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b|localhost(?::\d+)?|127\.0\.0\.1(?::\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,R=t=>{if(!t||typeof t!="string"||!ot.test(t))return!1;try{return new URL(t),!0}catch{return!1}},S=(t,e)=>t?t.startsWith("http://")||t.startsWith("https://")?t:`${e!==void 0?e?"https":"http":T(t)?"http":"https"}://${t}`:"",at=t=>t?.replace(/^https?:\/\//,"")??"",m=t=>{if(!t)return"";let e=t.trim();if(e.includes("?")||e.includes("#"))try{const n=new URL(e);return n.pathname.length>1&&n.pathname.endsWith("/")&&(n.pathname=n.pathname.slice(0,-1)),n.toString()}catch{return e.length>1&&e.endsWith("/")?e.slice(0,-1):e}return e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e},ct=t=>{try{const e=new URL(t);return`${e.protocol}//${e.host}`}catch{return t}},it=t=>{try{if(!R(t))return t;const e=new URL(t).hostname;return e.startsWith("www.")?e.slice(4):e}catch{return t}},ut=t=>t?/^https?:\/\//.test(t):!1,T=t=>{if(!t)return!1;try{const n=new URL(S(t)).hostname;return n==="localhost"||n==="127.0.0.1"||n.endsWith(".localhost")}catch{return t.includes("localhost")||t.includes("127.0.0.1")}},lt=(t,...e)=>{if(!t)return"";let n=m(t);for(const s of e){if(!s)continue;const o=s.replace(/^\/+|\/+$/g,"");o&&(n+=`/${o}`)}return n},v=t=>{try{const e=new URL(t),n={};return e.searchParams.forEach((s,o)=>{n[o]=s}),n}catch{return{}}},mt=(t,e)=>{try{const n=new URL(t);Object.entries(e).forEach(([o,a])=>{n.searchParams.set(o,String(a))});let s=n.toString();return s=s.replace(/\/\?/,"?"),s}catch{return t}},ht=t=>{if(!t)return"";try{const e=new URL(t);return e.search="",m(e.toString())}catch{const e=t.indexOf("?");return e!==-1?t.substring(0,e):t}},ft=async(t,e={})=>{if(!t)return!1;const{timeout:n=5e3,successStatusBelow:s=400,userAgent:o="Mozilla/5.0 (compatible; URLChecker/1.0)"}=e,a=m(t),c=async h=>{const f=new AbortController,k=setTimeout(()=>f.abort(),n);try{const Ft=await fetch(a,{method:h,signal:f.signal,redirect:"follow",headers:{"User-Agent":o}});return clearTimeout(k),Ft}catch{return clearTimeout(k),null}},i=await c("HEAD");if(i&&i.status<s)return!0;const l=await c("GET");return l!==null&&l.status<s},gt=(t,e,n)=>e!==void 0&&n!==void 0?Math.min(Math.max(t,e),n):e!==void 0?Math.max(t,e):n!==void 0?Math.min(t,n):t,dt=t=>{if(t==null)return;const e=Number.parseFloat(t.toString());return Number.isNaN(e)?void 0:e},yt=(t,e=2)=>{const n=10**e;return Math.round((t+Number.EPSILON)*n)/n},pt=(t={})=>t.constructor===Object&&!Object.entries(t).length,bt=(t,e)=>t in e,wt=t=>(e,n)=>{const s=t.indexOf(Object.keys(e)[0]??""),o=t.indexOf(Object.keys(n)[0]??"");return s===-1&&o===-1?0:s===-1?1:o===-1?-1:s-o},Ut=(t,e)=>Object.keys(t).sort(e).reduce((n,s)=>({...n,[s]:t[s]}),{}),Mt=(t,e)=>{const n={};for(const s of e)s in t&&(n[s]=t[s]);return n},D=t=>Math.max(t&&!Number.isNaN(Number(t))?Number.parseInt(t||"1",10):1,1),Pt=(t,e)=>{const{page:n,...s}=v(t),a=(D(n)-1)*e;return{take:e,skip:a,...s}},Et=(t,e,n)=>(t.set("page",n.toString()),`${e}?${t.toString()}`),Rt=t=>{try{return JSON.parse(t)}catch{return t}},St=t=>typeof t=="object"?JSON.stringify(t):t,Tt=()=>Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),vt=(t=16)=>{const e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";return Array.from({length:t},()=>e[Math.floor(Math.random()*e.length)]).join("")},Dt=(t,e)=>Math.floor(Math.random()*(e-t+1))+t,It=t=>Array.from({length:t},()=>Math.floor(Math.random()*10)).join(""),Nt=t=>{const e=Math.floor(Math.random()*t.length);return t[e]},Ot=t=>{const e=Object.keys(t),n=e[Math.floor(e.length*Math.random())];return t[n]},Ct=t=>typeof t!="string"?"":t.length===0?t:t[0]?.toUpperCase()+t.slice(1),kt=t=>typeof t!="string"?"":t.length===0?t:t[0]?.toLowerCase()+t.slice(1),I=(t,e)=>new Intl.DateTimeFormat(t,{dateStyle:e}),Lt=(t,e)=>new Intl.DateTimeFormat(t,{timeStyle:e}),Bt=(t,e,n)=>new Intl.DateTimeFormat(t,{dateStyle:e,timeStyle:n}),N=(t,e="medium",n="en-US")=>I(n,e).format(new Date(t)),O=(t,e="short",n="en-US")=>Lt(n,e).format(new Date(t)),C=(t,e="medium",n="short",s="en-US")=>Bt(s,e,n).format(new Date(t)),$t=(t,e,n="medium",s="short",o="en-US")=>{switch(e){case"date":return N(t,n,o);case"time":return O(t,s,o);default:return C(t,n,s,o)}},jt=(t,e,n="medium",s="en-US")=>I(s,n).formatRange(new Date(t),new Date(e)),At=(t,e=265)=>t?Math.ceil(t.trim().split(/\s+/).length/e):0;r.addProtocol=S,r.checkUrlAvailability=ft,r.convertNewlines=p,r.formatBytes=rt,r.formatCurrency=et,r.formatDate=N,r.formatDateOrTime=$t,r.formatDateRange=jt,r.formatDateTime=C,r.formatIntervalAmount=nt,r.formatMimeType=st,r.formatNumber=tt,r.formatTime=O,r.formatToDecimals=g,r.getBaseUrl=ct,r.getCurrentPage=D,r.getDomain=it,r.getElementPosition=q,r.getErrorMessage=G,r.getExcerpt=$,r.getInitials=F,r.getPageLink=Et,r.getPageParams=Pt,r.getQueryParams=v,r.getRandomColor=Tt,r.getRandomDigits=It,r.getRandomElement=Nt,r.getRandomNumber=Dt,r.getRandomProperty=Ot,r.getRandomString=vt,r.getReadTime=At,r.getShortcutLabel=B,r.isCuid=A,r.isEmptyObject=pt,r.isErrorWithMessage=P,r.isExternalUrl=ut,r.isKeyInObject=bt,r.isLightColor=Z,r.isLocalhostUrl=T,r.isMimeTypeMatch=J,r.isTruthy=b,r.isValidUrl=R,r.joinAsSentence=K,r.joinUrlPaths=lt,r.keepNumberInRange=gt,r.lcFirst=kt,r.maybeParseJson=Rt,r.maybeStringifyJson=St,r.normalizeUrl=m,r.nullsToUndefined=U,r.parseNumericValue=dt,r.pickFromObject=Mt,r.preciseRound=yt,r.processBatch=M,r.processBatchWithErrorHandling=H,r.publish=X,r.publishEscape=x,r.range=L,r.removeProtocol=at,r.removeQueryParams=ht,r.setInputValue=z,r.setQueryParams=mt,r.sleep=d,r.slugify=j,r.sortObject=Ut,r.sortObjectKeys=wt,r.splitArrayIntoChunks=w,r.stripHtml=y,r.subscribe=_,r.toBase64=W,r.toErrorWithMessage=E,r.tryCatch=V,r.ucFirst=Ct,r.unsubscribe=Y,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=index.umd.cjs.map
|
package/dist/index.umd.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.cjs","sources":["../src/helpers/helpers.ts","../src/batch/batch.ts","../src/colors/colors.ts","../src/dom/dom.ts","../src/errors/errors.ts","../src/events/events.ts","../src/format/format.ts","../src/http/http.ts","../src/numbers/numbers.ts","../src/objects/objects.ts","../src/pagination/pagination.ts","../src/parsers/parsers.ts","../src/random/random.ts","../src/string/string.ts","../src/time/time.ts"],"sourcesContent":["import slugifyString from \"@sindresorhus/slugify\"\nimport type { ReplaceNullWithUndefined } from \"..\"\n\n/**\n * A collection of helper functions used throughout the application.\n */\n\n/**\n * A utility function that generates an array of numbers within a specified range.\n * @param start - The starting number of the range.\n * @param end - The ending number of the range.\n * @returns An array of numbers within the specified range.\n */\nexport const range = (start: number, end: number) => {\n const length = end - start + 1\n\n return Array.from({ length }, (_, idx) => idx + start)\n}\n\n/**\n * Delays the execution of the function by the specified amount of time.\n * @param delay - The amount of time to delay the execution of the function, in milliseconds.\n */\nexport const sleep = async (delay: number) => {\n return new Promise(resolve => setTimeout(resolve, delay))\n}\n\n/**\n * Returns a label for the first search key shortcut found.\n * @returns The label for the shortcut.\n */\nexport const getShortcutLabel = ({ key, metaKey }: { key: string; metaKey?: boolean }) => {\n const label = `${metaKey ? \"⌘\" : \"\"}${key.toUpperCase()}`\n return label\n}\n\n/**\n * Strip html tags from a string\n * @param string - string to strip tags from\n * @returns string without html tags\n */\nexport const stripHtml = (string: string) => {\n return string.replace(/<[^>]*>?/gm, \"\")\n}\n\n/**\n * Convert newlines to specified element\n * @param string - string to convert\n * @param replacement - replacement to convert newlines to\n * @returns string with newlines converted to specified element\n */\nexport const convertNewlines = (string: string, replacement = \" \") => {\n return string.replace(/\\n+/g, replacement)\n}\n\n/**\n * Get an excerpt from a string\n * @param content - The string to get an excerpt from\n * @param length - The length of the excerpt\n * @returns An excerpt from the string\n */\nexport const getExcerpt = (content: string | undefined | null, length = 250) => {\n if (!content) {\n return null\n }\n\n const plainText = convertNewlines(stripHtml(content))\n const text = plainText.slice(0, length).trim()\n\n if (text.length < plainText.length) {\n return `${text}...`\n }\n\n return text\n}\n\n/**\n * Converts a string into a slugified version.\n *\n * @param input The string to be slugified.\n * @param decamelize Whether to decamelize the string. Defaults to false.\n * @returns The slugified string.\n */\nexport const slugify = (input: string, decamelize = false): string => {\n return slugifyString(input, {\n decamelize,\n customReplacements: [\n [\"#\", \"sharp\"],\n [\"+\", \"plus\"],\n ],\n })\n}\n\n/**\n * Check if a given string is a valid cuid\n * @param id A string to check\n * @returns A boolean indicating if the string is a cuid\n */\nexport const isCuid = (id: string) => {\n return id.length === 25 && id[0] === \"c\"\n}\n\n/**\n * Check if a value is truthy\n * @param value - The value to check\n * @returns A boolean indicating if the value is truthy\n */\nexport const isTruthy = <T>(value?: T | undefined | null | false): value is T => {\n return !!value\n}\n\n/**\n * Get the initials from a string\n * @param value A string to get the initials from\n * @param limit The maximum number of initials to return\n * @returns The initials from the string\n */\nexport const getInitials = (value?: string | null, limit = 0) => {\n const val = (value || \"\").trim()\n\n // If the value is empty, a single character, or two characters (already initials)\n if (val.length === 0 || val.length === 1 || val.length === 2) {\n return val.toUpperCase()\n }\n\n const values = val.split(\" \").filter(isTruthy)\n const initials = values.map(name => name.charAt(0).toUpperCase()).join(\"\")\n\n if (limit > 0) {\n return initials.slice(0, limit)\n }\n\n return initials\n}\n\n/**\n * Converts a File object to a Base64 encoded string.\n * @param file - The File object to be converted.\n * @returns A promise that resolves with the Base64 encoded string.\n */\nexport const toBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(file)\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = error => reject(error)\n })\n}\n\n/**\n * Splits an array into chunks of a specified size.\n * @param items - The array of items to split into chunks.\n * @param chunkSize - The size of each chunk.\n * @returns An array of arrays, each containing a chunk of the original array.\n */\nexport const splitArrayIntoChunks = <T>(items: T[], chunkSize: number) => {\n const chunks: T[][] = []\n\n for (let i = 0; i < items.length; i += chunkSize) {\n chunks.push(items.slice(i, i + chunkSize))\n }\n\n return chunks\n}\n\n/**\n * Set the value of an HTMLInputElement using its native value setter.\n *\n * @param input - The HTMLInputElement to set the value of.\n * @param value - The value to set on the input element.\n */\nexport const setInputValue = (\n input: HTMLInputElement | null | undefined,\n value: unknown,\n triggerChange = false,\n) => {\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n \"value\",\n )?.set\n\n nativeInputValueSetter?.call(input, value)\n\n // Trigger a change event if the value was changed\n triggerChange && input?.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/**\n * Joins an array of strings into a sentence, with a maximum of 3 items.\n *\n * @param items The array of strings to be joined.\n * @param maxItems The maximum number of items to include in the sentence.\n * @returns The joined sentence.\n */\nexport const joinAsSentence = (items: string[], maxItems = 3, lastItem = \"and\") => {\n return items\n .slice(0, maxItems)\n .join(\", \")\n .replace(/, ([^,]*)$/, ` ${lastItem} $1`)\n}\n\n/**\n * A type representing a successful result with data and no error.\n */\ntype Success<T> = {\n data: T\n error: null\n}\n\n/**\n * A type representing a failed result with no data and an error.\n */\ntype Failure<E> = {\n data: null\n error: E\n}\n\n/**\n * A type representing a result with either data or an error.\n */\ntype Result<T, E = Error> = Success<T> | Failure<E>\n\n/**\n * Wraps a promise and returns a result object with the data or error\n * @param promise - The promise to wrap\n * @returns A result object with the data or error\n */\nexport const tryCatch = async <T, E = Error>(promise: Promise<T>): Promise<Result<T, E>> => {\n try {\n const data = await promise\n return { data, error: null }\n } catch (error) {\n return { data: null, error: error as E }\n }\n}\n\n/**\n * Converts all null values in an object to undefined.\n * @param obj - The object to convert.\n * @returns The converted object.\n */\nexport const nullsToUndefined = <T>(obj: T): ReplaceNullWithUndefined<T> => {\n if (obj === null) {\n return undefined as any\n }\n\n // object check based on: https://stackoverflow.com/a/51458052/6489012\n if (obj?.constructor.name === \"Object\") {\n for (const key in obj) {\n obj[key] = nullsToUndefined(obj[key]) as any\n }\n }\n\n return obj as any\n}\n\n/**\n * Checks if a MIME type matches any of the provided patterns.\n * Supports wildcard matching for subtypes (e.g., \"image/*\").\n *\n * @param mimeType - The MIME type to check (e.g., \"image/jpeg\", \"text/plain\")\n * @param patterns - Array of MIME type patterns to match against (e.g., [\"image/*\", \"text/plain\"])\n * @returns True if the MIME type matches any of the patterns, false otherwise\n *\n * @example\n * ```typescript\n * isMimeTypeMatch(\"image/jpeg\", [\"image/*\"]) // returns true\n * isMimeTypeMatch(\"text/plain\", [\"image/*\", \"text/plain\"]) // returns true\n * isMimeTypeMatch(\"application/json\", [\"image/*\"]) // returns false\n * ```\n */\nexport const isMimeTypeMatch = (mimeType: string, patterns: string[]) => {\n return patterns.some(pattern => {\n // Split type/subtype for both mimeType and pattern\n const [type, subtype] = mimeType.split(\"/\")\n const [pType, pSubtype] = pattern.split(\"/\")\n\n // Type must match\n if (type !== pType) return false\n\n // Wildcard matches any subtype\n if (pSubtype === \"*\") return true\n\n // Exact subtype match\n return subtype === pSubtype\n })\n}\n","import { sleep, splitArrayIntoChunks } from \"../helpers/helpers\"\n\ntype ProcessBatchOptions = {\n batchSize: number\n concurrency?: number\n delay?: number\n}\n/**\n * Process items in batches with controlled concurrency and delays\n * Useful for handling external API rate limits\n */\nexport const processBatch = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions,\n): Promise<R[]> => {\n const { batchSize, concurrency = batchSize, delay = 0 } = options\n\n if (items.length === 0) return []\n\n const results: R[] = []\n const batches = splitArrayIntoChunks(items, batchSize)\n\n for (const [i, batch] of batches.entries()) {\n console.log(`Processing batch ${i + 1}/${batches.length} (${batch.length} items)`)\n\n // Process batch with controlled concurrency\n const batchResults = await processWithConcurrency(batch, processor, concurrency)\n results.push(...batchResults)\n\n // Add delay between batches (except for the last batch)\n if (delay > 0 && i < batches.length - 1) {\n console.log(`Waiting ${delay}ms before next batch...`)\n await sleep(delay)\n }\n }\n\n return results\n}\n\n/**\n * Batch processing with error handling - continues processing even if some items fail\n */\nexport const processBatchWithErrorHandling = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions & {\n onError?: (error: Error, item: T) => void\n },\n): Promise<Array<R | Error>> => {\n const { onError } = options\n\n const wrappedProcessor = async (item: T): Promise<R | Error> => {\n try {\n return await processor(item)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err, item)\n return err\n }\n }\n\n return processBatch(items, wrappedProcessor, options)\n}\n\n/**\n * Process items with controlled concurrency using a semaphore-like approach\n */\nconst processWithConcurrency = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n concurrency: number,\n): Promise<R[]> => {\n if (concurrency >= items.length) {\n // If concurrency is higher than items count, just process all at once\n return Promise.all(items.map(processor))\n }\n\n const results: R[] = []\n const executing: Promise<void>[] = []\n\n for (const item of items) {\n const promise = processor(item).then(result => {\n results.push(result)\n })\n\n executing.push(promise)\n\n // If we've reached the concurrency limit, wait for one to finish\n if (executing.length >= concurrency) {\n await Promise.race(executing)\n // Remove completed promises\n executing.splice(0, executing.length - executing.filter(p => p !== promise).length)\n }\n }\n\n // Wait for all remaining promises to complete\n await Promise.all(executing)\n\n return results\n}\n","/**\n * Check if a given color in hexadecimal format is a light color.\n * Only supports 6-digit hex colors (RGB). If longer string is provided, it will be trimmed.\n *\n * @param hexa - The hexadecimal color code to check (e.g. \"#FF0000\").\n * @returns A boolean indicating if the color is light.\n */\nexport const isLightColor = (hexa: string): boolean => {\n // Remove # if present and trim to 6 characters\n const hex = hexa.replace(\"#\", \"\").substring(0, 6)\n\n // Parse RGB values\n const r = Number.parseInt(hex.substring(0, 2), 16)\n const g = Number.parseInt(hex.substring(2, 4), 16)\n const b = Number.parseInt(hex.substring(4, 6), 16)\n\n // Calculate perceived brightness\n const brightness = r * 0.299 + g * 0.587 + b * 0.114\n\n return brightness > 186\n}\n","/**\n * Returns the position of an element with the given ID relative to the top of the viewport.\n * @param id - The ID of the element to get the position of.\n * @returns An object with the ID and top position of the element, or undefined if the element is not found.\n */\nexport const getElementPosition = (id?: string) => {\n const el = document.getElementById(id || \"\")\n if (!el) return\n\n const style = window.getComputedStyle(el)\n const scrollMt = Number.parseFloat(style.scrollMarginTop)\n const top = Math.floor(window.scrollY + el.getBoundingClientRect().top - scrollMt)\n\n return { id, top }\n}\n","/**\n * Utility functions for working with errors.\n */\n\n/**\n * An error object with a message property.\n */\nexport type ErrorWithMessage = {\n message: string\n}\n\n/**\n * Type guard function that checks if an object is an ErrorWithMessage.\n * @param error - The object to check.\n * @returns True if the object is an ErrorWithMessage, false otherwise.\n */\nexport const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n )\n}\n\n/**\n * Converts an unknown value to an ErrorWithMessage object.\n * If the value is already an ErrorWithMessage, it is returned as-is.\n * Otherwise, a new Error object is created with the value stringified as its message.\n * @param maybeError - The value to convert.\n * @returns An ErrorWithMessage object.\n */\nexport const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => {\n if (isErrorWithMessage(maybeError)) return maybeError\n\n try {\n return new Error(JSON.stringify(maybeError))\n } catch {\n // fallback in case there's an error stringifying the maybeError\n // like with circular references for example.\n return new Error(String(maybeError))\n }\n}\n\n/**\n * Gets the error message from an unknown value.\n * If the value is an ErrorWithMessage, its message property is returned.\n * Otherwise, the value is stringified and returned as the error message.\n * @param error - The value to get the error message from.\n * @returns The error message as a string.\n */\nexport const getErrorMessage = (error: unknown) => {\n return toErrorWithMessage(error).message\n}\n","/**\n * Utility functions for working with events.\n */\n\n/**\n * Subscribes to a custom event.\n * @param eventName - The name of the event to subscribe to.\n * @param listener - The function to be called when the event is triggered.\n */\nexport const subscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.addEventListener(eventName, listener)\n}\n\n/**\n * Unsubscribes from a custom event.\n * @param eventName - The name of the event to unsubscribe from.\n * @param listener - The function to be removed from the event listeners.\n */\nexport const unsubscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.removeEventListener(eventName, listener)\n}\n\n/**\n * Publishes a custom event.\n * @param eventName - The name of the event to publish.\n * @param data - The data to be passed along with the event.\n */\nexport const publish = (eventName: string, data: unknown) => {\n const event = new CustomEvent(eventName, { detail: data })\n document.dispatchEvent(event)\n}\n\n/**\n * Publishes a keyboard event for the Escape key.\n */\nexport const publishEscape = () => {\n const event = new KeyboardEvent(\"keydown\", { key: \"Escape\" })\n document.dispatchEvent(event)\n}\n","/**\n * Utility functions for formatting data.\n */\n\ntype Notation = Intl.NumberFormatOptions[\"notation\"]\ntype Currency = Intl.NumberFormatOptions[\"currency\"]\n\n/**\n * Formats a number with thousands separators.\n * @param number - The number to format.\n * @param notation - The notation to use for formatting. Defaults to 'compact'.\n * @param locale - The locale to use for formatting. Defaults to 'en-US'.\n * @returns The formatted number as a string.\n */\nexport const formatNumber = (number: number, notation: Notation = \"compact\", locale = \"en-US\") => {\n const formatter = new Intl.NumberFormat(locale, { notation })\n\n return formatter.format(number)\n}\n\n/**\n * Formats a given amount of money into a currency string.\n * @param amount The amount of money to format.\n * @param currency The currency to format the amount in. Defaults to 'USD'.\n * @returns The formatted currency string.\n */\nexport const formatCurrency = (amount: number, currency: Currency = \"USD\") => {\n const formatter = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency,\n })\n\n return formatter.format(amount).replace(/\\D00(?=\\D*$)/, \"\")\n}\n\n/**\n * Formats a given amount with an interval\n * @param amount The amount of money to format.\n * @param interval The interval, either 'month' or 'year'. Defaults to 'month'.\n * @returns The formatted amount per interval.\n */\nexport const formatIntervalAmount = (amount: number, interval: \"month\" | \"year\" = \"month\") => {\n return formatToDecimals(amount / (interval === \"year\" ? 12 : 1), 2)\n}\n\n/**\n * Formats a number to a specified number of decimal places.\n * @param number - The number to format.\n * @param precision - The number of decimal places to format to.\n * @returns The formatted number as a string.\n */\nexport const formatToDecimals = (number: number, precision = 0): string => {\n return number.toFixed(precision < 0 ? 0 : precision).replace(/\\.0+$/, \"\")\n}\n\n/**\n * Formats a number of bytes to a human-readable string.\n * @param bytes - The number of bytes to format.\n * @param precision - The number of decimal places to format the size to.\n * @returns The formatted size as a string.\n */\nexport const formatBytes = (bytes: number, precision = 0): string => {\n const k = 1024\n\n if (bytes < k) {\n const factor = 10 ** precision\n const size = Math.floor((bytes / k) * factor) / factor\n return `${size} KB`\n }\n\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\n return `${size} ${sizes[i]}`\n}\n\n/**\n * Formats a MIME type string to a more readable format.\n * @param mimeType - The MIME type string to format.\n * @returns The formatted MIME type string.\n */\nexport const formatMimeType = (mimeType: string): string | undefined => {\n const [, subtype] = mimeType.split(\"/\")\n let type: string | undefined\n\n switch (subtype) {\n case \"*\":\n return undefined\n default:\n type = subtype\n }\n\n return type?.toUpperCase()\n}\n","/**\n * Utility functions for URL manipulation and validation.\n */\n\n/**\n * Enhanced URL validation using regex pattern and URL constructor\n * Supports both regular domains and localhost/IP addresses\n */\nconst URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,63}\\b|localhost(?::\\d+)?|127\\.0\\.0\\.1(?::\\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/\n\n/**\n * Checks if a URL is valid using both regex and URL constructor validation\n * @param url - The URL to validate\n * @returns True if the URL is valid\n */\nexport const isValidUrl = (url?: string): boolean => {\n if (!url || typeof url !== \"string\") return false\n\n // First check with regex for basic format\n if (!URL_REGEX.test(url)) return false\n\n // Then validate with URL constructor\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Adds protocol to a URL string\n * @param url - The URL string without protocol\n * @param secure - Whether to use https (default: true, false for localhost)\n * @returns URL with protocol added\n */\nexport const addProtocol = (url?: string, secure?: boolean): string => {\n if (!url) return \"\"\n\n // Don't add protocol if already present\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) return url\n\n // Determine protocol based on secure flag and URL\n const protocol =\n secure !== undefined ? (secure ? \"https\" : \"http\") : isLocalhostUrl(url) ? \"http\" : \"https\"\n\n return `${protocol}://${url}`\n}\n\n/**\n * Removes protocol from a URL string\n * @param url - The URL string with protocol\n * @returns URL without protocol\n */\nexport const removeProtocol = (url?: string): string => {\n return url?.replace(/^https?:\\/\\//, \"\") ?? \"\"\n}\n\n/**\n * Normalizes a URL by removing trailing slashes and cleaning up format\n * @param url - The URL to normalize\n * @returns Normalized URL\n */\nexport const normalizeUrl = (url?: string): string => {\n if (!url) return \"\"\n\n let normalized = url.trim()\n\n // Handle URLs with query parameters or hash fragments\n if (normalized.includes(\"?\") || normalized.includes(\"#\")) {\n try {\n const parsedUrl = new URL(normalized)\n // Remove trailing slash from pathname only\n if (parsedUrl.pathname.length > 1 && parsedUrl.pathname.endsWith(\"/\")) {\n parsedUrl.pathname = parsedUrl.pathname.slice(0, -1)\n }\n return parsedUrl.toString()\n } catch {\n // Fallback for invalid URLs - just remove trailing slash if no query/hash\n return normalized.length > 1 && normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized\n }\n }\n\n // Simple case: remove trailing slash but keep root slash\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1)\n }\n\n return normalized\n}\n\n/**\n * Gets the base URL (protocol + hostname + port)\n * @param url - The URL string\n * @returns Base URL without path, search, or hash\n */\nexport const getBaseUrl = (url: string): string => {\n try {\n const parsedUrl = new URL(url)\n return `${parsedUrl.protocol}//${parsedUrl.host}`\n } catch {\n return url\n }\n}\n\n/**\n * Extracts the domain name from a URL\n * @param url - The URL string\n * @returns Domain name without www prefix\n */\nexport const getDomain = (url: string): string => {\n try {\n if (!isValidUrl(url)) return url\n\n const hostname = new URL(url).hostname\n return hostname.startsWith(\"www.\") ? hostname.slice(4) : hostname\n } catch {\n return url\n }\n}\n\n/**\n * Checks if a URL is external (has protocol)\n * @param url - The URL to check\n * @returns True if URL is external (contains protocol)\n */\nexport const isExternalUrl = (url?: string): boolean => {\n if (!url) return false\n return /^https?:\\/\\//.test(url)\n}\n\n/**\n * Checks if a URL is a localhost URL\n * @param url - The URL to check\n * @returns True if URL points to localhost\n */\nexport const isLocalhostUrl = (url?: string): boolean => {\n if (!url) return false\n\n try {\n const parsedUrl = new URL(addProtocol(url))\n const hostname = parsedUrl.hostname\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname.endsWith(\".localhost\")\n } catch {\n return url.includes(\"localhost\") || url.includes(\"127.0.0.1\")\n }\n}\n\n/**\n * Joins URL paths safely\n * @param base - Base URL\n * @param paths - Path segments to join\n * @returns Combined URL\n */\nexport const joinUrlPaths = (base: string, ...paths: string[]): string => {\n if (!base) return \"\"\n\n let result = normalizeUrl(base)\n\n for (const path of paths) {\n if (!path) continue\n\n const cleanPath = path.replace(/^\\/+|\\/+$/g, \"\") // Remove leading/trailing slashes\n if (cleanPath) {\n result += `/${cleanPath}`\n }\n }\n\n return result\n}\n\n/**\n * Extracts query parameters from a URL\n * @param url - The URL string\n * @returns Object containing query parameters\n */\nexport const getQueryParams = (url: string): Record<string, string> => {\n try {\n const parsedUrl = new URL(url)\n const params: Record<string, string> = {}\n\n parsedUrl.searchParams.forEach((value, key) => {\n params[key] = value\n })\n\n return params\n } catch {\n return {}\n }\n}\n\n/**\n * Adds or updates query parameters in a URL\n * @param url - The base URL\n * @param params - Parameters to add/update\n * @returns URL with updated parameters\n */\nexport const setQueryParams = (\n url: string,\n params: Record<string, string | number | boolean>,\n): string => {\n try {\n const parsedUrl = new URL(url)\n\n Object.entries(params).forEach(([key, value]) => {\n parsedUrl.searchParams.set(key, String(value))\n })\n\n let result = parsedUrl.toString()\n\n // Special case: remove trailing slash before query parameters for cleaner URLs\n result = result.replace(/\\/\\?/, \"?\")\n\n return result\n } catch {\n return url\n }\n}\n\n/**\n * Removes query parameters from a URL\n * @param url - The URL string\n * @returns URL without query parameters\n */\nexport const removeQueryParams = (url?: string): string => {\n if (!url) return \"\"\n\n try {\n const parsedUrl = new URL(url)\n parsedUrl.search = \"\"\n return normalizeUrl(parsedUrl.toString())\n } catch {\n // For invalid URLs, try simple string manipulation\n const questionIndex = url.indexOf(\"?\")\n return questionIndex !== -1 ? url.substring(0, questionIndex) : url\n }\n}\n\n// Legacy aliases for backward compatibility (deprecated)\n/** @deprecated Use addProtocol instead */\nexport const addHttp = addProtocol\n\n/** @deprecated Use removeProtocol instead */\nexport const removeHttp = removeProtocol\n\n/** @deprecated Use normalizeUrl instead */\nexport const removeTrailingSlash = normalizeUrl\n\n/** @deprecated Use normalizeUrl instead */\nexport const stripTrailingSlash = normalizeUrl\n\n/** @deprecated Use getBaseUrl instead */\nexport const stripURLSubpath = getBaseUrl\n\n/** @deprecated Use getDomain instead */\nexport const getUrlHostname = getDomain\n\n/** @deprecated Use removeQueryParams instead */\nexport const removeSearchParams = removeQueryParams\n\n/** @deprecated Use getQueryParams instead */\nexport const getSearchParams = getQueryParams\n\n/** @deprecated Use setQueryParams instead */\nexport const addSearchParams = setQueryParams\n","/**\n * Utility functions for working with numbers.\n */\n\n/**\n * Keep a number within a specified range.\n * @param value - The number to keep within the range.\n * @param min - The minimum value of the range.\n * @param max - The maximum value of the range.\n * @returns The number within the specified range.\n */\nexport const keepNumberInRange = (value: number, min?: number, max?: number) => {\n if (min !== undefined && max !== undefined) {\n return Math.min(Math.max(value, min), max)\n }\n if (min !== undefined) {\n return Math.max(value, min)\n }\n if (max !== undefined) {\n return Math.min(value, max)\n }\n\n return value\n}\n\n/**\n * Parse a string into a numeric value.\n * @param value - The value to parse into a numeric value.\n * @returns The parsed numeric value, or `undefined` if the value cannot be parsed.\n */\nexport const parseNumericValue = (value?: string | number | null) => {\n if (value === undefined || value === null) return undefined\n const parsed = Number.parseFloat(value.toString())\n\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\n/**\n * Rounds a number to a specified number of decimal places, with an adjustment for floating point precision.\n * @param value - The number to round.\n * @param decimals - The number of decimal places to round to. Defaults to 2.\n * @returns The rounded number.\n */\nexport const preciseRound = (value: number, decimals = 2) => {\n const factor = Math.pow(10, decimals)\n\n return Math.round((value + Number.EPSILON) * factor) / factor\n}\n","/**\n * Utility functions for working with objects.\n */\n\n/**\n * Checks if an object is empty (has no own properties).\n * @param obj - The object to check.\n * @returns `true` if the object is empty, `false` otherwise.\n */\nexport const isEmptyObject = (obj: Record<string, unknown> = {}) => {\n return obj.constructor === Object && !Object.entries(obj).length\n}\n\n/**\n * Checks if a key is present in an object.\n * @param key - The key to check.\n * @param obj - The object to check.\n * @returns `true` if the key is present in the object, `false` otherwise.\n */\nexport const isKeyInObject = <T extends object>(key: PropertyKey, obj: T): key is keyof T => {\n return key in obj\n}\n\n/**\n * Sorts two objects based on their keys' positions in an array of keys.\n * @param keys - An array of keys to sort the objects by.\n * @param a - The first object to compare.\n * @param b - The second object to compare.\n * @returns A number indicating the sort order of the two objects.\n */\nexport const sortObjectKeys = (keys: string[]) => {\n return (a: Record<string, unknown>, b: Record<string, unknown>) => {\n const aIndex = keys.indexOf(Object.keys(a)[0] ?? \"\")\n const bIndex = keys.indexOf(Object.keys(b)[0] ?? \"\")\n\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n\n return aIndex - bIndex\n }\n}\n\n/**\n * Sorts the keys of an object in alphabetical order and returns a new object with the sorted keys.\n * @param obj - The input object to sort.\n * @param comparator - An optional comparator function to use when sorting the keys.\n * @returns - A new object with the sorted keys.\n */\nexport const sortObject = <T extends Record<K, unknown>, K extends keyof T>(\n obj: T,\n comparator?: (a: unknown, b: unknown) => number,\n) => {\n return Object.keys(obj)\n .sort(comparator)\n .reduce((result, key) => {\n return { ...result, [key as K]: obj[key as K] }\n }, {} as T)\n}\n\n/**\n * Creates a new object with only the specified properties from the source object.\n * Provides full type safety and intellisense for the picked properties.\n *\n * @param obj - The source object to pick properties from\n * @param keys - Array of property keys to pick from the source object\n * @returns A new object containing only the specified properties\n *\n * @example\n * const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' }\n * const publicUser = pick(user, ['id', 'name', 'email'])\n * // Result: { id: 1, name: 'John', email: 'john@example.com' }\n */\nexport const pickFromObject = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key]\n }\n }\n return result\n}\n","/**\n * Utility functions for pagination and page management.\n */\n\nimport { getQueryParams } from \"../http/http\"\n\n/**\n * Represents the parameters for a paginated query.\n * @template T - The type of the query parameters.\n */\nexport type GetPageParams<T> = T & {\n take: number\n skip: number\n}\n\n/**\n * Returns the current page number from a string.\n * @param page - The page number as a string.\n * @returns The current page number as a number.\n */\nexport const getCurrentPage = (page?: string | null) => {\n return Math.max(page && !Number.isNaN(Number(page)) ? Number.parseInt(page || \"1\", 10) : 1, 1)\n}\n\n/**\n * Returns an object containing the parameters for a paginated query.\n * @template T - The type of the query parameters.\n * @param url - The URL to get the page parameters from.\n * @param take - The number of items to take per page.\n * @returns An object containing the parameters for a paginated query.\n */\nexport const getPageParams = <T extends object>(url: string, take: number) => {\n const { page, ...params } = getQueryParams(url)\n\n const currentPage = getCurrentPage(page)\n const skip = (currentPage - 1) * take\n\n return { take, skip, ...params } as GetPageParams<T>\n}\n\n/**\n * Returns a link to a specific page of a paginated query.\n * @param searchParams - The search parameters object.\n * @param pathname - The pathname of the URL.\n * @param page - The page number to link to.\n * @returns A link to the specified page of the paginated query.\n */\nexport const getPageLink = (searchParams: URLSearchParams, pathname: string, page: number) => {\n searchParams.set(\"page\", page.toString())\n\n return `${pathname}?${searchParams.toString()}`\n}\n","/**\n * Utility functions for parsing and stringifying JSON.\n */\n\n/**\n * Parses a string as JSON and returns the result. If the string cannot be parsed as JSON, returns the original string.\n * @param value - The string to parse as JSON.\n * @returns The parsed JSON object or the original string.\n * @template T - The type of the parsed JSON object.\n */\nexport const maybeParseJson = <T>(value: string) => {\n try {\n return JSON.parse(value) as T\n } catch {\n return value\n }\n}\n\n/**\n * Returns a JSON string representation of the given object, or the original string if it's not an object.\n * @param value - The value to stringify.\n * @returns The JSON string representation of the object, or the original string if it's not an object.\n */\nexport const maybeStringifyJson = (value?: object | string) => {\n if (typeof value === \"object\") {\n return JSON.stringify(value)\n }\n\n return value\n}\n","/**\n * Utility functions for generating random values.\n */\n\n/**\n * Returns a random hexadecimal color code.\n * @returns A string representing a random hexadecimal color code.\n */\nexport const getRandomColor = (): string => {\n return Math.floor(Math.random() * 16777215)\n .toString(16)\n .padStart(6, \"0\")\n}\n\n/**\n * Returns a random string of characters.\n * @param length - The desired length of the random string\n * @returns A string representing a random string of characters.\n */\nexport const getRandomString = (length = 16): string => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(\"\")\n}\n\n/**\n * Generates a random number between the specified minimum and maximum values (inclusive).\n *\n * @param min The minimum value for the random number.\n * @param max The maximum value for the random number.\n * @returns A random number between the specified minimum and maximum values.\n */\nexport const getRandomNumber = (min: number, max: number) => {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * Generate a random string of digits\n * @param length Length of the digits string\n * @returns Random digits string\n */\nexport const getRandomDigits = (length: number) => {\n return Array.from({ length }, () => Math.floor(Math.random() * 10)).join(\"\")\n}\n\n/**\n * Returns a random element from an array.\n *\n * @param array - The array to get a random element from.\n * @returns A random element from the array.\n */\nexport const getRandomElement = <T>(array: T[]): T => {\n const index = Math.floor(Math.random() * array.length)\n return array[index]!\n}\n\n/**\n * Returns a random property value from an object.\n * @param obj - The object to get a random property value from.\n * @returns A random property value from the object.\n */\nexport const getRandomProperty = <T>(obj: Record<string, T>): T => {\n const keys = Object.keys(obj)\n const randomKey = keys[Math.floor(keys.length * Math.random())]!\n\n return obj[randomKey]!\n}\n","/**\n * Utility functions for working with strings.\n */\n\n/**\n * Uppercases the first character in the `string`.\n * @param string - The string to uppercase the first character of.\n * @returns The string with the first character in uppercase.\n */\nexport const ucFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toUpperCase() + string.slice(1)\n}\n\n/**\n * Lowercases the first character in the `string`.\n * @param string - The string to lowercase the first character of.\n * @returns The string with the first character in lowercase.\n */\nexport const lcFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toLowerCase() + string.slice(1)\n}\n","/**\n * Utility functions related to time.\n */\ntype DateStyle = Intl.DateTimeFormatOptions[\"dateStyle\"]\ntype TimeStyle = Intl.DateTimeFormatOptions[\"timeStyle\"]\ntype Timestamp = string | number | Date\n\nconst getDateFormatter = (locale: string, dateStyle: DateStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle })\n}\n\nconst getTimeFormatter = (locale: string, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { timeStyle })\n}\n\nconst getDateTimeFormatter = (locale: string, dateStyle: DateStyle, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle, timeStyle })\n}\n\n/**\n * Formats a date according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date string.\n */\nexport const formatDate = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted time string.\n */\nexport const formatTime = (\n timestamp: Timestamp,\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getTimeFormatter(locale, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date and time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date and time string.\n */\nexport const formatDateTime = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getDateTimeFormatter(locale, dateStyle, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date or time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param type - The type of formatting to use. Can be 'date', 'time', or 'datetime'.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date or time string.\n */\nexport const formatDateOrTime = (\n timestamp: Timestamp,\n type: \"date\" | \"time\" | \"datetime\",\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n switch (type) {\n case \"date\":\n return formatDate(timestamp, dateStyle, locale)\n case \"time\":\n return formatTime(timestamp, timeStyle, locale)\n default:\n return formatDateTime(timestamp, dateStyle, timeStyle, locale)\n }\n}\n\n/**\n * Formats a date range.\n * @param start - The start date.\n * @param end - The end date.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date range string.\n */\nexport const formatDateRange = (\n start: Timestamp,\n end: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).formatRange(new Date(start), new Date(end))\n}\n\n/**\n * Calculates the estimated read time for a given content.\n * @param content - The content to calculate the read time for.\n * @param wpm - The average words per minute to use for the calculation. Defaults to 265.\n * @returns The estimated read time in minutes.\n */\nexport const getReadTime = (content: string | null, wpm = 265): number => {\n if (!content) {\n return 0\n }\n\n return Math.ceil(content.trim().split(/\\s+/).length / wpm)\n}\n"],"names":["range","start","end","length","_","idx","sleep","delay","resolve","getShortcutLabel","key","metaKey","stripHtml","string","convertNewlines","replacement","getExcerpt","content","plainText","text","slugify","input","decamelize","slugifyString","isCuid","id","isTruthy","value","getInitials","limit","val","initials","name","toBase64","file","reject","reader","error","splitArrayIntoChunks","items","chunkSize","chunks","i","setInputValue","triggerChange","joinAsSentence","maxItems","lastItem","tryCatch","promise","nullsToUndefined","obj","isMimeTypeMatch","mimeType","patterns","pattern","type","subtype","pType","pSubtype","processBatch","processor","options","batchSize","concurrency","results","batches","batch","batchResults","processWithConcurrency","processBatchWithErrorHandling","onError","item","err","executing","result","p","isLightColor","hexa","hex","r","g","b","getElementPosition","el","style","scrollMt","top","isErrorWithMessage","toErrorWithMessage","maybeError","getErrorMessage","subscribe","eventName","listener","unsubscribe","publish","data","event","publishEscape","formatNumber","number","notation","locale","formatCurrency","amount","currency","formatIntervalAmount","interval","formatToDecimals","precision","formatBytes","bytes","factor","formatMimeType","URL_REGEX","isValidUrl","url","addProtocol","secure","isLocalhostUrl","removeProtocol","normalizeUrl","normalized","parsedUrl","getBaseUrl","getDomain","hostname","isExternalUrl","joinUrlPaths","base","paths","path","cleanPath","getQueryParams","params","setQueryParams","removeQueryParams","questionIndex","addHttp","removeHttp","removeTrailingSlash","stripTrailingSlash","stripURLSubpath","getUrlHostname","removeSearchParams","getSearchParams","addSearchParams","keepNumberInRange","min","max","parseNumericValue","parsed","preciseRound","decimals","isEmptyObject","isKeyInObject","sortObjectKeys","keys","a","aIndex","bIndex","sortObject","comparator","pickFromObject","getCurrentPage","page","getPageParams","take","skip","getPageLink","searchParams","pathname","maybeParseJson","maybeStringifyJson","getRandomColor","getRandomString","chars","getRandomNumber","getRandomDigits","getRandomElement","array","index","getRandomProperty","randomKey","ucFirst","lcFirst","getDateFormatter","dateStyle","getTimeFormatter","timeStyle","getDateTimeFormatter","formatDate","timestamp","formatTime","formatDateTime","formatDateOrTime","formatDateRange","getReadTime","wpm"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.cjs","sources":["../src/helpers/helpers.ts","../src/batch/batch.ts","../src/colors/colors.ts","../src/dom/dom.ts","../src/errors/errors.ts","../src/events/events.ts","../src/format/format.ts","../src/http/http.ts","../src/numbers/numbers.ts","../src/objects/objects.ts","../src/pagination/pagination.ts","../src/parsers/parsers.ts","../src/random/random.ts","../src/string/string.ts","../src/time/time.ts"],"sourcesContent":["import slugifyString from \"@sindresorhus/slugify\"\nimport type { ReplaceNullWithUndefined } from \"..\"\n\n/**\n * A collection of helper functions used throughout the application.\n */\n\n/**\n * A utility function that generates an array of numbers within a specified range.\n * @param start - The starting number of the range.\n * @param end - The ending number of the range.\n * @returns An array of numbers within the specified range.\n */\nexport const range = (start: number, end: number) => {\n const length = end - start + 1\n\n return Array.from({ length }, (_, idx) => idx + start)\n}\n\n/**\n * Delays the execution of the function by the specified amount of time.\n * @param delay - The amount of time to delay the execution of the function, in milliseconds.\n */\nexport const sleep = async (delay: number) => {\n return new Promise(resolve => setTimeout(resolve, delay))\n}\n\n/**\n * Returns a label for the first search key shortcut found.\n * @returns The label for the shortcut.\n */\nexport const getShortcutLabel = ({ key, metaKey }: { key: string; metaKey?: boolean }) => {\n const label = `${metaKey ? \"⌘\" : \"\"}${key.toUpperCase()}`\n return label\n}\n\n/**\n * Strip html tags from a string\n * @param string - string to strip tags from\n * @returns string without html tags\n */\nexport const stripHtml = (string: string) => {\n return string.replace(/<[^>]*>?/gm, \"\")\n}\n\n/**\n * Convert newlines to specified element\n * @param string - string to convert\n * @param replacement - replacement to convert newlines to\n * @returns string with newlines converted to specified element\n */\nexport const convertNewlines = (string: string, replacement = \" \") => {\n return string.replace(/\\n+/g, replacement)\n}\n\n/**\n * Get an excerpt from a string\n * @param content - The string to get an excerpt from\n * @param length - The length of the excerpt\n * @returns An excerpt from the string\n */\nexport const getExcerpt = (content: string | undefined | null, length = 250) => {\n if (!content) {\n return null\n }\n\n const plainText = convertNewlines(stripHtml(content))\n const text = plainText.slice(0, length).trim()\n\n if (text.length < plainText.length) {\n return `${text}...`\n }\n\n return text\n}\n\n/**\n * Converts a string into a slugified version.\n *\n * @param input The string to be slugified.\n * @param decamelize Whether to decamelize the string. Defaults to false.\n * @returns The slugified string.\n */\nexport const slugify = (input: string, decamelize = false): string => {\n return slugifyString(input, {\n decamelize,\n customReplacements: [\n [\"#\", \"sharp\"],\n [\"+\", \"plus\"],\n ],\n })\n}\n\n/**\n * Check if a given string is a valid cuid\n * @param id A string to check\n * @returns A boolean indicating if the string is a cuid\n */\nexport const isCuid = (id: string) => {\n return id.length === 25 && id[0] === \"c\"\n}\n\n/**\n * Check if a value is truthy\n * @param value - The value to check\n * @returns A boolean indicating if the value is truthy\n */\nexport const isTruthy = <T>(value?: T | undefined | null | false): value is T => {\n return !!value\n}\n\n/**\n * Get the initials from a string\n * @param value A string to get the initials from\n * @param limit The maximum number of initials to return\n * @returns The initials from the string\n */\nexport const getInitials = (value?: string | null, limit = 0) => {\n const val = (value || \"\").trim()\n\n // If the value is empty, a single character, or two characters (already initials)\n if (val.length === 0 || val.length === 1 || val.length === 2) {\n return val.toUpperCase()\n }\n\n const values = val.split(\" \").filter(isTruthy)\n const initials = values.map(name => name.charAt(0).toUpperCase()).join(\"\")\n\n if (limit > 0) {\n return initials.slice(0, limit)\n }\n\n return initials\n}\n\n/**\n * Converts a File object to a Base64 encoded string.\n * @param file - The File object to be converted.\n * @returns A promise that resolves with the Base64 encoded string.\n */\nexport const toBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(file)\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = error => reject(error)\n })\n}\n\n/**\n * Splits an array into chunks of a specified size.\n * @param items - The array of items to split into chunks.\n * @param chunkSize - The size of each chunk.\n * @returns An array of arrays, each containing a chunk of the original array.\n */\nexport const splitArrayIntoChunks = <T>(items: T[], chunkSize: number) => {\n const chunks: T[][] = []\n\n for (let i = 0; i < items.length; i += chunkSize) {\n chunks.push(items.slice(i, i + chunkSize))\n }\n\n return chunks\n}\n\n/**\n * Set the value of an HTMLInputElement using its native value setter.\n *\n * @param input - The HTMLInputElement to set the value of.\n * @param value - The value to set on the input element.\n */\nexport const setInputValue = (\n input: HTMLInputElement | null | undefined,\n value: unknown,\n triggerChange = false,\n) => {\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n HTMLInputElement.prototype,\n \"value\",\n )?.set\n\n nativeInputValueSetter?.call(input, value)\n\n // Trigger a change event if the value was changed\n triggerChange && input?.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/**\n * Joins an array of strings into a sentence, with a maximum of 3 items.\n *\n * @param items The array of strings to be joined.\n * @param maxItems The maximum number of items to include in the sentence.\n * @returns The joined sentence.\n */\nexport const joinAsSentence = (items: string[], maxItems = 3, lastItem = \"and\") => {\n return items\n .slice(0, maxItems)\n .join(\", \")\n .replace(/, ([^,]*)$/, ` ${lastItem} $1`)\n}\n\n/**\n * A type representing a successful result with data and no error.\n */\ntype Success<T> = {\n data: T\n error: null\n}\n\n/**\n * A type representing a failed result with no data and an error.\n */\ntype Failure<E> = {\n data: null\n error: E\n}\n\n/**\n * A type representing a result with either data or an error.\n */\ntype Result<T, E = Error> = Success<T> | Failure<E>\n\n/**\n * Wraps a promise and returns a result object with the data or error\n * @param promise - The promise to wrap\n * @returns A result object with the data or error\n */\nexport const tryCatch = async <T, E = Error>(promise: Promise<T>): Promise<Result<T, E>> => {\n try {\n const data = await promise\n return { data, error: null }\n } catch (error) {\n return { data: null, error: error as E }\n }\n}\n\n/**\n * Converts all null values in an object to undefined.\n * @param obj - The object to convert.\n * @returns The converted object.\n */\nexport const nullsToUndefined = <T>(obj: T): ReplaceNullWithUndefined<T> => {\n if (obj === null) {\n return undefined as any\n }\n\n // object check based on: https://stackoverflow.com/a/51458052/6489012\n if (obj?.constructor.name === \"Object\") {\n for (const key in obj) {\n obj[key] = nullsToUndefined(obj[key]) as any\n }\n }\n\n return obj as any\n}\n\n/**\n * Checks if a MIME type matches any of the provided patterns.\n * Supports wildcard matching for subtypes (e.g., \"image/*\").\n *\n * @param mimeType - The MIME type to check (e.g., \"image/jpeg\", \"text/plain\")\n * @param patterns - Array of MIME type patterns to match against (e.g., [\"image/*\", \"text/plain\"])\n * @returns True if the MIME type matches any of the patterns, false otherwise\n *\n * @example\n * ```typescript\n * isMimeTypeMatch(\"image/jpeg\", [\"image/*\"]) // returns true\n * isMimeTypeMatch(\"text/plain\", [\"image/*\", \"text/plain\"]) // returns true\n * isMimeTypeMatch(\"application/json\", [\"image/*\"]) // returns false\n * ```\n */\nexport const isMimeTypeMatch = (mimeType: string, patterns: string[]) => {\n return patterns.some(pattern => {\n // Split type/subtype for both mimeType and pattern\n const [type, subtype] = mimeType.split(\"/\")\n const [pType, pSubtype] = pattern.split(\"/\")\n\n // Type must match\n if (type !== pType) return false\n\n // Wildcard matches any subtype\n if (pSubtype === \"*\") return true\n\n // Exact subtype match\n return subtype === pSubtype\n })\n}\n","import { sleep, splitArrayIntoChunks } from \"../helpers/helpers\"\n\ntype ProcessBatchOptions = {\n batchSize: number\n concurrency?: number\n delay?: number\n}\n/**\n * Process items in batches with controlled concurrency and delays\n * Useful for handling external API rate limits\n */\nexport const processBatch = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions,\n): Promise<R[]> => {\n const { batchSize, concurrency = batchSize, delay = 0 } = options\n\n if (items.length === 0) return []\n\n const results: R[] = []\n const batches = splitArrayIntoChunks(items, batchSize)\n\n for (const [i, batch] of batches.entries()) {\n console.log(`Processing batch ${i + 1}/${batches.length} (${batch.length} items)`)\n\n // Process batch with controlled concurrency\n const batchResults = await processWithConcurrency(batch, processor, concurrency)\n results.push(...batchResults)\n\n // Add delay between batches (except for the last batch)\n if (delay > 0 && i < batches.length - 1) {\n console.log(`Waiting ${delay}ms before next batch...`)\n await sleep(delay)\n }\n }\n\n return results\n}\n\n/**\n * Batch processing with error handling - continues processing even if some items fail\n */\nexport const processBatchWithErrorHandling = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n options: ProcessBatchOptions & {\n onError?: (error: Error, item: T) => void\n },\n): Promise<Array<R | Error>> => {\n const { onError } = options\n\n const wrappedProcessor = async (item: T): Promise<R | Error> => {\n try {\n return await processor(item)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err, item)\n return err\n }\n }\n\n return processBatch(items, wrappedProcessor, options)\n}\n\n/**\n * Process items with controlled concurrency using a semaphore-like approach\n */\nconst processWithConcurrency = async <T, R>(\n items: T[],\n processor: (item: T) => Promise<R>,\n concurrency: number,\n): Promise<R[]> => {\n if (concurrency >= items.length) {\n // If concurrency is higher than items count, just process all at once\n return Promise.all(items.map(processor))\n }\n\n const results: R[] = []\n const executing: Promise<void>[] = []\n\n for (const item of items) {\n const promise = processor(item).then(result => {\n results.push(result)\n })\n\n executing.push(promise)\n\n // If we've reached the concurrency limit, wait for one to finish\n if (executing.length >= concurrency) {\n await Promise.race(executing)\n // Remove completed promises\n executing.splice(0, executing.length - executing.filter(p => p !== promise).length)\n }\n }\n\n // Wait for all remaining promises to complete\n await Promise.all(executing)\n\n return results\n}\n","/**\n * Check if a given color in hexadecimal format is a light color.\n * Only supports 6-digit hex colors (RGB). If longer string is provided, it will be trimmed.\n *\n * @param hexa - The hexadecimal color code to check (e.g. \"#FF0000\").\n * @returns A boolean indicating if the color is light.\n */\nexport const isLightColor = (hexa: string): boolean => {\n // Remove # if present and trim to 6 characters\n const hex = hexa.replace(\"#\", \"\").substring(0, 6)\n\n // Parse RGB values\n const r = Number.parseInt(hex.substring(0, 2), 16)\n const g = Number.parseInt(hex.substring(2, 4), 16)\n const b = Number.parseInt(hex.substring(4, 6), 16)\n\n // Calculate perceived brightness\n const brightness = r * 0.299 + g * 0.587 + b * 0.114\n\n return brightness > 186\n}\n","/**\n * Returns the position of an element with the given ID relative to the top of the viewport.\n * @param id - The ID of the element to get the position of.\n * @returns An object with the ID and top position of the element, or undefined if the element is not found.\n */\nexport const getElementPosition = (id?: string) => {\n const el = document.getElementById(id || \"\")\n if (!el) return\n\n const style = window.getComputedStyle(el)\n const scrollMt = Number.parseFloat(style.scrollMarginTop)\n const top = Math.floor(window.scrollY + el.getBoundingClientRect().top - scrollMt)\n\n return { id, top }\n}\n","/**\n * Utility functions for working with errors.\n */\n\n/**\n * An error object with a message property.\n */\nexport type ErrorWithMessage = {\n message: string\n}\n\n/**\n * Type guard function that checks if an object is an ErrorWithMessage.\n * @param error - The object to check.\n * @returns True if the object is an ErrorWithMessage, false otherwise.\n */\nexport const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"message\" in error &&\n typeof (error as Record<string, unknown>).message === \"string\"\n )\n}\n\n/**\n * Converts an unknown value to an ErrorWithMessage object.\n * If the value is already an ErrorWithMessage, it is returned as-is.\n * Otherwise, a new Error object is created with the value stringified as its message.\n * @param maybeError - The value to convert.\n * @returns An ErrorWithMessage object.\n */\nexport const toErrorWithMessage = (maybeError: unknown): ErrorWithMessage => {\n if (isErrorWithMessage(maybeError)) return maybeError\n\n try {\n return new Error(JSON.stringify(maybeError))\n } catch {\n // fallback in case there's an error stringifying the maybeError\n // like with circular references for example.\n return new Error(String(maybeError))\n }\n}\n\n/**\n * Gets the error message from an unknown value.\n * If the value is an ErrorWithMessage, its message property is returned.\n * Otherwise, the value is stringified and returned as the error message.\n * @param error - The value to get the error message from.\n * @returns The error message as a string.\n */\nexport const getErrorMessage = (error: unknown) => {\n return toErrorWithMessage(error).message\n}\n","/**\n * Utility functions for working with events.\n */\n\n/**\n * Subscribes to a custom event.\n * @param eventName - The name of the event to subscribe to.\n * @param listener - The function to be called when the event is triggered.\n */\nexport const subscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.addEventListener(eventName, listener)\n}\n\n/**\n * Unsubscribes from a custom event.\n * @param eventName - The name of the event to unsubscribe from.\n * @param listener - The function to be removed from the event listeners.\n */\nexport const unsubscribe = (eventName: string, listener: EventListenerOrEventListenerObject) => {\n document.removeEventListener(eventName, listener)\n}\n\n/**\n * Publishes a custom event.\n * @param eventName - The name of the event to publish.\n * @param data - The data to be passed along with the event.\n */\nexport const publish = (eventName: string, data: unknown) => {\n const event = new CustomEvent(eventName, { detail: data })\n document.dispatchEvent(event)\n}\n\n/**\n * Publishes a keyboard event for the Escape key.\n */\nexport const publishEscape = () => {\n const event = new KeyboardEvent(\"keydown\", { key: \"Escape\" })\n document.dispatchEvent(event)\n}\n","/**\n * Utility functions for formatting data.\n */\n\ntype Notation = Intl.NumberFormatOptions[\"notation\"]\ntype Currency = Intl.NumberFormatOptions[\"currency\"]\n\n/**\n * Formats a number with thousands separators.\n * @param number - The number to format.\n * @param notation - The notation to use for formatting. Defaults to 'compact'.\n * @param locale - The locale to use for formatting. Defaults to 'en-US'.\n * @returns The formatted number as a string.\n */\nexport const formatNumber = (number: number, notation: Notation = \"compact\", locale = \"en-US\") => {\n const formatter = new Intl.NumberFormat(locale, { notation })\n\n return formatter.format(number)\n}\n\n/**\n * Formats a given amount of money into a currency string.\n * @param amount The amount of money to format.\n * @param currency The currency to format the amount in. Defaults to 'USD'.\n * @returns The formatted currency string.\n */\nexport const formatCurrency = (amount: number, currency: Currency = \"USD\") => {\n const formatter = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency,\n })\n\n return formatter.format(amount).replace(/\\D00(?=\\D*$)/, \"\")\n}\n\n/**\n * Formats a given amount with an interval\n * @param amount The amount of money to format.\n * @param interval The interval, either 'month' or 'year'. Defaults to 'month'.\n * @returns The formatted amount per interval.\n */\nexport const formatIntervalAmount = (amount: number, interval: \"month\" | \"year\" = \"month\") => {\n return formatToDecimals(amount / (interval === \"year\" ? 12 : 1), 2)\n}\n\n/**\n * Formats a number to a specified number of decimal places.\n * @param number - The number to format.\n * @param precision - The number of decimal places to format to.\n * @returns The formatted number as a string.\n */\nexport const formatToDecimals = (number: number, precision = 0): string => {\n return number.toFixed(precision < 0 ? 0 : precision).replace(/\\.0+$/, \"\")\n}\n\n/**\n * Formats a number of bytes to a human-readable string.\n * @param bytes - The number of bytes to format.\n * @param precision - The number of decimal places to format the size to.\n * @returns The formatted size as a string.\n */\nexport const formatBytes = (bytes: number, precision = 0): string => {\n const k = 1024\n\n if (bytes < k) {\n const factor = 10 ** precision\n const size = Math.floor((bytes / k) * factor) / factor\n return `${size} KB`\n }\n\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\n return `${size} ${sizes[i]}`\n}\n\n/**\n * Formats a MIME type string to a more readable format.\n * @param mimeType - The MIME type string to format.\n * @returns The formatted MIME type string.\n */\nexport const formatMimeType = (mimeType: string): string | undefined => {\n const [, subtype] = mimeType.split(\"/\")\n let type: string | undefined\n\n switch (subtype) {\n case \"*\":\n return undefined\n default:\n type = subtype\n }\n\n return type?.toUpperCase()\n}\n","/**\n * Utility functions for URL manipulation and validation.\n */\n\n/**\n * Enhanced URL validation using regex pattern and URL constructor\n * Supports both regular domains and localhost/IP addresses\n */\nconst URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?(?:[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,63}\\b|localhost(?::\\d+)?|127\\.0\\.0\\.1(?::\\d+)?)(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/\n\n/**\n * Checks if a URL is valid using both regex and URL constructor validation\n * @param url - The URL to validate\n * @returns True if the URL is valid\n */\nexport const isValidUrl = (url?: string): boolean => {\n if (!url || typeof url !== \"string\") return false\n\n // First check with regex for basic format\n if (!URL_REGEX.test(url)) return false\n\n // Then validate with URL constructor\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Adds protocol to a URL string\n * @param url - The URL string without protocol\n * @param secure - Whether to use https (default: true, false for localhost)\n * @returns URL with protocol added\n */\nexport const addProtocol = (url?: string, secure?: boolean): string => {\n if (!url) return \"\"\n\n // Don't add protocol if already present\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) return url\n\n // Determine protocol based on secure flag and URL\n const protocol =\n secure !== undefined ? (secure ? \"https\" : \"http\") : isLocalhostUrl(url) ? \"http\" : \"https\"\n\n return `${protocol}://${url}`\n}\n\n/**\n * Removes protocol from a URL string\n * @param url - The URL string with protocol\n * @returns URL without protocol\n */\nexport const removeProtocol = (url?: string): string => {\n return url?.replace(/^https?:\\/\\//, \"\") ?? \"\"\n}\n\n/**\n * Normalizes a URL by removing trailing slashes and cleaning up format\n * @param url - The URL to normalize\n * @returns Normalized URL\n */\nexport const normalizeUrl = (url?: string): string => {\n if (!url) return \"\"\n\n let normalized = url.trim()\n\n // Handle URLs with query parameters or hash fragments\n if (normalized.includes(\"?\") || normalized.includes(\"#\")) {\n try {\n const parsedUrl = new URL(normalized)\n // Remove trailing slash from pathname only\n if (parsedUrl.pathname.length > 1 && parsedUrl.pathname.endsWith(\"/\")) {\n parsedUrl.pathname = parsedUrl.pathname.slice(0, -1)\n }\n return parsedUrl.toString()\n } catch {\n // Fallback for invalid URLs - just remove trailing slash if no query/hash\n return normalized.length > 1 && normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized\n }\n }\n\n // Simple case: remove trailing slash but keep root slash\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1)\n }\n\n return normalized\n}\n\n/**\n * Gets the base URL (protocol + hostname + port)\n * @param url - The URL string\n * @returns Base URL without path, search, or hash\n */\nexport const getBaseUrl = (url: string): string => {\n try {\n const parsedUrl = new URL(url)\n return `${parsedUrl.protocol}//${parsedUrl.host}`\n } catch {\n return url\n }\n}\n\n/**\n * Extracts the domain name from a URL\n * @param url - The URL string\n * @returns Domain name without www prefix\n */\nexport const getDomain = (url: string): string => {\n try {\n if (!isValidUrl(url)) return url\n\n const hostname = new URL(url).hostname\n return hostname.startsWith(\"www.\") ? hostname.slice(4) : hostname\n } catch {\n return url\n }\n}\n\n/**\n * Checks if a URL is external (has protocol)\n * @param url - The URL to check\n * @returns True if URL is external (contains protocol)\n */\nexport const isExternalUrl = (url?: string): boolean => {\n if (!url) return false\n return /^https?:\\/\\//.test(url)\n}\n\n/**\n * Checks if a URL is a localhost URL\n * @param url - The URL to check\n * @returns True if URL points to localhost\n */\nexport const isLocalhostUrl = (url?: string): boolean => {\n if (!url) return false\n\n try {\n const parsedUrl = new URL(addProtocol(url))\n const hostname = parsedUrl.hostname\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname.endsWith(\".localhost\")\n } catch {\n return url.includes(\"localhost\") || url.includes(\"127.0.0.1\")\n }\n}\n\n/**\n * Joins URL paths safely\n * @param base - Base URL\n * @param paths - Path segments to join\n * @returns Combined URL\n */\nexport const joinUrlPaths = (base: string, ...paths: string[]): string => {\n if (!base) return \"\"\n\n let result = normalizeUrl(base)\n\n for (const path of paths) {\n if (!path) continue\n\n const cleanPath = path.replace(/^\\/+|\\/+$/g, \"\") // Remove leading/trailing slashes\n if (cleanPath) {\n result += `/${cleanPath}`\n }\n }\n\n return result\n}\n\n/**\n * Extracts query parameters from a URL\n * @param url - The URL string\n * @returns Object containing query parameters\n */\nexport const getQueryParams = (url: string): Record<string, string> => {\n try {\n const parsedUrl = new URL(url)\n const params: Record<string, string> = {}\n\n parsedUrl.searchParams.forEach((value, key) => {\n params[key] = value\n })\n\n return params\n } catch {\n return {}\n }\n}\n\n/**\n * Adds or updates query parameters in a URL\n * @param url - The base URL\n * @param params - Parameters to add/update\n * @returns URL with updated parameters\n */\nexport const setQueryParams = (\n url: string,\n params: Record<string, string | number | boolean>,\n): string => {\n try {\n const parsedUrl = new URL(url)\n\n Object.entries(params).forEach(([key, value]) => {\n parsedUrl.searchParams.set(key, String(value))\n })\n\n let result = parsedUrl.toString()\n\n // Special case: remove trailing slash before query parameters for cleaner URLs\n result = result.replace(/\\/\\?/, \"?\")\n\n return result\n } catch {\n return url\n }\n}\n\n/**\n * Removes query parameters from a URL\n * @param url - The URL string\n * @returns URL without query parameters\n */\nexport const removeQueryParams = (url?: string): string => {\n if (!url) return \"\"\n\n try {\n const parsedUrl = new URL(url)\n parsedUrl.search = \"\"\n return normalizeUrl(parsedUrl.toString())\n } catch {\n // For invalid URLs, try simple string manipulation\n const questionIndex = url.indexOf(\"?\")\n return questionIndex !== -1 ? url.substring(0, questionIndex) : url\n }\n}\n\n/** Options for checkUrlAvailability */\nexport type CheckUrlAvailabilityOptions = {\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number\n /** HTTP status codes below this value are considered successful (default: 400) */\n successStatusBelow?: number\n /** User-Agent header to send with requests */\n userAgent?: string\n}\n\n/**\n * Checks if a URL is accessible by making an HTTP request.\n * First tries a HEAD request, then falls back to GET if HEAD fails.\n * @param url - The URL to check\n * @param options - Configuration options for the request\n * @returns True if the URL is accessible (status < 400), false otherwise\n */\nexport const checkUrlAvailability = async (\n url: string,\n options: CheckUrlAvailabilityOptions = {},\n): Promise<boolean> => {\n if (!url) {\n return false\n }\n\n const {\n timeout = 5000,\n successStatusBelow = 400,\n userAgent = \"Mozilla/5.0 (compatible; URLChecker/1.0)\",\n } = options\n\n const normalizedUrl = normalizeUrl(url)\n\n const makeRequest = async (method: \"HEAD\" | \"GET\"): Promise<Response | null> => {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(normalizedUrl, {\n method,\n signal: controller.signal,\n redirect: \"follow\",\n headers: { \"User-Agent\": userAgent },\n })\n clearTimeout(timeoutId)\n return response\n } catch {\n clearTimeout(timeoutId)\n return null\n }\n }\n\n const headResponse = await makeRequest(\"HEAD\")\n\n if (headResponse && headResponse.status < successStatusBelow) {\n return true\n }\n\n const getResponse = await makeRequest(\"GET\")\n return getResponse !== null && getResponse.status < successStatusBelow\n}\n","/**\n * Utility functions for working with numbers.\n */\n\n/**\n * Keep a number within a specified range.\n * @param value - The number to keep within the range.\n * @param min - The minimum value of the range.\n * @param max - The maximum value of the range.\n * @returns The number within the specified range.\n */\nexport const keepNumberInRange = (value: number, min?: number, max?: number) => {\n if (min !== undefined && max !== undefined) {\n return Math.min(Math.max(value, min), max)\n }\n if (min !== undefined) {\n return Math.max(value, min)\n }\n if (max !== undefined) {\n return Math.min(value, max)\n }\n\n return value\n}\n\n/**\n * Parse a string into a numeric value.\n * @param value - The value to parse into a numeric value.\n * @returns The parsed numeric value, or `undefined` if the value cannot be parsed.\n */\nexport const parseNumericValue = (value?: string | number | null) => {\n if (value === undefined || value === null) return undefined\n const parsed = Number.parseFloat(value.toString())\n\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\n/**\n * Rounds a number to a specified number of decimal places, with an adjustment for floating point precision.\n * @param value - The number to round.\n * @param decimals - The number of decimal places to round to. Defaults to 2.\n * @returns The rounded number.\n */\nexport const preciseRound = (value: number, decimals = 2) => {\n const factor = 10 ** decimals\n\n return Math.round((value + Number.EPSILON) * factor) / factor\n}\n","/**\n * Utility functions for working with objects.\n */\n\n/**\n * Checks if an object is empty (has no own properties).\n * @param obj - The object to check.\n * @returns `true` if the object is empty, `false` otherwise.\n */\nexport const isEmptyObject = (obj: Record<string, unknown> = {}) => {\n return obj.constructor === Object && !Object.entries(obj).length\n}\n\n/**\n * Checks if a key is present in an object.\n * @param key - The key to check.\n * @param obj - The object to check.\n * @returns `true` if the key is present in the object, `false` otherwise.\n */\nexport const isKeyInObject = <T extends object>(key: PropertyKey, obj: T): key is keyof T => {\n return key in obj\n}\n\n/**\n * Sorts two objects based on their keys' positions in an array of keys.\n * @param keys - An array of keys to sort the objects by.\n * @param a - The first object to compare.\n * @param b - The second object to compare.\n * @returns A number indicating the sort order of the two objects.\n */\nexport const sortObjectKeys = (keys: string[]) => {\n return (a: Record<string, unknown>, b: Record<string, unknown>) => {\n const aIndex = keys.indexOf(Object.keys(a)[0] ?? \"\")\n const bIndex = keys.indexOf(Object.keys(b)[0] ?? \"\")\n\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n\n return aIndex - bIndex\n }\n}\n\n/**\n * Sorts the keys of an object in alphabetical order and returns a new object with the sorted keys.\n * @param obj - The input object to sort.\n * @param comparator - An optional comparator function to use when sorting the keys.\n * @returns - A new object with the sorted keys.\n */\nexport const sortObject = <T extends Record<K, unknown>, K extends keyof T>(\n obj: T,\n comparator?: (a: unknown, b: unknown) => number,\n) => {\n return Object.keys(obj)\n .sort(comparator)\n .reduce((result, key) => {\n return { ...result, [key as K]: obj[key as K] }\n }, {} as T)\n}\n\n/**\n * Creates a new object with only the specified properties from the source object.\n * Provides full type safety and intellisense for the picked properties.\n *\n * @param obj - The source object to pick properties from\n * @param keys - Array of property keys to pick from the source object\n * @returns A new object containing only the specified properties\n *\n * @example\n * const user = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' }\n * const publicUser = pick(user, ['id', 'name', 'email'])\n * // Result: { id: 1, name: 'John', email: 'john@example.com' }\n */\nexport const pickFromObject = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>\n for (const key of keys) {\n if (key in obj) {\n result[key] = obj[key]\n }\n }\n return result\n}\n","/**\n * Utility functions for pagination and page management.\n */\n\nimport { getQueryParams } from \"../http/http\"\n\n/**\n * Represents the parameters for a paginated query.\n * @template T - The type of the query parameters.\n */\nexport type GetPageParams<T> = T & {\n take: number\n skip: number\n}\n\n/**\n * Returns the current page number from a string.\n * @param page - The page number as a string.\n * @returns The current page number as a number.\n */\nexport const getCurrentPage = (page?: string | null) => {\n return Math.max(page && !Number.isNaN(Number(page)) ? Number.parseInt(page || \"1\", 10) : 1, 1)\n}\n\n/**\n * Returns an object containing the parameters for a paginated query.\n * @template T - The type of the query parameters.\n * @param url - The URL to get the page parameters from.\n * @param take - The number of items to take per page.\n * @returns An object containing the parameters for a paginated query.\n */\nexport const getPageParams = <T extends object>(url: string, take: number) => {\n const { page, ...params } = getQueryParams(url)\n\n const currentPage = getCurrentPage(page)\n const skip = (currentPage - 1) * take\n\n return { take, skip, ...params } as GetPageParams<T>\n}\n\n/**\n * Returns a link to a specific page of a paginated query.\n * @param searchParams - The search parameters object.\n * @param pathname - The pathname of the URL.\n * @param page - The page number to link to.\n * @returns A link to the specified page of the paginated query.\n */\nexport const getPageLink = (searchParams: URLSearchParams, pathname: string, page: number) => {\n searchParams.set(\"page\", page.toString())\n\n return `${pathname}?${searchParams.toString()}`\n}\n","/**\n * Utility functions for parsing and stringifying JSON.\n */\n\n/**\n * Parses a string as JSON and returns the result. If the string cannot be parsed as JSON, returns the original string.\n * @param value - The string to parse as JSON.\n * @returns The parsed JSON object or the original string.\n * @template T - The type of the parsed JSON object.\n */\nexport const maybeParseJson = <T>(value: string) => {\n try {\n return JSON.parse(value) as T\n } catch {\n return value\n }\n}\n\n/**\n * Returns a JSON string representation of the given object, or the original string if it's not an object.\n * @param value - The value to stringify.\n * @returns The JSON string representation of the object, or the original string if it's not an object.\n */\nexport const maybeStringifyJson = (value?: object | string) => {\n if (typeof value === \"object\") {\n return JSON.stringify(value)\n }\n\n return value\n}\n","/**\n * Utility functions for generating random values.\n */\n\n/**\n * Returns a random hexadecimal color code.\n * @returns A string representing a random hexadecimal color code.\n */\nexport const getRandomColor = (): string => {\n return Math.floor(Math.random() * 16777215)\n .toString(16)\n .padStart(6, \"0\")\n}\n\n/**\n * Returns a random string of characters.\n * @param length - The desired length of the random string\n * @returns A string representing a random string of characters.\n */\nexport const getRandomString = (length = 16): string => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join(\"\")\n}\n\n/**\n * Generates a random number between the specified minimum and maximum values (inclusive).\n *\n * @param min The minimum value for the random number.\n * @param max The maximum value for the random number.\n * @returns A random number between the specified minimum and maximum values.\n */\nexport const getRandomNumber = (min: number, max: number) => {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * Generate a random string of digits\n * @param length Length of the digits string\n * @returns Random digits string\n */\nexport const getRandomDigits = (length: number) => {\n return Array.from({ length }, () => Math.floor(Math.random() * 10)).join(\"\")\n}\n\n/**\n * Returns a random element from an array.\n *\n * @param array - The array to get a random element from.\n * @returns A random element from the array.\n */\nexport const getRandomElement = <T>(array: T[]): T => {\n const index = Math.floor(Math.random() * array.length)\n return array[index]!\n}\n\n/**\n * Returns a random property value from an object.\n * @param obj - The object to get a random property value from.\n * @returns A random property value from the object.\n */\nexport const getRandomProperty = <T>(obj: Record<string, T>): T => {\n const keys = Object.keys(obj)\n const randomKey = keys[Math.floor(keys.length * Math.random())]!\n\n return obj[randomKey]!\n}\n","/**\n * Utility functions for working with strings.\n */\n\n/**\n * Uppercases the first character in the `string`.\n * @param string - The string to uppercase the first character of.\n * @returns The string with the first character in uppercase.\n */\nexport const ucFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toUpperCase() + string.slice(1)\n}\n\n/**\n * Lowercases the first character in the `string`.\n * @param string - The string to lowercase the first character of.\n * @returns The string with the first character in lowercase.\n */\nexport const lcFirst = (string: string) => {\n if (typeof string !== \"string\") {\n return \"\"\n }\n\n if (string.length === 0) {\n return string\n }\n\n return string[0]?.toLowerCase() + string.slice(1)\n}\n","/**\n * Utility functions related to time.\n */\ntype DateStyle = Intl.DateTimeFormatOptions[\"dateStyle\"]\ntype TimeStyle = Intl.DateTimeFormatOptions[\"timeStyle\"]\ntype Timestamp = string | number | Date\n\nconst getDateFormatter = (locale: string, dateStyle: DateStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle })\n}\n\nconst getTimeFormatter = (locale: string, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { timeStyle })\n}\n\nconst getDateTimeFormatter = (locale: string, dateStyle: DateStyle, timeStyle: TimeStyle) => {\n return new Intl.DateTimeFormat(locale, { dateStyle, timeStyle })\n}\n\n/**\n * Formats a date according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date string.\n */\nexport const formatDate = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted time string.\n */\nexport const formatTime = (\n timestamp: Timestamp,\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getTimeFormatter(locale, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date and time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date and time string.\n */\nexport const formatDateTime = (\n timestamp: Timestamp,\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n return getDateTimeFormatter(locale, dateStyle, timeStyle).format(new Date(timestamp))\n}\n\n/**\n * Formats a date or time according to the specified options.\n * @param timestamp - The timestamp to format.\n * @param type - The type of formatting to use. Can be 'date', 'time', or 'datetime'.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param timeStyle - The time formatting style to use. Defaults to 'short'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date or time string.\n */\nexport const formatDateOrTime = (\n timestamp: Timestamp,\n type: \"date\" | \"time\" | \"datetime\",\n dateStyle: DateStyle = \"medium\",\n timeStyle: TimeStyle = \"short\",\n locale = \"en-US\",\n) => {\n switch (type) {\n case \"date\":\n return formatDate(timestamp, dateStyle, locale)\n case \"time\":\n return formatTime(timestamp, timeStyle, locale)\n default:\n return formatDateTime(timestamp, dateStyle, timeStyle, locale)\n }\n}\n\n/**\n * Formats a date range.\n * @param start - The start date.\n * @param end - The end date.\n * @param dateStyle - The date formatting style to use. Defaults to 'medium'.\n * @param locale - The locale to use for formatting. Defaults to 'en'.\n * @returns The formatted date range string.\n */\nexport const formatDateRange = (\n start: Timestamp,\n end: Timestamp,\n dateStyle: DateStyle = \"medium\",\n locale = \"en-US\",\n) => {\n return getDateFormatter(locale, dateStyle).formatRange(new Date(start), new Date(end))\n}\n\n/**\n * Calculates the estimated read time for a given content.\n * @param content - The content to calculate the read time for.\n * @param wpm - The average words per minute to use for the calculation. Defaults to 265.\n * @returns The estimated read time in minutes.\n */\nexport const getReadTime = (content: string | null, wpm = 265): number => {\n if (!content) {\n return 0\n }\n\n return Math.ceil(content.trim().split(/\\s+/).length / wpm)\n}\n"],"names":["range","start","end","length","_","idx","sleep","delay","resolve","getShortcutLabel","key","metaKey","stripHtml","string","convertNewlines","replacement","getExcerpt","content","plainText","text","slugify","input","decamelize","slugifyString","isCuid","id","isTruthy","value","getInitials","limit","val","initials","name","toBase64","file","reject","reader","error","splitArrayIntoChunks","items","chunkSize","chunks","i","setInputValue","triggerChange","joinAsSentence","maxItems","lastItem","tryCatch","promise","nullsToUndefined","obj","isMimeTypeMatch","mimeType","patterns","pattern","type","subtype","pType","pSubtype","processBatch","processor","options","batchSize","concurrency","results","batches","batch","batchResults","processWithConcurrency","processBatchWithErrorHandling","onError","item","err","executing","result","p","isLightColor","hexa","hex","r","g","b","getElementPosition","el","style","scrollMt","top","isErrorWithMessage","toErrorWithMessage","maybeError","getErrorMessage","subscribe","eventName","listener","unsubscribe","publish","data","event","publishEscape","formatNumber","number","notation","locale","formatCurrency","amount","currency","formatIntervalAmount","interval","formatToDecimals","precision","formatBytes","bytes","factor","formatMimeType","URL_REGEX","isValidUrl","url","addProtocol","secure","isLocalhostUrl","removeProtocol","normalizeUrl","normalized","parsedUrl","getBaseUrl","getDomain","hostname","isExternalUrl","joinUrlPaths","base","paths","path","cleanPath","getQueryParams","params","setQueryParams","removeQueryParams","questionIndex","checkUrlAvailability","timeout","successStatusBelow","userAgent","normalizedUrl","makeRequest","method","controller","timeoutId","response","headResponse","getResponse","keepNumberInRange","min","max","parseNumericValue","parsed","preciseRound","decimals","isEmptyObject","isKeyInObject","sortObjectKeys","keys","a","aIndex","bIndex","sortObject","comparator","pickFromObject","getCurrentPage","page","getPageParams","take","skip","getPageLink","searchParams","pathname","maybeParseJson","maybeStringifyJson","getRandomColor","getRandomString","chars","getRandomNumber","getRandomDigits","getRandomElement","array","index","getRandomProperty","randomKey","ucFirst","lcFirst","getDateFormatter","dateStyle","getTimeFormatter","timeStyle","getDateTimeFormatter","formatDate","timestamp","formatTime","formatDateTime","formatDateOrTime","formatDateRange","getReadTime","wpm"],"mappings":"oTAaO,MAAMA,EAAQ,CAACC,EAAeC,IAAgB,CACnD,MAAMC,EAASD,EAAMD,EAAQ,EAE7B,OAAO,MAAM,KAAK,CAAE,OAAAE,CAAA,EAAU,CAACC,EAAGC,IAAQA,EAAMJ,CAAK,CACvD,EAMaK,EAAQ,MAAOC,GACnB,IAAI,QAAQC,GAAW,WAAWA,EAASD,CAAK,CAAC,EAO7CE,EAAmB,CAAC,CAAE,IAAAC,EAAK,QAAAC,KACxB,GAAGA,EAAU,IAAM,EAAE,GAAGD,EAAI,aAAa,GAS5CE,EAAaC,GACjBA,EAAO,QAAQ,aAAc,EAAE,EAS3BC,EAAkB,CAACD,EAAgBE,EAAc,MACrDF,EAAO,QAAQ,OAAQE,CAAW,EAS9BC,EAAa,CAACC,EAAoCd,EAAS,MAAQ,CAC9E,GAAI,CAACc,EACH,OAAO,KAGT,MAAMC,EAAYJ,EAAgBF,EAAUK,CAAO,CAAC,EAC9CE,EAAOD,EAAU,MAAM,EAAGf,CAAM,EAAE,KAAA,EAExC,OAAIgB,EAAK,OAASD,EAAU,OACnB,GAAGC,CAAI,MAGTA,CACT,EASaC,EAAU,CAACC,EAAeC,EAAa,KAC3CC,EAAcF,EAAO,CAC1B,WAAAC,EACA,mBAAoB,CAClB,CAAC,IAAK,OAAO,EACb,CAAC,IAAK,MAAM,CAAA,CACd,CACD,EAQUE,EAAUC,GACdA,EAAG,SAAW,IAAMA,EAAG,CAAC,IAAM,IAQ1BC,EAAeC,GACnB,CAAC,CAACA,EASEC,EAAc,CAACD,EAAuBE,EAAQ,IAAM,CAC/D,MAAMC,GAAOH,GAAS,IAAI,KAAA,EAG1B,GAAIG,EAAI,SAAW,GAAKA,EAAI,SAAW,GAAKA,EAAI,SAAW,EACzD,OAAOA,EAAI,YAAA,EAIb,MAAMC,EADSD,EAAI,MAAM,GAAG,EAAE,OAAOJ,CAAQ,EACrB,IAAIM,GAAQA,EAAK,OAAO,CAAC,EAAE,YAAA,CAAa,EAAE,KAAK,EAAE,EAEzE,OAAIH,EAAQ,EACHE,EAAS,MAAM,EAAGF,CAAK,EAGzBE,CACT,EAOaE,EAAYC,GAChB,IAAI,QAAQ,CAAC1B,EAAS2B,IAAW,CACtC,MAAMC,EAAS,IAAI,WACnBA,EAAO,cAAcF,CAAI,EACzBE,EAAO,OAAS,IAAM5B,EAAQ4B,EAAO,MAAgB,EACrDA,EAAO,QAAUC,GAASF,EAAOE,CAAK,CACxC,CAAC,EASUC,EAAuB,CAAIC,EAAYC,IAAsB,CACxE,MAAMC,EAAgB,CAAA,EAEtB,QAASC,EAAI,EAAGA,EAAIH,EAAM,OAAQG,GAAKF,EACrCC,EAAO,KAAKF,EAAM,MAAMG,EAAGA,EAAIF,CAAS,CAAC,EAG3C,OAAOC,CACT,EAQaE,EAAgB,CAC3BtB,EACAM,EACAiB,EAAgB,KACb,CAC4B,OAAO,yBACpC,iBAAiB,UACjB,OAAA,GACC,KAEqB,KAAKvB,EAAOM,CAAK,EAGzCiB,GAAiBvB,GAAO,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,EAAA,CAAM,CAAC,CAC7E,EASawB,EAAiB,CAACN,EAAiBO,EAAW,EAAGC,EAAW,QAChER,EACJ,MAAM,EAAGO,CAAQ,EACjB,KAAK,IAAI,EACT,QAAQ,aAAc,IAAIC,CAAQ,KAAK,EA6B/BC,EAAW,MAAqBC,GAA+C,CAC1F,GAAI,CAEF,MAAO,CAAE,KADI,MAAMA,EACJ,MAAO,IAAA,CACxB,OAASZ,EAAO,CACd,MAAO,CAAE,KAAM,KAAM,MAAAA,CAAA,CACvB,CACF,EAOaa,EAAuBC,GAAwC,CAC1E,GAAIA,IAAQ,KAKZ,IAAIA,GAAK,YAAY,OAAS,SAC5B,UAAWzC,KAAOyC,EAChBA,EAAIzC,CAAG,EAAIwC,EAAiBC,EAAIzC,CAAG,CAAC,EAIxC,OAAOyC,EACT,EAiBaC,EAAkB,CAACC,EAAkBC,IACzCA,EAAS,KAAKC,GAAW,CAE9B,KAAM,CAACC,EAAMC,CAAO,EAAIJ,EAAS,MAAM,GAAG,EACpC,CAACK,EAAOC,CAAQ,EAAIJ,EAAQ,MAAM,GAAG,EAG3C,OAAIC,IAASE,EAAc,GAGvBC,IAAa,IAAY,GAGtBF,IAAYE,CACrB,CAAC,EClRUC,EAAe,MAC1BrB,EACAsB,EACAC,IACiB,CACjB,KAAM,CAAE,UAAAC,EAAW,YAAAC,EAAcD,EAAW,MAAAxD,EAAQ,GAAMuD,EAE1D,GAAIvB,EAAM,SAAW,EAAG,MAAO,CAAA,EAE/B,MAAM0B,EAAe,CAAA,EACfC,EAAU5B,EAAqBC,EAAOwB,CAAS,EAErD,SAAW,CAACrB,EAAGyB,CAAK,IAAKD,EAAQ,UAAW,CAC1C,QAAQ,IAAI,oBAAoBxB,EAAI,CAAC,IAAIwB,EAAQ,MAAM,KAAKC,EAAM,MAAM,SAAS,EAGjF,MAAMC,EAAe,MAAMC,EAAuBF,EAAON,EAAWG,CAAW,EAC/EC,EAAQ,KAAK,GAAGG,CAAY,EAGxB7D,EAAQ,GAAKmC,EAAIwB,EAAQ,OAAS,IACpC,QAAQ,IAAI,WAAW3D,CAAK,yBAAyB,EACrD,MAAMD,EAAMC,CAAK,EAErB,CAEA,OAAO0D,CACT,EAKaK,EAAgC,MAC3C/B,EACAsB,EACAC,IAG8B,CAC9B,KAAM,CAAE,QAAAS,GAAYT,EAYpB,OAAOF,EAAarB,EAVK,MAAOiC,GAAgC,CAC9D,GAAI,CACF,OAAO,MAAMX,EAAUW,CAAI,CAC7B,OAASnC,EAAO,CACd,MAAMoC,EAAMpC,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EACpE,OAAAkC,IAAUE,EAAKD,CAAI,EACZC,CACT,CACF,EAE6CX,CAAO,CACtD,EAKMO,EAAyB,MAC7B9B,EACAsB,EACAG,IACiB,CACjB,GAAIA,GAAezB,EAAM,OAEvB,OAAO,QAAQ,IAAIA,EAAM,IAAIsB,CAAS,CAAC,EAGzC,MAAMI,EAAe,CAAA,EACfS,EAA6B,CAAA,EAEnC,UAAWF,KAAQjC,EAAO,CACxB,MAAMU,EAAUY,EAAUW,CAAI,EAAE,KAAKG,GAAU,CAC7CV,EAAQ,KAAKU,CAAM,CACrB,CAAC,EAEDD,EAAU,KAAKzB,CAAO,EAGlByB,EAAU,QAAUV,IACtB,MAAM,QAAQ,KAAKU,CAAS,EAE5BA,EAAU,OAAO,EAAGA,EAAU,OAASA,EAAU,OAAOE,GAAKA,IAAM3B,CAAO,EAAE,MAAM,EAEtF,CAGA,aAAM,QAAQ,IAAIyB,CAAS,EAEpBT,CACT,EC7FaY,EAAgBC,GAA0B,CAErD,MAAMC,EAAMD,EAAK,QAAQ,IAAK,EAAE,EAAE,UAAU,EAAG,CAAC,EAG1CE,EAAI,OAAO,SAASD,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EAC3CE,EAAI,OAAO,SAASF,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EAC3CG,EAAI,OAAO,SAASH,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EAKjD,OAFmBC,EAAI,KAAQC,EAAI,KAAQC,EAAI,KAE3B,GACtB,ECfaC,EAAsB1D,GAAgB,CACjD,MAAM2D,EAAK,SAAS,eAAe3D,GAAM,EAAE,EAC3C,GAAI,CAAC2D,EAAI,OAET,MAAMC,EAAQ,OAAO,iBAAiBD,CAAE,EAClCE,EAAW,OAAO,WAAWD,EAAM,eAAe,EAClDE,EAAM,KAAK,MAAM,OAAO,QAAUH,EAAG,sBAAA,EAAwB,IAAME,CAAQ,EAEjF,MAAO,CAAE,GAAA7D,EAAI,IAAA8D,CAAA,CACf,ECEaC,EAAsBnD,GAE/B,OAAOA,GAAU,UACjBA,IAAU,MACV,YAAaA,GACb,OAAQA,EAAkC,SAAY,SAW7CoD,EAAsBC,GAA0C,CAC3E,GAAIF,EAAmBE,CAAU,EAAG,OAAOA,EAE3C,GAAI,CACF,OAAO,IAAI,MAAM,KAAK,UAAUA,CAAU,CAAC,CAC7C,MAAQ,CAGN,OAAO,IAAI,MAAM,OAAOA,CAAU,CAAC,CACrC,CACF,EASaC,EAAmBtD,GACvBoD,EAAmBpD,CAAK,EAAE,QC3CtBuD,EAAY,CAACC,EAAmBC,IAAiD,CAC5F,SAAS,iBAAiBD,EAAWC,CAAQ,CAC/C,EAOaC,EAAc,CAACF,EAAmBC,IAAiD,CAC9F,SAAS,oBAAoBD,EAAWC,CAAQ,CAClD,EAOaE,EAAU,CAACH,EAAmBI,IAAkB,CAC3D,MAAMC,EAAQ,IAAI,YAAYL,EAAW,CAAE,OAAQI,EAAM,EACzD,SAAS,cAAcC,CAAK,CAC9B,EAKaC,EAAgB,IAAM,CACjC,MAAMD,EAAQ,IAAI,cAAc,UAAW,CAAE,IAAK,SAAU,EAC5D,SAAS,cAAcA,CAAK,CAC9B,ECxBaE,GAAe,CAACC,EAAgBC,EAAqB,UAAWC,EAAS,UAClE,IAAI,KAAK,aAAaA,EAAQ,CAAE,SAAAD,EAAU,EAE3C,OAAOD,CAAM,EASnBG,GAAiB,CAACC,EAAgBC,EAAqB,QAChD,IAAI,KAAK,aAAa,QAAS,CAC/C,MAAO,WACP,SAAAA,CAAA,CACD,EAEgB,OAAOD,CAAM,EAAE,QAAQ,eAAgB,EAAE,EAS/CE,GAAuB,CAACF,EAAgBG,EAA6B,UACzEC,EAAiBJ,GAAUG,IAAa,OAAS,GAAK,GAAI,CAAC,EASvDC,EAAmB,CAACR,EAAgBS,EAAY,IACpDT,EAAO,QAAQS,EAAY,EAAI,EAAIA,CAAS,EAAE,QAAQ,QAAS,EAAE,EAS7DC,GAAc,CAACC,EAAeF,EAAY,IAAc,CAGnE,GAAIE,EAAQ,KAAG,CACb,MAAMC,EAAS,IAAMH,EAErB,MAAO,GADM,KAAK,MAAOE,EAAQ,KAAKC,CAAM,EAAIA,CAClC,KAChB,CAEA,MAAMvE,EAAI,KAAK,MAAM,KAAK,IAAIsE,CAAK,EAAI,KAAK,IAAI,IAAC,CAAC,EAIlD,MAAO,GAHMH,EAAiBG,EAAQ,MAAKtE,EAAGoE,CAAS,CAGzC,IAFA,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAI,EAE1CpE,CAAC,CAAC,EAC5B,EAOawE,GAAkB7D,GAAyC,CACtE,KAAM,CAAA,CAAGI,CAAO,EAAIJ,EAAS,MAAM,GAAG,EACtC,IAAIG,EAEJ,OAAQC,EAAA,CACN,IAAK,IACH,OACF,QACED,EAAOC,CAAA,CAGX,OAAOD,GAAM,YAAA,CACf,ECtFM2D,GACJ,yJAOWC,EAAcC,GAA0B,CAInD,GAHI,CAACA,GAAO,OAAOA,GAAQ,UAGvB,CAACF,GAAU,KAAKE,CAAG,EAAG,MAAO,GAGjC,GAAI,CACF,WAAI,IAAIA,CAAG,EACJ,EACT,MAAQ,CACN,MAAO,EACT,CACF,EAQaC,EAAc,CAACD,EAAcE,IACnCF,EAGDA,EAAI,WAAW,SAAS,GAAKA,EAAI,WAAW,UAAU,EAAUA,EAM7D,GAFLE,IAAW,OAAaA,EAAS,QAAU,OAAUC,EAAeH,CAAG,EAAI,OAAS,OAEpE,MAAMA,CAAG,GATV,GAiBNI,GAAkBJ,GACtBA,GAAK,QAAQ,eAAgB,EAAE,GAAK,GAQhCK,EAAgBL,GAAyB,CACpD,GAAI,CAACA,EAAK,MAAO,GAEjB,IAAIM,EAAaN,EAAI,KAAA,EAGrB,GAAIM,EAAW,SAAS,GAAG,GAAKA,EAAW,SAAS,GAAG,EACrD,GAAI,CACF,MAAMC,EAAY,IAAI,IAAID,CAAU,EAEpC,OAAIC,EAAU,SAAS,OAAS,GAAKA,EAAU,SAAS,SAAS,GAAG,IAClEA,EAAU,SAAWA,EAAU,SAAS,MAAM,EAAG,EAAE,GAE9CA,EAAU,SAAA,CACnB,MAAQ,CAEN,OAAOD,EAAW,OAAS,GAAKA,EAAW,SAAS,GAAG,EACnDA,EAAW,MAAM,EAAG,EAAE,EACtBA,CACN,CAIF,OAAIA,EAAW,OAAS,GAAKA,EAAW,SAAS,GAAG,IAClDA,EAAaA,EAAW,MAAM,EAAG,EAAE,GAG9BA,CACT,EAOaE,GAAcR,GAAwB,CACjD,GAAI,CACF,MAAMO,EAAY,IAAI,IAAIP,CAAG,EAC7B,MAAO,GAAGO,EAAU,QAAQ,KAAKA,EAAU,IAAI,EACjD,MAAQ,CACN,OAAOP,CACT,CACF,EAOaS,GAAaT,GAAwB,CAChD,GAAI,CACF,GAAI,CAACD,EAAWC,CAAG,EAAG,OAAOA,EAE7B,MAAMU,EAAW,IAAI,IAAIV,CAAG,EAAE,SAC9B,OAAOU,EAAS,WAAW,MAAM,EAAIA,EAAS,MAAM,CAAC,EAAIA,CAC3D,MAAQ,CACN,OAAOV,CACT,CACF,EAOaW,GAAiBX,GACvBA,EACE,eAAe,KAAKA,CAAG,EADb,GASNG,EAAkBH,GAA0B,CACvD,GAAI,CAACA,EAAK,MAAO,GAEjB,GAAI,CAEF,MAAMU,EADY,IAAI,IAAIT,EAAYD,CAAG,CAAC,EACf,SAC3B,OAAOU,IAAa,aAAeA,IAAa,aAAeA,EAAS,SAAS,YAAY,CAC/F,MAAQ,CACN,OAAOV,EAAI,SAAS,WAAW,GAAKA,EAAI,SAAS,WAAW,CAC9D,CACF,EAQaY,GAAe,CAACC,KAAiBC,IAA4B,CACxE,GAAI,CAACD,EAAM,MAAO,GAElB,IAAIvD,EAAS+C,EAAaQ,CAAI,EAE9B,UAAWE,KAAQD,EAAO,CACxB,GAAI,CAACC,EAAM,SAEX,MAAMC,EAAYD,EAAK,QAAQ,aAAc,EAAE,EAC3CC,IACF1D,GAAU,IAAI0D,CAAS,GAE3B,CAEA,OAAO1D,CACT,EAOa2D,EAAkBjB,GAAwC,CACrE,GAAI,CACF,MAAMO,EAAY,IAAI,IAAIP,CAAG,EACvBkB,EAAiC,CAAA,EAEvC,OAAAX,EAAU,aAAa,QAAQ,CAACjG,EAAOjB,IAAQ,CAC7C6H,EAAO7H,CAAG,EAAIiB,CAChB,CAAC,EAEM4G,CACT,MAAQ,CACN,MAAO,CAAA,CACT,CACF,EAQaC,GAAiB,CAC5BnB,EACAkB,IACW,CACX,GAAI,CACF,MAAMX,EAAY,IAAI,IAAIP,CAAG,EAE7B,OAAO,QAAQkB,CAAM,EAAE,QAAQ,CAAC,CAAC7H,EAAKiB,CAAK,IAAM,CAC/CiG,EAAU,aAAa,IAAIlH,EAAK,OAAOiB,CAAK,CAAC,CAC/C,CAAC,EAED,IAAIgD,EAASiD,EAAU,SAAA,EAGvB,OAAAjD,EAASA,EAAO,QAAQ,OAAQ,GAAG,EAE5BA,CACT,MAAQ,CACN,OAAO0C,CACT,CACF,EAOaoB,GAAqBpB,GAAyB,CACzD,GAAI,CAACA,EAAK,MAAO,GAEjB,GAAI,CACF,MAAMO,EAAY,IAAI,IAAIP,CAAG,EAC7B,OAAAO,EAAU,OAAS,GACZF,EAAaE,EAAU,UAAU,CAC1C,MAAQ,CAEN,MAAMc,EAAgBrB,EAAI,QAAQ,GAAG,EACrC,OAAOqB,IAAkB,GAAKrB,EAAI,UAAU,EAAGqB,CAAa,EAAIrB,CAClE,CACF,EAmBasB,GAAuB,MAClCtB,EACAvD,EAAuC,KAClB,CACrB,GAAI,CAACuD,EACH,MAAO,GAGT,KAAM,CACJ,QAAAuB,EAAU,IACV,mBAAAC,EAAqB,IACrB,UAAAC,EAAY,0CAAA,EACVhF,EAEEiF,EAAgBrB,EAAaL,CAAG,EAEhC2B,EAAc,MAAOC,GAAqD,CAC9E,MAAMC,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAA,EAASN,CAAO,EAE9D,GAAI,CACF,MAAMQ,GAAW,MAAM,MAAML,EAAe,CAC1C,OAAAE,EACA,OAAQC,EAAW,OACnB,SAAU,SACV,QAAS,CAAE,aAAcJ,CAAA,CAAU,CACpC,EACD,oBAAaK,CAAS,EACfC,EACT,MAAQ,CACN,oBAAaD,CAAS,EACf,IACT,CACF,EAEME,EAAe,MAAML,EAAY,MAAM,EAE7C,GAAIK,GAAgBA,EAAa,OAASR,EACxC,MAAO,GAGT,MAAMS,EAAc,MAAMN,EAAY,KAAK,EAC3C,OAAOM,IAAgB,MAAQA,EAAY,OAAST,CACtD,EClSaU,GAAoB,CAAC5H,EAAe6H,EAAcC,IACzDD,IAAQ,QAAaC,IAAQ,OACxB,KAAK,IAAI,KAAK,IAAI9H,EAAO6H,CAAG,EAAGC,CAAG,EAEvCD,IAAQ,OACH,KAAK,IAAI7H,EAAO6H,CAAG,EAExBC,IAAQ,OACH,KAAK,IAAI9H,EAAO8H,CAAG,EAGrB9H,EAQI+H,GAAqB/H,GAAmC,CACnE,GAA2BA,GAAU,KAAM,OAC3C,MAAMgI,EAAS,OAAO,WAAWhI,EAAM,UAAU,EAEjD,OAAO,OAAO,MAAMgI,CAAM,EAAI,OAAYA,CAC5C,EAQaC,GAAe,CAACjI,EAAekI,EAAW,IAAM,CAC3D,MAAM5C,EAAS,IAAM4C,EAErB,OAAO,KAAK,OAAOlI,EAAQ,OAAO,SAAWsF,CAAM,EAAIA,CACzD,ECtCa6C,GAAgB,CAAC3G,EAA+B,KACpDA,EAAI,cAAgB,QAAU,CAAC,OAAO,QAAQA,CAAG,EAAE,OAS/C4G,GAAgB,CAAmBrJ,EAAkByC,IACzDzC,KAAOyC,EAUH6G,GAAkBC,GACtB,CAACC,EAA4BhF,IAA+B,CACjE,MAAMiF,EAASF,EAAK,QAAQ,OAAO,KAAKC,CAAC,EAAE,CAAC,GAAK,EAAE,EAC7CE,EAASH,EAAK,QAAQ,OAAO,KAAK/E,CAAC,EAAE,CAAC,GAAK,EAAE,EAEnD,OAAIiF,IAAW,IAAMC,IAAW,GAAW,EACvCD,IAAW,GAAW,EACtBC,IAAW,GAAW,GAEnBD,EAASC,CAClB,EASWC,GAAa,CACxBlH,EACAmH,IAEO,OAAO,KAAKnH,CAAG,EACnB,KAAKmH,CAAU,EACf,OAAO,CAAC3F,EAAQjE,KACR,CAAE,GAAGiE,EAAQ,CAACjE,CAAQ,EAAGyC,EAAIzC,CAAQ,CAAA,GAC3C,CAAA,CAAO,EAgBD6J,GAAiB,CAC5BpH,EACA8G,IACe,CACf,MAAMtF,EAAS,CAAA,EACf,UAAWjE,KAAOuJ,EACZvJ,KAAOyC,IACTwB,EAAOjE,CAAG,EAAIyC,EAAIzC,CAAG,GAGzB,OAAOiE,CACT,EChEa6F,EAAkBC,GACtB,KAAK,IAAIA,GAAQ,CAAC,OAAO,MAAM,OAAOA,CAAI,CAAC,EAAI,OAAO,SAASA,GAAQ,IAAK,EAAE,EAAI,EAAG,CAAC,EAUlFC,GAAgB,CAAmBrD,EAAasD,IAAiB,CAC5E,KAAM,CAAE,KAAAF,EAAM,GAAGlC,CAAA,EAAWD,EAAejB,CAAG,EAGxCuD,GADcJ,EAAeC,CAAI,EACX,GAAKE,EAEjC,MAAO,CAAE,KAAAA,EAAM,KAAAC,EAAM,GAAGrC,CAAA,CAC1B,EASasC,GAAc,CAACC,EAA+BC,EAAkBN,KAC3EK,EAAa,IAAI,OAAQL,EAAK,SAAA,CAAU,EAEjC,GAAGM,CAAQ,IAAID,EAAa,UAAU,ICxClCE,GAAqBrJ,GAAkB,CAClD,GAAI,CACF,OAAO,KAAK,MAAMA,CAAK,CACzB,MAAQ,CACN,OAAOA,CACT,CACF,EAOasJ,GAAsBtJ,GAC7B,OAAOA,GAAU,SACZ,KAAK,UAAUA,CAAK,EAGtBA,ECpBIuJ,GAAiB,IACrB,KAAK,MAAM,KAAK,OAAA,EAAW,QAAQ,EACvC,SAAS,EAAE,EACX,SAAS,EAAG,GAAG,EAQPC,GAAkB,CAAChL,EAAS,KAAe,CACtD,MAAMiL,EAAQ,iEACd,OAAO,MAAM,KAAK,CAAE,OAAAjL,GAAU,IAAMiL,EAAM,KAAK,MAAM,KAAK,OAAA,EAAWA,EAAM,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAC9F,EASaC,GAAkB,CAAC7B,EAAaC,IACpC,KAAK,MAAM,KAAK,OAAA,GAAYA,EAAMD,EAAM,EAAE,EAAIA,EAQ1C8B,GAAmBnL,GACvB,MAAM,KAAK,CAAE,OAAAA,CAAA,EAAU,IAAM,KAAK,MAAM,KAAK,SAAW,EAAE,CAAC,EAAE,KAAK,EAAE,EAShEoL,GAAuBC,GAAkB,CACpD,MAAMC,EAAQ,KAAK,MAAM,KAAK,OAAA,EAAWD,EAAM,MAAM,EACrD,OAAOA,EAAMC,CAAK,CACpB,EAOaC,GAAwBvI,GAA8B,CACjE,MAAM8G,EAAO,OAAO,KAAK9G,CAAG,EACtBwI,EAAY1B,EAAK,KAAK,MAAMA,EAAK,OAAS,KAAK,OAAA,CAAQ,CAAC,EAE9D,OAAO9G,EAAIwI,CAAS,CACtB,ECxDaC,GAAW/K,GAClB,OAAOA,GAAW,SACb,GAGLA,EAAO,SAAW,EACbA,EAGFA,EAAO,CAAC,GAAG,cAAgBA,EAAO,MAAM,CAAC,EAQrCgL,GAAWhL,GAClB,OAAOA,GAAW,SACb,GAGLA,EAAO,SAAW,EACbA,EAGFA,EAAO,CAAC,GAAG,cAAgBA,EAAO,MAAM,CAAC,EC5B5CiL,EAAmB,CAACvF,EAAgBwF,IACjC,IAAI,KAAK,eAAexF,EAAQ,CAAE,UAAAwF,EAAW,EAGhDC,GAAmB,CAACzF,EAAgB0F,IACjC,IAAI,KAAK,eAAe1F,EAAQ,CAAE,UAAA0F,EAAW,EAGhDC,GAAuB,CAAC3F,EAAgBwF,EAAsBE,IAC3D,IAAI,KAAK,eAAe1F,EAAQ,CAAE,UAAAwF,EAAW,UAAAE,EAAW,EAUpDE,EAAa,CACxBC,EACAL,EAAuB,SACvBxF,EAAS,UAEFuF,EAAiBvF,EAAQwF,CAAS,EAAE,OAAO,IAAI,KAAKK,CAAS,CAAC,EAU1DC,EAAa,CACxBD,EACAH,EAAuB,QACvB1F,EAAS,UAEFyF,GAAiBzF,EAAQ0F,CAAS,EAAE,OAAO,IAAI,KAAKG,CAAS,CAAC,EAW1DE,EAAiB,CAC5BF,EACAL,EAAuB,SACvBE,EAAuB,QACvB1F,EAAS,UAEF2F,GAAqB3F,EAAQwF,EAAWE,CAAS,EAAE,OAAO,IAAI,KAAKG,CAAS,CAAC,EAYzEG,GAAmB,CAC9BH,EACA5I,EACAuI,EAAuB,SACvBE,EAAuB,QACvB1F,EAAS,UACN,CACH,OAAQ/C,EAAA,CACN,IAAK,OACH,OAAO2I,EAAWC,EAAWL,EAAWxF,CAAM,EAChD,IAAK,OACH,OAAO8F,EAAWD,EAAWH,EAAW1F,CAAM,EAChD,QACE,OAAO+F,EAAeF,EAAWL,EAAWE,EAAW1F,CAAM,CAAA,CAEnE,EAUaiG,GAAkB,CAC7BvM,EACAC,EACA6L,EAAuB,SACvBxF,EAAS,UAEFuF,EAAiBvF,EAAQwF,CAAS,EAAE,YAAY,IAAI,KAAK9L,CAAK,EAAG,IAAI,KAAKC,CAAG,CAAC,EAS1EuM,GAAc,CAACxL,EAAwByL,EAAM,MACnDzL,EAIE,KAAK,KAAKA,EAAQ,KAAA,EAAO,MAAM,KAAK,EAAE,OAASyL,CAAG,EAHhD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primoui/utils",
|
|
3
3
|
"description": "A lightweight set of utilities",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": {
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
"email": "piotr@kulpinski.pl",
|
|
10
10
|
"url": "https://kulpinski.pl"
|
|
11
11
|
},
|
|
12
|
-
"repository":
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "https://github.com/primoui/utils.git"
|
|
15
|
-
},
|
|
12
|
+
"repository": "git+https://github.com/primoui/utils.git",
|
|
16
13
|
"files": [
|
|
17
14
|
"dist"
|
|
18
15
|
],
|
|
@@ -29,7 +26,7 @@
|
|
|
29
26
|
"clean": "rimraf ./dist",
|
|
30
27
|
"build": "vite build && tsc --emitDeclarationOnly",
|
|
31
28
|
"prebuild": "bun run clean",
|
|
32
|
-
"lint": "bun biome
|
|
29
|
+
"lint": "bun biome check --write .",
|
|
33
30
|
"format": "bun biome format --write ."
|
|
34
31
|
},
|
|
35
32
|
"dependencies": {
|