@getdashfy/utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # `@getdashfy/utils`
2
+
3
+ > Formatting and utility functions for Dashfy - numbers, currencies, bytes, dates, times, strings, platform detection, and more.
4
+
5
+ ## Introduction
6
+
7
+ `@getdashfy/utils` provides formatting and utility functions for Dashfy dashboards. It includes:
8
+
9
+ - **Format** - A flexible format API inspired by [Numeral.js](https://numeraljs.com) for numbers, dates, times, bytes, and more
10
+ - **Error** - Safe error message extraction
11
+ - **Function** - Utilities for common patterns
12
+ - **Object** - Safe nested property access
13
+ - **Platform** - Environment detection and OS helpers
14
+ - **String** - String manipulation and display helpers
15
+ - **Libs** - Re-export of [date-fns](https://date-fns.org)
16
+
17
+ ## Install
18
+
19
+ Install with your favorite package manager:
20
+
21
+ #### `npm`
22
+
23
+ ```bash
24
+ npm install @getdashfy/utils
25
+ ```
26
+
27
+ #### `pnpm`
28
+
29
+ ```bash
30
+ pnpm add @getdashfy/utils
31
+ ```
32
+
33
+ #### `yarn`
34
+
35
+ ```bash
36
+ yarn add @getdashfy/utils
37
+ ```
38
+
39
+ #### `bun`
40
+
41
+ ```bash
42
+ bun add @getdashfy/utils
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```ts
48
+ import { format, debounce, get, truncate, isClient } from '@getdashfy/utils'
49
+
50
+ // Format numbers with thousands separators
51
+ format(1000, '0,0')
52
+ // '1,000'
53
+
54
+ // Format compact notation
55
+ format(1500000, '0.0a')
56
+ // '1.5M'
57
+
58
+ // Format relative dates
59
+ format(new Date(), 'relative')
60
+ // 'less than a minute ago'
61
+
62
+ // Safely access nested object properties
63
+ get({ user: { name: 'John' } }, 'user.name')
64
+ // 'John'
65
+
66
+ // Truncate a long string
67
+ truncate('This is a long title that needs trimming', 20)
68
+ // 'This is a long title...'
69
+
70
+ // Debounce a function call
71
+ const debouncedSearch = debounce((query: string) => fetchResults(query), 300)
72
+
73
+ // Check execution environment
74
+ isClient // true in browser
75
+ ```
76
+
77
+ ## Development
78
+
79
+ ```bash
80
+ # Install dependencies
81
+ pnpm install
82
+
83
+ # Build
84
+ pnpm build
85
+
86
+ # Watch mode
87
+ pnpm dev
88
+
89
+ # Run tests
90
+ pnpm test
91
+
92
+ # Run tests with coverage
93
+ pnpm test:coverage
94
+
95
+ # Type check
96
+ pnpm typecheck
97
+ ```
98
+
99
+ ## Community
100
+
101
+ Join the community on [Dashfy's Discord server](https://dashfy.dev/discord) to discuss the project, ask questions, or get help.
102
+
103
+ Join the conversation on X (Twitter) and follow [@dashfydev](https://x.com/dashfydev) for updates and announcements.
104
+
105
+ ## License
106
+
107
+ This project is licensed under the AGPL-3.0 License - see the [LICENSE](./LICENSE) file for details.
package/dist/index.cjs ADDED
@@ -0,0 +1,497 @@
1
+ 'use strict';
2
+
3
+ var dateFns = require('date-fns');
4
+ var prettyBytes = require('pretty-bytes');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var dateFns__namespace = /*#__PURE__*/_interopNamespace(dateFns);
27
+ var prettyBytes__default = /*#__PURE__*/_interopDefault(prettyBytes);
28
+
29
+ // src/index.ts
30
+
31
+ // src/error/error.ts
32
+ function isErrorWithMessage(error) {
33
+ return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
34
+ }
35
+ function toErrorWithMessage(maybeError) {
36
+ if (isErrorWithMessage(maybeError)) {
37
+ return maybeError;
38
+ }
39
+ try {
40
+ return new Error(JSON.stringify(maybeError));
41
+ } catch {
42
+ return new Error(String(maybeError));
43
+ }
44
+ }
45
+ function getErrorMessage(error) {
46
+ return toErrorWithMessage(error).message;
47
+ }
48
+
49
+ // src/format/core.ts
50
+ var defaultLocale;
51
+ function setDefaultLocale(locale) {
52
+ defaultLocale = locale;
53
+ }
54
+ function getDefaultLocale() {
55
+ return defaultLocale;
56
+ }
57
+ function resolveLocale(options) {
58
+ return options?.locale ?? defaultLocale;
59
+ }
60
+ function handleZeroNull(value, options, formatFn) {
61
+ if (value === null || value === void 0) {
62
+ if (options?.nullFormat !== void 0) {
63
+ return options.nullFormat;
64
+ }
65
+ return null;
66
+ }
67
+ if (typeof value === "number" && value === 0 && options?.zeroFormat !== void 0) {
68
+ return options.zeroFormat;
69
+ }
70
+ return formatFn(value);
71
+ }
72
+
73
+ // src/format/bytes.ts
74
+ function formatBytes(value, options) {
75
+ const result = handleZeroNull(value, options, (valueToFormat) => {
76
+ const byteCount = typeof valueToFormat === "bigint" ? Number(valueToFormat) : valueToFormat;
77
+ const locale = resolveLocale(options);
78
+ return prettyBytes__default.default(byteCount, {
79
+ binary: options?.binary,
80
+ bits: options?.bits,
81
+ signed: options?.signed,
82
+ locale: locale ? Array.isArray(locale) ? locale[0] : locale : false,
83
+ minimumFractionDigits: options?.minimumFractionDigits,
84
+ maximumFractionDigits: options?.maximumFractionDigits
85
+ });
86
+ });
87
+ const fallbackValue = value === null || value === void 0 ? 0 : typeof value === "bigint" ? Number(value) : value;
88
+ return result ?? prettyBytes__default.default(fallbackValue);
89
+ }
90
+ function formatBytesPerSecond(value, options) {
91
+ const result = handleZeroNull(value, options, (valueToFormat) => {
92
+ const byteCount = typeof valueToFormat === "bigint" ? Number(valueToFormat) : valueToFormat;
93
+ const locale = resolveLocale(options);
94
+ return `${prettyBytes__default.default(byteCount, {
95
+ binary: options?.binary,
96
+ bits: options?.bits,
97
+ signed: options?.signed,
98
+ locale: locale ? Array.isArray(locale) ? locale[0] : locale : false,
99
+ minimumFractionDigits: options?.minimumFractionDigits,
100
+ maximumFractionDigits: options?.maximumFractionDigits
101
+ })}/s`;
102
+ });
103
+ const fallbackValue = value === null || value === void 0 ? 0 : typeof value === "bigint" ? Number(value) : value;
104
+ return result ?? `${prettyBytes__default.default(fallbackValue)}/s`;
105
+ }
106
+
107
+ // src/format/currency.ts
108
+ function formatCurrency(value, options) {
109
+ const result = handleZeroNull(value, options, (valueToFormat) => {
110
+ const locale = resolveLocale(options);
111
+ const formatOptions = {
112
+ style: "currency",
113
+ currency: options?.currency ?? "USD",
114
+ minimumFractionDigits: options?.minimumFractionDigits,
115
+ maximumFractionDigits: options?.maximumFractionDigits ?? 2
116
+ };
117
+ return new Intl.NumberFormat(locale, formatOptions).format(valueToFormat);
118
+ });
119
+ const fallbackResult = new Intl.NumberFormat(resolveLocale(options), {
120
+ style: "currency",
121
+ currency: options?.currency ?? "USD"
122
+ }).format(value);
123
+ return result ?? fallbackResult;
124
+ }
125
+ function formatDate(value, options) {
126
+ const date = value instanceof Date ? value : typeof value === "number" ? new Date(value * 1e3) : new Date(value);
127
+ const pattern = options?.format ?? "MMM d, yyyy";
128
+ return dateFns.format(date, pattern);
129
+ }
130
+ function formatRelativeTime(value, options) {
131
+ const date = value instanceof Date ? value : typeof value === "number" ? new Date(value * 1e3) : new Date(value);
132
+ return dateFns.formatDistanceToNow(date, { addSuffix: options?.addSuffix ?? true });
133
+ }
134
+
135
+ // src/format/exponential.ts
136
+ function formatExponential(value, options) {
137
+ const result = handleZeroNull(value, options, (valueToFormat) => {
138
+ const locale = resolveLocale(options);
139
+ const formatOptions = {
140
+ notation: "scientific",
141
+ minimumFractionDigits: options?.minimumFractionDigits,
142
+ maximumFractionDigits: options?.maximumFractionDigits ?? 2
143
+ };
144
+ return new Intl.NumberFormat(locale, formatOptions).format(valueToFormat);
145
+ });
146
+ const fallbackResult = new Intl.NumberFormat(resolveLocale(options), {
147
+ notation: "scientific"
148
+ }).format(value);
149
+ return result ?? fallbackResult;
150
+ }
151
+
152
+ // src/format/list.ts
153
+ function formatList(items, options) {
154
+ if (items.length === 0) {
155
+ return "";
156
+ }
157
+ const locale = resolveLocale(options);
158
+ return new Intl.ListFormat(locale, {
159
+ type: options?.type ?? "conjunction",
160
+ style: options?.style ?? "long"
161
+ }).format(items);
162
+ }
163
+
164
+ // src/format/number.ts
165
+ function formatNumber(value, options) {
166
+ const result = handleZeroNull(value, options, (valueToFormat) => {
167
+ const locale = resolveLocale(options);
168
+ const opts = {
169
+ minimumFractionDigits: options?.minimumFractionDigits,
170
+ maximumFractionDigits: options?.maximumFractionDigits ?? (valueToFormat % 1 === 0 ? 0 : 2),
171
+ ...options?.notation && { notation: options.notation },
172
+ ...options?.compactDisplay && { compactDisplay: options.compactDisplay }
173
+ };
174
+ return new Intl.NumberFormat(locale, opts).format(valueToFormat);
175
+ });
176
+ const fallbackResult = new Intl.NumberFormat(resolveLocale(options)).format(value);
177
+ return result ?? fallbackResult;
178
+ }
179
+
180
+ // src/format/ordinal.ts
181
+ function formatOrdinal(value, options) {
182
+ const result = handleZeroNull(value, options, (valueToFormat) => {
183
+ const locale = resolveLocale(options);
184
+ const roundedValue = Math.round(valueToFormat);
185
+ const intl = new Intl.NumberFormat(locale, {
186
+ minimumFractionDigits: options?.minimumFractionDigits,
187
+ maximumFractionDigits: options?.maximumFractionDigits ?? 0
188
+ });
189
+ const ord = typeof Intl.PluralRules !== "undefined" ? new Intl.PluralRules(locale, { type: "ordinal" }).select(roundedValue) : "other";
190
+ const suffixes = {
191
+ one: "st",
192
+ two: "nd",
193
+ few: "rd",
194
+ other: "th"
195
+ };
196
+ return `${intl.format(roundedValue)}${suffixes[ord] ?? "th"}`;
197
+ });
198
+ return result ?? `${value}th`;
199
+ }
200
+
201
+ // src/format/percent.ts
202
+ function formatPercent(value, options) {
203
+ const result = handleZeroNull(value, options, (valueToFormat) => {
204
+ const toFormat = options?.scaleBy100 !== false ? valueToFormat : valueToFormat / 100;
205
+ const locale = resolveLocale(options);
206
+ const opts = {
207
+ style: "percent",
208
+ minimumFractionDigits: options?.minimumFractionDigits,
209
+ maximumFractionDigits: options?.maximumFractionDigits ?? 0
210
+ };
211
+ return new Intl.NumberFormat(locale, opts).format(toFormat);
212
+ });
213
+ const fallbackResult = new Intl.NumberFormat(resolveLocale(options), { style: "percent" }).format(
214
+ value
215
+ );
216
+ return result ?? fallbackResult;
217
+ }
218
+
219
+ // src/format/temperature.ts
220
+ function formatTemperature(value, options) {
221
+ const result = handleZeroNull(value, options, (valueToFormat) => {
222
+ const locale = resolveLocale(options);
223
+ const unit = options?.unit ?? "celsius";
224
+ const maxFrac = options?.maximumFractionDigits ?? 0;
225
+ const minFrac = options?.minimumFractionDigits;
226
+ if (unit === "kelvin") {
227
+ const opts2 = {
228
+ minimumFractionDigits: minFrac,
229
+ maximumFractionDigits: maxFrac
230
+ };
231
+ return `${new Intl.NumberFormat(locale, opts2).format(valueToFormat)} K`;
232
+ }
233
+ const unitKey = unit === "celsius" ? "celsius" : "fahrenheit";
234
+ const opts = {
235
+ style: "unit",
236
+ unit: unitKey,
237
+ minimumFractionDigits: minFrac,
238
+ maximumFractionDigits: maxFrac
239
+ };
240
+ return new Intl.NumberFormat(locale, opts).format(valueToFormat);
241
+ });
242
+ const fallbackResult = new Intl.NumberFormat(resolveLocale(options), {
243
+ style: "unit",
244
+ unit: "celsius"
245
+ }).format(value);
246
+ return result ?? fallbackResult;
247
+ }
248
+ function formatTime(seconds, options) {
249
+ const result = handleZeroNull(seconds, options, (secondsToFormat) => {
250
+ if (options?.style === "long" || options?.style === void 0) {
251
+ const duration = dateFns.intervalToDuration({ start: 0, end: secondsToFormat * 1e3 });
252
+ const formatted = dateFns.formatDuration(duration, {
253
+ format: options?.includeSeconds !== false ? ["hours", "minutes", "seconds"] : ["hours", "minutes"]
254
+ });
255
+ return formatted || "0 seconds";
256
+ }
257
+ const h = Math.floor(secondsToFormat / 3600);
258
+ const m = Math.floor((secondsToFormat - h * 3600) / 60);
259
+ const sec = Math.round(secondsToFormat - h * 3600 - m * 60);
260
+ const pad = (value) => value < 10 ? `0${value}` : String(value);
261
+ return `${pad(h)}:${pad(m)}:${pad(sec)}`;
262
+ });
263
+ return result ?? "0:00:00";
264
+ }
265
+ function formatTimeCompact(seconds, options) {
266
+ const result = handleZeroNull(seconds, options, (secondsToFormat) => {
267
+ const days = Math.floor(secondsToFormat / 86400);
268
+ const hours = Math.floor(secondsToFormat % 86400 / 3600);
269
+ const minutes = Math.floor(secondsToFormat % 3600 / 60);
270
+ const parts = [];
271
+ if (days > 0) {
272
+ parts.push(`${days}d`);
273
+ }
274
+ if (hours > 0) {
275
+ parts.push(`${hours}h`);
276
+ }
277
+ if (minutes > 0) {
278
+ parts.push(`${minutes}m`);
279
+ }
280
+ return parts.length > 0 ? parts.join(" ") : "0m";
281
+ });
282
+ return result ?? "0m";
283
+ }
284
+
285
+ // src/format/format.ts
286
+ function format(value, formatString, options) {
287
+ if (value === null || value === void 0) {
288
+ if (options?.nullFormat !== void 0) {
289
+ return options.nullFormat;
290
+ }
291
+ return "";
292
+ }
293
+ const str = formatString.trim().toLowerCase();
294
+ if (str === "ordinal" || str === "0o") {
295
+ return formatOrdinal(Number(value), options);
296
+ }
297
+ if (str === "relative") {
298
+ const date = value instanceof Date ? value : typeof value === "number" ? new Date(value * 1e3) : new Date(value);
299
+ return formatRelativeTime(date, { ...options, addSuffix: true });
300
+ }
301
+ if (str === "list") {
302
+ return formatList(value, options);
303
+ }
304
+ if (str === "exponential" || str === "0.00e+0" || /e[+-]/.test(str)) {
305
+ return formatExponential(Number(value), options);
306
+ }
307
+ if (/\$|currency/.test(str)) {
308
+ const currency = str.includes("\u20AC") ? "EUR" : str.includes("\xA3") ? "GBP" : "USD";
309
+ return formatCurrency(Number(value), { ...options, currency });
310
+ }
311
+ if (/%|percent/.test(str)) {
312
+ const decimals2 = /\.(0+)/.exec(str)?.[1]?.length ?? 0;
313
+ return formatPercent(Number(value), {
314
+ ...options,
315
+ maximumFractionDigits: decimals2
316
+ });
317
+ }
318
+ if (str === "bps" || str.endsWith("/s")) {
319
+ const binary = str.includes("ib");
320
+ return formatBytesPerSecond(typeof value === "bigint" ? value : Number(value), {
321
+ ...options,
322
+ binary
323
+ });
324
+ }
325
+ if (/0\s?i?b|bytes/.test(str)) {
326
+ const binary = str.includes("ib");
327
+ return formatBytes(typeof value === "bigint" ? value : Number(value), {
328
+ ...options,
329
+ binary
330
+ });
331
+ }
332
+ if (str === "uptime" && typeof value === "number") {
333
+ return formatTimeCompact(value, options);
334
+ }
335
+ if ((str === "time" || str === "duration") && typeof value === "number") {
336
+ return formatTime(value, { ...options, style: "long" });
337
+ }
338
+ if (/[:]/.test(str) && typeof value === "number") {
339
+ return formatTime(value, { ...options, style: "short" });
340
+ }
341
+ if (/temp|°|celsius|fahrenheit|kelvin/.test(str)) {
342
+ const unit = str.includes("f") ? "fahrenheit" : str.includes("k") ? "kelvin" : "celsius";
343
+ return formatTemperature(Number(value), { ...options, unit });
344
+ }
345
+ if (/date|short|long|iso/.test(str)) {
346
+ const date = value instanceof Date ? value : typeof value === "number" ? new Date(value * 1e3) : new Date(value);
347
+ if (str === "short") {
348
+ return formatDate(date, { ...options, format: "MMM d" });
349
+ }
350
+ if (str === "long") {
351
+ return formatDate(date, { ...options, format: "MMMM d, yyyy" });
352
+ }
353
+ if (str === "iso") {
354
+ return formatDate(date, { ...options, format: "yyyy-MM-dd'T'HH:mm:ss" });
355
+ }
356
+ return formatDate(date, options);
357
+ }
358
+ const num = typeof value === "number" ? value : typeof value === "bigint" ? Number(value) : Number.parseFloat(String(value));
359
+ if (Number.isNaN(num)) {
360
+ return options?.nullFormat ?? "";
361
+ }
362
+ const decimals = /\.(0+)/.exec(str)?.[1]?.length ?? 0;
363
+ const compact = /a|abbrev/.test(str);
364
+ if (compact) {
365
+ return formatNumber(num, {
366
+ ...options,
367
+ notation: "compact",
368
+ compactDisplay: str.includes(" ") ? "long" : "short",
369
+ maximumFractionDigits: decimals || 1
370
+ });
371
+ }
372
+ return formatNumber(num, {
373
+ ...options,
374
+ minimumFractionDigits: decimals,
375
+ maximumFractionDigits: decimals
376
+ });
377
+ }
378
+
379
+ // src/function/debounce.ts
380
+ function debounce(func, wait) {
381
+ let timeout = null;
382
+ return function executedFunction(...args) {
383
+ const later = () => {
384
+ timeout = null;
385
+ func(...args);
386
+ };
387
+ if (timeout !== null) {
388
+ clearTimeout(timeout);
389
+ }
390
+ timeout = setTimeout(later, wait);
391
+ };
392
+ }
393
+
394
+ // src/object/get.ts
395
+ function get(obj, path) {
396
+ const keys = path.split(".");
397
+ let result = obj;
398
+ for (const key of keys) {
399
+ if (result && typeof result === "object" && key in result) {
400
+ result = result[key];
401
+ } else {
402
+ return void 0;
403
+ }
404
+ }
405
+ return result;
406
+ }
407
+
408
+ // src/platform/env.ts
409
+ var isDevelopment = process.env.NODE_ENV === "development";
410
+ var isProduction = process.env.NODE_ENV === "production";
411
+ var isTest = process.env.NODE_ENV === "test";
412
+
413
+ // src/platform/os.ts
414
+ var isMac = typeof navigator !== "undefined" && navigator.platform.toUpperCase().includes("MAC");
415
+ var modKey = isMac ? "\u2318" : "Ctrl";
416
+
417
+ // src/platform/runtime.ts
418
+ var isClient = typeof window !== "undefined";
419
+ var isServer = typeof window === "undefined";
420
+
421
+ // src/string/stringifyValue.ts
422
+ function stringifyValue(value) {
423
+ if (value === null || value === void 0) {
424
+ return "";
425
+ }
426
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
427
+ return String(value);
428
+ }
429
+ return JSON.stringify(value);
430
+ }
431
+
432
+ // src/string/truncate.ts
433
+ function truncate(str, length) {
434
+ if (str.length <= length) {
435
+ return str;
436
+ }
437
+ return `${str.slice(0, length)}...`;
438
+ }
439
+
440
+ // src/string/valueToDisplayString.ts
441
+ function valueToDisplayString(value) {
442
+ if (value === null) {
443
+ return "null";
444
+ }
445
+ if (value === void 0) {
446
+ return "undefined";
447
+ }
448
+ if (typeof value === "boolean") {
449
+ return value ? "true" : "false";
450
+ }
451
+ if (typeof value === "string") {
452
+ return value;
453
+ }
454
+ if (typeof value === "number") {
455
+ return String(value);
456
+ }
457
+ if (Array.isArray(value)) {
458
+ return `Array(${value.length})`;
459
+ }
460
+ if (typeof value === "object") {
461
+ return `Object(${Object.keys(value).length})`;
462
+ }
463
+ return String(value);
464
+ }
465
+
466
+ exports.dateFns = dateFns__namespace;
467
+ exports.debounce = debounce;
468
+ exports.format = format;
469
+ exports.formatBytes = formatBytes;
470
+ exports.formatBytesPerSecond = formatBytesPerSecond;
471
+ exports.formatCurrency = formatCurrency;
472
+ exports.formatDate = formatDate;
473
+ exports.formatExponential = formatExponential;
474
+ exports.formatList = formatList;
475
+ exports.formatNumber = formatNumber;
476
+ exports.formatOrdinal = formatOrdinal;
477
+ exports.formatPercent = formatPercent;
478
+ exports.formatRelativeTime = formatRelativeTime;
479
+ exports.formatTemperature = formatTemperature;
480
+ exports.formatTime = formatTime;
481
+ exports.formatTimeCompact = formatTimeCompact;
482
+ exports.get = get;
483
+ exports.getDefaultLocale = getDefaultLocale;
484
+ exports.getErrorMessage = getErrorMessage;
485
+ exports.isClient = isClient;
486
+ exports.isDevelopment = isDevelopment;
487
+ exports.isMac = isMac;
488
+ exports.isProduction = isProduction;
489
+ exports.isServer = isServer;
490
+ exports.isTest = isTest;
491
+ exports.modKey = modKey;
492
+ exports.setDefaultLocale = setDefaultLocale;
493
+ exports.stringifyValue = stringifyValue;
494
+ exports.truncate = truncate;
495
+ exports.valueToDisplayString = valueToDisplayString;
496
+ //# sourceMappingURL=index.cjs.map
497
+ //# sourceMappingURL=index.cjs.map