jell-utils 0.0.17 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,4 +1,60 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
50
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
51
+ if (ar || !(i in from)) {
52
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
53
+ ar[i] = from[i];
54
+ }
55
+ }
56
+ return to.concat(ar || Array.prototype.slice.call(from));
57
+ };
2
58
  Object.defineProperty(exports, "__esModule", { value: true });
3
59
  var util = {
4
60
  /**
@@ -11,23 +67,41 @@ var util = {
11
67
  return regex.test(message);
12
68
  },
13
69
  /**
14
- * @description return deep copied object by original object.
15
- * @todo need change Record<string, unknown> to Object(Typescript warning)
16
- * @param obj Record<string, unknown>
17
- * @returns Record<string, unknown>
70
+ * @description return deep copied object by original object using structuredClone or JSON fallback.
71
+ * @param obj object to deep clone
72
+ * @returns deep cloned object
18
73
  */
19
74
  clone: function (obj) {
20
- // if (obj === null || typeof obj !== 'object') {
21
- // return obj
22
- // }
23
- var copy = obj.constructor();
24
- for (var attr in obj) {
25
- if (Object.prototype.hasOwnProperty.call(obj, attr)) {
26
- // from: https://ourcodeworld.com/articles/read/1425/how-to-fix-eslint-error-do-not-access-objectprototype-method-hasownproperty-from-target-object-no-prototype-builtins
27
- copy[attr] = obj[attr];
75
+ if (obj === null || typeof obj !== 'object') {
76
+ return obj;
77
+ }
78
+ // Use structuredClone if available (modern browsers/Node.js 17+)
79
+ if (typeof structuredClone !== 'undefined') {
80
+ return structuredClone(obj);
81
+ }
82
+ // Fallback to JSON method (handles most common cases)
83
+ try {
84
+ return JSON.parse(JSON.stringify(obj));
85
+ }
86
+ catch (_a) {
87
+ // Final fallback for non-serializable objects
88
+ if (Array.isArray(obj)) {
89
+ return obj.map(function (item) {
90
+ return typeof item === 'object' && item !== null ? util.clone(item) : item;
91
+ });
92
+ }
93
+ var copy = {};
94
+ for (var key in obj) {
95
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
96
+ var value = obj[key];
97
+ copy[key] =
98
+ typeof value === 'object' && value !== null
99
+ ? util.clone(value)
100
+ : value;
101
+ }
28
102
  }
103
+ return copy;
29
104
  }
30
- return copy;
31
105
  },
32
106
  /**
33
107
  * @description encode uri parsing (like &lt; => <, &gt; => >)
@@ -38,48 +112,49 @@ var util = {
38
112
  return txt.replace(/&lt;/gi, '<').replace(/&gt;/gi, '>');
39
113
  },
40
114
  /**
41
- * @description parseNumber with defaultvalue
42
- * @param target original string
43
- * @param defaultValue return default value
44
- * @returns parsed number value.
115
+ * @description parseNumber with defaultvalue and proper validation
116
+ * @param target original string to parse
117
+ * @param defaultValue return default value if parsing fails
118
+ * @param isFloat whether to parse as float or integer
119
+ * @returns parsed number value or default value
45
120
  */
46
121
  parseNumber: function (target, defaultValue, isFloat) {
47
122
  if (isFloat === void 0) { isFloat = false; }
48
- if (!target || typeof parseInt(target) !== 'number') {
123
+ if (!target || typeof target !== 'string' || target.trim() === '') {
49
124
  return defaultValue;
50
125
  }
51
- return isFloat ? parseFloat(target) : parseInt(target);
126
+ var parsed = isFloat ? parseFloat(target) : parseInt(target, 10);
127
+ return isNaN(parsed) ? defaultValue : parsed;
52
128
  },
53
129
  /**
54
- *
55
- * @param target required format such as '00:11:22' / '11:22'
56
- * @param defaultValue
57
- * @returns
130
+ * @description parse time string to milliseconds
131
+ * @param target required format such as '00:11:22' (HH:MM:SS) or '11:22' (MM:SS)
132
+ * @param defaultValue default value if parsing fails
133
+ * @returns parsed milliseconds or default value
58
134
  */
59
135
  parseTime: function (target, defaultValue) {
60
- if (!target.includes(':'))
61
- return defaultValue ? defaultValue : parseInt(target);
62
- var matched = target.match(/:/gi);
63
- if (target && matched) {
64
- var isMilliSecond = matched.length == 2;
65
- var timeValues = target.split(':').map(function (value) { return parseInt(value, 0); });
66
- var minute = 0;
67
- var second = 0;
68
- var milliSecond = 0;
69
- if (isMilliSecond) {
70
- minute = timeValues[0] || 0;
71
- second = timeValues[1] || 0;
72
- milliSecond = timeValues[2] || 0;
73
- }
74
- else {
75
- minute = timeValues[0] || 0;
76
- second = timeValues[1] || 0;
77
- }
78
- var milliSeconds = minute * 60 * 1000 + second * 1000 + milliSecond;
79
- return milliSeconds;
136
+ if (!target || typeof target !== 'string' || target.trim() === '') {
137
+ return defaultValue;
138
+ }
139
+ if (target.indexOf(':') === -1) {
140
+ var parsed = parseInt(target, 10);
141
+ return isNaN(parsed) ? defaultValue : parsed;
142
+ }
143
+ var timeValues = target.split(':').map(function (value) {
144
+ var parsed = parseInt(value.trim(), 10);
145
+ return isNaN(parsed) ? 0 : parsed;
146
+ });
147
+ if (timeValues.length < 2 || timeValues.length > 3) {
148
+ return defaultValue;
149
+ }
150
+ var first = timeValues[0], second = timeValues[1], _a = timeValues[2], third = _a === void 0 ? 0 : _a;
151
+ if (timeValues.length === 3) {
152
+ // HH:MM:SS format - treat third value as milliseconds
153
+ return first * 60 * 1000 + second * 1000 + third;
80
154
  }
81
155
  else {
82
- return 0;
156
+ // MM:SS format
157
+ return first * 60 * 1000 + second * 1000;
83
158
  }
84
159
  },
85
160
  /**
@@ -94,6 +169,8 @@ var util = {
94
169
  },
95
170
  /**
96
171
  * @description generate string from inputed time with korean date format.
172
+ * @param dateString date input (Date object or string)
173
+ * @param isYear whether to include year in output
97
174
  * @returns string korean date format
98
175
  */
99
176
  getKoreanDate: function (dateString, isYear) {
@@ -101,11 +178,11 @@ var util = {
101
178
  if (isYear === void 0) { isYear = false; }
102
179
  var targetDate = new Date(dateString);
103
180
  var year = targetDate.getFullYear();
104
- var month = ("0" + (targetDate.getMonth() + 1)).substr(-2);
105
- var date = ("0" + targetDate.getDate()).substr(-2);
181
+ var month = "0".concat(targetDate.getMonth() + 1).slice(-2);
182
+ var date = "0".concat(targetDate.getDate()).slice(-2);
106
183
  if (isYear)
107
- return year + "\uB144 " + month + "\uC6D4 " + date + "\uC77C";
108
- return month + "\uC6D4 " + date + "\uC77C";
184
+ return "".concat(year, "\uB144 ").concat(month, "\uC6D4 ").concat(date, "\uC77C");
185
+ return "".concat(month, "\uC6D4 ").concat(date, "\uC77C");
109
186
  },
110
187
  /**
111
188
  * @description convert date to yyyy-mm-dd format string.
@@ -115,8 +192,8 @@ var util = {
115
192
  formatDate: function (date) {
116
193
  if (date === void 0) { date = new Date(); }
117
194
  var d = new Date(date);
118
- var month = "" + (d.getMonth() + 1);
119
- var day = "" + d.getDate();
195
+ var month = "".concat(d.getMonth() + 1);
196
+ var day = "".concat(d.getDate());
120
197
  var year = d.getFullYear();
121
198
  if (month.length < 2)
122
199
  month = '0' + month;
@@ -146,13 +223,13 @@ var util = {
146
223
  * @returns
147
224
  */
148
225
  replaceBetween: function (str, txt, startIndex, endIndex) {
149
- var newStr = "" + str.substring(0, startIndex) + txt + str.substring(endIndex);
226
+ var newStr = "".concat(str.substring(0, startIndex)).concat(txt).concat(str.substring(endIndex));
150
227
  return newStr;
151
228
  },
152
229
  /**
153
- * @description make camel cased word.
154
- * @param txt
155
- * @returns
230
+ * @description convert string to camelCase format
231
+ * @param txt input string to convert (supports snake_case and space-separated)
232
+ * @returns camelCase formatted string
156
233
  */
157
234
  toCamelCase: function (txt) {
158
235
  txt = typeof txt !== 'string' ? '' : txt;
@@ -170,9 +247,9 @@ var util = {
170
247
  return txtArr.join('');
171
248
  },
172
249
  /**
173
- * @description make snake cased word.
174
- * @param txt
175
- * @returns
250
+ * @description convert string to snake_case format
251
+ * @param txt input string to convert
252
+ * @returns snake_case formatted string
176
253
  */
177
254
  toSnakeCase: function (txt) {
178
255
  txt = typeof txt !== 'string' ? '' : txt;
@@ -180,85 +257,91 @@ var util = {
180
257
  return txt;
181
258
  },
182
259
  /**
183
- * @description make title case string(first character capital and word space)
184
- * @param txt
185
- * @returns
260
+ * @description make title case string (first character capital and word space)
261
+ * @param txt input string to convert
262
+ * @returns title case string with proper spacing
186
263
  */
187
264
  toTitleCase: function (txt) {
188
- txt = typeof txt !== 'string' ? '' : txt;
189
- // snake case
190
- txt = txt.replace(/_/g, ' ');
191
- txt = txt
192
- .split(' ')
193
- .map(function (word) { return word.charAt(0).toUpperCase().concat(word.substr(1)); })
265
+ if (typeof txt !== 'string')
266
+ return '';
267
+ // Convert snake_case to spaces and handle camelCase
268
+ var result = txt
269
+ .replace(/_/g, ' ') // Replace underscores with spaces
270
+ .replace(/([a-z])([A-Z])/g, '$1 $2') // Add space before capital letters in camelCase
271
+ .split(/\s+/) // Split by any whitespace
272
+ .filter(function (word) { return word.length > 0; }) // Remove empty strings
273
+ .map(function (word) { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); })
194
274
  .join(' ');
195
- // input space when camel case
196
- var upperCases = txt.match(/[A-Z]/g) || [];
197
- upperCases.forEach(function (varter) {
198
- var regEx = new RegExp(varter, 'g');
199
- txt = txt.replace(regEx, ' ' + varter);
200
- });
201
- while (/{2}/g.exec(txt) != null) {
202
- txt = txt.replace(/{2}/g, ' ');
203
- }
204
- txt = txt.trim();
205
- return txt;
275
+ return result;
206
276
  },
207
277
  /**
208
- * @description br tag to line break.
209
- * @param txt
210
- * @returns
278
+ * @description convert HTML br tags to line breaks
279
+ * @param txt input string with br tags
280
+ * @returns string with br tags converted to newlines
211
281
  */
212
282
  toText: function (txt) {
213
283
  return txt.replace(/<br>/gi, '\n');
214
284
  },
215
285
  /**
216
- * @description line break to br tag.
217
- * @param txt
218
- * @returns
286
+ * @description convert line breaks to HTML br tags
287
+ * @param txt input string with newlines
288
+ * @returns string with newlines converted to br tags
219
289
  */
220
290
  toHtml: function (txt) {
221
291
  return txt.replace(/\n/gi, '<br>');
222
292
  },
223
293
  /**
224
- * @description remove tag brace text.
225
- * @param txt string
226
- * @param isError (beta) check 'suggestCheck' tag.
227
- * @returns
294
+ * @description safely remove HTML tags from text, preventing XSS attacks
295
+ * @param txt input string with HTML tags
296
+ * @param preserveErrorTags whether to preserve elements with 'suggestCheck' class
297
+ * @returns sanitized text with specified tags removed
228
298
  */
229
- clearTag: function (txt, isError) {
230
- if (isError === void 0) { isError = false; }
231
- var txtWrap = document.createElement('div');
232
- txtWrap.innerHTML = txt;
233
- var tags = ['div', 'font', 'span'];
234
- tags.forEach(function (tag) {
235
- var elements = txtWrap.querySelectorAll(tag);
236
- var errorWraps = txtWrap.querySelectorAll(tag + ".suggestCheck");
237
- var isContinue = isError
238
- ? elements.length - errorWraps.length > 0
239
- : elements.length > 0;
240
- while (isContinue) {
241
- elements.forEach(function (element) {
242
- var isErrorWrap = element.className.includes('suggestCheck');
243
- if (!isError || !isErrorWrap) {
244
- element.outerHTML = element.innerHTML;
245
- }
246
- });
247
- elements = txtWrap.querySelectorAll(tag);
248
- errorWraps = txtWrap.querySelectorAll(tag + ".suggestCheck");
249
- isContinue = isError
250
- ? elements.length - errorWraps.length > 0
251
- : elements.length > 0;
299
+ clearTag: function (txt, preserveErrorTags) {
300
+ if (preserveErrorTags === void 0) { preserveErrorTags = false; }
301
+ if (typeof txt !== 'string')
302
+ return '';
303
+ // Create a temporary DOM element for safe parsing
304
+ var tempDiv = document.createElement('div');
305
+ // Set textContent first to avoid XSS, then replace with innerHTML for parsing
306
+ tempDiv.textContent = '';
307
+ try {
308
+ tempDiv.innerHTML = txt;
309
+ }
310
+ catch (_a) {
311
+ // If innerHTML parsing fails, return original text stripped of all tags
312
+ return txt.replace(/<[^>]*>/g, '');
313
+ }
314
+ var tagsToRemove = ['div', 'font', 'span'];
315
+ // Remove specified tags while preserving content
316
+ tagsToRemove.forEach(function (tagName) {
317
+ var elements = tempDiv.querySelectorAll(tagName);
318
+ for (var i = 0; i < elements.length; i++) {
319
+ var element = elements[i];
320
+ var hasErrorClass = element.className.indexOf('suggestCheck') !== -1;
321
+ // Skip if we want to preserve error tags and this element has the error class
322
+ if (preserveErrorTags && hasErrorClass) {
323
+ continue;
324
+ }
325
+ // Replace element with its content
326
+ var parent_1 = element.parentNode;
327
+ if (parent_1) {
328
+ // Create text node from inner content to prevent XSS
329
+ var textContent = element.textContent || '';
330
+ var textNode = document.createTextNode(textContent);
331
+ parent_1.replaceChild(textNode, element);
332
+ }
252
333
  }
253
334
  });
254
- var clearedTxt = txtWrap.innerHTML.replace(/<br>/gi, '\n');
255
- return clearedTxt;
335
+ // Convert <br> tags to newlines and get text content
336
+ var result = tempDiv.innerHTML.replace(/<br\s*\/?>/gi, '\n');
337
+ // Final sanitization: remove any remaining script tags and dangerous content
338
+ return result.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
256
339
  },
257
340
  /**
258
- * @description check same between two array.
259
- * @param a array
260
- * @param b array
261
- * @returns
341
+ * @description check if two arrays are equal (shallow comparison)
342
+ * @param a first array to compare
343
+ * @param b second array to compare
344
+ * @returns true if arrays are equal, false otherwise
262
345
  */
263
346
  equalArrays: function (a, b) {
264
347
  if (a === b)
@@ -272,6 +355,304 @@ var util = {
272
355
  return false;
273
356
  }
274
357
  return true;
358
+ },
359
+ /**
360
+ * @description check iOS device client with user agent.
361
+ * @returns true if client is iOS device, false otherwise
362
+ */
363
+ isiOS: function () {
364
+ return /iPhone|iPad|iPod/i.test(navigator.userAgent);
365
+ },
366
+ /**
367
+ * @description validate Korean business registration number (사업자등록번호)
368
+ * @param businessNumber business registration number string (10 digits)
369
+ * @returns true if valid, false otherwise
370
+ */
371
+ isBusinessNumber: function (businessNumber) {
372
+ var cleaned = businessNumber.replace(/[^0-9]/g, '');
373
+ if (cleaned.length !== 10)
374
+ return false;
375
+ var digits = cleaned.split('').map(Number);
376
+ var checksum = [1, 3, 7, 1, 3, 7, 1, 3, 5];
377
+ var sum = 0;
378
+ for (var i = 0; i < 9; i++) {
379
+ sum += digits[i] * checksum[i];
380
+ }
381
+ sum += Math.floor((digits[8] * 5) / 10);
382
+ var result = (10 - (sum % 10)) % 10;
383
+ return result === digits[9];
384
+ },
385
+ /**
386
+ * @description get nested object value by path string
387
+ * @param obj target object
388
+ * @param path path string (e.g., 'user.profile.name')
389
+ * @param defaultValue default value if path not found
390
+ * @returns value at path or default value
391
+ */
392
+ getByPath: function (obj, path, defaultValue) {
393
+ var keys = path.split('.');
394
+ var result = obj;
395
+ for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
396
+ var key = keys_1[_i];
397
+ if (result === null || result === undefined) {
398
+ return defaultValue;
399
+ }
400
+ result = result[key];
401
+ }
402
+ return result === undefined ? defaultValue : result;
403
+ },
404
+ /**
405
+ * @description set nested object value by path string
406
+ * @param obj target object
407
+ * @param path path string (e.g., 'user.profile.name')
408
+ * @param value value to set
409
+ */
410
+ setByPath: function (obj, path, value) {
411
+ var keys = path.split('.');
412
+ var lastKey = keys.pop();
413
+ if (!lastKey)
414
+ return;
415
+ var current = obj;
416
+ for (var _i = 0, keys_2 = keys; _i < keys_2.length; _i++) {
417
+ var key = keys_2[_i];
418
+ if (!(key in current) || typeof current[key] !== 'object') {
419
+ current[key] = {};
420
+ }
421
+ current = current[key];
422
+ }
423
+ current[lastKey] = value;
424
+ },
425
+ /**
426
+ * @description group array of objects by key
427
+ * @param array array to group
428
+ * @param key key to group by
429
+ * @returns grouped object
430
+ */
431
+ groupBy: function (array, key) {
432
+ return array.reduce(function (result, item) {
433
+ var groupKey = String(item[key]);
434
+ if (!result[groupKey]) {
435
+ result[groupKey] = [];
436
+ }
437
+ result[groupKey].push(item);
438
+ return result;
439
+ }, {});
440
+ },
441
+ /**
442
+ * @description extract only numbers from string
443
+ * @param str input string
444
+ * @returns string containing only numbers
445
+ */
446
+ extractNumbers: function (str) {
447
+ return str.replace(/[^0-9]/g, '');
448
+ },
449
+ /**
450
+ * @description get file extension from filename or path
451
+ * @param filename filename or path
452
+ * @returns file extension without dot (e.g., 'jpg', 'pdf')
453
+ */
454
+ getFileExtension: function (filename) {
455
+ var parts = filename.split('.');
456
+ return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
457
+ },
458
+ /**
459
+ * @description calculate difference between two dates
460
+ * @param date1 first date
461
+ * @param date2 second date
462
+ * @returns object with days, hours, minutes, seconds difference
463
+ */
464
+ dateDiff: function (date1, date2) {
465
+ if (date2 === void 0) { date2 = new Date(); }
466
+ var d1 = new Date(date1).getTime();
467
+ var d2 = new Date(date2).getTime();
468
+ var diff = Math.abs(d2 - d1);
469
+ var days = Math.floor(diff / (1000 * 60 * 60 * 24));
470
+ var hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
471
+ var minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
472
+ var seconds = Math.floor((diff % (1000 * 60)) / 1000);
473
+ return { days: days, hours: hours, minutes: minutes, seconds: seconds };
474
+ },
475
+ /**
476
+ * @description deep merge two objects
477
+ * @param target target object
478
+ * @param source source object
479
+ * @returns merged object
480
+ */
481
+ deepMerge: function (target, source) {
482
+ var result = __assign({}, target);
483
+ for (var key in source) {
484
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
485
+ var sourceValue = source[key];
486
+ var targetValue = result[key];
487
+ if (sourceValue &&
488
+ typeof sourceValue === 'object' &&
489
+ !Array.isArray(sourceValue) &&
490
+ targetValue &&
491
+ typeof targetValue === 'object' &&
492
+ !Array.isArray(targetValue)) {
493
+ result[key] = util.deepMerge(targetValue, sourceValue);
494
+ }
495
+ else {
496
+ result[key] = sourceValue;
497
+ }
498
+ }
499
+ }
500
+ return result;
501
+ },
502
+ /**
503
+ * @description retry async function with exponential backoff
504
+ * @param fn async function to retry
505
+ * @param maxRetries maximum number of retries
506
+ * @param delay initial delay in milliseconds
507
+ * @returns promise with function result
508
+ */
509
+ retry: function (fn_1) {
510
+ var args_1 = [];
511
+ for (var _i = 1; _i < arguments.length; _i++) {
512
+ args_1[_i - 1] = arguments[_i];
513
+ }
514
+ return __awaiter(void 0, __spreadArray([fn_1], args_1, true), void 0, function (fn, maxRetries, delay) {
515
+ var lastError, _loop_1, i, state_1;
516
+ if (maxRetries === void 0) { maxRetries = 3; }
517
+ if (delay === void 0) { delay = 1000; }
518
+ return __generator(this, function (_a) {
519
+ switch (_a.label) {
520
+ case 0:
521
+ _loop_1 = function (i) {
522
+ var _b, error_1;
523
+ return __generator(this, function (_c) {
524
+ switch (_c.label) {
525
+ case 0:
526
+ _c.trys.push([0, 2, , 5]);
527
+ _b = {};
528
+ return [4 /*yield*/, fn()];
529
+ case 1: return [2 /*return*/, (_b.value = _c.sent(), _b)];
530
+ case 2:
531
+ error_1 = _c.sent();
532
+ lastError = error_1;
533
+ if (!(i < maxRetries - 1)) return [3 /*break*/, 4];
534
+ return [4 /*yield*/, new Promise(function (resolve) {
535
+ return setTimeout(resolve, delay * Math.pow(2, i));
536
+ })];
537
+ case 3:
538
+ _c.sent();
539
+ _c.label = 4;
540
+ case 4: return [3 /*break*/, 5];
541
+ case 5: return [2 /*return*/];
542
+ }
543
+ });
544
+ };
545
+ i = 0;
546
+ _a.label = 1;
547
+ case 1:
548
+ if (!(i < maxRetries)) return [3 /*break*/, 4];
549
+ return [5 /*yield**/, _loop_1(i)];
550
+ case 2:
551
+ state_1 = _a.sent();
552
+ if (typeof state_1 === "object")
553
+ return [2 /*return*/, state_1.value];
554
+ _a.label = 3;
555
+ case 3:
556
+ i++;
557
+ return [3 /*break*/, 1];
558
+ case 4: throw lastError;
559
+ }
560
+ });
561
+ });
562
+ },
563
+ /**
564
+ * @description search Korean string by chosung (초성)
565
+ * @param str target string to search in
566
+ * @param search chosung search string
567
+ * @returns true if chosung matches
568
+ */
569
+ chosungSearch: function (str, search) {
570
+ var chosungList = [
571
+ 'ㄱ',
572
+ 'ㄲ',
573
+ 'ㄴ',
574
+ 'ㄷ',
575
+ 'ㄸ',
576
+ 'ㄹ',
577
+ 'ㅁ',
578
+ 'ㅂ',
579
+ 'ㅃ',
580
+ 'ㅅ',
581
+ 'ㅆ',
582
+ 'ㅇ',
583
+ 'ㅈ',
584
+ 'ㅉ',
585
+ 'ㅊ',
586
+ 'ㅋ',
587
+ 'ㅌ',
588
+ 'ㅍ',
589
+ 'ㅎ'
590
+ ];
591
+ var getChosung = function (char) {
592
+ var code = char.charCodeAt(0);
593
+ if (code >= 44032 && code <= 55203) {
594
+ var index = Math.floor((code - 44032) / 588);
595
+ return chosungList[index];
596
+ }
597
+ return char;
598
+ };
599
+ var strChosung = str.split('').map(getChosung).join('');
600
+ return strChosung.includes(search);
601
+ },
602
+ /**
603
+ * @description sort array of objects by key
604
+ * @param array array to sort
605
+ * @param key key to sort by
606
+ * @param order sort order ('asc' or 'desc')
607
+ * @returns sorted array
608
+ */
609
+ sortBy: function (array, key, order) {
610
+ if (order === void 0) { order = 'asc'; }
611
+ return __spreadArray([], array, true).sort(function (a, b) {
612
+ var aVal = a[key];
613
+ var bVal = b[key];
614
+ if (aVal < bVal)
615
+ return order === 'asc' ? -1 : 1;
616
+ if (aVal > bVal)
617
+ return order === 'asc' ? 1 : -1;
618
+ return 0;
619
+ });
620
+ },
621
+ /**
622
+ * @description convert object to URL query string
623
+ * @param obj object to convert
624
+ * @returns query string (e.g., 'key1=value1&key2=value2')
625
+ */
626
+ objectToQueryString: function (obj) {
627
+ return Object.entries(obj)
628
+ .filter(function (_a) {
629
+ var value = _a[1];
630
+ return value !== undefined && value !== null;
631
+ })
632
+ .map(function (_a) {
633
+ var key = _a[0], value = _a[1];
634
+ return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(String(value)));
635
+ })
636
+ .join('&');
637
+ },
638
+ /**
639
+ * @description mask sensitive string (e.g., credit card, phone)
640
+ * @param str string to mask
641
+ * @param visibleStart number of visible characters at start
642
+ * @param visibleEnd number of visible characters at end
643
+ * @param maskChar character to use for masking
644
+ * @returns masked string
645
+ */
646
+ maskString: function (str, visibleStart, visibleEnd, maskChar) {
647
+ if (visibleStart === void 0) { visibleStart = 3; }
648
+ if (visibleEnd === void 0) { visibleEnd = 3; }
649
+ if (maskChar === void 0) { maskChar = '*'; }
650
+ if (str.length <= visibleStart + visibleEnd)
651
+ return str;
652
+ var start = str.substring(0, visibleStart);
653
+ var end = str.substring(str.length - visibleEnd);
654
+ var masked = maskChar.repeat(str.length - visibleStart - visibleEnd);
655
+ return start + masked + end;
275
656
  }
276
657
  };
277
658
  exports.default = util;