@sap/cds-compiler 4.8.0 → 4.9.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 (92) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +14 -1
  6. package/doc/CHANGELOG_BETA.md +19 -0
  7. package/lib/api/main.js +59 -24
  8. package/lib/api/options.js +12 -1
  9. package/lib/api/validate.js +1 -5
  10. package/lib/base/builtins.js +27 -0
  11. package/lib/base/message-registry.js +32 -19
  12. package/lib/base/messages.js +50 -19
  13. package/lib/base/model.js +4 -5
  14. package/lib/checks/actionsFunctions.js +2 -2
  15. package/lib/checks/annotationsOData.js +3 -0
  16. package/lib/checks/defaultValues.js +5 -2
  17. package/lib/checks/queryNoDbArtifacts.js +3 -2
  18. package/lib/checks/validator.js +2 -34
  19. package/lib/compiler/assert-consistency.js +8 -2
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +34 -22
  22. package/lib/compiler/extend.js +33 -10
  23. package/lib/compiler/index.js +0 -1
  24. package/lib/compiler/lsp-api.js +5 -0
  25. package/lib/compiler/propagator.js +21 -18
  26. package/lib/compiler/resolve.js +44 -28
  27. package/lib/compiler/shared.js +60 -20
  28. package/lib/compiler/tweak-assocs.js +13 -88
  29. package/lib/compiler/xpr-rewrite.js +689 -0
  30. package/lib/edm/annotations/genericTranslation.js +80 -60
  31. package/lib/edm/edm.js +4 -4
  32. package/lib/edm/edmInboundChecks.js +33 -0
  33. package/lib/edm/edmPreprocessor.js +9 -6
  34. package/lib/gen/Dictionary.json +129 -14
  35. package/lib/gen/language.checksum +1 -1
  36. package/lib/gen/language.interp +1 -1
  37. package/lib/gen/languageParser.js +1523 -1518
  38. package/lib/json/from-csn.js +13 -4
  39. package/lib/json/to-csn.js +10 -11
  40. package/lib/language/genericAntlrParser.js +14 -6
  41. package/lib/main.d.ts +67 -14
  42. package/lib/main.js +1 -0
  43. package/lib/model/cloneCsn.js +6 -3
  44. package/lib/model/csnRefs.js +12 -7
  45. package/lib/model/csnUtils.js +13 -7
  46. package/lib/model/enrichCsn.js +3 -1
  47. package/lib/model/revealInternalProperties.js +2 -1
  48. package/lib/model/sortViews.js +14 -6
  49. package/lib/modelCompare/compare.js +33 -34
  50. package/lib/optionProcessor.js +27 -2
  51. package/lib/render/DuplicateChecker.js +6 -6
  52. package/lib/render/manageConstraints.js +1 -0
  53. package/lib/render/toCdl.js +3 -1
  54. package/lib/transform/db/applyTransformations.js +33 -0
  55. package/lib/transform/db/constraints.js +1 -1
  56. package/lib/transform/db/expansion.js +8 -3
  57. package/lib/transform/db/groupByOrderBy.js +2 -2
  58. package/lib/transform/db/temporal.js +6 -3
  59. package/lib/transform/db/transformExists.js +2 -2
  60. package/lib/transform/effective/annotations.js +194 -0
  61. package/lib/transform/effective/main.js +6 -8
  62. package/lib/transform/effective/misc.js +31 -10
  63. package/lib/transform/forOdata.js +23 -7
  64. package/lib/transform/forRelationalDB.js +1 -1
  65. package/lib/transform/localized.js +7 -6
  66. package/lib/transform/odata/flattening.js +189 -106
  67. package/lib/transform/odata/toFinalBaseType.js +1 -1
  68. package/lib/transform/odata/typesExposure.js +15 -12
  69. package/lib/transform/parseExpr.js +4 -4
  70. package/lib/transform/transformUtils.js +40 -37
  71. package/lib/transform/translateAssocsToJoins.js +47 -47
  72. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  73. package/package.json +1 -1
  74. package/share/messages/anno-missing-rewrite.md +45 -0
  75. package/share/messages/message-explanations.json +1 -0
  76. package/bin/.eslintrc.json +0 -17
  77. package/lib/api/.eslintrc.json +0 -37
  78. package/lib/checks/.eslintrc.json +0 -31
  79. package/lib/compiler/.eslintrc.json +0 -8
  80. package/lib/edm/.eslintrc.json +0 -46
  81. package/lib/inspect/.eslintrc.json +0 -4
  82. package/lib/json/.eslintrc.json +0 -4
  83. package/lib/language/.eslintrc.json +0 -4
  84. package/lib/model/.eslintrc.json +0 -13
  85. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  86. package/lib/render/.eslintrc.json +0 -22
  87. package/lib/transform/.eslintrc.json +0 -13
  88. package/lib/transform/db/.eslintrc.json +0 -41
  89. package/lib/transform/draft/.eslintrc.json +0 -4
  90. package/lib/transform/effective/.eslintrc.json +0 -4
  91. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  92. package/lib/utils/.eslintrc.json +0 -7
