@sap/cds-compiler 5.9.4 → 6.0.12

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 (114) hide show
  1. package/CHANGELOG.md +117 -319
  2. package/README.md +1 -1
  3. package/bin/cds_update_identifiers.js +3 -5
  4. package/bin/cdsc.js +24 -9
  5. package/bin/cdshi.js +1 -1
  6. package/bin/cdsse.js +4 -4
  7. package/doc/CHANGELOG_BETA.md +11 -0
  8. package/doc/CHANGELOG_DEPRECATED.md +29 -0
  9. package/lib/api/main.js +8 -5
  10. package/lib/api/options.js +12 -10
  11. package/lib/base/builtins.js +1 -0
  12. package/lib/base/message-registry.js +191 -99
  13. package/lib/base/messages.js +35 -21
  14. package/lib/base/model.js +14 -24
  15. package/lib/checks/actionsFunctions.js +10 -20
  16. package/lib/checks/annotationsOData.js +1 -1
  17. package/lib/checks/elements.js +35 -10
  18. package/lib/checks/enums.js +31 -0
  19. package/lib/checks/foreignKeys.js +2 -2
  20. package/lib/checks/hasPersistedElements.js +5 -0
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedWithoutKeys.js +5 -4
  23. package/lib/checks/queryNoDbArtifacts.js +10 -8
  24. package/lib/checks/types.js +5 -5
  25. package/lib/checks/validator.js +6 -4
  26. package/lib/compiler/assert-consistency.js +13 -9
  27. package/lib/compiler/checks.js +20 -52
  28. package/lib/compiler/define.js +31 -6
  29. package/lib/compiler/extend.js +5 -1
  30. package/lib/compiler/generate.js +14 -17
  31. package/lib/compiler/populate.js +8 -31
  32. package/lib/compiler/propagator.js +21 -35
  33. package/lib/compiler/resolve.js +64 -29
  34. package/lib/compiler/shared.js +16 -4
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +1 -1
  37. package/lib/edm/annotations/edmJson.js +23 -20
  38. package/lib/edm/annotations/genericTranslation.js +12 -10
  39. package/lib/edm/csn2edm.js +50 -56
  40. package/lib/edm/edm.js +33 -28
  41. package/lib/edm/edmInboundChecks.js +2 -2
  42. package/lib/edm/edmPreprocessor.js +54 -88
  43. package/lib/edm/edmUtils.js +9 -12
  44. package/lib/gen/BaseParser.js +63 -52
  45. package/lib/gen/CdlGrammar.checksum +1 -1
  46. package/lib/gen/CdlParser.js +1153 -1165
  47. package/lib/gen/Dictionary.json +21 -1
  48. package/lib/json/from-csn.js +70 -43
  49. package/lib/json/to-csn.js +6 -8
  50. package/lib/language/multiLineStringParser.js +3 -2
  51. package/lib/main.d.ts +58 -24
  52. package/lib/model/cloneCsn.js +3 -0
  53. package/lib/model/csnUtils.js +28 -39
  54. package/lib/model/xprAsTree.js +23 -9
  55. package/lib/modelCompare/compare.js +5 -4
  56. package/lib/optionProcessor.js +24 -17
  57. package/lib/parsers/AstBuildingParser.js +81 -25
  58. package/lib/parsers/XprTree.js +57 -3
  59. package/lib/parsers/identifiers.js +1 -1
  60. package/lib/parsers/index.js +0 -3
  61. package/lib/render/manageConstraints.js +25 -25
  62. package/lib/render/toCdl.js +173 -170
  63. package/lib/render/toHdbcds.js +126 -128
  64. package/lib/render/toRename.js +7 -7
  65. package/lib/render/toSql.js +128 -125
  66. package/lib/render/utils/common.js +47 -22
  67. package/lib/render/utils/delta.js +25 -25
  68. package/lib/render/utils/operators.js +2 -2
  69. package/lib/render/utils/pretty.js +5 -5
  70. package/lib/render/utils/sql.js +13 -13
  71. package/lib/render/utils/standardDatabaseFunctions.js +115 -103
  72. package/lib/render/utils/unique.js +4 -4
  73. package/lib/transform/db/applyTransformations.js +1 -1
  74. package/lib/transform/db/assertUnique.js +2 -2
  75. package/lib/transform/db/associations.js +6 -7
  76. package/lib/transform/db/assocsToQueries/utils.js +4 -5
  77. package/lib/transform/db/backlinks.js +12 -9
  78. package/lib/transform/db/cdsPersistence.js +8 -7
  79. package/lib/transform/db/constraints.js +13 -10
  80. package/lib/transform/db/expansion.js +7 -3
  81. package/lib/transform/db/flattening.js +4 -14
  82. package/lib/transform/db/processSqlServices.js +2 -1
  83. package/lib/transform/db/temporal.js +5 -7
  84. package/lib/transform/db/views.js +2 -4
  85. package/lib/transform/draft/db.js +8 -8
  86. package/lib/transform/draft/odata.js +10 -7
  87. package/lib/transform/forOdata.js +10 -5
  88. package/lib/transform/forRelationalDB.js +5 -75
  89. package/lib/transform/localized.js +1 -1
  90. package/lib/transform/odata/createForeignKeys.js +11 -10
  91. package/lib/transform/odata/flattening.js +8 -4
  92. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
  93. package/lib/transform/odata/typesExposure.js +3 -3
  94. package/lib/transform/transformUtils.js +4 -8
  95. package/lib/transform/translateAssocsToJoins.js +14 -7
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
  97. package/lib/utils/objectUtils.js +0 -17
  98. package/package.json +10 -13
  99. package/share/messages/def-upcoming-virtual-change.md +1 -1
  100. package/LICENSE +0 -37
  101. package/bin/cds_remove_invalid_whitespace.js +0 -138
  102. package/doc/CHANGELOG_ARCHIVE.md +0 -3604
  103. package/lib/gen/genericAntlrParser.js +0 -3
  104. package/lib/gen/language.checksum +0 -1
  105. package/lib/gen/language.interp +0 -456
  106. package/lib/gen/language.tokens +0 -180
  107. package/lib/gen/languageLexer.interp +0 -439
  108. package/lib/gen/languageLexer.js +0 -1483
  109. package/lib/gen/languageLexer.tokens +0 -167
  110. package/lib/gen/languageParser.js +0 -24941
  111. package/lib/language/antlrParser.js +0 -205
  112. package/lib/language/errorStrategy.js +0 -646
  113. package/lib/language/genericAntlrParser.js +0 -1572
  114. package/lib/parsers/CdlGrammar.g4 +0 -2070
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  /* eslint max-statements-per-line:off */
4
- const { setProp, isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
4
+ const { setProp, isBetaEnabled } = require('../base/model');
5
5
  const {
6
6
  forEachDefinition, forEachGeneric, forEachMemberRecursively,
7
7
  isEdmPropertyRendered, getUtils,
8
- applyTransformations, transformAnnotationExpression, findAnnotationExpression,
8
+ applyTransformations, applyTransformationsOnNonDictionary,
9
+ transformAnnotationExpression, findAnnotationExpression,
9
10
  cardinality2str,
10
11
  } = require('../model/csnUtils');
11
12
  const { isBuiltinType, isMagicVariable } = require('../base/builtins');
@@ -274,7 +275,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
274
275
  // if this definition has a root def and the root def is not the service/schema name
275
276
  // => service C { type D.E }, replace the prefix dots with underscores
276
277
  if (rootDef && defName !== rootDef && rootDef !== edmUtils.getSchemaPrefix(defName)) {
277
- const newDefName = `${ rootDef }.${ defName.replace(`${ rootDef }.`, '').replace(/\./g, '_') }`;
278
+ const underscoredDefName = defName
279
+ .replace(`${ rootDef }.`, '')
280
+ .replace(/\./g, '_');
281
+ const newDefName = `${ rootDef }.${ underscoredDefName }`;
278
282
  // store renamed types in correlation maps for later renaming
279
283
  if (def.kind === 'entity')
280
284
  dotEntityNameMap[defName] = newDefName;
@@ -322,11 +326,32 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
322
326
  if (node.$path && dotEntityNameMap[node.$path[1]])
323
327
  node.$path[1] = dotEntityNameMap[node.$path[1]];
324
328
 
329
+ if (node.projection || node.query) {
330
+ applyTransformationsOnNonDictionary(node.projection || node.query, undefined, {
331
+ ref: (parent, prop, ref) => {
332
+ if (dotEntityNameMap[ref[0]])
333
+ parent.ref[0] = dotEntityNameMap[ref[0]];
334
+ },
335
+ }, { directDict: true });
336
+ }
337
+
325
338
  rewriteReferencesInActions(node);
326
339
  };
327
340
 
328
- forEachMemberRecursively(def, applyOnNode);
341
+ const applyOnAnnoXprs = (node) => {
342
+ Object.keys(node).filter(pn => pn[0] === '@').forEach((anno) => {
343
+ transformAnnotationExpression(node, anno, {
344
+ ref: (elemRef, _prop, ref) => {
345
+ if (dotEntityNameMap[ref[0]] || dotTypeNameMap[ref[0]])
346
+ elemRef.ref[0] = dotEntityNameMap[ref[0]] || dotTypeNameMap[ref[0]];
347
+ },
348
+ });
349
+ });
350
+ };
351
+
352
+ forEachMemberRecursively(def, [ applyOnNode, applyOnAnnoXprs ]);
329
353
  applyOnNode(def);
354
+ applyOnAnnoXprs(def);
330
355
  // handle unbound action/function and params in views
331
356
  rewriteReferencesInActions(def);
332
357
  };
