@sap/cds-dk 4.4.2 → 4.5.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.
- package/CHANGELOG.md +24 -0
- package/bin/activate.js +1 -2
- package/bin/deploy/index.js +1 -1
- package/bin/deploy/to-sqlite.js +2 -2
- package/bin/extend.js +2 -4
- package/bin/login.js +7 -8
- package/bin/logout.js +1 -2
- package/bin/repl.js +1 -1
- package/bin/watch.js +12 -8
- package/lib/client/activate.js +243 -0
- package/lib/client/auth_manager.js +115 -0
- package/lib/client/base_command.js +55 -0
- package/lib/client/extend.js +179 -0
- package/lib/client/helper/cf_helper.js +186 -0
- package/lib/client/helper/dependency_helper.js +26 -0
- package/lib/client/helper/env_helper.js +4 -0
- package/lib/client/helper/fs_helper.js +83 -0
- package/lib/client/helper/logging.js +37 -0
- package/lib/client/helper/question.js +50 -0
- package/lib/client/helper/string_helper.js +53 -0
- package/lib/client/helper/url_helper.js +17 -0
- package/lib/client/index.js +14 -0
- package/lib/client/params.js +435 -0
- package/lib/client/settings_manager.js +526 -0
- package/lib/import/index.js +5 -18
- package/lib/import/v2parser.js +282 -27
- package/lib/import/v4parser.js +341 -54
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +13 -10
- package/lib/init/template/data/index.js +38 -46
- package/lib/init/template/hana/index.js +2 -2
- package/lib/init/template/lint/index.js +8 -6
- package/lib/init/template/mta/projectReader.js +8 -5
- package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -1
- package/lib/init/template/nodejs/index.js +1 -8
- package/lib/lint/Linter.js +62 -60
- package/lib/lint/io.js +9 -33
- package/npm-shrinkwrap.json +7684 -12883
- package/package.json +4 -5
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
const Logger = require('./helper/logging');
|
|
2
|
+
const { escapeRegex } = require('./helper/string_helper');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// PARAM BASE TYPES
|
|
6
|
+
|
|
7
|
+
const TokenStorage = Object.freeze({
|
|
8
|
+
plain: 0,
|
|
9
|
+
keyring: 1
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const Persistence = Object.freeze({
|
|
13
|
+
none: 0,
|
|
14
|
+
setting: 1,
|
|
15
|
+
auth: 2
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const ObfuscationLevel = Object.freeze({
|
|
19
|
+
none: 0, // Keep this at zero always!
|
|
20
|
+
full: 1,
|
|
21
|
+
partial: 2 // Allows a few start and end characters displayed in DEBUG output; only use with string-type params
|
|
22
|
+
});
|
|
23
|
+
const DISCLOSURE_LENGTH = 3;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parameters hold a name and a value and can only be set to compatible values.
|
|
27
|
+
*/
|
|
28
|
+
class Param {
|
|
29
|
+
/**
|
|
30
|
+
* Constructor.
|
|
31
|
+
* @param name the name
|
|
32
|
+
* @param val the initial value
|
|
33
|
+
* @param options constructor options
|
|
34
|
+
*/
|
|
35
|
+
constructor(name,
|
|
36
|
+
val,
|
|
37
|
+
options = {}) {
|
|
38
|
+
|
|
39
|
+
this.persist = Persistence.none;
|
|
40
|
+
this.obfuscate = ObfuscationLevel.none;
|
|
41
|
+
this.abbreviate = false;
|
|
42
|
+
this.internal = false;
|
|
43
|
+
this.allowedValues = null;
|
|
44
|
+
|
|
45
|
+
this.name = name;
|
|
46
|
+
this._val = val;
|
|
47
|
+
this.options = options;
|
|
48
|
+
Object.entries(options).forEach(([key, val]) => this[key] = val);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the name of the main param (as opposed to an alias).
|
|
53
|
+
*/
|
|
54
|
+
get mainName() {
|
|
55
|
+
return this.name;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Using Proxy to achieve aliasing by routing all but certain properties through to main param.
|
|
60
|
+
* @param name the alias name
|
|
61
|
+
*/
|
|
62
|
+
newAlias(name) {
|
|
63
|
+
return new Proxy(this, {
|
|
64
|
+
get(param, prop, alias) {
|
|
65
|
+
switch (prop) {
|
|
66
|
+
case 'clone':
|
|
67
|
+
return (val) => (val ? param.clone(val) : param).newAlias(alias.name);
|
|
68
|
+
case 'name':
|
|
69
|
+
return name;
|
|
70
|
+
default:
|
|
71
|
+
return param[prop];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set value.
|
|
79
|
+
* @param newVal the new value
|
|
80
|
+
*/
|
|
81
|
+
set val(newVal) {
|
|
82
|
+
this.validate(newVal);
|
|
83
|
+
this._val = newVal;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get value.
|
|
88
|
+
*/
|
|
89
|
+
get val() {
|
|
90
|
+
return this._val;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate the param value.
|
|
95
|
+
* @param val the value in question
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
validate(val) {
|
|
99
|
+
if (val === undefined) {
|
|
100
|
+
return; // Allow `undefined` always.
|
|
101
|
+
}
|
|
102
|
+
let expectedType = typeof this.val;
|
|
103
|
+
if (expectedType === 'undefined' && this.allowedValues) {
|
|
104
|
+
expectedType = typeof this.allowedValues[0];
|
|
105
|
+
}
|
|
106
|
+
if (typeof val !== expectedType) {
|
|
107
|
+
throw new Error(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`);
|
|
108
|
+
}
|
|
109
|
+
if (this.allowedValues && !(this.allowedValues).includes(val)) {
|
|
110
|
+
throw new Error(`Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Transform the value to make it suitable for output, i.e. return:
|
|
116
|
+
* - `undefined` for internal params (making them hidden)
|
|
117
|
+
* - `'...'` for (parts of) obfuscated or (when debug output is off) abbreviated params
|
|
118
|
+
* - `this.val` for all others
|
|
119
|
+
*/
|
|
120
|
+
format() {
|
|
121
|
+
return this.internal
|
|
122
|
+
? undefined
|
|
123
|
+
: this.obfuscate === ObfuscationLevel.none && (!this.abbreviate || process.env.DEBUG)
|
|
124
|
+
? this.val
|
|
125
|
+
: this.obfuscate === ObfuscationLevel.partial && process.env.DEBUG && typeof this.val === 'string'
|
|
126
|
+
? this.val.substr(0, DISCLOSURE_LENGTH) + '...' + this.val.substr(-DISCLOSURE_LENGTH)
|
|
127
|
+
: '...';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Return a new instance of this class with the same properties as the current object.
|
|
132
|
+
* @param val used as value if given
|
|
133
|
+
*/
|
|
134
|
+
clone(val = this.val) {
|
|
135
|
+
this.validate(val);
|
|
136
|
+
return new Param(this.name, val, this.options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Prevent this being changed.
|
|
141
|
+
*/
|
|
142
|
+
freeze() {
|
|
143
|
+
Object.freeze(this._val);
|
|
144
|
+
Object.freeze(this);
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// type ParamType<P> = P extends Param<infer U> ? U : never;
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
// CONCRETE PARAMS
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The known params and their default values, restricted to those needed by the app as a whole.
|
|
156
|
+
*/
|
|
157
|
+
class BasicParams {
|
|
158
|
+
constructor() {
|
|
159
|
+
// NON-PERSISTED PARAMS
|
|
160
|
+
// - general
|
|
161
|
+
this.projectFolder = new Param('projectFolder', '');
|
|
162
|
+
this.directory = this.projectFolder.newAlias('directory');
|
|
163
|
+
|
|
164
|
+
// - auth-related
|
|
165
|
+
this.passcode = new Param('passcode', '', { obfuscate: ObfuscationLevel.partial });
|
|
166
|
+
this.tokenStorage = new Param('tokenStorage', undefined, { allowedValues: ['plain', 'keyring'] });
|
|
167
|
+
this.plain = new Param('plain', false, { internal: true }); // for backward compatibility - ignored
|
|
168
|
+
this.renewLogin = new Param('renewLogin', false);
|
|
169
|
+
this.username = new Param('username', '');
|
|
170
|
+
this.password = new Param('password', '', { obfuscate: ObfuscationLevel.full });
|
|
171
|
+
this.clientid = new Param('clientid', '');
|
|
172
|
+
this.clientsecret = new Param('clientsecret', '', { obfuscate: ObfuscationLevel.partial });
|
|
173
|
+
|
|
174
|
+
// PERSISTED PARAMS (SETTINGS)
|
|
175
|
+
this.appUrl = new Param('appUrl', '', { persist: Persistence.setting });
|
|
176
|
+
this.to = this.appUrl.newAlias('to');
|
|
177
|
+
this.url = this.appUrl.newAlias('url');
|
|
178
|
+
this.subdomain = new Param('subdomain', '', { persist: Persistence.setting });
|
|
179
|
+
this.passcodeUrl = new Param('passcodeUrl', '', { persist: Persistence.setting, abbreviate: true });
|
|
180
|
+
this.passcode_url = this.passcodeUrl.newAlias('passcode_url');
|
|
181
|
+
this.tokenUrl = new Param('tokenUrl', '', { persist: Persistence.setting, abbreviate: true })
|
|
182
|
+
|
|
183
|
+
// PERSISTED AUTH PARAMS
|
|
184
|
+
this.token = new Param('token', '', { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
|
|
185
|
+
this.tokenExpirationDate = new Param('tokenExpirationDate', Number.MAX_SAFE_INTEGER, { persist: Persistence.auth });
|
|
186
|
+
this.refreshToken = new Param('refreshToken', '', { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
|
|
187
|
+
|
|
188
|
+
// INTERNAL PARAMS (HIDDEN FROM USER, COMPUTED)
|
|
189
|
+
this.reqAuth = new Param('reqAuth', null, { obfuscate: ObfuscationLevel.full, internal: true });
|
|
190
|
+
this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* All known params including those needed for specific commands.
|
|
196
|
+
*/
|
|
197
|
+
class AllParams extends BasicParams {
|
|
198
|
+
constructor() {
|
|
199
|
+
super();
|
|
200
|
+
|
|
201
|
+
// `extend` PARAMS
|
|
202
|
+
this.force = new Param('force', false);
|
|
203
|
+
this.templates = new Param('templates', false);
|
|
204
|
+
|
|
205
|
+
// `activate` PARAMS
|
|
206
|
+
this.undeploy = new Param('undeploy', false);
|
|
207
|
+
this.wsdl = new Param('wsdl', false);
|
|
208
|
+
|
|
209
|
+
// `logout` PARAMS
|
|
210
|
+
this.deleteSettings = new Param('deleteSettings', false);
|
|
211
|
+
this["delete-settings"] = this.deleteSettings.newAlias('delete-settings');
|
|
212
|
+
this.clearInvalid = new Param('clearInvalid', false);
|
|
213
|
+
this["clear-invalid"] = this.clearInvalid.newAlias('clear-invalid');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const ParamDefs = Object.freeze(new AllParams());
|
|
218
|
+
|
|
219
|
+
// PARAM COLLECTIONS
|
|
220
|
+
|
|
221
|
+
function collect(values, param) {
|
|
222
|
+
return Object.assign(values, {[param.name]: param.val});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A bundle of params with values. There are no requirements on the number of params contained.
|
|
227
|
+
*/
|
|
228
|
+
class ParamCollection {
|
|
229
|
+
constructor(paramValues = {}) {
|
|
230
|
+
this.params = {};
|
|
231
|
+
this.isFrozen = false;
|
|
232
|
+
|
|
233
|
+
for (const [name, val] of Object.entries(paramValues)) {
|
|
234
|
+
if (val != null) {
|
|
235
|
+
this.set(name, val);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Throw error if this is frozen.
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
checkFrozen() {
|
|
245
|
+
if (this.isFrozen) {
|
|
246
|
+
throw new Error('cannot modify read-only parameter collection');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Create a new frozen `ParamCollection`.
|
|
252
|
+
* @param paramValues the param values to set
|
|
253
|
+
*/
|
|
254
|
+
static frozen(paramValues = {}) {
|
|
255
|
+
const paramCollection = new ParamCollection(paramValues);
|
|
256
|
+
paramCollection.freeze();
|
|
257
|
+
return paramCollection;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get the name of a main param (as opposed to an alias).
|
|
262
|
+
* @param name the param in question
|
|
263
|
+
*/
|
|
264
|
+
static mainName(name) {
|
|
265
|
+
const paramDef = ParamDefs[name];
|
|
266
|
+
if (!paramDef) {
|
|
267
|
+
throw new Error(`Unknown parameter: '${name}'`);
|
|
268
|
+
}
|
|
269
|
+
return paramDef.mainName;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Whether this includes the given param.
|
|
274
|
+
* @param name the param in question
|
|
275
|
+
*/
|
|
276
|
+
has(name) {
|
|
277
|
+
return name in this.params;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Return the value of the given param.
|
|
282
|
+
* @param name the param in question
|
|
283
|
+
*/
|
|
284
|
+
get(name) {
|
|
285
|
+
const param = this.getParam(name);
|
|
286
|
+
return param && param.val;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Return the `Param` object for the given param.
|
|
291
|
+
* @param name the param in question
|
|
292
|
+
*/
|
|
293
|
+
getParam(name) {
|
|
294
|
+
return this.params[name] || ParamDefs[name];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Set a value for the given param. Will add the param to this collection unless already included.
|
|
299
|
+
* @param name the param in question
|
|
300
|
+
* @param val the value to set
|
|
301
|
+
*/
|
|
302
|
+
set(name, val) {
|
|
303
|
+
this.checkFrozen();
|
|
304
|
+
const mainName = ParamCollection.mainName(name);
|
|
305
|
+
if (this.has(mainName)) {
|
|
306
|
+
this.getParam(name).val = val;
|
|
307
|
+
} else {
|
|
308
|
+
this.params[mainName] = ParamDefs[mainName].clone(val);
|
|
309
|
+
}
|
|
310
|
+
if (mainName !== name) {
|
|
311
|
+
Logger.debug(`Migrated setting '${name}' to new key '${mainName}'`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Delete the given param from this collection.
|
|
317
|
+
* @param name the param in question
|
|
318
|
+
*/
|
|
319
|
+
delete(name) {
|
|
320
|
+
this.checkFrozen();
|
|
321
|
+
delete this.params[name];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Prevent this collection and all included params from being changed.
|
|
326
|
+
*/
|
|
327
|
+
freeze() {
|
|
328
|
+
this.toArray().forEach(param => param.freeze());
|
|
329
|
+
this.isFrozen = true;
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Add all params contained in another collection to this.
|
|
335
|
+
* @param params the other `ParamCollection`
|
|
336
|
+
*/
|
|
337
|
+
merge(params) {
|
|
338
|
+
this.checkFrozen();
|
|
339
|
+
for (const param of params) {
|
|
340
|
+
this.set(param.name, param.val);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Iterate through all contained param objects.
|
|
346
|
+
*/
|
|
347
|
+
*[Symbol.iterator]() {
|
|
348
|
+
for (const param of Object.values(this.params)) {
|
|
349
|
+
yield param;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Return a new array from all contained param objects.
|
|
355
|
+
*/
|
|
356
|
+
toArray() {
|
|
357
|
+
return Array.from(this);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get the number of contained params.
|
|
362
|
+
*/
|
|
363
|
+
get size() {
|
|
364
|
+
return Object.keys(this.params).length;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Return a new `ParamCollection` containing clones of all params contained in this collection.
|
|
369
|
+
*/
|
|
370
|
+
clone() {
|
|
371
|
+
const clonedParams = this.toArray()
|
|
372
|
+
.map(param => param.clone())
|
|
373
|
+
.reduce(collect, {});
|
|
374
|
+
return new ParamCollection(clonedParams);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Return an object mapping each of the contained params' names to its corresponding value.
|
|
379
|
+
* @param persistence the type of persistence to filter for (omit to disable filtering)
|
|
380
|
+
*/
|
|
381
|
+
toValueMap(persistence) {
|
|
382
|
+
return this.toArray()
|
|
383
|
+
.filter(param => param.persist === persistence || persistence === undefined)
|
|
384
|
+
.reduce(collect, {});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Return an object mapping each of the contained params' names to its corresponding value.
|
|
389
|
+
* Used by `JSON.stringify`.
|
|
390
|
+
*/
|
|
391
|
+
toJSON() {
|
|
392
|
+
return this.toValueMap();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Format this as a string. Omits `undefined` values.
|
|
397
|
+
* @param actualValues whether to include all actual param values, irrespective of any transformations normally
|
|
398
|
+
* applied (e.g. value obfuscations)
|
|
399
|
+
*/
|
|
400
|
+
format(actualValues = false) {
|
|
401
|
+
return this.toArray()
|
|
402
|
+
.reduce((result, param) => {
|
|
403
|
+
const val = actualValues ? param.val : param.format();
|
|
404
|
+
if (val !== undefined) {
|
|
405
|
+
result += `${param.name}: ${JSON.stringify(val)}, `;
|
|
406
|
+
}
|
|
407
|
+
return result;
|
|
408
|
+
}, '{ ')
|
|
409
|
+
.replace(/(,|(?<={)) $/, ' }');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Replace all occurrences of values of those params that are to be obfuscated with their (partly) obfuscated versions.
|
|
414
|
+
* Replacement applies only to values of query parameters.
|
|
415
|
+
* @param url the URL whose query parameters to replace
|
|
416
|
+
*/
|
|
417
|
+
obfuscateQueryParams(url) {
|
|
418
|
+
function reqParam(value) {
|
|
419
|
+
return new RegExp(`(?<==)${escapeRegex(value)}(?=&|$)`, 'g');
|
|
420
|
+
}
|
|
421
|
+
return this.toArray()
|
|
422
|
+
.filter(param => param.obfuscate)
|
|
423
|
+
.reduce((result, param) => result.replace(reqParam(param.val), param.format()), url);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
}
|
|
427
|
+
ParamCollection.prototype.toString = ParamCollection.prototype.format;
|
|
428
|
+
|
|
429
|
+
module.exports = {
|
|
430
|
+
Param,
|
|
431
|
+
ParamCollection,
|
|
432
|
+
Persistence,
|
|
433
|
+
TokenStorage,
|
|
434
|
+
ObfuscationLevel
|
|
435
|
+
}
|