datagrok-tools 4.13.34 → 4.13.36

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/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
3
 
4
+ ## 4.13.36 (2024-11-15)
5
+
6
+ ### Features
7
+
8
+ * Grok test reopens on each failed test
9
+
10
+ ## 4.13.35 (2024-11-4)
11
+
12
+ ### Features
13
+
14
+ * Grok help fixes
15
+
4
16
  ## 4.13.34 (2024-11-1)
5
17
 
6
18
  ### Features
@@ -4,14 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.help = void 0;
7
- var HELP = "\nUsage: grok <command>\n\nDatagrok's package management tool\n\nCommands:\n add Add an object template\n api Create wrapper functions\n check Check package content (function signatures, etc.)\n config Create and manage config files\n create Create a package\n init Modify a package template\n link Link `datagrok-api` and libraries for local development\n publish Upload a package\n publishAll Upload all packages\n test Run package tests\n testAll Run packages tests\n\nTo get help on a particular command, use:\n grok <command> --help\n\nRead more about the package development workflow:\nhttps://datagrok.ai/help/develop/develop\n";
7
+ var HELP = "\nUsage: grok <command>\n\nDatagrok's package management tool\n\nCommands:\n add Add an object template\n api Create wrapper functions\n check Check package content (function signatures, etc.)\n config Create and manage config files\n create Create a package\n init Modify a package template\n link Link `datagrok-api` and libraries for local development\n publish Upload a package\n test Run package tests\n testAll Run packages tests\n\nTo get help on a particular command, use:\n grok <command> --help\n\nRead more about the package development workflow:\nhttps://datagrok.ai/help/develop/develop\n";
8
8
  var HELP_ADD = "\nUsage: grok add <entity> <name>\n\nAdd an object template to your package:\n\ngrok add app <name>\ngrok add connection <name>\ngrok add detector <semantic-type-name>\ngrok add function [tag] <name>\ngrok add query <name>\ngrok add script [tag] <language> <name>\ngrok add view <name>\ngrok add viewer <name>\ngrok add tests\n\nPlease note that entity names may only include letters and numbers\n\nSupported languages for scripts:\njavascript, julia, node, octave, python, r\n\nAvailable tags:\npanel, init\n";
9
9
  var HELP_INIT = "\nUsage: grok init\n\nModify a package template by adding config files for linters, IDE, etc.\n\nOptions:\n[--eslint] [--ide] [--test] [--ts] [--git]\n\n--eslint Add a configuration for eslint\n--ide Add an IDE-specific configuration for debugging (vscode)\n--test Add tests support (TypeScript packages only)\n--ts Convert a JavaScript package to TypeScript\n--git Configure GIT and install commit linting tools.\n Read more: https://datagrok.ai/help/develop/advanced/git-policy\n";
10
10
  var HELP_API = "\nUsage: grok api\n\nCreate wrapper functions for package scripts and queries\n";
11
11
  var HELP_CONFIG = "\nUsage: grok config\n\nCreate or update a configuration file\n\nOptions:\n[--reset] [--server] [--alias] [-k | --key]\n\n--reset Restore the default config file template\n--server Use to add a server to the config (`grok config add --alias alias --server url --key key`)\n--alias Use in conjunction with the `server` option to set the server name\n--key Use in conjunction with the `server` option to set the developer key\n--default Use in conjunction with the `server` option to set the added server as default\n";
12
12
  var HELP_CREATE = "\nUsage: grok create [name]\n\nCreate a package:\n\ngrok create Create a package in the current working directory\ngrok create <name> Create a package in a folder with the specified name\n\nPlease note that the package name may only include letters, numbers, underscores, or hyphens\n\nOptions:\n[--eslint] [--ide] [--js | --ts] [--test]\n\n--eslint Add a configuration for eslint\n--ide Add an IDE-specific configuration for debugging (vscode)\n--js Create a JavaScript package\n--ts Create a TypeScript package (default)\n--test Add tests support (TypeScript packages only)\n";
