jell-utils 0.0.18 → 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/.claude/settings.local.json +16 -0
- package/.releaserc.json +115 -0
- package/CLAUDE.md +139 -0
- package/DEPLOYMENT.md +351 -0
- package/TEST_RESULTS.md +153 -0
- package/TEST_SIMULATION.md +27 -0
- package/coverage/clover.xml +300 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/index.js.html +2059 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +689 -0
- package/lib/index.d.ts +138 -39
- package/lib/index.js +492 -121
- package/package.json +45 -16
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
|
-
* @
|
|
16
|
-
* @
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 < => <, > => >)
|
|
@@ -38,48 +112,49 @@ var util = {
|
|
|
38
112
|
return txt.replace(/</gi, '<').replace(/>/gi, '>');
|
|
39
113
|
},
|
|
40
114
|
/**
|
|
41
|
-
* @description parseNumber with defaultvalue
|
|
42
|
-
* @param target original string
|
|
43
|
-
* @param defaultValue return default value
|
|
44
|
-
* @
|
|
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
|
|
123
|
+
if (!target || typeof target !== 'string' || target.trim() === '') {
|
|
49
124
|
return defaultValue;
|
|
50
125
|
}
|
|
51
|
-
|
|
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'
|
|
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.
|
|
61
|
-
return defaultValue
|
|
62
|
-
|
|
63
|
-
if (target
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
var
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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 =
|
|
105
|
-
var date =
|
|
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
|
|
108
|
-
return month
|
|
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 = ""
|
|
119
|
-
var day = ""
|
|
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 = ""
|
|
226
|
+
var newStr = "".concat(str.substring(0, startIndex)).concat(txt).concat(str.substring(endIndex));
|
|
150
227
|
return newStr;
|
|
151
228
|
},
|
|
152
229
|
/**
|
|
153
|
-
* @description
|
|
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
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
.
|
|
193
|
-
.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
225
|
-
* @param txt string
|
|
226
|
-
* @param
|
|
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,
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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
|
|
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)
|
|
@@ -275,13 +358,301 @@ var util = {
|
|
|
275
358
|
},
|
|
276
359
|
/**
|
|
277
360
|
* @description check iOS device client with user agent.
|
|
278
|
-
* @returns client is iOS
|
|
361
|
+
* @returns true if client is iOS device, false otherwise
|
|
279
362
|
*/
|
|
280
363
|
isiOS: function () {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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;
|
|
285
656
|
}
|
|
286
657
|
};
|
|
287
658
|
exports.default = util;
|