@@ -518,7 +543,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
518
543
  // create the Type Definition
519
544
  // modify the original parameter entity with backlink and new name
520
545
  if (csn.definitions[typeEntityName])
521
- error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: typeEntityName });
546
+ error('odata-duplicate-definition', [ 'definitions', entityName ], { '#': 'std', name: typeEntityName });
522
547
  else
523
548
  entityCsn.name = typeEntityName;
524
549
  setProp(entityCsn, '$entitySetName', typeEntitySetName);
@@ -649,7 +674,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
649
674
  // proxies are registered in model separately
650
675
  if (!isProxy) {
651
676
  if (csn.definitions[parameterCsn.name]) {
652
- error('odata-definition-exists', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
677
+ error('odata-duplicate-definition', [ 'definitions', entityName ], { '#': 'std', name: parameterCsn.name });
653
678
  }
654
679
  else {
655
680
  csn.definitions[parameterCsn.name] = parameterCsn;
@@ -694,7 +719,8 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
694
719
  if (!edmUtils.isStructuredArtifact(def))
695
720
  return;
696
721
 
697
- let keys = Object.create(null);
722
+ const keys = Object.create(null);
723
+ // eslint-disable-next-line
698
724
  const validFrom = [];
699
725
  const validKey = [];
700
726
 
@@ -741,38 +767,11 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
741
767
  edmAnnoPreproc.applyAppSpecificLateCsnTransformationOnElement(options, element, def, error);
742
768
  }, [], true, { elementsOnly: true });
743
769
 
744
- if (!isDeprecatedEnabled(options, '_v1KeysForTemporal')) {
745
- // if artifact has a cds.valid.key mention it as @Core.AlternateKey
746
- if (validKey.length) {
747
- const altKeys = [ { Key: [] } ];
748
- validKey.forEach(vk => altKeys[0].Key.push( { Name: vk.name, Alias: vk.name } ) );
749
- edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
750
- }
751
- }
752
- else if (validKey.length) {
753
- // if artifact has a cds.valid.key make this the only primary key and
754
- // add all @cds.valid.from + original primary keys as alternate keys
755
- // @Core.AlternateKeys: [{ Key: [ { Name: 'slID', Alias: 'slID' }, { Name: 'validFrom', Alias: 'validFrom'} ] }]
770
+ // if artifact has a cds.valid.key mention it as @Core.AlternateKey
771
+ if (validKey.length) {
756
772
  const altKeys = [ { Key: [] } ];
757
- Object.entries(([ kn, k ]) => {
758
- altKeys[0].Key.push( { Name: kn, Alias: kn } );
759
- delete k.key;
760
- });
761
- validFrom.forEach((e) => {
762
- altKeys[0].Key.push( { Name: e.name, Alias: e.name } );
763
- });
773
+ validKey.forEach(vk => altKeys[0].Key.push( { Name: vk.name, Alias: vk.name } ) );
764
774
  edmUtils.assignAnnotation(def, '@Core.AlternateKeys', altKeys);
765
- keys = Object.create(null);
766
- validKey.forEach((e) => {
767
- e.key = true;
768
- keys[e.name] = e;
769
- });
770
- }
771
- else {
772
- validFrom.forEach((e) => {
773
- e.key = true;
774
- keys[e.name] = e;
775
- });
776
775
  }
777
776
 
778
777
  // prepare the structure itself
@@ -892,7 +891,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
892
891
  // eslint-disable-next-line eqeqeq
893
892
  const srcMult = (partnerCsn.cardinality.src == 1) ? '0..1' : '*';
894
893
  const newMult
895
- = (element.cardinality?.min == 1 && element.cardinality?.max == 1) // eslint-disable-line eqeqeq
894
+ = (element.cardinality?.min == 1 && element.cardinality?.max == 1) // eslint-disable-line
896
895
  ? 1
897
896
  : (element.cardinality?.max === '*' || element.cardinality?.max > 1)
898
897
  ? '*'
@@ -1040,7 +1039,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1040
1039
  else if (!assocOK) {
1041
1040
  // if there is already a proxy (generated by a 'good' association)
1042
1041
  // and this association is not a good one, don't expose this association.
1043
- muteNavProp(element, 'oncond');
1042
+ muteNavProp(element);
1044
1043
  return;
1045
1044
  }
1046
1045
  if (proxy) {
@@ -1060,7 +1059,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1060
1059
  }
1061
1060
  }
1062
1061
  else {
1063
- muteNavProp(element, assocOK ? 'std' : 'oncond');
1062
+ muteNavProp(element);
1064
1063
  return;
1065
1064
  }
1066
1065
  }