13
- var HELP_PUBLISH = "\nUsage: grok publish [host]\n\nUpload a package\n\nOptions:\n[--build|--rebuild] [--debug|--release] [-k | --key] [--suffix]\n\nRunning `grok publish` is the same as running `grok publish defaultHost --build --debug`\n";
14
- var HELP_CHECK = "\nUsage: grok check\n\nOptions:\n[-r | --recursive]\n\n--recursive Check all packages in the current directory\n--all Publish all available packages \n--refresh Publish all available already loaded packages \n\nCheck package content (function signatures, import statements of external modules, etc.)\n";
13
+ var HELP_PUBLISH = "\nUsage: grok publish [host]\n\nUpload a package\n\nOptions:\n[--build|--rebuild] [--debug|--release] [-k | --key] [--suffix] [--all] [--refresh]\n\n--all Publish all available packages \n--refresh Publish all available already loaded packages \n\nRunning `grok publish` is the same as running `grok publish defaultHost --build --debug`\n";
14
+ var HELP_CHECK = "\nUsage: grok check\n\nOptions:\n[-r | --recursive]\n\n--recursive Check all packages in the current directory\n\nCheck package content (function signatures, import statements of external modules, etc.)\n";
15
15
  var HELP_TEST = "\nUsage: grok test\n\nOptions:\n[--package] [--category] [--test] [--host] [--csv] [--gui] [--skip-build] [--skip-publish] [--link] [--catchUnhandled] [--report] [--record] [--verbose] [--platform] [--benchmark] [--stress-test]\n\n--package Specify a package name to run tests for\n--category Specify a category name to run tests for\n--test Specify a test name to run \n--host Host alias as in the config file\n--csv Save the test report in a CSV file\n--gui Launch graphical interface (non-headless mode)\n--catchUnhandled Catch unhandled exceptions during test execution (default=true)\n--report Report failed tests to audit, notifies package author (default=false)\n--skip-build Skip the package build step\n--skip-publish Skip the package publication step\n--link \t Link the package to local utils\n--record Records the test execution process in mp4 format\n--verbose Prints detailed information about passed and skipped tests in the console\n--platform Runs only platform tests (applicable for ApiTests package only)\n--core Runs package & core tests (applicable for DevTools package only)\n--benchmark \t Runs tests in benchmark mode\n--stress-test Runs shuffled stress-test only\n \nRun package tests\n\nSee instructions:\nhttps://datagrok.ai/help/develop/how-to/test-packages#local-testing\n";
16
16
  var HELP_TESTALL = "\nUsage: grok testall\n\nOptions:\n[--packages] [--host] [--csv] [--gui] [--skip-build] [--skip-publish] [--link-package] [--catchUnhandled] [--report] [--record] [--verbose] [--benchmark] [--stress-test] [--order] [--tags] [--testRepeat] [--workersCount]\n\n--packages Specify a packages names to run tests for\n--host Host alias as in the config file\n--csv Save the test report in a CSV file\n--gui Launch graphical interface (non-headless mode)\n--catchUnhandled Catch unhandled exceptions during test execution (default=true)\n--report Report failed tests to audit, notifies packages author (default=false)\n--skip-build Skip the packages build step\n--skip-publish Skip the packages publication step\n--link-package \t Link the packages to local utils\n--record Records the test execution process in mp4 format\n--verbose Prints detailed information about passed and skipped tests in the console\n--core Runs packages & core tests (applicable for DevTools packages only)\n--benchmark \t Runs tests in benchmark mode\n--stress-test Runs shuffled stress-test only\n--order Specify order for tests invocation\n--tags Filter tests by tag name for run\n--testRepeat Set amount of tests repeats\n--workersCount Set amount of workers for tests run\n\nRun tests of all or specified packages \n\nSee instructions:\nhttps://datagrok.ai/help/develop/how-to/test-packages#local-testing\n";
17
17
  var HELP_LINK = "\nUsage: grok link\n\nLink `datagrok-api` and libraries for local development\n";
@@ -14,6 +14,7 @@ var _path = _interopRequireDefault(require("path"));
14
14
  var _jsYaml = _interopRequireDefault(require("js-yaml"));
