npm-groovy-lint 14.6.0 → 14.6.1-beta202408252305.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.
Files changed (85) hide show
  1. package/README.md +13 -0
  2. package/lib/codenarc-caller.js +54 -53
  3. package/lib/codenarc-factory.js +41 -50
  4. package/lib/config.js +22 -19
  5. package/lib/filter.js +8 -15
  6. package/lib/groovy-lint-fix.js +32 -34
  7. package/lib/groovy-lint-rules.js +12 -12
  8. package/lib/groovy-lint.js +40 -41
  9. package/lib/index.js +3 -3
  10. package/lib/java/{CodeNarc-3.4.0-alpha+3346775f.jar → CodeNarc-3.5.0.jar} +0 -0
  11. package/lib/java/CodeNarcServer.jar +0 -0
  12. package/lib/options.js +45 -54
  13. package/lib/output.js +23 -23
  14. package/lib/rules/AssignmentInConditional.js +7 -7
  15. package/lib/rules/BlankLineBeforePackage.js +11 -11
  16. package/lib/rules/BlockEndsWithBlankLine.js +6 -6
  17. package/lib/rules/BlockStartsWithBlankLine.js +6 -6
  18. package/lib/rules/BracesForClass.js +8 -8
  19. package/lib/rules/BracesForForLoop.js +9 -9
  20. package/lib/rules/BracesForIfElse.js +9 -9
  21. package/lib/rules/BracesForMethod.js +11 -11
  22. package/lib/rules/BracesForTryCatchFinally.js +7 -7
  23. package/lib/rules/CatchException.js +4 -4
  24. package/lib/rules/ClassEndsWithBlankLine.js +6 -6
  25. package/lib/rules/ClassStartsWithBlankLine.js +6 -6
  26. package/lib/rules/ClosingBraceNotAlone.js +8 -8
  27. package/lib/rules/ConsecutiveBlankLines.js +7 -7
  28. package/lib/rules/DuplicateImport.js +6 -6
  29. package/lib/rules/DuplicateNumberLiteral.js +6 -6
  30. package/lib/rules/DuplicateStringLiteral.js +6 -6
  31. package/lib/rules/ElseBlockBraces.js +10 -10
  32. package/lib/rules/ExplicitArrayListInstantiation.js +7 -7
  33. package/lib/rules/ExplicitLinkedListInstantiation.js +7 -7
  34. package/lib/rules/FileEndsWithoutNewline.js +7 -7
  35. package/lib/rules/GStringExpressionWithinString.js +7 -7
  36. package/lib/rules/IfStatementBraces.js +10 -10
  37. package/lib/rules/Indentation.js +14 -14
  38. package/lib/rules/IndentationClosingBraces.js +8 -8
  39. package/lib/rules/IndentationComments.js +6 -6
  40. package/lib/rules/InsecureRandom.js +14 -14
  41. package/lib/rules/JavaIoPackageAccess.js +6 -6
  42. package/lib/rules/MethodCount.js +6 -6
  43. package/lib/rules/MethodParameterTypeRequired.js +6 -6
  44. package/lib/rules/MethodReturnTypeRequired.js +6 -6
  45. package/lib/rules/MisorderedStaticImports.js +14 -14
  46. package/lib/rules/MissingBlankLineAfterImports.js +6 -6
  47. package/lib/rules/MissingBlankLineAfterPackage.js +6 -6
  48. package/lib/rules/NoDef.js +4 -4
  49. package/lib/rules/NoJavaUtilDate.js +4 -4
  50. package/lib/rules/NoTabCharacter.js +7 -7
  51. package/lib/rules/SimpleDateFormatMissingLocale.js +4 -4
  52. package/lib/rules/SpaceAfterCatch.js +7 -7
  53. package/lib/rules/SpaceAfterComma.js +9 -9
  54. package/lib/rules/SpaceAfterFor.js +8 -8
  55. package/lib/rules/SpaceAfterIf.js +10 -10
  56. package/lib/rules/SpaceAfterMethodCallName.js +7 -7
  57. package/lib/rules/SpaceAfterOpeningBrace.js +9 -9
  58. package/lib/rules/SpaceAfterSemicolon.js +8 -8
  59. package/lib/rules/SpaceAfterSwitch.js +8 -8
  60. package/lib/rules/SpaceAfterWhile.js +7 -7
  61. package/lib/rules/SpaceAroundOperator.js +14 -14
  62. package/lib/rules/SpaceBeforeClosingBrace.js +8 -8
  63. package/lib/rules/SpaceBeforeOpeningBrace.js +9 -9
  64. package/lib/rules/SpaceInsideParentheses.js +17 -17
  65. package/lib/rules/SystemExit.js +4 -4
  66. package/lib/rules/TrailingWhitespace.js +8 -8
  67. package/lib/rules/UnnecessaryDefInFieldDeclaration.js +8 -8
  68. package/lib/rules/UnnecessaryDefInMethodDeclaration.js +7 -7
  69. package/lib/rules/UnnecessaryDefInVariableDeclaration.js +9 -9
  70. package/lib/rules/UnnecessaryDotClass.js +7 -7
  71. package/lib/rules/UnnecessaryFinalOnPrivateMethod.js +7 -7
  72. package/lib/rules/UnnecessaryGString.js +14 -14
  73. package/lib/rules/UnnecessaryGroovyImport.js +6 -6
  74. package/lib/rules/UnnecessaryPackageReference.js +9 -9
  75. package/lib/rules/UnnecessaryParenthesesForMethodCallWithClosure.js +9 -9
  76. package/lib/rules/UnnecessaryPublicModifier.js +4 -4
  77. package/lib/rules/UnnecessarySemicolon.js +10 -13
  78. package/lib/rules/UnnecessaryToString.js +8 -8
  79. package/lib/rules/UnusedImport.js +10 -10
  80. package/lib/rules/UnusedMethodParameter.js +6 -6
  81. package/lib/rules/UnusedVariable.js +6 -6
  82. package/lib/rules/VariableName.js +11 -11
  83. package/lib/rules/VariableTypeRequired.js +11 -11
  84. package/lib/utils.js +62 -43
  85. package/package.json +22 -19