@@ -1072,12 +1071,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1072
1071
  });
1073
1072
  }
1074
1073
 
1075
- function muteNavProp( elt, msg = 'std' ) {
1074
+ function muteNavProp( elt ) {
1076
1075
  edmUtils.assignAnnotation(elt, '@odata.navigable', false);
1077
- if (elt._target['@cds.autoexpose'] !== false) {
1078
- warning('odata-navigation', elt.$path,
1079
- { target: elt._target.name, service: globalSchemaPrefix, '#': msg });
1080
- }
1076
+ if (elt._target['@cds.autoexpose'] !== false)
1077
+ info('odata-navigation', elt.$path, { target: elt._target.name, service: globalSchemaPrefix });
1081
1078
  }
1082
1079
 
1083
1080
  function createSchemaRefFor( targetSchemaName ) {
@@ -1100,8 +1097,9 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1100
1097
 
1101
1098
  // 1) construct the proxy definition
1102
1099
  // proxyDefinitionName: strip the serviceName and replace '.' with '_'
1103
- const defName
1104
- = `${ assoc._target.name.replace(`${ proxySchemaName }.`, '').replace(/\./g, '_') }`;
1100
+ const defName = assoc._target.name
1101
+ .replace(`${ proxySchemaName }.`, '')
1102
+ .replace(/\./g, '_');
1105
1103
 
1106
1104
  // fullName: Prepend serviceName and if in same service add '_proxy'
1107
1105
  const proxy = isParamProxy
@@ -1152,10 +1150,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1152
1150
  if (!newElt) {
1153
1151
  if (csnUtils.isAssocOrComposition(e)) {
1154
1152
  if (!e.on && e.keys) {
1155
- if (options.odataNoTransitiveProxies)
1156
- newElt = convertManagedAssocIntoStruct(e);
1157
- else
1158
- newElt = createProxyOrSchemaRefForManagedAssoc(e);
1153
+ newElt = createProxyOrSchemaRefForManagedAssoc(e);
1159
1154
  }
1160
1155
  else {
1161
1156
  info(null, [ 'definitions', struct.name, 'elements', assoc.name ],
@@ -1306,7 +1301,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1306
1301
  });
1307
1302
  }
1308
1303
  else if (elem.keys && !elem.on) {
1309
- // a primary key can never be an unmanaged association
1304
+ // a primary key can never be an unmanaged association
1310
1305
  type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
1311
1306
  }
1312
1307
  if (forceToNotNull) {
@@ -1316,7 +1311,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1316
1311
  newElt.cardinality = {};
1317
1312
  newElt.cardinality.min = 1;
1318
1313
  }
1319
- // if odata-spec-violation-key-null is checking on min>1, this can be an else
1314
+ // if odata-unexpected-nullable-key is checking on min>1, this can be an else
1320
1315
  newElt.notNull = true;
1321
1316
  }
1322
1317
  setProp(type.elements[elemName], 'name', elem.name);
@@ -1326,35 +1321,6 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1326
1321
  }
1327
1322
  }