15
15
  var utils = _interopRequireWildcard(require("../utils/utils"));
16
16
  var color = _interopRequireWildcard(require("../utils/color-utils"));
17
+ var Papa = _interopRequireWildcard(require("papaparse"));
17
18
  var _testUtils = _interopRequireWildcard(require("../utils/test-utils"));
18
19
  var testUtils = _testUtils;
19
20
  var _orderFunctions = require("../utils/order-functions");
@@ -34,7 +35,7 @@ function test(_x) {
34
35
  }
35
36
  function _test() {
36
37
  _test = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(args) {
37
- var config, packageJsonData, packageName, packagesDir, categoryToCheck, testToCheck, res;
38
+ var config, packageJsonData, packageName, packagesDir, res;
38
39
  return _regenerator["default"].wrap(function _callee$(_context) {
39
40
  while (1) switch (_context.prev = _context.next) {
40
41
  case 0:
@@ -49,32 +50,26 @@ function _test() {
49
50
  }));
50
51
  packageName = args["package"] ? utils.kebabToCamelCase(args["package"]) : utils.kebabToCamelCase(utils.removeScope(packageJsonData.name));
51
52
  packagesDir = _path["default"].basename(curDir) === "packages" ? curDir : _path["default"].dirname(curDir);
52
- categoryToCheck = undefined;
53
- testToCheck = undefined;
54
- if (args.category) {
55
- categoryToCheck = args.category.toString();
56
- if (args.test) testToCheck = args.test.toString();
57
- }
58
53
  console.log('Environment variable `TARGET_PACKAGE` is set to', packageName);
59
54
  if (args.platform && packageName !== 'ApiTests') color.warn('--platform flag can only be used in the ApiTests package');
60
55
  if (args.core && packageName !== 'DevTools') color.warn('--core flag can only be used in the DevTools package');
61
56
  if (args["package"]) {
62
- _context.next = 16;
57
+ _context.next = 13;
63
58
  break;
64
59
  }
65
- _context.next = 16;
60
+ _context.next = 13;
66
61
  return testUtils.loadPackages(packagesDir, packageName, args.host, args['skip-publish'], args['skip-build'], args.link);
67
- case 16:
62
+ case 13:
68
63
  process.env.TARGET_PACKAGE = packageName;
69
- _context.next = 19;
70
- return runTesting(args, categoryToCheck, testToCheck);
71
- case 19:
64
+ _context.next = 16;
65
+ return runTesting(args);
66
+ case 16:
72
67
  res = _context.sent;
73
68
  if (args.csv) (0, _testUtils.saveCsvResults)([res.csv], csvReportDir);
74
69
  (0, _testUtils.printWorkersResult)(res, args.verbose);
75
70
  if (!res) testUtils.exitWithCode(1);else testUtils.exitWithCode(0);
76
71
  return _context.abrupt("return", true);
77
- case 24:
72
+ case 21:
78
73
  case "end":
79
74
  return _context.stop();
80
75
  }
@@ -89,13 +84,13 @@ function isArgsValid(args) {
89
84
  })) return false;
90
85
  return true;
91
86
  }
