topkat-utils 1.2.117 → 1.3.4
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 +260 -219
- package/coverage/clover.xml +523 -427
- package/coverage/coverage-final.json +10 -10
- package/coverage/lcov-report/clean-stack-trace.ts.html +13 -10
- package/coverage/lcov-report/config.ts.html +2 -2
- package/coverage/lcov-report/error-utils.ts.html +2 -2
- package/coverage/lcov-report/index.html +56 -56
- package/coverage/lcov-report/is-empty.ts.html +1 -1
- package/coverage/lcov-report/is-nodejs.ts.html +1 -1
- package/coverage/lcov-report/is-object.ts.html +1 -1
- package/coverage/lcov-report/isset.ts.html +1 -1
- package/coverage/lcov-report/logger-utils.ts.html +215 -215
- package/coverage/lcov-report/loop-utils.ts.html +179 -8
- package/coverage/lcov-report/math-utils.ts.html +35 -8
- package/coverage/lcov-report/object-utils.ts.html +20 -8
- package/coverage/lcov-report/regexp-utils.ts.html +45 -12
- package/coverage/lcov-report/remove-circular-json-stringify.ts.html +1 -1
- package/coverage/lcov-report/string-utils.ts.html +54 -12
- package/coverage/lcov-report/timer-utils.ts.html +2 -2
- package/coverage/lcov-report/transaction-utils.ts.html +1 -1
- package/coverage/lcov.info +626 -607
- package/dist/src/clean-stack-trace.js +1 -0
- package/dist/src/clean-stack-trace.js.map +1 -1
- package/dist/src/error-utils.js +1 -1
- package/dist/src/error-utils.js.map +1 -1
- package/dist/src/loop-utils.d.ts +54 -2
- package/dist/src/loop-utils.js +48 -2
- package/dist/src/loop-utils.js.map +1 -1
- package/dist/src/math-utils.d.ts +1 -1
- package/dist/src/math-utils.js +6 -2
- package/dist/src/math-utils.js.map +1 -1
- package/dist/src/object-utils.d.ts +3 -1
- package/dist/src/object-utils.js +7 -4
- package/dist/src/object-utils.js.map +1 -1
- package/dist/src/regexp-utils.d.ts +9 -4
- package/dist/src/regexp-utils.js +8 -5
- package/dist/src/regexp-utils.js.map +1 -1
- package/dist/src/string-utils.d.ts +13 -4
- package/dist/src/string-utils.js +16 -6
- package/dist/src/string-utils.js.map +1 -1
- package/dist/src/timer-utils.d.ts +1 -1
- package/dist/src/timer-utils.js +1 -1
- package/package.json +45 -8
- package/src/clean-stack-trace.ts +1 -0
- package/src/error-utils.ts +1 -1
- package/src/loop-utils.ts +59 -2
- package/src/math-utils.ts +11 -2
- package/src/object-utils.ts +6 -2
- package/src/regexp-utils.ts +17 -6
- package/src/string-utils.ts +19 -5
- package/src/timer-utils.ts +1 -1
package/package.json
CHANGED
|
@@ -1,21 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "topkat-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"types": "index.ts",
|
|
6
6
|
"main": "dist",
|
|
7
|
-
"description": "
|
|
8
|
-
"
|
|
9
|
-
|
|
7
|
+
"description": "A comprehensive collection of TypeScript/JavaScript utility functions for common programming tasks. Includes validation, object manipulation, date handling, string formatting, and more. Zero dependencies, fully typed, and optimized for performance.",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"typescript",
|
|
10
|
+
"javascript",
|
|
11
|
+
"utils",
|
|
12
|
+
"utilities",
|
|
13
|
+
"validation",
|
|
14
|
+
"date",
|
|
15
|
+
"string",
|
|
16
|
+
"object",
|
|
17
|
+
"array",
|
|
18
|
+
"url",
|
|
19
|
+
"path",
|
|
20
|
+
"logger",
|
|
21
|
+
"error-handling",
|
|
22
|
+
"functional",
|
|
23
|
+
"zero-dependencies",
|
|
24
|
+
"performance",
|
|
25
|
+
"type-safe",
|
|
26
|
+
"dot-notation",
|
|
27
|
+
"template-engine",
|
|
28
|
+
"deep-clone",
|
|
29
|
+
"merge",
|
|
30
|
+
"flatten",
|
|
31
|
+
"case-conversion"
|
|
32
|
+
],
|
|
33
|
+
"author": "https://github.com/top-kat",
|
|
34
|
+
"license": "MIT",
|
|
10
35
|
"repository": {
|
|
11
36
|
"type": "git",
|
|
12
37
|
"url": "git+https://github.com/top-kat/utils.git"
|
|
13
38
|
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/top-kat/utils/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/top-kat/utils#readme",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=14.0.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
14
49
|
"scripts": {
|
|
15
50
|
"build": "tsc",
|
|
16
|
-
"
|
|
17
|
-
"bump
|
|
18
|
-
"bump:
|
|
51
|
+
"// Please run BUMP command with NPM not Yarn": "",
|
|
52
|
+
"bump": "yarn test:ci && yarn build && bump --patch",
|
|
53
|
+
"bump:major": "yarn test:ci && yarn build && bump --major",
|
|
54
|
+
"bump:minor": "yarn test:ci && yarn build && bump --minor",
|
|
55
|
+
"bump:patch": "yarn test:ci && yarn build && bump --patch",
|
|
19
56
|
"test": "jest --watchAll",
|
|
20
57
|
"test:ci": "jest --ci --runInBand --maxConcurrency=1 --silent"
|
|
21
58
|
},
|
|
@@ -24,8 +61,8 @@
|
|
|
24
61
|
"@types/node": "^18.11.18",
|
|
25
62
|
"@typescript-eslint/eslint-plugin": "latest",
|
|
26
63
|
"@typescript-eslint/parser": "latest",
|
|
27
|
-
"bump-simple": "^1.0.0",
|
|
28
64
|
"eslint": "^8.0.0",
|
|
65
|
+
"bump-simple": "^1.0.29",
|
|
29
66
|
"jest": "^29.7.0",
|
|
30
67
|
"ts-jest": "^29.2.4",
|
|
31
68
|
"ts-node": "^10.9.2",
|
package/src/clean-stack-trace.ts
CHANGED
package/src/error-utils.ts
CHANGED
|
@@ -51,7 +51,7 @@ export const failSafe = tryCatch // ALIAS
|
|
|
51
51
|
function extraInfosRendererDefault(extraInfos) {
|
|
52
52
|
return [
|
|
53
53
|
'== EXTRA INFOS ==',
|
|
54
|
-
removeCircularJSONstringify(extraInfos, 2)
|
|
54
|
+
removeCircularJSONstringify({ ...extraInfos, msg: undefined, message: undefined }, 2)
|
|
55
55
|
]
|
|
56
56
|
}
|
|
57
57
|
|
package/src/loop-utils.ts
CHANGED
|
@@ -5,7 +5,37 @@ import { ObjectGeneric } from './types'
|
|
|
5
5
|
import { err500IfNotSet } from './error-utils'
|
|
6
6
|
import { isObject } from './is-object'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Iterates over a specified number of times, passing each iteration's result to the next callback
|
|
10
|
+
* @returns Array containing results from all iterations
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Generate Fibonacci sequence
|
|
15
|
+
* forI(8, (i, prev, results) => {
|
|
16
|
+
* if (i <= 1) return 1
|
|
17
|
+
* return results[i-1] + results[i-2]
|
|
18
|
+
* })
|
|
19
|
+
* ```
|
|
20
|
+
* // Returns: [1, 1, 2, 3, 5, 8, 13, 21]
|
|
21
|
+
*/
|
|
22
|
+
export function forI<T extends any[] | any>(
|
|
23
|
+
/** Number of times to iterate */
|
|
24
|
+
nbIterations: number,
|
|
25
|
+
/** Function called for each iteration with:
|
|
26
|
+
* - number: Current iteration index (0-based)
|
|
27
|
+
* - previousValue: Result from previous iteration
|
|
28
|
+
* - arrayOfPreviousValues: Array of all previous results
|
|
29
|
+
*/
|
|
30
|
+
callback: (
|
|
31
|
+
/** Current iteration index (0-based) */
|
|
32
|
+
number: number,
|
|
33
|
+
/** Result from previous iteration */
|
|
34
|
+
previousValue,
|
|
35
|
+
/** Array of all previous results */
|
|
36
|
+
arrayOfPreviousValues: any[]
|
|
37
|
+
) => T
|
|
38
|
+
): T[] {
|
|
9
39
|
const results: any[] = []
|
|
10
40
|
for (let i = 0; i < nbIterations; i++) {
|
|
11
41
|
const prevValue = results[results.length - 1]
|
|
@@ -14,6 +44,20 @@ export function forI<T extends any[] | any>(nbIterations: number, callback: (num
|
|
|
14
44
|
return results
|
|
15
45
|
}
|
|
16
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Iterates over a specified number of times, passing each iteration's result to the next callback
|
|
49
|
+
* @returns Array containing results from all iterations
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Generate Fibonacci sequence ASYNC
|
|
54
|
+
* await forI(8, async (i, prev, results) => {
|
|
55
|
+
* if (i <= 1) return 1
|
|
56
|
+
* return new Promise(resolve => setTimeout(() => resolve(results[i-1] + results[i-2]), 100))
|
|
57
|
+
* })
|
|
58
|
+
* ```
|
|
59
|
+
* // Returns: [1, 1, 2, 3, 5, 8, 13, 21]
|
|
60
|
+
*/
|
|
17
61
|
export async function forIasync<T extends any[] | any>(nbIterations: number, callback: (number) => T): Promise<T[]> {
|
|
18
62
|
const results: any[] = []
|
|
19
63
|
for (let i = 0; i < nbIterations; i++) {
|
|
@@ -43,7 +87,20 @@ export type RecursiveConfig = {
|
|
|
43
87
|
* NOTE: will remove circular references
|
|
44
88
|
* /!\ check return values
|
|
45
89
|
*/
|
|
46
|
-
export async function recursiveGenericFunction(
|
|
90
|
+
export async function recursiveGenericFunction(
|
|
91
|
+
/** The object or array you want to recursively browse */
|
|
92
|
+
item: ObjectGeneric | any[],
|
|
93
|
+
/** The callback you want to apply on items including the main one */
|
|
94
|
+
callback: RecursiveCallback,
|
|
95
|
+
/** Optional configuration object */
|
|
96
|
+
config: RecursiveConfig = {},
|
|
97
|
+
/** Optional base address for the callback function */
|
|
98
|
+
addr$ = '',
|
|
99
|
+
/** Technical field */
|
|
100
|
+
lastElementKey: string | number = '',
|
|
101
|
+
parent?,
|
|
102
|
+
techFieldToAvoidCircularDependency: any[] = []
|
|
103
|
+
) {
|
|
47
104
|
err500IfNotSet({ callback })
|
|
48
105
|
|
|
49
106
|
if (!config.isObjectTestFunction) config.isObjectTestFunction = isObject
|
package/src/math-utils.ts
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
import { isset } from './isset'
|
|
5
5
|
|
|
6
6
|
/** Round with custom number of decimals (default:0) */
|
|
7
|
-
export function round(number: number | string, decimals = 0) {
|
|
7
|
+
export function round(number: number | string, decimals = 0) {
|
|
8
|
+
return Math.round((typeof number === 'number' ? number : parseFloat(number)) * Math.pow(10, decimals)) / Math.pow(10, decimals)
|
|
9
|
+
}
|
|
10
|
+
|
|
8
11
|
/** Round with custom number of decimals (default:2) */
|
|
9
|
-
export function round2
|
|
12
|
+
export function round2<T extends 'number' | 'string' = 'string'>(
|
|
13
|
+
number: number | string,
|
|
14
|
+
decimals = 2,
|
|
15
|
+
format: T = 'string' as T
|
|
16
|
+
) {
|
|
17
|
+
return (format === 'string' ? Number(number).toFixed(decimals) : round(number, decimals)) as T extends 'number' ? number : string
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
/** Is number between two numbers (including those numbers) */
|
|
12
21
|
export function isBetween(number: number, min: number, max: number, inclusive = true) { return inclusive ? number <= max && number >= min : number < max && number > min }
|
package/src/object-utils.ts
CHANGED
|
@@ -79,7 +79,7 @@ export function findByAddressAll<ReturnAddresses extends boolean = false>(
|
|
|
79
79
|
if (addr === '') return (returnAddresses ? [addr, obj, undefined, undefined] : obj) as any
|
|
80
80
|
const addrRegexp = new RegExp('^' + escapeRegexp(
|
|
81
81
|
addr.replace(/\.?\[(\d+)\]/g, '.$1'), // replace .[4] AND [4] TO .4
|
|
82
|
-
{
|
|
82
|
+
{ parseWildcard: true, wildcardNotMatchingChars: '.[' }) + '$'
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
const matchingItems: any[] = []
|
|
@@ -243,13 +243,16 @@ export function reassignForbidden(o) {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
/** All fileds and subFields of the object will become readOnly */
|
|
246
|
-
export function
|
|
246
|
+
export function readOnlyRecursive(object) {
|
|
247
247
|
recursiveGenericFunctionSync(object, (item, _, lastElementKey, parent) => {
|
|
248
248
|
if (typeof item === 'object') parent[lastElementKey] = readOnly(item)
|
|
249
249
|
})
|
|
250
250
|
return object
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/** @deprecated use readOnlyRecursive instead */
|
|
254
|
+
export const readOnlyForAll = readOnlyRecursive
|
|
255
|
+
|
|
253
256
|
export function objFilterUndefinedRecursive(obj) {
|
|
254
257
|
if (obj) {
|
|
255
258
|
const flattenedObj = flattenObject(obj)
|
|
@@ -396,6 +399,7 @@ export function flattenObject(data, config: { withoutArraySyntax?: boolean, with
|
|
|
396
399
|
for (const p in cur) recurse(cur[p], (prop ? prop + '.' : '') + p.replace(/\./g, '%')) // allow prop to contain special chars like points);
|
|
397
400
|
|
|
398
401
|
if (isEmpty && prop) result[prop] = {}
|
|
402
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
399
403
|
} catch (error) {
|
|
400
404
|
// eslint-disable-next-line no-console
|
|
401
405
|
console.warn('Circular reference in flattenObject, impossible to parse')
|
package/src/regexp-utils.ts
CHANGED
|
@@ -3,13 +3,24 @@
|
|
|
3
3
|
//----------------------------------------
|
|
4
4
|
import { C } from './logger-utils'
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* *
|
|
6
|
+
/**
|
|
7
|
+
* Will escape all special character in a string to output a valid regexp string to put in RegExp(myString)
|
|
8
|
+
* * Eg: from `'path.*.addr(*)'` will output `'aa\..*?\.addr\(.*?\)'` so regexp match `'path.random.addr(randomItem)'`
|
|
9
|
+
* * Options:
|
|
10
|
+
* * parseWildcard => config will replace '*' by '.*?' which is the best for 'match all until'
|
|
11
|
+
* * wildcardNotMatchingChars => if provided, will replace '*' by `[^${wildcardNotMatchingChars}]` instead of '.*?'. This allows wildcard not to match certains characters
|
|
9
12
|
*/
|
|
10
|
-
export function escapeRegexp(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
export function escapeRegexp(
|
|
14
|
+
str: string,
|
|
15
|
+
config: {
|
|
16
|
+
/** will replace '*' by '.*?' which is the best for 'match all until' */
|
|
17
|
+
parseWildcard?: boolean,
|
|
18
|
+
/** if provided, will replace '*' by `[^${wildcardNotMatchingChars}]` instead of '.*?'. This allows wildcard not to match certains characters */
|
|
19
|
+
wildcardNotMatchingChars?: string
|
|
20
|
+
} = {}
|
|
21
|
+
): string {
|
|
22
|
+
const { parseWildcard = false, wildcardNotMatchingChars } = config
|
|
23
|
+
if (parseWildcard) return str.replace(/[-[\]{}()+?.,\\^$|#\s]/g, '\\$&').replace(/\*/g, wildcardNotMatchingChars ? `[^${wildcardNotMatchingChars}]` : '.*?')
|
|
13
24
|
else return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
|
14
25
|
}
|
|
15
26
|
|
package/src/string-utils.ts
CHANGED
|
@@ -7,12 +7,19 @@ import { isset } from './isset'
|
|
|
7
7
|
|
|
8
8
|
const getWordBits = (wb: string[] | [string[]]): string[] => Array.isArray(wb[0]) ? wb[0] : wb as any
|
|
9
9
|
|
|
10
|
-
/**Eg: camelCase */
|
|
10
|
+
/**Eg: camelCase('hello', 'world') => 'helloWorld' */
|
|
11
11
|
export function camelCase(...wordBits: string[] | [string[]]): string {
|
|
12
12
|
const wordBitsReal = getWordBits(wordBits)
|
|
13
13
|
return wordBitsReal.filter(e => e).map((w, i) => i === 0 ? w.toLowerCase() : capitalize1st(w, true)).join('')
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/** Replace 'hello-world', 'hello World', 'hello_World', 'helloWorld' => 'helloWorld' */
|
|
17
|
+
export function camelCaseify(word: string) {
|
|
18
|
+
const wordBitsReal = word.replace(/([A-Z])/g, ' $1').split(/[- _]/g)
|
|
19
|
+
return camelCase(wordBitsReal)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
16
23
|
/**Eg: snake_case
|
|
17
24
|
* trimmed but not lowerCased
|
|
18
25
|
*/
|
|
@@ -22,19 +29,23 @@ export function snakeCase(...wordBits: string[] | [string[]]): string {
|
|
|
22
29
|
}
|
|
23
30
|
/**Eg: kebab-case
|
|
24
31
|
* trimmed AND lowerCased
|
|
25
|
-
* undefined, null
|
|
32
|
+
* undefined, null are removed
|
|
26
33
|
*/
|
|
27
34
|
export function kebabCase(...wordBits: string[] | [string[]]): string {
|
|
28
35
|
const wordBitsReal = getWordBits(wordBits)
|
|
29
36
|
return wordBitsReal.filter(e => e).map(w => w.trim().toLowerCase()).join('-')
|
|
30
37
|
}
|
|
31
|
-
/**Eg: PascalCase
|
|
38
|
+
/**Eg: PascalCase
|
|
39
|
+
* undefined, null are removed
|
|
40
|
+
*/
|
|
32
41
|
export function pascalCase(...wordBits: string[] | [string[]]): string {
|
|
33
42
|
const wordBitsReal = getWordBits(wordBits)
|
|
34
43
|
return wordBitsReal.filter(e => e).map(w => capitalize1st(w, true)).join('')
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
/**Eg: Titlecase
|
|
46
|
+
/**Eg: Titlecase
|
|
47
|
+
* undefined, null are removed
|
|
48
|
+
*/
|
|
38
49
|
export function titleCase(...wordBits: string[] | [string[]]): string {
|
|
39
50
|
const wordBitsReal = getWordBits(wordBits)
|
|
40
51
|
return capitalize1st(wordBitsReal.filter(e => e).map(w => w.trim()).join(''), true)
|
|
@@ -94,7 +105,7 @@ export function getValuesBetweenStrings(str: string, openingOrSeparator, closing
|
|
|
94
105
|
// handle unwanted nested structure like characters in a strings that may be a unmatched closing / opening character
|
|
95
106
|
// Eg: {'azer}aze'}
|
|
96
107
|
if (ignoreUntil && char === ignoreUntil && precedingChar !== '\\') ignoreUntil = false
|
|
97
|
-
else if (ignoreUntil && char !== ignoreUntil) true
|
|
108
|
+
else if (ignoreUntil && char !== ignoreUntil) ignoreUntil = true
|
|
98
109
|
else if (ignoreBetweenOpen.includes(char)) {
|
|
99
110
|
const indexChar = ignoreBetweenOpen.findIndex(char2 => char2 === char)
|
|
100
111
|
ignoreUntil = ignoreBetweenClose[indexChar]
|
|
@@ -203,8 +214,11 @@ export function pathJoinSafe(...pathBits: string[]) {
|
|
|
203
214
|
|
|
204
215
|
|
|
205
216
|
export type MiniTemplaterOptions = {
|
|
217
|
+
/** replacer for undefined values */
|
|
206
218
|
valueWhenNotSet: string
|
|
219
|
+
/** override default regexp that match content between `{{ }}`. It must be 'g' and first capturing group matching the value to replace. Default: /{{\s*([^}]*)\s*}}/g*/
|
|
207
220
|
regexp: RegExp
|
|
221
|
+
/** replacer for undefined values */
|
|
208
222
|
valueWhenContentUndefined: string
|
|
209
223
|
}
|
|
210
224
|
/** Replace variables in a string like: `Hello {{userName}}!`
|