voodoojs 0.4.6

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.
Files changed (54) hide show
  1. package/README.md +77 -0
  2. package/dist/chunk-234ZLC6W.js +401 -0
  3. package/dist/chunk-4HQEOXTK.js +10271 -0
  4. package/dist/chunk-5777LJVW.js +64 -0
  5. package/dist/chunk-5CKGDARU.js +1845 -0
  6. package/dist/chunk-A2UOVQBP.js +82 -0
  7. package/dist/chunk-E27NRARW.js +16 -0
  8. package/dist/chunk-JZIYRIY6.js +1196 -0
  9. package/dist/chunk-NNU6WOOU.js +641 -0
  10. package/dist/chunk-PQZEVFVZ.js +448 -0
  11. package/dist/chunk-RJUNPXQF.js +946 -0
  12. package/dist/chunk-U76IRJKH.js +72 -0
  13. package/dist/essential.cjs +13889 -0
  14. package/dist/essential.d.cts +24 -0
  15. package/dist/essential.d.ts +24 -0
  16. package/dist/essential.js +51 -0
  17. package/dist/gpu.cjs +2008 -0
  18. package/dist/gpu.d.cts +68 -0
  19. package/dist/gpu.d.ts +68 -0
  20. package/dist/gpu.js +273 -0
  21. package/dist/http.cjs +467 -0
  22. package/dist/http.d.cts +148 -0
  23. package/dist/http.d.ts +148 -0
  24. package/dist/http.js +7 -0
  25. package/dist/index-CaLD-0oh.d.cts +608 -0
  26. package/dist/index-CaLD-0oh.d.ts +608 -0
  27. package/dist/index-DTllqUtj.d.cts +261 -0
  28. package/dist/index-DTllqUtj.d.ts +261 -0
  29. package/dist/index.cjs +23063 -0
  30. package/dist/index.d.cts +1603 -0
  31. package/dist/index.d.ts +1603 -0
  32. package/dist/index.js +6924 -0
  33. package/dist/query-CKJ4oSpG.d.cts +1595 -0
  34. package/dist/query-DQFRmu3u.d.ts +1595 -0
  35. package/dist/reactivity.cjs +676 -0
  36. package/dist/reactivity.d.cts +188 -0
  37. package/dist/reactivity.d.ts +188 -0
  38. package/dist/reactivity.js +4 -0
  39. package/dist/socket.cjs +2685 -0
  40. package/dist/socket.d.cts +167 -0
  41. package/dist/socket.d.ts +167 -0
  42. package/dist/socket.js +238 -0
  43. package/dist/style-XEUAGGJK.js +5 -0
  44. package/dist/utils.cjs +397 -0
  45. package/dist/utils.d.cts +111 -0
  46. package/dist/utils.d.ts +111 -0
  47. package/dist/utils.js +4 -0
  48. package/dist/voodoo.core.js +8213 -0
  49. package/dist/voodoo.core.min.js +146 -0
  50. package/dist/voodoo.full.js +21193 -0
  51. package/dist/voodoo.full.min.js +1784 -0
  52. package/dist/voodoo.js +14185 -0
  53. package/dist/voodoo.min.js +420 -0
  54. package/package.json +127 -0
