@publikit/utils 0.1.1 → 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ ### Added
6
+
7
+ - Initial release of `@publikit/utils` as a Publikit L0 foundation package.
8
+ - `cn` for `clsx` + `tailwind-merge` class-name composition.
9
+ - `ClassValue` type export for typed class-name composition.
10
+ - `debounce` with `cancel()` and `flush()` helpers.
11
+ - `getErrorMessage` for safe unknown error extraction.
12
+ - `formatBytes` and `calculateUsagePercentage` formatting utilities.
13
+ - Subpath exports: `@publikit/utils/cn`, `/async`, `/error`, and `/format`.
14
+ - `docs/PUBLISHING.md`.
15
+ - Coverage script.
16
+
17
+ ### Changed
18
+
19
+ - Package metadata now points to `packages/utils`.
20
+ - README documents the L0 role and headless L2 usage.
21
+ - Test coverage now includes Tailwind conflict merging and additional edge cases.
package/README.md CHANGED
@@ -4,6 +4,10 @@ Framework-agnostic utility functions for Publikit packages and applications.
4
4
 
5
5
  - **No React peer dependency** — safe for scripts, edge workers, and non-UI code
6
6
  - **Tree-shakeable** — `sideEffects: false`
7
+ - **L0 foundation** — shared helpers for headless L2 packages and consumer apps
8
+
9
+ See the [package taxonomy](../docs/PACKAGE-TAXONOMY.md) for where L0 utilities
10
+ fit in Publikit.
7
11
 
8
12
  ## Installation
9
13
 
@@ -16,11 +20,21 @@ npm install @publikit/utils
16
20
  | Export | Description |
17
21
  |--------|-------------|
18
22
  | `cn` | Merge Tailwind classes (`clsx` + `tailwind-merge`) |
23
+ | `ClassValue` | Type accepted by `cn` |
19
24
  | `debounce` | Debounce function calls |
20
25
  | `getErrorMessage` | Extract message from unknown errors |
21
26
  | `formatBytes` | Human-readable byte sizes |
22
27
  | `calculateUsagePercentage` | Percentage capped at 100 |
23
28
 
29
+ Subpath exports are available for focused imports:
30
+
31
+ ```ts
32
+ import { cn } from '@publikit/utils/cn';
33
+ import { debounce } from '@publikit/utils/async';
34
+ import { getErrorMessage } from '@publikit/utils/error';
35
+ import { formatBytes } from '@publikit/utils/format';
36
+ ```
37
+
24
38
  ## Usage
25
39
 
26
40
  ```ts
@@ -32,6 +46,16 @@ const message = getErrorMessage(error, 'Something went wrong');
32
46
  const size = formatBytes(1_048_576); // "1 MB"
33
47
  ```
34
48
 
49
+ ## Role in Headless L2 Packages
50
+
51
+ Headless L2 modules use `cn` to merge consumer-provided `classNames` with
52
+ conditional state classes. Non-Tailwind consumers can ignore that merge behavior
53
+ and style stable `data-slot` attributes instead.
54
+
55
+ `formatBytes`, `calculateUsagePercentage`, and `getErrorMessage` are app-level
56
+ convenience utilities; they are kept framework-agnostic so they can be reused in
57
+ PHO, scripts, and workers.
58
+
35
59
  ## Related packages
36
60
 
37
61
  - [`@publikit/hooks`](../hooks) — generic React hooks