@@ -1,18 +1,18 @@
1
1
  // Variable name invalid
2
- const { getVariableRange } = require("../utils");
2
+ import { getVariableRange } from "../utils.js";
3
3
 
4
4
  const rule = {
5
5
  variables: [
6
6
  {
7
7
  name: "VARIABLE_NAME",
8
- regex: /Variable named (.*) in (.*) does not match the pattern (.*)/
9
- }
8
+ regex: /Variable named (.*) in (.*) does not match the pattern (.*)/,
9
+ },
10
10
  ],
11
11
  range: {
12
12
  type: "function",
13
13
  func: (errLine, errItem, evaluatedVars) => {
14
14
  return getVariableRange(errLine, evaluatedVars, "VARIABLE_NAME", errItem);
15
- }
15
+ },
16
16
  },
17
17
  rangeTests: [
18
18
  {
@@ -22,15 +22,15 @@ def RANDOM_ID = 0
22
22
  expectedRange: {
23
23
  start: {
24
24
  line: 2,
25
- character: 4
25
+ character: 4,
26
26
  },
27
27
  end: {
28
28
  line: 2,
29
- character: 13
30
- }
31
- }
32
- }
33
- ]
29
+ character: 13,
30
+ },
31
+ },
32
+ },
33
+ ],
34
34
  };
35
35
 
36
- module.exports = { rule };
36
+ export { rule };
@@ -1,18 +1,18 @@
1
1
  // Variable type required
2
- const { getVariableRange } = require("../utils");
2
+ import { getVariableRange } from "../utils.js";
3
3
 
4
4
  const rule = {
5
5
  variables: [
6
6
  {
7
7
  name: "VARIABLE_NAME",
8
- regex: /The type is not specified for variable "(.*)"/
9
- }
8
+ regex: /The type is not specified for variable "(.*)"/,
9
+ },
10
10
  ],
11
11
  range: {
12
12
  type: "function",
13
13
  func: (errLine, errItem, evaluatedVars) => {
14
14
  return getVariableRange(errLine, evaluatedVars, "VARIABLE_NAME", errItem);
15
- }
15
+ },
16
16
  },
