@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
@@ -49,14 +49,14 @@
49
49
  // inspectRef() - to enable caching.
50
50
  //
51
51
  // If any of these conditions are not given, our functions usually simply
52
- // throws an exception (which might even be a plain TypeError), but it might
52
+ // throw an exception (which might even be a plain TypeError), but it might
53
53
  // also just return any value. CSN processors can provide user-friendly error
54
54
  // messages by calling the Core Compiler in case of exceptions. For details,
55
55
  // see internalDoc/CoreCompiler.md#use-of-the-core-compiler-for-csn-processors.
56
56
 
57
57
  // During a transformation, care must be taken to adhere to these conditions.
58
58
  // E.g. a structure flattening function cannot create an element `s_x` and
59
- // delete `s` and then still expects inspectRef() to be able to resolve a
59
+ // delete `s` and then still expect inspectRef() to be able to resolve a
60
60
  // reference `['s', 'x']`.
61
61
 
62
62
  // There are currently 3 (SQL) backend issues for which we provide a workaround:
@@ -205,15 +205,14 @@ const artifactProperties = [ 'elements', 'columns', 'keys', 'mixin', 'enum',
205
205
  // - lexical: false | Function - determines where to look first for “lexical names”
206
206
  // - dynamic: String - describes the dynamic environment (if in query)
207
207
  // - assoc: String, with dynamic: 'global' - what to do with assoc steps
208
- // * 'static': visit elements of anonymous aspect if not last ref item
209
208
  // * 'target': always follow target, including last ref item
210
- // * other (& not provided) = follow target if not last ref item
209
+ // * other (& not provided) = follow target (targetAspect if no target) if not last ref item
211
210
  const referenceSemantics = {
212
211
  $init: { $initOnly: true },
213
- type: { lexical: false, dynamic: 'global', assoc: 'static' },
214
- includes: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
215
- target: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
216
- targetAspect: { lexical: false, dynamic: 'global', assoc: 'static' },
212
+ type: { lexical: false, dynamic: 'global' },
213
+ includes: { lexical: false, dynamic: 'global' },
214
+ target: { lexical: false, dynamic: 'global' },
215
+ targetAspect: { lexical: false, dynamic: 'global' },
217
216
  from: { lexical: false, dynamic: 'global', assoc: 'target' },
218
217
  keys: { lexical: false, dynamic: 'target' },
219
218
  keys_origin: { lexical: false, dynamic: 'target' },
@@ -265,6 +264,9 @@ function csnRefs( csn, universalReady ) {
265
264
  queryOrMain,
266
265
  getColumn: elem => getCache( elem, '_column' ),
267
266
  getElement: col => getCache( col, '_element' ),
267
+ /** Returns the column's name; either explicit, implicit or internal one. */
268
+ getColumnName: col => getCache( col, '$as' ),
269
+ $getQueries: def => getCache( def, '$queries' ), // unstable API
268
270
  initDefinition,
269
271
  dropDefinitionCache,
270
272
  targetAspect,
@@ -314,7 +316,7 @@ function csnRefs( csn, universalReady ) {
314
316
  env = effectiveType( env.items );
315
317
  if (env.elements) // shortcut
316
318
  return env;
317
- const target = (staticAssoc ? targetAspect( env ) : env.target);
319
+ const target = (staticAssoc ? targetAspect( env ) : env.target || env.targetAspect);
318
320
  if (typeof target !== 'string')
319
321
  return target || env;
320
322
  const def = csn.definitions[target];
@@ -352,7 +354,7 @@ function csnRefs( csn, universalReady ) {
352
354
 
353
355
  function fromArtifactRef( ref ) {
354
356
  // do not cache while there is second param
355
- const art = artifactFromRef( ref, true );
357
+ const art = artifactFromRef( ref );
356
358
  if (art)
357
359
  return art;
358
360
  throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
@@ -363,7 +365,7 @@ function csnRefs( csn, universalReady ) {
363
365
  let art = csn.definitions[pathId( head )];
364
366
  initDefinition( art );
365
367
  for (const elem of tail) {
366
- const env = navigationEnv( art, true );
368
+ const env = navigationEnv( art );
367
369
  art = env.elements[pathId( elem )];
368
370
  }
369
371
  return art;
@@ -758,7 +760,7 @@ function csnRefs( csn, universalReady ) {
758
760
  qcache.elements = elements;
759
761
  const { columns } = qcache._select;
760
762
  if (elements && columns)
761
- columns.map( c => initColumnElement( c, qcache ) );
763
+ columns.map( (col, colIndex) => initColumnElement( col, colIndex, qcache ) );
762
764
  else if (columns && !elements)
763
765
  throw new ModelError( `Query elements not available: ${ Object.keys( (index ? qcache._select : main) ).join('+') }`);
764
766
  } );
@@ -784,16 +786,19 @@ function csnRefs( csn, universalReady ) {
784
786
  : { $aliases: Object.create(null), $next: pcache.$next };
785
787
  }
786
788
 
787
- function initColumnElement( col, parentElementOrQueryCache ) {
789
+ function initColumnElement( col, colIndex, parentElementOrQueryCache ) {
788
790
  if (col === '*')
789
791
  return;
790
792
  if (col.inline) {
791
- col.inline.map( c => initColumnElement( c, parentElementOrQueryCache ) );
793
+ col.inline.map( c => initColumnElement( c, null, parentElementOrQueryCache ) );
792
794
  return;
793
795
  }
794
796
  setCache( col, '_parent', // not set for query (has property _select)
795
797
  !parentElementOrQueryCache._select && parentElementOrQueryCache );
796
- const as = col.as || col.func || implicitAs( col.ref );
798
+ let as = columnAlias( col );
799
+ if (!as && colIndex !== null)
800
+ as = `$_column_${ colIndex + 1 }`;
801
+ setCache( col, '$as', as );
797
802
  let type = parentElementOrQueryCache;
798
803
  while (type.items)
799
804
  type = type.items;
@@ -801,7 +806,7 @@ function csnRefs( csn, universalReady ) {
801
806
  if (elem) // TODO to.sql: something is strange if this is not set
802
807
  setCache( elem, '_column', col );
803
808
  if (col.expand)
804
- col.expand.map( c => initColumnElement( c, elem ) );
809
+ col.expand.map( c => initColumnElement( c, null, elem ) );
805
810
  }
806
811
 
807
812
  // property name convention in cache:
@@ -843,10 +848,11 @@ function csnRefs( csn, universalReady ) {
843
848
  }
844
849
  }
845
850
 
846
- // Return value of a query SELECT for the query node, or the main artifact,
847
- // i.e. a value with an `elements` property.
848
- // TODO: only used in forRelationalDB - move somewhere else
849
851
  /**
852
+ * Return value of a query SELECT for the query node, or the main artifact,
853
+ * i.e. a value with an `elements` property.
854
+ * TODO: only used in forRelationalDB - move somewhere else
855
+ *
850
856
  * @param {object} query node (object with SET or SELECT property)
851
857
  * @param {object} main definition
852
858
  */
@@ -1122,6 +1128,17 @@ function isSelectQuery( query ) {
1122
1128
  return true;
1123
1129
  }
1124
1130
 
1131
+ /**
1132
+ * Alias is either explicit or implicit from reference or function without arguments.
1133
+ * If the column is an expression without explicit alias, `false` is returned.
1134
+ * Use csnRefs.getColumnName() instead.
1135
+ *
1136
+ * @returns {string|false}
1137
+ */
1138
+ function columnAlias( col ) {
1139
+ return col.as || (!col.args && col.func) || (col.ref && implicitAs( col.ref ));
1140
+ }
1141
+
1125
1142
  module.exports = {
1126
1143
  csnRefs,
1127
1144
  traverseQuery,
@@ -1129,4 +1146,5 @@ module.exports = {
1129
1146
  implicitAs,
1130
1147
  analyseCsnPath,
1131
1148
  pathId,
1149
+ columnAlias,
1132
1150
  };
@@ -69,7 +69,8 @@ function getUtils( model, universalReady ) {
69
69
  };
70
70
 
71
71
  /**
72
- * Compute and return $combined for the given query.
72
+ * Compute and return $combined sources for the given query,
73
+ * that is, a map of elements that combine e.g. UNION sources.
73
74
  *
74
75
  * @param {CSN.Query} query
75
76
  * @returns {object}
@@ -291,11 +292,8 @@ function getUtils( model, universalReady ) {
291
292
  if (!absoluteName.startsWith('@'))
292
293
  throw new CompilerAssertion(`Annotation name should start with "@": ${ absoluteName }`);
293
294
 
294
- // Only overwrite if undefined or null
295
- if (node[absoluteName] === undefined || node[absoluteName] === null) {
296
- // Assemble the annotation
297
- node[absoluteName] = theValue;
298
- }
295
+ // Assemble the annotation
296
+ node[absoluteName] ??= theValue;
299
297
  }
300
298
 
301
299
  /**
@@ -332,12 +330,18 @@ function getUtils( model, universalReady ) {
332
330
  * - Does _not_ return the underlying type definition! It is an object with all relevant
333
331
  * type properties collected while traversing the type chain!
334
332
  *
335
- * @todo Rename to e.g. getFinalTypeInfo()
333
+ * @param {string|object} type
334
+ * Type as string or type ref, i.e. `{ ref: [...] }`
335
+ *
336
+ * @param {(string)=>CSN.Artifact} getArtifactRef
337
+ * Function used to get an artifact for a reference. Useful in case that the caller
338
+ * has a custom cache or a CSN that is in in-between state.
339
+ * TODO: Can we make getUtils() more modular, so that this argument is already
340
+ * passed to it?
336
341
  *
337
- * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
338
342
  * @returns {object|null}
339
343
  */
340
- function getFinalTypeInfo( type ) {
344
+ function getFinalTypeInfo( type, getArtifactRef = artifactRef ) {
341
345
  type = normalizeTypeRef(type);
342
346
  if (!type)
343
347
  return null;
@@ -362,7 +366,7 @@ function getUtils( model, universalReady ) {
362
366
  if (typeof type === 'string' && (isBuiltinType( type ) || type === special$self))
363
367
  return _cacheResolved({ type });
364
368
 
365
- const typeRef = artifactRef(type); // throws if not found
369
+ const typeRef = getArtifactRef(type); // default artifactRef() throws if not found
366
370
  const isNonScalar = _cacheNonScalar({ ...typeRef, type });
367
371
  if (isNonScalar)
368
372
  return finalBaseTypeCache[resolvedKey];
@@ -380,7 +384,7 @@ function getUtils( model, universalReady ) {
380
384
  finalBaseTypeCache[resolvedKey] = true;
381
385
 
382
386
  // Continue the search
383
- const finalBase = getFinalTypeInfo(type);
387
+ const finalBase = getFinalTypeInfo(type, getArtifactRef);
384
388
  if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
385
389
  return _cacheResolved(null);
386
390
 
@@ -515,8 +519,8 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
515
519
  */
516
520
  function forEachMember( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
517
521
  constructCallback = (_construct, _prop, _path) => {} ) {
518
- // Allow processing _ignored elements if requested
519
- if (ignoreIgnore && construct._ignore)
522
+ // Allow processing $ignored elements if requested
523
+ if (ignoreIgnore && construct.$ignore)
520
524
  return;
521
525
 
522
526
  // `items` itself is a structure that can contain "elements", and more.
@@ -775,7 +779,7 @@ function getArtifactDatabaseNameOf( artifactName, sqlMapping, csn, sqlDialect =
775
779
  * @returns {string} The resulting name
776
780
  */
777
781
  function getResultingName( csn, namingMode, artifactName ) {
778
- if (namingMode === 'plain' || artifactName.indexOf('.') === -1)
782
+ if (namingMode === 'plain' || !artifactName.includes('.'))
779
783
  return artifactName;
780
784
 
781
785
  const namespace = getNamespace(csn, artifactName);
@@ -964,7 +968,7 @@ function isPersistedOnDatabase( art ) {
964
968
  */
965
969
  function isPersistedAsView( artifact ) {
966
970
  return artifact && artifact.kind === 'entity' &&
967
- !artifact._ignore &&
971
+ !artifact.$ignore &&
968
972
  !artifact.abstract &&
969
973
  ((artifact.query || artifact.projection) && !hasAnnotationValue(artifact, '@cds.persistence.table')) &&
970
974
  !hasAnnotationValue(artifact, '@cds.persistence.skip') &&
@@ -978,7 +982,7 @@ function isPersistedAsView( artifact ) {
978
982
  */
979
983
  function isPersistedAsTable( artifact ) {
980
984
  return artifact.kind === 'entity' &&
981
- !artifact._ignore &&
985
+ !artifact.$ignore &&
982
986
  !artifact.abstract &&
983
987
  (!artifact.query && !artifact.projection || hasAnnotationValue(artifact, '@cds.persistence.table')) &&
984
988
  !hasAnnotationValue(artifact, '@cds.persistence.skip') &&
@@ -1077,6 +1081,9 @@ function getLastPartOfRef( ref ) {
1077
1081
  *
1078
1082
  * Overwrite existing ones only if 'overwrite' is true.
1079
1083
  *
1084
+ * IMPORTANT: Consider using copyAnnotationsAndDoc() instead!
1085
+ * Don't forget about doc comments!
1086
+ *
1080
1087
  * @param {object} fromNode
1081
1088
  * @param {object} toNode
1082
1089
  * @param {boolean} [overwrite]
@@ -1120,6 +1127,32 @@ function copyAnnotationsAndDoc( fromNode, toNode, overwrite = false ) {
1120
1127
  }
1121
1128
  }
1122
1129
 
1130
+ /**
1131
+ * Same as `copyAnnotationsAndDoc()` but deletes the annotations on source
1132
+ * side after copying them. Useful when applying annotations from `cds.extensions`.
1133
+ *
1134
+ * Overwrite existing ones only if 'overwrite' is true.
1135
+ *
1136
+ * @param {object} sourceNode
1137
+ * @param {object} targetNode
1138
+ * @param {boolean} [overwrite]
1139
+ */
1140
+ function moveAnnotationsAndDoc( sourceNode, targetNode, overwrite = false ) {
1141
+ // Ignore if no targetNode (in case of errors)
1142
+ if (!targetNode)
1143
+ return;
1144
+
1145
+ const annotations = Object.keys(sourceNode)
1146
+ .filter(key => key.startsWith('@') || key === 'doc');
1147
+
1148
+ for (const anno of annotations) {
1149
+ if (targetNode[anno] === undefined || overwrite) {
1150
+ targetNode[anno] = sourceNode[anno];
1151
+ delete sourceNode[anno];
1152
+ }
1153
+ }
1154
+ }
1155
+
1123
1156
  /**
1124
1157
  * Applies annotations from `csn.extensions` to definitions and their elements.
1125
1158
  *
@@ -1147,7 +1180,7 @@ function applyAnnotationsFromExtensions( csn, config ) {
1147
1180
  if (name && filter(name)) {
1148
1181
  const def = csn.definitions[name];
1149
1182
  if (def) {
1150
- copyAnnotationsAndDoc(ext, def, config.override);
1183
+ moveAnnotationsAndDoc(ext, def, config.override);
1151
1184
  applyAnnotationsToElements(ext, def);
1152
1185
  }
1153
1186
  else if (config.notFound) {
@@ -1169,7 +1202,7 @@ function applyAnnotationsFromExtensions( csn, config ) {
1169
1202
  forEach(ext.elements, (key, sourceElem) => {
1170
1203
  const targetElem = def.elements[key];
1171
1204
  if (targetElem) {
1172
- copyAnnotationsAndDoc(sourceElem, targetElem, config.override);
1205
+ moveAnnotationsAndDoc(sourceElem, targetElem, config.override);
1173
1206
  applyAnnotationsToElements(sourceElem, targetElem);
1174
1207
  }
1175
1208
  });
@@ -17,7 +17,7 @@ const { CompilerAssertion } = require('../base/error');
17
17
  const $inferred = Symbol.for('cds.$inferred');
18
18
  const $location = Symbol.for('cds.$location');
19
19
 
20
- class NOT_A_DICTIONARY {} // used for consol.log display
20
+ class NOT_A_DICTIONARY {} // used for console.log display
21
21
 
22
22
  function locationString( loc ) {
23
23
  if (Array.isArray(loc))
@@ -102,8 +102,10 @@ function revealInternalProperties( model, nameOrPath ) {
102
102
  _status: primOrString, // is a string anyway
103
103
  $annotations: as => as.map( $annotation ),
104
104
  $messageFunctions: () => '‹some functions›',
105
+ $functions: () => '‹some functions›',
106
+ $builtins: nameOrPath === '++' ? builtinsDictionary : () => '‹reveal with -R ++›',
105
107
  };
106
- uniqueId = 1;
108
+ uniqueId = -1;
107
109
  return revealXsnPath(nameOrPath, model);
108
110
 
109
111
  // Returns the desired artifact/dictionary in the XSN.
@@ -129,7 +131,7 @@ function revealInternalProperties( model, nameOrPath ) {
129
131
  // `name.space/S/E/elements/a/kind/`
130
132
  // `name.space/S/E/elements/a/type/scope/`
131
133
  function revealXsnPath( path, xsn ) {
132
- if (!path || path === '+')
134
+ if (!path || path === '+' || path === '++')
133
135
  return reveal( xsn );
134
136
 
135
137
  path = path.split('/');
@@ -173,7 +175,9 @@ function revealInternalProperties( model, nameOrPath ) {
173
175
  if (!Array.isArray(deps))
174
176
  return primOrString( deps );
175
177
  return deps
176
- .map( d => `${ d.location ? '' : '-' }${ artifactIdentifier( d.art ) }`);
178
+ .map( d => (d.location
179
+ ? `${ artifactIdentifier( d.art ) } @${ locationString( d.location ) }`
180
+ : artifactIdentifier( d.art )) );
177
181
  }
178
182
 
179
183
  function layerExtends( dict ) {
@@ -213,7 +217,13 @@ function revealInternalProperties( model, nameOrPath ) {
213
217
  }
214
218
 
215
219
  function artifactDictionary( node, parent ) {
216
- if (!node || typeof node !== 'object' || !model.definitions || parent === model )
220
+ if (parent === model )
221
+ return dictionary( node ); // no dictionary or no definitions section
222
+ return builtinsDictionary( node );
223
+ }
224
+
225
+ function builtinsDictionary( node, parent ) {
226
+ if (!node || typeof node !== 'object' || !model.definitions )
217
227
  return dictionary( node ); // no dictionary or no definitions section
218
228
  const dict = Object.create( Object.getPrototypeOf(node)
219
229
  ? NOT_A_DICTIONARY.prototype
@@ -237,7 +247,8 @@ function revealInternalProperties( model, nameOrPath ) {
237
247
  ? NOT_A_DICTIONARY.prototype
238
248
  : Object.prototype );
239
249
  for (const prop of Object.getOwnPropertyNames( node )) { // also non-enumerable
240
- r[prop] = reveal( node[prop], node, prop );
250
+ if (node !== model.definitions || nameOrPath === '++' || !node[prop].builtin)
251
+ r[prop] = reveal( node[prop], node, prop );
241
252
  }
242
253
  if (node[$inferred] && !node['[$inferred]'])
243
254
  r['[$inferred]'] = node[$inferred];
@@ -275,8 +286,8 @@ function revealInternalProperties( model, nameOrPath ) {
275
286
 
276
287
  const r = Object.create( Object.getPrototypeOf( node ) );
277
288
  // property to recognize === objects
278
- if (node.kind && node.__unique_id__ == null)
279
- Object.defineProperty( node, '__unique_id__', { value: ++uniqueId } );
289
+ if (node.kind && node.__unique_id__ == null && node.$effectiveSeqNo == null && !node.builtin)
290
+ Object.defineProperty( node, '__unique_id__', { value: uniqueId-- } );
280
291
 
281
292
  for (const prop of Object.getOwnPropertyNames( node )) { // also non-enumerable
282
293
  const func = transformers[prop] ||
@@ -287,8 +298,8 @@ function revealInternalProperties( model, nameOrPath ) {
287
298
  }
288
299
 
289
300
  function targetAspect( node, parent ) {
290
- if (node.elements && uniqueId && node.__unique_id__ == null)
291
- Object.defineProperty( node, '__unique_id__', { value: ++uniqueId } );
301
+ if (node.elements && node.__unique_id__ == null && node.$effectiveSeqNo == null)
302
+ Object.defineProperty( node, '__unique_id__', { value: uniqueId-- } );
292
303
  return reveal( node, parent );
293
304
  }
294
305
 
@@ -305,19 +316,16 @@ function array( node, fn ) {
305
316
  }
306
317
 
307
318
  function artifactIdentifier( node, parent ) {
319
+ if (!node)
320
+ return `${ node }`;
308
321
  if (Array.isArray(node))
309
322
  return node.map( a => artifactIdentifier( a, node ) );
310
- if (uniqueId && node.__unique_id__ == null)
311
- Object.defineProperty( node, '__unique_id__', { value: ++uniqueId } );
312
- let outer = uniqueId ? `##${ node.__unique_id__ }` : '';
313
- if (node._outer) {
314
- // eslint-disable-next-line no-nested-ternary
315
- outer = (node._outer.items === node) ? `/items${ outer }`
316
- // eslint-disable-next-line no-nested-ternary
317
- : (node._outer.returns === node) ? `/returns${ outer }` // TODO returns now normal
318
- // eslint-disable-next-line no-nested-ternary
319
- : (node._outer.targetAspect === node) ? `/target${ outer }`
320
- : `/returns/items${ outer }`;
323
+ if (uniqueId && node.__unique_id__ == null && node.$effectiveSeqNo == null && !node.builtin)
324
+ Object.defineProperty( node, '__unique_id__', { value: uniqueId-- } );
325
+ const outerNum = node.$effectiveSeqNo || node.__unique_id__;
326
+ let outer = outerNum != null ? `##${ outerNum }` : '';
327
+ if (node._outer) { // anon aspect in targetAspect | items
328
+ outer = (node._outer.targetAspect === node) ? `/target${ outer }` : `/items${ outer }`;
321
329
  node = node._outer;
322
330
  }
323
331
  if (node === parent)
@@ -328,7 +336,7 @@ function artifactIdentifier( node, parent ) {
328
336
  return '$magicVariables';
329
337
  if (!node.name) {
330
338
  try {
331
- return `${ locationString( node.location ) || '' }##${ node.__unique_id__ }`;
339
+ return `${ locationString( node.location ) || '' }##${ outerNum }`;
332
340
  // return JSON.stringify(node);
333
341
  }
334
342
  catch (e) {