package/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 4.9.0 - 2024-04-25
11
+
12
+ ### Added
13
+
14
+ - compiler: Annotations with expressions are now rewritten when propagated.
15
+ - for.seal: Added API function that produces a CSN for SEAL.
16
+ - for.odata/to.edm(x): Support annotation path expressions including path flattening.
17
+
18
+ ### Changed
19
+
20
+ - parser: A space between `.` and `*`/`{` (nested projections) is now a warning.
21
+ Use `bin/cds_remove_invalid_whitespace.js` to automatically fix this warning.
22
+ - compiler:
23
+ + Published compositions with filters are changed to associations.
24
+ + Expressions as annotation values are rejected for few known annotations that don't expect them.
25
+ - Update OData vocabularies: 'Aggregation', 'Capabilities', 'Common', 'Hierarchy', 'PersonalData', 'Session', 'UI'.
26
+ - to.edm(x): Exposed anonymous parameter types are now prefixed with `ap`, `bap` and `ep` for actions, bound actions and entities.
27
+
28
+ ### Fixed
29
+
30
+ - compiler:
31
+ + Deprecated `$parameters` is no longer proposed in code completion.
32
+ + Duplicate mixin definitions lead to failing name resolution.
33
+ - to.cdl: Types were always rendered for associations with filters, even if it would lead to a compilation failure.
34
+ - to.edm(x):
35
+ + Fix a recursion bug in entity parameter handling.
36
+ + Fix event exclusion in service preprocessing.
10
37
 
11
38
  ## Version 4.8.0 - 2024-03-21
12
39
 
@@ -39,9 +66,6 @@ The compiler behavior concerning `beta` features can change at any time without
39
66
  + Turn types and aspects into dummies to reduce CSN size.
40
67
  + Correctly detect a removed `.default` and forcefully set the default to `null`.
41
68
 
42
- ### Removed
43
-
44
-
45
69
  ## Version 4.7.6 - 2024-02-29
46
70
 
47
71
  ### Fixed
@@ -83,7 +107,8 @@ The compiler behavior concerning `beta` features can change at any time without
83
107
  - compiler: published associations with filters sometimes had the filter applied twice
84
108
  if used in inline aspect compositions
85
109
  - to.sql|hdi|hdbcds[.migration]:
86
- + With `withHanaAssociations`: `false`, remove the association elements from the final CSN in order to correctly detect them during migration scenarios and
110
+ + With `withHanaAssociations`: `false`, remove the association elements from the final CSN in order
111
+ to correctly detect them during migration scenarios and
87
112
  with generated `hdbcds`.
88
113
  + Skip expensive processing (for calculated elements and nested projections) if the model doesn't use it.