1328
1323
 
1329
- // Convert a managed association into a structured type and
1330
- // eliminate nested foreign key associations
1331
- function convertManagedAssocIntoStruct( e ) {
1332
- const newElt = cloneCsnNonDict(e, options);
1333
- newElt.elements = Object.create(null);
1334
- // remove all unwanted garbage
1335
- delete newElt.keys;
1336
- delete newElt.target;
1337
- delete newElt.type;
1338
- // if this association has no keys or if it is a redirected parameterized entity,
1339
- // use the primary keys of the target
1340
- const keys = (!e._target.$isParamEntity && e.keys) ||
1341
- Object.keys(e._target.$keys).map(k => ({ ref: [ k ] }));
1342
- keys.forEach((k) => {
1343
- let art = e._target || csnUtils.getCsnDef(e.target);
1344
- for (const ps of k.ref)
1345
- art = art.elements[ps];
1346
-
1347
- // art is in the target side, clone it and remove key property
1348
- const cloneArt = cloneCsnNonDict(art, options);
1349
- setProp(cloneArt, 'name', art.name);
1350
- if (e.key)
1351
- cloneArt.notNull = true;
1352
- delete cloneArt.key;
1353
- newElt.elements[art.name] = cloneArt;
1354
- });
1355
- return newElt;
1356
- }
1357
-
1358
1324
  // create a new element and wire the proxy as new target.
