@primoui/utils 1.3.3 → 1.3.4
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/format/format.d.ts.map +1 -1
- package/dist/index.js +47 -45
- 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 +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/format/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAA;AACpD,KAAK,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAA;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,WAAU,QAAoB,EAAE,eAAgB,WAI5F,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,WAAU,QAAgB,WAOxE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,EAAE,WAAU,OAAO,GAAG,MAAgB,WAExF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,kBAAa,KAAG,MAEhE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,EAAE,kBAAa,KAAG,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/format/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,KAAK,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAA;AACpD,KAAK,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAA;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,WAAU,QAAoB,EAAE,eAAgB,WAI5F,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,WAAU,QAAgB,WAOxE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,EAAE,WAAU,OAAO,GAAG,MAAgB,WAExF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,kBAAa,KAAG,MAEhE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,EAAE,kBAAa,KAAG,MAc1D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,GAAG,SAY1D,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import d from "@sindresorhus/slugify";
|
|
2
|
-
const
|
|
2
|
+
const A = (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)), H = ({ key: t, metaKey: e }) => `${e ? "⌘" : ""}${t.toUpperCase()}`,
|
|
5
|
+
}, y = async (t) => new Promise((e) => setTimeout(e, t)), H = ({ key: t, metaKey: e }) => `${e ? "⌘" : ""}${t.toUpperCase()}`, w = (t) => t.replace(/<[^>]*>?/gm, ""), b = (t, e = " ") => t.replace(/\n+/g, e), K = (t, e = 250) => {
|
|
6
6
|
if (!t)
|
|
7
7
|
return null;
|
|
8
|
-
const r = w(
|
|
8
|
+
const r = b(w(t)), n = r.slice(0, e).trim();
|
|
9
9
|
return n.length < r.length ? `${n}...` : n;
|
|
10
|
-
},
|
|
10
|
+
}, J = (t, e = !1) => d(t, {
|
|
11
11
|
decamelize: e,
|
|
12
12
|
customReplacements: [
|
|
13
13
|
["#", "sharp"],
|
|
@@ -108,10 +108,12 @@ const z = (t, e) => {
|
|
|
108
108
|
style: "currency",
|
|
109
109
|
currency: e
|
|
110
110
|
}).format(t).replace(/\D00(?=\D*$)/, ""), ut = (t, e = "month") => h(t / (e === "year" ? 12 : 1), 2), h = (t, e = 0) => t.toFixed(e < 0 ? 0 : e).replace(/\.0+$/, ""), lt = (t, e = 0) => {
|
|
111
|
-
if (t
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
if (t < 1024) {
|
|
112
|
+
const c = 10 ** e;
|
|
113
|
+
return `${Math.floor(t / 1024 * c) / c} KB`;
|
|
114
|
+
}
|
|
115
|
+
const n = Math.floor(Math.log(t) / Math.log(1024));
|
|
116
|
+
return `${h(t / 1024 ** n, e)} ${["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][n]}`;
|
|
115
117
|
}, ht = (t) => {
|
|
116
118
|
const [, e] = t.split("/");
|
|
117
119
|
let r;
|
|
@@ -129,7 +131,7 @@ const z = (t, e) => {
|
|
|
129
131
|
} catch {
|
|
130
132
|
return !1;
|
|
131
133
|
}
|
|
132
|
-
}, m = (t, e) => t ? t.startsWith("http://") || t.startsWith("https://") ? t : `${e !== void 0 ? e ? "https" : "http" :
|
|
134
|
+
}, m = (t, e) => t ? t.startsWith("http://") || t.startsWith("https://") ? t : `${e !== void 0 ? e ? "https" : "http" : T(t) ? "http" : "https"}://${t}` : "", O = (t) => t?.replace(/^https?:\/\//, "") ?? "", i = (t) => {
|
|
133
135
|
if (!t) return "";
|
|
134
136
|
let e = t.trim();
|
|
135
137
|
if (e.includes("?") || e.includes("#"))
|
|
@@ -140,14 +142,14 @@ const z = (t, e) => {
|
|
|
140
142
|
return e.length > 1 && e.endsWith("/") ? e.slice(0, -1) : e;
|
|
141
143
|
}
|
|
142
144
|
return e.length > 1 && e.endsWith("/") && (e = e.slice(0, -1)), e;
|
|
143
|
-
},
|
|
145
|
+
}, $ = (t) => {
|
|
144
146
|
try {
|
|
145
147
|
const e = new URL(t);
|
|
146
148
|
return `${e.protocol}//${e.host}`;
|
|
147
149
|
} catch {
|
|
148
150
|
return t;
|
|
149
151
|
}
|
|
150
|
-
},
|
|
152
|
+
}, D = (t) => {
|
|
151
153
|
try {
|
|
152
154
|
if (!I(t)) return t;
|
|
153
155
|
const e = new URL(t).hostname;
|
|
@@ -155,7 +157,7 @@ const z = (t, e) => {
|
|
|
155
157
|
} catch {
|
|
156
158
|
return t;
|
|
157
159
|
}
|
|
158
|
-
}, mt = (t) => t ? /^https?:\/\//.test(t) : !1,
|
|
160
|
+
}, mt = (t) => t ? /^https?:\/\//.test(t) : !1, T = (t) => {
|
|
159
161
|
if (!t) return !1;
|
|
160
162
|
try {
|
|
161
163
|
const r = new URL(m(t)).hostname;
|
|
@@ -201,7 +203,7 @@ const z = (t, e) => {
|
|
|
201
203
|
const e = t.indexOf("?");
|
|
202
204
|
return e !== -1 ? t.substring(0, e) : t;
|
|
203
205
|
}
|
|
204
|
-
}, ft = m, gt = O, dt = i, yt = i,
|
|
206
|
+
}, ft = m, gt = O, dt = i, yt = i, wt = $, bt = D, Ut = B, St = p, Mt = L, Pt = (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, Et = (t) => {
|
|
205
207
|
if (t == null) return;
|
|
206
208
|
const e = Number.parseFloat(t.toString());
|
|
207
209
|
return Number.isNaN(e) ? void 0 : e;
|
|
@@ -211,101 +213,101 @@ const z = (t, e) => {
|
|
|
211
213
|
}, vt = (t = {}) => t.constructor === Object && !Object.entries(t).length, Nt = (t, e) => t in e, It = (t) => (e, r) => {
|
|
212
214
|
const n = t.indexOf(Object.keys(e)[0] ?? ""), s = t.indexOf(Object.keys(r)[0] ?? "");
|
|
213
215
|
return n === -1 && s === -1 ? 0 : n === -1 ? 1 : s === -1 ? -1 : n - s;
|
|
214
|
-
}, Ot = (t, e) => Object.keys(t).sort(e).reduce((r, n) => ({ ...r, [n]: t[n] }), {}),
|
|
216
|
+
}, Ot = (t, e) => Object.keys(t).sort(e).reduce((r, n) => ({ ...r, [n]: t[n] }), {}), $t = (t, e) => {
|
|
215
217
|
const r = {};
|
|
216
218
|
for (const n of e)
|
|
217
219
|
n in t && (r[n] = t[n]);
|
|
218
220
|
return r;
|
|
219
|
-
}, C = (t) => Math.max(t && !Number.isNaN(Number(t)) ? Number.parseInt(t || "1", 10) : 1, 1),
|
|
221
|
+
}, C = (t) => Math.max(t && !Number.isNaN(Number(t)) ? Number.parseInt(t || "1", 10) : 1, 1), Dt = (t, e) => {
|
|
220
222
|
const { page: r, ...n } = p(t), o = (C(r) - 1) * e;
|
|
221
223
|
return { take: e, skip: o, ...n };
|
|
222
|
-
},
|
|
224
|
+
}, Tt = (t, e, r) => (t.set("page", r.toString()), `${e}?${t.toString()}`), Lt = (t) => {
|
|
223
225
|
try {
|
|
224
226
|
return JSON.parse(t);
|
|
225
227
|
} catch {
|
|
226
228
|
return t;
|
|
227
229
|
}
|
|
228
|
-
}, Bt = (t) => typeof t == "object" ? JSON.stringify(t) : t, Ct = () => Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0"),
|
|
230
|
+
}, Bt = (t) => typeof t == "object" ? JSON.stringify(t) : t, Ct = () => Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0"), kt = (t = 16) => {
|
|
229
231
|
const e = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
230
232
|
return Array.from({ length: t }, () => e[Math.floor(Math.random() * e.length)]).join("");
|
|
231
|
-
},
|
|
233
|
+
}, xt = (t, e) => Math.floor(Math.random() * (e - t + 1)) + t, Ft = (t) => Array.from({ length: t }, () => Math.floor(Math.random() * 10)).join(""), jt = (t) => {
|
|
232
234
|
const e = Math.floor(Math.random() * t.length);
|
|
233
235
|
return t[e];
|
|
234
236
|
}, Wt = (t) => {
|
|
235
237
|
const e = Object.keys(t), r = e[Math.floor(e.length * Math.random())];
|
|
236
238
|
return t[r];
|
|
237
|
-
},
|
|
239
|
+
}, zt = (t) => typeof t != "string" ? "" : t.length === 0 ? t : t[0]?.toUpperCase() + t.slice(1), At = (t) => typeof t != "string" ? "" : t.length === 0 ? t : t[0]?.toLowerCase() + t.slice(1), f = (t, e) => new Intl.DateTimeFormat(t, { dateStyle: e }), k = (t, e) => new Intl.DateTimeFormat(t, { timeStyle: e }), x = (t, e, r) => new Intl.DateTimeFormat(t, { dateStyle: e, timeStyle: r }), F = (t, e = "medium", r = "en-US") => f(r, e).format(new Date(t)), j = (t, e = "short", r = "en-US") => k(r, e).format(new Date(t)), W = (t, e = "medium", r = "short", n = "en-US") => x(n, e, r).format(new Date(t)), Ht = (t, e, r = "medium", n = "short", s = "en-US") => {
|
|
238
240
|
switch (e) {
|
|
239
241
|
case "date":
|
|
240
|
-
return
|
|
242
|
+
return F(t, r, s);
|
|
241
243
|
case "time":
|
|
242
|
-
return
|
|
244
|
+
return j(t, n, s);
|
|
243
245
|
default:
|
|
244
246
|
return W(t, r, n, s);
|
|
245
247
|
}
|
|
246
|
-
},
|
|
248
|
+
}, Kt = (t, e, r = "medium", n = "en-US") => f(n, r).formatRange(new Date(t), new Date(e)), Jt = (t, e = 265) => t ? Math.ceil(t.trim().split(/\s+/).length / e) : 0;
|
|
247
249
|
export {
|
|
248
250
|
ft as addHttp,
|
|
249
251
|
m as addProtocol,
|
|
250
252
|
Mt as addSearchParams,
|
|
251
|
-
|
|
253
|
+
b as convertNewlines,
|
|
252
254
|
lt as formatBytes,
|
|
253
255
|
it as formatCurrency,
|
|
254
|
-
|
|
256
|
+
F as formatDate,
|
|
255
257
|
Ht as formatDateOrTime,
|
|
256
|
-
|
|
258
|
+
Kt as formatDateRange,
|
|
257
259
|
W as formatDateTime,
|
|
258
260
|
ut as formatIntervalAmount,
|
|
259
261
|
ht as formatMimeType,
|
|
260
262
|
at as formatNumber,
|
|
261
|
-
|
|
263
|
+
j as formatTime,
|
|
262
264
|
h as formatToDecimals,
|
|
263
|
-
|
|
265
|
+
$ as getBaseUrl,
|
|
264
266
|
C as getCurrentPage,
|
|
265
|
-
|
|
267
|
+
D as getDomain,
|
|
266
268
|
et as getElementPosition,
|
|
267
269
|
rt as getErrorMessage,
|
|
268
|
-
|
|
270
|
+
K as getExcerpt,
|
|
269
271
|
Z as getInitials,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
+
Tt as getPageLink,
|
|
273
|
+
Dt as getPageParams,
|
|
272
274
|
p as getQueryParams,
|
|
273
275
|
Ct as getRandomColor,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
276
|
+
Ft as getRandomDigits,
|
|
277
|
+
jt as getRandomElement,
|
|
278
|
+
xt as getRandomNumber,
|
|
277
279
|
Wt as getRandomProperty,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
+
kt as getRandomString,
|
|
281
|
+
Jt as getReadTime,
|
|
280
282
|
St as getSearchParams,
|
|
281
283
|
H as getShortcutLabel,
|
|
282
|
-
|
|
284
|
+
bt as getUrlHostname,
|
|
283
285
|
V as isCuid,
|
|
284
286
|
vt as isEmptyObject,
|
|
285
287
|
R as isErrorWithMessage,
|
|
286
288
|
mt as isExternalUrl,
|
|
287
289
|
Nt as isKeyInObject,
|
|
288
290
|
tt as isLightColor,
|
|
289
|
-
|
|
291
|
+
T as isLocalhostUrl,
|
|
290
292
|
q as isMimeTypeMatch,
|
|
291
293
|
U as isTruthy,
|
|
292
294
|
I as isValidUrl,
|
|
293
295
|
G as joinAsSentence,
|
|
294
296
|
pt as joinUrlPaths,
|
|
295
297
|
Pt as keepNumberInRange,
|
|
296
|
-
|
|
298
|
+
At as lcFirst,
|
|
297
299
|
Lt as maybeParseJson,
|
|
298
300
|
Bt as maybeStringifyJson,
|
|
299
301
|
i as normalizeUrl,
|
|
300
302
|
M as nullsToUndefined,
|
|
301
303
|
Et as parseNumericValue,
|
|
302
|
-
|
|
304
|
+
$t as pickFromObject,
|
|
303
305
|
Rt as preciseRound,
|
|
304
306
|
P as processBatch,
|
|
305
307
|
X as processBatchWithErrorHandling,
|
|
306
308
|
ot as publish,
|
|
307
309
|
ct as publishEscape,
|
|
308
|
-
|
|
310
|
+
A as range,
|
|
309
311
|
gt as removeHttp,
|
|
310
312
|
O as removeProtocol,
|
|
311
313
|
B as removeQueryParams,
|
|
@@ -314,18 +316,18 @@ export {
|
|
|
314
316
|
_ as setInputValue,
|
|
315
317
|
L as setQueryParams,
|
|
316
318
|
y as sleep,
|
|
317
|
-
|
|
319
|
+
J as slugify,
|
|
318
320
|
Ot as sortObject,
|
|
319
321
|
It as sortObjectKeys,
|
|
320
322
|
S as splitArrayIntoChunks,
|
|
321
|
-
|
|
323
|
+
w as stripHtml,
|
|
322
324
|
yt as stripTrailingSlash,
|
|
323
|
-
|
|
325
|
+
wt as stripURLSubpath,
|
|
324
326
|
nt as subscribe,
|
|
325
327
|
Q as toBase64,
|
|
326
328
|
v as toErrorWithMessage,
|
|
327
329
|
Y as tryCatch,
|
|
328
|
-
|
|
330
|
+
zt as ucFirst,
|
|
329
331
|
st as unsubscribe
|
|
330
332
|
};
|
|
331
333
|
//# 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 if (bytes === 0) {\n return \"0 Bytes\"\n }\n\n const k = 1024\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"Bytes\", \"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","k","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","factor","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// 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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"}
|
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 k=(t,e)=>{const n=e-t+1;return Array.from({length:n},(s,o)=>o+t)},g=async t=>new Promise(e=>setTimeout(e,t)),F=({key:t,metaKey:e})=>`${e?"⌘":""}${t.toUpperCase()}`,d=t=>t.replace(/<[^>]*>?/gm,""),y=(t,e=" ")=>t.replace(/\n+/g,e),W=(t,e=250)=>{if(!t)return null;const n=y(d(t)),s=n.slice(0,e).trim();return s.length<n.length?`${s}...`:s},A=(t,e=!1)=>u(t,{decamelize:e,customReplacements:[["#","sharp"],["+","plus"]]}),z=t=>t.length===25&&t[0]==="c",p=t=>!!t,H=(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(p).map(a=>a.charAt(0).toUpperCase()).join("");return e>0?o.slice(0,e):o},K=t=>new Promise((e,n)=>{const s=new FileReader;s.readAsDataURL(t),s.onload=()=>e(s.result),s.onerror=o=>n(o)}),b=(t,e)=>{const n=[];for(let s=0;s<t.length;s+=e)n.push(t.slice(s,s+e));return n},V=(t,e,n=!1)=>{Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"value")?.set?.call(t,e),n&&t?.dispatchEvent(new Event("input",{bubbles:!0}))},J=(t,e=3,n="and")=>t.slice(0,e).join(", ").replace(/, ([^,]*)$/,` ${n} $1`),Q=async t=>{try{return{data:await t,error:null}}catch(e){return{data:null,error:e}}},w=t=>{if(t!==null){if(t?.constructor.name==="Object")for(const e in t)t[e]=w(t[e]);return t}},Z=(t,e)=>e.some(n=>{const[s,o]=t.split("/"),[a,c]=n.split("/");return s!==a?!1:c==="*"?!0:o===c}),P=async(t,e,n)=>{const{batchSize:s,concurrency:o=s,delay:a=0}=n;if(t.length===0)return[];const c=[],i=b(t,s);for(const[j,B]of i.entries()){console.log(`Processing batch ${j+1}/${i.length} (${B.length} items)`);const Jt=await q(B,e,o);c.push(...Jt),a>0&&j<i.length-1&&(console.log(`Waiting ${a}ms before next batch...`),await g(a))}return c},_=async(t,e,n)=>{const{onError:s}=n;return P(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},G=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},Y=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}},S=t=>typeof t=="object"&&t!==null&&"message"in t&&typeof t.message=="string",U=t=>{if(S(t))return t;try{return new Error(JSON.stringify(t))}catch{return new Error(String(t))}},X=t=>U(t).message,x=(t,e)=>{document.addEventListener(t,e)},tt=(t,e)=>{document.removeEventListener(t,e)},et=(t,e)=>{const n=new CustomEvent(t,{detail:e});document.dispatchEvent(n)},nt=()=>{const t=new KeyboardEvent("keydown",{key:"Escape"});document.dispatchEvent(t)},rt=(t,e="compact",n="en-US")=>new Intl.NumberFormat(n,{notation:e}).format(t),st=(t,e="USD")=>new Intl.NumberFormat("en-US",{style:"currency",currency:e}).format(t).replace(/\D00(?=\D*$)/,""),ot=(t,e="month")=>m(t/(e==="year"?12:1),2),m=(t,e=0)=>t.toFixed(e<0?0:e).replace(/\.0+$/,""),at=(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`${m(t/1024**s,e)} ${["B","KB","MB","GB","TB","PB","EB","ZB","YB"][s]}`},ct=t=>{const[,e]=t.split("/");let n;switch(e){case"*":return;default:n=e}return n?.toUpperCase()},it=/^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()@:%_+.~#?&/=]*)$/,M=t=>{if(!t||typeof t!="string"||!it.test(t))return!1;try{return new URL(t),!0}catch{return!1}},h=(t,e)=>t?t.startsWith("http://")||t.startsWith("https://")?t:`${e!==void 0?e?"https":"http":v(t)?"http":"https"}://${t}`:"",E=t=>t?.replace(/^https?:\/\//,"")??"",l=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},R=t=>{try{const e=new URL(t);return`${e.protocol}//${e.host}`}catch{return t}},T=t=>{try{if(!M(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,v=t=>{if(!t)return!1;try{const n=new URL(h(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=l(t);for(const s of e){if(!s)continue;const o=s.replace(/^\/+|\/+$/g,"");o&&(n+=`/${o}`)}return n},f=t=>{try{const e=new URL(t),n={};return e.searchParams.forEach((s,o)=>{n[o]=s}),n}catch{return{}}},N=(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}},O=t=>{if(!t)return"";try{const e=new URL(t);return e.search="",l(e.toString())}catch{const e=t.indexOf("?");return e!==-1?t.substring(0,e):t}},mt=h,ht=E,ft=l,gt=l,dt=R,yt=T,pt=O,bt=f,wt=N,Pt=(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,St=t=>{if(t==null)return;const e=Number.parseFloat(t.toString());return Number.isNaN(e)?void 0:e},Ut=(t,e=2)=>{const n=Math.pow(10,e);return Math.round((t+Number.EPSILON)*n)/n},Mt=(t={})=>t.constructor===Object&&!Object.entries(t).length,Et=(t,e)=>t in e,Rt=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},Tt=(t,e)=>Object.keys(t).sort(e).reduce((n,s)=>({...n,[s]:t[s]}),{}),vt=(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),Nt=(t,e)=>{const{page:n,...s}=f(t),a=(D(n)-1)*e;return{take:e,skip:a,...s}},Ot=(t,e,n)=>(t.set("page",n.toString()),`${e}?${t.toString()}`),Dt=t=>{try{return JSON.parse(t)}catch{return t}},It=t=>typeof t=="object"?JSON.stringify(t):t,Lt=()=>Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),Ct=(t=16)=>{const e="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";return Array.from({length:t},()=>e[Math.floor(Math.random()*e.length)]).join("")},$t=(t,e)=>Math.floor(Math.random()*(e-t+1))+t,jt=t=>Array.from({length:t},()=>Math.floor(Math.random()*10)).join(""),Bt=t=>{const e=Math.floor(Math.random()*t.length);return t[e]},kt=t=>{const e=Object.keys(t),n=e[Math.floor(e.length*Math.random())];return t[n]},Ft=t=>typeof t!="string"?"":t.length===0?t:t[0]?.toUpperCase()+t.slice(1),Wt=t=>typeof t!="string"?"":t.length===0?t:t[0]?.toLowerCase()+t.slice(1),I=(t,e)=>new Intl.DateTimeFormat(t,{dateStyle:e}),At=(t,e)=>new Intl.DateTimeFormat(t,{timeStyle:e}),zt=(t,e,n)=>new Intl.DateTimeFormat(t,{dateStyle:e,timeStyle:n}),L=(t,e="medium",n="en-US")=>I(n,e).format(new Date(t)),C=(t,e="short",n="en-US")=>At(n,e).format(new Date(t)),$=(t,e="medium",n="short",s="en-US")=>zt(s,e,n).format(new Date(t)),Ht=(t,e,n="medium",s="short",o="en-US")=>{switch(e){case"date":return L(t,n,o);case"time":return C(t,s,o);default:return $(t,n,s,o)}},Kt=(t,e,n="medium",s="en-US")=>I(s,n).formatRange(new Date(t),new Date(e)),Vt=(t,e=265)=>t?Math.ceil(t.trim().split(/\s+/).length/e):0;r.addHttp=mt,r.addProtocol=h,r.addSearchParams=wt,r.convertNewlines=y,r.formatBytes=at,r.formatCurrency=st,r.formatDate=L,r.formatDateOrTime=Ht,r.formatDateRange=Kt,r.formatDateTime=$,r.formatIntervalAmount=ot,r.formatMimeType=ct,r.formatNumber=rt,r.formatTime=C,r.formatToDecimals=m,r.getBaseUrl=R,r.getCurrentPage=D,r.getDomain=T,r.getElementPosition=Y,r.getErrorMessage=X,r.getExcerpt=W,r.getInitials=H,r.getPageLink=Ot,r.getPageParams=Nt,r.getQueryParams=f,r.getRandomColor=Lt,r.getRandomDigits=jt,r.getRandomElement=Bt,r.getRandomNumber=$t,r.getRandomProperty=kt,r.getRandomString=Ct,r.getReadTime=Vt,r.getSearchParams=bt,r.getShortcutLabel=F,r.getUrlHostname=yt,r.isCuid=z,r.isEmptyObject=Mt,r.isErrorWithMessage=S,r.isExternalUrl=ut,r.isKeyInObject=Et,r.isLightColor=G,r.isLocalhostUrl=v,r.isMimeTypeMatch=Z,r.isTruthy=p,r.isValidUrl=M,r.joinAsSentence=J,r.joinUrlPaths=lt,r.keepNumberInRange=Pt,r.lcFirst=Wt,r.maybeParseJson=Dt,r.maybeStringifyJson=It,r.normalizeUrl=l,r.nullsToUndefined=w,r.parseNumericValue=St,r.pickFromObject=vt,r.preciseRound=Ut,r.processBatch=P,r.processBatchWithErrorHandling=_,r.publish=et,r.publishEscape=nt,r.range=k,r.removeHttp=ht,r.removeProtocol=E,r.removeQueryParams=O,r.removeSearchParams=pt,r.removeTrailingSlash=ft,r.setInputValue=V,r.setQueryParams=N,r.sleep=g,r.slugify=A,r.sortObject=Tt,r.sortObjectKeys=Rt,r.splitArrayIntoChunks=b,r.stripHtml=d,r.stripTrailingSlash=gt,r.stripURLSubpath=dt,r.subscribe=x,r.toBase64=K,r.toErrorWithMessage=U,r.tryCatch=Q,r.ucFirst=Ft,r.unsubscribe=tt,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 if (bytes === 0) {\n return \"0 Bytes\"\n }\n\n const k = 1024\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n const size = formatToDecimals(bytes / k ** i, precision)\n const sizes = [\"Bytes\", \"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","k","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","factor","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// 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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"}
|