@sap/cds-compiler 6.4.6 → 6.5.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 (68) hide show
  1. package/CHANGELOG.md +42 -1156
  2. package/README.md +1 -10
  3. package/bin/cdsc.js +1 -1
  4. package/doc/IncompatibleChanges_v5.md +436 -0
  5. package/doc/IncompatibleChanges_v6.md +659 -0
  6. package/doc/Versioning.md +3 -7
  7. package/lib/api/main.js +1 -0
  8. package/lib/api/options.js +5 -0
  9. package/lib/api/validate.js +3 -0
  10. package/lib/base/message-registry.js +31 -8
  11. package/lib/base/messages.js +1 -1
  12. package/lib/base/model.js +3 -2
  13. package/lib/checks/actionsFunctions.js +6 -3
  14. package/lib/checks/existsInForbiddenPlaces.js +32 -0
  15. package/lib/checks/validator.js +2 -0
  16. package/lib/compiler/assert-consistency.js +3 -5
  17. package/lib/compiler/checks.js +4 -8
  18. package/lib/compiler/define.js +330 -558
  19. package/lib/compiler/extend.js +302 -18
  20. package/lib/compiler/finalize-parse-cdl.js +2 -10
  21. package/lib/compiler/generate.js +33 -5
  22. package/lib/compiler/kick-start.js +8 -7
  23. package/lib/compiler/populate.js +25 -70
  24. package/lib/compiler/propagator.js +1 -2
  25. package/lib/compiler/resolve.js +4 -13
  26. package/lib/compiler/shared.js +18 -5
  27. package/lib/compiler/tweak-assocs.js +13 -9
  28. package/lib/compiler/utils.js +98 -2
  29. package/lib/compiler/xpr-rewrite.js +2 -1
  30. package/lib/edm/annotations/edmJson.js +9 -6
  31. package/lib/edm/annotations/genericTranslation.js +8 -4
  32. package/lib/edm/csn2edm.js +3 -4
  33. package/lib/edm/edmInboundChecks.js +1 -2
  34. package/lib/edm/edmPreprocessor.js +3 -3
  35. package/lib/gen/CdlGrammar.checksum +1 -1
  36. package/lib/gen/CdlParser.js +1 -1
  37. package/lib/gen/Dictionary.json +16 -1
  38. package/lib/json/from-csn.js +4 -6
  39. package/lib/json/to-csn.js +3 -3
  40. package/lib/model/csnRefs.js +19 -5
  41. package/lib/model/enrichCsn.js +4 -2
  42. package/lib/optionProcessor.js +8 -4
  43. package/lib/render/toSql.js +0 -4
  44. package/lib/render/utils/sql.js +3 -2
  45. package/lib/transform/db/applyTransformations.js +1 -1
  46. package/lib/transform/db/assertUnique.js +3 -3
  47. package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
  48. package/lib/transform/db/assocsToQueries/transformExists.js +14 -3
  49. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  50. package/lib/transform/db/backlinks.js +4 -4
  51. package/lib/transform/db/cdsPersistence.js +4 -4
  52. package/lib/transform/db/constraints.js +4 -4
  53. package/lib/transform/db/expansion.js +5 -5
  54. package/lib/transform/db/flattening.js +4 -5
  55. package/lib/transform/db/rewriteCalculatedElements.js +3 -3
  56. package/lib/transform/db/temporal.js +11 -11
  57. package/lib/transform/draft/db.js +2 -0
  58. package/lib/transform/draft/odata.js +5 -7
  59. package/lib/transform/effective/flattening.js +1 -2
  60. package/lib/transform/forOdata.js +3 -3
  61. package/lib/transform/forRelationalDB.js +1 -1
  62. package/lib/transform/odata/createForeignKeys.js +1 -2
  63. package/lib/transform/odata/flattening.js +1 -2
  64. package/lib/transform/odata/toFinalBaseType.js +52 -55
  65. package/lib/transform/transformUtils.js +3 -4
  66. package/package.json +1 -1
  67. package/doc/CHANGELOG_BETA.md +0 -464
  68. package/doc/CHANGELOG_DEPRECATED.md +0 -235
