@zuwy/frontend-engineering-system-utils 0.1.0 → 0.2.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 CHANGED
@@ -1,17 +1,17 @@
1
- # @zuwy/frontend-engineering-system-utils
2
-
3
- Shared utility functions for frontend-engineering-system.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pnpm add @zuwy/frontend-engineering-system-utils
9
- ```
10
-
11
- ## Usage
12
-
13
- ```ts
14
- import { readBoolean } from "@zuwy/frontend-engineering-system-utils"
15
-
16
- readBoolean("true")
17
- ```
1
+ # @zuwy/frontend-engineering-system-utils
2
+
3
+ Shared utility functions for frontend-engineering-system.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @zuwy/frontend-engineering-system-utils
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { readBoolean } from "@zuwy/frontend-engineering-system-utils"
15
+
16
+ readBoolean("true")
17
+ ```
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Removes duplicate values, keeping the first occurrence.
3
+ *
4
+ * @param array - The source array.
5
+ * @returns A new array without duplicates.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * unique([1, 2, 2, 3, 3, 3]) // [1, 2, 3]
10
+ * unique(["a", "a", "b"]) // ["a", "b"]
11
+ * ```
12
+ */
13
+ export declare function unique<T>(array: readonly T[]): T[];
14
+ /**
15
+ * Removes duplicates based on a derived key, keeping the first occurrence.
16
+ *
17
+ * @param array - The source array.
18
+ * @param keySelector - Returns the key used to detect duplicates.
19
+ * @returns A new array without duplicates.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const users = [
24
+ * { id: 1, name: "Alice" },
25
+ * { id: 1, name: "Alice (dup)" },
26
+ * { id: 2, name: "Bob" },
27
+ * ]
28
+ * uniqueBy(users, (user) => user.id)
29
+ * // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
30
+ * ```
31
+ */
32
+ export declare function uniqueBy<T, K>(array: readonly T[], keySelector: (item: T) => K): T[];
33
+ /**
34
+ * Splits an array into groups of the given size. The last group may be shorter.
35
+ *
36
+ * @param array - The source array.
37
+ * @param size - The length of each group. Must be a positive integer.
38
+ * @returns A new array of chunked arrays.
39
+ * @throws {RangeError} When `size` is not a positive integer.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
44
+ * ```
45
+ */
46
+ export declare function chunk<T>(array: readonly T[], size: number): T[][];
47
+ /**
48
+ * Groups the items of an array by a derived key.
49
+ *
50
+ * @param array - The source array.
51
+ * @param keySelector - Returns the group key for each item.
52
+ * @returns An object mapping each key to the list of matching items.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? "even" : "odd"))
57
+ * // { odd: [1, 3], even: [2, 4] }
58
+ * ```
59
+ */
60
+ export declare function groupBy<T, K extends PropertyKey>(array: readonly T[], keySelector: (item: T) => K): Record<K, T[]>;
package/dist/array.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Removes duplicate values, keeping the first occurrence.
3
+ *
4
+ * @param array - The source array.
5
+ * @returns A new array without duplicates.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * unique([1, 2, 2, 3, 3, 3]) // [1, 2, 3]
10
+ * unique(["a", "a", "b"]) // ["a", "b"]
11
+ * ```
12
+ */
13
+ export function unique(array) {
14
+ return Array.from(new Set(array));
15
+ }
16
+ /**
17
+ * Removes duplicates based on a derived key, keeping the first occurrence.
18
+ *
19
+ * @param array - The source array.
20
+ * @param keySelector - Returns the key used to detect duplicates.
21
+ * @returns A new array without duplicates.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const users = [
26
+ * { id: 1, name: "Alice" },
27
+ * { id: 1, name: "Alice (dup)" },
28
+ * { id: 2, name: "Bob" },
29
+ * ]
30
+ * uniqueBy(users, (user) => user.id)
31
+ * // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
32
+ * ```
33
+ */
34
+ export function uniqueBy(array, keySelector) {
35
+ const seen = new Set();
36
+ const result = [];
37
+ for (const item of array) {
38
+ const key = keySelector(item);
39
+ if (!seen.has(key)) {
40
+ seen.add(key);
41
+ result.push(item);
42
+ }
43
+ }
44
+ return result;
45
+ }
46
+ /**
47
+ * Splits an array into groups of the given size. The last group may be shorter.
48
+ *
49
+ * @param array - The source array.
50
+ * @param size - The length of each group. Must be a positive integer.
51
+ * @returns A new array of chunked arrays.
52
+ * @throws {RangeError} When `size` is not a positive integer.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
57
+ * ```
58
+ */
59
+ export function chunk(array, size) {
60
+ if (!Number.isInteger(size) || size <= 0) {
61
+ throw new RangeError("chunk size must be a positive integer");
62
+ }
63
+ const result = [];
64
+ for (let index = 0; index < array.length; index += size) {
65
+ result.push(array.slice(index, index + size));
66
+ }
67
+ return result;
68
+ }
69
+ /**
70
+ * Groups the items of an array by a derived key.
71
+ *
72
+ * @param array - The source array.
73
+ * @param keySelector - Returns the group key for each item.
74
+ * @returns An object mapping each key to the list of matching items.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? "even" : "odd"))
79
+ * // { odd: [1, 3], even: [2, 4] }
80
+ * ```
81
+ */
82
+ export function groupBy(array, keySelector) {
83
+ const result = {};
84
+ for (const item of array) {
85
+ const key = keySelector(item);
86
+ const group = result[key];
87
+ if (group) {
88
+ group.push(item);
89
+ }
90
+ else {
91
+ result[key] = [item];
92
+ }
93
+ }
94
+ return result;
95
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pauses execution for the given duration.
3
+ *
4
+ * @param duration - Milliseconds to wait.
5
+ * @returns A promise that resolves after the duration.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * console.log("start")
10
+ * await sleep(1000)
11
+ * console.log("one second later")
12
+ * ```
13
+ */
14
+ export declare function sleep(duration: number): Promise<void>;
15
+ /**
16
+ * Options for {@link retry}.
17
+ */
18
+ export interface RetryOptions {
19
+ /** Maximum number of retries, excluding the first attempt. Defaults to 3. */
20
+ retries?: number;
21
+ /** Milliseconds to wait before each retry. Defaults to 0. */
22
+ delay?: number;
23
+ }
24
+ /**
25
+ * Runs a function and retries it until it succeeds or the retry limit is reached.
26
+ *
27
+ * @param fn - The function to execute. It may return a value or a promise.
28
+ * @param options - Retry options.
29
+ * @returns The resolved value of `fn`.
30
+ * @throws The last error thrown by `fn` when all attempts fail.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const data = await retry(() => fetch("/api/data").then((res) => res.json()), {
35
+ * retries: 3,
36
+ * delay: 500,
37
+ * })
38
+ * ```
39
+ */
40
+ export declare function retry<T>(fn: () => Promise<T> | T, options?: RetryOptions): Promise<T>;
package/dist/async.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pauses execution for the given duration.
3
+ *
4
+ * @param duration - Milliseconds to wait.
5
+ * @returns A promise that resolves after the duration.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * console.log("start")
10
+ * await sleep(1000)
11
+ * console.log("one second later")
12
+ * ```
13
+ */
14
+ export function sleep(duration) {
15
+ return new Promise((resolve) => {
16
+ setTimeout(resolve, duration);
17
+ });
18
+ }
19
+ /**
20
+ * Runs a function and retries it until it succeeds or the retry limit is reached.
21
+ *
22
+ * @param fn - The function to execute. It may return a value or a promise.
23
+ * @param options - Retry options.
24
+ * @returns The resolved value of `fn`.
25
+ * @throws The last error thrown by `fn` when all attempts fail.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const data = await retry(() => fetch("/api/data").then((res) => res.json()), {
30
+ * retries: 3,
31
+ * delay: 500,
32
+ * })
33
+ * ```
34
+ */
35
+ export async function retry(fn, options = {}) {
36
+ const { retries = 3, delay = 0 } = options;
37
+ let lastError;
38
+ for (let attempt = 0; attempt <= retries; attempt += 1) {
39
+ try {
40
+ return await fn();
41
+ }
42
+ catch (error) {
43
+ lastError = error;
44
+ if (attempt < retries && delay > 0) {
45
+ await sleep(delay);
46
+ }
47
+ }
48
+ }
49
+ throw lastError;
50
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * A debounced function returned by {@link debounce}.
3
+ */
4
+ export interface DebouncedFunction<Args extends unknown[]> {
5
+ (...args: Args): void;
6
+ /** Cancels the pending invocation, if any. */
7
+ cancel: () => void;
8
+ }
9
+ /**
10
+ * Creates a debounced function that delays invoking `fn` until `wait`
11
+ * milliseconds have elapsed since the last call.
12
+ *
13
+ * @param fn - The function to debounce.
14
+ * @param wait - Delay in milliseconds. Defaults to 300.
15
+ * @returns A debounced function with a `cancel` method.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const search = debounce((keyword: string) => {
20
+ * console.log("searching:", keyword)
21
+ * }, 300)
22
+ *
23
+ * search("a")
24
+ * search("ab") // only "ab" is logged, 300ms after the last call
25
+ * search.cancel() // cancels the pending invocation
26
+ * ```
27
+ */
28
+ export declare function debounce<Args extends unknown[]>(fn: (...args: Args) => void, wait?: number): DebouncedFunction<Args>;
29
+ /**
30
+ * A throttled function returned by {@link throttle}.
31
+ */
32
+ export interface ThrottledFunction<Args extends unknown[]> {
33
+ (...args: Args): void;
34
+ /** Cancels the trailing invocation, if any. */
35
+ cancel: () => void;
36
+ }
37
+ /**
38
+ * Creates a throttled function that invokes `fn` at most once per `wait`
39
+ * milliseconds. Calls made during the window are invoked at the end of it.
40
+ *
41
+ * @param fn - The function to throttle.
42
+ * @param wait - Minimum interval between invocations in milliseconds. Defaults to 300.
43
+ * @returns A throttled function with a `cancel` method.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const onScroll = throttle(() => {
48
+ * console.log(window.scrollY)
49
+ * }, 200)
50
+ *
51
+ * window.addEventListener("scroll", onScroll)
52
+ * // ...later
53
+ * window.removeEventListener("scroll", onScroll)
54
+ * onScroll.cancel()
55
+ * ```
56
+ */
57
+ export declare function throttle<Args extends unknown[]>(fn: (...args: Args) => void, wait?: number): ThrottledFunction<Args>;
58
+ /**
59
+ * Wraps a function so it is executed only once. Subsequent calls return the
60
+ * result of the first invocation.
61
+ *
62
+ * @param fn - The function to wrap.
63
+ * @returns A function that runs `fn` at most once.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const init = once(() => {
68
+ * console.log("initialized")
69
+ * return 42
70
+ * })
71
+ *
72
+ * init() // logs "initialized", returns 42
73
+ * init() // returns 42 without logging again
74
+ * ```
75
+ */
76
+ export declare function once<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Creates a debounced function that delays invoking `fn` until `wait`
3
+ * milliseconds have elapsed since the last call.
4
+ *
5
+ * @param fn - The function to debounce.
6
+ * @param wait - Delay in milliseconds. Defaults to 300.
7
+ * @returns A debounced function with a `cancel` method.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const search = debounce((keyword: string) => {
12
+ * console.log("searching:", keyword)
13
+ * }, 300)
14
+ *
15
+ * search("a")
16
+ * search("ab") // only "ab" is logged, 300ms after the last call
17
+ * search.cancel() // cancels the pending invocation
18
+ * ```
19
+ */
20
+ export function debounce(fn, wait = 300) {
21
+ let timer;
22
+ const cancel = () => {
23
+ if (timer !== undefined) {
24
+ clearTimeout(timer);
25
+ timer = undefined;
26
+ }
27
+ };
28
+ const debounced = (...args) => {
29
+ cancel();
30
+ timer = setTimeout(() => {
31
+ timer = undefined;
32
+ fn(...args);
33
+ }, wait);
34
+ };
35
+ return Object.assign(debounced, { cancel });
36
+ }
37
+ /**
38
+ * Creates a throttled function that invokes `fn` at most once per `wait`
39
+ * milliseconds. Calls made during the window are invoked at the end of it.
40
+ *
41
+ * @param fn - The function to throttle.
42
+ * @param wait - Minimum interval between invocations in milliseconds. Defaults to 300.
43
+ * @returns A throttled function with a `cancel` method.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const onScroll = throttle(() => {
48
+ * console.log(window.scrollY)
49
+ * }, 200)
50
+ *
51
+ * window.addEventListener("scroll", onScroll)
52
+ * // ...later
53
+ * window.removeEventListener("scroll", onScroll)
54
+ * onScroll.cancel()
55
+ * ```
56
+ */
57
+ export function throttle(fn, wait = 300) {
58
+ let lastCall = 0;
59
+ let timer;
60
+ let lastArgs;
61
+ const invoke = () => {
62
+ lastCall = Date.now();
63
+ timer = undefined;
64
+ if (lastArgs !== undefined) {
65
+ fn(...lastArgs);
66
+ lastArgs = undefined;
67
+ }
68
+ };
69
+ const cancel = () => {
70
+ if (timer !== undefined) {
71
+ clearTimeout(timer);
72
+ timer = undefined;
73
+ }
74
+ lastArgs = undefined;
75
+ lastCall = 0;
76
+ };
77
+ const throttled = (...args) => {
78
+ lastArgs = args;
79
+ const remaining = wait - (Date.now() - lastCall);
80
+ if (remaining <= 0) {
81
+ if (timer !== undefined) {
82
+ clearTimeout(timer);
83
+ timer = undefined;
84
+ }
85
+ invoke();
86
+ return;
87
+ }
88
+ if (timer === undefined) {
89
+ timer = setTimeout(invoke, remaining);
90
+ }
91
+ };
92
+ return Object.assign(throttled, { cancel });
93
+ }
94
+ /**
95
+ * Wraps a function so it is executed only once. Subsequent calls return the
96
+ * result of the first invocation.
97
+ *
98
+ * @param fn - The function to wrap.
99
+ * @returns A function that runs `fn` at most once.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const init = once(() => {
104
+ * console.log("initialized")
105
+ * return 42
106
+ * })
107
+ *
108
+ * init() // logs "initialized", returns 42
109
+ * init() // returns 42 without logging again
110
+ * ```
111
+ */
112
+ export function once(fn) {
113
+ let called = false;
114
+ let result;
115
+ return (...args) => {
116
+ if (!called) {
117
+ called = true;
118
+ result = fn(...args);
119
+ }
120
+ return result;
121
+ };
122
+ }
package/dist/index.d.ts CHANGED
@@ -1 +1,10 @@
1
1
  export { readBoolean } from "./boolean.js";
2
+ export { isNil, isDefined, isPlainObject, isEmpty } from "./type.js";
3
+ export { unique, uniqueBy, chunk, groupBy } from "./array.js";
4
+ export { pick, omit, deepClone } from "./object.js";
5
+ export { capitalize, truncate, camelCase, kebabCase } from "./string.js";
6
+ export { clamp, randomInt } from "./number.js";
7
+ export { debounce, throttle, once } from "./function.js";
8
+ export type { DebouncedFunction, ThrottledFunction } from "./function.js";
9
+ export { sleep, retry } from "./async.js";
10
+ export type { RetryOptions } from "./async.js";
package/dist/index.js CHANGED
@@ -1 +1,8 @@
1
1
  export { readBoolean } from "./boolean.js";
2
+ export { isNil, isDefined, isPlainObject, isEmpty } from "./type.js";
3
+ export { unique, uniqueBy, chunk, groupBy } from "./array.js";
4
+ export { pick, omit, deepClone } from "./object.js";
5
+ export { capitalize, truncate, camelCase, kebabCase } from "./string.js";
6
+ export { clamp, randomInt } from "./number.js";
7
+ export { debounce, throttle, once } from "./function.js";
8
+ export { sleep, retry } from "./async.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Clamps a number within the inclusive `min` and `max` bounds.
3
+ *
4
+ * @param value - The number to clamp.
5
+ * @param min - The lower bound.
6
+ * @param max - The upper bound.
7
+ * @returns The clamped number.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * clamp(5, 0, 10) // 5
12
+ * clamp(-1, 0, 10) // 0
13
+ * clamp(99, 0, 10) // 10
14
+ * ```
15
+ */
16
+ export declare function clamp(value: number, min: number, max: number): number;
17
+ /**
18
+ * Returns a random integer between `min` and `max`, both inclusive.
19
+ *
20
+ * @param min - The lower bound.
21
+ * @param max - The upper bound.
22
+ * @returns A random integer within `[min, max]`.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * randomInt(1, 6) // e.g. 4
27
+ * ```
28
+ */
29
+ export declare function randomInt(min: number, max: number): number;
package/dist/number.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Clamps a number within the inclusive `min` and `max` bounds.
3
+ *
4
+ * @param value - The number to clamp.
5
+ * @param min - The lower bound.
6
+ * @param max - The upper bound.
7
+ * @returns The clamped number.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * clamp(5, 0, 10) // 5
12
+ * clamp(-1, 0, 10) // 0
13
+ * clamp(99, 0, 10) // 10
14
+ * ```
15
+ */
16
+ export function clamp(value, min, max) {
17
+ return Math.min(Math.max(value, min), max);
18
+ }
19
+ /**
20
+ * Returns a random integer between `min` and `max`, both inclusive.
21
+ *
22
+ * @param min - The lower bound.
23
+ * @param max - The upper bound.
24
+ * @returns A random integer within `[min, max]`.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * randomInt(1, 6) // e.g. 4
29
+ * ```
30
+ */
31
+ export function randomInt(min, max) {
32
+ const lower = Math.ceil(min);
33
+ const upper = Math.floor(max);
34
+ return Math.floor(Math.random() * (upper - lower + 1)) + lower;
35
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Creates a new object containing only the picked properties.
3
+ *
4
+ * @param object - The source object.
5
+ * @param keys - The property keys to pick.
6
+ * @returns A new object with only the picked properties.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * pick({ id: 1, name: "Alice", age: 30 }, ["id", "name"])
11
+ * // { id: 1, name: "Alice" }
12
+ * ```
13
+ */
14
+ export declare function pick<T extends object, K extends keyof T>(object: T, keys: readonly K[]): Pick<T, K>;
15
+ /**
16
+ * Creates a shallow copy of an object without the omitted properties.
17
+ *
18
+ * @param object - The source object.
19
+ * @param keys - The property keys to omit.
20
+ * @returns A shallow copy without the omitted properties.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * omit({ id: 1, name: "Alice", age: 30 }, ["age"])
25
+ * // { id: 1, name: "Alice" }
26
+ * ```
27
+ */
28
+ export declare function omit<T extends object, K extends keyof T>(object: T, keys: readonly K[]): Omit<T, K>;
29
+ /**
30
+ * Creates a deep clone of the given value. Plain objects, arrays and `Date`
31
+ * instances are cloned recursively, while other values are returned as-is.
32
+ *
33
+ * @param value - The value to clone.
34
+ * @returns A deep clone of the value.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const source = { user: { name: "Alice" }, tags: ["a"] }
39
+ * const copy = deepClone(source)
40
+ * copy.user.name = "Bob"
41
+ * source.user.name // "Alice" (the original is untouched)
42
+ * ```
43
+ */
44
+ export declare function deepClone<T>(value: T): T;
package/dist/object.js ADDED
@@ -0,0 +1,77 @@
1
+ import { isPlainObject } from "./type.js";
2
+ /**
3
+ * Creates a new object containing only the picked properties.
4
+ *
5
+ * @param object - The source object.
6
+ * @param keys - The property keys to pick.
7
+ * @returns A new object with only the picked properties.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * pick({ id: 1, name: "Alice", age: 30 }, ["id", "name"])
12
+ * // { id: 1, name: "Alice" }
13
+ * ```
14
+ */
15
+ export function pick(object, keys) {
16
+ const result = {};
17
+ for (const key of keys) {
18
+ if (key in object) {
19
+ result[key] = object[key];
20
+ }
21
+ }
22
+ return result;
23
+ }
24
+ /**
25
+ * Creates a shallow copy of an object without the omitted properties.
26
+ *
27
+ * @param object - The source object.
28
+ * @param keys - The property keys to omit.
29
+ * @returns A shallow copy without the omitted properties.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * omit({ id: 1, name: "Alice", age: 30 }, ["age"])
34
+ * // { id: 1, name: "Alice" }
35
+ * ```
36
+ */
37
+ export function omit(object, keys) {
38
+ const result = { ...object };
39
+ for (const key of keys) {
40
+ delete result[key];
41
+ }
42
+ return result;
43
+ }
44
+ function clone(value) {
45
+ if (Array.isArray(value)) {
46
+ return value.map((item) => clone(item));
47
+ }
48
+ if (value instanceof Date) {
49
+ return new Date(value.getTime());
50
+ }
51
+ if (isPlainObject(value)) {
52
+ const result = {};
53
+ for (const [key, item] of Object.entries(value)) {
54
+ result[key] = clone(item);
55
+ }
56
+ return result;
57
+ }
58
+ return value;
59
+ }
60
+ /**
61
+ * Creates a deep clone of the given value. Plain objects, arrays and `Date`
62
+ * instances are cloned recursively, while other values are returned as-is.
63
+ *
64
+ * @param value - The value to clone.
65
+ * @returns A deep clone of the value.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const source = { user: { name: "Alice" }, tags: ["a"] }
70
+ * const copy = deepClone(source)
71
+ * copy.user.name = "Bob"
72
+ * source.user.name // "Alice" (the original is untouched)
73
+ * ```
74
+ */
75
+ export function deepClone(value) {
76
+ return clone(value);
77
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Converts the first character of a string to upper case.
3
+ *
4
+ * @param value - The source string.
5
+ * @returns A new string with the first character upper-cased.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * capitalize("hello") // "Hello"
10
+ * capitalize("") // ""
11
+ * ```
12
+ */
13
+ export declare function capitalize(value: string): string;
14
+ /**
15
+ * Truncates a string to the given length, appending a suffix when it is cut.
16
+ *
17
+ * @param value - The source string.
18
+ * @param maxLength - Maximum length of the result, including the suffix.
19
+ * @param suffix - Appended when the string is truncated. Defaults to `"..."`.
20
+ * @returns The truncated string.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * truncate("Hello, world!", 8) // "Hello..."
25
+ * truncate("Hi", 8) // "Hi"
26
+ * ```
27
+ */
28
+ export declare function truncate(value: string, maxLength: number, suffix?: string): string;
29
+ /**
30
+ * Converts a string to camelCase.
31
+ *
32
+ * @param value - The source string. Segments may be separated by `-`, `_` or whitespace.
33
+ * @returns The camelCased string.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * camelCase("foo-bar baz") // "fooBarBaz"
38
+ * camelCase("FOO_BAR") // "fooBar"
39
+ * ```
40
+ */
41
+ export declare function camelCase(value: string): string;
42
+ /**
43
+ * Converts a string to kebab-case.
44
+ *
45
+ * @param value - The source string. camelCase boundaries, whitespace and `_` become `-`.
46
+ * @returns The kebab-cased string.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * kebabCase("fooBar") // "foo-bar"
51
+ * kebabCase("Foo_Bar baz") // "foo-bar-baz"
52
+ * ```
53
+ */
54
+ export declare function kebabCase(value: string): string;
package/dist/string.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Converts the first character of a string to upper case.
3
+ *
4
+ * @param value - The source string.
5
+ * @returns A new string with the first character upper-cased.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * capitalize("hello") // "Hello"
10
+ * capitalize("") // ""
11
+ * ```
12
+ */
13
+ export function capitalize(value) {
14
+ return value.charAt(0).toUpperCase() + value.slice(1);
15
+ }
16
+ /**
17
+ * Truncates a string to the given length, appending a suffix when it is cut.
18
+ *
19
+ * @param value - The source string.
20
+ * @param maxLength - Maximum length of the result, including the suffix.
21
+ * @param suffix - Appended when the string is truncated. Defaults to `"..."`.
22
+ * @returns The truncated string.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * truncate("Hello, world!", 8) // "Hello..."
27
+ * truncate("Hi", 8) // "Hi"
28
+ * ```
29
+ */
30
+ export function truncate(value, maxLength, suffix = "...") {
31
+ if (value.length <= maxLength) {
32
+ return value;
33
+ }
34
+ return value.slice(0, Math.max(0, maxLength - suffix.length)) + suffix;
35
+ }
36
+ /**
37
+ * Converts a string to camelCase.
38
+ *
39
+ * @param value - The source string. Segments may be separated by `-`, `_` or whitespace.
40
+ * @returns The camelCased string.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * camelCase("foo-bar baz") // "fooBarBaz"
45
+ * camelCase("FOO_BAR") // "fooBar"
46
+ * ```
47
+ */
48
+ export function camelCase(value) {
49
+ return value
50
+ .split(/[-_\s]+/)
51
+ .filter((word) => word.length > 0)
52
+ .map((word, index) => {
53
+ const lower = word.toLowerCase();
54
+ return index === 0 ? lower : capitalize(lower);
55
+ })
56
+ .join("");
57
+ }
58
+ /**
59
+ * Converts a string to kebab-case.
60
+ *
61
+ * @param value - The source string. camelCase boundaries, whitespace and `_` become `-`.
62
+ * @returns The kebab-cased string.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * kebabCase("fooBar") // "foo-bar"
67
+ * kebabCase("Foo_Bar baz") // "foo-bar-baz"
68
+ * ```
69
+ */
70
+ export function kebabCase(value) {
71
+ return value
72
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
73
+ .replace(/[\s_]+/g, "-")
74
+ .replace(/-+/g, "-")
75
+ .replace(/^-|-$/g, "")
76
+ .toLowerCase();
77
+ }
package/dist/type.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Checks whether a value is `null` or `undefined`.
3
+ *
4
+ * @param value - The value to check.
5
+ * @returns `true` when the value is `null` or `undefined`.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * isNil(null) // true
10
+ * isNil(undefined) // true
11
+ * isNil(0) // false
12
+ * ```
13
+ */
14
+ export declare function isNil(value: unknown): value is null | undefined;
15
+ /**
16
+ * Checks whether a value is neither `null` nor `undefined`, narrowing the type.
17
+ *
18
+ * @param value - The value to check.
19
+ * @returns `true` when the value is defined.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * isDefined(0) // true
24
+ * isDefined("") // true
25
+ * isDefined(null) // false
26
+ *
27
+ * const list = [1, null, 2].filter(isDefined) // number[]
28
+ * ```
29
+ */
30
+ export declare function isDefined<T>(value: T | null | undefined): value is T;
31
+ /**
32
+ * Checks whether a value is a plain object, e.g. created via `{}` or `new Object()`.
33
+ * Arrays, class instances, `Date`, `Map` and `null` are not considered plain objects.
34
+ *
35
+ * @param value - The value to check.
36
+ * @returns `true` when the value is a plain object.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * isPlainObject({}) // true
41
+ * isPlainObject(Object.create(null)) // true
42
+ * isPlainObject([]) // false
43
+ * isPlainObject(new Date()) // false
44
+ * ```
45
+ */
46
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
47
+ /**
48
+ * Checks whether a value is "empty". `null`/`undefined`, empty strings and arrays,
49
+ * empty `Map`/`Set` and plain objects without own keys are treated as empty.
50
+ *
51
+ * @param value - The value to check.
52
+ * @returns `true` when the value is empty.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * isEmpty(null) // true
57
+ * isEmpty("") // true
58
+ * isEmpty([]) // true
59
+ * isEmpty(new Map()) // true
60
+ * isEmpty({}) // true
61
+ * isEmpty({ a: 1 }) // false
62
+ * ```
63
+ */
64
+ export declare function isEmpty(value: unknown): boolean;
package/dist/type.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Checks whether a value is `null` or `undefined`.
3
+ *
4
+ * @param value - The value to check.
5
+ * @returns `true` when the value is `null` or `undefined`.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * isNil(null) // true
10
+ * isNil(undefined) // true
11
+ * isNil(0) // false
12
+ * ```
13
+ */
14
+ export function isNil(value) {
15
+ return value === null || value === undefined;
16
+ }
17
+ /**
18
+ * Checks whether a value is neither `null` nor `undefined`, narrowing the type.
19
+ *
20
+ * @param value - The value to check.
21
+ * @returns `true` when the value is defined.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * isDefined(0) // true
26
+ * isDefined("") // true
27
+ * isDefined(null) // false
28
+ *
29
+ * const list = [1, null, 2].filter(isDefined) // number[]
30
+ * ```
31
+ */
32
+ export function isDefined(value) {
33
+ return value !== null && value !== undefined;
34
+ }
35
+ /**
36
+ * Checks whether a value is a plain object, e.g. created via `{}` or `new Object()`.
37
+ * Arrays, class instances, `Date`, `Map` and `null` are not considered plain objects.
38
+ *
39
+ * @param value - The value to check.
40
+ * @returns `true` when the value is a plain object.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * isPlainObject({}) // true
45
+ * isPlainObject(Object.create(null)) // true
46
+ * isPlainObject([]) // false
47
+ * isPlainObject(new Date()) // false
48
+ * ```
49
+ */
50
+ export function isPlainObject(value) {
51
+ if (typeof value !== "object" || value === null) {
52
+ return false;
53
+ }
54
+ const prototype = Object.getPrototypeOf(value);
55
+ return prototype === Object.prototype || prototype === null;
56
+ }
57
+ /**
58
+ * Checks whether a value is "empty". `null`/`undefined`, empty strings and arrays,
59
+ * empty `Map`/`Set` and plain objects without own keys are treated as empty.
60
+ *
61
+ * @param value - The value to check.
62
+ * @returns `true` when the value is empty.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * isEmpty(null) // true
67
+ * isEmpty("") // true
68
+ * isEmpty([]) // true
69
+ * isEmpty(new Map()) // true
70
+ * isEmpty({}) // true
71
+ * isEmpty({ a: 1 }) // false
72
+ * ```
73
+ */
74
+ export function isEmpty(value) {
75
+ if (isNil(value)) {
76
+ return true;
77
+ }
78
+ if (typeof value === "string" || Array.isArray(value)) {
79
+ return value.length === 0;
80
+ }
81
+ if (value instanceof Map || value instanceof Set) {
82
+ return value.size === 0;
83
+ }
84
+ if (isPlainObject(value)) {
85
+ return Object.keys(value).length === 0;
86
+ }
87
+ return false;
88
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zuwy/frontend-engineering-system-utils",
3
3
  "author": "Wenyin Zu",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "Shared utility functions for @zuwy/frontend-engineering-system.",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -20,23 +20,22 @@
20
20
  "README.md",
21
21
  "LICENSE"
22
22
  ],
23
- "scripts": {
24
- "build": "tsc -p tsconfig.build.json",
25
- "lint": "eslint .",
26
- "typecheck": "tsc --noEmit",
27
- "pack:check": "npm pack --dry-run",
28
- "prepublishOnly": "pnpm lint && pnpm typecheck && pnpm build && pnpm pack:check"
29
- },
30
23
  "publishConfig": {
31
24
  "registry": "https://registry.npmjs.org/",
32
25
  "access": "public"
33
26
  },
34
27
  "devDependencies": {
35
- "@zuwy/frontend-engineering-system-eslint-config": "workspace:*",
36
- "@zuwy/frontend-engineering-system-ts-config": "workspace:*",
37
28
  "typescript": "~6.0.2",
38
29
  "eslint": "^10.5.0",
39
30
  "eslint-config-prettier": "^10.1.8",
40
- "typescript-eslint": "^8.61.0"
31
+ "typescript-eslint": "^8.61.0",
32
+ "@zuwy/frontend-engineering-system-eslint-config": "0.0.0",
33
+ "@zuwy/frontend-engineering-system-ts-config": "1.0.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "lint": "eslint .",
38
+ "typecheck": "tsc --noEmit",
39
+ "pack:check": "npm pack --dry-run"
41
40
  }
42
- }
41
+ }