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.
Files changed (51) hide show
  1. package/README.md +260 -219
  2. package/coverage/clover.xml +523 -427
  3. package/coverage/coverage-final.json +10 -10
  4. package/coverage/lcov-report/clean-stack-trace.ts.html +13 -10
  5. package/coverage/lcov-report/config.ts.html +2 -2
  6. package/coverage/lcov-report/error-utils.ts.html +2 -2
  7. package/coverage/lcov-report/index.html +56 -56
  8. package/coverage/lcov-report/is-empty.ts.html +1 -1
  9. package/coverage/lcov-report/is-nodejs.ts.html +1 -1
  10. package/coverage/lcov-report/is-object.ts.html +1 -1
  11. package/coverage/lcov-report/isset.ts.html +1 -1
  12. package/coverage/lcov-report/logger-utils.ts.html +215 -215
  13. package/coverage/lcov-report/loop-utils.ts.html +179 -8
  14. package/coverage/lcov-report/math-utils.ts.html +35 -8
  15. package/coverage/lcov-report/object-utils.ts.html +20 -8
  16. package/coverage/lcov-report/regexp-utils.ts.html +45 -12
  17. package/coverage/lcov-report/remove-circular-json-stringify.ts.html +1 -1
  18. package/coverage/lcov-report/string-utils.ts.html +54 -12
  19. package/coverage/lcov-report/timer-utils.ts.html +2 -2
  20. package/coverage/lcov-report/transaction-utils.ts.html +1 -1
  21. package/coverage/lcov.info +626 -607
  22. package/dist/src/clean-stack-trace.js +1 -0
  23. package/dist/src/clean-stack-trace.js.map +1 -1
  24. package/dist/src/error-utils.js +1 -1
  25. package/dist/src/error-utils.js.map +1 -1
  26. package/dist/src/loop-utils.d.ts +54 -2
  27. package/dist/src/loop-utils.js +48 -2
  28. package/dist/src/loop-utils.js.map +1 -1
  29. package/dist/src/math-utils.d.ts +1 -1
  30. package/dist/src/math-utils.js +6 -2
  31. package/dist/src/math-utils.js.map +1 -1
  32. package/dist/src/object-utils.d.ts +3 -1
  33. package/dist/src/object-utils.js +7 -4
  34. package/dist/src/object-utils.js.map +1 -1
  35. package/dist/src/regexp-utils.d.ts +9 -4
  36. package/dist/src/regexp-utils.js +8 -5
  37. package/dist/src/regexp-utils.js.map +1 -1
  38. package/dist/src/string-utils.d.ts +13 -4
  39. package/dist/src/string-utils.js +16 -6
  40. package/dist/src/string-utils.js.map +1 -1
  41. package/dist/src/timer-utils.d.ts +1 -1
  42. package/dist/src/timer-utils.js +1 -1
  43. package/package.json +45 -8
  44. package/src/clean-stack-trace.ts +1 -0
  45. package/src/error-utils.ts +1 -1
  46. package/src/loop-utils.ts +59 -2
  47. package/src/math-utils.ts +11 -2
  48. package/src/object-utils.ts +6 -2
  49. package/src/regexp-utils.ts +17 -6
  50. package/src/string-utils.ts +19 -5
  51. 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.2.117",
3
+ "version": "1.3.4",
4
4
  "type": "commonjs",
5
5
  "types": "index.ts",
6
6
  "main": "dist",
7
- "description": "FAST AND FUNCTIONAL UTILS",
8
- "author": "topkat",
9
- "license": "ISC",
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
- "bump:major": "yarn test:ci && npm run build && node node_modules/bump-simple/bump-simple.js --major",
17
- "bump:minor": "yarn test:ci && npm run build && node node_modules/bump-simple/bump-simple.js --minor",
18
- "bump:patch": "yarn test:ci && npm run build && node node_modules/bump-simple/bump-simple.js --patch",
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",
@@ -25,6 +25,7 @@ export function cleanStackTrace(stack) {
25
25
  'Object.throw',
26
26
  'mongoose/lib/utils',
27
27
  'at Array.forEach (<anonymous>)',
28
+ '/core.error.'
28
29
  ]
29
30
  const linesClean = lines
30
31
  .map((line, i) => {
@@ -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
- export function forI<T extends any[] | any>(nbIterations: number, callback: (number: number, previousValue, arrayOfPreviousValues: any[]) => T): T[] {
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(item: ObjectGeneric | any[], callback: RecursiveCallback, config: RecursiveConfig = {}, addr$ = '', lastElementKey: string | number = '', parent?, techFieldToAvoidCircularDependency: any[] = []) {
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) { return Math.round((typeof number === 'number' ? number : parseFloat(number)) * Math.pow(10, decimals)) / Math.pow(10, decimals) }
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(number: number | string, decimals = 2) { return round(number, decimals) }
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 }
@@ -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
- { parseStarChar: true, wildcardNotMatchingChars: '.[' }) + '$'
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 readOnlyForAll(object) {
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')
@@ -3,13 +3,24 @@
3
3
  //----------------------------------------
4
4
  import { C } from './logger-utils'
5
5
 
6
- /** ESCAPE REGEXP
7
- * * parseStarChar => config will replace '*' by '.*?' which is the best for 'match all until'
8
- * * wildcardNotMatchingChars => list of characters not to match, eg: '.[' will match all except '.' and '[' /!\ will be outputted as a regexp [^.[] so don't forget to ESCAPE characters like ']' => '\]'
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(str: string, config: { parseStarChar?: boolean, wildcardNotMatchingChars?: string } = {}): string {
11
- const { parseStarChar = false, wildcardNotMatchingChars } = config
12
- if (parseStarChar) return str.replace(/[-[\]{}()+?.,\\^$|#\s]/g, '\\$&').replace(/\*/g, wildcardNotMatchingChars ? `[^${wildcardNotMatchingChars}]` : '.*?')
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
 
@@ -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 undefined, null... => '' */
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 undefined, null... => '' */
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}}!`
@@ -70,7 +70,7 @@ export async function retryWithDelay(
70
70
  }
71
71
 
72
72
  /** Use this timer to measure code performances.
73
- * @example ```
73
+ * @example ```typescript
74
74
  const time = perfTimer()
75
75
 
76
76
  for(let i = 0; i < 9999;i++) longProcess()