1359
1325
  // Create a new proxy if:
1360
1326
  // 1) source and target schema names are different (otherwise)
@@ -1509,8 +1475,8 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1509
1475
  }
1510
1476
  else if (alreadyRegistered && !alreadyRegistered.$proxy &&
1511
1477
  alreadyRegistered.kind !== 'entity') {
1512
- warning('odata-definition-exists', [ 'definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name ],
1513
- { '#': 'proxy', name: fqProxyName, kind: alreadyRegistered.kind });
1478
+ warning('odata-duplicate-proxy', [ 'definitions', proxy.$origin._parent.name, 'elements', proxy.$origin.name ],
1479
+ { name: fqProxyName, kind: alreadyRegistered.kind });
1514
1480
  }
1515
1481
  }
1516
1482
  else if (!schemas[fqSchemaName]) {
@@ -1639,7 +1605,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1639
1605
  else if (eltCsn.target && !eltCsn.on) {
1640
1606
  // if this association has no keys or if it is a redirected parameterized entity,
1641
1607
  // use the primary keys of the target
1642
- const keys = (!eltCsn._target.$isParamEntity && eltCsn.keys) ||
1608
+ const keys = (!eltCsn._target.$isParamEntity && !eltCsn.on && (eltCsn.keys ?? [])) ||
1643
1609
  Object.keys(eltCsn._target.$keys).map(k => ({ ref: [ k ] }));
1644
1610
  let pathSegment = prefix;
1645
1611
  keys.forEach((k) => {
@@ -1668,7 +1634,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1668
1634
  const eltDef = elt.items || elt;
1669
1635
  if ((!elt.key && (eltDef.notNull === undefined || eltDef.notNull === false)) ||
1670
1636
  elt.key && (eltDef.notNull !== undefined && eltDef.notNull === false)) {
1671
- message('odata-spec-violation-key-null', location,
1637
+ message('odata-unexpected-nullable-key', location,
1672
1638
  { name: pathSegment || elt.name, '#': !pathSegment ? 'std' : 'scalar' });
1673
1639
  }
1674
1640
  // many is either directly on elements or on the type
@@ -1682,7 +1648,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1682
1648
  (options.odataFormat !== 'flat' && !options.odataForeignKeys) &&
1683
1649
  elt.cardinality?.max && elt.cardinality.max !== 1) {
1684
1650
  // many primary key can be induced by a many parameter of a view
1685
- message('odata-spec-violation-key-array', location,
1651
+ message('odata-unexpected-arrayed-key', location,
1686
1652
  {
1687
1653
  name: pathSegment || elt.name,
1688
1654
  value: cardinality2str(elt),
@@ -1714,7 +1680,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1714
1680
  'Edm.TimeOfDay': 1,
1715
1681
  };
1716
1682
  if (!(edmType in legalEdmTypes)) {
1717
- message('odata-spec-violation-key-type', location,
1683
+ message('odata-invalid-key-type', location,
1718
1684
  {
1719
1685
  name: pathSegment, type: type.type, id: edmType, '#': pathSegment ? 'std' : 'scalar',
1720
1686
  });
@@ -344,7 +344,7 @@ function finalizeReferentialConstraints( csn, assocCsn, options, info ) {
344
344
  const renderedKeys = Object.values(principalEntity.$keys).filter(isConstraintCandidate).map(v => v.name);
345
345
  if (options.isV2() && intersect(renderedKeys, remainingPrincipalRefs).length !== renderedKeys.length) {
346
346
  if (options.odataV2PartialConstr) {
347
- info('odata-spec-violation-constraints',
347
+ info('odata-incomplete-constraints',
348
348
  [ 'definitions', assocCsn._parent.name, 'elements', assocCsn.name ], { version: '2.0' });
349
349
  }
350
350
  else {
@@ -379,7 +379,7 @@ function finalizeReferentialConstraints( csn, assocCsn, options, info ) {
379
379
  const renderedKeys = Object.values(assocCsn._target.$keys).filter(isConstraintCandidate).map(v => v.name);
380
380
  if (options.isV2() && intersect(renderedKeys, remainingPrincipalRefs).length !== renderedKeys.length) {
381
381
  if (options.odataV2PartialConstr) {
382
- info('odata-spec-violation-constraints',
382
+ info('odata-incomplete-constraints',
383
383
  [ 'definitions', assocCsn._parent.name, 'elements', assocCsn.name ], { version: '2.0' } );
384
384
  }
385
385
  else {
@@ -446,23 +446,20 @@ function determineMultiplicity( csn ) {
446
446
  if (!csn.cardinality)
447
447
  csn.cardinality = Object.create(null);
448
448
 
449
- if (!csn.cardinality.src)
450
- csn.cardinality.src = isAssoc ? '*' : '1';
451
- if (!csn.cardinality.min)
452
- csn.cardinality.min = 0;
453
- if (!csn.cardinality.max)
454
- csn.cardinality.max = 1;
449
+ csn.cardinality.src ??= isAssoc ? '*' : '1';
450
+ csn.cardinality.min ??= 0;
451
+ csn.cardinality.max ??= 1;
455
452
 
456
453
  const srcCardinality
457
- = (csn.cardinality.src == 1) // eslint-disable-line eqeqeq
458
- ? (!isAssoc || csn.cardinality.srcmin == 1) // eslint-disable-line eqeqeq
454
+ = (csn.cardinality.src == 1) // eslint-disable-line
455
+ ? (!isAssoc || csn.cardinality.srcmin == 1) // eslint-disable-line
459
456
  ? '1'
460
457
  : '0..1'
461
458
  : '*';
462
- const tgtCardinality
459
+ const tgtCardinality // eslint-disable-next-line no-nested-ternary
463
460
  = (csn.cardinality.max > 1 || csn.cardinality.max === '*')
464
461
  ? '*'
465
- : (csn.cardinality.min == 1) // eslint-disable-line eqeqeq
462
+ : (csn.cardinality.min == 1) // eslint-disable-line
466
463
  ? '1'
467
464
  : '0..1';
468
465