package/dist/utils.cjs ADDED
@@ -0,0 +1,397 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Voodoo.js v0.4.6
5
+ * JavaScript feels like magic.
6
+ * (c) 2026 Voodoo.js contributors. MIT License.
7
+ */
8
+
9
+ // src/utils/index.ts
10
+ function uuid() {
11
+ const c = globalThis.crypto;
12
+ if (c?.randomUUID) return c.randomUUID();
13
+ if (c?.getRandomValues) {
14
+ const bytes = c.getRandomValues(new Uint8Array(16));
15
+ bytes[6] = bytes[6] & 15 | 64;
16
+ bytes[8] = bytes[8] & 63 | 128;
17
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
18
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
19
+ }
20
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
21
+ const r = Math.random() * 16 | 0;
22
+ return (ch === "x" ? r : r & 3 | 8).toString(16);
23
+ });
24
+ }
25
+ function uid(prefix = "v") {
26
+ return `${prefix}${Math.random().toString(36).slice(2, 9)}`;
27
+ }
28
+ function sleep(ms) {
29
+ return new Promise((resolve) => setTimeout(resolve, ms));
30
+ }
31
+ function parseDuration(value, fallback = 0) {
32
+ if (value == null || value === "") return fallback;
33
+ if (typeof value === "number") return value;
34
+ const match = /^\s*([\d.]+)\s*(ms|s|m|h)?\s*$/i.exec(String(value));
35
+ if (!match) return fallback;
36
+ const amount = parseFloat(match[1]);
37
+ switch ((match[2] || "ms").toLowerCase()) {
38
+ case "s":
39
+ return amount * 1e3;
40
+ case "m":
41
+ return amount * 6e4;
42
+ case "h":
43
+ return amount * 36e5;
44
+ default:
45
+ return amount;
46
+ }
47
+ }
48
+ function debounce(fn, wait = 250, immediate = false) {
49
+ let timer = null;
50
+ let lastArgs = null;
51
+ let lastThis;
52
+ const debounced = function(...args) {
53
+ lastArgs = args;
54
+ lastThis = this;
55
+ const callNow = immediate && timer === null;
56
+ if (timer) clearTimeout(timer);
57
+ timer = setTimeout(() => {
58
+ timer = null;
59
+ if (!immediate && lastArgs) fn.apply(lastThis, lastArgs);
60
+ }, wait);
61
+ if (callNow) fn.apply(this, args);
62
+ };
63
+ debounced.cancel = () => {
64
+ if (timer) clearTimeout(timer);
65
+ timer = null;
66
+ lastArgs = null;
67
+ };
68
+ debounced.flush = () => {
69
+ if (timer && lastArgs) {
70
+ clearTimeout(timer);
71
+ timer = null;
72
+ fn.apply(lastThis, lastArgs);
73
+ }
74
+ };
75
+ return debounced;
76
+ }
77
+ function throttle(fn, wait = 250) {
78
+ let last = 0;
79
+ let timer = null;
80
+ let lastArgs = null;
81
+ const throttled = function(...args) {
82
+ const now = Date.now();
83
+ lastArgs = args;
84
+ const remaining = wait - (now - last);
85
+ if (remaining <= 0) {
86
+ if (timer) {
87
+ clearTimeout(timer);
88
+ timer = null;
89
+ }
90
+ last = now;
91
+ fn.apply(this, args);
92
+ } else if (!timer) {
93
+ timer = setTimeout(() => {
94
+ last = Date.now();
95
+ timer = null;
96
+ if (lastArgs) fn.apply(this, lastArgs);
97
+ }, remaining);
98
+ }
99
+ };
100
+ throttled.cancel = () => {
101
+ if (timer) clearTimeout(timer);
102
+ timer = null;
103
+ };
104
+ throttled.flush = () => {
105
+ if (timer && lastArgs) {
106
+ clearTimeout(timer);
107
+ timer = null;
108
+ fn.apply(null, lastArgs);
109
+ }
110
+ };
111
+ return throttled;
112
+ }
113
+ function once(fn) {
114
+ let called = false;
115
+ let result;
116
+ return function(...args) {
117
+ if (!called) {
118
+ called = true;
119
+ result = fn.apply(this, args);
120
+ }
121
+ return result;
122
+ };
123
+ }
124
+ function memoize(fn, keyFn = (...args) => JSON.stringify(args)) {
125
+ const cache = /* @__PURE__ */ new Map();
126
+ const memoized = function(...args) {
127
+ const key = keyFn(...args);
128
+ if (cache.has(key)) return cache.get(key);
129
+ const value = fn.apply(this, args);
130
+ cache.set(key, value);
131
+ return value;
132
+ };
133
+ memoized.cache = cache;
134
+ return memoized;
135
+ }
136
+ function clone(value) {
137
+ if (value === null || typeof value !== "object") return value;
138
+ if (typeof structuredClone === "function") {
139
+ try {
140
+ return structuredClone(value);
141
+ } catch {
142
+ }
143
+ }
144
+ if (Array.isArray(value)) return value.map((v) => clone(v));
145
+ if (value instanceof Date) return new Date(value.getTime());
146
+ if (value instanceof Map) return new Map([...value].map(([k, v]) => [k, clone(v)]));
147
+ if (value instanceof Set) return new Set([...value].map((v) => clone(v)));
148
+ const out = {};
149
+ for (const [k, v] of Object.entries(value)) out[k] = clone(v);
150
+ return out;
151
+ }
152
+ function merge(target, ...sources) {
153
+ for (const source of sources) {
154
+ if (!source) continue;
155
+ for (const [key, value] of Object.entries(source)) {
156
+ const current = target[key];
157
+ if (value && typeof value === "object" && !Array.isArray(value) && current && typeof current === "object" && !Array.isArray(current)) {
158
+ target[key] = merge({ ...current }, value);
159
+ } else {
160
+ target[key] = value;
161
+ }
162
+ }
163
+ }
164
+ return target;
165
+ }
166
+ function groupBy(list, key) {
167
+ const out = {};
168
+ const getKey = typeof key === "function" ? key : (item) => item?.[key];
169
+ for (const item of list) {
170
+ const k = String(getKey(item));
171
+ (out[k] || (out[k] = [])).push(item);
172
+ }
173
+ return out;
174
+ }
175
+ function unique(list, key) {
176
+ if (!key) return [...new Set(list)];
177
+ const getKey = typeof key === "function" ? key : (item) => item?.[key];
178
+ const seen = /* @__PURE__ */ new Set();
179
+ const out = [];
180
+ for (const item of list) {
181
+ const k = getKey(item);
182
+ if (seen.has(k)) continue;
183
+ seen.add(k);
184
+ out.push(item);
185
+ }
186
+ return out;
187
+ }
188
+ function chunk(list, size = 10) {
189
+ if (size < 1) return [list];
190
+ const out = [];
191
+ for (let i = 0; i < list.length; i += size) out.push(list.slice(i, i + size));
192
+ return out;
193
+ }
194
+ function sortBy(list, key, direction = "asc") {
195
+ const getKey = typeof key === "function" ? key : (item) => item?.[key];
196
+ const factor = direction === "desc" ? -1 : 1;
197
+ return [...list].sort((a, b) => {
198
+ const va = getKey(a);
199
+ const vb = getKey(b);
200
+ if (va == null && vb == null) return 0;
201
+ if (va == null) return 1;
202
+ if (vb == null) return -1;
203
+ if (typeof va === "string" && typeof vb === "string") {
204
+ return va.localeCompare(vb, void 0, { numeric: true }) * factor;
205
+ }
206
+ return (va > vb ? 1 : va < vb ? -1 : 0) * factor;
207
+ });
208
+ }
209
+ function get(object, path, fallback) {
210
+ const parts = path.split(".");
211
+ let current = object;
212
+ for (const part of parts) {
213
+ if (current == null) return fallback;
214
+ current = current[part];
215
+ }
216
+ return current ?? fallback;
217
+ }
218
+ function set(object, path, value) {
219
+ const parts = path.split(".");
220
+ let current = object;
221
+ for (let i = 0; i < parts.length - 1; i++) {
222
+ const key = parts[i];
223
+ if (typeof current[key] !== "object" || current[key] === null) {
224
+ current[key] = /^\d+$/.test(parts[i + 1]) ? [] : {};
225
+ }
226
+ current = current[key];
227
+ }
228
+ current[parts[parts.length - 1]] = value;
229
+ }
230
+ function random(min = 0, max = 1) {
231
+ return Math.floor(Math.random() * (max - min + 1)) + min;
232
+ }
233
+ function sample(list) {
234
+ return list[Math.floor(Math.random() * list.length)];
235
+ }
236
+ function slugify(text, separator = "-") {
237
+ return String(text).normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, separator).replace(new RegExp(`\\${separator}{2,}`, "g"), separator).replace(new RegExp(`^\\${separator}|\\${separator}$`, "g"), "");
238
+ }
239
+ function truncate(text, length = 100, suffix = "...") {
240
+ const value = String(text ?? "");
241
+ if (value.length <= length) return value;
242
+ return value.slice(0, Math.max(0, length - suffix.length)).trimEnd() + suffix;
243
+ }
244
+ function capitalize(text) {
245
+ const value = String(text ?? "");
246
+ return value.charAt(0).toUpperCase() + value.slice(1);
247
+ }
248
+ function titleCase(text) {
249
+ return String(text ?? "").replace(/\w\S*/g, (word) => capitalize(word.toLowerCase()));
250
+ }
251
+ function escapeHtml(text) {
252
+ return String(text ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
253
+ }
254
+ function stripTags(html) {
255
+ return String(html ?? "").replace(/<\/?[^>]+(>|$)/g, "");
256
+ }
257
+ var defaultLocale = "pt-BR";
258
+ var defaultCurrency = "BRL";
259
+ function setFormatDefaults(locale, currency) {
260
+ if (locale) defaultLocale = locale;
261
+ if (currency) defaultCurrency = currency;
262
+ }
263
+ function formatCurrency(value, options = {}) {
264
+ const n = typeof value === "string" ? parseFloat(value) : value;
265
+ if (n == null || Number.isNaN(n)) return "";
266
+ return new Intl.NumberFormat(options.locale ?? defaultLocale, {
267
+ style: "currency",
268
+ currency: options.currency ?? defaultCurrency
269
+ }).format(n);
270
+ }
271
+ function formatNumber(value, options = {}) {
272
+ const n = typeof value === "string" ? parseFloat(value) : value;
273
+ if (n == null || Number.isNaN(n)) return "";
274
+ const { locale, ...rest } = options;
275
+ return new Intl.NumberFormat(locale ?? defaultLocale, rest).format(n);
276
+ }
277
+ function formatDate(value, format = "short", locale) {
278
+ const date = value instanceof Date ? value : new Date(value);
279
+ if (Number.isNaN(date.getTime())) return "";
280
+ const loc = locale ?? defaultLocale;
281
+ if (typeof format === "object") return new Intl.DateTimeFormat(loc, format).format(date);
282
+ const presets = {
283
+ short: { day: "2-digit", month: "2-digit", year: "numeric" },
284
+ long: { day: "2-digit", month: "long", year: "numeric" },
285
+ full: { dateStyle: "full" },
286
+ time: { hour: "2-digit", minute: "2-digit" },
287
+ datetime: { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" }
288
+ };
289
+ if (presets[format]) return new Intl.DateTimeFormat(loc, presets[format]).format(date);
290
+ const pad = (n) => String(n).padStart(2, "0");
291
+ return format.replace(/YYYY/g, String(date.getFullYear())).replace(/YY/g, String(date.getFullYear()).slice(-2)).replace(/MM/g, pad(date.getMonth() + 1)).replace(/DD/g, pad(date.getDate())).replace(/HH/g, pad(date.getHours())).replace(/mm/g, pad(date.getMinutes())).replace(/ss/g, pad(date.getSeconds()));
292
+ }
293
+ function relativeTime(value, locale) {
294
+ const date = value instanceof Date ? value : new Date(value);
295
+ if (Number.isNaN(date.getTime())) return "";
296
+ const diff = date.getTime() - Date.now();
297
+ const abs = Math.abs(diff);
298
+ const units = [
299
+ ["year", 31536e6],
300
+ ["month", 2592e6],
301
+ ["week", 6048e5],
302
+ ["day", 864e5],
303
+ ["hour", 36e5],
304
+ ["minute", 6e4],
305
+ ["second", 1e3]
306
+ ];
307
+ const rtf = new Intl.RelativeTimeFormat(locale ?? defaultLocale, { numeric: "auto" });
308
+ for (const [unit, ms] of units) {
309
+ if (abs >= ms || unit === "second") {
310
+ return rtf.format(Math.round(diff / ms), unit);
311
+ }
312
+ }
313
+ return "";
314
+ }
315
+ function formatFileSize(bytes, decimals = 1) {
316
+ const n = Number(bytes);
317
+ if (!n || Number.isNaN(n)) return "0 B";
318
+ const units = ["B", "KB", "MB", "GB", "TB", "PB"];
319
+ const i = Math.min(Math.floor(Math.log(Math.abs(n)) / Math.log(1024)), units.length - 1);
320
+ return `${(n / 1024 ** i).toFixed(i === 0 ? 0 : decimals)} ${units[i]}`;
321
+ }
322
+ function formatPercent(value, decimals = 0, locale) {
323
+ return new Intl.NumberFormat(locale ?? defaultLocale, {
324
+ style: "percent",
325
+ minimumFractionDigits: decimals,
326
+ maximumFractionDigits: decimals
327
+ }).format(value);
328
+ }
329
+ var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
330
+ function matchesMedia(query) {
331
+ if (!isBrowser || typeof window.matchMedia !== "function") return false;
332
+ try {
333
+ return window.matchMedia(query).matches;
334
+ } catch {
335
+ return false;
336
+ }
337
+ }
338
+ var device = {
339
+ get touch() {
340
+ return isBrowser && ("ontouchstart" in window || navigator.maxTouchPoints > 0);
341
+ },
342
+ get mobile() {
343
+ return matchesMedia("(max-width: 767px)");
344
+ },
345
+ get tablet() {
346
+ return matchesMedia("(min-width: 768px) and (max-width: 1023px)");
347
+ },
348
+ get desktop() {
349
+ return matchesMedia("(min-width: 1024px)");
350
+ },
351
+ get online() {
352
+ return !isBrowser || navigator.onLine;
353
+ },
354
+ get reducedMotion() {
355
+ return matchesMedia("(prefers-reduced-motion: reduce)");
356
+ },
357
+ get darkMode() {
358
+ return matchesMedia("(prefers-color-scheme: dark)");
359
+ }
360
+ };
361
+
362
+ exports.capitalize = capitalize;
363
+ exports.chunk = chunk;
364
+ exports.clone = clone;
365
+ exports.debounce = debounce;
366
+ exports.device = device;
367
+ exports.escapeHtml = escapeHtml;
368
+ exports.formatCurrency = formatCurrency;
369
+ exports.formatDate = formatDate;
370
+ exports.formatFileSize = formatFileSize;
371
+ exports.formatNumber = formatNumber;
372
+ exports.formatPercent = formatPercent;
373
+ exports.get = get;
374
+ exports.groupBy = groupBy;
375
+ exports.isBrowser = isBrowser;
376
+ exports.matchesMedia = matchesMedia;
377
+ exports.memoize = memoize;
378
+ exports.merge = merge;
379
+ exports.once = once;
380
+ exports.parseDuration = parseDuration;
381
+ exports.random = random;
382
+ exports.relativeTime = relativeTime;
383
+ exports.sample = sample;
384
+ exports.set = set;
385
+ exports.setFormatDefaults = setFormatDefaults;
386
+ exports.sleep = sleep;
387
+ exports.slugify = slugify;
388
+ exports.sortBy = sortBy;
389
+ exports.stripTags = stripTags;
390
+ exports.throttle = throttle;
391
+ exports.titleCase = titleCase;
392
+ exports.truncate = truncate;
393
+ exports.uid = uid;
394
+ exports.unique = unique;
395
+ exports.uuid = uuid;
396
+ //# sourceMappingURL=utils.cjs.map
397
+ //# sourceMappingURL=utils.cjs.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @module utils
3
+ *
4
+ * Pure utilities. None of them touch the DOM, so the module works the same in
5
+ * browser, Node, Bun, and Deno. Everything here is tree-shakeable.
6
+ */
7
+ /** UUID v4. Uses `crypto.randomUUID` when available. */
8
+ declare function uuid(): string;
9
+ /** Short identifier, useful for element ids. */
10
+ declare function uid(prefix?: string): string;
11
+ /** Pauses execution. `await V.sleep(500)`. */
12
+ declare function sleep(ms: number): Promise<void>;
13
+ /**
14
+ * Converts `"300"`, `"300ms"`, `"1.5s"`, and `"2m"` to milliseconds.
15
+ * Accepts `null` because the most common source is `getAttribute`, which returns null.
16
+ */
17
+ declare function parseDuration(value: string | number | null | undefined, fallback?: number): number;
18
+ interface DebouncedFunction<T extends (...args: any[]) => any> {
19
+ (...args: Parameters<T>): void;
20
+ cancel(): void;
21
+ flush(): void;
22
+ }
23
+ /**
24
+ * Delays execution until it stops being called for `wait` ms.
25
+ *
26
+ * ```js
27
+ * const search = V.debounce(fetchProducts, 300)
28
+ * ```
29
+ */
30
+ declare function debounce<T extends (...args: any[]) => any>(fn: T, wait?: number, immediate?: boolean): DebouncedFunction<T>;
31
+ /** Limits to at most one execution every `wait` ms. */
32
+ declare function throttle<T extends (...args: any[]) => any>(fn: T, wait?: number): DebouncedFunction<T>;
33
+ /** Executes the function once and memoizes the return value. */
34
+ declare function once<T extends (...args: any[]) => any>(fn: T): T;
35
+ /** Result cache by argument. */
36
+ declare function memoize<T extends (...args: any[]) => any>(fn: T, keyFn?: (...args: Parameters<T>) => string): T & {
37
+ cache: Map<string, ReturnType<T>>;
38
+ };
39
+ /** Deep copy. Uses `structuredClone` when available. */
40
+ declare function clone<T>(value: T): T;
41
+ /** Deep merges objects. Arrays are replaced, not concatenated. */
42
+ declare function merge<T extends Record<string, any>>(target: T, ...sources: Array<Partial<T>>): T;
43
+ /** Groups by key or by function. */
44
+ declare function groupBy<T>(list: T[], key: string | ((item: T) => string | number)): Record<string, T[]>;
45
+ /** Removes duplicates. Accepts key for objects. */
46
+ declare function unique<T>(list: T[], key?: string | ((item: T) => unknown)): T[];
47
+ /** Divides into fixed-size chunks. */
48
+ declare function chunk<T>(list: T[], size?: number): T[][];
49
+ /** Sorts by key without altering the original array. */
50
+ declare function sortBy<T>(list: T[], key: string | ((item: T) => any), direction?: 'asc' | 'desc'): T[];
51
+ /** Safely reads a nested path: `get(obj, 'a.b.0.c')`. */
52
+ declare function get<T = unknown>(object: unknown, path: string, fallback?: T): T | undefined;
53
+ /** Writes to a nested path, creating intermediate objects. */
54
+ declare function set(object: Record<string, any>, path: string, value: unknown): void;
55
+ /** Random integer between min and max, inclusive. */
56
+ declare function random(min?: number, max?: number): number;
57
+ /** Randomly picks an item from a list. */
58
+ declare function sample<T>(list: T[]): T | undefined;
59
+ /** Converts text to URL slug, removing accents. */
60
+ declare function slugify(text: string, separator?: string): string;
61
+ /** Truncates text at limit and adds ellipsis. */
62
+ declare function truncate(text: string, length?: number, suffix?: string): string;
63
+ /** First letter uppercase. */
64
+ declare function capitalize(text: string): string;
65
+ /** First letter of each word uppercase. */
66
+ declare function titleCase(text: string): string;
67
+ /** Escapes dangerous characters for interpolating text in HTML. */
68
+ declare function escapeHtml(text: string): string;
69
+ /** Removes all tags from HTML, leaving only text. */
70
+ declare function stripTags(html: string): string;
71
+ interface FormatOptions {
72
+ locale?: string;
73
+ currency?: string;
74
+ }
75
+ /** Sets the locale and currency used by formatters. */
76
+ declare function setFormatDefaults(locale?: string, currency?: string): void;
77
+ /** Formats as currency: `formatCurrency(1234.5)` returns `R$ 1.234,50`. */
78
+ declare function formatCurrency(value: number | string, options?: FormatOptions): string;
79
+ /** Formats number with locale separators. */
80
+ declare function formatNumber(value: number | string, options?: Intl.NumberFormatOptions & FormatOptions): string;
81
+ /** Formats dates accepting Date, timestamp, or ISO string. */
82
+ declare function formatDate(value: Date | string | number, format?: string | Intl.DateTimeFormatOptions, locale?: string): string;
83
+ /** Human-readable relative time: `5 minutes ago`, `in 2 days`. */
84
+ declare function relativeTime(value: Date | string | number, locale?: string): string;
85
+ /** Human-readable file size: `1.4 MB`. */
86
+ declare function formatFileSize(bytes: number, decimals?: number): string;
87
+ /** Formatted percentage. */
88
+ declare function formatPercent(value: number, decimals?: number, locale?: string): string;
89
+ /** `true` when DOM is available. */
90
+ declare const isBrowser: boolean;
91
+ /**
92
+ * Safely queries a media query.
93
+ *
94
+ * `matchMedia` doesn't exist everywhere: it's missing in jsdom and old webviews.
95
+ * Without this guard, reading `device.reducedMotion` would throw TypeError, and since
96
+ * UI directives read this property while opening and closing panels, the exception
97
+ * would interrupt the method and leave `aria-expanded` and focus in the wrong state.
98
+ */
99
+ declare function matchesMedia(query: string): boolean;
100
+ /** Device information, calculated on demand. */
101
+ declare const device: {
102
+ readonly touch: boolean;
103
+ readonly mobile: boolean;
104
+ readonly tablet: boolean;
105
+ readonly desktop: boolean;
106
+ readonly online: boolean;
107
+ readonly reducedMotion: boolean;
108
+ readonly darkMode: boolean;
109
+ };
110
+
111
+ export { type DebouncedFunction, type FormatOptions, capitalize, chunk, clone, debounce, device, escapeHtml, formatCurrency, formatDate, formatFileSize, formatNumber, formatPercent, get, groupBy, isBrowser, matchesMedia, memoize, merge, once, parseDuration, random, relativeTime, sample, set, setFormatDefaults, sleep, slugify, sortBy, stripTags, throttle, titleCase, truncate, uid, unique, uuid };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @module utils
3
+ *
4
+ * Pure utilities. None of them touch the DOM, so the module works the same in
5
+ * browser, Node, Bun, and Deno. Everything here is tree-shakeable.
6
+ */
7
+ /** UUID v4. Uses `crypto.randomUUID` when available. */
8
+ declare function uuid(): string;
9
+ /** Short identifier, useful for element ids. */
10
+ declare function uid(prefix?: string): string;
11
+ /** Pauses execution. `await V.sleep(500)`. */
12
+ declare function sleep(ms: number): Promise<void>;
13
+ /**
14
+ * Converts `"300"`, `"300ms"`, `"1.5s"`, and `"2m"` to milliseconds.
15
+ * Accepts `null` because the most common source is `getAttribute`, which returns null.
16
+ */
17
+ declare function parseDuration(value: string | number | null | undefined, fallback?: number): number;
18
+ interface DebouncedFunction<T extends (...args: any[]) => any> {
19
+ (...args: Parameters<T>): void;
20
+ cancel(): void;
21
+ flush(): void;
22
+ }
23
+ /**
24
+ * Delays execution until it stops being called for `wait` ms.
25
+ *
26
+ * ```js
27
+ * const search = V.debounce(fetchProducts, 300)
28
+ * ```
29
+ */
30
+ declare function debounce<T extends (...args: any[]) => any>(fn: T, wait?: number, immediate?: boolean): DebouncedFunction<T>;
31
+ /** Limits to at most one execution every `wait` ms. */
32
+ declare function throttle<T extends (...args: any[]) => any>(fn: T, wait?: number): DebouncedFunction<T>;
33
+ /** Executes the function once and memoizes the return value. */
34
+ declare function once<T extends (...args: any[]) => any>(fn: T): T;
35
+ /** Result cache by argument. */
36
+ declare function memoize<T extends (...args: any[]) => any>(fn: T, keyFn?: (...args: Parameters<T>) => string): T & {
37
+ cache: Map<string, ReturnType<T>>;
38
+ };
39
+ /** Deep copy. Uses `structuredClone` when available. */
40
+ declare function clone<T>(value: T): T;
41
+ /** Deep merges objects. Arrays are replaced, not concatenated. */
42
+ declare function merge<T extends Record<string, any>>(target: T, ...sources: Array<Partial<T>>): T;
43
+ /** Groups by key or by function. */
44
+ declare function groupBy<T>(list: T[], key: string | ((item: T) => string | number)): Record<string, T[]>;
45
+ /** Removes duplicates. Accepts key for objects. */
46
+ declare function unique<T>(list: T[], key?: string | ((item: T) => unknown)): T[];
47
+ /** Divides into fixed-size chunks. */
48
+ declare function chunk<T>(list: T[], size?: number): T[][];
49
+ /** Sorts by key without altering the original array. */
50
+ declare function sortBy<T>(list: T[], key: string | ((item: T) => any), direction?: 'asc' | 'desc'): T[];
51
+ /** Safely reads a nested path: `get(obj, 'a.b.0.c')`. */
52
+ declare function get<T = unknown>(object: unknown, path: string, fallback?: T): T | undefined;
53
+ /** Writes to a nested path, creating intermediate objects. */
54
+ declare function set(object: Record<string, any>, path: string, value: unknown): void;
55
+ /** Random integer between min and max, inclusive. */
56
+ declare function random(min?: number, max?: number): number;
57
+ /** Randomly picks an item from a list. */
58
+ declare function sample<T>(list: T[]): T | undefined;
59
+ /** Converts text to URL slug, removing accents. */
60
+ declare function slugify(text: string, separator?: string): string;
61
+ /** Truncates text at limit and adds ellipsis. */
62
+ declare function truncate(text: string, length?: number, suffix?: string): string;
63
+ /** First letter uppercase. */
64
+ declare function capitalize(text: string): string;
65
+ /** First letter of each word uppercase. */
66
+ declare function titleCase(text: string): string;
67
+ /** Escapes dangerous characters for interpolating text in HTML. */
68
+ declare function escapeHtml(text: string): string;
69
+ /** Removes all tags from HTML, leaving only text. */
70
+ declare function stripTags(html: string): string;
71
+ interface FormatOptions {
72
+ locale?: string;
73
+ currency?: string;
74
+ }
75
+ /** Sets the locale and currency used by formatters. */
76
+ declare function setFormatDefaults(locale?: string, currency?: string): void;
77
+ /** Formats as currency: `formatCurrency(1234.5)` returns `R$ 1.234,50`. */
78
+ declare function formatCurrency(value: number | string, options?: FormatOptions): string;
79
+ /** Formats number with locale separators. */
80
+ declare function formatNumber(value: number | string, options?: Intl.NumberFormatOptions & FormatOptions): string;
81
+ /** Formats dates accepting Date, timestamp, or ISO string. */
82
+ declare function formatDate(value: Date | string | number, format?: string | Intl.DateTimeFormatOptions, locale?: string): string;
83
+ /** Human-readable relative time: `5 minutes ago`, `in 2 days`. */
84
+ declare function relativeTime(value: Date | string | number, locale?: string): string;
85
+ /** Human-readable file size: `1.4 MB`. */
86
+ declare function formatFileSize(bytes: number, decimals?: number): string;
87
+ /** Formatted percentage. */
88
+ declare function formatPercent(value: number, decimals?: number, locale?: string): string;
89
+ /** `true` when DOM is available. */
90
+ declare const isBrowser: boolean;
91
+ /**
92
+ * Safely queries a media query.
93
+ *
94
+ * `matchMedia` doesn't exist everywhere: it's missing in jsdom and old webviews.
95
+ * Without this guard, reading `device.reducedMotion` would throw TypeError, and since
96
+ * UI directives read this property while opening and closing panels, the exception
97
+ * would interrupt the method and leave `aria-expanded` and focus in the wrong state.
98
+ */
99
+ declare function matchesMedia(query: string): boolean;
100
+ /** Device information, calculated on demand. */
101
+ declare const device: {
102
+ readonly touch: boolean;
103
+ readonly mobile: boolean;
104
+ readonly tablet: boolean;
105
+ readonly desktop: boolean;
106
+ readonly online: boolean;
107
+ readonly reducedMotion: boolean;
108
+ readonly darkMode: boolean;
109
+ };
110
+
111
+ export { type DebouncedFunction, type FormatOptions, capitalize, chunk, clone, debounce, device, escapeHtml, formatCurrency, formatDate, formatFileSize, formatNumber, formatPercent, get, groupBy, isBrowser, matchesMedia, memoize, merge, once, parseDuration, random, relativeTime, sample, set, setFormatDefaults, sleep, slugify, sortBy, stripTags, throttle, titleCase, truncate, uid, unique, uuid };
package/dist/utils.js ADDED
@@ -0,0 +1,4 @@
1
+ export { capitalize, chunk, clone, debounce, device, escapeHtml, formatCurrency, formatDate, formatFileSize, formatNumber, formatPercent, get, groupBy, isBrowser, matchesMedia, memoize, merge, once, parseDuration, random, relativeTime, sample, set, setFormatDefaults, sleep, slugify, sortBy, stripTags, throttle, titleCase, truncate, uid, unique, uuid } from './chunk-234ZLC6W.js';
2
+ import './chunk-E27NRARW.js';
3
+ //# sourceMappingURL=utils.js.map
4
+ //# sourceMappingURL=utils.js.map