package/dist/async.cjs ADDED
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ // async.ts
4
+ function debounce(fn, ms) {
5
+ let timeoutId = null;
6
+ let lastArgs = null;
7
+ const debounced = (...args) => {
8
+ lastArgs = args;
9
+ if (timeoutId) clearTimeout(timeoutId);
10
+ timeoutId = setTimeout(() => {
11
+ timeoutId = null;
12
+ if (lastArgs) {
13
+ fn(...lastArgs);
14
+ lastArgs = null;
15
+ }
16
+ }, ms);
17
+ };
18
+ debounced.cancel = () => {
19
+ if (timeoutId) clearTimeout(timeoutId);
20
+ timeoutId = null;
21
+ lastArgs = null;
22
+ };
23
+ debounced.flush = () => {
24
+ if (!timeoutId || !lastArgs) return;
25
+ clearTimeout(timeoutId);
26
+ timeoutId = null;
27
+ fn(...lastArgs);
28
+ lastArgs = null;
29
+ };
30
+ return debounced;
31
+ }
32
+
33
+ exports.debounce = debounce;
34
+ //# sourceMappingURL=async.cjs.map
35
+ //# sourceMappingURL=async.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../async.ts"],"names":[],"mappings":";;;AAYO,SAAS,QAAA,CACd,IACA,EAAA,EACyB;AACzB,EAAA,IAAI,SAAA,GAAkD,IAAA;AACtD,EAAA,IAAI,QAAA,GAAqB,IAAA;AAEzB,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAY;AAChC,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,QAAA,QAAA,GAAW,IAAA;AAAA,MACb;AAAA,IACF,GAAG,EAAE,CAAA;AAAA,EACP,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,SAAA,CAAU,QAAQ,MAAM;AACtB,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,QAAA,EAAU;AAC7B,IAAA,YAAA,CAAa,SAAS,CAAA;AACtB,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,OAAO,SAAA;AACT","file":"async.cjs","sourcesContent":["/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Debounced function with cancel and flush helpers.
3
+ */
4
+ interface DebouncedFunction<A extends unknown[], R> {
5
+ (...args: A): void;
6
+ cancel(): void;
7
+ flush(): void;
8
+ }
9
+ /**
10
+ * Debounce a function — only invoke after `ms` milliseconds of inactivity.
11
+ */
12
+ declare function debounce<A extends unknown[], R>(fn: (...args: A) => R, ms: number): DebouncedFunction<A, R>;
13
+
14
+ export { type DebouncedFunction, debounce };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Debounced function with cancel and flush helpers.
3
+ */
4
+ interface DebouncedFunction<A extends unknown[], R> {
5
+ (...args: A): void;
6
+ cancel(): void;
7
+ flush(): void;
8
+ }
9
+ /**
10
+ * Debounce a function — only invoke after `ms` milliseconds of inactivity.
11
+ */
12
+ declare function debounce<A extends unknown[], R>(fn: (...args: A) => R, ms: number): DebouncedFunction<A, R>;
13
+
14
+ export { type DebouncedFunction, debounce };
package/dist/async.js ADDED
@@ -0,0 +1,33 @@
1
+ // async.ts
2
+ function debounce(fn, ms) {
3
+ let timeoutId = null;
4
+ let lastArgs = null;
5
+ const debounced = (...args) => {
6
+ lastArgs = args;
7
+ if (timeoutId) clearTimeout(timeoutId);
8
+ timeoutId = setTimeout(() => {
9
+ timeoutId = null;
10
+ if (lastArgs) {
11
+ fn(...lastArgs);
12
+ lastArgs = null;
13
+ }
14
+ }, ms);
15
+ };
16
+ debounced.cancel = () => {
17
+ if (timeoutId) clearTimeout(timeoutId);
18
+ timeoutId = null;
19
+ lastArgs = null;
20
+ };
21
+ debounced.flush = () => {
22
+ if (!timeoutId || !lastArgs) return;
23
+ clearTimeout(timeoutId);
24
+ timeoutId = null;
25
+ fn(...lastArgs);
26
+ lastArgs = null;
27
+ };
28
+ return debounced;
29
+ }
30
+
31
+ export { debounce };
32
+ //# sourceMappingURL=async.js.map
33
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../async.ts"],"names":[],"mappings":";AAYO,SAAS,QAAA,CACd,IACA,EAAA,EACyB;AACzB,EAAA,IAAI,SAAA,GAAkD,IAAA;AACtD,EAAA,IAAI,QAAA,GAAqB,IAAA;AAEzB,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAY;AAChC,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,QAAA,QAAA,GAAW,IAAA;AAAA,MACb;AAAA,IACF,GAAG,EAAE,CAAA;AAAA,EACP,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,SAAA,CAAU,QAAQ,MAAM;AACtB,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,QAAA,EAAU;AAC7B,IAAA,YAAA,CAAa,SAAS,CAAA;AACtB,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,OAAO,SAAA;AACT","file":"async.js","sourcesContent":["/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n"]}
package/dist/cn.cjs ADDED
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var clsx = require('clsx');
4
+ var tailwindMerge = require('tailwind-merge');
5
+
6
+ // cn.ts
7
+ function cn(...inputs) {
8
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
9
+ }
10
+
11
+ exports.cn = cn;
12
+ //# sourceMappingURL=cn.cjs.map
13
+ //# sourceMappingURL=cn.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cn.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B","file":"cn.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\nexport type { ClassValue } from 'clsx';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n"]}
package/dist/cn.d.cts ADDED
@@ -0,0 +1,9 @@
1
+ import { ClassValue } from 'clsx';
2
+ export { ClassValue } from 'clsx';
3
+
4
+ /**
5
+ * Merge Tailwind CSS class names with conflict resolution.
6
+ */
7
+ declare function cn(...inputs: ClassValue[]): string;
8
+
9
+ export { cn };
package/dist/cn.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { ClassValue } from 'clsx';
2
+ export { ClassValue } from 'clsx';
3
+
4
+ /**
5
+ * Merge Tailwind CSS class names with conflict resolution.
6
+ */
7
+ declare function cn(...inputs: ClassValue[]): string;
8
+
9
+ export { cn };
package/dist/cn.js ADDED
@@ -0,0 +1,11 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // cn.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ export { cn };
10
+ //# sourceMappingURL=cn.js.map
11
+ //# sourceMappingURL=cn.js.map
package/dist/cn.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cn.ts"],"names":[],"mappings":";;;;AAQO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B","file":"cn.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\nexport type { ClassValue } from 'clsx';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n"]}
package/dist/error.cjs ADDED
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ // error.ts
4
+ function getErrorMessage(error, defaultMessage) {
5
+ if (error instanceof Error && error.message) {
6
+ return error.message;
7
+ }
8
+ if (typeof error === "string" && error.trim()) {
9
+ return error;
10
+ }
11
+ if (error && typeof error === "object" && "message" in error) {
12
+ const message = error.message;
13
+ if (typeof message === "string" && message.trim()) {
14
+ return message;
15
+ }
16
+ }
17
+ return defaultMessage;
18
+ }
19
+
20
+ exports.getErrorMessage = getErrorMessage;
21
+ //# sourceMappingURL=error.cjs.map
22
+ //# sourceMappingURL=error.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../error.ts"],"names":[],"mappings":";;;AAGO,SAAS,eAAA,CAAgB,OAAgB,cAAA,EAAgC;AAC9E,EAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,OAAA,EAAS;AAC3C,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AAEA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAK,EAAG;AAC7C,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,aAAa,KAAA,EAAO;AAC5D,IAAA,MAAM,UAAW,KAAA,CAA+B,OAAA;AAChD,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,CAAQ,MAAK,EAAG;AACjD,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,cAAA;AACT","file":"error.cjs","sourcesContent":["/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown error value.
3
+ */
4
+ declare function getErrorMessage(error: unknown, defaultMessage: string): string;
5
+
6
+ export { getErrorMessage };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown error value.
3
+ */
4
+ declare function getErrorMessage(error: unknown, defaultMessage: string): string;
5
+
6
+ export { getErrorMessage };
package/dist/error.js ADDED
@@ -0,0 +1,20 @@
1
+ // error.ts
2
+ function getErrorMessage(error, defaultMessage) {
3
+ if (error instanceof Error && error.message) {
4
+ return error.message;
5
+ }
6
+ if (typeof error === "string" && error.trim()) {
7
+ return error;
8
+ }
9
+ if (error && typeof error === "object" && "message" in error) {
10
+ const message = error.message;
11
+ if (typeof message === "string" && message.trim()) {
12
+ return message;
13
+ }
14
+ }
15
+ return defaultMessage;
16
+ }
17
+
18
+ export { getErrorMessage };
19
+ //# sourceMappingURL=error.js.map
20
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../error.ts"],"names":[],"mappings":";AAGO,SAAS,eAAA,CAAgB,OAAgB,cAAA,EAAgC;AAC9E,EAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,OAAA,EAAS;AAC3C,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AAEA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAK,EAAG;AAC7C,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,aAAa,KAAA,EAAO;AAC5D,IAAA,MAAM,UAAW,KAAA,CAA+B,OAAA;AAChD,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,CAAQ,MAAK,EAAG;AACjD,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,cAAA;AACT","file":"error.js","sourcesContent":["/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n"]}
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ // format.ts
4
+ function formatBytes(bytes) {
5
+ if (!Number.isFinite(bytes) || bytes <= 0) {
6
+ return "0 B";
7
+ }
8
+ const k = 1024;
9
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
10
+ const i = Math.min(
11
+ Math.floor(Math.log(bytes) / Math.log(k)),
12
+ sizes.length - 1
13
+ );
14
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
15
+ }
16
+ function calculateUsagePercentage(current, limit) {
17
+ if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {
18
+ return 0;
19
+ }
20
+ if (current <= 0) {
21
+ return 0;
22
+ }
23
+ return Math.min(100, Math.round(current / limit * 100));
24
+ }
25
+
26
+ exports.calculateUsagePercentage = calculateUsagePercentage;
27
+ exports.formatBytes = formatBytes;
28
+ //# sourceMappingURL=format.cjs.map
29
+ //# sourceMappingURL=format.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../format.ts"],"names":[],"mappings":";;;AAGO,SAAS,YAAY,KAAA,EAAuB;AACjD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACzC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,MAAM,QAAQ,CAAC,GAAA,EAAK,IAAA,EAAM,IAAA,EAAM,MAAM,IAAI,CAAA;AAC1C,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,IACxC,MAAM,MAAA,GAAS;AAAA,GACjB;AACA,EAAA,OAAO,CAAA,EAAG,UAAA,CAAA,CAAY,KAAA,GAAQ,IAAA,CAAK,IAAI,CAAA,EAAG,CAAC,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAC,CAAC,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACvE;AAKO,SAAS,wBAAA,CAAyB,SAAiB,KAAA,EAAuB;AAC/E,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AACtE,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAW,CAAA,EAAG;AAChB,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA,CAAK,IAAI,GAAA,EAAK,IAAA,CAAK,MAAO,OAAA,GAAU,KAAA,GAAS,GAAG,CAAC,CAAA;AAC1D","file":"format.cjs","sourcesContent":["/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Format a byte count as a human-readable string.
3
+ */
4
+ declare function formatBytes(bytes: number): string;
5
+ /**
6
+ * Calculate usage percentage capped at 100.
7
+ */
8
+ declare function calculateUsagePercentage(current: number, limit: number): number;
9
+
10
+ export { calculateUsagePercentage, formatBytes };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Format a byte count as a human-readable string.
3
+ */
4
+ declare function formatBytes(bytes: number): string;
5
+ /**
6
+ * Calculate usage percentage capped at 100.
7
+ */
8
+ declare function calculateUsagePercentage(current: number, limit: number): number;
9
+
10
+ export { calculateUsagePercentage, formatBytes };
package/dist/format.js ADDED
@@ -0,0 +1,26 @@
1
+ // format.ts
2
+ function formatBytes(bytes) {
3
+ if (!Number.isFinite(bytes) || bytes <= 0) {
4
+ return "0 B";
5
+ }
6
+ const k = 1024;
7
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
8
+ const i = Math.min(
9
+ Math.floor(Math.log(bytes) / Math.log(k)),
10
+ sizes.length - 1
11
+ );
12
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
13
+ }
14
+ function calculateUsagePercentage(current, limit) {
15
+ if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {
16
+ return 0;
17
+ }
18
+ if (current <= 0) {
19
+ return 0;
20
+ }
21
+ return Math.min(100, Math.round(current / limit * 100));
22
+ }
23
+
24
+ export { calculateUsagePercentage, formatBytes };
25
+ //# sourceMappingURL=format.js.map
26
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../format.ts"],"names":[],"mappings":";AAGO,SAAS,YAAY,KAAA,EAAuB;AACjD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACzC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,MAAM,QAAQ,CAAC,GAAA,EAAK,IAAA,EAAM,IAAA,EAAM,MAAM,IAAI,CAAA;AAC1C,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,IACxC,MAAM,MAAA,GAAS;AAAA,GACjB;AACA,EAAA,OAAO,CAAA,EAAG,UAAA,CAAA,CAAY,KAAA,GAAQ,IAAA,CAAK,IAAI,CAAA,EAAG,CAAC,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAC,CAAC,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACvE;AAKO,SAAS,wBAAA,CAAyB,SAAiB,KAAA,EAAuB;AAC/E,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AACtE,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAW,CAAA,EAAG;AAChB,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA,CAAK,IAAI,GAAA,EAAK,IAAA,CAAK,MAAO,OAAA,GAAU,KAAA,GAAS,GAAG,CAAC,CAAA;AAC1D","file":"format.js","sourcesContent":["/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../cn.ts","../async.ts","../error.ts","../format.ts"],"names":["twMerge","clsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n","/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n","/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n","/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
1
+ {"version":3,"sources":["../cn.ts","../async.ts","../error.ts","../format.ts"],"names":["twMerge","clsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\nexport type { ClassValue } from 'clsx';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n","/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n","/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n","/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
package/dist/index.d.cts CHANGED
@@ -1,35 +1,5 @@
1
- import { ClassValue } from 'clsx';
2
-
3
- /**
4
- * Merge Tailwind CSS class names with conflict resolution.
5
- */
6
- declare function cn(...inputs: ClassValue[]): string;
7
-
8
- /**
9
- * Debounced function with cancel and flush helpers.
10
- */
11
- interface DebouncedFunction<A extends unknown[], R> {
12
- (...args: A): void;
13
- cancel(): void;
14
- flush(): void;
15
- }
16
- /**
17
- * Debounce a function — only invoke after `ms` milliseconds of inactivity.
18
- */
19
- declare function debounce<A extends unknown[], R>(fn: (...args: A) => R, ms: number): DebouncedFunction<A, R>;
20
-
21
- /**
22
- * Extract a human-readable message from an unknown error value.
23
- */
24
- declare function getErrorMessage(error: unknown, defaultMessage: string): string;
25
-
26
- /**
27
- * Format a byte count as a human-readable string.
28
- */
29
- declare function formatBytes(bytes: number): string;
30
- /**
31
- * Calculate usage percentage capped at 100.
32
- */
33
- declare function calculateUsagePercentage(current: number, limit: number): number;
34
-
35
- export { type DebouncedFunction, calculateUsagePercentage, cn, debounce, formatBytes, getErrorMessage };
1
+ export { cn } from './cn.cjs';
2
+ export { DebouncedFunction, debounce } from './async.cjs';
3
+ export { getErrorMessage } from './error.cjs';
4
+ export { calculateUsagePercentage, formatBytes } from './format.cjs';
5
+ export { ClassValue } from 'clsx';
package/dist/index.d.ts CHANGED
@@ -1,35 +1,5 @@
1
- import { ClassValue } from 'clsx';
2
-
3
- /**
4
- * Merge Tailwind CSS class names with conflict resolution.
5
- */
6
- declare function cn(...inputs: ClassValue[]): string;
7
-
8
- /**
9
- * Debounced function with cancel and flush helpers.
10
- */
11
- interface DebouncedFunction<A extends unknown[], R> {
12
- (...args: A): void;
13
- cancel(): void;
14
- flush(): void;
15
- }
16
- /**
17
- * Debounce a function — only invoke after `ms` milliseconds of inactivity.
18
- */
19
- declare function debounce<A extends unknown[], R>(fn: (...args: A) => R, ms: number): DebouncedFunction<A, R>;
20
-
21
- /**
22
- * Extract a human-readable message from an unknown error value.
23
- */
24
- declare function getErrorMessage(error: unknown, defaultMessage: string): string;
25
-
26
- /**
27
- * Format a byte count as a human-readable string.
28
- */
29
- declare function formatBytes(bytes: number): string;
30
- /**
31
- * Calculate usage percentage capped at 100.
32
- */
33
- declare function calculateUsagePercentage(current: number, limit: number): number;
34
-
35
- export { type DebouncedFunction, calculateUsagePercentage, cn, debounce, formatBytes, getErrorMessage };
1
+ export { cn } from './cn.js';
2
+ export { DebouncedFunction, debounce } from './async.js';
3
+ export { getErrorMessage } from './error.js';
4
+ export { calculateUsagePercentage, formatBytes } from './format.js';
5
+ export { ClassValue } from 'clsx';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../cn.ts","../async.ts","../error.ts","../format.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n","/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n","/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n","/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
1
+ {"version":3,"sources":["../cn.ts","../async.ts","../error.ts","../format.ts"],"names":[],"mappings":";;;;AAQO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;;;ACEO,SAAS,QAAA,CACd,IACA,EAAA,EACyB;AACzB,EAAA,IAAI,SAAA,GAAkD,IAAA;AACtD,EAAA,IAAI,QAAA,GAAqB,IAAA;AAEzB,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,KAAY;AAChC,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,QAAA,QAAA,GAAW,IAAA;AAAA,MACb;AAAA,IACF,GAAG,EAAE,CAAA;AAAA,EACP,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AACrC,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,SAAA,CAAU,QAAQ,MAAM;AACtB,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,QAAA,EAAU;AAC7B,IAAA,YAAA,CAAa,SAAS,CAAA;AACtB,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;;;AC3CO,SAAS,eAAA,CAAgB,OAAgB,cAAA,EAAgC;AAC9E,EAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,OAAA,EAAS;AAC3C,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AAEA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAK,EAAG;AAC7C,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,aAAa,KAAA,EAAO;AAC5D,IAAA,MAAM,UAAW,KAAA,CAA+B,OAAA;AAChD,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,CAAQ,MAAK,EAAG;AACjD,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,cAAA;AACT;;;ACjBO,SAAS,YAAY,KAAA,EAAuB;AACjD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACzC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,CAAA,GAAI,IAAA;AACV,EAAA,MAAM,QAAQ,CAAC,GAAA,EAAK,IAAA,EAAM,IAAA,EAAM,MAAM,IAAI,CAAA;AAC1C,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,IACxC,MAAM,MAAA,GAAS;AAAA,GACjB;AACA,EAAA,OAAO,CAAA,EAAG,UAAA,CAAA,CAAY,KAAA,GAAQ,IAAA,CAAK,IAAI,CAAA,EAAG,CAAC,CAAA,EAAG,OAAA,CAAQ,CAAC,CAAC,CAAC,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACvE;AAKO,SAAS,wBAAA,CAAyB,SAAiB,KAAA,EAAuB;AAC/E,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AACtE,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAW,CAAA,EAAG;AAChB,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA,CAAK,IAAI,GAAA,EAAK,IAAA,CAAK,MAAO,OAAA,GAAU,KAAA,GAAS,GAAG,CAAC,CAAA;AAC1D","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\nexport type { ClassValue } from 'clsx';\r\n\r\n/**\r\n * Merge Tailwind CSS class names with conflict resolution.\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n","/**\r\n * Debounced function with cancel and flush helpers.\r\n */\r\nexport interface DebouncedFunction<A extends unknown[], R> {\r\n (...args: A): void;\r\n cancel(): void;\r\n flush(): void;\r\n}\r\n\r\n/**\r\n * Debounce a function — only invoke after `ms` milliseconds of inactivity.\r\n */\r\nexport function debounce<A extends unknown[], R>(\r\n fn: (...args: A) => R,\r\n ms: number\r\n): DebouncedFunction<A, R> {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: A | null = null;\r\n\r\n const debounced = (...args: A) => {\r\n lastArgs = args;\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n }, ms);\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastArgs = null;\r\n };\r\n\r\n debounced.flush = () => {\r\n if (!timeoutId || !lastArgs) return;\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n","/**\r\n * Extract a human-readable message from an unknown error value.\r\n */\r\nexport function getErrorMessage(error: unknown, defaultMessage: string): string {\r\n if (error instanceof Error && error.message) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string' && error.trim()) {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n const message = (error as { message: unknown }).message;\r\n if (typeof message === 'string' && message.trim()) {\r\n return message;\r\n }\r\n }\r\n\r\n return defaultMessage;\r\n}\r\n","/**\r\n * Format a byte count as a human-readable string.\r\n */\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) {\r\n return '0 B';\r\n }\r\n\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\r\n const i = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(k)),\r\n sizes.length - 1\r\n );\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n}\r\n\r\n/**\r\n * Calculate usage percentage capped at 100.\r\n */\r\nexport function calculateUsagePercentage(current: number, limit: number): number {\r\n if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) {\r\n return 0;\r\n }\r\n\r\n if (current <= 0) {\r\n return 0;\r\n }\r\n\r\n return Math.min(100, Math.round((current / limit) * 100));\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@publikit/utils",
3
- "version": "0.1.1",
3
+ "version": "1.0.0",
4
4
  "description": "Framework-agnostic utility functions for Publikit packages and applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -11,11 +11,32 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
+ },
15
+ "./cn": {
16
+ "types": "./dist/cn.d.ts",
17
+ "import": "./dist/cn.js",
18
+ "require": "./dist/cn.cjs"
19
+ },
20
+ "./async": {
21
+ "types": "./dist/async.d.ts",
22
+ "import": "./dist/async.js",
23
+ "require": "./dist/async.cjs"
24
+ },
25
+ "./error": {
26
+ "types": "./dist/error.d.ts",
27
+ "import": "./dist/error.js",
28
+ "require": "./dist/error.cjs"
29
+ },
30
+ "./format": {
31
+ "types": "./dist/format.d.ts",
32
+ "import": "./dist/format.js",
33
+ "require": "./dist/format.cjs"
14
34
  }
15
35
  },
16
36
  "files": [
17
37
  "dist",
18
38
  "README.md",
39
+ "CHANGELOG.md",
19
40
  "LICENSE"
20
41
  ],
21
42
  "sideEffects": false,
@@ -25,7 +46,8 @@
25
46
  "clean": "rimraf dist",
26
47
  "prepublishOnly": "npm run clean && npm run build",
27
48
  "typecheck": "tsc --noEmit",
28
- "test": "vitest run"
49
+ "test": "vitest run",
50
+ "test:coverage": "vitest run --coverage"
29
51
  },
30
52
  "keywords": [
31
53
  "utilities",
@@ -39,9 +61,9 @@
39
61
  "repository": {
40
62
  "type": "git",
41
63
  "url": "git+https://github.com/pirimera/publikit.git",
42
- "directory": "utils"
64
+ "directory": "packages/utils"
43
65
  },
44
- "homepage": "https://github.com/pirimera/publikit/tree/main/utils#readme",
66
+ "homepage": "https://github.com/pirimera/publikit/tree/main/packages/utils#readme",
45
67
  "bugs": {
46
68
  "url": "https://github.com/pirimera/publikit/issues"
47
69
  },
@@ -59,6 +81,7 @@
59
81
  "rimraf": "^5.0.0",
60
82
  "tsup": "^8.0.0",
61
83
  "typescript": "^5.8.3",
84
+ "@vitest/coverage-v8": "^4.0.18",
62
85
  "vitest": "^4.0.18"
63
86
  }
64
87
  }