@sap/cds-compiler 2.10.4 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +4 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +9 -23
  7. package/lib/api/options.js +12 -4
  8. package/lib/api/validate.js +23 -2
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/message-registry.js +10 -2
  12. package/lib/base/messages.js +23 -9
  13. package/lib/base/model.js +5 -4
  14. package/lib/base/optionProcessorHelper.js +56 -22
  15. package/lib/checks/selectItems.js +4 -0
  16. package/lib/checks/unknownMagic.js +6 -3
  17. package/lib/compiler/assert-consistency.js +7 -0
  18. package/lib/compiler/base.js +65 -0
  19. package/lib/compiler/builtins.js +28 -1
  20. package/lib/compiler/checks.js +2 -1
  21. package/lib/compiler/definer.js +58 -91
  22. package/lib/compiler/index.js +16 -4
  23. package/lib/compiler/propagator.js +5 -2
  24. package/lib/compiler/resolver.js +93 -34
  25. package/lib/compiler/shared.js +29 -202
  26. package/lib/compiler/utils.js +173 -0
  27. package/lib/edm/annotations/genericTranslation.js +1 -1
  28. package/lib/edm/csn2edm.js +3 -2
  29. package/lib/edm/edmPreprocessor.js +31 -36
  30. package/lib/edm/edmUtils.js +3 -3
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +17 -1
  33. package/lib/gen/language.tokens +79 -73
  34. package/lib/gen/languageLexer.interp +19 -1
  35. package/lib/gen/languageLexer.js +779 -731
  36. package/lib/gen/languageLexer.tokens +71 -65
  37. package/lib/gen/languageParser.js +4668 -4072
  38. package/lib/json/from-csn.js +10 -10
  39. package/lib/json/to-csn.js +169 -34
  40. package/lib/language/antlrParser.js +11 -0
  41. package/lib/language/genericAntlrParser.js +72 -14
  42. package/lib/language/language.g4 +73 -0
  43. package/lib/main.d.ts +136 -17
  44. package/lib/main.js +3 -1
  45. package/lib/model/api.js +2 -2
  46. package/lib/model/csnRefs.js +108 -31
  47. package/lib/model/csnUtils.js +63 -29
  48. package/lib/model/enrichCsn.js +36 -9
  49. package/lib/model/revealInternalProperties.js +20 -4
  50. package/lib/modelCompare/compare.js +2 -1
  51. package/lib/optionProcessor.js +29 -18
  52. package/lib/render/DuplicateChecker.js +1 -1
  53. package/lib/render/toCdl.js +9 -3
  54. package/lib/render/toHdbcds.js +16 -36
  55. package/lib/render/toSql.js +23 -5
  56. package/lib/transform/db/constraints.js +278 -119
  57. package/lib/transform/db/draft.js +3 -2
  58. package/lib/transform/db/expansion.js +6 -4
  59. package/lib/transform/db/flattening.js +17 -1
  60. package/lib/transform/db/transformExists.js +61 -2
  61. package/lib/transform/db/views.js +438 -0
  62. package/lib/transform/forHanaNew.js +56 -435
  63. package/lib/transform/forOdataNew.js +9 -2
  64. package/lib/transform/localized.js +2 -0
  65. package/lib/transform/transformUtilsNew.js +10 -0
  66. package/lib/transform/translateAssocsToJoins.js +5 -13
  67. package/lib/utils/file.js +5 -3
  68. package/lib/utils/term.js +65 -42
  69. package/lib/utils/timetrace.js +48 -26
  70. package/package.json +1 -1
package/lib/main.d.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  //
5
5
  // These types are improved step by step and use a lot any types at the moment.
6
6
 
7
+ // Author's note: All "options" interfaces should actually be types. However, due to
8
+ // https://github.com/TypeStrong/typedoc/issues/1519 we can't use
9
+ // intersection types at the moment.
10
+
7
11
  export = compiler;
8
12
 