@@ -19,8 +19,14 @@ const {
19
19
  copyExpr,
20
20
  setExpandStatusAnnotate,
21
21
  linkToOrigin,
22
+ initItemsLinks,
23
+ setMemberParent,
22
24
  createAndLinkCalcDepElement,
25
+ initExprAnnoBlock,
26
+ initDollarSelf,
27
+ initBoundSelfParam,
23
28
  dependsOnSilent,
29
+ storeExtension,
24
30
  pathName,
25
31
  annotationHasEllipsis,
26
32
  } = require('./utils');
@@ -62,7 +68,8 @@ function extend( model ) {
62
68
  resolveTypeArgumentsUnchecked,
63
69
  resolveDefinitionName,
64
70
  attachAndEmitValidNames,
65
- initMembers,
71
+ targetIsTargetAspect,
72
+ checkRedefinition,
66
73
  initSelectItems,
67
74
  } = model.$functions;
68
75
 
@@ -730,12 +737,15 @@ function extend( model ) {
730
737
 
731
738
  for (const ext of extensions) {
732
739
  if (!artReturns && art.kind !== 'annotate') {
733
- warning( 'ext-unexpected-returns', [ ext.returns.location, ext ], {
740
+ const msgId = ext.returns && hasSecurityAnno( ext.returns )
741
+ ? 'ext-unexpected-returns-sec'
742
+ : 'ext-unexpected-returns';
743
+ message( msgId, [ ext.returns.location, ext ], {
734
744
  '#': (isAction ? art.kind : 'std'), keyword: 'returns',
735
745
  }, {
736
746
  std: 'Unexpected $(KEYWORD); only actions and functions have return parameters',
737
747
  action: 'Unexpected $(KEYWORD) for action without return parameter',
738
- // function without `returns` can happen via CSN input!
748
+ // function without `returns` can happen via CSN input! TODO: check in parser
739
749
  function: 'Unexpected $(KEYWORD) for function without return parameter',
740
750
  } );
741
751
  // Do not put completely wrong returns into a “super annotate” statement;
@@ -816,7 +826,8 @@ function extend( model ) {
816
826
  // if we consider this an error or such sub annotates are then ignored
817
827
  // (i.e. not put into the "super annotate").
818
828
  const dict = parent[prop];
819
- if (!dict) {
829
+ const securityRelevant = hasSecurityAnno( ext ) ? '-sec' : '';
830
+ if (!dict && !securityRelevant) {
820
831
  // TODO: check - for each name? - better locations
821
832
  const location = ext._parent?.[prop]?.[$location] || ext.name.location;
822
833
  // Remark: no `elements` dict location with `annotate Main:elem`
@@ -851,37 +862,36 @@ function extend( model ) {
851
862
  }
852
863
  return false;
853
864
  }
854
- else if (!dict[name]) {
865
+ else if (!dict?.[name]) {
855
866
  // TODO: make variant `returns` an auto-variant for ($ART) ?
856
- const inReturns = parent._parent?.returns && parent._parent;
857
- const art = inReturns || parent;
867
+ const inReturns = parent._parent?.returns;
858
868
  switch (prop) {
859
869
  case 'elements':
860
870
  if (canBeDraftMember( name, parent, draftElements ))
861
871
  break;
862
- notFound( 'ext-undefined-element', ext.name.location, ext,
863
- { '#': (inReturns ? 'returns' : 'element'), art, name },
872
+ notFound( `ext-undefined-element${ securityRelevant }`, ext.name.location, ext,
873
+ { '#': (inReturns ? 'returns' : 'element'), name },
864
874
  parent.elements );
865
875
  break;
866
876
  case 'enum': // TODO: extra msg id?
867
- notFound( 'ext-undefined-element', ext.name.location, ext,
868
- { '#': (inReturns ? 'enum-returns' : 'enum'), art, name },
877
+ notFound( `ext-undefined-element${ securityRelevant }`, ext.name.location, ext,
878
+ { '#': (inReturns ? 'enum-returns' : 'enum'), name },
869
879
  parent.enum );
870
880
  break;
871
881
  case 'foreignKeys':
872
- notFound( 'ext-undefined-key', ext.name.location, ext,
882
+ notFound( `ext-undefined-key${ securityRelevant }`, ext.name.location, ext,
873
883
  { name }, parent.foreignKeys );
874
884
  break;
875
885
  case 'params':
876
- notFound( 'ext-undefined-param', ext.name.location, ext,
877
- { '#': 'param', art: parent, name },
886
+ notFound( `ext-undefined-param${ securityRelevant }`, ext.name.location, ext,
887
+ { '#': 'param', name },
878
888
  parent.params );
879
889
  break;
880
890
  case 'actions':
881
891
  if (canBeDraftMember( name, parent, draftBoundActions ))
882
892
  break;
883
- notFound( 'ext-undefined-action', ext.name.location, ext,
884
- { '#': 'action', art: parent, name },
893
+ notFound( `ext-undefined-action${ securityRelevant }`, ext.name.location, ext,
894
+ { '#': 'action', name },
885
895
  parent.actions );
886
896
  break;
887
897
  default:
@@ -924,8 +934,16 @@ function extend( model ) {
924
934
  forEachMember( annotate, createSuperAnnotate );
925
935
  }
926
936
 
937
+ function hasSecurityAnno( ext ) {
938
+ return ext['@restrict'] || ext['@requires'] ||
939
+ Object.keys( ext ).some( prop => prop.startsWith( '@ams.' ) );
940
+ }
941
+
927
942
  function checkRemainingMainExtensions( art, ext ) {
928
- if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
943
+ const refCtx = ext.kind === 'annotate' && hasSecurityAnno( ext )
944
+ ? 'annotate-sec'
945
+ : ext.kind;
946
+ if (!resolvePath( ext.name, refCtx, ext )) // error for extend, info for annotate
929
947
  return;
930
948
 
931
949
  if (art?.builtin) {
@@ -1186,6 +1204,272 @@ function extend( model ) {
1186
1204
  }
1187
1205
  }
1188
1206
 
1207
+ // TODO TMP: copied from ./define.js: -----------------------------------------
1208
+
1209
+ /**
1210
+ * Set property `_parent` for all elements in `parent` to `parent` and do so
1211
+ * recursively for all sub elements.
1212
+ *
1213
+ * If not for extensions: construct === parent
1214
+ *
1215
+ * TODO: separate extension!
1216
+ */
1217
+ function initMembers( construct, parent, block ) {
1218
+ // TODO: split extend from init
1219
+ const main = parent._main || parent;
1220
+ const isQueryExtension = construct.kind === 'extend' && main.query;
1221
+ let obj = initItemsLinks( construct, block );
1222
+ initExprAnnoBlock( construct, block );
1223
+ if (obj.target && targetIsTargetAspect( obj )) {
1224
+ obj.targetAspect = obj.target;
1225
+ delete obj.target;
1226
+ }
1227
+ const { targetAspect } = obj;
1228
+ if (targetAspect) {
1229
+ if (obj.foreignKeys) {
1230
+ error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], construct ] );
1231
+ delete obj.foreignKeys; // continuation semantics: not specified
1232
+ }
1233
+ if (obj.on && !obj.target) {
1234
+ error( 'type-unexpected-on-condition', [ obj.on.location, construct ] );
1235
+ delete obj.on; // continuation semantics: not specified
1236
+ }
1237
+ if (targetAspect.elements)
1238
+ initAnonymousAspect();
1239
+ }
1240
+ if (obj !== parent && obj.elements && parent.enum) { // applying the extension
1241
+ initElementsAsEnum();
1242
+ }
1243
+ else {
1244
+ if (checkDefinitions( construct, parent, 'elements', obj.elements || false ))
1245
+ forEachInOrder( obj, 'elements', init );
1246
+ if (checkDefinitions( construct, parent, 'enum', obj.enum || false ))
1247
+ forEachGeneric( obj, 'enum', init );
1248
+ }
1249
+
1250
+ if (obj.foreignKeys)
1251
+ forEachInOrder( obj, 'foreignKeys', init );
1252
+ if (checkDefinitions( construct, parent, 'actions' ))
1253
+ forEachGeneric( construct, 'actions', init );
1254
+ if (checkDefinitions( construct, parent, 'params' ))
1255
+ forEachInOrder( construct, 'params', init );
1256
+ const { returns } = construct;
1257
+ if (returns) {
1258
+ const { kind } = construct;
1259
+ returns.kind = (kind === 'extend' || kind === 'annotate') ? kind : 'param';
1260
+ init( returns, '' ); // '' is special name for returns parameter
1261
+ }
1262
+ return;
1263
+
1264
+ function initElementsAsEnum() {
1265
+ // in extensions, extended enums are represented as elements
1266
+ let hasElement = false;
1267
+ for (const n in obj.elements) {
1268
+ const e = obj.elements[n];
1269
+ if (e.kind === 'extend')
1270
+ continue;
1271
+ const noVal = e.value?.val === undefined && e.value?.sym === undefined;
1272
+ // TODO: forbid #symbol as enum value
1273
+ if (e.$syntax === 'element' || // `extend … with elements` or `extend with { element … }`
1274
+ noVal && e.$syntax !== 'enum' || // no value in CDL input
1275
+ e.virtual || e.key || e.masked || e.type || e.elements || e.items || e.stored) {
1276
+ // We do not want to complain separately about all element properties:
1277
+ error( 'ext-unexpected-element', [ e.location, construct ],
1278
+ { name: e.name.id, code: 'extend … with enum' },
1279
+ // eslint-disable-next-line @stylistic/max-len
1280
+ 'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
1281
+ // Don't emit 'ext-expecting-enum' if this error is emitted.
1282
+ return;
1283
+ }
1284
+ e.kind = 'enum';
1285
+ if (noVal || e.$syntax !== 'enum')
1286
+ hasElement = true; // warning with CDL input or `name: {}` in CSN input
1287
+ }
1288
+ if (hasElement) {
1289
+ // This message is similar to the one above. In v6, we could probably
1290
+ // turn this warning into an error, remove `$syntax: 'element',
1291
+ // and use the above `ext-unexpected-element` only for CSN input.
1292
+ warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
1293
+ { code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
1294
+ }
1295
+ forEachGeneric( { enum: obj.elements }, 'enum', init );
1296
+ }
1297
+
1298
+ function initAnonymousAspect() {
1299
+ // TODO: main?
1300
+ const inEntity = parent._main?.kind === 'entity';
1301
+ // TODO: also allow indirectly (component in component in entity)?
1302
+ setLink( targetAspect, '_outer', obj );
1303
+ setLink( targetAspect, '_parent', parent._parent );
1304
+ setLink( targetAspect, '_main', null ); // for name resolution
1305
+
1306
+ parent = targetAspect;
1307
+ construct = parent; // avoid extension behavior
1308
+ targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1309
+ setLink( targetAspect, '_block', block );
1310
+ initDollarSelf( targetAspect );
1311
+ // allow ref of up_ in anonymous aspect inside entity
1312
+ // (TODO: complain if used and the managed composition is included into
1313
+ // another entity - might induce auto-redirection):
1314
+ if (inEntity && !targetAspect.elements.up_) {
1315
+ const up = {
1316
+ name: { id: 'up_' },
1317
+ kind: '$navElement',
1318
+ location: obj.location,
1319
+ };
1320
+ setLink( up, '_parent', targetAspect );
1321
+ setLink( up, '_main', targetAspect ); // used on main artifact
1322
+ // recompilation case: both target and targetAspect → allow up_ in that case, too:
1323
+ const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
1324
+ const entity = name && model.definitions[name];
1325
+ if (entity && entity.elements)
1326
+ setLink( up, '_origin', entity.elements.up_ );
1327
+ // processAspectComposition/expand() sets _origin to element of
1328
+ // generated target entity
1329
+ targetAspect.$tableAliases.up_ = up;
1330
+ }
1331
+ obj = targetAspect;
1332
+ }
1333
+
1334
+ function init( elem, name, prop ) {
1335
+ if (!elem.kind) // wrong CSN input
1336
+ elem.kind = dictKinds[prop];
1337
+ if (!elem.name && !elem._outer) {
1338
+ const ref = elem.targetElement || elem.kind === 'element' && elem.value;
1339
+ if (ref && ref.path) {
1340
+ elem.name = Object.assign( { $inferred: 'as' },
1341
+ ref.path[ref.path.length - 1] );
1342
+ }
1343
+ else { // RETURNS, parser robustness
1344
+ elem.name = { id: name, location: elem.location };
1345
+ }
1346
+ }
1347
+
1348
+ if (!elem._block)
1349
+ setLink( elem, '_block', block );
1350
+ // if (!kindProperties[ elem.kind ]) console.log(elem.kind,elem.name)
1351
+ if ((elem.kind === 'extend' || elem.kind === 'annotate')) {
1352
+ storeExtension( elem, name, prop, parent );
1353
+ return;
1354
+ }
1355
+ if (isQueryExtension && elem.kind === 'element') {
1356
+ error( 'extend-query', [ elem.location, construct ], // TODO: searchName ?
1357
+ { code: 'extend projection' },
1358
+ 'Use $(CODE) to add select items to the query entity' );
1359
+ return;
1360
+ }
1361
+
1362
+ const existing = parent[prop]?.[name];
1363
+ const add = construct !== parent && (!existing || elem.$inferred !== 'include');
1364
+ // don't dump with `entity T {}; extend T with { extend e {}; e {}; e {} };`:
1365
+ if (elem.$duplicates === true && add)
1366
+ elem.$duplicates = null;
1367
+ setMemberParent( elem, name, parent, add && prop );
1368
+ checkRedefinition( elem );
1369
+ initMembers( elem, elem, elem._block );
1370
+ if (elem.kind === 'action' || elem.kind === 'function')
1371
+ initBoundSelfParam( elem.params, elem._main );
1372
+
1373
+ // for a correct home path, setMemberParent needed to be called
1374
+
1375
+ if (!elem.value || elem.kind !== 'element' ||
1376
+ elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1377
+ return;
1378
+ // -> it's a calculated element
1379
+ if (!elem.type && elem.value?.type) { // top-level CAST( expr AS type )
1380
+ if (!elem.target)
1381
+ elem.type = { ...elem.value.type, $inferred: 'cast' };
1382
+ }
1383
+ elem.$syntax = 'calc';
1384
+ // TODO: it is not just "syntax" - maybe better test for `$calcDepElement`?
1385
+ createAndLinkCalcDepElement( elem );
1386
+
1387
+ // Special case (hack) for calculated elements that use composition+filter:
1388
+ // See "Notes on `$enclosed`" in `ExposingAssocWithFilter.md` for details.
1389
+ if (elem.target && elem.value.path?.[elem.value.path.length - 1]?.where) {
1390
+ delete elem.type;
1391
+ delete elem.on;
1392
+ delete elem.target;
1393
+ }
1394
+ }
1395
+ }
1396
+
1397
+ // TODO: is only necessary for extensions - make special for extend/annotate
1398
+ function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
1399
+ // TODO: do differently, see also annotateMembers() in resolver
1400
+ // To have been checked by parsers:
1401
+ // - artifacts (CDL-only anyway) only inside [extend] context|service
1402
+ if (!dict)
1403
+ return false;
1404
+ const feature = kindProperties[parent.kind ?? 'element'][prop];
1405
+ if (feature &&
1406
+ (feature === true || construct.kind !== 'extend' || feature( prop, parent )))
1407
+ return true;
1408
+ const location = dict[$location];
1409
+
1410
+ // TODO: a bit inconsistent = not a simple switch on `prop`…
1411
+ if (prop === 'actions') {
1412
+ if (Object.keys( dict ).length) {
1413
+ error( 'def-unexpected-actions', [ location, construct ], {}, // TODO: ext-
1414
+ 'Actions and functions only exist top-level and for entities' ); // or aspects
1415
+ }
1416
+ else {
1417
+ warning( 'ext-ignoring-actions', [ location, construct ], {},
1418
+ 'Actions and functions only exist top-level and for entities' );
1419
+ return false;
1420
+ }
1421
+ }
1422
+ else if (parent.kind === 'action' || parent.kind === 'function') {
1423
+ error( 'ext-unexpected-action', [ construct.location, construct ], { '#': parent.kind }, {
1424
+ std: 'Actions and functions can\'t be extended, only annotated', // TODO: → ext-unsupported
1425
+ action: 'Actions can\'t be extended, only annotated',
1426
+ function: 'Functions can\'t be extended, only annotated',
1427
+ } );
1428
+ }
1429
+ else if (prop === 'params') {
1430
+ if (!feature) {
1431
+ // Note: This error can't be triggered at the moment. But as we likely want to
1432
+ // allow extensions with params in the future, we keep the code.
1433
+ error( 'def-unexpected-params', [ location, construct ], {},
1434
+ 'Parameters only exist for entities, actions or functions' );
1435
+ }
1436
+ else {
1437
+ // remark: we could allow this
1438
+ error( 'extend-with-params', [ location, construct ], {},
1439
+ 'Extending artifacts with parameters is not supported' );
1440
+ }
1441
+ }
1442
+ else if (feature) { // allowed in principle, but not with extend
1443
+ if (!Object.keys( dict ).length) {
1444
+ warning( 'ext-ignoring-elements', [ location, construct ], {},
1445
+ 'Only structures with directly specified elements can be extended by elements' );
1446
+ return false;
1447
+ }
1448
+ else if (parent.$inferred === 'include') { // special case for better error message
1449
+ const variant = (construct.enum || construct.elements) ? 'elements' : 'std';
1450
+ error( 'ref-expected-direct-structure', [ location, construct ],
1451
+ { '#': variant, art: parent } );
1452
+ }
1453
+ else {
1454
+ error( 'extend-type', [ location, construct ], {},
1455
+ 'Only structures or enum types can be extended with elements/enums' );
1456
+ }
1457
+ }
1458
+ else if (prop === 'elements') {
1459
+ error( 'def-unexpected-elements', [ location, construct ], {},
1460
+ 'Elements only exist in entities, types or typed constructs' );
1461
+ }
1462
+ else if (prop === 'columns') {
1463
+ error( 'extend-columns', [ location, construct ], { art: construct } );
1464
+ }
1465
+ else { // if (prop === 'enum') {
1466
+ error( 'def-unexpected-enum', [ location, construct ], {},
1467
+ 'Enum symbols can only be defined for types or typed constructs' );
1468
+ }
1469
+ return construct === parent;
1470
+ }
1471
+
1472
+
1189
1473
  // includes ----------------------------------------------------------------
1190
1474
 
1191
1475
  /**
@@ -1228,7 +1512,7 @@ function extend( model ) {
1228
1512
  * Sets `_ancestors` links on `art`.
1229
1513
  *
1230
1514
  * TODO: try to set `_ancestors` only to entities (but beware “intermediate”
1231
- * non-entities).
1515
+ * non-entities - TODO: make intermediate non-entities stop chain).
1232
1516
  *
1233
1517
  * Examples:
1234
1518
  * ext === art: `entity E : F {}` => add elements of F to E
@@ -42,7 +42,7 @@ function finalizeParseCdl( model ) {
42
42
  for (const ext of late[name]._extensions) {
43
43
  ext.name.id = resolveUncheckedPath( ext.name, '_uncheckedExtension', ext );
44
44
  // Initialize members and define annotations in sub-elements.
45
- initMembers( ext, ext, ext._block, true );
45
+ initMembers( ext );
46
46
  extensions.push( ext );
47
47
  }
48
48
  }
@@ -117,15 +117,7 @@ function finalizeParseCdl( model ) {
117
117
  // containing it. Otherwise some type's aren't properly resolved.
118
118
  // TODO: If resolveTypeUnchecked is reworked, we may be able to simplify this coding.
119
119
  (artifact.$queries || []).forEach( art => resolveTypesForParseCdl( art, artifact ) );
120
- (artifact.columns || []).forEach( (col) => {
121
- // TODO: Can we use "ensureColumnName" of populate.js? It depends on column indices
122
- // _after_ wildcards were expanded, though.
123
- if (!col.name && col.value?.path) {
124
- const last = col.value.path.at(-1);
125
- col.name = { id: last?.id || '', location: last?.location, $inferred: 'as' };
126
- }
127
- resolveTypesForParseCdl( col, artifact );
128
- } );
120
+ (artifact.columns || []).forEach( col => resolveTypesForParseCdl( col, artifact ) );
129
121
  forEachGeneric( artifact, 'mixin', art => resolveTypesForParseCdl( art, artifact ) );
130
122
 
131
123
  // For better error messages for `type of`s in `returns`, we pass the object as the new main.
@@ -31,7 +31,7 @@ function generate( model ) {
31
31
  const {
32
32
  resolvePath,
33
33
  resolveUncheckedPath,
34
- initArtifact,
34
+ initMainArtifact,
35
35
  extendArtifactBefore,
36
36
  applyIncludes,
37
37
  } = model.$functions;
@@ -340,7 +340,7 @@ function generate( model ) {
340
340
  for (const orig of textElems)
341
341
  addElementToTextsEntity( orig, art, fioriEnabled, assertUniqueValue );
342
342
 
343
- initArtifact( art );
343
+ initMainArtifact( art );
344
344
  if (art.includes) {
345
345
  // add elements `locale`, etc. which are required below.
346
346
  applyIncludes( art, art ); // TODO: rethink - can we avoid this if only new extend?
@@ -411,6 +411,8 @@ function generate( model ) {
411
411
  const { location } = art;
412
412
 
413
413
  art.includes = [ createInclude( textsAspectName, location ) ];
414
+ propagateEarly( art, '@cds.autoexpose' );
415
+ propagateEarly( art, '@fiori.draft.enabled' );
414
416
 
415
417
  if (fioriEnabled) {
416
418
  // "Early" include; only for element `locale`, which has its `key` property
@@ -490,6 +492,7 @@ function generate( model ) {
490
492
 
491
493
  /**
492
494
  * Create a structure that can be used as an item in `includes`.
495
+ * TODO: replace by linkMainArtifact()
493
496
  *
494
497
  * @param {string} name
495
498
  * @param {XSN.Location} location
@@ -638,7 +641,7 @@ function generate( model ) {
638
641
  'An aspect $(TARGET) with an element named $(NAME) can\'t be used as target' );
639
642
  return false;
640
643
  }
641
- if (model.definitions[entityName]) {
644
+ if (model.definitions[entityName]) { // TODO: allow a gap (namespace)?
642
645
  error( null, [ location, elem ], { art: entityName },
643
646
  // eslint-disable-next-line @stylistic/max-len
644
647
  'Target entity $(ART) can\'t be created as there is another definition with this name' );
@@ -696,8 +699,11 @@ function generate( model ) {
696
699
  $inferred: 'composition-entity',
697
700
  };
698
701
  if (target.name) { // named target aspect
699
- if (!isDeprecatedEnabled( options, 'noCompositionIncludes' ))
702
+ if (!isDeprecatedEnabled( options, 'noCompositionIncludes' )) {
700
703
  art.includes = [ createInclude( target.name.id, location ) ];
704
+ propagateEarly( art, '@cds.autoexpose' );
705
+ propagateEarly( art, '@fiori.draft.enabled' );
706
+ }
701
707
  setLink( art, '_origin', target );
702
708
  setLink( art, '_upperAspects', [ target, ...(elem._main._upperAspects || []) ] );
703
709
  }
@@ -736,7 +742,7 @@ function generate( model ) {
736
742
 
737
743
  setLink( art, '_block', model.$internal );
738
744
  model.definitions[entityName] = art;
739
- initArtifact( art );
745
+ initMainArtifact( art );
740
746
 
741
747
  // Apply annotations to generated artifact, prepare (not apply!) element
742
748
  // annotations (remark: adding elements is not allowed for generated artifacts):
@@ -858,4 +864,26 @@ function checkTextsLanguageAssocOption( model, options ) {
858
864
  }
859
865
 
860
866
 
867
+ /**
868
+ * Propagate the given `prop` (e.g. annotation) early, i.e. copy it from all `.includes`
869
+ * if they have the property.
870
+ * TEMPORARY copy from ./extend.js
871
+ *
872
+ * @param {XSN.Definition} art
873
+ * @param {string} prop
874
+ */
875
+ function propagateEarly( art, prop ) {
876
+ if (art[prop])
877
+ return;
878
+ for (const ref of art.includes) {
879
+ const aspect = ref._artifact;
880
+ if (aspect) {
881
+ const anno = aspect[prop];
882
+ if (anno && (anno.val !== null || !art[prop]))
883
+ art[prop] = Object.assign( { $inferred: 'include' }, anno );
884
+ }
885
+ }
886
+ }
887
+
888
+
861
889
  module.exports = generate;
@@ -32,6 +32,9 @@ function kickStart( model ) {
32
32
  * - service: the artifact of the embedding service
33
33
  * This function must be called ordered: parent first
34
34
  *
35
+ * Remark: _service links are not already set in define.js, because we might
36
+ * have a @cds.redirection.service in the future
37
+ *
35
38
  * @param {string} name Artifact name
36
39
  */
37
40
  function setAncestorsAndService( name ) {
@@ -50,16 +53,12 @@ function kickStart( model ) {
50
53
  return;
51
54
  // To be removed when nested services are allowed
52
55
  if (!isBetaEnabled( options, 'nestedServices' ) && art.kind === 'service') {
53
- while (parent.kind !== 'service')
54
- parent = parent._parent;
55
- message( 'service-nested-service', [ art.name.location, art ], { art: parent },
56
+ message( 'service-nested-service', [ art.name.location, art ], { art: service },
56
57
  'A service can\'t be nested within a service $(ART)' );
57
58
  }
58
59
  else if (art.kind === 'context') {
59
- while (parent.kind !== 'service')
60
- parent = parent._parent;
61
60
  // TODO: remove this error
62
- message( 'service-nested-context', [ art.name.location, art ], { art: parent },
61
+ message( 'service-nested-context', [ art.name.location, art ], { art: service },
63
62
  'A context can\'t be nested within a service $(ART)' );
64
63
  }
65
64
  }
@@ -73,7 +72,9 @@ function kickStart( model ) {
73
72
  // Service1.E = projection on E
74
73
 
75
74
  // Remark: _ancestors are also set with includes, and there also for aspects,
76
- // types and events.
75
+ // types and events (TODO: entity only)
76
+ //
77
+ // Remark: _ancestors are also tested in populate.js for minmal exposure
77
78
  const chain = [];
78
79
  const autoexposed = annotationVal( art['@cds.autoexposed'] );
79
80
  // no need to set preferredRedirectionTarget in the while loop as we would