89
114
  + Don't greedily set alias on subqueries if not required.
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Remove whitespace where the compiler does not expect it and will
4
+ // emit an error in the next major version.
5
+ //
6
+ // This script removes whitespace where necessary.
7
+ // It does not remove comments, even though they count as whitespace.
8
+ //
9
+ // Example CDS:
10
+ // entity P as projection on E { s. * };
11
+ // will become:
12
+ // entity P as projection on E { s.* };
13
+ //
14
+ // Usage:
15
+ // ./cds_remove_invalid_whitespace.js my_file.cds
16
+ //
17
+ // If you want to update all files in a directory, you can use
18
+ // this shell script:
19
+ // find . -type f -iname '*.cds' -exec ./cds_remove_invalid_whitespace.js {} \;
20
+ //
21
+ // Note that you need to update the path to this script in the commands above.
22
+ //
23
+
24
+ 'use strict';
25
+
26
+ const parseLanguage = require('../lib/language/antlrParser');
27
+ const { createMessageFunctions } = require('../lib/base/messages');
28
+
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+
32
+ const cliArgs = process.argv.slice(2);
33
+ const filepath = cliArgs[0];
34
+
35
+ if (filepath === '--help' || filepath === '-h')
36
+ exitError();
37
+
38
+ if (cliArgs.length !== 1)
39
+ exitError(`Expected exactly one argument, ${cliArgs.length} given`);
40
+
41
+ if (!filepath)
42
+ exitError('Expected non-empty filepath as argument!');
43
+
44
+ // Do not use allow-list approach.
45
+ // There may be CDS files with other extensions than `.cds`.
46
+ if (filepath.endsWith('.csn') || filepath.endsWith('.json'))
47
+ exitError('Only CDS files can be passed! Found CSN file!');
48
+
49
+ const sourceStr = fs.readFileSync(filepath, 'utf-8');
50
+ const newSourceStr = modernizeWhitespace(sourceStr, filepath);
51
+ if (newSourceStr !== sourceStr)
52
+ fs.writeFileSync(filepath, newSourceStr);
53
+ process.exit(0); // success
54
+
55
+ // --------------------------------------------------------
56
+
57
+ function modernizeWhitespace( source, filename ) {
58
+ const options = { messages: [], attachTokens: true };
59
+ const messageFunctions = createMessageFunctions( options, 'parse', null );
60
+
61
+ // parseLanguage does not throw on CompilationError, so
62
+ // we do not need a try...catch block.
63
+ const ast = parseLanguage(source, filename, options, messageFunctions);
64
+
65
+ // To avoid spam, only report errors.
66
+ // Users should use the compiler to get all messages.
67
+ const errors = options.messages.filter(msg => (msg.severity === 'Error'));
68
+ if (errors.length > 0) {
69
+ errors.forEach((msg) => {
70
+ console.error(msg.toString());
71
+ });
72
+ console.error(`Found ${errors.length} errors! \n`);
73
+ exitError('The CDS parser emitted errors. Fix them first and try again.');
74
+ }
75
+
76
+ if (!ast.artifacts)
77
+ return source;
78
+
79
+ let currentOffset = 0;
80
+
81
+ const { tokens } = ast.tokenStream;
82
+ for (let i = 0; i < tokens.length; ++i) {
83
+ const token = tokens[i];
84
+ const next = tokens[i + 1];
85
+ if (next && token.type === ast.tokenStream.DOTbeforeBRACE &&
86
+ (next.type === ast.tokenStream.ASTERISK || next.type === ast.tokenStream.BRACE))
87
+ updateWhitespace(token, next);
88
+ }
89
+
90
+ return source;
91
+
92
+ // -----------------------------------------------
93
+
94
+ function updateWhitespace( dot, braceOrAsterisk ) {
95
+ if (dot.stop === braceOrAsterisk.start)
96
+ return;
97
+
98
+ const from = dot.stop + currentOffset + 1; // end points at the position *before* the character
99
+ const to = braceOrAsterisk.start + currentOffset;
100
+
101
+ source = replaceSliceInSource(source, from, to, '');
102
+
103
+ currentOffset -= to - from;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Replaces a given span with @p replaceWith
109
+ *
110
+ * @param {string} source
111
+ * @param {number} startIndex
112
+ * @param {number} endIndex
113
+ * @param {string} replaceWith
114
+ * @return {string}
115
+ */
116
+ function replaceSliceInSource( source, startIndex, endIndex, replaceWith ) {
117
+ return source.substring(0, startIndex) +
118
+ replaceWith +
119
+ source.substring(endIndex);
120
+ }
121
+
122
+ /**
123
+ * @param {string} [msg]
124
+ */
125
+ function exitError( msg ) {
126
+ if (msg)
127
+ console.error(msg);
128
+ usage();
129
+ process.exit(1);
130
+ }
131
+
132
+ function usage() {
133
+ console.error('');
134
+ console.error(`usage: ${path.basename(process.argv[1])} <filename>`);
135
+ }
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Update CDS Annotation References to use v4 expression syntax.
4
+ //
5
+ // This script replaces bare, unchecked references in annotation assignments
6
+ // with the new syntax using `(`.
7
+ //
8
+ // Example CDS:
9
+ // @anno: ID entity E { key ID : String; };
10
+ // will become:
11
+ // @anno: (ID) entity E { key ID : String; };
12
+ //
13
+ // Usage:
14
+ // ./cds_update_annotations.js my_file.cds
15
+ //
16
+ // If you want to update all identifiers in a directory, you can use
17
+ // this Shell script:
18
+ // find . -type f -iname '*.cds' -exec ./cds_update_annotations.js {} \;
19
+ //
20
+ // Note that you need to update the path to this script in the commands above.
21
+ //
22
+
23
+ 'use strict';
24
+
25
+ const parseLanguage = require('../lib/language/antlrParser');
26
+ const { createMessageFunctions } = require('../lib/base/messages');
27
+
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { forEachMemberRecursively } = require('../lib/base/model');
31
+
32
+ const cliArgs = process.argv.slice(2);
33
+ const filepath = cliArgs[0];
34
+
35
+ if (filepath === '--help' || filepath === '-h')
36
+ exitError();
37
+
38
+ if (cliArgs.length !== 1)
39
+ exitError(`Expected exactly one argument, ${cliArgs.length} given`);
40
+
41
+ if (!filepath)
42
+ exitError('Expected non-empty filepath as argument!');
43
+
44
+ // Do not use allow-list approach.
45
+ // There may be CDS files with other extensions than `.cds`.
46
+ if (filepath.endsWith('.csn') || filepath.endsWith('.json'))
47
+ exitError('Only CDS files can be passed! Found CSN file!');
48
+
49
+ const sourceStr = fs.readFileSync(filepath, 'utf-8');
50
+ const newSourceStr = modernizeAnnotationExpressions(sourceStr, filepath);
51
+ if (newSourceStr !== sourceStr)
52
+ fs.writeFileSync(filepath, newSourceStr);
53
+ process.exit(0); // success
54
+
55
+ // --------------------------------------------------------
56
+
57
+ function modernizeAnnotationExpressions( source, filename ) {
58
+ const options = { messages: [], attachTokens: true };
59
+ const messageFunctions = createMessageFunctions( options, 'parse', null );
60
+
61
+ // parseLanguage does not throw on CompilationError, so
62
+ // we do not need a try...catch block.
63
+ const ast = parseLanguage(source, filename, options, messageFunctions);
64
+
65
+ // To avoid spam, only report errors.
66
+ // Users should use the compiler to get all messages.
67
+ const errors = options.messages.filter(msg => (msg.severity === 'Error'));
68
+ if (errors.length > 0) {
69
+ errors.forEach((msg) => {
70
+ console.error(msg.toString());
71
+ });
72
+ console.error(`Found ${errors.length} errors! \n`);
73
+ exitError('The parser emitted errors. Please fix them first and try again.');
74
+ }
75
+
76
+ if (!ast.artifacts)
77
+ return source;
78
+
79
+ let currentOffset = 0;
80
+
81
+ for (const artName in ast.artifacts) {
82
+ const art = ast.artifacts[artName];
83
+ updateAnnosOfArtifact(art);
84
+ forEachMemberRecursively(art, updateAnnosOfArtifact);
85
+ }
86
+ for (const vocName in ast.vocabularies) {
87
+ const voc = ast.vocabularies[vocName];
88
+ updateAnnosOfArtifact(voc);
89
+ forEachMemberRecursively(voc, updateAnnosOfArtifact);
90
+ }
91
+ for (const ext of ast.extensions)
92
+ updateAnnosOfArtifact(ext);
93
+
94
+ return source;
95
+
96
+ // -----------------------------------------------
97
+
98
+ function updateAnnosOfArtifact( art ) {
99
+ for (const prop in art) {
100
+ if (prop.charAt(0) === '@')
101
+ updateAnno(art[prop]);
102
+ }
103
+ }
104
+
105
+ function updateAnno( anno ) {
106
+ if (anno.literal === 'array') {
107
+ anno.val.forEach(val => updateAnno( val ));
108
+ }
109
+ else if (anno.literal === 'struct') {
110
+ const struct = Object.values(anno.struct);
111
+ struct.forEach(val => updateAnno( val ));
112
+ }
113
+ else if (!anno.$tokenTexts && anno.path) {
114
+ const first = anno.path[0];
115
+ const last = anno.path[anno.path.length - 1];
116
+ insertParentheses(first, last);
117
+ }
118
+ }
119
+
120
+ function findTokenFor( node ) {
121
+ const loc = node.location;
122
+ // We use slow linear search; we could do binary search, but not performance critical.
123
+ const { tokens } = ast.tokenStream;
124
+ for (const token of tokens) {
125
+ if (token.column + 1 === loc.col && token.line === loc.line)
126
+ return token;
127
+ }
128
+ return null;
129
+ }
130
+
131
+ function insertParentheses( first, last ) {
132
+ const firstToken = findTokenFor(first);
133
+ const lastToken = findTokenFor(last);
134
+
135
+ if (!firstToken?.stop || !lastToken?.stop)
136
+ throw new Error(`INTERNAL ERROR: Identifier at ${firstToken?.start} or ${lastToken?.start} has no end! (${filepath})`);
137
+
138
+ const start = firstToken.start + currentOffset;
139
+ const end = lastToken.stop + currentOffset + 1; // end points at the position *before* the character
140
+
141
+ const original = source.substring(start, end);
142
+ if (original.includes('@'))
143
+ return; // we ignore annotation references for now
144
+
145
+ const newSource = `( ${original} )`;
146
+ source = replaceSliceInSource(source, start, end, newSource);
147
+
148
+ currentOffset += (newSource.length - original.length);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Replaces a given span with @p replaceWith
154
+ *
155
+ * @param {string} source
156
+ * @param {number} startIndex
157
+ * @param {number} endIndex
158
+ * @param {string} replaceWith
159
+ * @return {string}
160
+ */
161
+ function replaceSliceInSource( source, startIndex, endIndex, replaceWith ) {
162
+ return source.substring(0, startIndex) +
163
+ replaceWith +
164
+ source.substring(endIndex);
165
+ }
166
+
167
+ /**
168
+ * @param {string} [msg]
169
+ */
170
+ function exitError( msg ) {
171
+ if (msg)
172
+ console.error(msg);
173
+ usage();
174
+ process.exit(1);
175
+ }
176
+
177
+ function usage() {
178
+ console.error('');
179
+ console.error(`usage: ${path.basename(process.argv[1])} <filename>`);
180
+ }
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- //
4
3
  // Update CDS Delimited Identifiers
5
4
  //
6
5
  // This script replaces the old delimited identifier style with the new one
@@ -12,11 +11,11 @@
12
11
  // entity ![My Entity] { ... }
13
12
  //
14
13
  // Usage:
15
- // cds_update_identifiers.js my_file.cds
14
+ // ./cds_update_identifiers.js my_file.cds
16
15
  //
17
16
  // If you want to update all identifiers in a directory, you can use
18
17
  // this Shell script:
19
- // find . -type f -iname '*.cds' -exec cds_update_identifiers.js {} \;
18
+ // find . -type f -iname '*.cds' -exec ./cds_update_identifiers.js {} \;
20
19
  //
21
20
  // Note that you need to update the path to this script in the commands above.
22
21
  //
@@ -70,7 +69,7 @@ function modernizeIdentifierStyle( source, filename ) {
70
69
  console.error(msg.toString());
71
70
  });
72
71
  console.error(`Found ${errors.length} errors! \n`);
73
- exitError('The parser emitted errors. Please fix them first and try again.');
72
+ exitError('The CDS parser emitted errors. Fix them first and try again.');
74
73
  }
75
74
 
76
75
  let currentOffset = 0;
package/bin/cdsc.js CHANGED
@@ -278,6 +278,7 @@ function executeCommandLine( command, options, args ) {
278
278
  toSql,
279
279
  inspect,
280
280
  toEffectiveCsn,
281
+ forSeal,
281
282
  };
282
283
  const commandsWithoutCompilation = {
283
284
  explain,
@@ -318,7 +319,7 @@ function executeCommandLine( command, options, args ) {
318
319
  }
319
320
 
320
321
  function toEffectiveCsn( model ) {
321
- const features = [ 'resolveSimpleTypes', 'resolveProjections' ];
322
+ const features = [ 'resolveSimpleTypes', 'resolveProjections', 'remapOdataAnnotations', 'keepLocalized' ];
322
323
  for (const feature of features) {
323
324
  if (options[feature]) // map to boolean equivalent
324
325
  options[feature] = options[feature] === 'true';
@@ -329,6 +330,18 @@ function executeCommandLine( command, options, args ) {
329
330
  return model;
330
331
  }
331
332
 
333
+ function forSeal( model ) {
334
+ const features = [ 'remapOdataAnnotations' ];
335
+ for (const feature of features) {
336
+ if (options[feature]) // map to boolean equivalent
337
+ options[feature] = options[feature] === 'true';
338
+ }
339
+ const csn = options.directBackend ? model : compactModel(model, options);
340
+ displayNamedCsn(main.for.seal(csn, options), 'seal');
341
+
342
+ return model;
343
+ }
344
+
332
345
  // Execute the command line option 'toCsn' and display the results.
333
346
  // Return the original model (for chaining)
334
347
  function toCsn( model ) {
@@ -8,6 +8,25 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 4.9.0 - 2024-04-25
12
+
13
+ ## Removed `odataAnnotationExpressions`
14
+
15
+ It is now enabled by default.
16
+
17
+ ## Removed `odataPathsInAnnotationExpressions`
18
+
19
+ It is now enabled by default.
20
+
21
+ ## Removed `annotationExpressions`
22
+
23
+ It is now enabled by default.
24
+
25
+ ## Added `temporalRawProjection`
26
+
27
+ Enables revocation of temporal where clause in projections of temporal entities if the
28
+ `@cds.valid { from, to }` annotations on the projection elements have falsy values.
29
+
11
30
  ## Version 4.8.0 - 2024-03-21
12
31
 
13
32
  ### Removed `vectorType`
package/lib/api/main.js CHANGED
@@ -26,10 +26,7 @@ const baseError = lazyload('../base/error');
26
26
  const csnToEdm = lazyload('../edm/csn2edm');
27
27
  const trace = lazyload('./trace');
28
28
  const cloneCsn = lazyload('../model/cloneCsn');
29
-
30
- const { forEach, forEachKey } = require('../utils/objectUtils');
31
- const { makeMessageFunction } = require('../base/messages');
32
- const { sortCsnForTests } = require('../model/cloneCsn');
29
+ const objectUtils = lazyload('../utils/objectUtils');
33
30
 
34
31
  /**
35
32
  * Return the artifact name for use for the hdbresult object
@@ -59,18 +56,18 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
59
56
  function attachTransformerCharacteristics( csn, transformation, options,
60
57
  relevantOptionNames, optionalOptionNames = [] ) {
61
58
  const relevant = {};
62
- for (const name of relevantOptionNames ) {
59
+ for (const name of relevantOptionNames) {
63
60
  if (options[name] !== undefined)
64
61
  relevant[name] = options[name];
65
62
  }
66
63
 
67
- for (const name of optionalOptionNames ) {
64
+ for (const name of optionalOptionNames) {
68
65
  if (options[name] !== undefined)
69
66
  relevant[name] = options[name];
70
67
  }
71
68
 
72
69
  // eslint-disable-next-line sonarjs/no-empty-collection
73
- for (const name of relevantGeneralOptions ) {
70
+ for (const name of relevantGeneralOptions) {
74
71
  if (options[name] !== undefined)
75
72
  relevant[name] = options[name];
76
73
  }
@@ -142,7 +139,7 @@ function isPreTransformed( csn, transformation ) {
142
139
  function odataInternal( csn, internalOptions, messageFunctions ) {
143
140
  internalOptions.transformation = 'odata';
144
141
  let oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
145
- oDataCsn = sortCsnForTests(oDataCsn, internalOptions);
142
+ oDataCsn = cloneCsn.sortCsnForTests(oDataCsn, internalOptions);
146
143
  messageFunctions.setModel(oDataCsn);
147
144
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
148
145
  return oDataCsn;
@@ -273,13 +270,12 @@ function forHdbcds( csn, options, messageFunctions ) {
273
270
  *
274
271
  * @param {CSN.Model} csn Plain input CSN
275
272
  * @param {EffectiveCsnOptions} options Options
273
+ * @param {EffectiveCsnOptions} internalOptions Options that were already processed
276
274
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
277
275
  * @returns {CSN.Model} CSN transformed
278
276
  * @private
279
277
  */
280
- function forEffective( csn, options, messageFunctions ) {
281
- const internalOptions = prepareOptions.for.effective(options);
282
- internalOptions.transformation = 'effective';
278
+ function forEffectiveInternal( csn, options, internalOptions, messageFunctions ) {
283
279
  messageFunctions.setOptions( internalOptions );
284
280
  if (options.tenantDiscriminator) {
285
281
  messageFunctions.error('api-invalid-option', null, {
@@ -294,6 +290,40 @@ function forEffective( csn, options, messageFunctions ) {
294
290
  return cloneCsn.sortCsnForTests(eCsn, internalOptions);
295
291
  }
296
292
 
293
+ /**
294
+ * SEAL CSN transformation
295
+ *
296
+ * @param {CSN.Model} csn Plain input CSN
297
+ * @param {EffectiveCsnOptions} options Options
298
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
299
+ * @returns {CSN.Model} CSN transformed
300
+ * @private
301
+ */
302
+ function forSeal( csn, options, messageFunctions ) {
303
+ const internalOptions = prepareOptions.for.seal(options);
304
+ internalOptions.transformation = 'effective';
305
+ return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
306
+ }
307
+
308
+ /**
309
+ * Effective CSN transformation
310
+ *
311
+ * @param {CSN.Model} csn Plain input CSN
312
+ * @param {EffectiveCsnOptions} options Options
313
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
314
+ * @returns {CSN.Model} CSN transformed
315
+ * @private
316
+ */
317
+ function forEffective( csn, options, messageFunctions ) {
318
+ const internalOptions = prepareOptions.for.effective(options);
319
+ internalOptions.transformation = 'effective';
320
+ // for.effective is still beta mode
321
+ if (!baseModel.isBetaEnabled(options, 'effectiveCsn'))
322
+ throw new baseError.CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
323
+
324
+ return forEffectiveInternal(csn, options, internalOptions, messageFunctions);
325
+ }
326
+
297
327
  /**
298
328
  * Process the given CSN into SQL.
299
329
  *
@@ -352,7 +382,7 @@ function hdi( csn, options, messageFunctions ) {
352
382
  const sqlArtifactsWithCSNNamesToSort = Object.create(null);
353
383
  const sqlArtifactsNotToSort = Object.create(null);
354
384
 
355
- forEach(flat, (key) => {
385
+ objectUtils.forEach(flat, (key) => {
356
386
  const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');
357
387
  nameMapping[artifactNameLikeInCsn] = key;
358
388
  if (key.endsWith('.hdbtable') || key.endsWith('.hdbview'))
@@ -370,7 +400,7 @@ function hdi( csn, options, messageFunctions ) {
370
400
  }, Object.create(null));
371
401
 
372
402
  // now add the not-sorted stuff, like indices
373
- forEach(sqlArtifactsNotToSort, (key) => {
403
+ objectUtils.forEach(sqlArtifactsNotToSort, (key) => {
374
404
  sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];
375
405
  });
376
406
 
@@ -392,7 +422,7 @@ function hdi( csn, options, messageFunctions ) {
392
422
  function remapNames( dict, csn, filter ) {
393
423
  const result = Object.create(null);
394
424
 
395
- forEach(dict, (key, value) => {
425
+ objectUtils.forEach(dict, (key, value) => {
396
426
  const name = remapName(key, csn, filter);
397
427
  result[name] = value;
398
428
  });
@@ -540,7 +570,7 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
540
570
  const createAndAlterSqls = [];
541
571
  // Turn the structured result into just a flat dictionary of "artifact name": "sql"
542
572
  const flatSqlDict = Object.values(hdbkinds).reduce((prev, curr) => {
543
- forEach(curr, (name, value) => {
573
+ objectUtils.forEach(curr, (name, value) => {
544
574
  prev[name] = value;
545
575
  });
546
576
  return prev;
@@ -629,9 +659,9 @@ function hdiMigration( csn, options, messageFunctions, beforeImage ) {
629
659
  */
630
660
  function createSqlDefinitions( hdbkinds, afterImage ) {
631
661
  const result = [];
632
- forEach(hdbkinds, (kind, artifacts) => {
662
+ objectUtils.forEach(hdbkinds, (kind, artifacts) => {
633
663
  const suffix = `.${ kind }`;
634
- forEach(artifacts, (name, sqlStatement) => {
664
+ objectUtils.forEach(artifacts, (name, sqlStatement) => {
635
665
  if ( kind !== 'hdbindex' )
636
666
  result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
637
667
  else
@@ -649,7 +679,7 @@ function createSqlDefinitions( hdbkinds, afterImage ) {
649
679
  */
650
680
  function createSqlDeletions( deletions, beforeImage ) {
651
681
  const result = [];
652
- forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
682
+ objectUtils.forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
653
683
  return result;
654
684
  }
655
685
  /**
@@ -661,7 +691,7 @@ function createSqlDeletions( deletions, beforeImage ) {
661
691
  */
662
692
  function createSqlMigrations( migrations, afterImage ) {
663
693
  const result = [];
664
- forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
694
+ objectUtils.forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
665
695
  return result;
666
696
  }
667
697
 
@@ -682,6 +712,10 @@ function hdbcds( csn, options, messageFunctions ) {
682
712
  internalOptions.transformation = 'hdbcds';
683
713
  messageFunctions.setOptions( internalOptions );
684
714
 
715
+ // no "isBetaEnabled", because this warning must also appear with "deprecated" flags
716
+ if (internalOptions.betaMode || internalOptions.beta?.v5preview)
717
+ messageFunctions.warning('api-deprecated-v5', null, null);
718
+
685
719
  if (options.tenantDiscriminator) {
686
720
  messageFunctions.error('api-invalid-option', null, {
687
721
  '#': 'forbidden',
@@ -1012,10 +1046,10 @@ function preparedCsnToEdmAll( csn, options, messageFunctions ) {
1012
1046
  */
1013
1047
  function flattenResultStructure( toProcess ) {
1014
1048
  const result = {};
1015
- forEach(toProcess, (fileType, artifacts) => {
1049
+ objectUtils.forEach(toProcess, (fileType, artifacts) => {
1016
1050
  if (fileType === 'messages')
1017
1051
  return;
1018
- forEach(artifacts, (filename) => {
1052
+ objectUtils.forEach(artifacts, (filename) => {
1019
1053
  result[`${ filename }.${ fileType }`] = artifacts[filename];
1020
1054
  });
1021
1055
  });
@@ -1039,14 +1073,15 @@ module.exports = {
1039
1073
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
1040
1074
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
1041
1075
  for_effective: publishCsnProcessor(forEffective, 'for.effective'),
1076
+ for_seal: publishCsnProcessor(forSeal, 'for.seal'),
1042
1077
 
1043
1078
  /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
1044
1079
  preparedCsnToEdmx(csn, service, options) {
1045
- preparedCsnToEdmx(csn, service, options, makeMessageFunction( csn, options, 'to.edmx' ));
1080
+ preparedCsnToEdmx(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edmx' ));
1046
1081
  },
1047
1082
  /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
1048
1083
  preparedCsnToEdm(csn, service, options) {
1049
- preparedCsnToEdm(csn, service, options, makeMessageFunction( csn, options, 'to.edm' ));
1084
+ preparedCsnToEdm(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edm' ));
1050
1085
  },
1051
1086
  };
1052
1087
 
@@ -1160,7 +1195,7 @@ function checkOutdatedOptions( options, messageFunctions ) {
1160
1195
  });
1161
1196
  }
1162
1197
 
1163
- forEachKey(options.variableReplacements || {}, (name) => {
1198
+ objectUtils.forEachKey(options.variableReplacements || {}, (name) => {
1164
1199
  if (!name.startsWith('$') && name !== 'user' && name !== 'locale') {
1165
1200
  messageFunctions.error('api-invalid-variable-replacement', null, {
1166
1201
  '#': 'noDollar', option: 'variableReplacements', code: '$', name,