qsu 1.0.7 → 1.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/README.md +37 -11
- package/dist/index.js +1 -415
- package/logo.webp +0 -0
- package/package.json +13 -10
- package/.eslintignore +0 -2
- package/.eslintrc.cjs +0 -44
- package/qsu-logo.png +0 -0
- package/tsconfig.json +0 -23
package/README.md
CHANGED
|
@@ -1,19 +1,45 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
5
|
### Node.js Quick & Simple Utility for JavaScript
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
[](https://github.com/jooy2/qsu/blob/master/LICENSE)
|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
</td>
|
|
18
|
+
</tr>
|
|
19
|
+
<tr>
|
|
20
|
+
<td>📊</td>
|
|
21
|
+
<td>
|
|
22
|
+
|
|
23
|
+
[](https://www.npmjs.com/package/qsu)
|
|
24
|
+
[](https://www.npmjs.com/package/qsu)
|
|
25
|
+

|
|
26
|
+

|
|
27
|
+

|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
<tr>
|
|
33
|
+
<td>💕</td>
|
|
34
|
+
<td>
|
|
35
|
+
|
|
36
|
+
[](https://github.com/jooy2)
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
</td>
|
|
40
|
+
</tr>
|
|
41
|
+
</table>
|
|
42
|
+
|
|
17
43
|
</div>
|
|
18
44
|
|
|
19
45
|
A collection of complex or useful features that are often used in **JavaScript**. It is implemented to be used in both a client or server environment.
|
package/dist/index.js
CHANGED
|
@@ -1,415 +1 @@
|
|
|
1
|
-
import { basename, extname } from 'path';
|
|
2
|
-
import { randomBytes, createCipheriv, createDecipheriv, createHash, } from 'crypto';
|
|
3
|
-
export default class Qsu {
|
|
4
|
-
/*
|
|
5
|
-
* Misc
|
|
6
|
-
* */
|
|
7
|
-
static sleep(delay) {
|
|
8
|
-
return new Promise((resolve) => {
|
|
9
|
-
setTimeout(resolve, delay);
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
/*
|
|
13
|
-
* Math
|
|
14
|
-
* */
|
|
15
|
-
static numRandom(min, max) {
|
|
16
|
-
if (!min && !max) {
|
|
17
|
-
return (Math.random() > 0.5) ? 1 : 0;
|
|
18
|
-
}
|
|
19
|
-
const limit = !max ? min : max;
|
|
20
|
-
const offset = (!max || min >= max) ? null : min;
|
|
21
|
-
return Math.floor(Math.random() * (offset ? (limit - offset + 1) : limit + 1)) + (offset || 0);
|
|
22
|
-
}
|
|
23
|
-
static sum(...args) {
|
|
24
|
-
const val = args.length > 0 && typeof args[0] === 'object' ? args[0] : args;
|
|
25
|
-
let total = 0;
|
|
26
|
-
for (let i = 0, iLen = val.length; i < iLen; i += 1) {
|
|
27
|
-
if (typeof val[i] === 'number') {
|
|
28
|
-
total += val[i];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return total;
|
|
32
|
-
}
|
|
33
|
-
static mul(...args) {
|
|
34
|
-
const val = args.length > 0 && typeof args[0] === 'object' ? args[0] : args;
|
|
35
|
-
let total = val[0];
|
|
36
|
-
for (let i = 1, iLen = val.length; i < iLen; i += 1) {
|
|
37
|
-
if (typeof val[i] === 'number') {
|
|
38
|
-
total *= val[i];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return total;
|
|
42
|
-
}
|
|
43
|
-
/*
|
|
44
|
-
* Date
|
|
45
|
-
* */
|
|
46
|
-
static dayDiff(date1, date2) {
|
|
47
|
-
const date2c = date2 || new Date();
|
|
48
|
-
return Math.ceil(Math.abs(date2c.getTime() - date1.getTime()) / (1000 * 3600 * 24));
|
|
49
|
-
}
|
|
50
|
-
static today(separator = '-', yearFirst = true) {
|
|
51
|
-
const date = new Date();
|
|
52
|
-
const month = date.getMonth() + 1;
|
|
53
|
-
const day = date.getDate();
|
|
54
|
-
const dateArr = [
|
|
55
|
-
`${month < 10 ? '0' : ''}${month}`,
|
|
56
|
-
`${day < 10 ? '0' : ''}${day}`,
|
|
57
|
-
];
|
|
58
|
-
if (yearFirst) {
|
|
59
|
-
dateArr.unshift(date.getFullYear().toString());
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
dateArr.push(date.getFullYear().toString());
|
|
63
|
-
}
|
|
64
|
-
return dateArr.join(separator);
|
|
65
|
-
}
|
|
66
|
-
static isRealDate(date) {
|
|
67
|
-
const dateConverted = typeof date === 'string' ? new Date(date) : date;
|
|
68
|
-
if (!dateConverted.getTime()) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
return dateConverted.toISOString().slice(0, 10) === date;
|
|
72
|
-
}
|
|
73
|
-
/*
|
|
74
|
-
* Array
|
|
75
|
-
* */
|
|
76
|
-
static arrShuffle(array) {
|
|
77
|
-
if (array.length === 1) {
|
|
78
|
-
return array[0];
|
|
79
|
-
}
|
|
80
|
-
const newArray = array;
|
|
81
|
-
for (let i = array.length - 1; i > 0; i -= 1) {
|
|
82
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
83
|
-
[newArray[i], newArray[j]] = [array[j], array[i]];
|
|
84
|
-
}
|
|
85
|
-
return newArray;
|
|
86
|
-
}
|
|
87
|
-
static arrWithDefault(defaultValue, length = 0) {
|
|
88
|
-
if (length < 1) {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
return Array(length).fill(defaultValue);
|
|
92
|
-
}
|
|
93
|
-
static arrUnique(array) {
|
|
94
|
-
if (this.is2dArray(array)) {
|
|
95
|
-
return array.map((x) => JSON.stringify(x))
|
|
96
|
-
.reverse()
|
|
97
|
-
.filter((e, i, a) => a.indexOf(e, i + 1) === -1)
|
|
98
|
-
.reverse()
|
|
99
|
-
.map((x) => JSON.parse(x));
|
|
100
|
-
}
|
|
101
|
-
return [...new Set(array)];
|
|
102
|
-
}
|
|
103
|
-
static arrWithNumber(start, end) {
|
|
104
|
-
if (start > end) {
|
|
105
|
-
throw new Error('end is greater than start.');
|
|
106
|
-
}
|
|
107
|
-
return Array.from({ length: (end - start) + 1 }, (_, i) => i + start);
|
|
108
|
-
}
|
|
109
|
-
static average(array) {
|
|
110
|
-
return array.reduce((p, c) => p + c, 0) / array.length;
|
|
111
|
-
}
|
|
112
|
-
static arrMove(array, from, to) {
|
|
113
|
-
const arrayLength = array.length;
|
|
114
|
-
if (arrayLength <= from || arrayLength <= to) {
|
|
115
|
-
throw new Error('Invalid move params');
|
|
116
|
-
}
|
|
117
|
-
array.splice(to, 0, array.splice(from, 1)[0]);
|
|
118
|
-
return array;
|
|
119
|
-
}
|
|
120
|
-
/*
|
|
121
|
-
* String
|
|
122
|
-
* */
|
|
123
|
-
static removeSpecialChar(str, withoutSpace) {
|
|
124
|
-
return str.replace(new RegExp(`[^a-zA-Z가-힣ㄱ-ㅎㅏ-ㅣ0-9\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f${withoutSpace ? ' ' : ''}]`, 'gi'), '');
|
|
125
|
-
}
|
|
126
|
-
static removeNewLine(str, replaceTo = '') {
|
|
127
|
-
return str.replace(/(\r\n|\n|\r)/gm, replaceTo).trim();
|
|
128
|
-
}
|
|
129
|
-
static capitalizeFirst(str) {
|
|
130
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
131
|
-
}
|
|
132
|
-
static capitalizeEachWords(str, natural) {
|
|
133
|
-
const splitStr = str.trim().toLowerCase().split(' ');
|
|
134
|
-
for (let i = 0, iLen = splitStr.length; i < iLen; i += 1) {
|
|
135
|
-
if (!natural || !this.contains(splitStr[i], [
|
|
136
|
-
'in', 'on', 'the', 'at', 'and', 'or', 'of', 'for', 'to', 'that',
|
|
137
|
-
'a', 'by', 'it', 'is', 'as', 'are', 'were', 'was', 'nor', 'an',
|
|
138
|
-
], true)) {
|
|
139
|
-
splitStr[i] = this.capitalizeFirst(splitStr[i]);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return this.capitalizeFirst(splitStr.join(' '));
|
|
143
|
-
}
|
|
144
|
-
static strNumberOf(str, search) {
|
|
145
|
-
return (str.match(new RegExp(search, 'g')) || []).length;
|
|
146
|
-
}
|
|
147
|
-
static strShuffle(str) {
|
|
148
|
-
return [...str].sort(() => Math.random() - 0.5).join('');
|
|
149
|
-
}
|
|
150
|
-
static strRandom(length, additionalCharacters) {
|
|
151
|
-
const availCharacters = `abcdefghijklmnopqrstuvwxyz0123456789${additionalCharacters}`;
|
|
152
|
-
const availCharacterLength = availCharacters.length;
|
|
153
|
-
let result = '';
|
|
154
|
-
let newChar;
|
|
155
|
-
for (let i = 0; i < length; i += 1) {
|
|
156
|
-
newChar = availCharacters.charAt(Math.floor(Math.random() * availCharacterLength));
|
|
157
|
-
newChar = Math.random() < 0.5 ? newChar.toUpperCase() : newChar;
|
|
158
|
-
result += newChar;
|
|
159
|
-
}
|
|
160
|
-
return result;
|
|
161
|
-
}
|
|
162
|
-
static strBlindRandom(str, blindLength, blindStr = '*') {
|
|
163
|
-
let currentStr = str;
|
|
164
|
-
let hideCount = 0;
|
|
165
|
-
let tempIdx = 0;
|
|
166
|
-
let currentStrLength = 0;
|
|
167
|
-
const totalStrLength = currentStr.length;
|
|
168
|
-
while ((hideCount < blindLength) && (currentStrLength < totalStrLength)) {
|
|
169
|
-
tempIdx = this.numRandom(0, totalStrLength);
|
|
170
|
-
if (/[a-zA-Z가-힣]/.test(currentStr.substring(tempIdx, tempIdx + 1))) {
|
|
171
|
-
currentStr = `${currentStr.substring(0, tempIdx + 1)}${blindStr}${currentStr.substring(tempIdx + 2)}`;
|
|
172
|
-
hideCount += 1;
|
|
173
|
-
}
|
|
174
|
-
currentStrLength += 1;
|
|
175
|
-
}
|
|
176
|
-
return currentStr;
|
|
177
|
-
}
|
|
178
|
-
static truncate(str, length, ellipsis = '') {
|
|
179
|
-
let convStr = str;
|
|
180
|
-
if (str.length > length) {
|
|
181
|
-
convStr = str.substring(0, length) + ellipsis;
|
|
182
|
-
}
|
|
183
|
-
return convStr;
|
|
184
|
-
}
|
|
185
|
-
static split(str, ...splitter) {
|
|
186
|
-
const splitters = splitter.length > 0 && typeof splitter[0] === 'object' ? splitter[0] : splitter;
|
|
187
|
-
const splitterLength = splitters.length;
|
|
188
|
-
let charPattern = '';
|
|
189
|
-
let strPattern = '';
|
|
190
|
-
for (let i = 0; i < splitterLength; i += 1) {
|
|
191
|
-
const spl = splitters[i];
|
|
192
|
-
if (spl.length > 1) {
|
|
193
|
-
strPattern += `${strPattern.length < 1 ? '' : '|'}${spl
|
|
194
|
-
.replace(/\\/g, '\\\\')
|
|
195
|
-
.replace(/\[/g, '\\[')
|
|
196
|
-
.replace(/]/g, '\\]')
|
|
197
|
-
.replace(/\?/g, '\\?')
|
|
198
|
-
.replace(/\./g, '\\.')
|
|
199
|
-
.replace(/\{/g, '\\{')
|
|
200
|
-
.replace(/}/g, '\\}')
|
|
201
|
-
.replace(/\+/g, '\\+')}`;
|
|
202
|
-
}
|
|
203
|
-
else if (spl === '-' || spl === '[' || spl === ']') {
|
|
204
|
-
charPattern += `\\${spl}`;
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
charPattern += spl;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (charPattern.length < 1 && strPattern.length < 1) {
|
|
211
|
-
return [str];
|
|
212
|
-
}
|
|
213
|
-
if (charPattern.length > 0) {
|
|
214
|
-
charPattern = `[${charPattern}]`;
|
|
215
|
-
if (strPattern.length > 0) {
|
|
216
|
-
strPattern = `|${strPattern}`;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return str.split(new RegExp(`${charPattern}${strPattern}+`, 'gi'));
|
|
220
|
-
}
|
|
221
|
-
static encrypt(str, secret, algorithm = 'aes-256-cbc', ivSize = 16) {
|
|
222
|
-
if (!str || str.length < 1) {
|
|
223
|
-
return '';
|
|
224
|
-
}
|
|
225
|
-
const iv = randomBytes(ivSize);
|
|
226
|
-
const cipher = createCipheriv(algorithm, secret, iv);
|
|
227
|
-
let enc = cipher.update(str);
|
|
228
|
-
enc = Buffer.concat([enc, cipher.final()]);
|
|
229
|
-
return `${iv.toString('hex')}:${enc.toString('hex')}`;
|
|
230
|
-
}
|
|
231
|
-
static decrypt(str, secret, algorithm = 'aes-256-cbc') {
|
|
232
|
-
if (!str || str.length < 1) {
|
|
233
|
-
return '';
|
|
234
|
-
}
|
|
235
|
-
const arrStr = str.split(':');
|
|
236
|
-
const decipher = createDecipheriv(algorithm, secret, Buffer.from(arrStr.shift(), 'hex'));
|
|
237
|
-
let decrypted = decipher.update(Buffer.from(arrStr.join(':'), 'hex'));
|
|
238
|
-
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
239
|
-
return decrypted.toString();
|
|
240
|
-
}
|
|
241
|
-
static md5(str) {
|
|
242
|
-
return createHash('md5').update(str).digest('hex');
|
|
243
|
-
}
|
|
244
|
-
static sha1(str) {
|
|
245
|
-
return createHash('sha1').update(str).digest('hex');
|
|
246
|
-
}
|
|
247
|
-
static sha256(str) {
|
|
248
|
-
return createHash('sha256').update(str).digest('hex');
|
|
249
|
-
}
|
|
250
|
-
static encodeBase64(str) {
|
|
251
|
-
return Buffer.from(str, 'utf8').toString('base64');
|
|
252
|
-
}
|
|
253
|
-
static decodeBase64(encodedStr) {
|
|
254
|
-
return Buffer.from(encodedStr, 'base64').toString('utf8');
|
|
255
|
-
}
|
|
256
|
-
static strUnique(str) {
|
|
257
|
-
return [...new Set(str)].join('');
|
|
258
|
-
}
|
|
259
|
-
/*
|
|
260
|
-
* Verify
|
|
261
|
-
* */
|
|
262
|
-
static isEqual(leftOperand, ...rightOperand) {
|
|
263
|
-
const rightOperands = rightOperand.length > 0 && typeof rightOperand[0] === 'object' ? rightOperand[0] : rightOperand;
|
|
264
|
-
const rightOperandLength = rightOperands.length;
|
|
265
|
-
for (let i = 0; i < rightOperandLength; i += 1) {
|
|
266
|
-
// eslint-disable-next-line eqeqeq
|
|
267
|
-
if (rightOperands[i] != leftOperand) {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
static isEqualStrict(leftOperand, ...rightOperand) {
|
|
274
|
-
const rightOperands = rightOperand.length > 0 && typeof rightOperand[0] === 'object' ? rightOperand[0] : rightOperand;
|
|
275
|
-
const rightOperandLength = rightOperands.length;
|
|
276
|
-
for (let i = 0; i < rightOperandLength; i += 1) {
|
|
277
|
-
if (rightOperands[i] !== leftOperand) {
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
static isEmpty(data) {
|
|
284
|
-
if (!data) {
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
287
|
-
switch (typeof data) {
|
|
288
|
-
case 'string':
|
|
289
|
-
return data.length < 1;
|
|
290
|
-
case 'object':
|
|
291
|
-
if (Array.isArray(data)) {
|
|
292
|
-
return data.length < 1;
|
|
293
|
-
}
|
|
294
|
-
return Object.keys(data).length < 1;
|
|
295
|
-
default:
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
static isUrl(url, withProtocol = false, strict = false) {
|
|
300
|
-
if (strict && url.indexOf('.') === -1) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
try {
|
|
304
|
-
new URL(`${(withProtocol && url.indexOf('://') === -1) ? 'https://' : ''}${url}`).toString();
|
|
305
|
-
}
|
|
306
|
-
catch (e) {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
static contains(str, search, exact = false) {
|
|
312
|
-
if (typeof search === 'string') {
|
|
313
|
-
return str.length < 1 ? false : str.indexOf(search) !== -1;
|
|
314
|
-
}
|
|
315
|
-
for (let i = 0, iLen = search.length; i < iLen; i += 1) {
|
|
316
|
-
if (exact) {
|
|
317
|
-
if (str === search[i]) {
|
|
318
|
-
return true;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
else if (str.indexOf(search[i]) !== -1) {
|
|
322
|
-
return true;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
static is2dArray(array) {
|
|
328
|
-
return array.filter(Array.isArray).length > 0;
|
|
329
|
-
}
|
|
330
|
-
static between(range, number, inclusive = false) {
|
|
331
|
-
const minM = Math.min.apply(Math, [range[0], range[1]]);
|
|
332
|
-
const maxM = Math.max.apply(Math, [range[0], range[1]]);
|
|
333
|
-
return inclusive ? number >= minM && number <= maxM : number > minM && number < maxM;
|
|
334
|
-
}
|
|
335
|
-
static len(data) {
|
|
336
|
-
if (!data) {
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
switch (typeof data) {
|
|
340
|
-
case 'object':
|
|
341
|
-
return Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
342
|
-
case 'number':
|
|
343
|
-
case 'bigint':
|
|
344
|
-
return data.toString().length;
|
|
345
|
-
case 'boolean':
|
|
346
|
-
return data ? 4 : 5;
|
|
347
|
-
case 'function':
|
|
348
|
-
return data().length;
|
|
349
|
-
case 'string':
|
|
350
|
-
default:
|
|
351
|
-
return data.length;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
static isBotAgent(userAgent) {
|
|
355
|
-
return /bot|naverbot|google|Googlebot|Googlebot-Mobile|Googlebot-Image|Google favicon|Chrome-Lighthouse|Mediapartners-Google|bingbot|slurp|java|wget|curl|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|dotbot|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|Twitterbot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis/i.test(userAgent);
|
|
356
|
-
}
|
|
357
|
-
/*
|
|
358
|
-
* Format
|
|
359
|
-
* */
|
|
360
|
-
static numberFormat(number) {
|
|
361
|
-
return new Intl.NumberFormat().format(number);
|
|
362
|
-
}
|
|
363
|
-
static fileName(filePath, withExtension = false) {
|
|
364
|
-
if (withExtension) {
|
|
365
|
-
return basename(filePath);
|
|
366
|
-
}
|
|
367
|
-
return basename(filePath, extname(filePath));
|
|
368
|
-
}
|
|
369
|
-
static fileSize(bytes, decimals = 2) {
|
|
370
|
-
if (bytes === 0 || bytes < 0) {
|
|
371
|
-
return '0 Bytes';
|
|
372
|
-
}
|
|
373
|
-
const byteCalc = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
374
|
-
return `${parseFloat((bytes / 1024 ** byteCalc).toFixed((decimals < 0 ? 0 : decimals)))} ${['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][byteCalc]}`;
|
|
375
|
-
}
|
|
376
|
-
static fileExt(filePath) {
|
|
377
|
-
if (filePath.indexOf('.') === -1) {
|
|
378
|
-
return 'Unknown';
|
|
379
|
-
}
|
|
380
|
-
const pSpl = filePath.trim().toLowerCase().split('.');
|
|
381
|
-
return pSpl.length > 0 ? pSpl[pSpl.length - 1] : 'Unknown';
|
|
382
|
-
}
|
|
383
|
-
static msToTime(milliseconds = 0, withMilliseconds = false, separator = ':') {
|
|
384
|
-
const ms = Math.floor((milliseconds % 1000) / 100);
|
|
385
|
-
let sec = Math.floor((milliseconds / 1000) % 60);
|
|
386
|
-
let min = Math.floor((milliseconds / (1000 * 60)) % 60);
|
|
387
|
-
let hour = Math.floor(milliseconds / (1000 * 60 * 60));
|
|
388
|
-
hour = (hour < 10) ? `0${hour}` : hour;
|
|
389
|
-
min = (min < 10) ? `0${min}` : min;
|
|
390
|
-
sec = (sec < 10) ? `0${sec}` : sec;
|
|
391
|
-
return `${hour}${separator}${min}${separator}${sec}${withMilliseconds ? `.${ms}` : ''}`;
|
|
392
|
-
}
|
|
393
|
-
static secToTime(seconds = 0, onlyHour = false, separator = ':') {
|
|
394
|
-
let sec = Math.floor(seconds % 60);
|
|
395
|
-
let min = Math.floor((seconds / 60) % 60);
|
|
396
|
-
let hour = Math.floor(seconds / (60 * 60));
|
|
397
|
-
hour = (hour < 10) ? `0${hour}` : hour;
|
|
398
|
-
min = (min < 10) ? `0${min}` : min;
|
|
399
|
-
sec = (sec < 10) ? `0${sec}` : sec;
|
|
400
|
-
return onlyHour ? hour.toString() : `${hour}${separator}${min}${separator}${sec}`;
|
|
401
|
-
}
|
|
402
|
-
static license(options) {
|
|
403
|
-
const br = options.htmlBr ? '<br/>' : '\n';
|
|
404
|
-
const yearString = `${options.yearStart}${options.yearEnd ? `-${options.yearEnd}` : ''}`;
|
|
405
|
-
const authorString = `${options.author}${options.email ? ` <${options.email}>` : ''}`;
|
|
406
|
-
switch (options.type.replace(/\.-_,\s/g, '').toLowerCase()) {
|
|
407
|
-
case 'apache20':
|
|
408
|
-
return `Copyright ${yearString} ${authorString}${br}${br}Licensed under the Apache License, Version 2.0 (the "License");${br}you may not use this file except in compliance with the License.${br}You may obtain a copy of the License at${br}${br} http://www.apache.org/licenses/LICENSE-2.0${br}${br}Unless required by applicable law or agreed to in writing, software${br}distributed under the License is distributed on an "AS IS" BASIS,${br}WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.${br}See the License for the specific language governing permissions and${br}limitations under the License.`;
|
|
409
|
-
case 'mit':
|
|
410
|
-
default:
|
|
411
|
-
return `Copyright (c) ${yearString} ${authorString}${br}${br}Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:${br}${br}The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.${br}${br}THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
export { Qsu, };
|
|
1
|
+
import{basename as t,extname as e,win32 as r}from"path";import{randomBytes as o,createCipheriv as n,createDecipheriv as i,createHash as a}from"crypto";export default class s{static sleep(t){return new Promise((e=>{setTimeout(e,t)}))}static numRandom(t,e){if(!t&&!e)return Math.random()>.5?1:0;const r=e||t,o=!e||t>=e?null:t;return Math.floor(Math.random()*(o?r-o+1:r+1))+(o||0)}static sum(...t){const e=t.length>0&&"object"==typeof t[0]?t[0]:t;let r=0;for(let t=0,o=e.length;t<o;t+=1)"number"==typeof e[t]&&(r+=e[t]);return r}static mul(...t){const e=t.length>0&&"object"==typeof t[0]?t[0]:t;let r=e[0];for(let t=1,o=e.length;t<o;t+=1)"number"==typeof e[t]&&(r*=e[t]);return r}static dayDiff(t,e){const r=e||new Date;return Math.ceil(Math.abs(r.getTime()-t.getTime())/864e5)}static today(t="-",e=!0){const r=new Date,o=r.getMonth()+1,n=r.getDate(),i=[`${o<10?"0":""}${o}`,`${n<10?"0":""}${n}`];return e?i.unshift(r.getFullYear().toString()):i.push(r.getFullYear().toString()),i.join(t)}static isRealDate(t){const e="string"==typeof t?new Date(t):t;return!!e.getTime()&&e.toISOString().slice(0,10)===t}static arrShuffle(t){if(1===t.length)return t[0];const e=t;for(let r=t.length-1;r>0;r-=1){const o=Math.floor(Math.random()*(r+1));[e[r],e[o]]=[t[o],t[r]]}return e}static arrWithDefault(t,e=0){return e<1?[]:Array(e).fill(t)}static arrUnique(t){return this.is2dArray(t)?t.map((t=>JSON.stringify(t))).reverse().filter(((t,e,r)=>-1===r.indexOf(t,e+1))).reverse().map((t=>JSON.parse(t))):[...new Set(t)]}static arrWithNumber(t,e){if(t>e)throw new Error("end is greater than start.");return Array.from({length:e-t+1},((e,r)=>r+t))}static average(t){return t.reduce(((t,e)=>t+e),0)/t.length}static arrMove(t,e,r){const o=t.length;if(o<=e||o<=r)throw new Error("Invalid move params");return t.splice(r,0,t.splice(e,1)[0]),t}static removeSpecialChar(t,e){return t?t.replace(new RegExp(`[^a-zA-Z가-힣ㄱ-ㅎㅏ-ㅣ0-9-ヿ㐀-䶿一-鿿豈-ヲ-゚${e?" ":""}]`,"gi"),""):""}static removeNewLine(t,e=""){return t?t.replace(/(\r\n|\n|\r)/gm,e).trim():""}static capitalizeFirst(t){return t?t.charAt(0).toUpperCase()+t.slice(1):""}static capitalizeEachWords(t,e){if(!t)return"";const r=t.trim().toLowerCase().split(" ");for(let t=0,o=r.length;t<o;t+=1)e&&this.contains(r[t],["in","on","the","at","and","or","of","for","to","that","a","by","it","is","as","are","were","was","nor","an"],!0)||(r[t]=this.capitalizeFirst(r[t]));return this.capitalizeFirst(r.join(" "))}static strNumberOf(t,e){return t?(t.match(new RegExp(e,"g"))||[]).length:0}static strShuffle(t){return t?[...t].sort((()=>Math.random()-.5)).join(""):""}static strRandom(t,e){const r=`abcdefghijklmnopqrstuvwxyz0123456789${e}`,o=r.length;let n,i="";for(let e=0;e<t;e+=1)n=r.charAt(Math.floor(Math.random()*o)),n=Math.random()<.5?n.toUpperCase():n,i+=n;return i}static strBlindRandom(t,e,r="*"){if(!t)return"";let o=t,n=0,i=0,a=0;const s=o.length;for(;n<e&&a<s;)i=this.numRandom(0,s),/[a-zA-Z가-힣]/.test(o.substring(i,i+1))&&(o=`${o.substring(0,i+1)}${r}${o.substring(i+2)}`,n+=1),a+=1;return o}static truncate(t,e,r=""){if(!t)return"";let o=t;return t.length>e&&(o=t.substring(0,e)+r),o}static split(t,...e){if(!t)return[];const r=e.length>0&&"object"==typeof e[0]?e[0]:e,o=r.length;let n="",i="";for(let t=0;t<o;t+=1){const e=r[t];e.length>1?i+=`${i.length<1?"":"|"}${e.replace(/\\/g,"\\\\").replace(/\[/g,"\\[").replace(/]/g,"\\]").replace(/\?/g,"\\?").replace(/\./g,"\\.").replace(/\{/g,"\\{").replace(/}/g,"\\}").replace(/\+/g,"\\+")}`:n+="-"===e||"["===e||"]"===e?`\\${e}`:e}return n.length<1&&i.length<1?[t]:(n.length>0&&(n=`[${n}]`,i.length>0&&(i=`|${i}`)),t.split(new RegExp(`${n}${i}+`,"gi")))}static encrypt(t,e,r="aes-256-cbc",i=16){if(!t||t.length<1)return"";const a=o(i),s=n(r,e,a);let c=s.update(t);return c=Buffer.concat([c,s.final()]),`${a.toString("hex")}:${c.toString("hex")}`}static decrypt(t,e,r="aes-256-cbc"){if(!t||t.length<1)return"";const o=t.split(":"),n=i(r,e,Buffer.from(o.shift(),"hex"));let a=n.update(Buffer.from(o.join(":"),"hex"));return a=Buffer.concat([a,n.final()]),a.toString()}static md5(t){return a("md5").update(t).digest("hex")}static sha1(t){return a("sha1").update(t).digest("hex")}static sha256(t){return a("sha256").update(t).digest("hex")}static encodeBase64(t){return Buffer.from(t,"utf8").toString("base64")}static decodeBase64(t){return Buffer.from(t,"base64").toString("utf8")}static strUnique(t){return t?[...new Set(t)].join(""):""}static isEqual(t,...e){const r=e.length>0&&"object"==typeof e[0]?e[0]:e,o=r.length;for(let e=0;e<o;e+=1)if(r[e]!=t)return!1;return!0}static isEqualStrict(t,...e){const r=e.length>0&&"object"==typeof e[0]?e[0]:e,o=r.length;for(let e=0;e<o;e+=1)if(r[e]!==t)return!1;return!0}static isEmpty(t){if(!t)return!0;switch(typeof t){case"string":return t.length<1;case"object":return Array.isArray(t)?t.length<1:Object.keys(t).length<1;default:return!1}}static isUrl(t,e=!1,r=!1){if(r&&-1===t.indexOf("."))return!1;try{new URL(`${e&&-1===t.indexOf("://")?"https://":""}${t}`).toString()}catch(t){return!1}return!0}static contains(t,e,r=!1){if("string"==typeof e)return!(t.length<1)&&-1!==t.indexOf(e);for(let o=0,n=e.length;o<n;o+=1)if(r){if(t===e[o])return!0}else if(-1!==t.indexOf(e[o]))return!0;return!1}static is2dArray(t){return t.filter(Array.isArray).length>0}static between(t,e,r=!1){const o=Math.min.apply(Math,[t[0],t[1]]),n=Math.max.apply(Math,[t[0],t[1]]);return r?e>=o&&e<=n:e>o&&e<n}static len(t){if(!t)return 0;switch(typeof t){case"object":return Array.isArray(t)?t.length:Object.keys(t).length;case"number":case"bigint":return t.toString().length;case"boolean":return t?4:5;case"function":return t().length;default:return t.length}}static isBotAgent(t){return/bot|naverbot|google|Googlebot|Googlebot-Mobile|Googlebot-Image|Google favicon|Chrome-Lighthouse|Mediapartners-Google|bingbot|slurp|java|wget|curl|Commons-HttpClient|Python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|jyxobot|FAST-WebCrawler|FAST Enterprise Crawler|biglotron|teoma|convera|seekbot|gigablast|exabot|ngbot|ia_archiver|GingerCrawler|webmon |httrack|webcrawler|grub.org|UsineNouvelleCrawler|antibot|netresearchserver|speedy|fluffy|bibnum.bnf|findlink|msrbot|panscient|yacybot|AISearchBot|IOI|ips-agent|tagoobot|MJ12bot|dotbot|woriobot|yanga|buzzbot|mlbot|yandexbot|purebot|Linguee Bot|Voyager|CyberPatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|Adidxbot|blekkobot|ezooms|Mail.RU_Bot|discobot|heritrix|findthatfile|europarchive.org|NerdByNature.Bot|sistrix crawler|ahrefsbot|Aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|facebookexternalhit|yeti|RetrevoPageAnalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|DuckDuckBot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnam gnam spider|web-archive-net.com.bot|backlinkcrawler|coccoc|integromedb|content crawler spider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler.com|siteexplorer.info|elisabot|proximic|changedetection|blexbot|arabot|WeSEE:Search|niki-bot|CrystalSemanticsBot|rogerbot|360Spider|psbot|InterfaxScanBot|Lipperhey SEO Service|CC Metadata Scaper|g00g1e.net|GrapeshotCrawler|urlappendbot|brainobot|fr-crawler|binlar|SimpleCrawler|Livelapbot|Twitterbot|cXensebot|smtbot|bnf.fr_bot|A6-Indexer|ADmantX|Facebot|OrangeBot|memorybot|AdvBot|MegaIndex|SemanticScholarBot|ltx71|nerdybot|xovibot|BUbiNG|Qwantify|archive.org_bot|Applebot|TweetmemeBot|crawler4j|findxbot|SemrushBot|yoozBot|lipperhey|y!j-asr|Domain Re-Animator Bot|AddThis/i.test(t)}static numberFormat(t){return(new Intl.NumberFormat).format(t)}static fileName(o,n=!1){return o?-1===o.indexOf("/")?n?r.basename(o):r.basename(o,e(o)):n?t(o):t(o,e(o)):""}static fileSize(t,e=2){if(0===t||t<0)return"0 Bytes";const r=Math.floor(Math.log(t)/Math.log(1024));return`${parseFloat((t/1024**r).toFixed(e<0?0:e))} ${["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][r]}`}static fileExt(t){if(-1===t.indexOf("."))return"Unknown";const e=t.trim().toLowerCase().split(".");return e.length>0?e[e.length-1]:"Unknown"}static msToTime(t=0,e=!1,r=":"){const o=Math.floor(t%1e3/100);let n=Math.floor(t/1e3%60),i=Math.floor(t/6e4%60),a=Math.floor(t/36e5);return a=a<10?`0${a}`:a,i=i<10?`0${i}`:i,n=n<10?`0${n}`:n,`${a}${r}${i}${r}${n}${e?`.${o}`:""}`}static secToTime(t=0,e=!1,r=":"){let o=Math.floor(t%60),n=Math.floor(t/60%60),i=Math.floor(t/3600);return i=i<10?`0${i}`:i,n=n<10?`0${n}`:n,o=o<10?`0${o}`:o,e?i.toString():`${i}${r}${n}${r}${o}`}static license(t){const e=t.htmlBr?"<br/>":"\n",r=`${t.yearStart}${t.yearEnd?`-${t.yearEnd}`:""}`,o=`${t.author}${t.email?` <${t.email}>`:""}`;return"apache20"===t.type.replace(/\.-_,\s/g,"").toLowerCase()?`Copyright ${r} ${o}${e}${e}Licensed under the Apache License, Version 2.0 (the "License");${e}you may not use this file except in compliance with the License.${e}You may obtain a copy of the License at${e}${e} http://www.apache.org/licenses/LICENSE-2.0${e}${e}Unless required by applicable law or agreed to in writing, software${e}distributed under the License is distributed on an "AS IS" BASIS,${e}WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.${e}See the License for the specific language governing permissions and${e}limitations under the License.`:`Copyright (c) ${r} ${o}${e}${e}Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:${e}${e}The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.${e}${e}THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`}}export{s as Qsu};
|
package/logo.webp
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qsu",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Quick and Simple Utility for JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"test": "npm run
|
|
8
|
+
"build": "tsc && npm run minify",
|
|
9
|
+
"test": "npm run build && mocha --parallel test/*.spec.js",
|
|
10
10
|
"lint": "eslint .",
|
|
11
|
-
"lint:fix": "eslint --fix ."
|
|
11
|
+
"lint:fix": "eslint --fix .",
|
|
12
|
+
"minify": "terser dist/index.js --config-file terser.config.json -o dist/index.js",
|
|
13
|
+
"prepare": "npm run build"
|
|
12
14
|
},
|
|
13
15
|
"engines": {
|
|
14
16
|
"node": ">=14.0.0"
|
|
@@ -43,15 +45,16 @@
|
|
|
43
45
|
"math"
|
|
44
46
|
],
|
|
45
47
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^18.
|
|
47
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
48
|
-
"@typescript-eslint/parser": "^5.
|
|
49
|
-
"date-fns": "^2.29.
|
|
50
|
-
"eslint": "^8.
|
|
48
|
+
"@types/node": "^18.7.14",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
|
50
|
+
"@typescript-eslint/parser": "^5.36.1",
|
|
51
|
+
"date-fns": "^2.29.2",
|
|
52
|
+
"eslint": "^8.22.0",
|
|
51
53
|
"eslint-config-airbnb": "^19.0.4",
|
|
52
54
|
"eslint-plugin-import": "^2.26.0",
|
|
53
55
|
"mocha": "^10.0.0",
|
|
56
|
+
"terser": "^5.15.0",
|
|
54
57
|
"ts-node": "^10.9.1",
|
|
55
|
-
"typescript": "^4.
|
|
58
|
+
"typescript": "^4.8.2"
|
|
56
59
|
}
|
|
57
60
|
}
|
package/.eslintignore
DELETED
package/.eslintrc.cjs
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
parserOptions: {
|
|
3
|
-
sourceType: 'module',
|
|
4
|
-
ecmaVersion: 2020,
|
|
5
|
-
},
|
|
6
|
-
env: {
|
|
7
|
-
node: true,
|
|
8
|
-
es6: true,
|
|
9
|
-
},
|
|
10
|
-
parser: '@typescript-eslint/parser',
|
|
11
|
-
plugins: [
|
|
12
|
-
'@typescript-eslint',
|
|
13
|
-
],
|
|
14
|
-
extends: [
|
|
15
|
-
'airbnb/base',
|
|
16
|
-
'plugin:@typescript-eslint/recommended',
|
|
17
|
-
],
|
|
18
|
-
rules: {
|
|
19
|
-
'linebreak-style': ['error', 'windows'],
|
|
20
|
-
'arrow-parens': 0,
|
|
21
|
-
'max-len': 0,
|
|
22
|
-
'@typescript-eslint/no-explicit-any': 0
|
|
23
|
-
},
|
|
24
|
-
overrides: [
|
|
25
|
-
{
|
|
26
|
-
files: ['test/*.spec.js'],
|
|
27
|
-
rules: {
|
|
28
|
-
'import/extensions': 0,
|
|
29
|
-
'no-undef': 0,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
settings: {
|
|
34
|
-
'import/parsers': {
|
|
35
|
-
'@typescript-eslint/parser': ['.ts', '.js'],
|
|
36
|
-
},
|
|
37
|
-
'import/resolver': {
|
|
38
|
-
node: {
|
|
39
|
-
paths: ['lib'],
|
|
40
|
-
extensions: ['.js', '.ts'],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
package/qsu-logo.png
DELETED
|
Binary file
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"baseUrl": "lib",
|
|
4
|
-
"target": "es2020",
|
|
5
|
-
"module": "es2022",
|
|
6
|
-
"lib": [
|
|
7
|
-
"es2020"
|
|
8
|
-
],
|
|
9
|
-
"moduleResolution": "Node",
|
|
10
|
-
"allowSyntheticDefaultImports": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"outDir": "./dist",
|
|
13
|
-
"strict": true
|
|
14
|
-
},
|
|
15
|
-
"include": [
|
|
16
|
-
"lib/**/*.ts",
|
|
17
|
-
"lib/**/*.d.ts"
|
|
18
|
-
],
|
|
19
|
-
"exclude": [
|
|
20
|
-
"node_modules",
|
|
21
|
-
"**/*.spec.ts"
|
|
22
|
-
]
|
|
23
|
-
}
|