@sap/cds-compiler 4.0.2 → 4.2.2

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 (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -67,6 +67,9 @@
67
67
  'use strict';
68
68
 
69
69
  const { locationString, hasErrors } = require('../base/messages');
70
+ const {
71
+ XsnSource, XsnName, XsnArtifact, CsnLocation,
72
+ } = require('./classes');
70
73
 
71
74
 
72
75
  // Properties that can appear where a type can have type arguments.
@@ -77,14 +80,15 @@ const typeProperties = [
77
80
 
78
81
  class InternalConsistencyError extends Error {
79
82
  constructor(msg) {
80
- super(`cds-compiler XSN consistency: ${ msg }`);
83
+ super( `cds-compiler XSN consistency: ${ msg }` );
81
84
  }
82
85
  }
83
86
 
84
87
  function assertConsistency( model, stage ) {
85
88
  const stageParser = typeof stage === 'object';
86
89
  const options = stageParser && stage || model.options || { testMode: true };
87
- if (!options.testMode || options.parseOnly && !stageParser)
90
+ if (!options.testMode || options.testMode === '$noAssertConsistency' ||
91
+ options.parseOnly && !stageParser)
88
92
  return;
89
93
 
90
94
  const schema = {
@@ -105,6 +109,7 @@ function assertConsistency( model, stage ) {
105
109
  '$blocks',
106
110
  '$messageFunctions',
107
111
  '$functions',
112
+ '$assert',
108
113
  '_sortedSources',
109
114
  ],
110
115
  },
@@ -122,12 +127,14 @@ function assertConsistency( model, stage ) {
122
127
  '$withLocalized',
123
128
  '$sources',
124
129
  ],
130
+ instanceOf: XsnSource,
125
131
  },
126
132
  location: {
127
133
  // every thing with a $location in CSN must have a XSN location even
128
134
  // with syntax errors (currently even internal artifacts like $using):
129
135
  isRequired: parent => noSyntaxErrors() || parent && parent.kind,
130
136
  kind: true,
137
+ instanceOf: CsnLocation,
131
138
  requires: [ 'file' ], // line is optional in top-level location
132
139
  optional: [ 'line', 'col', 'endLine', 'endCol', '$notFound' ],
133
140
  schema: {
@@ -138,8 +145,8 @@ function assertConsistency( model, stage ) {
138
145
  $notFound: { test: isBoolean },
139
146
  },
140
147
  },
141
- sources: { test: isDictionary( isObject ) },
142
- _sortedSources: { test: isArray( isObject ) },
148
+ sources: { test: isDictionary( isObject ), instanceOf: XsnSource },
149
+ _sortedSources: { test: isArray( isObject ), instanceOf: XsnSource },
143
150
  file: { test: isString },
144
151
  dirname: { test: isString }, // TODO: really necessary?
145
152
  realname: { test: isString }, // TODO: really necessary?
@@ -158,11 +165,13 @@ function assertConsistency( model, stage ) {
158
165
  test: isDictionary( definition ),
159
166
  requires: [ 'kind', 'location', 'name' ],
160
167
  optional: thoseWithKind,
168
+ instanceOf: XsnArtifact,
161
169
  },
162
170
  vocabularies: {
163
171
  test: isDictionary( definition ),
164
172
  requires: [ 'kind', 'name' ],
165
173
  optional: thoseWithKind,
174
+ instanceOf: XsnArtifact,
166
175
  },
167
176
  extensions: {
168
177
  kind: [ 'context' ], // syntax error (as opposed to HANA CDS), but still there
@@ -174,7 +183,7 @@ function assertConsistency( model, stage ) {
174
183
  i18n: {
175
184
  test: isDictionary( ( val, parent, prop, spec, lang ) => {
176
185
  const textValueIsString = (v, p, textProp, s, textKey) => {
177
- isString(v.val, p, textKey, s);
186
+ isString( v.val, p, textKey, s );
178
187
  };
179
188
  const innerDict = isDictionary( textValueIsString );
180
189
  return innerDict( val, parent, lang, spec );
@@ -197,7 +206,7 @@ function assertConsistency( model, stage ) {
197
206
  ],
198
207
  schema: {
199
208
  kind: { test: isString, enum: [ 'builtin' ] },
200
- name: { test: isObject, requires: [ 'id', 'element' ] },
209
+ name: { test: isObject, instanceOf: XsnName, requires: [ 'id', 'element' ] },
201
210
  $autoElement: { test: isString },
202
211
  $uncheckedElements: { test: isBoolean },
203
212
  $requireElementAccess: { test: isBoolean },
@@ -240,7 +249,7 @@ function assertConsistency( model, stage ) {
240
249
  typeProps$: { kind: true, enumerable: false, test: TODO },
241
250
  actions: { kind: true, inherits: 'definitions' },
242
251
  enum: { kind: true, inherits: 'definitions' },
243
- foreignKeys: { kind: true, inherits: 'definitions' },
252
+ foreignKeys: { kind: true, inherits: 'definitions', instanceOf: 'ignore' },
244
253
  $keysNavigation: { kind: true, test: TODO },
245
254
  params: { kind: true, inherits: 'definitions' },
246
255
  _extendType: { kind: true, test: TODO },
@@ -261,7 +270,8 @@ function assertConsistency( model, stage ) {
261
270
  requires: [ 'op', 'location', 'from' ],
262
271
  optional: [
263
272
  'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
264
- 'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '_origin', '_block',
273
+ 'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '$limit',
274
+ '_origin', '_block',
265
275
  '_projections', '_parent', '_main', '_effectiveType', '$effectiveSeqNo', '$expand',
266
276
  '$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
267
277
  ],
@@ -275,7 +285,7 @@ function assertConsistency( model, stage ) {
275
285
  requires: [ 'op', 'location', 'args', 'join' ],
276
286
  optional: [
277
287
  'on', '$parens', 'cardinality',
278
- 'kind', 'name', '_block', '_parent', '_main',
288
+ 'kind', 'name', '_block', '_parent', '_main', '_user',
279
289
  '$tableAliases', '_combined', '_joinParent', '$joinArgsIndex',
280
290
  '_leadingQuery', '_$next', '_deps',
281
291
  ],
@@ -306,6 +316,7 @@ function assertConsistency( model, stage ) {
306
316
  columns: {
307
317
  kind: [ 'extend', '$column' ],
308
318
  test: isArray( column ),
319
+ instanceOf: XsnArtifact,
309
320
  optional: thoseWithKind,
310
321
  enum: [ '*' ],
311
322
  requires: [ 'location' ],
@@ -325,6 +336,7 @@ function assertConsistency( model, stage ) {
325
336
  nulls: { test: locationVal( isString ), enum: [ 'first', 'last' ] },
326
337
  $orderBy: { inherits: 'orderBy' },
327
338
  groupBy: { inherits: 'value', test: isArray( expression ) },
339
+ $limit: { test: TODO },
328
340
  limit: { requires: [ 'rows' ], optional: [ 'offset', 'location' ] },
329
341
  rows: { inherits: 'value' },
330
342
  offset: { inherits: 'value' },
@@ -383,6 +395,7 @@ function assertConsistency( model, stage ) {
383
395
  'select', '$join', 'mixin',
384
396
  'source', 'namespace', 'using',
385
397
  '$tableAlias', '$navElement',
398
+ 'builtin', // magic variables
386
399
  ],
387
400
  },
388
401
  // locations of parentheses pairs around expression:
@@ -496,11 +509,12 @@ function assertConsistency( model, stage ) {
496
509
  ],
497
510
  // TODO: name requires if not in parser?
498
511
  },
499
- $priority: { test: isOneOf([ undefined, false, 'extend', 'annotate' ]) },
512
+ $priority: { test: isOneOf( [ undefined, false, 'extend', 'annotate' ] ) },
500
513
  $annotations: { parser: true, kind: true, test: TODO }, // deprecated, still there for cds-lsp
501
514
  name: {
502
515
  isRequired: stageParser && (() => false), // not required in parser
503
516
  kind: true,
517
+ instanceOf: 'ignore', // TODO: XsnName,
504
518
  schema: {
505
519
  select: { test: TODO },
506
520
  }, // TODO: rename query prop in name
@@ -527,6 +541,7 @@ function assertConsistency( model, stage ) {
527
541
  kind: [ 'action', 'function' ],
528
542
  requires: [ 'kind', 'location' ],
529
543
  optional: thoseWithKind,
544
+ instanceOf: XsnArtifact,
530
545
  },
531
546
  items: {
532
547
  kind: true,
@@ -592,7 +607,8 @@ function assertConsistency( model, stage ) {
592
607
  // query specific
593
608
  'where', 'columns', 'mixin', 'quantifier', 'offset',
594
609
  'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
595
- 'limit', '_status', '_origin', '_effectiveType', '$effectiveSeqNo',
610
+ '$limit', 'limit', '_status', '_origin',
611
+ '_effectiveType', '$effectiveSeqNo',
596
612
  ],
597
613
  },
598
614
  _leadingQuery: { kind: true, test: TODO },
@@ -634,7 +650,7 @@ function assertConsistency( model, stage ) {
634
650
  $inferred: {
635
651
  parser: true,
636
652
  kind: true,
637
- test: isOneOf([
653
+ test: isOneOf( [
638
654
  '', // constructed “super annotate” statement, redirected user-provided target
639
655
  // Uppercase values are used in logic, lowercase value are "just for us", i.e.
640
656
  // debugging or to add properties such as $generated in Universal CSN.
@@ -666,7 +682,7 @@ function assertConsistency( model, stage ) {
666
682
  'rewrite', // on-conditions or FKeys are rewritten
667
683
  'parent-origin', // annotation/property copied from parent that does not come through
668
684
  // $origin and is not a direct annotation
669
- ]),
685
+ ] ),
670
686
  },
671
687
 
672
688
  // Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
@@ -675,7 +691,7 @@ function assertConsistency( model, stage ) {
675
691
  $expand: {
676
692
  kind: true,
677
693
  // See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
678
- test: isOneOf([ 'origin', 'annotate', 'target' ]),
694
+ test: isOneOf( [ 'origin', 'annotate', 'target' ] ),
679
695
  },
680
696
  $inCycle: { kind: true, test: isBoolean },
681
697
 
@@ -683,9 +699,10 @@ function assertConsistency( model, stage ) {
683
699
  $extra: { parser: true, test: TODO }, // for unexpected properties in CSN
684
700
  $withLocalized: { test: isBoolean },
685
701
  $sources: { parser: true, test: isArray( isString ) },
686
- $expected: { parser: true, test: isOneOf([ 'approved-exists', 'exists' ]) },
702
+ $expected: { parser: true, test: isOneOf( [ 'approved-exists', 'exists' ] ) },
687
703
  $messageFunctions: { test: TODO },
688
704
  $functions: { test: TODO },
705
+ $assert: { test: TODO }, // currently just for missing Error[ref-cycle]
689
706
  };
690
707
  let _noSyntaxErrors = null;
691
708
  assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
@@ -714,8 +731,10 @@ function assertConsistency( model, stage ) {
714
731
  : {}.propertyIsEnumerable.call( parent, prop ) !== enumerable)
715
732
  throw new InternalConsistencyError( `Unexpected enumerability ${ !enumerable }${ at( [ node, parent ], prop ) }` );
716
733
  }
717
- (spec.test || standard)( node, parent, prop, spec,
718
- typeof noPropertyTest === 'string' && noPropertyTest );
734
+ if (node !== undefined) { // ignore if undefined
735
+ (spec.test || standard)( node, parent, prop, spec,
736
+ typeof noPropertyTest === 'string' && noPropertyTest );
737
+ }
719
738
  }
720
739
 
721
740
  function definition( node, parent, prop, spec, name ) {
@@ -768,7 +787,7 @@ function assertConsistency( model, stage ) {
768
787
  const requires = spec.requires || [];
769
788
  // Do not test 'requires' with parse errors:
770
789
  for (const p of requires) {
771
- if (!names.includes(p)) {
790
+ if (!names.includes( p )) {
772
791
  const req = spec.schema && spec.schema[p] && spec.schema[p].isRequired;
773
792
  if ((req || schema[p] && schema[p].isRequired || noSyntaxErrors)( node ))
774
793
  throw new InternalConsistencyError( `Required property '${ p }' missing in object${ at( [ node, parent ], prop, name ) }` );
@@ -776,13 +795,14 @@ function assertConsistency( model, stage ) {
776
795
  }
777
796
  const optional = spec.optional || [];
778
797
  for (const n of names) {
779
- const opt = Array.isArray(optional)
798
+ const opt = Array.isArray( optional )
780
799
  ? optional.includes( n ) || optional.includes( n.charAt(0) )
781
800
  : optional( n, spec );
782
- if (!(opt || requires.includes( n ) || n === '$extra'))
783
- throw new InternalConsistencyError( `Property '${ n }' is not expected${ at( [ node[n], node, parent ], prop, name ) }` );
784
-
785
- assertProp( node[n], node, n, spec.schema && spec.schema[n] );
801
+ if (node[n] !== undefined) {
802
+ if (!(opt || requires.includes( n ) || n === '$extra'))
803
+ throw new InternalConsistencyError( `Property '${ n }' is not expected${ at( [ node[n], node, parent ], prop, name ) }` );
804
+ assertProp( node[n], node, n, spec.schema && spec.schema[n] );
805
+ }
786
806
  }
787
807
  }
788
808
 
@@ -838,7 +858,7 @@ function assertConsistency( model, stage ) {
838
858
  // TODO CSN parser?: { val: <token>, literal: 'token' } for keywords
839
859
  if (typeof node === 'string')
840
860
  return;
841
- while (Array.isArray(node)) {
861
+ while (Array.isArray( node )) {
842
862
  // TODO: also check getOwnPropertyNames(node)
843
863
  if (node.length !== 1) {
844
864
  node.forEach( n => expression( n, parent, prop, spec ) );
@@ -850,7 +870,7 @@ function assertConsistency( model, stage ) {
850
870
  return;
851
871
  isObject( node, parent, prop, spec, idx );
852
872
 
853
- const s = spec[expressionSpec(node)] || {};
873
+ const s = spec[expressionSpec( node )] || {};
854
874
  const sub = Object.assign( {}, s.inherits && schema[s.inherits], s );
855
875
  if (spec.requires && sub.requires)
856
876
  sub.requires = [ ...sub.requires, ...spec.requires ];
@@ -873,9 +893,9 @@ function assertConsistency( model, stage ) {
873
893
  }
874
894
 
875
895
  function args( node, parent, prop, spec ) {
876
- if (Array.isArray(node)) {
896
+ if (Array.isArray( node )) {
877
897
  if (parent.op && parent.op.val === 'xpr') // remove keywords for `xpr` expressions
878
- node = node.filter( a => typeof a !== 'string');
898
+ node = node.filter( a => typeof a !== 'string' );
879
899
  node.forEach( (item, idx) => expression( item, parent, prop, spec, idx ) );
880
900
  }
881
901
  else if (node && typeof node === 'object' && !Object.getPrototypeOf( node )) {
@@ -910,7 +930,7 @@ function assertConsistency( model, stage ) {
910
930
 
911
931
  function isArray( func = standard ) {
912
932
  return function vector( node, parent, prop, spec ) {
913
- if (!Array.isArray(node))
933
+ if (!Array.isArray( node ))
914
934
  throw new InternalConsistencyError( `Expected array${ at( [ null, parent ], prop ) }` );
915
935
  node.forEach( (item, n) => func( item, parent, prop, spec, n ) );
916
936
  };
@@ -921,7 +941,9 @@ function assertConsistency( model, stage ) {
921
941
  const valSchema = { val: Object.assign( {}, spec, { test: func } ) };
922
942
  const requires = [ 'val', 'location' ];
923
943
  const optional = [ 'literal', '$inferred', '$priority', '_pathHead' ];
924
- standard( node, parent, prop, { schema: valSchema, requires, optional }, name );
944
+ standard( node, parent, prop, {
945
+ schema: valSchema, requires, optional, instanceOf: spec.instanceOf,
946
+ }, name );
925
947
  };
926
948
  }
927
949
 
@@ -941,33 +963,42 @@ function assertConsistency( model, stage ) {
941
963
 
942
964
  function isOneOf( values ) {
943
965
  return function isOneOfInner( node, parent, prop ) {
944
- if (!values.includes(node))
945
- throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify(values) }${ at( [ node, parent ], prop ) }` );
966
+ if (!values.includes( node ))
967
+ throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify( values ) }${ at( [ node, parent ], prop ) }` );
946
968
  };
947
969
  }
948
970
 
949
971
  function isString( node, parent, prop, spec ) {
950
972
  if (typeof node !== 'string')
951
- throw new InternalConsistencyError( `Expected string${ at( [ node, parent ], prop ) }` );
973
+ throw new InternalConsistencyError( `Expected string but found ${ typeof node }${ at( [ node, parent ], prop ) }` );
952
974
  // TODO: also check getOwnPropertyNames(node)
953
975
  if (spec.enum && !spec.enum.includes( node ))
954
976
  throw new InternalConsistencyError( `Unexpected value '${ node }'${ at( [ node, parent ], prop ) }` );
955
977
  }
956
978
 
957
979
  function isVal( node, parent, prop, spec ) {
958
- if (Array.isArray(node))
980
+ if (Array.isArray( node ))
959
981
  node.forEach( (item, n) => standard( item, parent, prop, spec, n ) );
960
982
  else if (node !== null && ![ 'string', 'number', 'boolean' ].includes( typeof node ))
961
983
  throw new InternalConsistencyError( `Expected array or simple value${ at( [ null, parent ], prop ) }` );
962
984
  }
963
985
 
964
986
  function isObject( node, parent, prop, spec, name ) {
965
- if (!node || typeof node !== 'object' || Object.getPrototypeOf( node ) !== Object.prototype)
966
- throw new InternalConsistencyError( `Expected standard object${ at( [ null, parent ], prop, name ) }` );
987
+ if (!node || typeof node !== 'object' )
988
+ throw new InternalConsistencyError( `Expected object${ at( [ null, parent ], prop, name ) }` );
989
+ const found = Object.getPrototypeOf( node )?.constructor?.name || 'null';
990
+ if (!spec.instanceOf && Object.getPrototypeOf( node ) !== Object.prototype)
991
+ throw new InternalConsistencyError( `Expected standard object but found ${ found }${ at( [ null, parent ], prop, name ) }` );
992
+ // TODO
993
+ // else if (spec.instanceOf && spec.instanceOf !== 'ignore' &&
994
+ // Object.getPrototypeOf( node ) !== spec.instanceOf.prototype)
995
+ // eslint-disable-next-line max-len
996
+ // throw new InternalConsistencyError( `Expected object of class ${ spec.instanceOf.name } but found ${ found }${ at( [ null, parent ], prop, name ) }` );
967
997
  }
968
998
 
999
+
969
1000
  function inDefinitions( art, parent, prop, spec, name ) {
970
- if (Array.isArray(art)) // do not check with redefinitions
1001
+ if (Array.isArray( art )) // do not check with redefinitions
971
1002
  return;
972
1003
  isObject( art, parent, prop, spec, name );
973
1004
  if (stageParser) {
@@ -979,7 +1010,7 @@ function assertConsistency( model, stage ) {
979
1010
  !(model.vocabularies && model.vocabularies[art.name.absolute])) {
980
1011
  // TODO: sign ignored artifacts with $inferred = 'IGNORED'
981
1012
  if (parent.kind === 'source' ||
982
- art.name.absolute && art.name.absolute.startsWith('localized.'))
1013
+ art.name.absolute && art.name.absolute.startsWith( 'localized.' ))
983
1014
  standard( art, parent, prop, spec, name );
984
1015
  else
985
1016
  throw new InternalConsistencyError( `Expected definition${ at( [ art, parent ], prop, name ) }` );
@@ -988,11 +1019,11 @@ function assertConsistency( model, stage ) {
988
1019
 
989
1020
  function isScope( node, parent, prop ) {
990
1021
  // artifact refs in CDL have scope:0 in XSN
991
- if (Number.isInteger(node))
1022
+ if (Number.isInteger( node ))
992
1023
  return;
993
1024
  const validValues = [ 'typeOf', 'global', 'param' ];
994
- if (!validValues.includes(node))
995
- throw new InternalConsistencyError( `Property '${ prop }' must be either "${ validValues.join('", "') }" or a number but was "${ node }"` );
1025
+ if (!validValues.includes( node ))
1026
+ throw new InternalConsistencyError( `Property '${ prop }' must be either "${ validValues.join( '", "' ) }" or a number but was "${ node }"` );
996
1027
  }
997
1028
 
998
1029
  function TODO() { /* no-op */ }
@@ -29,7 +29,11 @@ const kindProperties = {
29
29
  $inline: { normalized: 'element' }, // column with inline property
30
30
  event: { elements: true, include: true },
31
31
  type: { elements: propExists, enum: propExists, include: true },
32
- aspect: { elements: propExists, actions: true, include: true },
32
+ aspect: {
33
+ elements: propExists,
34
+ actions: ( _p, parent ) => propExists( 'elements', parent ),
35
+ include: true,
36
+ },
33
37
  annotation: { elements: propExists, enum: propExists },
34
38
  enum: { normalized: 'element', dict: 'enum' },
35
39
  element: { elements: propExists, enum: propExists, dict: 'elements' },
@@ -72,6 +76,7 @@ function propExists( prop, parent ) {
72
76
  * have a "sparse" name structure.
73
77
  *
74
78
  * @param {XSN.Artifact} art
79
+ * @returns {XSN.Name}
75
80
  */
76
81
  function getArtifactName( art ) {
77
82
  if (!art.name || art.name.absolute) // no name or “old style”
@@ -109,7 +114,7 @@ function getMemberNameProp( elem, kind ) {
109
114
  obj = obj.items;
110
115
  if (obj.elements || obj.enum)
111
116
  return 'element';
112
- throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
117
+ throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join( '+' ) }` );
113
118
  }
114
119
 
115
120
  module.exports = {
@@ -235,7 +235,7 @@ const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]\d+)?[ \t]*$/i;
235
235
  const quotedLiteralPatterns = {
236
236
  x: {
237
237
  test_variant: 'uneven-hex',
238
- test_fn: (str => Number.isInteger(str.length / 2)),
238
+ test_fn: (str => Number.isInteger( str.length / 2 )),
239
239
  unexpected_variant: 'invalid-hex',
240
240
  unexpected_char: /[^0-9a-f]/i,
241
241
  json_type: 'string',
@@ -293,12 +293,12 @@ const quotedLiteralPatterns = {
293
293
  */
294
294
  function checkDate( year, month, day ) {
295
295
  // Negative years are allowed
296
- year = Math.abs(Number.parseInt(year, 10));
297
- month = Number.parseInt(month, 10);
298
- day = Number.parseInt(day, 10);
296
+ year = Math.abs( Number.parseInt( year, 10 ) );
297
+ month = Number.parseInt( month, 10 );
298
+ day = Number.parseInt( day, 10 );
299
299
  // If any is NaN, the condition will be false.
300
300
  // Year 0 does not exist, but ISO 8601 allows it and defines it as 1 BC.
301
- return !Number.isNaN(year) && month > 0 && month < 13 && day > 0 && day < 32;
301
+ return !Number.isNaN( year ) && month > 0 && month < 13 && day > 0 && day < 32;
302
302
  }
303
303
 
304
304
  /**
@@ -308,9 +308,9 @@ function checkDate( year, month, day ) {
308
308
  * @returns {boolean} True if the date is valid.
309
309
  */
310
310
  function checkTime( hour, minutes, seconds ) {
311
- hour = Number.parseInt(hour, 10);
312
- minutes = Number.parseInt(minutes, 10);
313
- seconds = seconds ? Number.parseInt(seconds, 10) : 0;
311
+ hour = Number.parseInt( hour, 10 );
312
+ minutes = Number.parseInt( minutes, 10 );
313
+ seconds = seconds ? Number.parseInt( seconds, 10 ) : 0;
314
314
  if (hour === 24) // allow 24:00:00 (ISO 8601 version earlier than 2019)
315
315
  return minutes === 0 && seconds === 0;
316
316
  // If any is NaN, the condition will be false.
@@ -332,47 +332,47 @@ const typeCategories = {
332
332
  geo: [],
333
333
  };
334
334
  // Fill type categories with `cds.*` types
335
- Object.keys(core).forEach((type) => {
335
+ Object.keys( core ).forEach( (type) => {
336
336
  if (core[type].category)
337
- typeCategories[core[type].category].push(`cds.${ type }`);
338
- });
337
+ typeCategories[core[type].category].push( `cds.${ type }` );
338
+ } );
339
339
  // Fill type categories with `cds.hana.*` types
340
- Object.keys(coreHana).forEach((type) => {
340
+ Object.keys( coreHana ).forEach( (type) => {
341
341
  if (coreHana[type].category)
342
- typeCategories[coreHana[type].category].push(`cds.hana.${ type }`);
343
- });
342
+ typeCategories[coreHana[type].category].push( `cds.hana.${ type }` );
343
+ } );
344
344
 
345
345
  /** @param {string} typeName */
346
346
  function isIntegerTypeName( typeName ) {
347
- return typeCategories.integer.includes(typeName);
347
+ return typeCategories.integer.includes( typeName );
348
348
  }
349
349
  /** @param {string} typeName */
350
350
  function isDecimalTypeName( typeName ) {
351
- return typeCategories.decimal.includes(typeName);
351
+ return typeCategories.decimal.includes( typeName );
352
352
  }
353
353
  /** @param {string} typeName */
354
354
  function isNumericTypeName( typeName ) {
355
- return isIntegerTypeName(typeName) || isDecimalTypeName(typeName);
355
+ return isIntegerTypeName( typeName ) || isDecimalTypeName( typeName );
356
356
  }
357
357
  /** @param {string} typeName */
358
358
  function isStringTypeName( typeName ) {
359
- return typeCategories.string.includes(typeName);
359
+ return typeCategories.string.includes( typeName );
360
360
  }
361
361
  /** @param {string} typeName */
362
362
  function isDateOrTimeTypeName( typeName ) {
363
- return typeCategories.dateTime.includes(typeName);
363
+ return typeCategories.dateTime.includes( typeName );
364
364
  }
365
365
  /** @param {string} typeName */
366
366
  function isBooleanTypeName( typeName ) {
367
- return typeCategories.boolean.includes(typeName);
367
+ return typeCategories.boolean.includes( typeName );
368
368
  }
369
369
  /** @param {string} typeName */
370
370
  function isBinaryTypeName( typeName ) {
371
- return typeCategories.binary.includes(typeName);
371
+ return typeCategories.binary.includes( typeName );
372
372
  }
373
373
  /** @param {string} typeName */
374
374
  function isGeoTypeName( typeName ) {
375
- return typeCategories.geo.includes(typeName);
375
+ return typeCategories.geo.includes( typeName );
376
376
  }
377
377
  /**
378
378
  * Whether the given type name is a relation, i.e. an association or composition.
@@ -380,7 +380,7 @@ function isGeoTypeName( typeName ) {
380
380
  * @param {string} typeName
381
381
  */
382
382
  function isRelationTypeName( typeName ) {
383
- return typeCategories.relation.includes(typeName);
383
+ return typeCategories.relation.includes( typeName );
384
384
  }
385
385
 
386
386
  /**
@@ -390,10 +390,10 @@ function isRelationTypeName( typeName ) {
390
390
  * @returns {boolean}
391
391
  */
392
392
  function isInReservedNamespace( absolute ) {
393
- return absolute.startsWith( 'cds.') &&
394
- !absolute.match(/^cds\.foundation(\.|$)/) &&
395
- !absolute.match(/^cds\.outbox(\.|$)/) && // Requested by Node runtime
396
- !absolute.match(/^cds\.xt(\.|$)/); // Requested by Mtx
393
+ return absolute === 'cds' || absolute.startsWith( 'cds.' ) &&
394
+ !absolute.match( /^cds\.foundation(\.|$)/ ) &&
395
+ !absolute.match( /^cds\.outbox(\.|$)/ ) && // Requested by Node runtime
396
+ !absolute.match( /^cds\.xt(\.|$)/ ); // Requested by Mtx
397
397
  }
398
398
 
399
399
  /**
@@ -408,7 +408,7 @@ function isInReservedNamespace( absolute ) {
408
408
  * @returns {boolean}
409
409
  */
410
410
  function isBuiltinType( type ) {
411
- return typeof type === 'string' && isInReservedNamespace(type);
411
+ return typeof type === 'string' && isInReservedNamespace( type );
412
412
  }
413
413
 
414
414
  /**
@@ -446,7 +446,7 @@ function initBuiltins( model ) {
446
446
  builtin,
447
447
  location: builtinLocation(),
448
448
  };
449
- setProp( art, '_subArtifacts', Object.create(null) );
449
+ setProp( art, '_subArtifacts', Object.create( null ) );
450
450
  return art;
451
451
  }
452
452
 
@@ -460,7 +460,7 @@ function initBuiltins( model ) {
460
460
  * @returns {object} Artifacts dictionary with the builtin artifacts without prefixes.
461
461
  */
462
462
  function env( builtins, prefix, parent ) {
463
- const artifacts = Object.create(null);
463
+ const artifacts = Object.create( null );
464
464
  for (const name of Object.keys( builtins )) {
465
465
  const absolute = prefix + name;
466
466
  // TODO: reconsider whether to set a type to itself - looks wrong
@@ -481,11 +481,14 @@ function initBuiltins( model ) {
481
481
  }
482
482
 
483
483
  function setMagicVariables( builtins ) {
484
- const artifacts = Object.create(null);
484
+ const artifacts = Object.create( null );
485
485
  for (const name in builtins) {
486
486
  const magic = builtins[name];
487
487
  // TODO: rename to $builtinFunction
488
- const art = { kind: 'builtin', name: { id: name, absolute: name } };
488
+ const art = {
489
+ kind: 'builtin',
490
+ name: { id: name, absolute: '', element: name },
491
+ };
489
492
  artifacts[name] = art;
490
493
 
491
494
  if (magic.$autoElement)
@@ -507,18 +510,14 @@ function initBuiltins( model ) {
507
510
  if (!elements)
508
511
  return;
509
512
 
510
- const names = Object.keys(elements);
513
+ const names = Object.keys( elements );
511
514
  if (names.length > 0 && !art.elements)
512
- art.elements = Object.create(null);
515
+ art.elements = Object.create( null );
513
516
 
514
517
  for (const n of names) {
515
518
  const magic = {
516
- kind: 'builtin',
517
- name: {
518
- id: n,
519
- absolute: art.name.absolute,
520
- element: art.name.element ? `${ art.name.element }.${ n }` : n,
521
- },
519
+ kind: 'builtin', // TODO: '$variable'
520
+ name: { id: n, absolute: '', element: `${ art.name.element }.${ n }` },
522
521
  };
523
522
  // Propagate this property so that it is available for sub-elements.
524
523
  if (art.$uncheckedElements)
@@ -526,7 +525,7 @@ function initBuiltins( model ) {
526
525
  setProp( magic, '_parent', art );
527
526
  // setProp( magic, '_effectiveType', magic );
528
527
  if (elements[n] && typeof elements[n] === 'object')
529
- createMagicElements(magic, elements[n]);
528
+ createMagicElements( magic, elements[n] );
530
529
 
531
530
  art.elements[n] = magic;
532
531
  }