@sasjs/cli 4.20.9 → 5.0.3

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.
@@ -53,42 +53,49 @@ var internal_1 = require("./internal");
53
53
  */
54
54
  function deploy(target, isLocal) {
55
55
  return __awaiter(this, void 0, void 0, function () {
56
- var streamConfig, buildDestinationFolder, finalFilePathJSON, appLoc, jsonObject, servicesFolder, webIndexFileName, buildOutputFileName, deployScriptPath, deployScripts;
56
+ var streamConfig, deployServicePack, buildDestinationFolder, finalFilePathJSON, appLoc, jsonObject, servicesFolder, webIndexFileName, buildOutputFileName, deployScriptPath, deployScripts;
57
57
  var _this = this;
58
58
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
59
59
  return __generator(this, function (_l) {
60
60
  switch (_l.label) {
61
- case 0: return [4 /*yield*/, (0, config_1.getStreamConfig)(target)];
61
+ case 0: return [4 /*yield*/, (0, config_1.getStreamConfig)(target)
62
+ // A target that declares no deployConfig used to have one injected by the
63
+ // @sasjs/utils Target constructor. That injection is gone (sasjs/utils#277),
64
+ // so an absent deployConfig is treated as the historical default - deploy the
65
+ // service pack - rather than refusing to deploy. An explicit
66
+ // deployServicePack: false is still honoured.
67
+ ];
62
68
  case 1:
63
69
  streamConfig = _l.sent();
64
- if (!((_a = target.deployConfig) === null || _a === void 0 ? void 0 : _a.deployServicePack)) return [3 /*break*/, 7];
70
+ deployServicePack = (_b = (_a = target.deployConfig) === null || _a === void 0 ? void 0 : _a.deployServicePack) !== null && _b !== void 0 ? _b : true;
71
+ if (!deployServicePack) return [3 /*break*/, 7];
65
72
  buildDestinationFolder = process.sasjsConstants.buildDestinationFolder;
66
73
  finalFilePathJSON = path_1.default.join(buildDestinationFolder, "".concat(target.name, ".json"));
67
74
  if (!(target.serverType === utils_1.ServerType.SasViya)) return [3 /*break*/, 3];
68
75
  appLoc = encodeURI(target.appLoc);
69
- (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(appLoc, " ."));
76
+ (_c = process.logger) === null || _c === void 0 ? void 0 : _c.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(appLoc, " ."));
70
77
  return [4 /*yield*/, (0, deployToSasViyaWithServicePack_1.deployToSasViyaWithServicePack)(finalFilePathJSON, target, isLocal, true)];
71
78
  case 2:
72
79
  jsonObject = _l.sent();
73
80
  servicesFolder = jsonObject === null || jsonObject === void 0 ? void 0 : jsonObject.members.find(function (member) {
74
81
  return (member === null || member === void 0 ? void 0 : member.name) === 'services' && (member === null || member === void 0 ? void 0 : member.type) === utils_1.MemberType.folder;
75
82
  });
76
- webIndexFileName = (_e = (_d = (_c = servicesFolder === null || servicesFolder === void 0 ? void 0 : servicesFolder.members) === null || _c === void 0 ? void 0 : _c.find(function (member) { return (member === null || member === void 0 ? void 0 : member.type) === utils_1.MemberType.file; })) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e : '';
77
- (_f = process.logger) === null || _f === void 0 ? void 0 : _f.success('Build pack has been successfully deployed.');
78
- (_g = process.logger) === null || _g === void 0 ? void 0 : _g.success(target.serverType === utils_1.ServerType.SasViya && webIndexFileName
83
+ webIndexFileName = (_f = (_e = (_d = servicesFolder === null || servicesFolder === void 0 ? void 0 : servicesFolder.members) === null || _d === void 0 ? void 0 : _d.find(function (member) { return (member === null || member === void 0 ? void 0 : member.type) === utils_1.MemberType.file; })) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '';
84
+ (_g = process.logger) === null || _g === void 0 ? void 0 : _g.success('Build pack has been successfully deployed.');
85
+ (_h = process.logger) === null || _h === void 0 ? void 0 : _h.success(target.serverType === utils_1.ServerType.SasViya && webIndexFileName
79
86
  ? "".concat(target.serverUrl, "/SASJobExecution?_file=").concat(appLoc, "/services/").concat(webIndexFileName)
80
87
  : "".concat(target.serverUrl, "/SASJobExecution?_folder=").concat(appLoc));
81
88
  return [3 /*break*/, 7];
82
89
  case 3:
83
90
  if (!(target.serverType === utils_1.ServerType.Sasjs)) return [3 /*break*/, 5];
84
- (_h = process.logger) === null || _h === void 0 ? void 0 : _h.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(target.appLoc, " ."));
91
+ (_j = process.logger) === null || _j === void 0 ? void 0 : _j.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(target.appLoc, " ."));
85
92
  return [4 /*yield*/, (0, internal_1.deployToSasjsWithServicePack)(finalFilePathJSON, target, streamConfig)];
86
93
  case 4:
87
94
  _l.sent();
88
95
  return [3 /*break*/, 7];
89
96
  case 5:
90
97
  if (!(target.serverType === utils_1.ServerType.Sas9)) return [3 /*break*/, 7];
91
- buildOutputFileName = ((_j = target.buildConfig) === null || _j === void 0 ? void 0 : _j.buildOutputFileName) || "".concat(target.name, ".sas");
98
+ buildOutputFileName = ((_k = target.buildConfig) === null || _k === void 0 ? void 0 : _k.buildOutputFileName) || "".concat(target.name, ".sas");
92
99
  deployScriptPath = path_1.default.join(buildDestinationFolder, buildOutputFileName);
93
100
  return [4 /*yield*/, (0, internal_1.executeSasScript)(deployScriptPath, target, streamConfig)];
94
101
  case 6:
@@ -97,7 +104,7 @@ function deploy(target, isLocal) {
97
104
  case 7: return [4 /*yield*/, (0, internal_1.getDeployScripts)(target)];
98
105
  case 8:
99
106
  deployScripts = _l.sent();
100
- if (deployScripts.length === 0 && !((_k = target.deployConfig) === null || _k === void 0 ? void 0 : _k.deployServicePack)) {
107
+ if (deployScripts.length === 0 && !deployServicePack) {
101
108
  throw new Error("Deployment failed.\nPlease either enable the 'deployServicePack' option or add deployment script paths to 'deployScripts' in your target's 'deployConfig'.");
102
109
  }
103
110
  return [4 /*yield*/, (0, utils_1.asyncForEach)(deployScripts, function (deployScript) { return __awaiter(_this, void 0, void 0, function () {
@@ -154,6 +154,32 @@ describe('deploy', function () {
154
154
  }
155
155
  });
156
156
  }); });
157
+ it('should deploy the service pack when the target declares no deployConfig', function () { return __awaiter(void 0, void 0, void 0, function () {
158
+ var target, fileTree;
159
+ return __generator(this, function (_a) {
160
+ switch (_a.label) {
161
+ case 0:
162
+ target = new utils_1.Target(__assign(__assign({}, commonTargetFields), { serverType: utils_1.ServerType.SasViya }));
163
+ fileTree = {
164
+ members: [
165
+ {
166
+ name: 'mockedFile',
167
+ type: utils_1.MemberType.file,
168
+ code: 'mocked code'
169
+ }
170
+ ]
171
+ };
172
+ jest
173
+ .spyOn(deployToSasViyaWithServicePackModule, 'deployToSasViyaWithServicePack')
174
+ .mockImplementation(function () { return Promise.resolve(fileTree); });
175
+ return [4 /*yield*/, (0, deploy_1.deploy)(target, true)];
176
+ case 1:
177
+ _a.sent();
178
+ expect(deployToSasViyaWithServicePackModule.deployToSasViyaWithServicePack).toHaveBeenCalledWith(path_1.default.join(process.sasjsConstants.buildDestinationFolder, "".concat(target.name, ".json")), target, true, true);
179
+ return [2 /*return*/];
180
+ }
181
+ });
182
+ }); });
157
183
  it('should deploy services to SASJS as a service pack', function () { return __awaiter(void 0, void 0, void 0, function () {
158
184
  var target;
159
185
  return __generator(this, function (_a) {
@@ -94,7 +94,7 @@ var shell_1 = require("../../utils/shell");
94
94
  */
95
95
  function generateDocs(target, config, outDirectory) {
96
96
  return __awaiter(this, void 0, void 0, function () {
97
- var _a, serverUrl, newOutDirectory, enableLineage, doxyContentFromConfig, _b, macroCoreFolders, macroFolders, programFolders, serviceFolders, jobFolders, combinedFolders, PROJECT_NAME, PROJECT_BRIEF, packageJsonContent, packageJson, e_1, doxyContent, layoutFilePath, LAYOUT_FILE, readMePath, doxyParams, doxyConfigPath, spinner, pathExists, _c, stderr, code, foldersListForDot;
97
+ var _a, serverUrl, newOutDirectory, enableLineage, doxyContentFromConfig, _b, macroCoreFolders, macroFolders, programFolders, serviceFolders, jobFolders, combinedFolders, PROJECT_NAME, PROJECT_BRIEF, packageJsonContent, packageJson, e_1, doxyContent, layoutFilePath, LAYOUT_FILE, readMePath, doxyVariables, doxyConfigPath, spinner, pathExists, _c, stderr, code, foldersListForDot;
98
98
  var _this = this;
99
99
  var _d, _e;
100
100
  return __generator(this, function (_f) {
@@ -163,7 +163,7 @@ function generateDocs(target, config, outDirectory) {
163
163
  case 5:
164
164
  LAYOUT_FILE = _f.sent();
165
165
  readMePath = path_1.default.join(doxyContent.path, doxyContent.readMe);
166
- doxyParams = setVariableCmd({
166
+ doxyVariables = {
167
167
  DOXY_HTML_OUTPUT: newOutDirectory,
168
168
  DOXY_INPUT: "\"".concat(readMePath, "\" ").concat(combinedFolders),
169
169
  DOXY_MAINPAGE: readMePath,
@@ -175,7 +175,7 @@ function generateDocs(target, config, outDirectory) {
175
175
  PROJECT_BRIEF: PROJECT_BRIEF,
176
176
  PROJECT_LOGO: path_1.default.join(doxyContent.path, doxyContent.logo),
177
177
  PROJECT_NAME: PROJECT_NAME
178
- });
178
+ };
179
179
  return [4 /*yield*/, getDoxyConfigPath(doxyContent.path)];
180
180
  case 6:
181
181
  doxyConfigPath = _f.sent();
@@ -194,8 +194,9 @@ function generateDocs(target, config, outDirectory) {
194
194
  case 9: return [4 /*yield*/, (0, utils_1.createFolder)(newOutDirectory)];
195
195
  case 10:
196
196
  _f.sent();
197
- _c = (0, shell_1.exec)("".concat(doxyParams, " doxygen \"").concat(doxyConfigPath, "\""), {
198
- silent: process.env.LOG_LEVEL !== utils_1.LogLevel.Debug
197
+ _c = (0, shell_1.execFile)('doxygen', [doxyConfigPath], {
198
+ silent: process.env.LOG_LEVEL !== utils_1.LogLevel.Debug,
199
+ env: doxyVariables
199
200
  }), stderr = _c.stderr, code = _c.code;
200
201
  if (process.env.LOG_LEVEL !== utils_1.LogLevel.Debug)
201
202
  spinner.stop();
@@ -228,18 +229,6 @@ function generateDocs(target, config, outDirectory) {
228
229
  });
229
230
  });
230
231
  }
231
- function setVariableCmd(params) {
232
- var command = '';
233
- if ((0, utils_1.isWindows)()) {
234
- for (var param in params)
235
- command += "set ".concat(param, "=").concat(params[param], " && ");
236
- }
237
- else {
238
- for (var param in params)
239
- command += "".concat(param, "='").concat(params[param], "' ");
240
- }
241
- return command;
242
- }
243
232
  var getDoxyConfigPath = function (doxyContentPath) { return __awaiter(void 0, void 0, void 0, function () {
244
233
  var doxyFilePath;
245
234
  return __generator(this, function (_a) {
@@ -555,7 +555,7 @@ describe('sasjs doc', function () {
555
555
  return __generator(this, function (_a) {
556
556
  switch (_a.label) {
557
557
  case 0:
558
- jest.spyOn(shell, 'exec').mockReturnValueOnce({
558
+ jest.spyOn(shell, 'execFile').mockReturnValueOnce({
559
559
  code: 1,
560
560
  stderr: 'error: bad config',
561
561
  stdout: ''
@@ -571,7 +571,7 @@ describe('sasjs doc', function () {
571
571
  return __generator(this, function (_a) {
572
572
  switch (_a.label) {
573
573
  case 0:
574
- jest.spyOn(shell, 'exec').mockReturnValueOnce({
574
+ jest.spyOn(shell, 'execFile').mockReturnValueOnce({
575
575
  code: 1,
576
576
  stderr: 'command not found',
577
577
  stdout: ''
@@ -107,6 +107,11 @@ var targetCommand_1 = require("../../types/command/targetCommand");
107
107
  var utils_1 = require("../../utils");
108
108
  var path_1 = __importDefault(require("path"));
109
109
  var utils_2 = require("@sasjs/utils");
110
+ // The hash and sync helpers live in the fs subpath. The barrel no longer
111
+ // re-exports them, because it is the only module in @sasjs/utils that imports
112
+ // crypto, which put the whole AES/RSA/ECDSA polyfill suite into every browser
113
+ // bundle that imported anything from the barrel (sasjs/utils#275, #283).
114
+ var fs_1 = require("@sasjs/utils/fs");
110
115
  var executeCode_1 = require("./internal/executeCode");
111
116
  var FsSubCommand;
112
117
  (function (FsSubCommand) {
@@ -269,7 +274,7 @@ var FSCommand = /** @class */ (function (_super) {
269
274
  return [4 /*yield*/, this.getOutputPath()];
270
275
  case 2:
271
276
  outputPath = _a.sent();
272
- return [4 /*yield*/, (0, utils_2.generateCompileProgram)(folderPath)];
277
+ return [4 /*yield*/, (0, fs_1.generateCompileProgram)(folderPath)];
273
278
  case 3:
274
279
  programContent = _a.sent();
275
280
  return [4 /*yield*/, (0, utils_2.createFile)(outputPath, programContent)
@@ -306,7 +311,7 @@ var FSCommand = /** @class */ (function (_super) {
306
311
  case 3:
307
312
  _f.trys.push([3, 7, , 9]);
308
313
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info('generating compile program');
309
- return [4 /*yield*/, (0, utils_2.generateCompileProgram)(localFolderPath)];
314
+ return [4 /*yield*/, (0, fs_1.generateCompileProgram)(localFolderPath)];
310
315
  case 4:
311
316
  code = _f.sent();
312
317
  (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info('setting fsTaregt at the start of compiled program');
@@ -383,7 +388,7 @@ var FSCommand = /** @class */ (function (_super) {
383
388
  case 2:
384
389
  _v.trys.push([2, 15, , 17]);
385
390
  (_h = process.logger) === null || _h === void 0 ? void 0 : _h.info('generating program to get remote hash');
386
- return [4 /*yield*/, (0, utils_2.generateProgramToGetRemoteHash)(obj.remote)];
391
+ return [4 /*yield*/, (0, fs_1.generateProgramToGetRemoteHash)(obj.remote)];
387
392
  case 3:
388
393
  program = _v.sent();
389
394
  return [4 /*yield*/, createProgramFile(program, path_1.default.join(outputFolder, 'getRemoteHash.sas'))];
@@ -397,12 +402,12 @@ var FSCommand = /** @class */ (function (_super) {
397
402
  case 6:
398
403
  _v.sent();
399
404
  (_k = process.logger) === null || _k === void 0 ? void 0 : _k.info('extracting hashes from log');
400
- remoteHashes = (0, utils_2.extractHashArray)(log);
405
+ remoteHashes = (0, fs_1.extractHashArray)(log);
401
406
  return [4 /*yield*/, createHashFile(JSON.stringify(remoteHashes, null, 2), path_1.default.join(outputFolder, 'hashesBeforeSync.json'))];
402
407
  case 7:
403
408
  _v.sent();
404
409
  (_l = process.logger) === null || _l === void 0 ? void 0 : _l.info('creating the hash of local folder');
405
- return [4 /*yield*/, (0, utils_2.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
410
+ return [4 /*yield*/, (0, fs_1.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
406
411
  case 8:
407
412
  localHash = _v.sent();
408
413
  remoteHashMap_1 = remoteHashes.reduce(function (map, item) {
@@ -417,12 +422,12 @@ var FSCommand = /** @class */ (function (_super) {
417
422
  return [2 /*return*/, { value: command_1.ReturnCode.Success }];
418
423
  }
419
424
  (_o = process.logger) === null || _o === void 0 ? void 0 : _o.info('Extract differences from local and remote hash');
420
- hashedDiff = (0, utils_2.compareHashes)(localHash, remoteHashMap_1);
425
+ hashedDiff = (0, fs_1.compareHashes)(localHash, remoteHashMap_1);
421
426
  return [4 /*yield*/, createHashFile(JSON.stringify(hashedDiff, null, 2), path_1.default.join(outputFolder, 'hashesDiff.json'))];
422
427
  case 9:
423
428
  _v.sent();
424
429
  (_p = process.logger) === null || _p === void 0 ? void 0 : _p.info('generating program to sync differences');
425
- return [4 /*yield*/, (0, utils_2.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
430
+ return [4 /*yield*/, (0, fs_1.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
426
431
  case 10:
427
432
  syncProgram = _v.sent();
428
433
  return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
@@ -435,7 +440,7 @@ var FSCommand = /** @class */ (function (_super) {
435
440
  return [4 /*yield*/, (0, utils_1.saveLog)(syncLog, path_1.default.join(outputFolder, 'sync.log'), '', false)];
436
441
  case 13:
437
442
  _v.sent();
438
- syncedHash = (0, utils_2.extractHashArray)(syncLog);
443
+ syncedHash = (0, fs_1.extractHashArray)(syncLog);
439
444
  return [4 /*yield*/, createHashFile(JSON.stringify(syncedHash, null, 2), path_1.default.join(outputFolder, 'hashesAfterSync.json'))];
440
445
  case 14:
441
446
  _v.sent();
@@ -459,7 +464,7 @@ var FSCommand = /** @class */ (function (_super) {
459
464
  (_a = process.logger) === null || _a === void 0 ? void 0 : _a.log("* ".concat(item));
460
465
  });
461
466
  }
462
- resourcesNotPresentLocally = (0, utils_2.findResourcesNotPresentLocally)(localHash, syncedHashMap);
467
+ resourcesNotPresentLocally = (0, fs_1.findResourcesNotPresentLocally)(localHash, syncedHashMap);
463
468
  if (resourcesNotPresentLocally.length) {
464
469
  (_s = process.logger) === null || _s === void 0 ? void 0 : _s.log('The following resources are present in remote directory but not in local:');
465
470
  resourcesNotPresentLocally.forEach(function (item) {
@@ -23,6 +23,13 @@ export declare function findTargetInConfiguration(targetName: string, targetScop
23
23
  export declare function getGlobalRcFile(): Promise<any>;
24
24
  export declare function getLocalRcFile(): Promise<Configuration | null>;
25
25
  export declare function saveGlobalRcFile(content: string): Promise<string>;
26
+ /**
27
+ * Saves a target to the global SASjs config.
28
+ *
29
+ * `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
30
+ * target - the mandatory attributes only. Pass true to write the full set of
31
+ * injected defaults, including a deployConfig the caller never declared.
32
+ */
26
33
  export declare function saveToGlobalConfig(target: Target, isDefault?: boolean, saveWithDefaultValues?: boolean): Promise<string>;
27
34
  export declare function removeFromGlobalConfig(targetName: string): Promise<void>;
28
35
  export declare function removeFromLocalConfig(targetName: string): Promise<void>;
@@ -33,6 +40,13 @@ export declare function getLocalOrGlobalConfig(): Promise<{
33
40
  }>;
34
41
  export declare function getSyncDirectories(target: Target, isLocal: boolean): Promise<SyncDirectoryMap[]>;
35
42
  export declare function saveLocalConfigFile(content: string): Promise<string>;
43
+ /**
44
+ * Saves a target to the project's local sasjsconfig.json.
45
+ *
46
+ * `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
47
+ * target - the mandatory attributes only. Pass true to write the full set of
48
+ * injected defaults, including a deployConfig the caller never declared.
49
+ */
36
50
  export declare function saveToLocalConfig(target: Target, isDefault?: boolean, saveWithDefaultValues?: boolean): Promise<string>;
37
51
  export declare function getFolders(): Promise<any>;
38
52
  /**
@@ -447,12 +447,19 @@ function saveGlobalRcFile(content) {
447
447
  });
448
448
  });
449
449
  }
450
+ /**
451
+ * Saves a target to the global SASjs config.
452
+ *
453
+ * `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
454
+ * target - the mandatory attributes only. Pass true to write the full set of
455
+ * injected defaults, including a deployConfig the caller never declared.
456
+ */
450
457
  function saveToGlobalConfig(target_1) {
451
458
  return __awaiter(this, arguments, void 0, function (target, isDefault, saveWithDefaultValues) {
452
459
  var globalConfig, targetJson, existingTargetIndex;
453
460
  var _a;
454
461
  if (isDefault === void 0) { isDefault = false; }
455
- if (saveWithDefaultValues === void 0) { saveWithDefaultValues = true; }
462
+ if (saveWithDefaultValues === void 0) { saveWithDefaultValues = false; }
456
463
  return __generator(this, function (_b) {
457
464
  switch (_b.label) {
458
465
  case 0: return [4 /*yield*/, getGlobalRcFile()];
@@ -606,12 +613,19 @@ function saveLocalConfigFile(content) {
606
613
  });
607
614
  });
608
615
  }
616
+ /**
617
+ * Saves a target to the project's local sasjsconfig.json.
618
+ *
619
+ * `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
620
+ * target - the mandatory attributes only. Pass true to write the full set of
621
+ * injected defaults, including a deployConfig the caller never declared.
622
+ */
609
623
  function saveToLocalConfig(target_1) {
610
624
  return __awaiter(this, arguments, void 0, function (target, isDefault, saveWithDefaultValues) {
611
625
  var targetJson, config, existingTargetIndex, configPath;
612
626
  var _a, _b;
613
627
  if (isDefault === void 0) { isDefault = false; }
614
- if (saveWithDefaultValues === void 0) { saveWithDefaultValues = true; }
628
+ if (saveWithDefaultValues === void 0) { saveWithDefaultValues = false; }
615
629
  return __generator(this, function (_c) {
616
630
  switch (_c.label) {
617
631
  case 0:
@@ -17,15 +17,27 @@ export interface ShellResult {
17
17
  stderr: string;
18
18
  code: number;
19
19
  }
20
+ export interface ExecOptions {
21
+ /**
22
+ * Whether the command's output also reaches this process's streams, which is
23
+ * how shelljs' option of the same name behaved.
24
+ */
25
+ silent?: boolean;
26
+ /** Extra variables for the child, on top of this process's environment. */
27
+ env?: NodeJS.ProcessEnv;
28
+ }
29
+ /** Runs a command through the platform shell. */
30
+ export declare const exec: (command: string, options?: ExecOptions) => ShellResult;
20
31
  /**
21
- * Runs a command through the platform shell.
32
+ * Runs an executable with an argument array, without a shell.
22
33
  *
23
- * `silent` decides whether the command's output also reaches this process's
24
- * streams, which is how shelljs' option of the same name behaved.
34
+ * `exec` hands its command to a shell, which re-parses everything in it: a path
35
+ * containing a quote, `$` or `&` changes the command's meaning, and a variable
36
+ * assigned on the command line is no different - on Windows the assignment form
37
+ * does not even quote the value. Arguments and `env` given here are passed to
38
+ * the child as they are, so they are safe whatever they contain.
25
39
  */
26
- export declare const exec: (command: string, options?: {
27
- silent?: boolean;
28
- }) => ShellResult;
40
+ export declare const execFile: (command: string, args: string[], options?: ExecOptions) => ShellResult;
29
41
  /**
30
42
  * Copies the contents of a folder into another folder, matching
31
43
  * `cp -r <source>/. <destination>`.
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __values = (this && this.__values) || function(o) {
3
14
  var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
15
  if (m) return m.call(o);
@@ -14,28 +25,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
26
  };
16
27
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.which = exports.ls = exports.rm = exports.cp = exports.exec = void 0;
28
+ exports.which = exports.ls = exports.rm = exports.cp = exports.execFile = exports.exec = void 0;
18
29
  var child_process_1 = require("child_process");
19
30
  var fs_1 = __importDefault(require("fs"));
20
31
  var path_1 = __importDefault(require("path"));
21
32
  var utils_1 = require("@sasjs/utils");
22
33
  /**
23
- * Runs a command through the platform shell.
24
- *
25
- * `silent` decides whether the command's output also reaches this process's
26
- * streams, which is how shelljs' option of the same name behaved.
34
+ * The options both runners share. shelljs allowed 20MB of output; spawnSync's
35
+ * 1MB default kills a chatty command mid-run, and a deploy script can easily be
36
+ * chatty.
27
37
  */
28
- var exec = function (command, options) {
38
+ var spawnOptions = function (options) { return ({
39
+ encoding: 'utf8',
40
+ stdio: ['ignore', 'pipe', 'pipe'],
41
+ maxBuffer: 20 * 1024 * 1024,
42
+ env: options.env ? __assign(__assign({}, process.env), options.env) : undefined
43
+ }); };
44
+ /** The result both runners answer with, from one place. */
45
+ var toShellResult = function (result, options) {
29
46
  var _a, _b, _c;
30
- if (options === void 0) { options = {}; }
31
- var result = (0, child_process_1.spawnSync)(command, {
32
- shell: true,
33
- encoding: 'utf8',
34
- stdio: ['ignore', 'pipe', 'pipe'],
35
- // shelljs allowed 20MB of output; spawnSync's 1MB default kills a chatty
36
- // command mid-run, and a deploy script can easily be chatty.
37
- maxBuffer: 20 * 1024 * 1024
38
- });
39
47
  var stdout = (_a = result.stdout) !== null && _a !== void 0 ? _a : '';
40
48
  var stderr = (_b = result.stderr) !== null && _b !== void 0 ? _b : '';
41
49
  if (!options.silent) {
@@ -56,7 +64,26 @@ var exec = function (command, options) {
56
64
  }
57
65
  return { stdout: stdout, stderr: stderr, code: (_c = result.status) !== null && _c !== void 0 ? _c : 0 };
58
66
  };
67
+ /** Runs a command through the platform shell. */
68
+ var exec = function (command, options) {
69
+ if (options === void 0) { options = {}; }
70
+ return toShellResult((0, child_process_1.spawnSync)(command, __assign(__assign({}, spawnOptions(options)), { shell: true })), options);
71
+ };
59
72
  exports.exec = exec;
73
+ /**
74
+ * Runs an executable with an argument array, without a shell.
75
+ *
76
+ * `exec` hands its command to a shell, which re-parses everything in it: a path
77
+ * containing a quote, `$` or `&` changes the command's meaning, and a variable
78
+ * assigned on the command line is no different - on Windows the assignment form
79
+ * does not even quote the value. Arguments and `env` given here are passed to
80
+ * the child as they are, so they are safe whatever they contain.
81
+ */
82
+ var execFile = function (command, args, options) {
83
+ if (options === void 0) { options = {}; }
84
+ return toShellResult((0, child_process_1.spawnSync)(command, args, spawnOptions(options)), options);
85
+ };
86
+ exports.execFile = execFile;
60
87
  /**
61
88
  * Copies the contents of a folder into another folder, matching
62
89
  * `cp -r <source>/. <destination>`.
@@ -770,6 +770,61 @@ describe('saveToGlobalConfig', function () {
770
770
  }
771
771
  });
772
772
  }); });
773
+ it('should persist the target with injected defaults when saveWithDefaultValues is true', function () { return __awaiter(void 0, void 0, void 0, function () {
774
+ var appName, target, config, configTarget;
775
+ var _a;
776
+ return __generator(this, function (_b) {
777
+ switch (_b.label) {
778
+ case 0:
779
+ appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
780
+ target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
781
+ return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true, true)];
782
+ case 1:
783
+ _b.sent();
784
+ return [4 /*yield*/, (0, config_1.getGlobalRcFile)()];
785
+ case 2:
786
+ config = (_b.sent());
787
+ configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
788
+ expect(configTarget).toBeTruthy();
789
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeTruthy();
790
+ return [4 /*yield*/, (0, config_1.removeFromGlobalConfig)(target.name)];
791
+ case 3:
792
+ _b.sent();
793
+ return [2 /*return*/];
794
+ }
795
+ });
796
+ }); });
797
+ it('should persist a minimal target when saveWithDefaultValues is false', function () { return __awaiter(void 0, void 0, void 0, function () {
798
+ var appName, target, config, configTarget;
799
+ var _a;
800
+ return __generator(this, function (_b) {
801
+ switch (_b.label) {
802
+ case 0:
803
+ appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
804
+ target = new utils_1.Target({
805
+ name: appName,
806
+ serverUrl: 'http://test.sasjs',
807
+ appLoc: "/Public/app/cli-tests/".concat(appName),
808
+ serverType: utils_1.ServerType.SasViya
809
+ });
810
+ return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true)];
811
+ case 1:
812
+ _b.sent();
813
+ return [4 /*yield*/, (0, config_1.getGlobalRcFile)()];
814
+ case 2:
815
+ config = (_b.sent());
816
+ expect(config.defaultTarget).toEqual(target.name);
817
+ configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
818
+ expect(configTarget).toBeTruthy();
819
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeFalsy();
820
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.buildConfig).toBeFalsy();
821
+ return [4 /*yield*/, (0, config_1.removeFromGlobalConfig)(target.name)];
822
+ case 3:
823
+ _b.sent();
824
+ return [2 /*return*/];
825
+ }
826
+ });
827
+ }); });
773
828
  it('should not set the target as default when isDefault is false', function () { return __awaiter(void 0, void 0, void 0, function () {
774
829
  var appName, target, config, configTarget;
775
830
  var _a;
@@ -922,6 +977,60 @@ describe('saveToLocalConfig', function () {
922
977
  }
923
978
  });
924
979
  }); });
980
+ it('should persist the target with injected defaults when saveWithDefaultValues is true', function () { return __awaiter(void 0, void 0, void 0, function () {
981
+ var appName, target, config, configTarget;
982
+ var _a;
983
+ return __generator(this, function (_b) {
984
+ switch (_b.label) {
985
+ case 0:
986
+ appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
987
+ target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
988
+ return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true, true)];
989
+ case 1:
990
+ _b.sent();
991
+ return [4 /*yield*/, (0, config_1.getLocalConfig)()];
992
+ case 2:
993
+ config = (_b.sent());
994
+ configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
995
+ expect(configTarget).toBeTruthy();
996
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeTruthy();
997
+ return [4 /*yield*/, (0, config_1.removeFromLocalConfig)(target.name)];
998
+ case 3:
999
+ _b.sent();
1000
+ return [2 /*return*/];
1001
+ }
1002
+ });
1003
+ }); });
1004
+ it('should persist a minimal target when saveWithDefaultValues is omitted', function () { return __awaiter(void 0, void 0, void 0, function () {
1005
+ var appName, target, config, configTarget;
1006
+ var _a;
1007
+ return __generator(this, function (_b) {
1008
+ switch (_b.label) {
1009
+ case 0:
1010
+ appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
1011
+ target = new utils_1.Target({
1012
+ name: appName,
1013
+ serverUrl: 'http://test.sasjs',
1014
+ appLoc: "/Public/app/cli-tests/".concat(appName),
1015
+ serverType: utils_1.ServerType.SasViya
1016
+ });
1017
+ return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true)];
1018
+ case 1:
1019
+ _b.sent();
1020
+ return [4 /*yield*/, (0, config_1.getLocalConfig)()];
1021
+ case 2:
1022
+ config = (_b.sent());
1023
+ configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
1024
+ expect(configTarget).toBeTruthy();
1025
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeFalsy();
1026
+ expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.buildConfig).toBeFalsy();
1027
+ return [4 /*yield*/, (0, config_1.removeFromLocalConfig)(target.name)];
1028
+ case 3:
1029
+ _b.sent();
1030
+ return [2 /*return*/];
1031
+ }
1032
+ });
1033
+ }); });
925
1034
  it('should not set the target as default when isDefault is false', function () { return __awaiter(void 0, void 0, void 0, function () {
926
1035
  var appName, target, config, configTarget;
927
1036
  var _a;
@@ -54,6 +54,33 @@ describe('shell', function () {
54
54
  expect(result.stdout.length).toEqual(3 * 1024 * 1024);
55
55
  });
56
56
  });
57
+ describe('execFile', function () {
58
+ // The values below are the point: a shell would interpret each of them -
59
+ // `&` and `;` separate commands, `$(...)` and backticks substitute, `'`
60
+ // closes a quote - and on Windows the `set NAME=value` form does not quote
61
+ // the value at all. Nothing here is parsed, so each arrives intact.
62
+ var metacharacters = "a'b&c;$(echo pwned)`id`|d>e";
63
+ it('should answer with stdout for an executable run with an argument array', function () {
64
+ var result = (0, shell_1.execFile)(process.execPath, ['-e', "process.stdout.write('hello')"], { silent: true });
65
+ expect(result.stdout).toEqual('hello');
66
+ expect(result.code).toEqual(0);
67
+ });
68
+ it('should pass an argument through without the shell interpreting it', function () {
69
+ var result = (0, shell_1.execFile)(process.execPath, ['-e', 'process.stdout.write(process.argv[1])', metacharacters], { silent: true });
70
+ expect(result.stdout).toEqual(metacharacters);
71
+ expect(result.code).toEqual(0);
72
+ });
73
+ it('should pass an environment value through without the shell interpreting it', function () {
74
+ var result = (0, shell_1.execFile)(process.execPath, ['-e', 'process.stdout.write(process.env.PROBE)'], { silent: true, env: { PROBE: metacharacters } });
75
+ expect(result.stdout).toEqual(metacharacters);
76
+ expect(result.code).toEqual(0);
77
+ });
78
+ it('should answer with the exit code and stderr for an executable that fails', function () {
79
+ var result = (0, shell_1.execFile)(process.execPath, ['-e', "process.stderr.write('boom'); process.exit(3)"], { silent: true });
80
+ expect(result.code).toEqual(3);
81
+ expect(result.stderr).toContain('boom');
82
+ });
83
+ });
57
84
  describe('ls', function () {
58
85
  beforeEach(function () {
59
86
  fs_1.default.mkdirSync(path_1.default.join(root, 'sasjs-react-seed-app-main'));
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -273,24 +262,6 @@ describe('utils', function () {
273
262
  });
274
263
  });
275
264
  });
276
- describe('checkNodeVersion', function () {
277
- afterEach(function () {
278
- jest.resetAllMocks();
279
- });
280
- it('should exit the process when node version is less than 12', function () {
281
- var originalProcess = global.process;
282
- global.process = __assign(__assign({}, originalProcess), { versions: __assign(__assign({}, originalProcess.versions), { node: '11.0.0' }) });
283
- var mockExit = (0, test_1.mockProcessExit)();
284
- (0, utils_1.checkNodeVersion)();
285
- expect(mockExit).toHaveBeenCalledWith(1);
286
- global.process = originalProcess;
287
- });
288
- it('should not exit the process when node version greater than 12', function () {
289
- jest.spyOn(process, 'exit');
290
- (0, utils_1.checkNodeVersion)();
291
- expect(process.exit).toHaveBeenCalledTimes(0);
292
- });
293
- });
294
265
  describe('getAdapterInstance', function () {
295
266
  it('should throw an error when target is not valid', function () {
296
267
  expect(function () { return (0, utils_1.getAdapterInstance)(null); }).toThrow('Unable to create SASjs adapter instance: Invalid target.');
@@ -84,6 +84,32 @@ var setEntryPlatform = function (zipPath, platform) {
84
84
  }
85
85
  fs_1.default.writeFileSync(zipPath, buffer);
86
86
  };
87
+ /**
88
+ * Rewrites an entry name in the archive bytes, in both the local file header
89
+ * and the central directory record.
90
+ *
91
+ * yazl refuses to create an entry whose name is absolute or contains `..`, so
92
+ * a traversal entry has to be patched in after the fact. The replacement must
93
+ * be the same length as the original: both records carry the name length, and
94
+ * changing it would move every offset that follows.
95
+ */
96
+ var replaceEntryName = function (zipPath, from, to) {
97
+ if (from.length !== to.length)
98
+ throw new Error('the replacement name must be the same length');
99
+ var buffer = fs_1.default.readFileSync(zipPath);
100
+ var fromBuffer = Buffer.from(from, 'utf8');
101
+ var toBuffer = Buffer.from(to, 'utf8');
102
+ var index = buffer.indexOf(fromBuffer);
103
+ var replaced = 0;
104
+ while (index !== -1) {
105
+ toBuffer.copy(buffer, index);
106
+ replaced++;
107
+ index = buffer.indexOf(fromBuffer, index + 1);
108
+ }
109
+ if (!replaced)
110
+ throw new Error("entry name not found in the archive: ".concat(from));
111
+ fs_1.default.writeFileSync(zipPath, buffer);
112
+ };
87
113
  describe('zip', function () {
88
114
  var tmpRoot = path_1.default.join(__dirname, "zip-spec-".concat((0, test_1.generateUniqueTimestamp)()));
89
115
  var destDir = path_1.default.join(tmpRoot, 'dest');
@@ -279,5 +305,33 @@ describe('zip', function () {
279
305
  }
280
306
  });
281
307
  }); });
308
+ /**
309
+ * The safety of `path.join(destDir, entry.fileName)` rests on the entry
310
+ * name never containing `..` or a leading slash. That is yauzl's
311
+ * validateFileName, which only runs because the reader is opened with
312
+ * decodeStrings - so the property is pinned here rather than left to the
313
+ * comment that documents it.
314
+ */
315
+ it('should refuse an entry whose name traverses out of the destination', function () { return __awaiter(void 0, void 0, void 0, function () {
316
+ var zipPath;
317
+ return __generator(this, function (_a) {
318
+ switch (_a.label) {
319
+ case 0:
320
+ zipPath = path_1.default.join(tmpRoot, 'traversal.zip');
321
+ return [4 /*yield*/, makeZip([{ name: 'aa/evil.txt', content: 'pwned' }], zipPath)];
322
+ case 1:
323
+ _a.sent();
324
+ replaceEntryName(zipPath, 'aa/evil.txt', '../evil.txt');
325
+ return [4 /*yield*/, expect((0, zip_1.extractZip)(zipPath, destDir)).rejects.toThrow()
326
+ // destDir/../evil.txt resolves to tmpRoot/evil.txt
327
+ ];
328
+ case 2:
329
+ _a.sent();
330
+ // destDir/../evil.txt resolves to tmpRoot/evil.txt
331
+ expect(fs_1.default.existsSync(path_1.default.join(tmpRoot, 'evil.txt'))).toBeFalse();
332
+ return [2 /*return*/];
333
+ }
334
+ });
335
+ }); });
282
336
  });
283
337
  });
@@ -30,7 +30,6 @@ export declare function parseLogLines(logJson: LogJson): string;
30
30
  export declare function convertToSASStatements(json: MacroVars): string;
31
31
  export declare const arrToObj: (arr: any[]) => any;
32
32
  export declare const millisecondsToDdHhMmSs: (milliseconds: number) => string;
33
- export declare function checkNodeVersion(): void;
34
33
  export declare function getAdapterInstance(target: Target): SASjs;
35
34
  export declare function displaySasjsRunnerError(username: string): void;
36
35
  export declare const loadEnvVariables: (fileName: string) => Promise<void>;
@@ -106,7 +106,6 @@ exports.executeShellScript = executeShellScript;
106
106
  exports.executePowerShellScript = executePowerShellScript;
107
107
  exports.parseLogLines = parseLogLines;
108
108
  exports.convertToSASStatements = convertToSASStatements;
109
- exports.checkNodeVersion = checkNodeVersion;
110
109
  exports.getAdapterInstance = getAdapterInstance;
111
110
  exports.displaySasjsRunnerError = displaySasjsRunnerError;
112
111
  exports.prefixAppLoc = prefixAppLoc;
@@ -646,15 +645,6 @@ var millisecondsToDdHhMmSs = function (milliseconds) {
646
645
  return "".concat(days, " day(s); ").concat(hours, " hour(s); ").concat(minutes, " minute(s); ").concat(seconds, " second(s)");
647
646
  };
648
647
  exports.millisecondsToDdHhMmSs = millisecondsToDdHhMmSs;
649
- function checkNodeVersion() {
650
- var _a;
651
- var nodeVersion = process.versions.node;
652
- var majorVersion = parseInt(nodeVersion.substr(0, 2));
653
- if (majorVersion < 12) {
654
- (_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('SASjs CLI requires at least NodeJS version 12. Please upgrade NodeJS and try again.');
655
- process.exit(1);
656
- }
657
- }
658
648
  function getAdapterInstance(target) {
659
649
  if (!target) {
660
650
  throw new Error('Unable to create SASjs adapter instance: Invalid target.');
package/package.json CHANGED
@@ -2,15 +2,13 @@
2
2
  "name": "@sasjs/cli",
3
3
  "description": "Command line interface for SASjs",
4
4
  "engines": {
5
- "node": "^14.18.0 || >=16.10.0"
5
+ "node": ">=22"
6
6
  },
7
7
  "main": "index.js",
8
8
  "files": [
9
9
  "build/**/*"
10
10
  ],
11
11
  "scripts": {
12
- "nodeVersionMessage": "echo \u001b[33m make sure you are running node lts version \u001b[0m",
13
- "checkNodeVersion": "node -e \"console.log('\\u001b[36m', 'Checking your Node.js version...'); const v = process.versions && process.versions.node; const ok = v && (() => { const [m, n] = v.split('.').map(Number); if (m === 14) return n >= 18; if (m === 15) return false; if (m === 16) return n >= 10; return m > 16 })(); if (!ok) { console.log('\\u001b[31m', '\\u274c You must be running Node.js ^14.18.0 || >=16.10.0 to install SASjs CLI.\\nYour current version is ' + v + '.\\nPlease install a more recent version and try again.'); process.exit(1) } else { console.log('\\u001b[32m', '\\u2705 Node.js version check passed. Continuing...') } console.log('\\u001b[0m', '');\"",
14
12
  "start:ci": "npm run build && npm run set:permissions",
15
13
  "start": "nodemon --watch \"src/**/*\" --exec \"npm run build && npm run set:permissions\"",
16
14
  "set:permissions": "node -e \"require('fs').chmodSync('build/index.js', '755')\"",
@@ -71,16 +69,15 @@
71
69
  "registry": "https://registry.npmjs.org"
72
70
  },
73
71
  "dependencies": {
74
- "@sasjs/adapter": "4.19.2",
75
- "@sasjs/core": "5.3.0",
76
- "@sasjs/lint": "4.1.2",
77
- "@sasjs/utils": "3.6.5",
72
+ "@sasjs/adapter": "5.1.1",
73
+ "@sasjs/core": "5.5.2",
74
+ "@sasjs/lint": "4.1.4",
75
+ "@sasjs/utils": "4.0.1",
76
+ "axios": "1.20.0",
78
77
  "chalk": "4.1.2",
79
- "dotenv": "17.4.2",
80
- "js-base64": "3.9.3",
78
+ "dotenv": "18.0.8",
79
+ "js-base64": "3.9.4",
81
80
  "jsdom": "26.0.0",
82
- "jwt-decode": "3.1.2",
83
- "lodash.groupby": "4.6.0",
84
81
  "lodash.uniqby": "4.7.0",
85
82
  "node-graphviz": "0.1.1",
86
83
  "node-powershell": "5.0.1",
@@ -101,7 +98,6 @@
101
98
  "@types/find": "0.2.4",
102
99
  "@types/jest": "30.0.0",
103
100
  "@types/jsdom": "21.1.1",
104
- "@types/lodash.groupby": "4.6.9",
105
101
  "@types/lodash.uniqby": "4.7.9",
106
102
  "@types/node": "22.10.2",
107
103
  "@types/prompts": "2.4.9",
@@ -109,17 +105,16 @@
109
105
  "@types/xml": "1.0.11",
110
106
  "@types/yauzl": "3.4.0",
111
107
  "@types/yazl": "3.3.1",
112
- "babel-jest": "30.5.1",
108
+ "babel-jest": "30.5.2",
113
109
  "copyfiles": "2.4.1",
114
- "jest": "30.5.1",
115
- "jest-cli": "30.5.1",
116
- "jest-environment-jsdom": "30.5.1",
117
- "jest-extended": "4.0.2",
110
+ "jest": "30.5.2",
111
+ "jest-cli": "30.5.2",
112
+ "jest-environment-jsdom": "30.5.2",
113
+ "jest-extended": "7.0.0",
118
114
  "nodemon": "3.1.14",
119
- "prettier": "3.9.6",
120
- "ts-jest": "29.4.12",
115
+ "prettier": "3.9.10",
116
+ "ts-jest": "29.4.15",
121
117
  "ts-node": "10.9.2",
122
- "tslib": "2.8.1",
123
118
  "typescript": "5.9.3",
124
119
  "util": "0.12.5"
125
120
  },
@@ -127,5 +122,5 @@
127
122
  "ws": "8.21.0",
128
123
  "nanoid": "3.3.18"
129
124
  },
130
- "version": "4.20.9"
125
+ "version": "5.0.3"
131
126
  }