17
17
  rangeTests: [
18
18
  {
@@ -22,15 +22,15 @@ def returnCode = 0
22
22
  expectedRange: {
23
23
  start: {
24
24
  line: 2,
25
- character: 4
25
+ character: 4,
26
26
  },
27
27
  end: {
28
28
  line: 2,
29
- character: 14
30
- }
31
- }
32
- }
33
- ]
29
+ character: 14,
30
+ },
31
+ },
32
+ },
33
+ ],
34
34
  };
35
35
 
36
- module.exports = { rule };
36
+ export { rule };
package/lib/utils.js CHANGED
@@ -1,22 +1,25 @@
1
1
  // Shared functions
2
- "use strict";
3
2
 
4
- const debug = require("debug")("npm-groovy-lint");
5
- const trace = require("debug")("npm-groovy-lint-trace");
6
- const decodeHtml = require("decode-html");
7
- const fse = require("fs-extra");
8
- const os = require("os");
3
+ import Debug from "debug";
4
+ const debug = Debug("npm-groovy-lint");
5
+ const trace = Debug("npm-groovy-lint-trace");
6
+ import fs from "fs-extra";
7
+ import * as os from "os";
8
+ import * as path from "path";
9
+ import { fileURLToPath } from "url";
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ import find from "find-package-json";
9
12
 
10
13
  const validErrorCombinations = {
11
14
  error: ["error", "warning", "info"],
12
15
  warning: ["warning", "info"],
13
16
  info: ["info"],
14
- none: ["error", "warning", "info"]
17
+ none: ["error", "warning", "info"],
15
18
  };
16
19
 
