@jthong/util 0.1.0-alpha.0 → 0.1.0-alpha.2
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 +7 -0
- package/dist/array/index.cjs +11 -0
- package/dist/array/index.cjs.map +1 -1
- package/dist/array/index.d.ts +91 -6
- package/dist/array/index.d.ts.map +1 -0
- package/dist/array/index.js +11 -1
- package/dist/array/index.js.map +1 -1
- package/dist/fn/index.cjs +54 -0
- package/dist/fn/index.cjs.map +1 -1
- package/dist/fn/index.d.ts +149 -5
- package/dist/fn/index.d.ts.map +1 -0
- package/dist/fn/index.js +51 -1
- package/dist/fn/index.js.map +1 -1
- package/dist/index.cjs +119 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -1
- package/dist/index.js.map +1 -1
- package/dist/object/index.cjs.map +1 -1
- package/dist/object/index.d.ts +44 -5
- package/dist/object/index.d.ts.map +1 -0
- package/dist/object/index.js.map +1 -1
- package/dist/string/index.cjs +11 -0
- package/dist/string/index.cjs.map +1 -1
- package/dist/string/index.d.ts +94 -6
- package/dist/string/index.d.ts.map +1 -0
- package/dist/string/index.js +10 -1
- package/dist/string/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/array/index.d.cts +0 -6
- package/dist/fn/index.d.cts +0 -5
- package/dist/index.d.cts +0 -4
- package/dist/object/index.d.cts +0 -5
- package/dist/string/index.d.cts +0 -6
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
// src/string/index.ts
|
|
10
|
+
var string_exports = {};
|
|
11
|
+
__export(string_exports, {
|
|
12
|
+
camelToKebab: () => camelToKebab,
|
|
13
|
+
capitalize: () => capitalize,
|
|
14
|
+
formatNumber: () => formatNumber,
|
|
15
|
+
kebabToCamel: () => kebabToCamel,
|
|
16
|
+
mask: () => mask,
|
|
17
|
+
truncate: () => truncate
|
|
18
|
+
});
|
|
4
19
|
var capitalize = (str) => str.length === 0 ? str : str[0].toUpperCase() + str.slice(1);
|
|
5
20
|
var camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, "");
|
|
6
21
|
var kebabToCamel = (str) => str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
7
22
|
var truncate = (str, max, suffix = "...") => str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;
|
|
23
|
+
var formatNumber = (n, locale = "en-US") => new Intl.NumberFormat(locale).format(n);
|
|
24
|
+
var mask = (str, opts = {}) => {
|
|
25
|
+
const start = Math.max(0, opts.start ?? 1);
|
|
26
|
+
const end = Math.max(0, opts.end ?? 0);
|
|
27
|
+
const char = opts.char ?? "*";
|
|
28
|
+
if (str.length <= start + end) return str;
|
|
29
|
+
const masked = char.repeat(str.length - start - end);
|
|
30
|
+
return str.slice(0, start) + masked + str.slice(str.length - end);
|
|
31
|
+
};
|
|
8
32
|
|
|
9
33
|
// src/array/index.ts
|
|
34
|
+
var array_exports = {};
|
|
35
|
+
__export(array_exports, {
|
|
36
|
+
chunk: () => chunk,
|
|
37
|
+
groupBy: () => groupBy,
|
|
38
|
+
range: () => range,
|
|
39
|
+
sortBy: () => sortBy,
|
|
40
|
+
unique: () => unique
|
|
41
|
+
});
|
|
10
42
|
var chunk = (arr, size) => {
|
|
11
43
|
if (size <= 0) return [];
|
|
12
44
|
const result = [];
|
|
@@ -24,6 +56,16 @@ var groupBy = (arr, keyFn) => {
|
|
|
24
56
|
}
|
|
25
57
|
return result;
|
|
26
58
|
};
|
|
59
|
+
var sortBy = (arr, keyFn, order = "asc") => {
|
|
60
|
+
const sign = order === "asc" ? 1 : -1;
|
|
61
|
+
return [...arr].sort((a, b) => {
|
|
62
|
+
const ka = keyFn(a);
|
|
63
|
+
const kb = keyFn(b);
|
|
64
|
+
if (ka < kb) return -1 * sign;
|
|
65
|
+
if (ka > kb) return 1 * sign;
|
|
66
|
+
return 0;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
27
69
|
var range = (start, end, step = 1) => {
|
|
28
70
|
const [from, to] = end === void 0 ? [0, start] : [start, end];
|
|
29
71
|
const result = [];
|
|
@@ -34,6 +76,12 @@ var range = (start, end, step = 1) => {
|
|
|
34
76
|
};
|
|
35
77
|
|
|
36
78
|
// src/object/index.ts
|
|
79
|
+
var object_exports = {};
|
|
80
|
+
__export(object_exports, {
|
|
81
|
+
isPlainObject: () => isPlainObject,
|
|
82
|
+
omit: () => omit,
|
|
83
|
+
pick: () => pick
|
|
84
|
+
});
|
|
37
85
|
var pick = (obj, keys) => {
|
|
38
86
|
const result = {};
|
|
39
87
|
for (const key of keys) {
|
|
@@ -55,6 +103,16 @@ var isPlainObject = (value) => {
|
|
|
55
103
|
};
|
|
56
104
|
|
|
57
105
|
// src/fn/index.ts
|
|
106
|
+
var fn_exports = {};
|
|
107
|
+
__export(fn_exports, {
|
|
108
|
+
debounce: () => debounce,
|
|
109
|
+
memoize: () => memoize,
|
|
110
|
+
once: () => once,
|
|
111
|
+
retry: () => retry,
|
|
112
|
+
sleep: () => sleep,
|
|
113
|
+
throttle: () => throttle,
|
|
114
|
+
withTimeout: () => withTimeout
|
|
115
|
+
});
|
|
58
116
|
var debounce = (fn, ms) => {
|
|
59
117
|
let timer;
|
|
60
118
|
return (...args) => {
|
|
@@ -73,20 +131,81 @@ var throttle = (fn, ms) => {
|
|
|
73
131
|
};
|
|
74
132
|
};
|
|
75
133
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
134
|
+
var once = (fn) => {
|
|
135
|
+
let called = false;
|
|
136
|
+
let result;
|
|
137
|
+
return (...args) => {
|
|
138
|
+
if (!called) {
|
|
139
|
+
called = true;
|
|
140
|
+
result = fn(...args);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
var memoize = (fn, keyFn = (...args) => JSON.stringify(args)) => {
|
|
146
|
+
const cache = /* @__PURE__ */ new Map();
|
|
147
|
+
return (...args) => {
|
|
148
|
+
const key = keyFn(...args);
|
|
149
|
+
const cached = cache.get(key);
|
|
150
|
+
if (cached !== void 0 || cache.has(key)) return cached;
|
|
151
|
+
const result = fn(...args);
|
|
152
|
+
cache.set(key, result);
|
|
153
|
+
return result;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
var retry = async (fn, opts = {}) => {
|
|
157
|
+
const { retries = 3, delay = 100, backoff = 2, onError } = opts;
|
|
158
|
+
let lastError;
|
|
159
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
160
|
+
try {
|
|
161
|
+
return await fn();
|
|
162
|
+
} catch (err) {
|
|
163
|
+
lastError = err;
|
|
164
|
+
onError?.(err, attempt);
|
|
165
|
+
if (attempt < retries) {
|
|
166
|
+
await sleep(delay * Math.pow(backoff, attempt));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
throw lastError;
|
|
171
|
+
};
|
|
172
|
+
var withTimeout = (promise, ms, errorMessage = "Operation timed out") => {
|
|
173
|
+
let timeoutId;
|
|
174
|
+
const timeout = new Promise((_, reject) => {
|
|
175
|
+
timeoutId = setTimeout(() => reject(new Error(errorMessage)), ms);
|
|
176
|
+
});
|
|
177
|
+
return Promise.race([
|
|
178
|
+
promise.finally(() => {
|
|
179
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
180
|
+
}),
|
|
181
|
+
timeout
|
|
182
|
+
]);
|
|
183
|
+
};
|
|
76
184
|
|
|
185
|
+
exports.array = array_exports;
|
|
77
186
|
exports.camelToKebab = camelToKebab;
|
|
78
187
|
exports.capitalize = capitalize;
|
|
79
188
|
exports.chunk = chunk;
|
|
80
189
|
exports.debounce = debounce;
|
|
190
|
+
exports.fn = fn_exports;
|
|
191
|
+
exports.formatNumber = formatNumber;
|
|
81
192
|
exports.groupBy = groupBy;
|
|
82
193
|
exports.isPlainObject = isPlainObject;
|
|
83
194
|
exports.kebabToCamel = kebabToCamel;
|
|
195
|
+
exports.mask = mask;
|
|
196
|
+
exports.memoize = memoize;
|
|
197
|
+
exports.object = object_exports;
|
|
84
198
|
exports.omit = omit;
|
|
199
|
+
exports.once = once;
|
|
85
200
|
exports.pick = pick;
|
|
86
201
|
exports.range = range;
|
|
202
|
+
exports.retry = retry;
|
|
87
203
|
exports.sleep = sleep;
|
|
204
|
+
exports.sortBy = sortBy;
|
|
205
|
+
exports.string = string_exports;
|
|
88
206
|
exports.throttle = throttle;
|
|
89
207
|
exports.truncate = truncate;
|
|
90
208
|
exports.unique = unique;
|
|
209
|
+
exports.withTimeout = withTimeout;
|
|
91
210
|
//# sourceMappingURL=index.cjs.map
|
|
92
211
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/string/index.ts","../src/array/index.ts","../src/object/index.ts","../src/fn/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export const capitalize = (str: string): string =>\n str.length === 0 ? str : str[0]!.toUpperCase() + str.slice(1);\n\nexport const camelToKebab = (str: string): string =>\n str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, \"\");\n\nexport const kebabToCamel = (str: string): string =>\n str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\nexport const truncate = (str: string, max: number, suffix = \"...\"): string =>\n str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;\n","export const chunk = <T>(arr: readonly T[], size: number): T[][] => {\n if (size <= 0) return [];\n const result: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n};\n\nexport const unique = <T>(arr: readonly T[]): T[] => Array.from(new Set(arr));\n\nexport const groupBy = <T, K extends PropertyKey>(\n arr: readonly T[],\n keyFn: (item: T) => K,\n): Record<K, T[]> => {\n const result = {} as Record<K, T[]>;\n for (const item of arr) {\n const key = keyFn(item);\n (result[key] ??= []).push(item);\n }\n return result;\n};\n\nexport const range = (start: number, end?: number, step = 1): number[] => {\n const [from, to] = end === undefined ? [0, start] : [start, end];\n const result: number[] = [];\n for (let i = from; step > 0 ? i < to : i > to; i += step) {\n result.push(i);\n }\n return result;\n};\n","export const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n","export const debounce = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return (...args: Args) => {\n if (timer !== undefined) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n };\n};\n\nexport const throttle = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let last = 0;\n return (...args: Args) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n };\n};\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/string/index.ts","../src/array/index.ts","../src/object/index.ts","../src/fn/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 문자열의 첫 글자를 대문자로 변환합니다.\n *\n * @param str - 변환할 문자열\n * @returns 첫 글자가 대문자가 된 문자열. 빈 문자열은 그대로 반환됩니다.\n * @example\n * ```ts\n * capitalize(\"hello\"); // \"Hello\"\n * capitalize(\"\"); // \"\"\n * ```\n */\nexport const capitalize = (str: string): string =>\n str.length === 0 ? str : str[0]!.toUpperCase() + str.slice(1);\n\n/**\n * camelCase 문자열을 kebab-case로 변환합니다.\n *\n * @param str - camelCase 문자열\n * @returns kebab-case로 변환된 문자열\n * @example\n * ```ts\n * camelToKebab(\"backgroundColor\"); // \"background-color\"\n * camelToKebab(\"HTMLParser\"); // \"h-t-m-l-parser\"\n * ```\n */\nexport const camelToKebab = (str: string): string =>\n str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, \"\");\n\n/**\n * kebab-case 문자열을 camelCase로 변환합니다.\n *\n * @param str - kebab-case 문자열\n * @returns camelCase로 변환된 문자열\n * @example\n * ```ts\n * kebabToCamel(\"background-color\"); // \"backgroundColor\"\n * kebabToCamel(\"data-test-id\"); // \"dataTestId\"\n * ```\n */\nexport const kebabToCamel = (str: string): string =>\n str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\n/**\n * 문자열이 최대 길이를 초과하면 잘라내고 접미사를 붙입니다.\n *\n * @param str - 원본 문자열\n * @param max - 결과의 최대 길이 (접미사 포함)\n * @param suffix - 잘렸을 때 붙일 접미사 (기본값 `\"...\"`)\n * @returns 최대 길이 이하면 원본, 초과하면 잘린 문자열 + 접미사\n * @example\n * ```ts\n * truncate(\"hello world\", 8); // \"hello...\"\n * truncate(\"hi\", 5); // \"hi\" (변경 없음)\n * truncate(\"hello world\", 8, \"…\"); // \"hello w…\"\n * ```\n */\nexport const truncate = (str: string, max: number, suffix = \"...\"): string =>\n str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;\n\n/**\n * 숫자에 천 단위 구분자를 추가합니다. `Intl.NumberFormat` 기반이라 음수/소수/로케일을 모두 지원합니다.\n *\n * @param n - 포맷할 숫자\n * @param locale - BCP 47 로케일 태그 (기본값 `\"en-US\"`)\n * @returns 천 단위 구분자가 들어간 문자열\n * @example\n * ```ts\n * formatNumber(1234567); // \"1,234,567\"\n * formatNumber(-1234.56); // \"-1,234.56\"\n * formatNumber(1234567, \"de-DE\"); // \"1.234.567\" (독일은 점이 구분자)\n * ```\n */\nexport const formatNumber = (n: number, locale = \"en-US\"): string =>\n new Intl.NumberFormat(locale).format(n);\n\n/**\n * {@link mask} 함수의 옵션.\n */\nexport interface MaskOptions {\n /** 앞에서 보존할 문자 수. 기본값 `1`. */\n start?: number;\n /** 뒤에서 보존할 문자 수. 기본값 `0`. */\n end?: number;\n /** 가릴 때 사용할 문자. 기본값 `\"*\"`. */\n char?: string;\n}\n\n/**\n * 문자열의 중간 일부를 마스킹 문자로 가립니다. 이메일/전화번호 등 민감 정보 표시에 유용합니다.\n *\n * 문자열이 `start + end`보다 짧으면 원본을 그대로 반환합니다.\n *\n * @param str - 원본 문자열\n * @param opts - 마스킹 옵션 ({@link MaskOptions} 참고)\n * @returns 마스킹된 문자열\n * @example\n * ```ts\n * mask(\"01012345678\", { start: 3, end: 4 }); // \"010****5678\"\n * mask(\"jthong@gmail.com\", { start: 1, end: 10 }); // \"j*****@gmail.com\"\n * mask(\"secret\", { start: 0, end: 0, char: \"#\" }); // \"######\"\n * mask(\"hi\", { start: 1, end: 1 }); // \"hi\" (너무 짧아 변경 없음)\n * ```\n */\nexport const mask = (str: string, opts: MaskOptions = {}): string => {\n const start = Math.max(0, opts.start ?? 1);\n const end = Math.max(0, opts.end ?? 0);\n const char = opts.char ?? \"*\";\n if (str.length <= start + end) return str;\n const masked = char.repeat(str.length - start - end);\n return str.slice(0, start) + masked + str.slice(str.length - end);\n};\n","/**\n * 배열을 지정한 크기로 분할합니다. 마지막 청크는 크기보다 작을 수 있습니다.\n *\n * @param arr - 분할할 배열\n * @param size - 각 청크의 크기 (양수)\n * @returns 청크들의 배열. `size <= 0`이면 빈 배열을 반환합니다.\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]\n * chunk([1, 2, 3], 5); // [[1, 2, 3]]\n * chunk([1, 2, 3], 0); // []\n * ```\n */\nexport const chunk = <T>(arr: readonly T[], size: number): T[][] => {\n if (size <= 0) return [];\n const result: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n};\n\n/**\n * 배열에서 중복 요소를 제거합니다. 등호 비교는 `Set`을 사용하므로 원시값/참조값 모두에 동작하지만,\n * 객체는 참조 동일성으로만 비교됩니다.\n *\n * @param arr - 원본 배열\n * @returns 중복이 제거된 새 배열 (입력 순서 유지)\n * @example\n * ```ts\n * unique([1, 2, 2, 3, 1]); // [1, 2, 3]\n * unique([\"a\", \"b\", \"a\"]); // [\"a\", \"b\"]\n * ```\n */\nexport const unique = <T>(arr: readonly T[]): T[] => Array.from(new Set(arr));\n\n/**\n * 키 함수가 반환한 값으로 배열을 그룹화합니다.\n *\n * @param arr - 그룹화할 배열\n * @param keyFn - 각 요소에서 그룹 키를 추출하는 함수\n * @returns 키별로 묶인 객체. 키는 `keyFn`의 반환값.\n * @example\n * ```ts\n * const users = [\n * { name: \"Alice\", role: \"admin\" },\n * { name: \"Bob\", role: \"user\" },\n * { name: \"Carol\", role: \"admin\" },\n * ];\n * groupBy(users, (u) => u.role);\n * // {\n * // admin: [{ name: \"Alice\", role: \"admin\" }, { name: \"Carol\", role: \"admin\" }],\n * // user: [{ name: \"Bob\", role: \"user\" }],\n * // }\n * ```\n */\nexport const groupBy = <T, K extends PropertyKey>(\n arr: readonly T[],\n keyFn: (item: T) => K,\n): Record<K, T[]> => {\n const result = {} as Record<K, T[]>;\n for (const item of arr) {\n const key = keyFn(item);\n (result[key] ??= []).push(item);\n }\n return result;\n};\n\ntype Comparable = string | number | bigint | boolean | Date;\n\n/**\n * 키 함수가 반환한 값을 기준으로 배열을 정렬합니다. **입력 배열은 변경되지 않습니다** (새 배열 반환).\n *\n * @param arr - 정렬할 배열\n * @param keyFn - 정렬 기준 값을 추출하는 함수 (`string` | `number` | `bigint` | `boolean` | `Date` 반환)\n * @param order - `\"asc\"` (오름차순, 기본값) 또는 `\"desc\"` (내림차순)\n * @returns 정렬된 새 배열\n * @example\n * ```ts\n * sortBy([{ n: 3 }, { n: 1 }, { n: 2 }], (i) => i.n);\n * // [{ n: 1 }, { n: 2 }, { n: 3 }]\n *\n * sortBy([\"banana\", \"apple\", \"cherry\"], (s) => s);\n * // [\"apple\", \"banana\", \"cherry\"]\n *\n * sortBy([1, 3, 2], (n) => n, \"desc\");\n * // [3, 2, 1]\n * ```\n */\nexport const sortBy = <T>(\n arr: readonly T[],\n keyFn: (item: T) => Comparable,\n order: \"asc\" | \"desc\" = \"asc\",\n): T[] => {\n const sign = order === \"asc\" ? 1 : -1;\n return [...arr].sort((a, b) => {\n const ka = keyFn(a);\n const kb = keyFn(b);\n if (ka < kb) return -1 * sign;\n if (ka > kb) return 1 * sign;\n return 0;\n });\n};\n\n/**\n * 정수 범위 배열을 생성합니다. Python의 `range()`와 같은 시맨틱.\n *\n * - 인자 1개: `[0, start)` 범위\n * - 인자 2개: `[start, end)` 범위\n * - 인자 3개: `[start, end)` 범위, `step` 간격\n *\n * @param start - 시작값 (또는 종료값, 인자 1개일 때)\n * @param end - 종료값 (배제, exclusive)\n * @param step - 증가폭. 기본값 `1`. 음수면 감소.\n * @returns 정수 배열\n * @example\n * ```ts\n * range(3); // [0, 1, 2]\n * range(2, 5); // [2, 3, 4]\n * range(0, 10, 3); // [0, 3, 6, 9]\n * range(5, 0, -1); // [5, 4, 3, 2, 1]\n * ```\n */\nexport const range = (start: number, end?: number, step = 1): number[] => {\n const [from, to] = end === undefined ? [0, start] : [start, end];\n const result: number[] = [];\n for (let i = from; step > 0 ? i < to : i > to; i += step) {\n result.push(i);\n }\n return result;\n};\n","/**\n * 객체에서 지정한 키만 추려낸 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 보존할 키들의 배열\n * @returns `keys`에 명시된 속성만 가진 새 객체\n * @example\n * ```ts\n * pick({ a: 1, b: 2, c: 3 }, [\"a\", \"c\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\n/**\n * 객체에서 지정한 키를 제외한 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 제외할 키들의 배열\n * @returns `keys`에 명시된 속성을 뺀 새 객체\n * @example\n * ```ts\n * omit({ a: 1, b: 2, c: 3 }, [\"b\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\n/**\n * 값이 **순수 객체**(plain object)인지 판별합니다.\n * `null`, 배열, 클래스 인스턴스, `Date` 등은 모두 `false`를 반환합니다.\n *\n * SSR/Node 환경에서도 안전하게 동작합니다 (브라우저 API 의존성 없음).\n *\n * @param value - 검사할 값\n * @returns `{}` 또는 `Object.create(null)`로 만든 객체면 `true`\n * @example\n * ```ts\n * isPlainObject({}); // true\n * isPlainObject({ a: 1 }); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject([]); // false\n * isPlainObject(new Date()); // false\n * isPlainObject(null); // false\n * ```\n */\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n","/**\n * 함수 호출을 지연시켜, 마지막 호출 후 `ms` 밀리초가 지나야 실제로 실행되도록 합니다.\n * 검색 입력, 윈도우 리사이즈 등 \"연속 입력이 멈춘 뒤\"에 한 번만 실행하고 싶을 때 사용합니다.\n *\n * @param fn - 디바운스할 함수\n * @param ms - 대기 시간 (밀리초)\n * @returns 디바운스가 적용된 새 함수\n * @example\n * ```ts\n * const onResize = debounce(() => console.log(\"resized\"), 200);\n * window.addEventListener(\"resize\", onResize);\n * // 사용자가 리사이즈를 멈춘 뒤 200ms 후에 한 번만 출력\n * ```\n */\nexport const debounce = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return (...args: Args) => {\n if (timer !== undefined) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n };\n};\n\n/**\n * `ms` 밀리초마다 최대 한 번씩만 함수가 실행되도록 제한합니다.\n * 스크롤/마우스 무브 등 너무 자주 호출되는 이벤트의 빈도를 줄일 때 사용합니다.\n *\n * @param fn - 스로틀할 함수\n * @param ms - 최소 호출 간격 (밀리초)\n * @returns 스로틀이 적용된 새 함수\n * @example\n * ```ts\n * const onScroll = throttle(() => console.log(\"scroll\"), 100);\n * window.addEventListener(\"scroll\", onScroll);\n * // 100ms에 최대 1번만 출력\n * ```\n */\nexport const throttle = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let last = 0;\n return (...args: Args) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n };\n};\n\n/**\n * 지정한 시간만큼 대기하는 Promise를 반환합니다.\n *\n * @param ms - 대기 시간 (밀리초)\n * @returns `ms` 이후 resolve되는 Promise\n * @example\n * ```ts\n * async function task() {\n * console.log(\"start\");\n * await sleep(1000);\n * console.log(\"1초 뒤\");\n * }\n * ```\n */\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * 첫 호출의 결과를 저장하고, 이후 호출 시 인자에 관계없이 같은 결과를 반환합니다.\n * 초기화 함수나 싱글톤 생성 등 \"한 번만 실행되어야 하는 작업\"에 유용합니다.\n *\n * @param fn - 한 번만 실행할 함수\n * @returns 첫 호출 결과를 캐싱하는 래퍼 함수\n * @example\n * ```ts\n * const init = once(() => {\n * console.log(\"initialized\");\n * return { ready: true };\n * });\n *\n * init(); // \"initialized\" 출력 후 { ready: true } 반환\n * init(); // 출력 없음, 같은 객체 반환\n * ```\n */\nexport const once = <Args extends unknown[], R>(\n fn: (...args: Args) => R,\n): ((...args: Args) => R) => {\n let called = false;\n let result: R;\n return (...args: Args): R => {\n if (!called) {\n called = true;\n result = fn(...args);\n }\n return result;\n };\n};\n\n/**\n * 함수의 결과를 인자별로 캐싱합니다. 동일한 인자로 다시 호출되면 캐시된 결과를 반환합니다.\n *\n * 기본 캐시 키는 `JSON.stringify(args)`로 생성되므로 객체 인자도 값 비교가 가능합니다.\n * 필요하면 `keyFn`을 직접 지정해 키 생성 로직을 커스터마이즈하세요.\n *\n * @param fn - 결과를 캐싱할 함수\n * @param keyFn - 인자에서 캐시 키를 만드는 함수 (기본값: `JSON.stringify(args)`)\n * @returns 결과 캐싱이 적용된 래퍼 함수\n * @example\n * ```ts\n * const slow = memoize((n: number) => {\n * console.log(\"compute\", n);\n * return n * 2;\n * });\n *\n * slow(2); // \"compute 2\" 출력 후 4\n * slow(2); // 출력 없음, 4\n * slow(3); // \"compute 3\" 출력 후 6\n *\n * // 커스텀 키: 객체 id 기준 캐싱\n * const byId = memoize(\n * (user: { id: number; name: string }) => fetchProfile(user.id),\n * (user) => String(user.id),\n * );\n * ```\n */\nexport const memoize = <Args extends unknown[], R>(\n fn: (...args: Args) => R,\n keyFn: (...args: Args) => string = (...args) => JSON.stringify(args),\n): ((...args: Args) => R) => {\n const cache = new Map<string, R>();\n return (...args: Args): R => {\n const key = keyFn(...args);\n const cached = cache.get(key);\n if (cached !== undefined || cache.has(key)) return cached as R;\n const result = fn(...args);\n cache.set(key, result);\n return result;\n };\n};\n\n/**\n * {@link retry} 함수의 옵션.\n */\nexport interface RetryOptions {\n /** 추가 재시도 횟수. 총 시도 횟수는 `retries + 1`. 기본값 `3`. */\n retries?: number;\n /** 첫 재시도 전 대기 시간(ms). 기본값 `100`. */\n delay?: number;\n /** 매 재시도마다 대기 시간에 곱해질 배수 (지수 백오프). 기본값 `2`. */\n backoff?: number;\n /** 매 실패마다 호출되는 콜백. `attempt`는 0부터 시작하는 시도 인덱스. */\n onError?: (error: unknown, attempt: number) => void;\n}\n\n/**\n * 함수를 호출하다가 예외가 나면 지수 백오프로 재시도합니다. 동기/비동기 함수 모두 지원합니다.\n *\n * 모든 시도가 실패하면 마지막 에러를 throw합니다.\n *\n * @param fn - 실행할 함수 (동기 또는 비동기)\n * @param opts - 재시도 옵션 ({@link RetryOptions} 참고)\n * @returns 함수의 결과를 담은 Promise\n * @example\n * ```ts\n * // 기본 옵션: 최대 3회 재시도, 100ms부터 2배씩 백오프 (100, 200, 400ms)\n * const data = await retry(() => fetch(\"/api/data\").then((r) => r.json()));\n *\n * // 커스텀 옵션\n * await retry(unstableTask, {\n * retries: 5,\n * delay: 200,\n * backoff: 1.5,\n * onError: (err, attempt) => console.warn(`시도 ${attempt + 1} 실패`, err),\n * });\n * ```\n */\nexport const retry = async <T>(\n fn: () => Promise<T> | T,\n opts: RetryOptions = {},\n): Promise<T> => {\n const { retries = 3, delay = 100, backoff = 2, onError } = opts;\n let lastError: unknown;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n onError?.(err, attempt);\n if (attempt < retries) {\n await sleep(delay * Math.pow(backoff, attempt));\n }\n }\n }\n throw lastError;\n};\n\n/**\n * Promise에 타임아웃을 부여합니다. 지정한 시간 안에 settle되지 않으면 reject됩니다.\n *\n * 원본 Promise가 먼저 끝나면 내부 타이머는 자동으로 해제되므로 타이머 누수가 없습니다.\n *\n * @param promise - 타임아웃을 걸 Promise\n * @param ms - 제한 시간 (밀리초)\n * @param errorMessage - 타임아웃 시 던질 에러 메시지 (기본값 `\"Operation timed out\"`)\n * @returns 원본 결과 또는 타임아웃 에러로 settle되는 Promise\n * @example\n * ```ts\n * try {\n * const data = await withTimeout(fetch(\"/slow-api\"), 3000);\n * } catch (err) {\n * // 3초 안에 안 오면 여기로 떨어짐\n * }\n *\n * await withTimeout(longTask(), 5000, \"작업이 너무 오래 걸립니다\");\n * ```\n */\nexport const withTimeout = <T>(\n promise: Promise<T>,\n ms: number,\n errorMessage = \"Operation timed out\",\n): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(errorMessage)), ms);\n });\n return Promise.race([\n promise.finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }),\n timeout,\n ]);\n};\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export * from "./string/index.js";
|
|
2
|
+
export * from "./array/index.js";
|
|
3
|
+
export * from "./object/index.js";
|
|
4
|
+
export * from "./fn/index.js";
|
|
5
|
+
import * as string from "./string/index.js";
|
|
6
|
+
import * as array from "./array/index.js";
|
|
7
|
+
import * as object from "./object/index.js";
|
|
8
|
+
import * as fn from "./fn/index.js";
|
|
9
|
+
export { string, array, object, fn };
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAE9B,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,eAAe,CAAC;AAEpC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
// src/string/index.ts
|
|
8
|
+
var string_exports = {};
|
|
9
|
+
__export(string_exports, {
|
|
10
|
+
camelToKebab: () => camelToKebab,
|
|
11
|
+
capitalize: () => capitalize,
|
|
12
|
+
formatNumber: () => formatNumber,
|
|
13
|
+
kebabToCamel: () => kebabToCamel,
|
|
14
|
+
mask: () => mask,
|
|
15
|
+
truncate: () => truncate
|
|
16
|
+
});
|
|
2
17
|
var capitalize = (str) => str.length === 0 ? str : str[0].toUpperCase() + str.slice(1);
|
|
3
18
|
var camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, "");
|
|
4
19
|
var kebabToCamel = (str) => str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
5
20
|
var truncate = (str, max, suffix = "...") => str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;
|
|
21
|
+
var formatNumber = (n, locale = "en-US") => new Intl.NumberFormat(locale).format(n);
|
|
22
|
+
var mask = (str, opts = {}) => {
|
|
23
|
+
const start = Math.max(0, opts.start ?? 1);
|
|
24
|
+
const end = Math.max(0, opts.end ?? 0);
|
|
25
|
+
const char = opts.char ?? "*";
|
|
26
|
+
if (str.length <= start + end) return str;
|
|
27
|
+
const masked = char.repeat(str.length - start - end);
|
|
28
|
+
return str.slice(0, start) + masked + str.slice(str.length - end);
|
|
29
|
+
};
|
|
6
30
|
|
|
7
31
|
// src/array/index.ts
|
|
32
|
+
var array_exports = {};
|
|
33
|
+
__export(array_exports, {
|
|
34
|
+
chunk: () => chunk,
|
|
35
|
+
groupBy: () => groupBy,
|
|
36
|
+
range: () => range,
|
|
37
|
+
sortBy: () => sortBy,
|
|
38
|
+
unique: () => unique
|
|
39
|
+
});
|
|
8
40
|
var chunk = (arr, size) => {
|
|
9
41
|
if (size <= 0) return [];
|
|
10
42
|
const result = [];
|
|
@@ -22,6 +54,16 @@ var groupBy = (arr, keyFn) => {
|
|
|
22
54
|
}
|
|
23
55
|
return result;
|
|
24
56
|
};
|
|
57
|
+
var sortBy = (arr, keyFn, order = "asc") => {
|
|
58
|
+
const sign = order === "asc" ? 1 : -1;
|
|
59
|
+
return [...arr].sort((a, b) => {
|
|
60
|
+
const ka = keyFn(a);
|
|
61
|
+
const kb = keyFn(b);
|
|
62
|
+
if (ka < kb) return -1 * sign;
|
|
63
|
+
if (ka > kb) return 1 * sign;
|
|
64
|
+
return 0;
|
|
65
|
+
});
|
|
66
|
+
};
|
|
25
67
|
var range = (start, end, step = 1) => {
|
|
26
68
|
const [from, to] = end === void 0 ? [0, start] : [start, end];
|
|
27
69
|
const result = [];
|
|
@@ -32,6 +74,12 @@ var range = (start, end, step = 1) => {
|
|
|
32
74
|
};
|
|
33
75
|
|
|
34
76
|
// src/object/index.ts
|
|
77
|
+
var object_exports = {};
|
|
78
|
+
__export(object_exports, {
|
|
79
|
+
isPlainObject: () => isPlainObject,
|
|
80
|
+
omit: () => omit,
|
|
81
|
+
pick: () => pick
|
|
82
|
+
});
|
|
35
83
|
var pick = (obj, keys) => {
|
|
36
84
|
const result = {};
|
|
37
85
|
for (const key of keys) {
|
|
@@ -53,6 +101,16 @@ var isPlainObject = (value) => {
|
|
|
53
101
|
};
|
|
54
102
|
|
|
55
103
|
// src/fn/index.ts
|
|
104
|
+
var fn_exports = {};
|
|
105
|
+
__export(fn_exports, {
|
|
106
|
+
debounce: () => debounce,
|
|
107
|
+
memoize: () => memoize,
|
|
108
|
+
once: () => once,
|
|
109
|
+
retry: () => retry,
|
|
110
|
+
sleep: () => sleep,
|
|
111
|
+
throttle: () => throttle,
|
|
112
|
+
withTimeout: () => withTimeout
|
|
113
|
+
});
|
|
56
114
|
var debounce = (fn, ms) => {
|
|
57
115
|
let timer;
|
|
58
116
|
return (...args) => {
|
|
@@ -71,7 +129,57 @@ var throttle = (fn, ms) => {
|
|
|
71
129
|
};
|
|
72
130
|
};
|
|
73
131
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
132
|
+
var once = (fn) => {
|
|
133
|
+
let called = false;
|
|
134
|
+
let result;
|
|
135
|
+
return (...args) => {
|
|
136
|
+
if (!called) {
|
|
137
|
+
called = true;
|
|
138
|
+
result = fn(...args);
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
var memoize = (fn, keyFn = (...args) => JSON.stringify(args)) => {
|
|
144
|
+
const cache = /* @__PURE__ */ new Map();
|
|
145
|
+
return (...args) => {
|
|
146
|
+
const key = keyFn(...args);
|
|
147
|
+
const cached = cache.get(key);
|
|
148
|
+
if (cached !== void 0 || cache.has(key)) return cached;
|
|
149
|
+
const result = fn(...args);
|
|
150
|
+
cache.set(key, result);
|
|
151
|
+
return result;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
var retry = async (fn, opts = {}) => {
|
|
155
|
+
const { retries = 3, delay = 100, backoff = 2, onError } = opts;
|
|
156
|
+
let lastError;
|
|
157
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
158
|
+
try {
|
|
159
|
+
return await fn();
|
|
160
|
+
} catch (err) {
|
|
161
|
+
lastError = err;
|
|
162
|
+
onError?.(err, attempt);
|
|
163
|
+
if (attempt < retries) {
|
|
164
|
+
await sleep(delay * Math.pow(backoff, attempt));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
throw lastError;
|
|
169
|
+
};
|
|
170
|
+
var withTimeout = (promise, ms, errorMessage = "Operation timed out") => {
|
|
171
|
+
let timeoutId;
|
|
172
|
+
const timeout = new Promise((_, reject) => {
|
|
173
|
+
timeoutId = setTimeout(() => reject(new Error(errorMessage)), ms);
|
|
174
|
+
});
|
|
175
|
+
return Promise.race([
|
|
176
|
+
promise.finally(() => {
|
|
177
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
178
|
+
}),
|
|
179
|
+
timeout
|
|
180
|
+
]);
|
|
181
|
+
};
|
|
74
182
|
|
|
75
|
-
export { camelToKebab, capitalize, chunk, debounce, groupBy, isPlainObject, kebabToCamel, omit, pick, range, sleep, throttle, truncate, unique };
|
|
183
|
+
export { array_exports as array, camelToKebab, capitalize, chunk, debounce, fn_exports as fn, formatNumber, groupBy, isPlainObject, kebabToCamel, mask, memoize, object_exports as object, omit, once, pick, range, retry, sleep, sortBy, string_exports as string, throttle, truncate, unique, withTimeout };
|
|
76
184
|
//# sourceMappingURL=index.js.map
|
|
77
185
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/string/index.ts","../src/array/index.ts","../src/object/index.ts","../src/fn/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export const capitalize = (str: string): string =>\n str.length === 0 ? str : str[0]!.toUpperCase() + str.slice(1);\n\nexport const camelToKebab = (str: string): string =>\n str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, \"\");\n\nexport const kebabToCamel = (str: string): string =>\n str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\nexport const truncate = (str: string, max: number, suffix = \"...\"): string =>\n str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;\n","export const chunk = <T>(arr: readonly T[], size: number): T[][] => {\n if (size <= 0) return [];\n const result: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n};\n\nexport const unique = <T>(arr: readonly T[]): T[] => Array.from(new Set(arr));\n\nexport const groupBy = <T, K extends PropertyKey>(\n arr: readonly T[],\n keyFn: (item: T) => K,\n): Record<K, T[]> => {\n const result = {} as Record<K, T[]>;\n for (const item of arr) {\n const key = keyFn(item);\n (result[key] ??= []).push(item);\n }\n return result;\n};\n\nexport const range = (start: number, end?: number, step = 1): number[] => {\n const [from, to] = end === undefined ? [0, start] : [start, end];\n const result: number[] = [];\n for (let i = from; step > 0 ? i < to : i > to; i += step) {\n result.push(i);\n }\n return result;\n};\n","export const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n","export const debounce = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return (...args: Args) => {\n if (timer !== undefined) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n };\n};\n\nexport const throttle = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let last = 0;\n return (...args: Args) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n };\n};\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/string/index.ts","../src/array/index.ts","../src/object/index.ts","../src/fn/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 문자열의 첫 글자를 대문자로 변환합니다.\n *\n * @param str - 변환할 문자열\n * @returns 첫 글자가 대문자가 된 문자열. 빈 문자열은 그대로 반환됩니다.\n * @example\n * ```ts\n * capitalize(\"hello\"); // \"Hello\"\n * capitalize(\"\"); // \"\"\n * ```\n */\nexport const capitalize = (str: string): string =>\n str.length === 0 ? str : str[0]!.toUpperCase() + str.slice(1);\n\n/**\n * camelCase 문자열을 kebab-case로 변환합니다.\n *\n * @param str - camelCase 문자열\n * @returns kebab-case로 변환된 문자열\n * @example\n * ```ts\n * camelToKebab(\"backgroundColor\"); // \"background-color\"\n * camelToKebab(\"HTMLParser\"); // \"h-t-m-l-parser\"\n * ```\n */\nexport const camelToKebab = (str: string): string =>\n str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, \"\");\n\n/**\n * kebab-case 문자열을 camelCase로 변환합니다.\n *\n * @param str - kebab-case 문자열\n * @returns camelCase로 변환된 문자열\n * @example\n * ```ts\n * kebabToCamel(\"background-color\"); // \"backgroundColor\"\n * kebabToCamel(\"data-test-id\"); // \"dataTestId\"\n * ```\n */\nexport const kebabToCamel = (str: string): string =>\n str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\n/**\n * 문자열이 최대 길이를 초과하면 잘라내고 접미사를 붙입니다.\n *\n * @param str - 원본 문자열\n * @param max - 결과의 최대 길이 (접미사 포함)\n * @param suffix - 잘렸을 때 붙일 접미사 (기본값 `\"...\"`)\n * @returns 최대 길이 이하면 원본, 초과하면 잘린 문자열 + 접미사\n * @example\n * ```ts\n * truncate(\"hello world\", 8); // \"hello...\"\n * truncate(\"hi\", 5); // \"hi\" (변경 없음)\n * truncate(\"hello world\", 8, \"…\"); // \"hello w…\"\n * ```\n */\nexport const truncate = (str: string, max: number, suffix = \"...\"): string =>\n str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;\n\n/**\n * 숫자에 천 단위 구분자를 추가합니다. `Intl.NumberFormat` 기반이라 음수/소수/로케일을 모두 지원합니다.\n *\n * @param n - 포맷할 숫자\n * @param locale - BCP 47 로케일 태그 (기본값 `\"en-US\"`)\n * @returns 천 단위 구분자가 들어간 문자열\n * @example\n * ```ts\n * formatNumber(1234567); // \"1,234,567\"\n * formatNumber(-1234.56); // \"-1,234.56\"\n * formatNumber(1234567, \"de-DE\"); // \"1.234.567\" (독일은 점이 구분자)\n * ```\n */\nexport const formatNumber = (n: number, locale = \"en-US\"): string =>\n new Intl.NumberFormat(locale).format(n);\n\n/**\n * {@link mask} 함수의 옵션.\n */\nexport interface MaskOptions {\n /** 앞에서 보존할 문자 수. 기본값 `1`. */\n start?: number;\n /** 뒤에서 보존할 문자 수. 기본값 `0`. */\n end?: number;\n /** 가릴 때 사용할 문자. 기본값 `\"*\"`. */\n char?: string;\n}\n\n/**\n * 문자열의 중간 일부를 마스킹 문자로 가립니다. 이메일/전화번호 등 민감 정보 표시에 유용합니다.\n *\n * 문자열이 `start + end`보다 짧으면 원본을 그대로 반환합니다.\n *\n * @param str - 원본 문자열\n * @param opts - 마스킹 옵션 ({@link MaskOptions} 참고)\n * @returns 마스킹된 문자열\n * @example\n * ```ts\n * mask(\"01012345678\", { start: 3, end: 4 }); // \"010****5678\"\n * mask(\"jthong@gmail.com\", { start: 1, end: 10 }); // \"j*****@gmail.com\"\n * mask(\"secret\", { start: 0, end: 0, char: \"#\" }); // \"######\"\n * mask(\"hi\", { start: 1, end: 1 }); // \"hi\" (너무 짧아 변경 없음)\n * ```\n */\nexport const mask = (str: string, opts: MaskOptions = {}): string => {\n const start = Math.max(0, opts.start ?? 1);\n const end = Math.max(0, opts.end ?? 0);\n const char = opts.char ?? \"*\";\n if (str.length <= start + end) return str;\n const masked = char.repeat(str.length - start - end);\n return str.slice(0, start) + masked + str.slice(str.length - end);\n};\n","/**\n * 배열을 지정한 크기로 분할합니다. 마지막 청크는 크기보다 작을 수 있습니다.\n *\n * @param arr - 분할할 배열\n * @param size - 각 청크의 크기 (양수)\n * @returns 청크들의 배열. `size <= 0`이면 빈 배열을 반환합니다.\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]\n * chunk([1, 2, 3], 5); // [[1, 2, 3]]\n * chunk([1, 2, 3], 0); // []\n * ```\n */\nexport const chunk = <T>(arr: readonly T[], size: number): T[][] => {\n if (size <= 0) return [];\n const result: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n};\n\n/**\n * 배열에서 중복 요소를 제거합니다. 등호 비교는 `Set`을 사용하므로 원시값/참조값 모두에 동작하지만,\n * 객체는 참조 동일성으로만 비교됩니다.\n *\n * @param arr - 원본 배열\n * @returns 중복이 제거된 새 배열 (입력 순서 유지)\n * @example\n * ```ts\n * unique([1, 2, 2, 3, 1]); // [1, 2, 3]\n * unique([\"a\", \"b\", \"a\"]); // [\"a\", \"b\"]\n * ```\n */\nexport const unique = <T>(arr: readonly T[]): T[] => Array.from(new Set(arr));\n\n/**\n * 키 함수가 반환한 값으로 배열을 그룹화합니다.\n *\n * @param arr - 그룹화할 배열\n * @param keyFn - 각 요소에서 그룹 키를 추출하는 함수\n * @returns 키별로 묶인 객체. 키는 `keyFn`의 반환값.\n * @example\n * ```ts\n * const users = [\n * { name: \"Alice\", role: \"admin\" },\n * { name: \"Bob\", role: \"user\" },\n * { name: \"Carol\", role: \"admin\" },\n * ];\n * groupBy(users, (u) => u.role);\n * // {\n * // admin: [{ name: \"Alice\", role: \"admin\" }, { name: \"Carol\", role: \"admin\" }],\n * // user: [{ name: \"Bob\", role: \"user\" }],\n * // }\n * ```\n */\nexport const groupBy = <T, K extends PropertyKey>(\n arr: readonly T[],\n keyFn: (item: T) => K,\n): Record<K, T[]> => {\n const result = {} as Record<K, T[]>;\n for (const item of arr) {\n const key = keyFn(item);\n (result[key] ??= []).push(item);\n }\n return result;\n};\n\ntype Comparable = string | number | bigint | boolean | Date;\n\n/**\n * 키 함수가 반환한 값을 기준으로 배열을 정렬합니다. **입력 배열은 변경되지 않습니다** (새 배열 반환).\n *\n * @param arr - 정렬할 배열\n * @param keyFn - 정렬 기준 값을 추출하는 함수 (`string` | `number` | `bigint` | `boolean` | `Date` 반환)\n * @param order - `\"asc\"` (오름차순, 기본값) 또는 `\"desc\"` (내림차순)\n * @returns 정렬된 새 배열\n * @example\n * ```ts\n * sortBy([{ n: 3 }, { n: 1 }, { n: 2 }], (i) => i.n);\n * // [{ n: 1 }, { n: 2 }, { n: 3 }]\n *\n * sortBy([\"banana\", \"apple\", \"cherry\"], (s) => s);\n * // [\"apple\", \"banana\", \"cherry\"]\n *\n * sortBy([1, 3, 2], (n) => n, \"desc\");\n * // [3, 2, 1]\n * ```\n */\nexport const sortBy = <T>(\n arr: readonly T[],\n keyFn: (item: T) => Comparable,\n order: \"asc\" | \"desc\" = \"asc\",\n): T[] => {\n const sign = order === \"asc\" ? 1 : -1;\n return [...arr].sort((a, b) => {\n const ka = keyFn(a);\n const kb = keyFn(b);\n if (ka < kb) return -1 * sign;\n if (ka > kb) return 1 * sign;\n return 0;\n });\n};\n\n/**\n * 정수 범위 배열을 생성합니다. Python의 `range()`와 같은 시맨틱.\n *\n * - 인자 1개: `[0, start)` 범위\n * - 인자 2개: `[start, end)` 범위\n * - 인자 3개: `[start, end)` 범위, `step` 간격\n *\n * @param start - 시작값 (또는 종료값, 인자 1개일 때)\n * @param end - 종료값 (배제, exclusive)\n * @param step - 증가폭. 기본값 `1`. 음수면 감소.\n * @returns 정수 배열\n * @example\n * ```ts\n * range(3); // [0, 1, 2]\n * range(2, 5); // [2, 3, 4]\n * range(0, 10, 3); // [0, 3, 6, 9]\n * range(5, 0, -1); // [5, 4, 3, 2, 1]\n * ```\n */\nexport const range = (start: number, end?: number, step = 1): number[] => {\n const [from, to] = end === undefined ? [0, start] : [start, end];\n const result: number[] = [];\n for (let i = from; step > 0 ? i < to : i > to; i += step) {\n result.push(i);\n }\n return result;\n};\n","/**\n * 객체에서 지정한 키만 추려낸 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 보존할 키들의 배열\n * @returns `keys`에 명시된 속성만 가진 새 객체\n * @example\n * ```ts\n * pick({ a: 1, b: 2, c: 3 }, [\"a\", \"c\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\n/**\n * 객체에서 지정한 키를 제외한 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 제외할 키들의 배열\n * @returns `keys`에 명시된 속성을 뺀 새 객체\n * @example\n * ```ts\n * omit({ a: 1, b: 2, c: 3 }, [\"b\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\n/**\n * 값이 **순수 객체**(plain object)인지 판별합니다.\n * `null`, 배열, 클래스 인스턴스, `Date` 등은 모두 `false`를 반환합니다.\n *\n * SSR/Node 환경에서도 안전하게 동작합니다 (브라우저 API 의존성 없음).\n *\n * @param value - 검사할 값\n * @returns `{}` 또는 `Object.create(null)`로 만든 객체면 `true`\n * @example\n * ```ts\n * isPlainObject({}); // true\n * isPlainObject({ a: 1 }); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject([]); // false\n * isPlainObject(new Date()); // false\n * isPlainObject(null); // false\n * ```\n */\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n","/**\n * 함수 호출을 지연시켜, 마지막 호출 후 `ms` 밀리초가 지나야 실제로 실행되도록 합니다.\n * 검색 입력, 윈도우 리사이즈 등 \"연속 입력이 멈춘 뒤\"에 한 번만 실행하고 싶을 때 사용합니다.\n *\n * @param fn - 디바운스할 함수\n * @param ms - 대기 시간 (밀리초)\n * @returns 디바운스가 적용된 새 함수\n * @example\n * ```ts\n * const onResize = debounce(() => console.log(\"resized\"), 200);\n * window.addEventListener(\"resize\", onResize);\n * // 사용자가 리사이즈를 멈춘 뒤 200ms 후에 한 번만 출력\n * ```\n */\nexport const debounce = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return (...args: Args) => {\n if (timer !== undefined) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n };\n};\n\n/**\n * `ms` 밀리초마다 최대 한 번씩만 함수가 실행되도록 제한합니다.\n * 스크롤/마우스 무브 등 너무 자주 호출되는 이벤트의 빈도를 줄일 때 사용합니다.\n *\n * @param fn - 스로틀할 함수\n * @param ms - 최소 호출 간격 (밀리초)\n * @returns 스로틀이 적용된 새 함수\n * @example\n * ```ts\n * const onScroll = throttle(() => console.log(\"scroll\"), 100);\n * window.addEventListener(\"scroll\", onScroll);\n * // 100ms에 최대 1번만 출력\n * ```\n */\nexport const throttle = <Args extends unknown[]>(\n fn: (...args: Args) => void,\n ms: number,\n): ((...args: Args) => void) => {\n let last = 0;\n return (...args: Args) => {\n const now = Date.now();\n if (now - last >= ms) {\n last = now;\n fn(...args);\n }\n };\n};\n\n/**\n * 지정한 시간만큼 대기하는 Promise를 반환합니다.\n *\n * @param ms - 대기 시간 (밀리초)\n * @returns `ms` 이후 resolve되는 Promise\n * @example\n * ```ts\n * async function task() {\n * console.log(\"start\");\n * await sleep(1000);\n * console.log(\"1초 뒤\");\n * }\n * ```\n */\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * 첫 호출의 결과를 저장하고, 이후 호출 시 인자에 관계없이 같은 결과를 반환합니다.\n * 초기화 함수나 싱글톤 생성 등 \"한 번만 실행되어야 하는 작업\"에 유용합니다.\n *\n * @param fn - 한 번만 실행할 함수\n * @returns 첫 호출 결과를 캐싱하는 래퍼 함수\n * @example\n * ```ts\n * const init = once(() => {\n * console.log(\"initialized\");\n * return { ready: true };\n * });\n *\n * init(); // \"initialized\" 출력 후 { ready: true } 반환\n * init(); // 출력 없음, 같은 객체 반환\n * ```\n */\nexport const once = <Args extends unknown[], R>(\n fn: (...args: Args) => R,\n): ((...args: Args) => R) => {\n let called = false;\n let result: R;\n return (...args: Args): R => {\n if (!called) {\n called = true;\n result = fn(...args);\n }\n return result;\n };\n};\n\n/**\n * 함수의 결과를 인자별로 캐싱합니다. 동일한 인자로 다시 호출되면 캐시된 결과를 반환합니다.\n *\n * 기본 캐시 키는 `JSON.stringify(args)`로 생성되므로 객체 인자도 값 비교가 가능합니다.\n * 필요하면 `keyFn`을 직접 지정해 키 생성 로직을 커스터마이즈하세요.\n *\n * @param fn - 결과를 캐싱할 함수\n * @param keyFn - 인자에서 캐시 키를 만드는 함수 (기본값: `JSON.stringify(args)`)\n * @returns 결과 캐싱이 적용된 래퍼 함수\n * @example\n * ```ts\n * const slow = memoize((n: number) => {\n * console.log(\"compute\", n);\n * return n * 2;\n * });\n *\n * slow(2); // \"compute 2\" 출력 후 4\n * slow(2); // 출력 없음, 4\n * slow(3); // \"compute 3\" 출력 후 6\n *\n * // 커스텀 키: 객체 id 기준 캐싱\n * const byId = memoize(\n * (user: { id: number; name: string }) => fetchProfile(user.id),\n * (user) => String(user.id),\n * );\n * ```\n */\nexport const memoize = <Args extends unknown[], R>(\n fn: (...args: Args) => R,\n keyFn: (...args: Args) => string = (...args) => JSON.stringify(args),\n): ((...args: Args) => R) => {\n const cache = new Map<string, R>();\n return (...args: Args): R => {\n const key = keyFn(...args);\n const cached = cache.get(key);\n if (cached !== undefined || cache.has(key)) return cached as R;\n const result = fn(...args);\n cache.set(key, result);\n return result;\n };\n};\n\n/**\n * {@link retry} 함수의 옵션.\n */\nexport interface RetryOptions {\n /** 추가 재시도 횟수. 총 시도 횟수는 `retries + 1`. 기본값 `3`. */\n retries?: number;\n /** 첫 재시도 전 대기 시간(ms). 기본값 `100`. */\n delay?: number;\n /** 매 재시도마다 대기 시간에 곱해질 배수 (지수 백오프). 기본값 `2`. */\n backoff?: number;\n /** 매 실패마다 호출되는 콜백. `attempt`는 0부터 시작하는 시도 인덱스. */\n onError?: (error: unknown, attempt: number) => void;\n}\n\n/**\n * 함수를 호출하다가 예외가 나면 지수 백오프로 재시도합니다. 동기/비동기 함수 모두 지원합니다.\n *\n * 모든 시도가 실패하면 마지막 에러를 throw합니다.\n *\n * @param fn - 실행할 함수 (동기 또는 비동기)\n * @param opts - 재시도 옵션 ({@link RetryOptions} 참고)\n * @returns 함수의 결과를 담은 Promise\n * @example\n * ```ts\n * // 기본 옵션: 최대 3회 재시도, 100ms부터 2배씩 백오프 (100, 200, 400ms)\n * const data = await retry(() => fetch(\"/api/data\").then((r) => r.json()));\n *\n * // 커스텀 옵션\n * await retry(unstableTask, {\n * retries: 5,\n * delay: 200,\n * backoff: 1.5,\n * onError: (err, attempt) => console.warn(`시도 ${attempt + 1} 실패`, err),\n * });\n * ```\n */\nexport const retry = async <T>(\n fn: () => Promise<T> | T,\n opts: RetryOptions = {},\n): Promise<T> => {\n const { retries = 3, delay = 100, backoff = 2, onError } = opts;\n let lastError: unknown;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n onError?.(err, attempt);\n if (attempt < retries) {\n await sleep(delay * Math.pow(backoff, attempt));\n }\n }\n }\n throw lastError;\n};\n\n/**\n * Promise에 타임아웃을 부여합니다. 지정한 시간 안에 settle되지 않으면 reject됩니다.\n *\n * 원본 Promise가 먼저 끝나면 내부 타이머는 자동으로 해제되므로 타이머 누수가 없습니다.\n *\n * @param promise - 타임아웃을 걸 Promise\n * @param ms - 제한 시간 (밀리초)\n * @param errorMessage - 타임아웃 시 던질 에러 메시지 (기본값 `\"Operation timed out\"`)\n * @returns 원본 결과 또는 타임아웃 에러로 settle되는 Promise\n * @example\n * ```ts\n * try {\n * const data = await withTimeout(fetch(\"/slow-api\"), 3000);\n * } catch (err) {\n * // 3초 안에 안 오면 여기로 떨어짐\n * }\n *\n * await withTimeout(longTask(), 5000, \"작업이 너무 오래 걸립니다\");\n * ```\n */\nexport const withTimeout = <T>(\n promise: Promise<T>,\n ms: number,\n errorMessage = \"Operation timed out\",\n): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(errorMessage)), ms);\n });\n return Promise.race([\n promise.finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }),\n timeout,\n ]);\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/object/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"sources":["../../src/object/index.ts"],"names":[],"mappings":";;;AAWO,IAAM,IAAA,GAAO,CAClB,GAAA,EACA,IAAA,KACe;AACf,EAAA,MAAM,SAAS,EAAC;AAChB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,OAAO,GAAA,EAAK,MAAA,CAAO,GAAG,CAAA,GAAI,IAAI,GAAG,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,MAAA;AACT;AAaO,IAAM,IAAA,GAAO,CAClB,GAAA,EACA,IAAA,KACe;AACf,EAAA,MAAM,MAAA,GAAS,EAAE,GAAG,GAAA,EAAI;AACxB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,OAAO,OAAO,GAAG,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,MAAA;AACT;AAoBO,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAqD;AACjF,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,MAAM,OAAO,KAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,cAAA,CAAe,KAAK,CAAA;AACzC,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,CAAO,SAAA;AAC5C","file":"index.cjs","sourcesContent":["/**\n * 객체에서 지정한 키만 추려낸 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 보존할 키들의 배열\n * @returns `keys`에 명시된 속성만 가진 새 객체\n * @example\n * ```ts\n * pick({ a: 1, b: 2, c: 3 }, [\"a\", \"c\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\n/**\n * 객체에서 지정한 키를 제외한 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 제외할 키들의 배열\n * @returns `keys`에 명시된 속성을 뺀 새 객체\n * @example\n * ```ts\n * omit({ a: 1, b: 2, c: 3 }, [\"b\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\n/**\n * 값이 **순수 객체**(plain object)인지 판별합니다.\n * `null`, 배열, 클래스 인스턴스, `Date` 등은 모두 `false`를 반환합니다.\n *\n * SSR/Node 환경에서도 안전하게 동작합니다 (브라우저 API 의존성 없음).\n *\n * @param value - 검사할 값\n * @returns `{}` 또는 `Object.create(null)`로 만든 객체면 `true`\n * @example\n * ```ts\n * isPlainObject({}); // true\n * isPlainObject({ a: 1 }); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject([]); // false\n * isPlainObject(new Date()); // false\n * isPlainObject(null); // false\n * ```\n */\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n"]}
|
package/dist/object/index.d.ts
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 객체에서 지정한 키만 추려낸 새 객체를 만듭니다. 원본은 변경되지 않습니다.
|
|
3
|
+
*
|
|
4
|
+
* @param obj - 원본 객체
|
|
5
|
+
* @param keys - 보존할 키들의 배열
|
|
6
|
+
* @returns `keys`에 명시된 속성만 가진 새 객체
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* pick({ a: 1, b: 2, c: 3 }, ["a", "c"]); // { a: 1, c: 3 }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare const pick: <T extends object, K extends keyof T>(obj: T, keys: readonly K[]) => Pick<T, K>;
|
|
13
|
+
/**
|
|
14
|
+
* 객체에서 지정한 키를 제외한 새 객체를 만듭니다. 원본은 변경되지 않습니다.
|
|
15
|
+
*
|
|
16
|
+
* @param obj - 원본 객체
|
|
17
|
+
* @param keys - 제외할 키들의 배열
|
|
18
|
+
* @returns `keys`에 명시된 속성을 뺀 새 객체
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* omit({ a: 1, b: 2, c: 3 }, ["b"]); // { a: 1, c: 3 }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const omit: <T extends object, K extends keyof T>(obj: T, keys: readonly K[]) => Omit<T, K>;
|
|
25
|
+
/**
|
|
26
|
+
* 값이 **순수 객체**(plain object)인지 판별합니다.
|
|
27
|
+
* `null`, 배열, 클래스 인스턴스, `Date` 등은 모두 `false`를 반환합니다.
|
|
28
|
+
*
|
|
29
|
+
* SSR/Node 환경에서도 안전하게 동작합니다 (브라우저 API 의존성 없음).
|
|
30
|
+
*
|
|
31
|
+
* @param value - 검사할 값
|
|
32
|
+
* @returns `{}` 또는 `Object.create(null)`로 만든 객체면 `true`
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* isPlainObject({}); // true
|
|
36
|
+
* isPlainObject({ a: 1 }); // true
|
|
37
|
+
* isPlainObject(Object.create(null)); // true
|
|
38
|
+
* isPlainObject([]); // false
|
|
39
|
+
* isPlainObject(new Date()); // false
|
|
40
|
+
* isPlainObject(null); // false
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare const isPlainObject: (value: unknown) => value is Record<string, unknown>;
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/object/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EACtD,KAAK,CAAC,EACN,MAAM,SAAS,CAAC,EAAE,KACjB,IAAI,CAAC,CAAC,EAAE,CAAC,CAMX,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EACtD,KAAK,CAAC,EACN,MAAM,SAAS,CAAC,EAAE,KACjB,IAAI,CAAC,CAAC,EAAE,CAAC,CAMX,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAI7E,CAAC"}
|
package/dist/object/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/object/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/object/index.ts"],"names":[],"mappings":";AAWO,IAAM,IAAA,GAAO,CAClB,GAAA,EACA,IAAA,KACe;AACf,EAAA,MAAM,SAAS,EAAC;AAChB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,OAAO,GAAA,EAAK,MAAA,CAAO,GAAG,CAAA,GAAI,IAAI,GAAG,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,MAAA;AACT;AAaO,IAAM,IAAA,GAAO,CAClB,GAAA,EACA,IAAA,KACe;AACf,EAAA,MAAM,MAAA,GAAS,EAAE,GAAG,GAAA,EAAI;AACxB,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,OAAO,OAAO,GAAG,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,MAAA;AACT;AAoBO,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAqD;AACjF,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,MAAM,OAAO,KAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,cAAA,CAAe,KAAK,CAAA;AACzC,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,CAAO,SAAA;AAC5C","file":"index.js","sourcesContent":["/**\n * 객체에서 지정한 키만 추려낸 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 보존할 키들의 배열\n * @returns `keys`에 명시된 속성만 가진 새 객체\n * @example\n * ```ts\n * pick({ a: 1, b: 2, c: 3 }, [\"a\", \"c\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const pick = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Pick<T, K> => {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in obj) result[key] = obj[key];\n }\n return result;\n};\n\n/**\n * 객체에서 지정한 키를 제외한 새 객체를 만듭니다. 원본은 변경되지 않습니다.\n *\n * @param obj - 원본 객체\n * @param keys - 제외할 키들의 배열\n * @returns `keys`에 명시된 속성을 뺀 새 객체\n * @example\n * ```ts\n * omit({ a: 1, b: 2, c: 3 }, [\"b\"]); // { a: 1, c: 3 }\n * ```\n */\nexport const omit = <T extends object, K extends keyof T>(\n obj: T,\n keys: readonly K[],\n): Omit<T, K> => {\n const result = { ...obj };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n};\n\n/**\n * 값이 **순수 객체**(plain object)인지 판별합니다.\n * `null`, 배열, 클래스 인스턴스, `Date` 등은 모두 `false`를 반환합니다.\n *\n * SSR/Node 환경에서도 안전하게 동작합니다 (브라우저 API 의존성 없음).\n *\n * @param value - 검사할 값\n * @returns `{}` 또는 `Object.create(null)`로 만든 객체면 `true`\n * @example\n * ```ts\n * isPlainObject({}); // true\n * isPlainObject({ a: 1 }); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject([]); // false\n * isPlainObject(new Date()); // false\n * isPlainObject(null); // false\n * ```\n */\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n};\n"]}
|
package/dist/string/index.cjs
CHANGED
|
@@ -5,10 +5,21 @@ var capitalize = (str) => str.length === 0 ? str : str[0].toUpperCase() + str.sl
|
|
|
5
5
|
var camelToKebab = (str) => str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, "");
|
|
6
6
|
var kebabToCamel = (str) => str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
7
7
|
var truncate = (str, max, suffix = "...") => str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;
|
|
8
|
+
var formatNumber = (n, locale = "en-US") => new Intl.NumberFormat(locale).format(n);
|
|
9
|
+
var mask = (str, opts = {}) => {
|
|
10
|
+
const start = Math.max(0, opts.start ?? 1);
|
|
11
|
+
const end = Math.max(0, opts.end ?? 0);
|
|
12
|
+
const char = opts.char ?? "*";
|
|
13
|
+
if (str.length <= start + end) return str;
|
|
14
|
+
const masked = char.repeat(str.length - start - end);
|
|
15
|
+
return str.slice(0, start) + masked + str.slice(str.length - end);
|
|
16
|
+
};
|
|
8
17
|
|
|
9
18
|
exports.camelToKebab = camelToKebab;
|
|
10
19
|
exports.capitalize = capitalize;
|
|
20
|
+
exports.formatNumber = formatNumber;
|
|
11
21
|
exports.kebabToCamel = kebabToCamel;
|
|
22
|
+
exports.mask = mask;
|
|
12
23
|
exports.truncate = truncate;
|
|
13
24
|
//# sourceMappingURL=index.cjs.map
|
|
14
25
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/string/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"sources":["../../src/string/index.ts"],"names":[],"mappings":";;;AAWO,IAAM,UAAA,GAAa,CAAC,GAAA,KACzB,GAAA,CAAI,WAAW,CAAA,GAAI,GAAA,GAAM,GAAA,CAAI,CAAC,CAAA,CAAG,WAAA,EAAY,GAAI,GAAA,CAAI,MAAM,CAAC;AAavD,IAAM,eAAe,CAAC,GAAA,KAC3B,GAAA,CAAI,OAAA,CAAQ,UAAU,CAAC,CAAA,KAAM,CAAA,CAAA,EAAI,CAAA,CAAE,aAAa,CAAA,CAAE,CAAA,CAAE,OAAA,CAAQ,MAAM,EAAE;AAa/D,IAAM,YAAA,GAAe,CAAC,GAAA,KAC3B,GAAA,CAAI,OAAA,CAAQ,WAAA,EAAa,CAAC,CAAA,EAAG,CAAA,KAAc,CAAA,CAAE,WAAA,EAAa;AAgBrD,IAAM,QAAA,GAAW,CAAC,GAAA,EAAa,GAAA,EAAa,SAAS,KAAA,KAC1D,GAAA,CAAI,UAAU,GAAA,GAAM,GAAA,GAAM,IAAI,KAAA,CAAM,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,MAAM,MAAA,CAAO,MAAM,CAAC,CAAA,GAAI;AAetE,IAAM,YAAA,GAAe,CAAC,CAAA,EAAW,MAAA,GAAS,OAAA,KAC/C,IAAI,IAAA,CAAK,YAAA,CAAa,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC;AA8BjC,IAAM,IAAA,GAAO,CAAC,GAAA,EAAa,IAAA,GAAoB,EAAC,KAAc;AACnE,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,SAAS,CAAC,CAAA;AACzC,EAAA,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,OAAO,CAAC,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,KAAK,IAAA,IAAQ,GAAA;AAC1B,EAAA,IAAI,GAAA,CAAI,MAAA,IAAU,KAAA,GAAQ,GAAA,EAAK,OAAO,GAAA;AACtC,EAAA,MAAM,SAAS,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,MAAA,GAAS,QAAQ,GAAG,CAAA;AACnD,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA,GAAI,SAAS,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,MAAA,GAAS,GAAG,CAAA;AAClE","file":"index.cjs","sourcesContent":["/**\n * 문자열의 첫 글자를 대문자로 변환합니다.\n *\n * @param str - 변환할 문자열\n * @returns 첫 글자가 대문자가 된 문자열. 빈 문자열은 그대로 반환됩니다.\n * @example\n * ```ts\n * capitalize(\"hello\"); // \"Hello\"\n * capitalize(\"\"); // \"\"\n * ```\n */\nexport const capitalize = (str: string): string =>\n str.length === 0 ? str : str[0]!.toUpperCase() + str.slice(1);\n\n/**\n * camelCase 문자열을 kebab-case로 변환합니다.\n *\n * @param str - camelCase 문자열\n * @returns kebab-case로 변환된 문자열\n * @example\n * ```ts\n * camelToKebab(\"backgroundColor\"); // \"background-color\"\n * camelToKebab(\"HTMLParser\"); // \"h-t-m-l-parser\"\n * ```\n */\nexport const camelToKebab = (str: string): string =>\n str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, \"\");\n\n/**\n * kebab-case 문자열을 camelCase로 변환합니다.\n *\n * @param str - kebab-case 문자열\n * @returns camelCase로 변환된 문자열\n * @example\n * ```ts\n * kebabToCamel(\"background-color\"); // \"backgroundColor\"\n * kebabToCamel(\"data-test-id\"); // \"dataTestId\"\n * ```\n */\nexport const kebabToCamel = (str: string): string =>\n str.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n\n/**\n * 문자열이 최대 길이를 초과하면 잘라내고 접미사를 붙입니다.\n *\n * @param str - 원본 문자열\n * @param max - 결과의 최대 길이 (접미사 포함)\n * @param suffix - 잘렸을 때 붙일 접미사 (기본값 `\"...\"`)\n * @returns 최대 길이 이하면 원본, 초과하면 잘린 문자열 + 접미사\n * @example\n * ```ts\n * truncate(\"hello world\", 8); // \"hello...\"\n * truncate(\"hi\", 5); // \"hi\" (변경 없음)\n * truncate(\"hello world\", 8, \"…\"); // \"hello w…\"\n * ```\n */\nexport const truncate = (str: string, max: number, suffix = \"...\"): string =>\n str.length <= max ? str : str.slice(0, Math.max(0, max - suffix.length)) + suffix;\n\n/**\n * 숫자에 천 단위 구분자를 추가합니다. `Intl.NumberFormat` 기반이라 음수/소수/로케일을 모두 지원합니다.\n *\n * @param n - 포맷할 숫자\n * @param locale - BCP 47 로케일 태그 (기본값 `\"en-US\"`)\n * @returns 천 단위 구분자가 들어간 문자열\n * @example\n * ```ts\n * formatNumber(1234567); // \"1,234,567\"\n * formatNumber(-1234.56); // \"-1,234.56\"\n * formatNumber(1234567, \"de-DE\"); // \"1.234.567\" (독일은 점이 구분자)\n * ```\n */\nexport const formatNumber = (n: number, locale = \"en-US\"): string =>\n new Intl.NumberFormat(locale).format(n);\n\n/**\n * {@link mask} 함수의 옵션.\n */\nexport interface MaskOptions {\n /** 앞에서 보존할 문자 수. 기본값 `1`. */\n start?: number;\n /** 뒤에서 보존할 문자 수. 기본값 `0`. */\n end?: number;\n /** 가릴 때 사용할 문자. 기본값 `\"*\"`. */\n char?: string;\n}\n\n/**\n * 문자열의 중간 일부를 마스킹 문자로 가립니다. 이메일/전화번호 등 민감 정보 표시에 유용합니다.\n *\n * 문자열이 `start + end`보다 짧으면 원본을 그대로 반환합니다.\n *\n * @param str - 원본 문자열\n * @param opts - 마스킹 옵션 ({@link MaskOptions} 참고)\n * @returns 마스킹된 문자열\n * @example\n * ```ts\n * mask(\"01012345678\", { start: 3, end: 4 }); // \"010****5678\"\n * mask(\"jthong@gmail.com\", { start: 1, end: 10 }); // \"j*****@gmail.com\"\n * mask(\"secret\", { start: 0, end: 0, char: \"#\" }); // \"######\"\n * mask(\"hi\", { start: 1, end: 1 }); // \"hi\" (너무 짧아 변경 없음)\n * ```\n */\nexport const mask = (str: string, opts: MaskOptions = {}): string => {\n const start = Math.max(0, opts.start ?? 1);\n const end = Math.max(0, opts.end ?? 0);\n const char = opts.char ?? \"*\";\n if (str.length <= start + end) return str;\n const masked = char.repeat(str.length - start - end);\n return str.slice(0, start) + masked + str.slice(str.length - end);\n};\n"]}
|