92
- function runTesting(_x2, _x3, _x4) {
87
+ function runTesting(_x2) {
93
88
  return _runTesting.apply(this, arguments);
94
89
  }
95
90
  function _runTesting() {
96
- _runTesting = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(args, categoryToCheck, testToCheck) {
91
+ _runTesting = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(args) {
97
92
  var _process$env$TARGET_P;
98
- var testsObj, parsed, organized, filtered, _iterator, _step, element, testsResults, r, _args$benchmark, _args$catchUnhandled, _args$gui, _args$record, _args$report, _args$verbose, testsLeft, _iterator2, _step2, testData;
93
+ var testsObj, parsed, organized, filtered, _iterator, _step, element, testsResults, r, _args$benchmark, _args$catchUnhandled, _args$gui, _args$record, _args$report, _args$verbose, testsLeft, testsToReproduce, _iterator2, _step2, testData, reproduced, _iterator3, _step3, _test2, _reproducedTest2;
99
94
  return _regenerator["default"].wrap(function _callee2$(_context2) {
100
95
  while (1) switch (_context2.prev = _context2.next) {
101
96
  case 0:
@@ -150,35 +145,189 @@ function _runTesting() {
150
145
  }, 1, testInvocationTimeout);
151
146
  case 12:
152
147
  r = _context2.sent;
153
- testsResults.push(r);
154
148
  testsLeft = [];
149
+ testsToReproduce = [];
155
150
  _iterator2 = _createForOfIteratorHelper(organized);
156
151
  try {
157
152
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
158
153
  testData = _step2.value;
159
154
  if (!r.csv.includes("".concat(testData.params.category, ",").concat(testData.params.test))) testsLeft.push(testData);
155
+ if (r.verboseFailed.includes("".concat(testData.params.category, ": ").concat(testData.params.test, " : Error:"))) {
156
+ testsToReproduce.push(testData);
157
+ }
160
158
  }
161
159
  } catch (err) {
162
160
  _iterator2.e(err);
163
161
  } finally {
164
162
  _iterator2.f();
165
163
  }
164
+ if (!(testsToReproduce.length > 0)) {
165
+ _context2.next = 42;
166
+ break;
167
+ }
168
+ _context2.next = 20;
169
+ return reproducedTest(args, testsToReproduce);
170
+ case 20:
171
+ reproduced = _context2.sent;
172
+ _iterator3 = _createForOfIteratorHelper(testsToReproduce);
173
+ _context2.prev = 22;
174
+ _iterator3.s();
175
+ case 24:
176
+ if ((_step3 = _iterator3.n()).done) {
177
+ _context2.next = 34;
178
+ break;
179
+ }
180
+ _test2 = _step3.value;
181
+ _reproducedTest2 = reproduced.get(_test2);
182
+ console.log(_test2);
183
+ if (!(_reproducedTest2 && !_reproducedTest2.failed)) {
184
+ _context2.next = 32;
185
+ break;
186
+ }
187
+ _context2.next = 31;
188
+ return updateResultsByReproduced(r, _reproducedTest2, _test2);
189
+ case 31:
190
+ r = _context2.sent;
191
+ case 32:
192
+ _context2.next = 24;
193
+ break;
194
+ case 34:
195
+ _context2.next = 39;
196
+ break;
197
+ case 36:
198
+ _context2.prev = 36;
199
+ _context2.t0 = _context2["catch"](22);
200
+ _iterator3.e(_context2.t0);
201
+ case 39:
202
+ _context2.prev = 39;
203
+ _iterator3.f();
204
+ return _context2.finish(39);
205
+ case 42:
206
+ testsResults.push(r);
166
207
  organized = testsLeft;
167
- case 18:
168
- if (r.verboseFailed.includes('EXECUTION TIMEOUT')) {
208
+ console.log(r);
209
+ case 45:
210
+ if (r.verboseFailed.includes('Error')) {
169
211
  _context2.next = 10;
170
212
  break;
171
213
  }
172
- case 19:
173
- _context2.next = 21;
214
+ case 46:
215
+ _context2.next = 48;
174
216
  return (0, _testUtils.mergeWorkersResults)(testsResults);
175
- case 21:
217
+ case 48:
176
218
  return _context2.abrupt("return", _context2.sent);
177
- case 22:
219
+ case 49:
178
220
  case "end":
179
221
  return _context2.stop();
180
222
  }
181
- }, _callee2);
223
+ }, _callee2, null, [[22, 36, 39, 42]]);
182
224
  }));
183
225
  return _runTesting.apply(this, arguments);