17
20
  function addImport(allLineLs, classToImport) {
18
21
  // Check if import is already there
19
- if (allLineLs.map(line => line.trim()).findIndex(line => line.startsWith(`import ${classToImport}`)) > -1) {
22
+ if (allLineLs.map((line) => line.trim()).findIndex((line) => line.startsWith(`import ${classToImport}`)) > -1) {
20
23
  return allLineLs;
21
24
  }
22
25
  // Add import after existing imports
@@ -26,13 +29,13 @@ function addImport(allLineLs, classToImport) {
26
29
  allLineLs
27
30
  .slice()
28
31
  .reverse()
29
- .findIndex(line => line.trim().startsWith("import"));
32
+ .findIndex((line) => line.trim().startsWith("import"));
30
33
  if (lastImportIndex > -1 && lastImportIndex !== allLineLs.length) {
31
34
  allLineLs.splice(lastImportIndex + 1, 0, `import ${classToImport}`);
32
35
  return allLineLs;
33
36
  }
34
37
  // Add import after package declaration
35
- const packageIndex = allLineLs.findIndex(line => line.trim().startsWith("package"));
38
+ const packageIndex = allLineLs.findIndex((line) => line.trim().startsWith("package"));
36
39
  if (packageIndex > -1) {
37
40
  allLineLs.splice(packageIndex + 1, 0, "");
38
41
  allLineLs.splice(packageIndex + 2, 0, `import ${classToImport}`);
@@ -63,7 +66,7 @@ function addSpaceAfterChar(line, char) {
63
66
  let pos = -1;
64
67
  const lineIndent = line.search(/\S/);
65
68
  const splits = line.split(char);
66
- const newArray = splits.map(str => {
69
+ const newArray = splits.map((str) => {
67
70
  pos++;
68
71
  if (pos === splits.length - 1) {
69
72
  return str.trimStart();
@@ -82,7 +85,7 @@ function addSpaceAroundChar(line, char, postReplaces = []) {
82
85
  //const regSplit = new RegExp(escapedChar + `+(?=(?:(?:[^']*'){2})*[^']*$)`);
83
86
  const regSplit = new RegExp(escapedChar + `+(?=(?:(?:[^"]*"){2})*[^"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=(?:(?:[^\`]*\`){2})*[^\`]*$)`);
84
87
  const splits = line.split(regSplit);
85
- const newArray = splits.map(str => {
88
+ const newArray = splits.map((str) => {
86
89
  pos++;
87
90
  if (pos === 0) {
88
91
  return str.trimEnd();
@@ -105,7 +108,7 @@ function addSpaceAroundChar(line, char, postReplaces = []) {
105
108
  // Checks that a string contains other things than a list of strings
106
109
  function containsOtherThan(str, stringArray) {
107
110
  const splits = splitMulti(str, stringArray);
108
- return splits.filter(item => item !== "").length > 0;
111
+ return splits.filter((item) => item !== "").length > 0;
109
112
  }
110
113
 
111
114
  // Get position to highlight in sources
@@ -154,17 +157,17 @@ function evaluateVariables(variableDefs, msg) {
154
157
  const regexRes = msg.match(varDef.regex);
155
158
  if (regexRes && regexRes.length > 1) {
156
159
  const regexPos = varDef.regexPos || 1;
157
- let value = decodeHtml(regexRes[regexPos]);
160
+ let value = decodeHTMLEntities(regexRes[regexPos]);
158
161
  value = escapeValue(value);
159
162
  const varValue =
160
163
  varDef.type && varDef.type === "number"
161
164
  ? parseInt(value, 10)
162
165
  : varDef.type && varDef.type === "array"
163
- ? JSON.parse(value)
164
- : value;
166
+ ? JSON.parse(value)
167
+ : value;
165
168
  evaluatedVars.push({
166
169
  name: varDef.name,
167
- value: varValue
170
+ value: varValue,
168
171
  });
169
172
  } else {
170
173
  trace(`GroovyLint: Unable to match ${varDef.regex} in ${msg}`);
@@ -203,7 +206,7 @@ function findRangeBetweenStrings(allLines, errItem, strStart, strEnd) {
203
206
  function getDefaultRange(allLines, errItem) {
204
207
  return {
205
208
  start: { line: errItem.line, character: 0 },
206
- end: { line: errItem.line, character: allLines[errItem.line - 1].length }
209
+ end: { line: errItem.line, character: allLines[errItem.line - 1].length },
207
210
  };
208
211
  }
209
212
 
@@ -217,16 +220,14 @@ function getLastStringRange(errLine, str, errItem) {
217
220
  const varStartPos = errLine.lastIndexOf(str);
218
221
  return {
219
222
  start: { line: errItem.line, character: varStartPos },
220
- end: { line: errItem.line, character: varStartPos + str.length }
223
+ end: { line: errItem.line, character: varStartPos + str.length },
221
224
  };
222
225
  }
223
226
 
224
- /*
225
227
  function getLastVariableRange(errLine, evaluatedVars, variable, errItem) {
226
228
  const varValue = getVariable(evaluatedVars, variable);
227
229
  return getLastStringRange(errLine, varValue, errItem);
228
230
  }
229
- */
230
231
 
231
232
  // Returns all strings which are not inside braces
232
233
  function getOutOfBracesStrings(str, exclude = []) {
@@ -250,14 +251,14 @@ function getOutOfBracesStrings(str, exclude = []) {
250
251
  }
251
252
  pos++;
252
253
  }
253
- return outOfBracesStrings.filter(item => !exclude.includes(item) && item !== "");
254
+ return outOfBracesStrings.filter((item) => !exclude.includes(item) && item !== "");
254
255
  }
255
256
 
256
257
  // Split source lines to analyse
257
258
  async function getSourceLines(source, fileNm) {
258
259
  let fileContent =
259
260
  source ||
260
- (await fse.readFile(fileNm).catch(err => {
261
+ (await fs.readFile(fileNm).catch((err) => {
261
262
  throw new Error(`Unable to read source lines: ${err}`); // Ensure that we have a stack trace.
262
263
  }));
263
264
  return normalizeNewLines(fileContent.toString()).split(os.EOL);
@@ -271,12 +272,12 @@ function getStringRange(errLine, strOrRegex, errItem) {
271
272
  return {
272
273
  start: {
273
274
  line: errItem.line,
274
- character: strOrRegex.lastIndex - match[0].length
275
+ character: strOrRegex.lastIndex - match[0].length,
275
276
  },
276
277
  end: {
277
278
  line: errItem.line,
278
- character: strOrRegex.lastIndex - 1
279
- }
279
+ character: strOrRegex.lastIndex - 1,
280
+ },
280
281
  };
281
282
  }
282
283
  // String matcher
@@ -284,12 +285,12 @@ function getStringRange(errLine, strOrRegex, errItem) {
284
285
  return {
285
286
  start: {
286
287
  line: errItem.line,
287
- character: varStartPos
288
+ character: varStartPos,
288
289
  },
289
290
  end: {
290
291
  line: errItem.line,
291
- character: varStartPos + strOrRegex.length
292
- }
292
+ character: varStartPos + strOrRegex.length,
293
+ },
293
294
  };
294
295
  }
295
296
 
@@ -308,7 +309,7 @@ function getStringRangeMultiline(allLines, str, errItem, levelKey) {
308
309
  const varStartPos = allLines[pos].indexOf(str);
309
310
  range = {
310
311
  start: { line: pos + 1, character: varStartPos },
311
- end: { line: pos + 1, character: varStartPos + str.length }
312
+ end: { line: pos + 1, character: varStartPos + str.length },
312
313
  };
313
314
  isFound = true;
314
315
  } else {
@@ -322,9 +323,9 @@ function getStringRangeMultiline(allLines, str, errItem, levelKey) {
322
323
 
323
324
  // Get variable value from evaluated vars
324
325
  function getVariable(evaluatedVars, name, optns = { mandatory: true, decodeHtml: false, line: "" }) {
325
- const matchingVars = evaluatedVars.filter(evaluatedVar => evaluatedVar.name === name);
326
+ const matchingVars = evaluatedVars.filter((evaluatedVar) => evaluatedVar.name === name);
326
327
  if (matchingVars && matchingVars.length > 0) {
327
- return optns.decodeHtml ? decodeHtml(matchingVars[0].value) : matchingVars[0].value;
328
+ return optns.decodeHtml ? decodeHTMLEntities(matchingVars[0].value) : matchingVars[0].value;
328
329
  } else if (optns.mandatory) {
329
330
  throw new Error("GroovyLint fix: missing mandatory variable " + name + " in " + JSON.stringify(evaluatedVars)) + "for line :\n" + optns.line;
330
331
  } else {
@@ -354,12 +355,7 @@ function moveOpeningBracket(allLines, variables) {
354
355
  if (realPos === -1) {
355
356
  throw new Error("Unable to find opening bracket");
356
357
  }
357
- if (
358
- allLines[realPos - 1]
359
- .trim()
360
- .replace(/ /g, "")
361
- .includes("){")
362
- ) {
358
+ if (allLines[realPos - 1].trim().replace(/ /g, "").includes("){")) {
363
359
  throw new Error("Fix not applied: probably a CodeNarc false positive");
364
360
  }
365
361
  // Add bracket after if
@@ -388,7 +384,7 @@ function normalizeNewLines(str) {
388
384
  function notBetweenQuotesOrComment(str, substr) {
389
385
  const singleQuotesMatch = str.match(/'(.*?)'/) || [];
390
386
  const doubleQuotesMatch = str.match(/"(.*?)"/) || [];
391
- const res = singleQuotesMatch.concat(doubleQuotesMatch).filter(match => match.includes(substr));
387
+ const res = singleQuotesMatch.concat(doubleQuotesMatch).filter((match) => match.includes(substr));
392
388
  if (res.length > 0) {
393
389
  return false;
394
390
  }
@@ -425,8 +421,7 @@ function getNpmGroovyLintVersion() {
425
421
  let v = process.env.npm_package_version;
426
422
  if (!v) {
427
423
  try {
428
- const FindPackageJson = require("find-package-json");
429
- const finder = FindPackageJson(__dirname);
424
+ const finder = find(__dirname);
430
425
  v = finder.next().value.version;
431
426
  } catch {
432
427
  v = "error";
@@ -435,18 +430,42 @@ function getNpmGroovyLintVersion() {
435
430
  return v;
436
431
  }
437
432
 
438
- module.exports = {
433
+ const entities = {
434
+ amp: "&",
435
+ apos: "'",
436
+ lt: "<",
437
+ gt: ">",
438
+ quot: '"',
439
+ nbsp: "\xa0",
440
+ };
441
+ const entityPattern = /&([a-z]+);/gi;
442
+
443
+ function decodeHTMLEntities(text) {
444
+ // A single replace pass with a static RegExp is faster than a loop
445
+ return text.replace(entityPattern, function (match, entity) {
446
+ entity = entity.toLowerCase();
447
+ if (Object.prototype.hasOwnProperty.call(entities, entity)) {
448
+ return entities[entity];
449
+ }
450
+ // return original string if there is no matching entity (no replace)
451
+ return match;
452
+ });
453
+ }
454
+
455
+ export {
439
456
  addImport,
440
457
  addSpaceAfterChar,
441
458
  addSpaceAroundChar,
442
459
  containsOtherThan,
443
460
  containsExceptInsideString,
461
+ decodeHTMLEntities,
444
462
  evaluateRange,
445
463
  evaluateRangeFromLine,
446
464
  evaluateVariables,
447
465
  findRangeBetweenStrings,
448
466
  getIndentLength,
449
467
  getLastStringRange,
468
+ getLastVariableRange,
450
469
  getNpmGroovyLintVersion,
451
470
  getOutOfBracesStrings,
452
471
  getSourceLines,
@@ -457,5 +476,5 @@ module.exports = {
457
476
  isErrorInLogLevelScope,
458
477
  isValidCodeLine,
459
478
  moveOpeningBracket,
460
- normalizeNewLines
479
+ normalizeNewLines,
461
480
  };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "npm-groovy-lint",
3
- "version": "14.6.0",
3
+ "version": "14.6.1-beta202408252305.0",
4
4
  "description": "Lint, format and auto-fix your Groovy / Jenkinsfile / Gradle files",
5
- "main": "index.js",
5
+ "exports": "./index.js",
6
+ "type": "module",
6
7
  "scripts": {
7
8
  "lint:fix": "eslint **/*.js --fix && prettier --write \"./lib/**/*.{js,jsx}\" --tab-width 4 --print-width 150",
8
9
  "server:run-from-source": "npm run server:kill && groovy -cp \"lib/java/*\" groovy/src/main/com/nvuillam/CodeNarcServer.groovy --server",
@@ -48,39 +49,41 @@
48
49
  "dependencies": {
49
50
  "ansi-colors": "^4.1.1",
50
51
  "axios": "^1.6.2",
51
- "chalk": "^4.1.2",
52
+ "chalk": "^5.3.0",
52
53
  "cli-progress": "^3.12.0",
53
54
  "commondir": "^1.0.1",
54
55
  "debug": "^4.1.1",
55
- "decode-html": "^2.0.0",
56
- "find-java-home": "^1.1.0",
56
+ "find-java-home": "^2.0.0",
57
57
  "find-package-json": "^1.2.0",
58
- "fs-extra": "^8.1.0",
59
- "glob": "^7.1.6",
58
+ "fs-extra": "^11.0.0",
59
+ "glob": "^11.0.0",
60
60
  "import-fresh": "^3.2.1",
61
61
  "java-caller": "^4.0.0",
62
62
  "js-yaml": "^4.1.0",
63
- "node-sarif-builder": "^2.0.3",
64
- "optionator": "^0.8.3",
65
- "strip-json-comments": "^3.0.1",
66
- "uuid": "^8.2.0"
63
+ "node-sarif-builder": "^3.0.0",
64
+ "optionator": "^0.9.0",
65
+ "strip-json-comments": "^5.0.0",
66
+ "uuid": "^10.0.0"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@babel/core": "^7.23.2",
70
70
  "@babel/eslint-parser": "^7.22.15",
71
+ "@eslint/eslintrc": "^3.1.0",
72
+ "@eslint/js": "^9.9.1",
71
73
  "adm-zip": "^0.5.10",
72
- "diff": "^4.0.2",
73
- "eslint": "^8.52.0",
74
+ "diff": "^5.0.0",
75
+ "eslint": "^9.0.0",
76
+ "globals": "^15.9.0",
74
77
  "handlebars": "^4.7.8",
75
78
  "mocha": "^10.2.0",
76
- "npm-run-all": "^4.1.5",
77
- "nyc": "^15.1.0",
78
- "prettier": "^1.19.1",
79
- "rimraf": "^3.0.2",
80
- "which": "^2.0.2"
79
+ "npm-run-all2": "^6.0.0",
80
+ "nyc": "^17.0.0",
81
+ "prettier": "^3.0.0",
82
+ "rimraf": "^6.0.0",
83
+ "which": "^4.0.0"
81
84
  },
82
85
  "engines": {
83
- "node": ">=18.0.0"
86
+ "node": ">=20.0.0"
84
87
  },
85
88
  "mocha": {
86
89
  "require": [