@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/bin/activate.js +1 -2
  3. package/bin/deploy/index.js +1 -1
  4. package/bin/deploy/to-sqlite.js +2 -2
  5. package/bin/extend.js +2 -4
  6. package/bin/login.js +7 -8
  7. package/bin/logout.js +1 -2
  8. package/bin/repl.js +1 -1
  9. package/bin/watch.js +12 -8
  10. package/lib/client/activate.js +243 -0
  11. package/lib/client/auth_manager.js +115 -0
  12. package/lib/client/base_command.js +55 -0
  13. package/lib/client/extend.js +179 -0
  14. package/lib/client/helper/cf_helper.js +186 -0
  15. package/lib/client/helper/dependency_helper.js +26 -0
  16. package/lib/client/helper/env_helper.js +4 -0
  17. package/lib/client/helper/fs_helper.js +83 -0
  18. package/lib/client/helper/logging.js +37 -0
  19. package/lib/client/helper/question.js +50 -0
  20. package/lib/client/helper/string_helper.js +53 -0
  21. package/lib/client/helper/url_helper.js +17 -0
  22. package/lib/client/index.js +14 -0
  23. package/lib/client/params.js +435 -0
  24. package/lib/client/settings_manager.js +526 -0
  25. package/lib/import/index.js +5 -18
  26. package/lib/import/v2parser.js +282 -27
  27. package/lib/import/v4parser.js +341 -54
  28. package/lib/init/constants.js +1 -1
  29. package/lib/init/index.js +13 -10
  30. package/lib/init/template/data/index.js +38 -46
  31. package/lib/init/template/hana/index.js +2 -2
  32. package/lib/init/template/lint/index.js +8 -6
  33. package/lib/init/template/mta/projectReader.js +8 -5
  34. package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -1
  35. package/lib/init/template/nodejs/index.js +1 -8
  36. package/lib/lint/Linter.js +62 -60
  37. package/lib/lint/io.js +9 -33
  38. package/npm-shrinkwrap.json +7684 -12883
  39. 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
+ }