226
+ }
227
+ function reproducedTest(_x3, _x4) {
228
+ return _reproducedTest.apply(this, arguments);
229
+ }
230
+ function _reproducedTest() {
231
+ _reproducedTest = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(args, testsToReproduce) {
232
+ var res, _iterator4, _step4, _args$benchmark2, _test3, r;
233
+ return _regenerator["default"].wrap(function _callee3$(_context3) {
234
+ while (1) switch (_context3.prev = _context3.next) {
235
+ case 0:
236
+ res = new Map();
237
+ _iterator4 = _createForOfIteratorHelper(testsToReproduce);
238
+ _context3.prev = 2;
239
+ _iterator4.s();
240
+ case 4:
241
+ if ((_step4 = _iterator4.n()).done) {
242
+ _context3.next = 12;
243
+ break;
244
+ }
245
+ _test3 = _step4.value;
246
+ _context3.next = 8;
247
+ return (0, _testUtils.runWorker)([_test3], {
248
+ benchmark: (_args$benchmark2 = args.benchmark) !== null && _args$benchmark2 !== void 0 ? _args$benchmark2 : false,
249
+ catchUnhandled: false,
250
+ gui: false,
251
+ record: false,
252
+ report: false,
253
+ verbose: false,
254
+ stopOnTimeout: true,
255
+ reproduce: true
256
+ }, 1, testInvocationTimeout);
257
+ case 8:
258
+ r = _context3.sent;
259
+ if (_test3.params.category && _test3.params.test) res.set(_test3, r);
260
+ case 10:
261
+ _context3.next = 4;
262
+ break;
263
+ case 12:
264
+ _context3.next = 17;
265
+ break;
266
+ case 14:
267
+ _context3.prev = 14;
268
+ _context3.t0 = _context3["catch"](2);
269
+ _iterator4.e(_context3.t0);
270
+ case 17:
271
+ _context3.prev = 17;
272
+ _iterator4.f();
273
+ return _context3.finish(17);
274
+ case 20:
275
+ return _context3.abrupt("return", res);
276
+ case 21:
277
+ case "end":
278
+ return _context3.stop();
279
+ }
280
+ }, _callee3, null, [[2, 14, 17, 20]]);
281
+ }));
282
+ return _reproducedTest.apply(this, arguments);
283
+ }
284
+ function updateResultsByReproduced(_x5, _x6, _x7) {
285
+ return _updateResultsByReproduced.apply(this, arguments);
286
+ }
287
+ function _updateResultsByReproduced() {
288
+ _updateResultsByReproduced = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(curentResult, reproducedResult, testsParams) {
289
+ var table2Dict, table1, table2, flakingMap;
290
+ return _regenerator["default"].wrap(function _callee4$(_context4) {
291
+ while (1) switch (_context4.prev = _context4.next) {
292
+ case 0:
293
+ table2Dict = {};
294
+ table1 = readCSVResultData(curentResult.csv);
295
+ table2 = readCSVResultData(reproducedResult.csv);
296
+ flakingMap = {};
297
+ table2.rows.forEach(function (row) {
298
+ var key = "".concat(row['category'], ",").concat(row['name']);
299
+ flakingMap[key] = row['flaking'];
300
+ });
301
+ table1.rows.forEach(function (row) {
302
+ var key = "".concat(row['category'], ",").concat(row['name']);
303
+ if (key in flakingMap) {
304
+ row['flaking'] = flakingMap[key];
305
+ }
306
+ });
307
+ curentResult.csv = Papa.unparse(table1.rows, {
308
+ columns: table1.headers
309
+ });
310
+ ;
311
+ curentResult.verboseFailed = curentResult.verboseFailed.replaceAll("".concat(testsParams.params.category, ": ").concat(testsParams.params.test, " : Error:"), "".concat(testsParams.params.category, ": ").concat(testsParams.params.test, " : Flaking Error:"));
312
+ return _context4.abrupt("return", curentResult);
313
+ case 10:
314
+ case "end":
315
+ return _context4.stop();
316
+ }
317
+ }, _callee4);
318
+ }));
319
+ return _updateResultsByReproduced.apply(this, arguments);
320
+ }
321
+ function readCSVResultData(data) {
322
+ var parsed = Papa.parse(data, {
323
+ header: true,
324
+ skipEmptyLines: true
325
+ });
326
+ if (parsed.errors.length > 0) {
327
+ throw new Error("Error parsing CSV file: ".concat(parsed.errors[0].message));
328
+ }
329
+ return {
330
+ headers: parsed.meta.fields || [],
331
+ rows: parsed.data
332
+ };
184
333
  }