9
13
  declare namespace compiler {
@@ -11,7 +15,7 @@ declare namespace compiler {
11
15
  /**
12
16
  * Options used by the core compiler and all backends.
13
17
  */
14
- export type Options = {
18
+ export interface Options {
15
19
  [option: string]: any,
16
20
 
17
21
  /**
@@ -57,6 +61,102 @@ declare namespace compiler {
57
61
  dictionaryPrototype?: any
58
62
  }
59
63
 
64
+ /**
65
+ * Options used by OData backends. Includes options for the OData
66
+ * transformer as well as for rendering EDM and EDMX.
67
+ */
68
+ export interface ODataOptions extends Options {
69
+ /**
70
+ * OData version for output files. Either 'v4' or 'v2'.
71
+ *
72
+ * @default 'v4'
73
+ */
74
+ odataVersion?: string | 'v4' | 'v2'
75
+ /**
76
+ * Whether to generate OData as flat or as structured.
77
+ * Structured is only supported for OData v4.
78
+ *
79
+ * @default 'flat'
80
+ */
81
+ odataFormat?: string | 'flat' | 'structured'
82
+ /**
83
+ * Naming mode used by the corresponding SQL.
84
+ *
85
+ * @default 'plain'
86
+ */
87
+ sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
88
+ /**
89
+ * If `true`, `cds.Compositions` are rendered as `edm:NavigationProperty` with the additional
90
+ * attribute `ContainsTarget="true"` and all contained entities (composition targets) have no
91
+ * `edm.EntitySet`.
92
+ *
93
+ * @note Only available for OData v4 EDM(X) rendering.
94
+ * @default false
95
+ */
96
+ odataContainment?: boolean
97
+ /**
98
+ * If `true`, render generated foreign keys for managed associations.
99
+ * By default foreign keys are never visible in structured OData APIs.
100
+ *
101
+ * @note Only available for structured OData v4 EDM(X) rendering.
102
+ * @default false
103
+ */
104
+ odataForeignKeys?: boolean
105
+ /**
106
+ * If `true`, association targets outside of the current service are added as
107
+ * `edm.EntityType` that only exposes their primary keys and have no `edm.EntitySet`.
108
+ * If the original association target is a service member, a corresponding `edm.Schema`
109
+ * representing the namespace of that service is added to `edm.Services`. All association
110
+ * targets that are no service members are collected in an `edm.Schema` with namespace `root`.
111
+ *
112
+ * @note Only valid for structured OData v4 EDM(X) rendering.
113
+ * @default false
114
+ * @since v2.1.0
115
+ */
116
+ odataProxies?: boolean
117
+ /**
118
+ * This option is an extension to `odataProxies`.
119
+ * If `true`, an `edm:Reference` instead of a proxy `edm.EntityType` is rendered for each
120
+ * association target that is a service member outside the current service instead of proxies.
121
+ *
122
+ * @note Only valid for structured OData v4 EDM(X) rendering.
123
+ * @default false
124
+ * @since v2.1.0
125
+ */
126
+ odataXServiceRefs?: boolean
127
+ /**
128
+ * The OData specification requires that all primary keys of the principal must be used as
129
+ * referential constraints. If an association is modelled with only a partial key, no
130
+ * referential constraints are added. If `true`, partial constraints are rendered for
131
+ * backwards compatibility and mocking scenarios. A spec violation warning is raised for
132
+ * each incomplete constraint.
133
+ *
134
+ * @note Only valid for OData v2 CSN transformation.
135
+ * @default false
136
+ * @since v2.2.6
137
+ */
138
+ odataV2PartialConstr?: boolean
139
+ /**
140
+ * Service name for which EDMX or EDM shall be rendered.
141
+ *
142
+ * @note Only available for `to.edmx()` and `to.edm()`. For `to.edmx.all()`
143
+ * and `to.edm.all()`, use `serviceNames` instead.
144
+ *
145
+ * @see serviceNames
146
+ */
147
+ service?: string
148
+ /**
149
+ * Array of service names for which EDMX or EDM shall be rendered.
150
+ * If unspecified, all services are rendered.
151
+ *
152
+ * @note Only available for `to.edmx.all()` and `to.edm.all()`. For `to.edmx()`
153
+ * and `to.edm()`, use `service` instead.
154
+ *
155
+ * @see service
156
+ */
157
+ serviceNames?: string[]
158
+ }
159
+
60
160
  /**
61
161
  * The compiler's package version.
62
162
  * For more details on versioning and SemVer, see `doc/Versioning.md`
@@ -83,6 +183,7 @@ declare namespace compiler {
83
183
  * {@link CompilationError} containing a vector of individual errors.
84
184
  *
85
185
  * @param filenames Array of files that should be compiled.
186
+ * @param dir Working directory. Relative paths in `filenames` will be resolved relatively to this directory.
86
187
  * @param options Compiler options. If you do not set `messages`, they will be printed to console.
87
188
  * @param fileCache A dictionary of absolute file names to the file content with values:
88
189
  * - false: the file does not exist
@@ -138,7 +239,7 @@ declare namespace compiler {
138
239
  * _Note_: Sorting is done in-place.
139
240
  *
140
241
  * Example of sorted messages:
141
- * ```txt
242
+ * ```
142
243
  * A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
143
244
  * A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
144
245
  * B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
@@ -188,10 +289,11 @@ declare namespace compiler {
188
289
  * form (i.e. one line)
189
290
  *
190
291
  * Example:
191
- * ```txt
292
+ * ```
192
293
  * <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
193
294
  * ```
194
295
  *
296
+ * @param msg Compiler message which shall be stringified.
195
297
  * @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
196
298
  * @param noMessageId If true, the message ID will _not_ be part of the string.
197
299
  * @param noHome If true, the semantic location will _not_ be part of the string.
@@ -201,26 +303,30 @@ declare namespace compiler {
201
303
  /**
202
304
  * Returns a message string with file- and semantic location if present
203
305
  * in multiline form.
204
- * The error (+ message id) will be colored according to their severity if
205
- * run on a TTY.
306
+ * The error (+ message id) can colored according to their severity.
206
307
  *
207
308
  * Example:
208
- * ```txt
209
- * Error[message-id]: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
309
+ * ```
310
+ * Error[message-id]: Can't find type `nu` in this scope
210
311
  * |
211
- * <source>.cds:3:11, at entity:“E”
312
+ * <source>.cds:3:11, at entity:“E”/element:“e
212
313
  * ```
213
314
  *
214
315
  * @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
215
316
  * @param config.noMessageId If true, no messages id (in brackets) will be shown.
216
317
  * @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
217
318
  * @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
319
+ * @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
320
+ * coloring will be used. If 'auto', we will decide based on certain factors such
321
+ * as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
322
+ * unset.
218
323
  */
219
324
  export function messageStringMultiline(msg: CompileMessage, config?: {
220
325
  normalizeFilename?: boolean
221
326
  noMessageId?: boolean
222
327
  hintExplanation?: boolean
223
328
  withLineSpacer?: boolean
329
+ color?: boolean | 'auto'
224
330
  }): string;
225
331
 
226
332
  /**
@@ -233,16 +339,24 @@ declare namespace compiler {
233
339
  * All lines are prepended by a pipe (`|`) and show the corresponding line number.
234
340
  *
235
341
  * Example Output:
236
- * ```txt
342
+ * ```
237
343
  * |
238
344
  * 13 | num * nu
239
345
  * | ^^
240
346
  * ```
241
347
  *
242
- * @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
243
- * @param msg Message whose location is used to print the message context.
348
+ * @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
349
+ * @param msg Message whose location is used to print the message context.
350
+ * @param config Configuration for the message context.
351
+ * @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
352
+ * coloring will be used. If 'auto', we will decide based on certain factors such
353
+ * as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
354
+ * unset.
355
+
244
356
  */
245
- export function messageContext(sourceLines: string[], msg: CompileMessage): string;
357
+ export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
358
+ color?: boolean | 'auto'
359
+ }): string;
246
360
 
247
361
  /**
248
362
  * Get an explanatory text for a complicated compiler message with ID
@@ -316,21 +430,26 @@ declare namespace compiler {
316
430
  * @alias for
317
431
  */
318
432
  export namespace For {
319
- function odata(): any;
433
+ /**
434
+ * Transform the given (generic) CSN into one that is used for OData.
435
+ * Changes include flattening, type resolution and more, according to
436
+ * the provided options.
437
+ */
438
+ function odata(csn: CSN, options: ODataOptions): any;
320
439
  }
321
440
 
322
441
  export namespace to {
323
442
  function cdl(csn: CSN, options: Options): object;
324
443
  function sql(csn: CSN, options: Options): any;
325
444
 
326
- function edm(csn: CSN, options: Options): any;
445
+ function edm(csn: CSN, options: ODataOptions): any;
327
446
  namespace edm {
328
- function all(csn: CSN, options: Options): any;
447
+ function all(csn: CSN, options: ODataOptions): any;
329
448
  }
330
449
 
331
- function edmx(csn: CSN, options: Options): any;
450
+ function edmx(csn: CSN, options: ODataOptions): any;
332
451
  namespace edmx {
333
- function all(csn: CSN, options: Options): any;
452
+ function all(csn: CSN, options: ODataOptions): any;
334
453
  }
335
454
 
336
455
  function hdbcds(csn: CSN, options: Options): any;
package/lib/main.js CHANGED
@@ -21,6 +21,7 @@ const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, dedupli
21
21
 
22
22
  const parseLanguage = require('./language/antlrParser');
23
23
  const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
24
+ const { fns } = require('./compiler/shared');
24
25
  const { define } = require('./compiler/definer');
25
26
 
26
27
  // The compiler version (taken from package.json)
@@ -43,13 +44,14 @@ const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
43
44
  function parseCdl( cdl, filename, options = {} ) {
44
45
  options = Object.assign( {}, options, { parseCdl: true } );
45
46
  const sources = Object.create(null);
46
- const model = { sources, options };
47
+ const model = { sources, options, $functions: {}, $volatileFunctions: {} };
47
48
  const messageFunctions = createMessageFunctions( options, 'parse', model );
48
49
  model.$messageFunctions = messageFunctions;
49
50
 
50
51
  const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
51
52
  messageFunctions );
52
53
  sources[filename] = xsn;
54
+ fns( model );
53
55
  define( model );
54
56
  messageFunctions.throwWithError();
55
57
  return compactModel( model );
package/lib/model/api.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * - the ‹property name› (might be useful if the same function is used for several props)
16
16
  */
17
17
  const defaultFunctions = {
18
- '@': () => {}, // do not traverse annotation assignments
18
+ '@': () => { /* do not traverse annotation assignments */ },
19
19
  args: dictionary,
20
20
  elements: dictionary,
21
21
  enum: dictionary,
@@ -23,7 +23,7 @@ const defaultFunctions = {
23
23
  actions: dictionary,
24
24
  mixin: dictionary,
25
25
  definitions: dictionary,
26
- '$': () => {}, // do not traverse properties starting with '$'
26
+ '$': () => { /* do not traverse properties starting with '$' */},
27
27
  }
28
28
 
29
29
  /**
@@ -225,17 +225,19 @@ function csnRefs( csn ) {
225
225
  const cachedType = getCache( art, '_effectiveType' );
226
226
  if (cachedType !== undefined)
227
227
  return cachedType;
228
- else if (!art.type && !art.$origin ||
229
- art.elements || art.target || art.targetAspect || art.enum)
230
- return setCache( art, '_effectiveType', art );
231
228
 
232
229
  const chain = [];
233
- while (getCache( art, '_effectiveType' ) === undefined && (art.type || art.$origin) &&
230
+ let origin;
231
+ while (getCache( art, '_effectiveType' ) === undefined &&
232
+ (origin = cached( art, '_origin', getOriginRaw )) &&
234
233
  !art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
235
234
  chain.push( art );
236
235
  setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
237
- art = (art.$origin) ? getOrigin( art ) : artifactRef( art.type, BUILTIN_TYPE );
236
+ art = origin;
238
237
  }
238
+ if (!chain.length)
239
+ return setCache( art, '_effectiveType', art );
240
+
239
241
  if (getCache( art, '_effectiveType' ) === 0)
240
242
  throw new Error( 'Circular type reference');
241
243
  const type = getCache( art, '_effectiveType' ) || art;
@@ -251,10 +253,16 @@ function csnRefs( csn ) {
251
253
  // here, we do not care whether it is semantically ok to navigate into sub
252
254
  // elements of array items (that is the task of the core compiler /
253
255
  // semantic check)
254
- while (type.items)
256
+ while (type.items) {
257
+ cached( type, '$origin', _a => setImplicitOrigin( type, origin ) );
255
258
  type = effectiveType( type.items );
259
+ }
256
260
  // cannot navigate along targetAspect!
257
- return (type.target) ? csn.definitions[type.target] : type;
261
+ const env = (type.target) ? csn.definitions[type.target] : type;
262
+ const origin = cached( env, '_origin', getOriginRaw );
263
+ if (origin && origin !== BUILTIN_TYPE)
264
+ cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
265
+ return env;
258
266
  }
259
267
 
260
268
  /**
@@ -279,38 +287,102 @@ function csnRefs( csn ) {
279
287
  function artifactPathRef( ref ) {
280
288
  const [ head, ...tail ] = ref.ref;
281
289
  let art = csn.definitions[pathId( head )];
282
- for (const elem of tail)
283
- art = navigationEnv( art ).elements[pathId( elem )];
290
+ for (const elem of tail) {
291
+ const env = navigationEnv( art );
292
+ art = env.elements[pathId( elem )];
293
+ }
284
294
  return art;
285
295
  }
286
296
 
287
- function getOrigin( def ) {
288
- const art = cached( def, '_origin', originPathRef );
289
- if (art)
290
- return art;
291
- throw new Error( 'Undefined origin reference' );
297
+ function getOrigin( art, alsoType ) {
298
+ const origin = cached( art, '_origin', getOriginRaw );
299
+ if (origin && origin !== BUILTIN_TYPE)
300
+ cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
301
+ return art.type && !alsoType ? undefined : origin;
292
302
  }
293
303
 
294
- function originPathRef( def ) {
295
- const [ head, ...tail ] = def.$origin;
296
- let art = csn.definitions[head];
297
- for (const elem of tail)
298
- art = originNavigation( art, elem );
299
- return art;
304
+ function getOriginRaw( art ) {
305
+ if (art.type) // TODO: make robust against "linked" = only direct
306
+ return artifactRef( art.type, BUILTIN_TYPE );
307
+ if (!art.$origin) // implicit $origin should have been set
308
+ return null;
309
+ // art.$origin must not be a string here - shortened refs should already
310
+ // have been used to set the _origin cache
311
+ if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
312
+ return cached( art.$origin, '_origin', getOriginRaw );
313
+ const [ head, ...tail ] = art.$origin;
314
+ let origin = csn.definitions[head];
315
+ // allow shorter $origin ref for actions/functions, just using a string:
316
+ let isAction = art.kind === 'action' || art.kind === 'function';
317
+ for (const elem of tail) {
318
+ origin = originNavigation( origin, elem, isAction );
319
+ isAction = false;
320
+ }
321
+ return origin;
300
322
  }
301
323
 
302
- function originNavigation( art, elem ) {
324
+ function originNavigation( art, elem, isAction ) {
303
325
  if (typeof elem !== 'string') {
304
326
  if (elem.action)
305
- return art.actions[elem.action]
327
+ return art.actions[elem.action];
306
328
  if (elem.param)
307
- return (elem.param ? art.params[elem.param] : art.returns);
329
+ return art.params[elem.param];
330
+ if (elem.returns)
331
+ return art.returns;
308
332
  }
309
- if (art.returns)
333
+ if (isAction)
334
+ return art.actions[elem];
335
+ // TODO: if we use effectiveType(), we might have more implicit prototypes
336
+ // we might then need a function like effectiveArtifact,
337
+ // which cares about actions/params
338
+ // let origin = cached( art, '_origin', getOriginRaw );
339
+ // while (art.items) {
340
+ // cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
341
+ // art = art.items;
342
+ // origin = cached( art, '_origin', getOriginRaw );
343
+ // }
344
+ // if (origin)
345
+ // cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
346
+ return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
347
+ }
348
+
349
+ // From the current CSN object, set implicit origin for the next navigation step
350
+ // Currently (TODO: ?) `elements` only, i.e. what is needed for name resolution.
351
+ function setImplicitOrigin( art, origin ) {
352
+ setMembersImplicit( art.actions, origin.actions );
353
+ setMembersImplicit( art.params, origin.params );
354
+ if (art.returns) {
310
355
  art = art.returns;
311
- while (art.items)
356
+ if (art.type || typeof art.$origin === 'object') // null, […], {…}
357
+ return true; // not implicit or shortened
358
+ origin = effectiveType( origin.returns );
359
+ setCache( art, '_origin', origin );
360
+ return true;
361
+ }
362
+ while (art.items) {
312
363
  art = art.items;
313
- return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
364
+ if (art.type || typeof art.$origin === 'object') // null, [], {…}
365
+ return true; // not implicit or shortened
366
+ origin = effectiveType( origin.items );
367
+ setCache( art, '_origin', origin );
368
+ }
369
+ setMembersImplicit( art.elements, origin.elements );
370
+ // The enum base type is _not_ where we find the origins of the enum symbols.
371
+ // A derived type of an enum type with individual annotations on a symbol
372
+ // has both 'type' and '$origin'.
373
+ if (!art.type || art.$origin)
374
+ setMembersImplicit( art.enum, origin.enum );
375
+ return true;
376
+ }
377
+
378
+ function setMembersImplicit( members, originMembers ) {
379
+ if (!members)
380
+ return;
381
+ for (const name in members) {
382
+ const elem = members[name];
383
+ if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
384
+ setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
385
+ }
314
386
  }
315
387
 
316
388
  /**
@@ -371,6 +443,9 @@ function csnRefs( csn ) {
371
443
  if (semantics.dynamic === 'global' || ref.global)
372
444
  return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
373
445
 
446
+ const origin = cached( main, '_origin', getOriginRaw )
447
+ if (origin)
448
+ cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
374
449
  cached( main, '$queries', allQueries );
375
450
  let qcache = query && cache.get( query.projection || query );
376
451
  // BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
@@ -387,7 +462,7 @@ function csnRefs( csn ) {
387
462
  while (cache) {
388
463
  const alias = tryAlias && cache.$aliases[head];
389
464
  if (alias)
390
- return resolvePath( path, alias._select || alias, 'alias', cache.$queryNumber );
465
+ return resolvePath( path, alias._select || alias._ref, 'alias', cache.$queryNumber );
391
466
  const mixin = cache._select.mixin && cache._select.mixin[head];
392
467
  if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
393
468
  return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
@@ -481,7 +556,8 @@ function csnRefs( csn ) {
481
556
  if (query.ref) { // ref in from
482
557
  // console.log('SQ:',query,cache.get(query))
483
558
  const as = query.as || implicitAs( query.ref );
484
- getCache( fromSelect, '$aliases' )[as] = fromRef( query );
559
+ const _ref = fromRef( query );
560
+ getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
485
561
  }
486
562
  else {
487
563
  const qcache = getQueryCache( parentQuery );
@@ -607,7 +683,7 @@ function queryOrMain( query, main ) {
607
683
  *
608
684
  * @param {CSN.Query} query
609
685
  * @param {CSN.QuerySelect} fromSelect
610
- * @param {CSN.Query} parentQuery
686
+ * @param {CSN.Query} parentQuery
611
687
  * @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
612
688
  */
613
689
  function traverseQuery( query, fromSelect, parentQuery, callback ) {
@@ -632,8 +708,9 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
632
708
 
633
709
  /**
634
710
  * @param {CSN.QueryFrom} from
635
- * @param {CSN.QuerySelect} select
636
- * @param {(from: CSN.QueryFrom, select: CSN.QuerySelect) => void} callback
711
+ * @param {CSN.QuerySelect} fromSelect
712
+ * @param {CSN.Query} parentQuery
713
+ * @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
637
714
  */
638
715
  function traverseFrom( from, fromSelect, parentQuery, callback ) {
639
716
  if (from.ref) {