@sasjs/cli 4.17.5 → 4.18.1
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/build/commands/deploy/spec/cbd.spec.server.viya.js +2 -2
- package/build/commands/shared/deployToSasViyaWithServicePack.js +4 -5
- package/build/commands/shared/spec/deployToSasViyaWithServicePack.spec.d.ts +1 -0
- package/build/commands/shared/spec/deployToSasViyaWithServicePack.spec.js +146 -0
- package/build/utils/auth.d.ts +8 -0
- package/build/utils/auth.js +8 -0
- package/build/utils/config.d.ts +29 -1
- package/build/utils/config.js +29 -1
- package/package.json +4 -4
|
@@ -255,13 +255,13 @@ describe('sasjs cbd with Viya', function () {
|
|
|
255
255
|
return __generator(this, function (_a) {
|
|
256
256
|
switch (_a.label) {
|
|
257
257
|
case 0:
|
|
258
|
-
jest.spyOn(configUtils, '
|
|
258
|
+
jest.spyOn(configUtils, 'getAuthConfig').mockImplementation(function () {
|
|
259
259
|
return Promise.reject('Token error');
|
|
260
260
|
});
|
|
261
261
|
return [4 /*yield*/, (0, build_1.build)(target)];
|
|
262
262
|
case 1:
|
|
263
263
|
_a.sent();
|
|
264
|
-
return [4 /*yield*/, expect((0, deploy_1.deploy)(target, true)).rejects.toThrow(new Error("Deployment failed. Request is not authenticated.\
|
|
264
|
+
return [4 /*yield*/, expect((0, deploy_1.deploy)(target, true)).rejects.toThrow(new Error("Deployment failed. Request is not authenticated.\nToken error"))];
|
|
265
265
|
case 2:
|
|
266
266
|
_a.sent();
|
|
267
267
|
jest.restoreAllMocks();
|
|
@@ -55,12 +55,11 @@ function deployToSasViyaWithServicePack(jsonFilePath, target, isLocal, isForced)
|
|
|
55
55
|
throw new Error('Provided data file must be valid json.');
|
|
56
56
|
}
|
|
57
57
|
populateCodeInServicePack(jsonObject);
|
|
58
|
-
return [4 /*yield*/, (0, utils_2.
|
|
58
|
+
return [4 /*yield*/, (0, utils_2.getAuthConfig)(target).catch(function (err) {
|
|
59
|
+
throw new Error("Deployment failed. Request is not authenticated.\n".concat((err === null || err === void 0 ? void 0 : err.message) || err));
|
|
60
|
+
})];
|
|
59
61
|
case 2:
|
|
60
|
-
access_token = _a.sent();
|
|
61
|
-
if (!access_token) {
|
|
62
|
-
throw new Error("Deployment failed. Request is not authenticated.\nPlease add the following variables to your .env".concat(isLocal ? ".".concat(target.name) : '', " file:\nCLIENT, SECRET, ACCESS_TOKEN, REFRESH_TOKEN"));
|
|
63
|
-
}
|
|
62
|
+
access_token = (_a.sent()).access_token;
|
|
64
63
|
sasjs = (0, utils_2.getSASjs)(target);
|
|
65
64
|
return [4 /*yield*/, sasjs
|
|
66
65
|
.deployServicePack(jsonObject, undefined, undefined, access_token, isForced)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
35
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
36
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
37
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
38
|
+
function step(op) {
|
|
39
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
40
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
41
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
42
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
43
|
+
switch (op[0]) {
|
|
44
|
+
case 0: case 1: t = op; break;
|
|
45
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
46
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
47
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
48
|
+
default:
|
|
49
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
50
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
51
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
52
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
53
|
+
if (t[2]) _.ops.pop();
|
|
54
|
+
_.trys.pop(); continue;
|
|
55
|
+
}
|
|
56
|
+
op = body.call(thisArg, _);
|
|
57
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
58
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
62
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
63
|
+
};
|
|
64
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
65
|
+
var path_1 = __importDefault(require("path"));
|
|
66
|
+
var os_1 = __importDefault(require("os"));
|
|
67
|
+
var utils_1 = require("@sasjs/utils");
|
|
68
|
+
var configUtils = __importStar(require("../../../utils/config"));
|
|
69
|
+
var deployToSasViyaWithServicePack_1 = require("../deployToSasViyaWithServicePack");
|
|
70
|
+
var target = new utils_1.Target({
|
|
71
|
+
name: 'test',
|
|
72
|
+
appLoc: '/Public/test/',
|
|
73
|
+
serverType: utils_1.ServerType.SasViya,
|
|
74
|
+
serverUrl: 'https://server.com',
|
|
75
|
+
contextName: 'test context'
|
|
76
|
+
});
|
|
77
|
+
var mockAuthConfig = {
|
|
78
|
+
client: 'cl13nt',
|
|
79
|
+
secret: '53cr3t',
|
|
80
|
+
access_token: 'acc355',
|
|
81
|
+
refresh_token: 'r3fr35h'
|
|
82
|
+
};
|
|
83
|
+
describe('deployToSasViyaWithServicePack', function () {
|
|
84
|
+
var jsonFilePath;
|
|
85
|
+
var deployServicePack;
|
|
86
|
+
beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
87
|
+
return __generator(this, function (_a) {
|
|
88
|
+
switch (_a.label) {
|
|
89
|
+
case 0:
|
|
90
|
+
jsonFilePath = path_1.default.join(os_1.default.tmpdir(), "servicepack-".concat(Date.now(), ".json"));
|
|
91
|
+
return [4 /*yield*/, (0, utils_1.createFile)(jsonFilePath, JSON.stringify({ members: [] }))];
|
|
92
|
+
case 1:
|
|
93
|
+
_a.sent();
|
|
94
|
+
deployServicePack = jest.fn().mockResolvedValue({});
|
|
95
|
+
jest
|
|
96
|
+
.spyOn(configUtils, 'getSASjs')
|
|
97
|
+
.mockImplementation(function () { return ({ deployServicePack: deployServicePack }); });
|
|
98
|
+
return [2 /*return*/];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}); });
|
|
102
|
+
afterEach(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
103
|
+
return __generator(this, function (_a) {
|
|
104
|
+
switch (_a.label) {
|
|
105
|
+
case 0: return [4 /*yield*/, (0, utils_1.deleteFile)(jsonFilePath)];
|
|
106
|
+
case 1:
|
|
107
|
+
_a.sent();
|
|
108
|
+
jest.restoreAllMocks();
|
|
109
|
+
return [2 /*return*/];
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}); });
|
|
113
|
+
it('refreshes and persists the access token via getAuthConfig instead of the non-persisting getAccessToken', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
114
|
+
var getAuthConfigSpy;
|
|
115
|
+
return __generator(this, function (_a) {
|
|
116
|
+
switch (_a.label) {
|
|
117
|
+
case 0:
|
|
118
|
+
getAuthConfigSpy = jest
|
|
119
|
+
.spyOn(configUtils, 'getAuthConfig')
|
|
120
|
+
.mockImplementation(function () { return Promise.resolve(mockAuthConfig); });
|
|
121
|
+
return [4 /*yield*/, expect((0, deployToSasViyaWithServicePack_1.deployToSasViyaWithServicePack)(jsonFilePath, target, true, false)).toResolve()];
|
|
122
|
+
case 1:
|
|
123
|
+
_a.sent();
|
|
124
|
+
expect(getAuthConfigSpy).toHaveBeenCalledWith(target);
|
|
125
|
+
expect(deployServicePack).toHaveBeenCalledWith({ members: [] }, undefined, undefined, mockAuthConfig.access_token, false);
|
|
126
|
+
return [2 /*return*/];
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}); });
|
|
130
|
+
it('surfaces the real auth/refresh error instead of a generic "add these variables" message when credentials already exist', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
131
|
+
var realError;
|
|
132
|
+
return __generator(this, function (_a) {
|
|
133
|
+
switch (_a.label) {
|
|
134
|
+
case 0:
|
|
135
|
+
realError = new Error('invalid_grant: refresh token has already been used');
|
|
136
|
+
jest.spyOn(configUtils, 'getAuthConfig').mockImplementation(function () {
|
|
137
|
+
return Promise.reject(realError);
|
|
138
|
+
});
|
|
139
|
+
return [4 /*yield*/, expect((0, deployToSasViyaWithServicePack_1.deployToSasViyaWithServicePack)(jsonFilePath, target, true, false)).rejects.toThrow(/invalid_grant: refresh token has already been used/)];
|
|
140
|
+
case 1:
|
|
141
|
+
_a.sent();
|
|
142
|
+
return [2 /*return*/];
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}); });
|
|
146
|
+
});
|
package/build/utils/auth.d.ts
CHANGED
|
@@ -18,6 +18,14 @@ export declare function isAccessTokenExpiring(token: string): boolean;
|
|
|
18
18
|
* @param {string} token- token string that will be evaluated
|
|
19
19
|
*/
|
|
20
20
|
export declare function isRefreshTokenExpiring(token?: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Exchanges a refresh token for a new access/refresh token pair.
|
|
23
|
+
* SAS Viya's refresh tokens are single-use and rotate on every call: the
|
|
24
|
+
* `refresh_token` returned here supersedes the one passed in, which becomes
|
|
25
|
+
* invalid immediately. Callers must persist the returned pair (see
|
|
26
|
+
* `saveTokens` in config.ts) or a later refresh attempt with the old token
|
|
27
|
+
* will be rejected by the server.
|
|
28
|
+
*/
|
|
21
29
|
export declare function refreshTokens(sasjsInstance: SASjs, clientId: string, clientSecret: string, refreshToken: string): Promise<{
|
|
22
30
|
access_token: string;
|
|
23
31
|
refresh_token: string;
|
package/build/utils/auth.js
CHANGED
|
@@ -101,6 +101,14 @@ function isRefreshTokenExpiring(token) {
|
|
|
101
101
|
return timeToLive <= 30; // 30 seconds
|
|
102
102
|
}
|
|
103
103
|
exports.isRefreshTokenExpiring = isRefreshTokenExpiring;
|
|
104
|
+
/**
|
|
105
|
+
* Exchanges a refresh token for a new access/refresh token pair.
|
|
106
|
+
* SAS Viya's refresh tokens are single-use and rotate on every call: the
|
|
107
|
+
* `refresh_token` returned here supersedes the one passed in, which becomes
|
|
108
|
+
* invalid immediately. Callers must persist the returned pair (see
|
|
109
|
+
* `saveTokens` in config.ts) or a later refresh attempt with the old token
|
|
110
|
+
* will be rejected by the server.
|
|
111
|
+
*/
|
|
104
112
|
function refreshTokens(sasjsInstance, clientId, clientSecret, refreshToken) {
|
|
105
113
|
return __awaiter(this, void 0, void 0, function () {
|
|
106
114
|
var _a, access_token, refresh_token;
|
package/build/utils/config.d.ts
CHANGED
|
@@ -61,11 +61,29 @@ export declare function getStreamConfig(target?: Target): Promise<StreamConfig>;
|
|
|
61
61
|
export declare function sanitizeAppLoc(appLoc: string): string;
|
|
62
62
|
export declare function getProjectRoot(): Promise<string>;
|
|
63
63
|
/**
|
|
64
|
-
* Gets the auth config for the specified target
|
|
64
|
+
* Gets the auth config for the specified target, refreshing the access token
|
|
65
|
+
* (via the refresh token) if it is expiring.
|
|
66
|
+
*
|
|
67
|
+
* Unlike {@link getAccessToken}, this persists any newly issued access/refresh
|
|
68
|
+
* token pair via {@link saveTokens}. That persistence is not optional: SAS Viya
|
|
69
|
+
* issues rotating, single-use refresh tokens, so once a refresh token has been
|
|
70
|
+
* used to mint a new pair, the old one is void. If the new pair isn't written
|
|
71
|
+
* back to `.env.{target}` / the global config, the *next* CLI invocation reads
|
|
72
|
+
* the now-stale refresh token from disk and fails to authenticate - which is
|
|
73
|
+
* indistinguishable, from the user's point of view, from never having
|
|
74
|
+
* refreshed at all. This is why every command that needs an authenticated
|
|
75
|
+
* request should call `getAuthConfig`, not `getAccessToken`.
|
|
65
76
|
* @param {Target} target - the target to get an access token for.
|
|
66
77
|
* @returns {AuthConfig} - an object containing an access token, refresh token, client ID and secret.
|
|
67
78
|
*/
|
|
68
79
|
export declare function getAuthConfig(target: Target): Promise<AuthConfig>;
|
|
80
|
+
/**
|
|
81
|
+
* Persists an access/refresh token pair to wherever the target's credentials
|
|
82
|
+
* live: `.env.{targetName}` for a local target, or `authConfig` in the global
|
|
83
|
+
* `~/.sasjsrc` otherwise. Called by {@link getAuthConfig} after every refresh
|
|
84
|
+
* so that a rotated (single-use) refresh token from SAS Viya is never reused -
|
|
85
|
+
* reusing it on a subsequent CLI invocation would be rejected by the server.
|
|
86
|
+
*/
|
|
69
87
|
export declare const saveTokens: (targetName: string, client: string, secret: string, access_token: string, refresh_token: string) => Promise<void>;
|
|
70
88
|
/**
|
|
71
89
|
* Gets the auth config for the specified SAS9 target.
|
|
@@ -84,6 +102,16 @@ export declare function getAuthConfigSAS9(target: Target): AuthConfigSas9;
|
|
|
84
102
|
* If a refresh token is unavailable, we will use the client ID and secret
|
|
85
103
|
* to obtain a new access token. Manual intervention is required in this case
|
|
86
104
|
* to navigate to the URL shown and type in an authorization code.
|
|
105
|
+
*
|
|
106
|
+
* IMPORTANT: unlike {@link getAuthConfig}, this does NOT persist a refreshed
|
|
107
|
+
* access/refresh token pair back to disk - the new tokens only live for the
|
|
108
|
+
* lifetime of this process. Since SAS Viya refresh tokens are single-use and
|
|
109
|
+
* rotate on every refresh, calling this from a command whose CLI invocation
|
|
110
|
+
* ends shortly after (leaving the stale refresh token on disk) will cause the
|
|
111
|
+
* *next* invocation to fail to authenticate. Only use this where that's
|
|
112
|
+
* acceptable - e.g. one-shot, same-process cleanup calls such as
|
|
113
|
+
* `removeTestServerFolder` in test support code. Any user-facing command
|
|
114
|
+
* should use `getAuthConfig` instead.
|
|
87
115
|
* @param {object} target - the target to get an access token for.
|
|
88
116
|
* @param {string} checkIfExpiring - flag that indicates whether to do an expiry check.
|
|
89
117
|
*/
|
package/build/utils/config.js
CHANGED
|
@@ -790,7 +790,18 @@ function getProjectRoot() {
|
|
|
790
790
|
}
|
|
791
791
|
exports.getProjectRoot = getProjectRoot;
|
|
792
792
|
/**
|
|
793
|
-
* Gets the auth config for the specified target
|
|
793
|
+
* Gets the auth config for the specified target, refreshing the access token
|
|
794
|
+
* (via the refresh token) if it is expiring.
|
|
795
|
+
*
|
|
796
|
+
* Unlike {@link getAccessToken}, this persists any newly issued access/refresh
|
|
797
|
+
* token pair via {@link saveTokens}. That persistence is not optional: SAS Viya
|
|
798
|
+
* issues rotating, single-use refresh tokens, so once a refresh token has been
|
|
799
|
+
* used to mint a new pair, the old one is void. If the new pair isn't written
|
|
800
|
+
* back to `.env.{target}` / the global config, the *next* CLI invocation reads
|
|
801
|
+
* the now-stale refresh token from disk and fails to authenticate - which is
|
|
802
|
+
* indistinguishable, from the user's point of view, from never having
|
|
803
|
+
* refreshed at all. This is why every command that needs an authenticated
|
|
804
|
+
* request should call `getAuthConfig`, not `getAccessToken`.
|
|
794
805
|
* @param {Target} target - the target to get an access token for.
|
|
795
806
|
* @returns {AuthConfig} - an object containing an access token, refresh token, client ID and secret.
|
|
796
807
|
*/
|
|
@@ -874,6 +885,13 @@ function getAuthConfig(target) {
|
|
|
874
885
|
});
|
|
875
886
|
}
|
|
876
887
|
exports.getAuthConfig = getAuthConfig;
|
|
888
|
+
/**
|
|
889
|
+
* Persists an access/refresh token pair to wherever the target's credentials
|
|
890
|
+
* live: `.env.{targetName}` for a local target, or `authConfig` in the global
|
|
891
|
+
* `~/.sasjsrc` otherwise. Called by {@link getAuthConfig} after every refresh
|
|
892
|
+
* so that a rotated (single-use) refresh token from SAS Viya is never reused -
|
|
893
|
+
* reusing it on a subsequent CLI invocation would be rejected by the server.
|
|
894
|
+
*/
|
|
877
895
|
var saveTokens = function (targetName, client, secret, access_token, refresh_token) { return __awaiter(void 0, void 0, void 0, function () {
|
|
878
896
|
var isLocalTarget, VERBOSE, envFileContent, envFilePath, globalConfig, target, targetJson;
|
|
879
897
|
var _a, _b, _c;
|
|
@@ -952,6 +970,16 @@ exports.getAuthConfigSAS9 = getAuthConfigSAS9;
|
|
|
952
970
|
* If a refresh token is unavailable, we will use the client ID and secret
|
|
953
971
|
* to obtain a new access token. Manual intervention is required in this case
|
|
954
972
|
* to navigate to the URL shown and type in an authorization code.
|
|
973
|
+
*
|
|
974
|
+
* IMPORTANT: unlike {@link getAuthConfig}, this does NOT persist a refreshed
|
|
975
|
+
* access/refresh token pair back to disk - the new tokens only live for the
|
|
976
|
+
* lifetime of this process. Since SAS Viya refresh tokens are single-use and
|
|
977
|
+
* rotate on every refresh, calling this from a command whose CLI invocation
|
|
978
|
+
* ends shortly after (leaving the stale refresh token on disk) will cause the
|
|
979
|
+
* *next* invocation to fail to authenticate. Only use this where that's
|
|
980
|
+
* acceptable - e.g. one-shot, same-process cleanup calls such as
|
|
981
|
+
* `removeTestServerFolder` in test support code. Any user-facing command
|
|
982
|
+
* should use `getAuthConfig` instead.
|
|
955
983
|
* @param {object} target - the target to get an access token for.
|
|
956
984
|
* @param {string} checkIfExpiring - flag that indicates whether to do an expiry check.
|
|
957
985
|
*/
|
package/package.json
CHANGED
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"registry": "https://registry.npmjs.org"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@sasjs/adapter": "4.17.
|
|
73
|
-
"@sasjs/core": "4.67.
|
|
72
|
+
"@sasjs/adapter": "4.17.1",
|
|
73
|
+
"@sasjs/core": "4.67.3",
|
|
74
74
|
"@sasjs/lint": "2.4.3",
|
|
75
75
|
"@sasjs/utils": "3.5.8",
|
|
76
76
|
"adm-zip": "0.5.10",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"util": "0.12.4"
|
|
121
121
|
},
|
|
122
122
|
"overrides": {
|
|
123
|
-
"ws": "8.
|
|
123
|
+
"ws": "8.21.0"
|
|
124
124
|
},
|
|
125
|
-
"version": "4.
|
|
125
|
+
"version": "4.18.1"
|
|
126
126
|
}
|