@@ -303,13 +303,6 @@ function exitWithCode(code) {
303
303
  console.log("Exiting with code ".concat(code));
304
304
  process.exit(code);
305
305
  }
306
- var TestContext = exports.TestContext = /*#__PURE__*/(0, _createClass2["default"])(function TestContext(catchUnhandled, report) {
307
- (0, _classCallCheck2["default"])(this, TestContext);
308
- (0, _defineProperty2["default"])(this, "catchUnhandled", true);
309
- (0, _defineProperty2["default"])(this, "report", false);
310
- if (catchUnhandled !== undefined) this.catchUnhandled = catchUnhandled;
311
- if (report !== undefined) this.report = report;
312
- });
313
306
  var recorderConfig = exports.recorderConfig = {
314
307
  followNewTab: true,
315
308
  fps: 25,
@@ -459,7 +452,7 @@ function _loadTestsList() {
459
452
  _iterator3,
460
453
  _step3,
461
454
  testPackage,
462
- key,
455
+ _key,
463
456
  _iterator4,
464
457
  _step4,
465
458
  testValue,
@@ -547,9 +540,9 @@ function _loadTestsList() {
547
540
  try {
548
541
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
549
542
  testPackage = _step3.value;
550
- for (key in testPackage.tests) {
551
- if (testPackage.tests.hasOwnProperty(key)) {
552
- _iterator4 = _createForOfIteratorHelper(testPackage.tests[key].tests);
543
+ for (_key in testPackage.tests) {
544
+ if (testPackage.tests.hasOwnProperty(_key)) {
545
+ _iterator4 = _createForOfIteratorHelper(testPackage.tests[_key].tests);
553
546
  try {
554
547
  for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
555
548
  testValue = _step4.value;
@@ -669,6 +662,7 @@ function _runWorker() {
669
662
  _context9.next = 22;
670
663
  return page.evaluate(function (testData, options) {
671
664
  if (options.benchmark) window.DG.Test.isInBenchmark = true;
665
+ if (options.reproduce) window.DG.Test.isReproducing = true;
672
666
  return new Promise(function (resolve, reject) {
673
667
  window.DG.Utils.executeTests(testData, options.stopOnTimeout).then(function (results) {
674
668
  resolve(results);
@@ -788,4 +782,11 @@ function _mergeWorkersResults() {
788
782
  }, _callee11, null, [[2, 23, 26, 29]]);
789
783
  }));
790
784
  return _mergeWorkersResults.apply(this, arguments);
791
- }
785
+ }
786
+ var TestContext = exports.TestContext = /*#__PURE__*/(0, _createClass2["default"])(function TestContext(catchUnhandled, report) {
787
+ (0, _classCallCheck2["default"])(this, TestContext);
788
+ (0, _defineProperty2["default"])(this, "catchUnhandled", true);
789
+ (0, _defineProperty2["default"])(this, "report", false);
790
+ if (catchUnhandled !== undefined) this.catchUnhandled = catchUnhandled;
791
+ if (report !== undefined) this.report = report;
792
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "4.13.34",
3
+ "version": "4.13.36",
4
4
  "description": "Utility to upload and publish packages to Datagrok",
5
5
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
6
6
  "dependencies": {
@@ -17,6 +17,7 @@
17
17
  "node-fetch": "^2.7.0",
18
18
  "node-recursive-directory": "^1.2.0",
19
19
  "os": "^0.1.2",
20
+ "papaparse": "^5.4.1",
20
21
  "path": "^0.12.7",
21
22
  "puppeteer": "^22.10.0",
22
23
  "puppeteer-screen-recorder": "3.0.3"
@@ -53,6 +54,7 @@
53
54
  "@types/inquirer": "^8.2.10",
54
55
  "@types/js-yaml": "^4.0.9",
55
56
  "@types/node": "^16.18.70",
57
+ "@types/papaparse": "^5.3.15",
56
58
  "@typescript-eslint/eslint-plugin": "^5.62.0",
57
59
  "@typescript-eslint/parser": "^5.62.0",
58
60
  "eslint": "^8.56.0",