Socket
Socket
Sign inDemoInstall

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
3
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/cds-compiler - npm Package Compare versions

Comparing version 1.24.4 to 1.26.2

lib/base/location.js

16

bin/cdsc.js

@@ -87,2 +87,18 @@ #!/usr/bin/env node

// Internally, parseCdl is an option so we map the command to it.
if (cmdLine.command === 'parseCdl') {
cmdLine.command = 'toCsn';
cmdLine.options.parseCdl = true;
if (cmdLine.args.files.length > 1) {
const err = `'parseCdl' expects exactly one file! ${cmdLine.args.files.length} provided.`;
displayUsage(err, optionProcessor.commands['parseCdl'].helpText, 2);
}
}
if (cmdLine.options.beta) {
const features = cmdLine.options.beta.split(',');
cmdLine.options.beta = {};
features.forEach((val) => cmdLine.options.beta[val] = true);
}
// Do the work for the selected command (default 'toCsn')

@@ -89,0 +105,0 @@ executeCommandLine(cmdLine.command || 'toCsn', cmdLine.options, cmdLine.args);

@@ -9,2 +9,90 @@ # ChangeLog for cdx compiler and backends

## Version 1.26.2 - 2020-04-24
### Added
- The client tool `cdsc` has got a new option `--beta <list>` which may be used to
specify a comma separated list of experimental features to be enabled.
- CSN in parse-cdl mode now has a `requires` property that represents `using`s from CDL.
### Fixed
- OData:
+ Change foreign key creation order for associations to respect their dependencies.
+ Use correct path during on-condition flattening.
+ Report error when using elements without types for **array of type of (element)** and similar definitions.
- HANA/SQL:
+ Fix references to `null` enum values in default clauses.
- Type arguments are now properly set in CSN when using parse-cdl mode.
- Avoid unjust warning if the `extensions` property of an input CSN contain `extend` statements.
## Version 1.26.0 - 2020-04-17
### Added
- The client tool `cdsc` has got a new command `parseCdl` which returns a CSN
that is close to the original CDL file. It does not resolve imports and does
not apply extensions.
- Unmanaged associations as primary keys are now warned about.
- `localized` in combination with `key` is now warned about.
- Enum values are now checked to only be either numbers or a strings - a warning is raised.
- Elements in mixin clauses that are _not_ unmanaged associations now produce an error.
### Changed
- HANA/SQL:
+ Raise warnings `rewrite-not-supported` and `rewrite-undefined-key` to errors.
- Compiler: Empty elements are now kept along for the propagation.
- OData: Annotate all elements of `DraftAdministrativeData` with `@Common.Label: '{i18n>"Draft_<elementName>"}'`
and elements 'DraftUUID', 'DraftIsCreatedByMe' and 'DraftIsProcessedByMe' with `@UI.Hidden`.
### Fixed
- Compiler: `type of <unmanaged assocation>` is now handled correctly by raising an error.
## Version 1.25.0 - 2020-04-09
### Changed
- Downgrade `chained array of`-error to a warning
- SQLite: Don't render implicit casts
## Version 1.24.6 - 2020-04-08
### Changed
- OData:
+ Improve messages for misaligned forward/backlink associations in EDM generator
+ For V2 add annotations `@sap.creatable: false`, `@sap.updatable: false`, `@sap.deletable: false`,
`@sap.pageable: false` to the Parameter EntityType and `@sap.creatable: false`, `@sap.updatable: false`,
`@sap.deletable: false`, `@sap.addressable: false` to the Result EntityType.
+ Update vocabularies 'Common' and 'Graph' and 'ODM'.
### Fixed
- Various messages mention more appropriate source locations.
- Improve messages for `array of`
- OData:
+ Render 'array of' for ReturnType correctly
+ Report error for view fields with no type information early
+ Handle associations in structures with an association as explicit key
### Removed
- The client tool `cdsc` does not offer the option `--std-json-parser` anymore,
as it had no effect.
## Version 1.24.4 - 2020-03-25

@@ -11,0 +99,0 @@

12

lib/api/main.js

@@ -9,2 +9,3 @@ /** @module API */

const alerts = require('../base/alerts');
const { emptyLocation } = require('../base/location');
const {

@@ -398,2 +399,5 @@ sortMessages, messageString, CompilationError, getMessageFunction, handleMessages,

function recompile(csn, options) {
// Explicitly set parseCdl to false because backends cannot handle
// the option and is only intended for CDL sources.
options.parseCdl = false;
/* eslint-disable global-require */

@@ -435,7 +439,9 @@ const { augment } = require('../json/from-csn');

throw err;
const message = getMessageFunction( csn, options );
message( 'api-recompiled-csn', emptyLocation('csn.json'),
null, {}, 'Info', 'CSN input had to be recompiled' );
// next line to be replaced by CSN parser call which reads the CSN object
const xsn = getXsn(csn, options);
const message = getMessageFunction( xsn, options );
message( 'api-recompiled-csn', { filename: 'csn.json', start: { offset: 0, line: 1, column: 1 } },
null, {}, 'Info', 'CSN input had to be recompiled' );
return processor( compactModel(xsn), options, ...args );

@@ -442,0 +448,0 @@ }

@@ -33,2 +33,3 @@ 'use strict';

'testMode',
'compatibility',
];

@@ -35,0 +36,0 @@

@@ -41,3 +41,3 @@ // Implementation of alerts

signal.warning = warning;
signal.ino = info;
signal.info = info;

@@ -44,0 +44,0 @@ let config = options.severities || {};

@@ -85,34 +85,2 @@ // Functions for dictionaries (Objects without prototype)

// Return the source location of the complete dictionary `dict`. If
// `extraLocation` is truthy, also consider this location.
// ASSUMPTION: all entries in the dictionary have a property `location` and
// `location.filename` has always the same value.
function dictLocation( dict, extraLocation ) {
if (!dict)
return extraLocation;
if (!(dict instanceof Array))
dict = Object.getOwnPropertyNames( dict ).map( name => dict[name] );
let locations = [].concat( ...dict.map( objLocations ) );
if (extraLocation)
locations.push( extraLocation );
let min = locations.reduce( (a,b) => a.start.offset < b.start.offset ? a : b );
let max = locations.reduce( (a,b) => (a.end||a.start).offset > (b.end||b.start).offset ? a : b );
return { filename: min.filename, start: min.start, end: max.end };
}
function objLocations( obj ) {
return (obj instanceof Array) ? obj.map( o => o.location ) : [ obj.location ];
}
// Create a location with location properties `filename` and `start` from
// argument `start`, and location property `end` from argument `end`.
function combinedLocation( start, end ) {
return {
filename: start.location.filename,
start: start.location.start,
end: end.location.end
};
}
// Push `entry` to the array value with key `name` in the dictionary `dict`.

@@ -137,4 +105,2 @@ function pushToDict( dict, name, entry ) {

clearDict,
dictLocation,
combinedLocation,
pushToDict,

@@ -141,0 +107,0 @@ forEachInDict,

@@ -6,3 +6,3 @@ const { CompileMessage } = require('../base/messages');

* @param {any} msg
* @param {XSN.Location|CSN.Location|any[]} location
* @param {XSN.Location|CSN.Location|CSN.Path} location
* @param {string} severity

@@ -18,6 +18,6 @@ * @param {string} message_id

location = searchForLocation(semanticLocation);
}
return new CompileMessage(location, msg, severity ? severity : msg._severity, message_id, beautifySemanticLocation(semanticLocation), context);
/** @param {CSN.Path} semanticLocation */
function searchForLocation(semanticLocation){

@@ -41,2 +41,3 @@ let last_location = null; // Don't display a location if we cannot find one!

/** @param {CSN.Path} semanticLocation */
function validateSemanticLocation(semanticLocation){

@@ -59,2 +60,3 @@

/** @param {CSN.Path} semanticLocation */
function beautifySemanticLocation(semanticLocation){

@@ -61,0 +63,0 @@ if(!semanticLocation){

@@ -6,2 +6,3 @@ // Functions and classes for syntax messages

const term = require('../utils/term');
const { normalizeLocation } = require('./location');

@@ -65,2 +66,6 @@ // For messageIds, where no severity has been provided via code (central def)

/**
* Returns true if at least one of the given messages is of severity "Error"
* @param {CSN.Message[]} messages
*/
function hasErrors( messages ) {

@@ -70,11 +75,16 @@ return messages && messages.some( m => m.severity === 'Error' );

// Return gnu-style error string for location `loc`:
// - 'File:Line:Col' without `loc.end`
// - 'File:Line:StartCol-EndCol' if Line = start.line = end.line
// - 'File:StartLine.StartCol-EndLine.EndCol' otherwise
function locationString( loc, normalizeFilename ) {
if (!loc)
/**
* Return gnu-style error string for location `loc`:
* - 'File:Line:Col' without `loc.end`
* - 'File:Line:StartCol-EndCol' if Line = start.line = end.line
* - 'File:StartLine.StartCol-EndLine.EndCol' otherwise
*
* @param {CSN.Location|XSN.Location} location
* @param {boolean} [normalizeFilename]
*/
function locationString( location, normalizeFilename ) {
if (!location)
return '<???>';
loc = normalizeLocation( loc );
let filename = (loc.filename && (normalizeFilename || normalizeFilename === 0))
const loc = normalizeLocation( location );
let filename = (loc.filename && normalizeFilename)
? loc.filename.replace( /\\/g, '/' )

@@ -145,3 +155,3 @@ : loc.filename;

* @param {any} location Location of the message
* @param {any} msg The message text
* @param {string} msg The message text
* @param {string} [severity='Error'] Severity: Debug, Info, Warning, Error

@@ -171,16 +181,2 @@ * @param {string} [id] The ID of the message - visible as property messageId

// Normalize location: to old-style at the moment, TODO: should switch to new-style
function normalizeLocation( loc ) {
if (!loc || !loc.file )
return loc;
let location = {
filename: loc.file,
start: { line: loc.line, column: loc.col || 0 },
end: { line: loc.endLine || loc.line, column: loc.endCol || loc.col || 0 },
};
if (!loc.endLine)
location.$weak = true;
return location;
}
/**

@@ -349,2 +345,3 @@ * Handle compiler messages, i.e. throw a compiler exception if there are

/** @param {XSN.Location} loc */
function weakLocation( loc ) {

@@ -361,3 +358,3 @@ // use return { ...location, $weak: true } if that is JS standard

*
* @param {object} err
* @param {CSN.Message} err
* @param {boolean} [normalizeFilename]

@@ -374,3 +371,2 @@ * @param {boolean} [noMessageId]

(!err.home || noHome && err.location && !err.location.$weak ? '' : ' (in ' + err.home + ')');
}

@@ -385,3 +381,3 @@

*
* @param {object} err
* @param {CSN.Message} err
* @param {boolean} [normalizeFilename]

@@ -410,3 +406,3 @@ * @param {boolean} [noMessageId]

* @param {string[]} sourceLines The source code split up into lines, e.g. by `src.split('\n')`
* @param {object} err Error object containing all details like line, message, etc.
* @param {CSN.Message} err Error object containing all details like line, message, etc.
*/

@@ -462,6 +458,10 @@ function messageContext(sourceLines, err) {

}
// Compare two messages `a` and `b`. Return 0 if they are equal, 1 if `a` is
// larger than `b`, and -1 if `a` is smaller than `b`. Messages without a location
// are considered larger than messages with a location.
/**
* Compare two messages `a` and `b`. Return 0 if they are equal, 1 if `a` is
* larger than `b`, and -1 if `a` is smaller than `b`. Messages without a location
* are considered larger than messages with a location.
*
* @param {CSN.Message} a
* @param {CSN.Message} b
*/
function compareMessage( a, b ) {

@@ -569,5 +569,4 @@ if (a.location && b.location) {

CompilationError,
normalizeLocation,
translatePathLocations
}

@@ -43,4 +43,4 @@ //

// Apply function `callback(member, memberName)` to each member in `construct`,
// recursively (i.e. also for sub-elements of elements).
// Apply function `callback(member, memberName, prop)` to each member in
// `construct`, recursively (i.e. also for sub-elements of elements).
function forEachMemberRecursively( construct, callback ) {

@@ -58,2 +58,45 @@ forEachMember( construct, ( member, memberName, prop ) => {

/**
* Apply function `callback` to all members of object `obj` (main artifact or
* parent member). Members are considered those in dictionaries `elements`,
* `enum`, `actions` and `params` of `obj`, `elements` and `enums` are also
* searched inside property `items` (array of). `$queries`, `mixin` and
* `columns` are also visited in contrast to `forEachMember()`.
* See function `forEachGeneric()` for details.
*
* @param {XSN.Artifact} construct
* @param {(member: object, memberName: string, prop: string) => any} callback
* @param {object} [target]
*/
function forEachMemberWithQuery( construct, callback, target ) {
let obj = construct.returns || construct; // why the extra `returns` for actions?
obj = obj.items || obj;
forEachGeneric( target || obj, 'elements', callback );
forEachGeneric( obj, 'enum', callback );
forEachGeneric( obj, 'foreignKeys', callback );
forEachGeneric( construct, 'actions', callback );
forEachGeneric( construct, 'params', callback );
// For Queries
forEachGeneric( construct, '$queries', callback );
forEachGeneric( construct, 'mixin', callback );
forEachGeneric( construct, 'columns', callback );
}
/**
* Apply function `callback(member, memberName, prop)` to each member in
* `construct`, recursively (i.e. also for sub-elements of elements).
* In contrast to `forEachMemberRecursively()` this function also traverses
* queries and mixins.
*
* @param {XSN.Artifact} construct
* @param {(member: object, memberName: string, prop: string) => any} callback
*/
function forEachMemberRecursivelyWithQuery( construct, callback ) {
forEachMemberWithQuery( construct, ( member, memberName, prop ) => {
callback( member, memberName, prop );
// Descend into nested members, too
forEachMemberRecursivelyWithQuery( member, callback );
});
}
// Apply function `callback` to all objects in dictionary `dict`, including all

@@ -167,2 +210,4 @@ // duplicates (found under the same name). Function `callback` is called with

forEachMemberRecursively,
forEachMemberWithQuery,
forEachMemberRecursivelyWithQuery,
forEachGeneric,

@@ -169,0 +214,0 @@ forEachInOrder,

'use strict';
const alerts = require('../base/alerts');
const { getMessageFunction } = require('../base/messages');
const { hasArtifactTypeInformation } = require('../model/modelUtils')

@@ -12,3 +14,7 @@ // Semantic checks that are performed on artifacts

if (!art.abstract && emptyOrOnlyVirtualElements(art)) {
signal(info`Dubious entity or type without non-virtual elements`, art.location, undefined, 'empty-entity-or-type');
const location = art.name && art.name.location || art.location;
if (art.kind === 'entity')
signal(info`Dubious entity without non-virtual elements`, location, undefined, 'empty-entity');
else if (art.kind === 'type')
signal(info`Dubious type without non-virtual elements`, location, undefined, 'empty-type');
}

@@ -50,5 +56,90 @@

/**
* If the given artifact is a type definition then check whether it is
* properly defined and has valid type information, e.g. information about
* its elements or references another valid type.
*
* @param {XSN.Definition} artifact
* @param {XSN.Model} model
*/
function checkTypeDefinitionHasType(artifact, model) {
if (artifact.kind !== 'type')
return;
checkArtifactHasProperType(artifact, model);
}
/**
* Check that the given artifact has proper type information.
* Either the artifact itself is a proper type or its `type` property
* references a proper type (including `many type of`).
*
* @param {XSN.Definition} artifact
* @param {XSN.Model} model
*/
function checkArtifactHasProperType(artifact, model) {
function warnAboutMissingType(art) {
// Can happen in CSN, e.g. `{ a: { kind: "type" } }` but should
// not happen in CDL.
const message = getMessageFunction(model);
message('check-proper-type', art.location, art, { art: artifact },
['Error'], {
std: 'Dubious type $(ART) without type information',
element: 'Dubious element $(MEMBER) of $(ART) without type information',
});
}
if (!hasArtifactTypeInformation(artifact)) {
warnAboutMissingType(artifact);
return;
}
// Check for `type of`
if (artifact.type) {
checkTypeOfHasProperType(artifact, model)
return;
}
const items = artifact.items;
if (!items)
return;
// `array of` without nested `type of`
if (!hasArtifactTypeInformation(items))
warnAboutMissingType(items);
else if (items && items.type)
// `array of type of`
checkTypeOfHasProperType(items, model)
}
/**
* Check that the `type of` information in the given artifact (i.e. `type` property)
* has proper type information or warn otherwise. The artifact's final type is checked.
*
* @param {XSN.Artifact} artifact
* @param {XSN.Model} model
*/
function checkTypeOfHasProperType(artifact, model) {
if (!artifact.type)
return;
const finalType = artifact.type._artifact && artifact.type._artifact._finalType;
if (finalType && !hasArtifactTypeInformation(finalType)) {
const message = getMessageFunction(model);
message('check-proper-type-of', artifact.type.location, artifact, { art: artifact.type },
'Info', {
std: 'Referred type of $(ART) does not contain proper type information',
element: 'Referred element $(MEMBER) of $(ART) does not contain proper type information',
});
return;
}
}
module.exports = {
checkNotEmptyOrOnlyVirtualElems,
checkNoUnmanagedAssocsInGroupByOrderBy,
checkTypeDefinitionHasType,
checkTypeOfHasProperType,
checkArtifactHasProperType,
};

@@ -5,7 +5,10 @@ 'use strict';

const alerts = require('../base/alerts');
const { checkArtifactHasProperType } = require('./checkArtifacts');
const { isComposition } = require('../model/modelUtils.js')
function checkPrimaryKeyTypeCompatibility(elem, model) {
const { error, signal } = alerts(model);
/**
* Run primary key checks on the given element, for example type checks.
*/
function checkPrimaryKey(elem, model) {
const { error, warning, signal } = alerts(model);
let type = '';

@@ -17,4 +20,23 @@ // apparently this is the resolved type (over an derived type chain)

/**
* Check that a primary key element is not an unmanaged association or contains unmanaged associations
*
* @param {any} element Element to check recursively
*/
function checkForUnmanagedAssociations(element){
if(element.on){
signal(warning`Unmanaged associations cannot be used as primary key`, elem.key.location);
}
// Recursively check sub-elements for structured types
if(element.elements){
for(let elemName of Object.keys(element.elements)){
const subElement = element.elements[elemName];
checkForUnmanagedAssociations(subElement);
}
}
}
/**
* Check that a primary key element is not array-like or contains array-like elements
*
*
* @param {any} element Element to check recursively

@@ -35,3 +57,12 @@ */

}
function checkLocalizedSubElement(element) {
if (!element.localized || element.localized.val !== true)
return;
if (element._parent && element._parent.kind === 'element') {
signal('Keyword "localized" is ignored for sub elements', element.localized.location, 'Warning', 'localized-sub-element');
}
}
if(elem.key && elem.key.val === true){

@@ -42,4 +73,6 @@ if(['cds.hana.ST_POINT', 'cds.hana.ST_GEOMETRY'].includes(type)){

checkForUnmanagedAssociations(elem);
checkKeyArrayLike(elem);
}
checkLocalizedSubElement(elem);
}

@@ -382,4 +415,42 @@

/**
* If the given element is not computed, check whether its final type has
* proper type information. Useful for checking that `type of` does not refer
* to a computed element.
*
* @param {XSN.Artifact} element
* @param {XSN.Model} model
*/
function checkElementHasValidTypeOf(element, model) {
// Computed elements, e.g. "1+1 as foo" in a view don't have a valid type and
// are skipped here. Their usage will still be warned about, though.
// Elements in projections are not tested as well as they don't have the
// origin's type information copied but reference them in _finalType.
if (element['@Core.Computed'] || element.origin)
return;
checkArtifactHasProperType(element, model);
}
/**
* Check that there are no .items containing .items.
*
* Type definitions and elements are checked - recursion is handled by the semanticChecks
*
* @param {object} obj element or type definition
* @param {object} model the whole model
*/
function checkForItemsChain(obj, model){
if(obj.items) {
const itemsType = obj.items.type ? obj.items.type._artifact : obj.items;
if(itemsType.items){
const { signal, warning } = alerts(model);
signal(warning`"Array of"/"many" must not be chained - ${obj.name.id}, ${itemsType.name.id}.`, obj.location, 'Warning', 'chained-array-of');
}
}
}
module.exports = {
checkPrimaryKeyTypeCompatibility,
checkPrimaryKey,
checkManagedAssoc,

@@ -390,3 +461,5 @@ checkVirtualElement,

checkLocalizedElement,
checkStructureCasting
checkStructureCasting,
checkElementHasValidTypeOf,
checkForItemsChain
};

@@ -7,9 +7,9 @@ 'use strict';

* Associations inside of an array-like must have all their foreign keys inside of the items.
*
* This effectively restricts it to
* - managed associations or
*
* This effectively restricts it to
* - managed associations or
* - unmanaged associations where the on-condition only references elements inside
of the items, $self usage must be forbidden.
*
* @param {object} member Member
*
* @param {CSN.Artifact} member Member
*/

@@ -16,0 +16,0 @@ function validateAssociationsInItems(member){

@@ -10,5 +10,6 @@ 'use strict';

const keywords = require('../base/keywords');
const { checkNotEmptyOrOnlyVirtualElems, checkNoUnmanagedAssocsInGroupByOrderBy } = require('./checkArtifacts');
const { checkPrimaryKeyTypeCompatibility, checkVirtualElement, checkManagedAssoc, checkCardinality,
checkLocalizedElement, checkStructureCasting } = require('./checkElements');
const { checkNotEmptyOrOnlyVirtualElems, checkNoUnmanagedAssocsInGroupByOrderBy,
checkTypeDefinitionHasType } = require('./checkArtifacts');
const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc, checkCardinality,
checkLocalizedElement, checkStructureCasting, checkForItemsChain, checkElementHasValidTypeOf } = require('./checkElements');
const { checkExpression } = require('./checkExpressions');

@@ -79,3 +80,3 @@ const { foreachPath } = require('../model/modelUtils');

entity: checkEntity,
enum: nothingToCheckYet,
enum: checkEnum,
event: nothingToCheckYet,

@@ -117,3 +118,3 @@ function: checkActionOrFunction,

* Called for each main artifact (no need to iterate its members).
* @param {object} artifact A "model.definitions" artifact
* @param {CSN.Artifact} artifact
*/

@@ -211,2 +212,29 @@ function checkGenericArtifact(artifact) {

/**
* Enum specific checks. For example this function checks that the value type
* of enum values are allowed.
*
* @param {XSN.Artifact} enumNode
*/
function checkEnum(enumNode) {
if (!enumNode.value)
return;
const type = enumNode.value.literal;
const loc = enumNode.value.location;
// Special handling to print a more detailed error message
if (type === 'enum') {
message('enum-value-ref', loc, enumNode, { }, 'Warning',
'References to other values are not allowed as enum values');
return;
}
const allowedValueTypes = ['number', 'string'];
if (!allowedValueTypes.includes(type)) {
message('enum-value-type', loc, enumNode, { }, 'Warning',
'Only strings or numbers are allowed as enum values');
}
}
// Traverses 'node' recursively and applies 'checkExpression' to all expressions

@@ -292,9 +320,13 @@ // found within paths (e.g. filters, parameters, ...)

checkManagedAssoc(type, model);
checkTypeDefinitionHasType(type, model);
checkForItemsChain(type, model);
}
function checkElement(elem) {
checkPrimaryKeyTypeCompatibility(elem, model);
checkElementHasValidTypeOf(elem, model);
checkPrimaryKey(elem, model);
checkVirtualElement(elem, model);
checkManagedAssoc(elem, model);
checkCardinality(elem, model);
checkForItemsChain(elem, model);
if (elem.onCond && !elem.onCond.$inferred) {

@@ -301,0 +333,0 @@ checkExpression(elem.onCond, model);

@@ -151,9 +151,36 @@ // Consistency checker on model (XSN = augmented CSN)

_assocSources: { kind: true, test: TODO }, // just null: isArray( inDefinitions ) during resolve
$magicVariables: { test: TODO },
$magicVariables: {
// $magicVariables contains "builtin" artifacts that differ from
// "normal artifacts" and therefore have a custom schema
requires: [ 'kind', 'artifacts' ],
schema: {
kind: { test: isString, enum: [ '$magicVariables' ] },
artifacts: {
// Do not use "normal" definitions spec because of these artifacts
// are missing the location property
test: isDictionary( definition ),
requires: [ 'kind', 'name' ],
optional: [ 'elements', '$autoElement', '_finalType', '_deps' ],
schema: {
kind: { test: isString, enum: [ 'builtin' ] },
name: { test: isObject, requires: [ 'id', 'element' ] },
$autoElement: { test: isString },
// missing location for normal "elements"
elements: { test: TODO },
},
},
},
},
$builtins: { test: TODO },
builtin: { kind: true, test: builtin },
$internal: { test: TODO },
$internal: {
test: standard,
requires: [ '$frontend' ],
schema: {
$frontend: { test: isString, enum: [ '$internal' ] },
},
},
version: { test: TODO }, // TODO: describe - better: 'header'
$version: { test: isString },
meta: { test: TODO },
meta: { test: TODO }, // never tested due to --test-mode
namespace: {

@@ -438,3 +465,23 @@ test: (model.$frontend !== 'json') ? standard : TODO,

queries: { kind: true, test: TODO }, // TODO: $queries with other structure
$queries: { kind: [ 'entity', 'view' ], test: TODO },
$queries: {
kind: [ 'entity', 'view' ],
test: isArray(),
requires: [
'kind', 'location', 'name',
'_parent', '_main', '_$next', '_block',
// query specific
'op', 'from', 'elements',
'$combined',
'$tableAliases', '_firstAliasInFrom',
'queries', // TODO: deprecated?
],
optional: [
'_finalType',
'_deps',
// query specific
'where', 'columns', 'mixin', 'quantifier', 'offset',
'orderBy', '$orderBy', 'groupBy', 'exclude', 'having',
'limit', '_tableAlias', '_elementsIndexNo',
],
},
_leadingQuery: { kind: true, test: TODO },

@@ -459,3 +506,3 @@ $navigation: { kind: true, test: TODO },

_entities: { test: TODO },
$compositionTargets: { test: TODO },
$compositionTargets: { test: isDictionary( isBoolean ) },
$lateExtensions: { test: TODO },

@@ -462,0 +509,0 @@ _ancestors: { kind: [ 'type', 'entity', 'view' ], test: isArray( TODO ) },

@@ -6,2 +6,3 @@ // The builtin artifacts of CDS

const { forEachInDict } = require('../base/dictionaries');
const { builtinLocation } = require('../base/location');

@@ -105,16 +106,9 @@ const core = {

kind: 'namespace',
name: { absolute: name, location: createDummyLocation() },
// buitlin namespaces don't have a cds file, so no location available
name: { absolute: name, location: builtinLocation() },
blocks: [],
artifacts: Object.create(null),
builtin,
location: createDummyLocation(),
location: builtinLocation(),
};
// buitlin namespaces don't have a cds file, so no location available
function createDummyLocation() {
return {
filename: '<built-in>',
start: { offset: 0, line: 1, column: 1 },
end: { offset: 0, line: 1, column: 1 },
};
}
}

@@ -121,0 +115,0 @@

@@ -60,3 +60,3 @@ //

}
// console.log('RUN:',refString(art), art.elements ? Object.keys(art.elements) : 0)
// console.log('RUN:', art.name, art.elements ? Object.keys(art.elements) : 0)
const chain = [];

@@ -95,3 +95,3 @@ let target = art;

runMembers( art );
// console.log('DONE:',refString(art), art.elements ? Object.keys(art.elements) : 0)
// console.log('DONE:', art.name, art.elements ? Object.keys(art.elements) : 0)
}

@@ -160,3 +160,3 @@

function expensive( prop, target, source ) {
// console.log(prop,refString(source),'->',target.kind,refString(target));
// console.log(prop,source.name,'->',target.kind,target.name);
if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))

@@ -172,2 +172,3 @@ // foreignKeys must always be copied with target to avoid any confusion

const dict = source[prop];
target[prop] = Object.create(null); // also propagate empty elements
for (const name in dict) {

@@ -174,0 +175,0 @@ const member = linkToOrigin( dict[name], name, target, prop, location );

@@ -46,3 +46,8 @@ // Compiler functions and utilities shared across all phases

using: {},
extend: { isExtension: true, noDep: 'special' },
extend: {
isExtension: true,
noDep: 'special',
elements: true, /* only for parse-cdl */
actions: true, /* only for parse-cdl */
},
annotate: {

@@ -127,2 +132,3 @@ isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,

resolvePath,
resolveTypeArguments,
defineAnnotations,

@@ -347,2 +353,38 @@ };

// Resolve the type arguments provided with a type referenced for artifact or
// element `artifact`. This function does nothing if the referred type
// `typeArtifact` does not have a `parameters` property (currently, only
// builtin-types have it, see ./builtins.js).
//
// For each property name `<prop>` in `typeArtifact.parameters`, we move a number
// in art.typeArguments (a vector of numbers with locations) to `artifact.<prop>`.
// TODO: error if no parameters applicable
// TODO: also check for number
function resolveTypeArguments(artifact, typeArtifact, user) {
const args = artifact.typeArguments || [];
const parameters = typeArtifact.parameters || [];
const parLength = parameters.length;
for (let i = 0; i < parLength; ++i) {
let par = parameters[i];
if (!(par instanceof Object))
par = { name: par };
if (!artifact[par.name] && (i < args.length || par.literal)) {
const { location } = artifact.type;
artifact[par.name] = args[i] || {
literal: par.literal, val: par.val, location, $inferred: 'type-param',
};
}
}
if (args.length > parLength) {
artifact.typeArguments = artifact.typeArguments.slice(parLength);
message( 'unexpected-type-arg', artifact.typeArguments[0].location,
user, { art: typeArtifact },
'Warning', 'Too many arguments for type $(ART)' );
}
else if (artifact.typeArguments) {
delete artifact.typeArguments;
}
}
// Set a cross-reference from the 'user' in artifact 'art'

@@ -380,2 +422,4 @@ // 'user' is a navigatable node, while 'where' gives a closer hint (e.g. an item in a path)

if (!spec.next && !extDict) {
// CSN artifact paths are always fully qualified so we use
// model.definitions for the JSON frontend.
extDict = (spec.useDefinitions || env.$frontend && env.$frontend !== 'cdl')

@@ -689,12 +733,2 @@ ? model.definitions

// Create a location with location properties `filename` and `start` from
// argument `start`, and location property `end` from argument `end`.
function combinedLocation( start, end ) {
return {
filename: start.location.filename,
start: start.location.start,
end: end.location.end,
};
}
// Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with

@@ -751,2 +785,4 @@ // locations):

elem.name.absolute = elem._main.name.absolute;
if (name == null)
return;
const normalized = kindProperties[elem.kind].normalized || elem.kind;

@@ -802,3 +838,2 @@ [ 'element', 'alias', 'query', 'param', 'action' ].forEach( ( kind ) => {

withAssociation,
combinedLocation,
};

@@ -187,3 +187,3 @@ 'use strict';

const { error, warning, info, signal } = alerts(csn);
const { signal } = alerts(csn);

@@ -257,3 +257,3 @@ // global variable where we store all the generated annotations

}
signal(warning`${fullMessage}`);
signal(signal.warning`${fullMessage}`);
}

@@ -273,3 +273,3 @@

}
signal(info`${fullMessage}`);
signal(signal.info`${fullMessage}`);
}

@@ -410,3 +410,3 @@

let mapType = (p) => (p.type.startsWith('cds.') && !p.type.startsWith('cds.foundation.')) ?
edmUtils.mapCdsToEdmType(p, signal, error, false /*is only called for v4*/) : p.type;
edmUtils.mapCdsToEdmType(p, signal, false /*is only called for v4*/) : p.type;
for (let n in action.params) {

@@ -413,0 +413,0 @@ let p = action.params[n];

'use strict';
const { emptyLocation } = require('../../base/location');
// https://www.npmjs.com/package/sax

@@ -7,7 +8,2 @@ const sax = require('sax');

const parser = sax.parser(true);
let locationTemplate = {
filename: null,
start: { offset: 0, line: 0, column: 0 },
end: { offset: 0, line: 0, column: 0 }
};

@@ -103,3 +99,2 @@ /* ======================================================

function parseXmlWithLocation(xml, filename) {
locationTemplate.filename = filename || '<input>';
let result = Object.create(null);

@@ -138,3 +133,3 @@ let currentNode = result;

// setup location of the current node
let loc = JSON.parse(JSON.stringify(locationTemplate));
let loc = emptyLocation(filename || '<input>');
setStartPosition(loc.start, parser);

@@ -141,0 +136,0 @@ Object.defineProperty(currentNode, '_location', { value: loc, enumerable: false });

@@ -34,3 +34,3 @@ 'use strict';

const { error, warning, signal } = alerts(csn);
const { signal } = alerts(csn);
checkCSNVersion(csn, _options);

@@ -47,3 +47,3 @@

if(services.length == 0)
signal(error`No Services found in model`);
signal(signal.error`No Services found in model`);

@@ -53,3 +53,3 @@ if(serviceName) {

if(serviceCsn == undefined) {
signal(warning`No service definition with name "${serviceName}" found in the model`);
signal(signal.warning`No service definition with name "${serviceName}" found in the model`);
}

@@ -143,3 +143,3 @@ else {

let artifactName = `${Schema.Namespace}.${name}`;
signal(error`Duplicate name "${name}" in Namespace "${Schema.Namespace}"`, ['definitions',artifactName]);
signal(signal.error`Duplicate name "${name}" in Namespace "${Schema.Namespace}"`, ['definitions',artifactName]);
}

@@ -153,3 +153,3 @@ }

if(Schema._children.length == 0) {
signal(warning`Schema is empty`, ['definitions',Schema.Namespace]);
signal(signal.warning`Schema is empty`, ['definitions',Schema.Namespace]);
}

@@ -170,6 +170,6 @@

if(properties.length === 0) {
signal(error`EntityType "${serviceName}.${EntityTypeName}" has no properties`, ['definitions',entityCsn.name]);
signal(signal.error`EntityType "${serviceName}.${EntityTypeName}" has no properties`, ['definitions',entityCsn.name]);
}
if(entityCsn.$edmKeyPaths.length === 0) {
signal(error`EntityType "${serviceName}.${EntityTypeName}" has no primary key`, ['definitions',entityCsn.name]);
signal(signal.error`EntityType "${serviceName}.${EntityTypeName}" has no primary key`, ['definitions',entityCsn.name]);
}

@@ -279,3 +279,3 @@

if(actionCsn.returns) {
actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns, fullQualified);
actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns);
// if binding type matches return type add attribute EntitySetPath

@@ -364,3 +364,3 @@ if(entityCsn != undefined && fullQualified(entityCsn.name) === actionNode._returnType._type) {

if(type && isBuiltinType(type))
type = edmUtils.mapCdsToEdmType(returns, signal, error, options.isV2());
type = edmUtils.mapCdsToEdmType(returns, signal, options.isV2());

@@ -541,3 +541,3 @@ if(type && action.returns.items)

reuseAssoc = !!forwardAssocCsn._NavigationProperty;
constraints = edmUtils.getReferentialConstraints(forwardAssocCsn, signal, warning, options.isFlatFormat);
constraints = edmUtils.getReferentialConstraints(forwardAssocCsn, signal, options.isFlatFormat);
constraints._multiplicity = edmUtils.determineMultiplicity(forwardAssocCsn);

@@ -544,0 +544,0 @@ }

@@ -10,3 +10,3 @@ // @ts-nocheck

const definitions = csn.definitions;
const { error, info, signal } = alerts(csn, options);
const { signal } = alerts(csn, options);

@@ -568,41 +568,2 @@ class Node

/* ReturnType is only used in v4, mapCdsToEdmType can be safely
called with V2=false */
class ReturnType extends Node
{
constructor(v, csn, fullQualified)
{
let typecsn = csn.items || csn;
let type = typecsn.type;
if(type && type.startsWith('cds.') && !type.startsWith('cds.foundation.')) {
type = edmUtils.mapCdsToEdmType(typecsn, signal, error, false, false);
}
else
type = fullQualified(type);
super(v, { Type: type });
edmUtils.addTypeFacets(this, csn);
this.set( { _type: type, _isCollection: csn.items != undefined, _nullable: true });
if(this._isCollection)
this.Type = `Collection(${this.Type})`
}
// we need Name but NO $kind, can't use standard to JSON()
toJSON()
{
let rt = Object.create(null);
// use the original type, not the decorated one
if(this._type !== 'Edm.String')
rt['$Type'] = this._type;
if(this._isCollection)
rt['$Collection'] = this._isCollection;
// !this._nullable if Nullable=true become default
if(this._nullable)
rt['$Nullable'] = this._nullable;
return rt;
}
}
class TypeBase extends Node

@@ -637,3 +598,3 @@ {

if(scalarType) {
this[typeName] = edmUtils.mapCdsToEdmType(scalarType, signal, error, this.v2, csn['@Core.MediaType']);
this[typeName] = edmUtils.mapCdsToEdmType(scalarType, signal, this.v2, csn['@Core.MediaType']);
// CDXCORE-CDXCORE-173 ignore type facets for Edm.Stream

@@ -660,3 +621,3 @@ if(this[typeName] !== 'Edm.Stream')

} else if(odataType) {
signal(info`@odata.Type: '${odataType}' is ignored, only 'Edm.String' and 'Edm.Int[16,32,64]' are allowed`, csn.$location);
signal(signal.info`@odata.Type: '${odataType}' is ignored, only 'Edm.String' and 'Edm.Int[16,32,64]' are allowed`, csn.$location);
}

@@ -702,2 +663,28 @@ }

/* ReturnType is only used in v4, mapCdsToEdmType can be safely
called with V2=false */
class ReturnType extends TypeBase
{
constructor(v, csn)
{
super(v, {}, csn);
/* all return types are nullable by default,
CDS does not allow to specify not null
*/
this.set( { _nullable: true });
}
// we need Name but NO $kind, can't use standard to JSON()
toJSON()
{
let json = Object.create(null);
this.toJSONattributes(json);
// !this._nullable if Nullable=true become default
if(this._nullable)
json['$Nullable'] = this._nullable;
return json;
}
}
class ComplexType extends TypeBase { }

@@ -704,0 +691,0 @@ class EntityType extends ComplexType

@@ -37,3 +37,3 @@ 'use strict';

const { error, warning, info, signal } = alerts(csn);
const { signal } = alerts(csn);
const {

@@ -52,3 +52,3 @@ getCsnDef,

forAll(csn.definitions, (a, n) => {
setProp (a, 'name', n);
assignProp (a, 'name', n);
});

@@ -122,3 +122,3 @@

else {
signal(error`Target ${element.target} cannot be found in the model`, [ 'definitions', struct.name, 'elements', element.name ]);
signal(signal.error`Target ${element.target} cannot be found in the model`, [ 'definitions', struct.name, 'elements', element.name ]);
}

@@ -214,5 +214,14 @@ }

kind: 'entity',
elements: Object.create(null)
elements: Object.create(null),
'@sap.semantics': 'parameters',
};
setProp(parameterCsn, '$keys', Object.create(null));
/*
<EntitySet Name="ZRHA_TEST_CDS" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSParameters" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
*/
assignProp(parameterCsn, '_EntitySetAttributes',
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.pageable': false });
assignProp(parameterCsn, '$keys', Object.create(null));
setProp(parameterCsn, '$isParamEntity', true);

@@ -265,2 +274,11 @@

}
/*
<EntitySet Name="ZRHA_TEST_CDSSet" EntityType="ZRHA_TEST_CDS_CDS.ZRHA_TEST_CDSType" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:addressable="false" sap:content-version="1"/>
*/
assignProp(entityCsn, '_EntitySetAttributes',
{'@sap.creatable': false, '@sap.updatable': false, '@sap.deletable': false, '@sap.addressable': false });
// redirect inbound associations/compositions to the parameter entity

@@ -394,5 +412,6 @@ Object.keys(entityCsn.$sources || {}).forEach(n => {

}
setProp(struct, '_EntitySetAttributes', Object.create(null));
setProp(struct, '$keys', keys);
assignProp(struct, '_EntitySetAttributes', Object.create(null));
assignProp(struct, '$keys', keys);
applyAppSpecificLateCsnTransformationOnStructure(options, struct);

@@ -439,3 +458,3 @@

if (element._ignore) return;
setProp(element, '_constraints', getReferentialConstraints(element, signal, warning, options.isFlatFormat));
setProp(element, '_constraints', getReferentialConstraints(element, signal, options.isFlatFormat));

@@ -450,3 +469,3 @@ // only in V2 we must set the target cardinality of the backlink to the forward:

// Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
signal(warning`Source cardinality "${element._constraints._originAssocCsn.cardinality.src}" of "${element._constraints._originAssocCsn._parent.name}/${element._constraints._originAssocCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
signal(signal.warning`Source cardinality "${element._constraints._originAssocCsn.cardinality.src}" of "${element._constraints._originAssocCsn._parent.name}/${element._constraints._originAssocCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
}

@@ -490,3 +509,3 @@ }

element._ignore = true;
signal(info`${element.type.replace('cds.', '')} "${element.name}" excluded, target "${element._target.name}" is annotated '@cds.autoexpose: ${element._target['@cds.autoexpose']}'`,
signal(signal.info`${element.type.replace('cds.', '')} "${element.name}" excluded, target "${element._target.name}" is annotated '@cds.autoexpose: ${element._target['@cds.autoexpose']}'`,
['definitions', struct.name, 'elements', element.name]);

@@ -537,3 +556,3 @@ return;

else {
signal(info`Unmanaged associations not supported as primary keys of proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
signal(signal.info`Unmanaged associations not supported as primary keys of proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
['definitions', struct.name, 'elements', element.name]);

@@ -556,3 +575,3 @@ }

element._ignore = true;
signal(info`Could not create proxy entity type "${name}" for unexposed association target "${element._target.name}", because target has no primary keys`,
signal(signal.info`Could not create proxy entity type "${name}" for unexposed association target "${element._target.name}", because target has no primary keys`,
['definitions', struct.name, 'elements', element.name]);

@@ -563,6 +582,6 @@ return;

if(csn.definitions[name] !== undefined)
signal(error`Duplicate proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
signal(signal.error`Duplicate proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
['definitions', struct.name, 'elements', element.name]);
else {
signal(info`Created proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
signal(signal.info`Created proxy entity type "${name}" for unexposed association target "${element._target.name}"`,
['definitions', struct.name, 'elements', element.name]);

@@ -579,3 +598,3 @@ csn.definitions[name] = proxy;

element._ignore = true;
signal(warning`No OData navigation property generated for association "${element.name}", as target "${element._target.name}" is outside any service`,
signal(signal.warning`No OData navigation property generated for association "${element.name}", as target "${element._target.name}" is outside any service`,
['definitions', struct.name, 'elements', element.name]);

@@ -632,3 +651,3 @@ return;

if (type) {
signal(error`Cannot create type "${typeName}" for "${parentName}" because the name is already used`);
signal(signal.error`Cannot create type "${typeName}" for "${parentName}" because the name is already used`);
return undefined;

@@ -698,3 +717,3 @@ }

let annos = Object.keys(eltCsn).filter(a=>a[0]==='@').join(', ');
signal(warning`${struct.name}: OData V4 primary key path: "${prefix}" is unexposed by one of these annotations "${annos}"`, ['definitions', struct.name, 'elements', eltCsn.name ]);
signal(signal.warning`${struct.name}: OData V4 primary key path: "${prefix}" is unexposed by one of these annotations "${annos}"`, ['definitions', struct.name, 'elements', eltCsn.name ]);
return keyPaths;

@@ -1024,4 +1043,11 @@ }

// Set non enumerable property if it doesn't exist yet
function assignProp(obj, prop, value) {
if(obj[prop] === undefined) {
setProp(obj, prop, value);
}
}
module.exports = {
initializeModel,
}

@@ -152,3 +152,3 @@ 'use strict';

function getReferentialConstraints(assocCsn, signal, warning, isFlatFormat)
function getReferentialConstraints(assocCsn, signal, isFlatFormat)
{

@@ -185,3 +185,3 @@ let result = { constraints: Object.create(null), selfs: [], termCount: 0 };

isBacklink = false;
signal(warning`"${assocCsn._parent.name}/${assocCsn.name}" references "${originAssocCsn._parent.name}/${partner}" in $self ON condition with target "${originAssocCsn._target.name}"`, ['definitions', parentArtifactName]);
signal(signal.info`"${assocCsn._parent.name}/${assocCsn.name}" references "${originAssocCsn._parent.name}/${partner}" with target "${originAssocCsn._target.name}" in ON condition with $self`, ['definitions', parentArtifactName]);
}

@@ -225,7 +225,7 @@ if(isAssociationOrComposition(originAssocCsn)) {

{
signal(warning`Cannot resolve backlink to ${assocCsn._target.name}/${partner}" from "${parentArtifactName}/${assocCsn.name}"`, ['definitions', parentArtifactName]);
signal(signal.warning`Cannot resolve backlink to ${assocCsn._target.name}/${partner}" from "${parentArtifactName}/${assocCsn.name}"`, ['definitions', parentArtifactName]);
}
}
else {
signal(warning`Could not resolve partner association from "$self" expression`, ['definitions', assocCsn._parent.name, 'elements', assocCsn.name]);
signal(signal.warning`Could not resolve partner association from "$self" expression`, ['definitions', assocCsn._parent.name, 'elements', assocCsn.name]);

@@ -422,7 +422,7 @@ }

function mapCdsToEdmType(csn, signal, error, isV2=false, isMediaType=false)
function mapCdsToEdmType(csn, signal, isV2=false, isMediaType=false)
{
let cdsType = csn.type;
if(cdsType === undefined) {
signal(error`no type found`, csn.$location);
signal(signal.error`no type found`, csn.$location);
return '<NOTYPE>';

@@ -479,3 +479,3 @@ }

if (edmType == undefined) {
signal(error`No Edm type available for "${cdsType}"`, csn.$location);
signal(signal.error`No Edm type available for "${cdsType}"`, csn.$location);
}

@@ -489,3 +489,3 @@ if(isV2)

if(['cds.hana.ST_POINT', 'cds.hana.ST_GEOMETRY'].includes(cdsType)) {
signal(error`"OData V2 does not support Geometry data types, ${cdsType}" cannot be mapped`, csn.$location);
signal(signal.error`"OData V2 does not support Geometry data types, ${cdsType}" cannot be mapped`, csn.$location);
}

@@ -492,0 +492,0 @@ if(cdsType === 'cds.DecimalFloat' || cdsType === 'cds.hana.SMALLDECIMAL')

@@ -84,3 +84,4 @@ // CSN frontend - transform CSN into XSN

const { normalizeLocation, getMessageFunction } = require('../base/messages');
const { normalizeLocation } = require('../base/location');
const { getMessageFunction } = require('../base/messages');
const { addToDict } = require('../base/dictionaries');

@@ -139,2 +140,5 @@

const schema = compileSchema( {
requires: {
type: renameTo( 'dependencies', arrayOf( stringVal, val => (val.literal === 'string') ) ),
},
// definitions: ------------------------------------------------------------

@@ -178,3 +182,3 @@ definitions: {

validKinds: [ 'action', 'function' ],
inKind: [ 'entity', 'annotate' ],
inKind: [ 'entity', 'annotate', 'extend' ],
},

@@ -196,2 +200,3 @@ params: {

requires: [ 'ref', 'xpr', 'val', '#', 'func', 'SELECT', 'SET' ], // requires one of...
inKind: [ 'extend' ], // only valid in extend and SELECT
},

@@ -517,3 +522,3 @@ keys: {

arrayOf: stringRef,
inKind: [ 'entity', 'type' ],
inKind: [ 'entity', 'type', 'extend' ],
},

@@ -578,3 +583,3 @@ returns: {

type: object,
optional: [ 'definitions', 'extensions', 'namespace', 'version', 'messages', 'meta', 'options', '@' ],
optional: [ 'requires', 'definitions', 'extensions', 'namespace', 'version', 'messages', 'meta', 'options', '@' ],
requires: false, // empty object OK

@@ -601,2 +606,3 @@ schema,

let virtualLine = 1;
/** @type {XSN.Location[]} */
let dollarLocations = [];

@@ -659,3 +665,3 @@

function arrayOf( fn ) {
function arrayOf( fn, filter = undefined ) {
return function arrayMap( val, spec, xsn, csn ) {

@@ -679,2 +685,4 @@ if (!isArray( val, spec ))

++virtualLine; // [] in one JSON line
if (filter)
return r.filter(filter);
return r;

@@ -774,2 +782,3 @@ };

const csnProps = Object.keys( def );
if (csnProps.length) {

@@ -951,2 +960,3 @@ const valueName = (prop === 'keys' || prop === 'foreignKeys' ? 'targetElement' : 'value');

}
function stringVal( val, spec ) {

@@ -1478,2 +1488,3 @@ if (typeof val === 'string' && val)

}
/** @type {XSN.Location} */
const loc = {

@@ -1480,0 +1491,0 @@ filename, start: { line, column }, end: { line, column }, $weak: true,

@@ -116,2 +116,3 @@ // Transform augmented CSN into compact "official" CSN

_typeIsExplicit: ignore,
expectedKind: ignore, // TODO: may be set in extensions but is unused
};

@@ -220,6 +221,18 @@

/**
* Compact the given XSN model and transform it into CSN.
*
* @param {XSN.Model} model
* @param {CSN.Options} options
* @returns {CSN.Model}
*/
function compactModel( model, options = model.options || {} ) {
gensrcFlavor = options.toCsn && options.toCsn.flavor === 'gensrc';
gensrcFlavor = options.parseCdl || options.toCsn && options.toCsn.flavor === 'gensrc';
strictMode = options.testMode;
const csn = {};
if (options.parseCdl) {
const using = usings( model.sources || {} );
if (using.length)
csn.requires = using;
}
set( 'definitions', csn, model );

@@ -254,2 +267,27 @@ const exts = extensions( model.extensions || [], csn, model );

/**
* Create a CSN `requires` array of dependencies.
*
* @param {object} sources Dictionary of source files to their AST/XSN.
*/
function usings( sources ) {
const sourceNames = Object.keys(sources);
if (sourceNames.length === 0)
return [];
// Take the first file as parseCdl should only receive one file.
const source = sources[sourceNames[0]];
const requires = [];
if (source && source.dependencies)
source.dependencies.map(dep => dep && requires.push(dep.val));
// Make unique and sort
return Array.from(new Set(requires)).sort();
}
/**
* @param {XSN.Extension[]} node
* @param {object} csn
* @param {object} model
*/
function extensions( node, csn, model ) {

@@ -487,12 +525,12 @@ const exts = node.map( standard ).sort(

if (node.scope === 'typeOf') { // TYPE OF without ':' in path
// Can only be rendered with root _artifact which is an element
// In CDL->CSN transformation, _artifact for first item in ref for TYPE OF
// <elem> should be set to { _parent: <current user of TYPE OF> }
// Root _artifact which is either element or main artifact for paths starting with $self.
// To make the CDL->CSN transformation simpler, the _artifact for first item could be
// a fake element with just a correct absolute name and _parent/_main links.
if (!root._main || root.kind === 'query') { // $self/$projection
// in query, only correct for leading query ->
// TODO: forbid TYPE OF elem / TYPE OF $self.elem in queries
return renderArtifactPath( [ { id: absolute }, ...path.slice(1) ], terse );
}
const parent = root._parent;
if (!parent) {
if (strictMode)
throw new Error( `Illegal artifact link in ${ locationString(node.location) }`);
return renderArtifactPath( path, terse ); // is wrong anyway
}
const structs = parent._main ? parent.name.element.split('.') : [];
const structs = parent.name.element ? parent.name.element.split('.') : [];
return { ref: [ absolute, ...structs, ...path.map( pathItem ) ] };

@@ -574,3 +612,5 @@ }

function enumValue( v, csn, node ) {
if (node.kind === 'enum')
// Enums can have values but if enums are extended, their kind is 'element',
// so we check whether the node is inside an extension.
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
Object.assign( csn, expression(v) );

@@ -880,3 +920,4 @@ }

function addExplicitAs( node, name, implicit ) {
if (name && (!name.calculated && !name.$inferred || !node.ref || implicit && implicit(name.id) ))
if (name && name.id &&
(!name.calculated && !name.$inferred || !node.ref || implicit && implicit(name.id) ))
node.as = name.id;

@@ -883,0 +924,0 @@ return node;

@@ -10,6 +10,8 @@ 'use strict';

* Must be a valid doc comment.
* @param {boolean} [normalizeLineBreak=false] Whether to normalize line breaks, i.e. replace
* `\r\n` with `\n`. Useful for test mode.
* @returns {string|null} Parsed contents or if the comment has an invalid format or
* does not have any content, null is returned.
*/
function parseDocComment(comment) {
function parseDocComment(comment, normalizeLineBreak = false) {
// Also return "null" for empty doc comments so that doc comment propagation

@@ -20,2 +22,5 @@ // can be stopped.

if (normalizeLineBreak)
comment = comment.replace(/\r\n/g, '\n');
let lines = comment.split('\n');

@@ -22,0 +27,0 @@

@@ -10,3 +10,3 @@ // Generic ANTLR parser class with AST-building functions

var { addToDictWithIndexNo } = require('../base/dictionaries');
const locUtils = require('../base/location');
const { parseDocComment } = require('./docCommentParser');

@@ -209,4 +209,8 @@

// Return start location of `token`, or the first token matched by the current
// rule if `token` is undefined
/**
* Return start location of `token`, or the first token matched by the current
* rule if `token` is undefined
*
* @returns {XSN.Location}
*/
function startLocation( token = this._ctx.start ) {

@@ -243,7 +247,3 @@ return {

start = { location: this.startLocation() };
return {
filename: start.location.filename,
start: start.location.start,
end: end && end.location && end.location.end
};
return locUtils.combinedLocation( start, end );
}

@@ -264,3 +264,4 @@

}
node.doc = this.tokenLocation( token, token, parseDocComment( token.text ) );
const normalizeLineBreak = !!this.options.testMode;
node.doc = this.tokenLocation( token, token, parseDocComment( token.text, normalizeLineBreak ) );
}

@@ -267,0 +268,0 @@

@@ -18,4 +18,4 @@ // Main entry point for the Research Vanilla CDS Compiler

const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
const { emptyWeakLocation } = require('./base/location');
// The compiler version (taken from package.json)

@@ -74,3 +74,3 @@ function version() {

message( 'file-unknown-ext',
{ filename, start: { offset: 0, line: 1, column: 1 } }, null,
emptyWeakLocation(filename), null,
{ file: ext && ext.slice(1), '#': !ext && 'none' },

@@ -145,3 +145,3 @@ 'Error', {

});
if (!options.parseOnly)
if (!options.parseOnly && !options.parseCdl)
all = all.then( readDependencies );

@@ -502,2 +502,7 @@ return all.then( function() {

define( model );
// do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
if (options.parseCdl)
return handleMessages( model );
resolve( model );

@@ -504,0 +509,0 @@ assertConsistency( model );

@@ -11,3 +11,3 @@ 'use strict'

* Get utility functions for a given CSN.
* @param model - (compact) CSN model
* @param {CSN.Model} model (Compact) CSN model
*/

@@ -34,9 +34,9 @@ function getUtils(model) {

getFinalBaseType,
inspectRef
inspectRef,
artifactRef
};
/**
* Create an object to track visited objects identified
* by a unique sring.
* @param id - initial entry (optional)
* Create an object to track visited objects identified by a unique string.
* @param {string} [id] Initial entry (optional)
*/

@@ -51,3 +51,3 @@ function createVisited(id) {

* add it to the list of visited identifiers.
* @param id - unique identifier
* @param {string} id unique identifier
*/

@@ -65,3 +65,3 @@ function check(id) {

* Get the CSN definition for an artifact name.
* @param defName - absolute name of the artifact
* @param {string} defName Absolute name of the artifact
*/

@@ -76,5 +76,6 @@ function getCsnDef(defName) {

/**
* Returns if an artifact is a structured type
* Returns true if an artifact is a structured type
* or a typedef of a structured type.
* @param obj - artifact
*
* @param {CSN.Artifact} obj
*/

@@ -89,3 +90,5 @@ function isStructured(obj) {

* If the artifact for typename isn't a typedef, the name itself is returned.
* @param typeName - absolute name
*
* @param {string} typeName Absolute type name
* @returns {object}
*/

@@ -108,3 +111,4 @@ function getFinalTypeDef(typeName) {

* Resolves typedefs to its final type (name) which is returned.
* @param typeName - absolute name
* @param {string} typeName Absolute type name
* @returns {string}
*/

@@ -134,3 +138,3 @@ function getFinalType(typeName) {

function isManagedAssociationElement(node) {
return node.target != undefined && node.on == undefined;
return node.target !== undefined && node.on === undefined;
}

@@ -141,3 +145,3 @@

* to any of them.
* @param typeName - absolute type name
* @param {string} typeName Absolute type name
*/

@@ -160,3 +164,3 @@ function isAssocOrComposition(typeName) {

* Returns if a type is an association or a typedef to it.
* @param typeName - absolute type name
* @param {string} typeName Absolute type name
*/

@@ -179,3 +183,3 @@ function isAssociation(typeName) {

* Returns if a type is an composition or a typedef to it.
* @param typeName - absolute type name
* @param {string} typeName Absolute type name
*/

@@ -223,3 +227,3 @@ function isComposition(typeName) {

* Return the namespace part of the artifact name.
* @param name - absolute name of artifact
* @param {string} name Absolute name of artifact
*/

@@ -240,3 +244,3 @@ function getNamespaceOfArtifact(name) {

*
* @param {any} absoluteName Name of the annotation, including the at-sign
* @param {string} absoluteName Name of the annotation, including the at-sign
* @param {any} theValue string value of the annotation

@@ -260,3 +264,5 @@ * @param {any} node Node to add the annotation to

* Returns null if the artifact doesn't live in a service.
* @param artifactName - absolute name of artifact
*
* @param {string} artifactName Absolute name of artifact
* @returns {string|null}
*/

@@ -413,3 +419,6 @@ function getServiceName(artifactName) {

/**
* Deeply clone the given CSN model and return it.
* @param {CSN.Model} csn
*/
function cloneCsn(csn){

@@ -425,5 +434,11 @@ // cannot require this earlier because to-csn has a dependency on this file

// Apply function `callback` to all artifacts in dictionary
// `model.definitions`. See function `forEachGeneric` for details.
// callback will be called with artifact, artifact name, csn-path to artifact
/**
* Apply function `callback` to all artifacts in dictionary
* `model.definitions`. See function `forEachGeneric` for details.
* Callback will be called with artifact, artifact name, property
* name ('definitions') and csn-path to artifact.
*
* @param {CSN.Model} csn
* @param {(art: CSN.Artifact, name: string, prop: string, path: string[]) => any} callback
*/
function forEachDefinition( csn, callback ) {

@@ -433,7 +448,14 @@ forEachGeneric( csn, 'definitions', callback );

// Apply function `callback` to all members of object `obj` (main artifact or
// parent member). Members are considered those in dictionaries `elements`,
// `enum`, `actions` and `params` of `obj`, `elements` and `enums` are also
// searched inside property `items` (array of). See function `forEachGeneric`
// for details.
/**
* Apply function `callback` to all members of object `obj` (main artifact or
* parent member). Members are considered those in dictionaries `elements`,
* `enum`, `actions` and `params` of `obj`, `elements` and `enums` are also
* searched inside property `items` (array of). See function `forEachGeneric`
* for details.
*
* @param {CSN.Artifact} construct
* @param {(art: CSN.Member, name: string, prop: string, path: string[]) => any} callback
* @param {string[]} [path]
* @param {boolean} [ignoreIgnore]
*/
function forEachMember( construct, callback, path=[], ignoreIgnore=true) {

@@ -465,4 +487,11 @@ let obj = construct.returns || construct; // why the extra `returns` for actions?

// Apply function `callback(member, memberName)` to each member in `construct`,
// recursively (i.e. also for sub-elements of elements).
/**
* Apply function `callback(member, memberName)` to each member in `construct`,
* recursively (i.e. also for sub-elements of elements).
*
* @param {CSN.Artifact} construct
* @param {(art: CSN.Member, name: string, prop: string, path: string[], origConstruct: CSN.Artifact) => any} callback
* @param {CSN.Path} [path]
* @param {boolean} [ignoreIgnore]
*/
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true) {

@@ -476,6 +505,13 @@ forEachMember( construct, ( member, memberName, prop, subpath ) => {

// Apply function `callback` to all objects in dictionary `dict`, including all
// duplicates (found under the same name). Function `callback` is called with
// the following arguments: the object, the name, and -if it is a duplicate-
// the array index and the array containing all duplicates.
/**
* Apply function `callback` to all objects in dictionary `dict`, including all
* duplicates (found under the same name). Function `callback` is called with
* the following arguments: the object, the name, and -if it is a duplicate-
* the array index and the array containing all duplicates.
*
* @param {object} obj
* @param {string} prop
* @param {(dict: object, name: string, prop: string, path: CSN.Path) => any} callback
* @param {CSN.Path} path
*/
function forEachGeneric( obj, prop, callback, path = []) {

@@ -492,3 +528,9 @@ let dict = obj[prop];

// For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
/**
* For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
*
* @param {object} node
* @param {(ref: any, node: object, path: CSN.Path) => any} callback
* @param {CSN.Path} path
*/
function forEachRef(node, callback, path = []) {

@@ -540,2 +582,7 @@ if (node == null || typeof node !== 'object') {

/**
* @param {CSN.Query} query
* @param {(query: CSN.Query, path: CSN.Path) => any} callback
* @param {CSN.Path} path
*/
function forAllQueries(query, callback, path = []){

@@ -572,2 +619,7 @@ return traverseQuery(query, callback, path);

/**
* @param {CSN.QueryFrom} from
* @param {Function} callback
* @param {CSN.Path} path
*/
function traverseFrom( from, callback, path = [] ) {

@@ -588,5 +640,7 @@ if (from.ref) // ignore

* Returns true if the element has a specific annotation set to the given value.
* @param artifact - the artifact object
* @param annotationName - the name of the annotation (including the at-sign)
* @param value - the value
*
* @param {CSN.Artifact} artifact
* @param {string} annotationName Name of the annotation (including the at-sign)
* @param {any} value
* @returns {boolean}
*/

@@ -597,6 +651,11 @@ function hasBoolAnnotation(artifact, annotationName, value = true) {

// EDM specific check: Render ordinary property if element is NOT ...
// 1) ... annotated @cds.api.ignore
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
// function accepts EDM internal and external options
/**
* EDM specific check: Render ordinary property if element is NOT ...
* 1) ... annotated @cds.api.ignore
* 2) ... annotated @odata.foreignKey4 and odataFormat: structured
* function accepts EDM internal and external options
*
* @param {CSN.Element} elementCsn
* @param {CSN.Options} options
*/
function isEdmPropertyRendered(elementCsn, options) {

@@ -609,3 +668,2 @@ let isStructuredFormat = options.toOdata && options.toOdata.odataFormat === 'structured' || options.isStructFormat;

module.exports = {

@@ -612,0 +670,0 @@ getUtils,

@@ -86,10 +86,9 @@ // For testing: reveal non-enumerable properties in CSN, display result of csnRefs

function simpleRef( node, prop ) {
let ref = node[prop];
if (typeof ref === 'string') {
const art = artifactRef( ref, null );
if (art || !ref.startsWith( 'cds.'))
node['_' + prop] = refLocation( art );
const notFound = (options.testMode) ? undefined : null;
const ref = node[prop];
if (Array.isArray( ref )) {
node['_' + prop] = ref.map( r => refLocation( artifactRef( r, notFound ) ) );
}
else if (Array.isArray( ref )) {
node['_' + prop] = ref.map( r => refLocation( artifactRef( r, null ) ) );
else if (typeof ref !== 'string' || !ref.startsWith( 'cds.')) {
node['_' + prop] = refLocation( artifactRef( ref, notFound ) );
}

@@ -96,0 +95,0 @@ // catch (e) {

@@ -125,2 +125,21 @@ 'use strict'

/**
* Check whether the given artifact has type information. An artifact has type
* information when it is either a builtin, a struct, an enum, an array, an
* association OR if it references another type, i.e. typeOf. For the latter
* case an artifact's final type must be checked.
*
* @param {object} artifact
* @returns {boolean}
*/
function hasArtifactTypeInformation(artifact) {
// When is what property set?
return artifact.builtin // => `Integer`
|| artifact.elements // => `type A {}`
|| artifact.items // => `type A : array of Integer`
|| artifact.enum // => `type A : Integer enum {}`, `type` also set
|| artifact.target // => `type A : Association to B;`
|| artifact.type; // => `type A : [type of] Integer`
}
// Produce a printable name (for error messages) for element or artifact 'node'

@@ -492,2 +511,3 @@ function printableName(node) {

isContainerArtifact,
hasArtifactTypeInformation,
printableName,

@@ -494,0 +514,0 @@ addStringAnnotationTo,

@@ -80,4 +80,45 @@ // Make internal properties of the XSN / augmented CSN visible

}
return reveal( name && name !== '+' ? { definitions: model.definitions[name] } : model );
return reveal( parseXsnPath(name, model) );
// Returns the desired artifact/dictionary in the XSN.
//
// Usage:
// 1. Whole Model
// Simply pass `+`.
// 2. Entity (e.g. in service)
// Use `S.E`, i.e. the artifact's name in XSN.
// 3. Specific Element
// To get an element `e` of `S.E`, use `S.E/elements/e`, i.e. the
// JSON path delimited by "/" instead of "." (to avoid conflicts with artifact's FQN).
// 4. All elements
// To list all elements, use `S.E/elements/`. The final slash is important.
// 5. Other dictionaries or internal properties
// Use the JSON-like path delimited by "/". Add a final slash, e.g. `E.elements.a.kind/`.
//
// The string before the last slash ("/") is used as the property name to
// reveal the properties. So if the last path segment is an element name, do
// not add a slash or the name may be mistaken as a property name.
//
// Examples:
// `name.space/S/E/elements/a/kind/`
// `name.space/S/E/elements/a/type/scope/`
function parseXsnPath(path, xsn) {
if (!path || path === '+')
return xsn;
path = path.split('/');
path.unshift('definitions');
for (const segment of path) {
if (xsn[segment])
xsn = xsn[segment]
else if (segment)
throw new Error(`Raw Output: Path segment "${ segment }" could not be found. Path: ${ JSON.stringify(path) }!"`)
}
const propName = path[path.length > 1 ? path.length - 2 : 0 ];
const obj = {};
obj[propName] = xsn;
return obj;
}
function artifactIdentifier( node, parent ) {

@@ -102,4 +143,10 @@ if (node instanceof Array)

return '$magicVariables';
if (!node.name)
return JSON.stringify(node);
if (!node.name) {
try{
return JSON.stringify(node);
}
catch (e) {
return e.toString();
}
}
switch (node.kind) {

@@ -181,3 +228,4 @@ case undefined: // TODO: remove this `returns` property for actions

function columns( nodes ) {
return nodes && nodes.map( c => (c._parent) ? artifactIdentifier( c ) : reveal( c ) );
// If we will have specified elements, we need another test to see columns in --parse-cdl
return nodes && nodes.map( c => (c._parent && c._parent.elements) ? artifactIdentifier( c ) : reveal( c ) );
}

@@ -184,0 +232,0 @@

@@ -26,4 +26,4 @@ const { createOptionProcessor } = require('./base/optionProcessorHelper');

.option(' --beta-mode')
.option(' --beta <list>')
.option(' --old-transformers')
.option(' --std-json-parser')
.option(' --long-autoexposed')

@@ -80,5 +80,5 @@ .option(' --hana-flavor')

--internal-msg Write raw messages with call stack to <stdout>/<stderr>
--beta-mode Enable unsupported, incomplete (beta) features
--beta-mode Enable all unsupported, incomplete (beta) features
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use
--old-transformers Use the old transformers that work on XSN instead of CSN
--std-json-parser Use standard JSON parser for CSN parsing with old CSN frontend
--hana-flavor Compile with backward compatibility for HANA CDS (incomplete)

@@ -89,3 +89,3 @@ --parse-only Stop compilation after parsing and write result to <stdout>

--doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
Backward compatibility options (deprecated, do not use)

@@ -101,2 +101,3 @@ --long-autoexposed Produce long names (with underscores) for autoexposed entities

toCsn [options] <files...> (default) Generate original model as CSN
parseCdl Generate a CSN that is close to the CDL source.
toRename [options] <files...> (internal) Generate SQL DDL rename statements

@@ -216,3 +217,4 @@ `);

.option('-c, --csn')
.help(`
.option('--compatibility')
.help(`
Usage: cdsc toSql [options] <files...>

@@ -253,2 +255,3 @@

-c, --csn Generate "sql_csn.json" with SQL-preprocessed model
--compatibility Try and create ON-conditions just like HDBCDS
`);

@@ -295,4 +298,16 @@

optionProcessor.command('parseCdl')
.option('-h, --help')
.help(`
Usage: cdsc parseCdl [options] <file>
Only parse the CDL and output a CSN that is close to the source. Does not
resolve imports, apply extensions or expand any queries.
Options
-h, --help Show this help text
`);
module.exports = {
optionProcessor
};

@@ -83,2 +83,3 @@

const { error, signal, warning, info } = alerts(csn, options);
const compatMode = options.toSql && options.toSql.compatibility && options.toSql.dialect === 'hana' && options.toSql.src === 'hdi';

@@ -209,2 +210,3 @@ // FIXME: Currently requires 'options.forHana', because it can only render HANA-ish SQL dialect

let childEnv = increaseIndent(env);
childEnv.isEntity = true;
let hanaTc = art.technicalConfig && art.technicalConfig.hana;

@@ -241,3 +243,3 @@ let result = '';

.join(', ');
if (uniqueFields !== '') {
if (uniqueFields !== '' && !compatMode) {
result += ',\n' + childEnv.indent + 'UNIQUE(' + uniqueFields + ')'

@@ -328,4 +330,8 @@ }

result += ' JOIN ';
result += quoteSqlId(absoluteCdsName(elm.target)) + ' AS ' + quoteSqlId(elementName) + ' ON (';
result += renderExpr(elm.on, env) + ')';
result += quoteSqlId(absoluteCdsName(elm.target)) + ' AS ' + quoteSqlId(elementName) + ` ON ${compatMode ? '' : '('}`;
if(compatMode && env.isEntity){
result += renderHdbcdsConformingOnCondition(prepOnCondition(elm.on), env);
} else {
result += renderExpr(elm.on, env) + ')';
}
}

@@ -548,3 +554,3 @@ return result;

result += env.indent + 'NULL AS ' + quoteSqlId(col.as || leaf);
} else if (col.cast) {
} else if (col.cast && !(options && options.toSql && options.toSql.dialect === 'sqlite')) {
result = env.indent + 'CAST(' + renderExpr(col, env, true) + ' AS ';

@@ -812,2 +818,196 @@ result += renderBuiltinType(col.cast.type) + renderTypeParameters(col.cast);

function prepOnCondition(onCondition){
return onCondition.map((x,i) => {
if(typeof x === 'string'){
if(i === 0){
return `${x} `;
} else if(i === onCondition.length-1){
return ` ${x}`;
} else {
return ` ${x} `;
}
} else {
return x;
}
})
}
function renderHdbcdsConformingOnCondition(x, env){
// Compound expression
if (x instanceof Array) {
// Simply concatenate array parts with spaces (with a tiny bit of beautification)
// FIXME: Take this for `toCdl`, too
let tokens = x.map(item => renderHdbcdsConformingOnCondition(item, env));
let result = '';
for (let i = 0; i < tokens.length; i++) {
result += tokens[i];
// No space after last token, after opening parentheses, before closing parentheses, before comma
}
return result;
}
// Various special cases represented as objects
else if (typeof x === 'object' && x !== null) {
// Literal value, possibly with explicit 'literal' property
if (x.val !== undefined) {
switch (x.literal || typeof x.val) {
case 'number':
case 'boolean':
case 'null':
// 17.42, NULL, TRUE
return String(x.val);
case 'x':
// x'f000'
return `${x.literal}'${x.val}'`;
case 'date':
case 'time':
case 'timestamp':
if (options.toSql.dialect === 'sqlite') {
// date('2017-11-02')
return `${x.literal}('${x.val}')`;
} else {
// date'2017-11-02'
return `${x.literal}'${x.val}'`;
}
case 'string':
// 'foo', with proper escaping
return `'${x.val.replace(/'/g, "''")}'`;
case 'object':
if (x.val === null) {
return 'NULL';
}
// otherwise fall through to
default:
throw new Error('Unknown literal or type: ' + JSON.stringify(x));
}
}
// Enum symbol
else if (x['#']) {
// #foo
// FIXME: We can't do enums yet because they are not resolved (and we don't bother finding their value by hand)
signal(error`Enum values are not yet supported for conversion to SQL`, x.location);
return '';
}
// Reference: Array of path steps, possibly preceded by ':'
else if (x.ref) {
if (options.forHana && !x.param && !x.global) {
if(x.ref[0] === '$user') {
// FIXME: this is all not enough: we might need an explicit select item alias
if (x.ref[1] === 'id') {
if (options.toSql.user && typeof options.toSql.user === 'string' || options.toSql.user instanceof String) {
return `'${options.toSql.user}'`;
}
else if ((options.toSql.user && options.toSql.user.id) && (typeof options.toSql.user.id === 'string' || options.toSql.user.id instanceof String)) {
return `'${options.toSql.user.id}'`;
} else {
return "SESSION_CONTEXT ( 'APPLICATIONUSER' ) ";
}
}
}
else if (x.ref[1] === 'locale') {
return "SESSION_CONTEXT ( 'LOCALE' ) ";
}
}
else if(x.ref[0] === '$at') {
if(x.ref[1] === 'from') {
return "SESSION_CONTEXT ( 'VALID-FROM' ) ";
}
else if(x.ref[1] === 'to') {
return "SESSION_CONTEXT ( 'VALID-TO' ) ";
}
}
// FIXME: We currently cannot distinguish whether '$parameters' was quoted or not - we
// assume that it was not if the path has length 2 (
if (firstPathStepId(x.ref) === '$parameters' && x.ref.length == 2) {
// Parameters must be uppercased and unquoted in SQL
return `:${x.ref[1].toUpperCase()}`;
}
if (x.param) {
return `:${x.ref[0].toUpperCase()}`;
}
return x.ref.map(renderPathStep)
.filter(s => s != '')
.map(ps => `"${ps.toUpperCase()}"`)
.join('.');
}
// Function call, possibly with args (use '=>' for named args)
else if (x.func) {
return renderFunc( x, options.toSql.dialect, a => renderArgs(a, '=>', env), true );
}
// Nested expression
else if (x.xpr) {
return renderHdbcdsConformingOnCondition(x.xpr, env);
}
// Sub-select
else if (x.SELECT) {
// renderQuery for SELECT does not bring its own parentheses (because it is also used in renderView)
return `(${renderQuery('<subselect>', x, increaseIndent(env))})`;
}
else if (x.SET) {
// renderQuery for SET always brings its own parentheses (because it is also used in renderViewSource)
return `${renderQuery('<union>', x, increaseIndent(env))}`;
}
else {
throw new Error('Unknown expression: ' + JSON.stringify(x));
}
}
// Not a literal value but part of an operator, function etc - just leave as it is
// FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
else {
return String(x).toUpperCase();
}
// Render a single path step 's' at path position 'idx', which can have filters or parameters or be a function
function renderPathStep(s, idx) {
// Simple id or absolute name
if (typeof(s) === 'string') {
// TODO: When is this actually executed and not handled already in renderExpr?
const magicForHana = {
'$now': 'CURRENT_TIMESTAMP',
'$user.id': "SESSION_CONTEXT('APPLICATIONUSER')",
'$user.locale': "SESSION_CONTEXT('LOCALE')",
}
// Some magic for first path steps
if (idx == 0) {
// HANA-specific translation of '$now' and '$user'
// FIXME: this is all not enough: we might need an explicit select item alias
if (magicForHana[s]) {
return magicForHana[s];
}
// Ignore initial $projection and initial $self
if (s === '$projection' || s === '$self') {
return '';
}
}
return quoteSqlId(s);
}
// ID with filters or parameters
else if (typeof s === 'object') {
// Sanity check
if (!s.func && !s.id) {
throw new Error('Unknown path step object: ' + JSON.stringify(s));
}
// Not really a path step but an object-like function call
if (s.func) {
return `${s.func}(${renderArgs(s.args, '=>', env)})`;
}
// Path step, possibly with view parameters and/or filters
let result = `"${quoteSqlId(s.id)}"`;
if (s.args) {
// View parameters
result += `(${renderArgs(s.args, '=>', env)})`;
}
if (s.where) {
// Filter, possibly with cardinality
// FIXME: Does SQL understand filter cardinalities?
result += `[${s.cardinality ? (s.cardinality.max + ': ') : ''}${renderHdbcdsConformingOnCondition(s.where, env)}]`;
}
return result;
}
else {
throw new Error('Unknown path step: ' + JSON.stringify(s));
}
}
}
// Render an expression (including paths and values) or condition 'x'.

@@ -814,0 +1014,0 @@ // (no trailing LF, don't indent if inline)

@@ -381,3 +381,4 @@ 'use strict';

switch(message.messageId){
case 'empty-entity-or-type':
case 'empty-entity':
case 'empty-type':
message.severity = 'Error';

@@ -384,0 +385,0 @@ break;

@@ -112,3 +112,4 @@ 'use strict';

isStructured,
inspectRef
inspectRef,
artifactRef
} = getUtils(csn);

@@ -174,4 +175,18 @@

if (def.kind !== 'entity') {
if (!isStructured(getFinalTypeDef(def)))
toFinalBaseType(def);
let finalTypeDef = def;
if(def.type && def.type.ref) {
finalTypeDef = artifactRef(def.type);
if(!finalTypeDef.type) {
signal(error`"${defName}" has no final type`, ['definitions', defName]);
return;
}
}
if (!isStructured(finalTypeDef)) {
try {
toFinalBaseType(def);
} catch(ex) {
signal(error`"${defName}" final base type not found`, ['definitions', defName]);
return
}
}
toFinalBaseType(def.items);

@@ -279,11 +294,11 @@ toFinalBaseType(def.returns);

if (def.kind === 'action' || def.kind === 'function') {
exposeStructTypesForAction(def, defName, service);
exposeTypesForAction(def, defName, service);
}
for (let actionName in def.actions || {}) {
exposeStructTypesForAction(def.actions[actionName], `${defName}_${actionName}`, service);
exposeTypesForAction(def.actions[actionName], `${defName}_${actionName}`, service);
}
if (def.kind === 'entity' || def.kind === 'view') {
let isAction = false
let isAction = false;
// If a member is of type 'array of T' where T is either user defined structured type outside of the service or anonymous type,

@@ -294,13 +309,9 @@ // then expose T and assign it do the member.

// and on params and returns of action/function
if (member.kind === 'action' || member.kind === 'function') {
isAction = true;
return;
}
if (['params', 'returns'].includes(propertyName) && isAction) return;
if (member.kind === 'action' || member.kind === 'function') isAction = true;
// anonymous defined "array of"
if (member.items || (member.type && getFinalTypeDef(member.type).items)) {
structuredOData ?
exposeArrayOfTypeOf(member, service, `${defNameWithoutServiceName(defName, service)}_${memberName}`, memberName)
: signal(error`"${memberName}": Element must not be an "array of" in flat mode`, path);
if (structuredOData)
exposeArrayOfTypeOf(member, service, `${defNameWithoutServiceName(defName, service)}_${memberName}`, memberName);
else if (!isAction) signal(error`"${memberName}": Element must not be an "array of" in flat mode`, path);
}

@@ -318,6 +329,11 @@ }, ['definitions', defName]);

// This must be done before 4.1, since all composition targets are annotated with @odata.draft.enabled in this step
let sortByAssociationDependency = require('./sortByAssociationDependency');
let sortedArtifactNames = sortByAssociationDependency(csn);
let flattenedKeyArts = Object.create(null);
forEachDefinition(csn, (def, defName) => {
sortedArtifactNames.forEach(defName => {
let def = csn.definitions[defName];
flattenForeignKeysForArt(def, defName, flattenedKeyArts);
});
})

@@ -433,2 +449,3 @@ // Fourth walk through the model: Now all artificially generated things are in place

let keyCount = 0;
/** @type {[string, CSN.Element][]} */
let mediaTypes = [];

@@ -518,3 +535,6 @@ // Walk the elements

switch (message.messageId) {
case 'empty-entity-or-type':
case 'enum-value-ref':
case 'empty-entity':
case 'empty-type':
case 'check-proper-type-of':
message.severity = 'Error';

@@ -634,9 +654,20 @@ break;

// in 'service' and make 'action' use them instead
function exposeStructTypesForAction(action, actionName, service) {
exposeStructTypeOf(action.returns, service, `return_${actionName.replace(/\./g, '_')}`, actionName);
function exposeTypesForAction(action, actionName, service) {
if (action.returns && isArreyed(action.returns))
exposeArrayOfTypeOf(action.returns, service, `return_${actionName.replace(/\./g, '_')}`, actionName);
else
exposeStructTypeOf(action.returns, service, `return_${actionName.replace(/\./g, '_')}`, actionName);
for (let paramName in action.params || {}) {
exposeStructTypeOf(action.params[paramName], service, `param_${actionName.replace(/\./g, '_')}_${paramName}`, actionName);
if (isArreyed(action.params[paramName]))
exposeArrayOfTypeOf(action.params[paramName], service, `param_${actionName.replace(/\./g, '_')}_${paramName}`, actionName);
else
exposeStructTypeOf(action.params[paramName], service, `param_${actionName.replace(/\./g, '_')}_${paramName}`, actionName);
}
}
function isArreyed(node) {
return node.items || (node.type && getFinalTypeDef(node.type).items);
}
// If a member is of type "array of <named type|anonymous type>", we expose the arrayed type,

@@ -651,17 +682,23 @@ // like we expose structures in structured mode

}
// handle the case: 'elem: array of Foo' and 'type Foo: array of ...'
// we do not see the 'items' property in the element and
// need to create a new type, which holds the items property
else if (node.type && getFinalTypeDef(node.type).items) {
if (!isArtifactInService(node.type, service)) {
let typeId = `${service}.${node.type}`;
let newType = exposeArrayedType(getFinalTypeDef(node.type), typeId);
// When we have in the model something like:
// type Foo: array of Bar; type Bar: { qux: Integer };
// In the type Foo we expand the first level of elements of the items like we have in CDL this:
// type Foo: array of { qux: Integer };
expandFirstLevelOfArrayed(newType);
node.type = typeId;
// we can have both of the 'type' and 'items' in the cases:
// 1. 'elem: Foo' and 'type Foo: array of Baz' and 'type Baz: { ... }'
// or 2. 'elem: Foo' and type Foo: array of Integer|String|...'
else if (node.type) {
// case 2. - in V2 we expand to the underlying base scalar and remove the type property
if (node.items && node.items.type && isBuiltinType(node.items.type)
&& options.toOdata.version === 'v2') delete node.type;
else if (getFinalTypeDef(node.type).items) {
if (!isArtifactInService(node.type, service)) {
let typeId = `${service}.${node.type}`;
let newType = exposeArrayedType(getFinalTypeDef(node.type), typeId);
// When we have in the model something like:
// type Foo: array of Bar; type Bar: { qux: Integer };
// In the type Foo we expand the first level of elements of the items like we have in CDL this:
// type Foo: array of { qux: Integer };
expandFirstLevelOfArrayed(newType);
node.type = typeId;
}
// case 1. - as we keep the type property, the items property is removed
if (node.items) delete node.items;
}
if (node.items) delete node.items;
}

@@ -718,3 +755,3 @@

}
copyAnnotations(node, type);
typeDef.kind === 'type' ? copyAnnotations(typeDef, type) : copyAnnotations(node, type);
if (structuredOData) delete node.elements;

@@ -765,6 +802,8 @@ node.type = `${service}.${typeId}`;

flattenedKeyArts[defName] = true;
let rootPath = ['definitions', defName]
const rootPath = ['definitions', defName];
forEachMemberRecursively(def, (member, memberName, prop, subpath, parent) => {
// Generate foreign key elements for managed associations
if (isManagedAssociationElement(member)) {
// Generate foreign key elements for managed associations.
// In the case of composition of aspect we might have an element which does not
// have on-cond and keys, therefore the check if a member has a 'keys'
if (isManagedAssociationElement(member) && member.keys) {
// Flatten foreign keys (replacing foreign keys that are managed associations by their respective foreign keys)

@@ -818,3 +857,3 @@ flattenForeignKeys(member, flattenedKeyArts, flattenForeignKeysForArt);

if (!structuredOData) // flat-mode
flattenOnCond(member, memberName, def.elements, defName);
flattenOnCond(member, memberName, def.elements, defName, rootPath.concat(subpath));
else // structured-mode

@@ -885,3 +924,2 @@ normalizeOnCondForStructuredMode(member)

}
// Generate the annotations describing the draft actions (only draft roots can be activated/edited)

@@ -888,0 +926,0 @@ if (artifact == rootArtifact) {

@@ -7,3 +7,3 @@ 'use strict';

const { setProp, cloneWithTransformations } = require('../base/model');
const { addStringAnnotationTo, printableName,
const { addBoolAnnotationTo, addStringAnnotationTo, printableName,
copyAnnotations, isStructuredElement, hasBoolAnnotation } = require('../model/modelUtils');

@@ -624,2 +624,4 @@ const alerts = require('../base/alerts');

let draftUuid = createScalarElement('DraftUUID', 'cds.UUID', true, undefined);
addBoolAnnotationTo('@UI.Hidden', true, draftUuid);
addStringAnnotationTo('@Common.Label', '{i18n>Draft_DraftUUID}', draftUuid);
addElement(draftUuid, artifact);

@@ -629,2 +631,3 @@

let creationDateTime = createScalarElement('CreationDateTime', 'cds.Timestamp', false, undefined);
addStringAnnotationTo('@Common.Label', '{i18n>Draft_CreationDateTime}', creationDateTime);
addElement(creationDateTime, artifact);

@@ -635,2 +638,3 @@

createdByUser.length = { literal: 'number', val: 256 };
addStringAnnotationTo('@Common.Label', '{i18n>Draft_CreatedByUser}', createdByUser);
addElement(createdByUser, artifact);

@@ -640,2 +644,4 @@

let draftIsCreatedByMe = createScalarElement('DraftIsCreatedByMe', 'cds.Boolean', false, undefined);
addBoolAnnotationTo('@UI.Hidden', true, draftIsCreatedByMe);
addStringAnnotationTo('@Common.Label', '{i18n>Draft_DraftIsCreatedByMe}', draftIsCreatedByMe);
addElement(draftIsCreatedByMe, artifact);

@@ -645,2 +651,3 @@

let lastChangeDateTime = createScalarElement('LastChangeDateTime', 'cds.Timestamp', false, undefined);
addStringAnnotationTo('@Common.Label', '{i18n>Draft_LastChangeDateTime}', lastChangeDateTime);
addElement(lastChangeDateTime, artifact);

@@ -651,2 +658,3 @@

lastChangedByUser.length = { literal: 'number', val: 256 };
addStringAnnotationTo('@Common.Label', '{i18n>Draft_LastChangedByUser}', lastChangedByUser);
addElement(lastChangedByUser, artifact);

@@ -657,2 +665,3 @@

inProcessByUser.length = { literal: 'number', val: 256 };
addStringAnnotationTo('@Common.Label', '{i18n>Draft_InProcessByUser}', inProcessByUser);
addElement(inProcessByUser, artifact);

@@ -662,2 +671,4 @@

let draftIsProcessedByMe = createScalarElement('DraftIsProcessedByMe', 'cds.Boolean', false, undefined);
addBoolAnnotationTo('@UI.Hidden', true, draftIsProcessedByMe);
addStringAnnotationTo('@Common.Label', '{i18n>Draft_DraftIsProcessedByMe}', draftIsProcessedByMe);
addElement(draftIsProcessedByMe, artifact);

@@ -664,0 +675,0 @@

@@ -230,2 +230,3 @@ 'use strict';

foreignKey.$generatedFieldName = foreignKeyElementName;
setProp(foreignKey, "$path", path); // attach $path to the newly created element - used for inspectRef in processAssociationOrComposition

@@ -311,2 +312,10 @@ result[foreignKeyElementName] = foreignKeyElement;

let result = Object.create(null);
const addGeneratedFlattenedElement = (e, eName) => {
if(result[eName]){
signal(error`Generated element ${eName} conflicts with other generated element`, pathInCsn)
} else {
result[eName] = e;
}
}
for (let childName in struct) {

@@ -320,3 +329,3 @@ let childElem = struct[childName];

let flatElem = grandChildElems[grandChildName];
result[flatElemName] = flatElem;
addGeneratedFlattenedElement(flatElem, flatElemName);
// TODO: check with values. In CSN such elements have only "@Core.Computed": true

@@ -328,3 +337,2 @@ // If the original element had a value, construct one for the flattened element

// Preserve the generated element name as it would have been with 'hdbcds' names
result[flatElemName] = flatElem;
}

@@ -337,3 +345,3 @@ } else {

setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
result[flatElemName] = flatElem;
addGeneratedFlattenedElement(flatElem, flatElemName);
}

@@ -406,3 +414,3 @@ }

// the non-enumerable property '_flatElementNameWithDots'.
function flattenOnCond(assoc, assocName, defElements, defName) {
function flattenOnCond(assoc, assocName, defElements, defName, path) {

@@ -452,3 +460,3 @@ if (!assoc.on) return; // nothing to do

node.ref.splice(0, 1);
}, ['definitions', defName, 'elements', assocName]);
}, path);
}

@@ -506,2 +514,4 @@

let finalBaseType = getFinalBaseType(node.type);
if(finalBaseType === null)
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
if (finalBaseType.elements) {

@@ -619,2 +629,4 @@ node.elements = finalBaseType.elements; // copy elements

let draftUuid = createScalarElement('DraftUUID', 'cds.UUID', true);
draftUuid.DraftUUID['@UI.Hidden'] = true;
draftUuid.DraftUUID['@Common.Label'] = '{i18n>Draft_DraftUUID}';
addElement(draftUuid, artifact, artifactName);

@@ -624,2 +636,3 @@

let creationDateTime = createScalarElement('CreationDateTime', 'cds.Timestamp');
creationDateTime.CreationDateTime['@Common.Label'] = '{i18n>Draft_CreationDateTime}';
addElement(creationDateTime, artifact, artifactName);

@@ -630,2 +643,3 @@

createdByUser['CreatedByUser'].length = 256;
createdByUser.CreatedByUser['@Common.Label'] = '{i18n>Draft_CreatedByUser}';
addElement(createdByUser, artifact, artifactName);

@@ -635,2 +649,4 @@

let draftIsCreatedByMe = createScalarElement('DraftIsCreatedByMe', 'cds.Boolean');
draftIsCreatedByMe.DraftIsCreatedByMe['@UI.Hidden'] = true;
draftIsCreatedByMe.DraftIsCreatedByMe['@Common.Label'] = '{i18n>Draft_DraftIsCreatedByMe}';
addElement(draftIsCreatedByMe, artifact, artifactName);

@@ -640,2 +656,3 @@

let lastChangeDateTime = createScalarElement('LastChangeDateTime', 'cds.Timestamp');
lastChangeDateTime.LastChangeDateTime['@Common.Label'] = '{i18n>Draft_LastChangeDateTime}';
addElement(lastChangeDateTime, artifact, artifactName);

@@ -646,2 +663,3 @@

lastChangedByUser['LastChangedByUser'].length = 256;
lastChangedByUser.LastChangedByUser['@Common.Label'] = '{i18n>Draft_LastChangedByUser}';
addElement(lastChangedByUser, artifact, artifactName);

@@ -652,2 +670,3 @@

inProcessByUser['InProcessByUser'].length = 256;
inProcessByUser.InProcessByUser['@Common.Label'] = '{i18n>Draft_InProcessByUser}';
addElement(inProcessByUser, artifact, artifactName);

@@ -657,2 +676,4 @@

let draftIsProcessedByMe = createScalarElement('DraftIsProcessedByMe', 'cds.Boolean');
draftIsProcessedByMe.DraftIsProcessedByMe['@UI.Hidden'] = true;
draftIsProcessedByMe.DraftIsProcessedByMe['@Common.Label'] = '{i18n>Draft_DraftIsProcessedByMe}';
addElement(draftIsProcessedByMe, artifact, artifactName);

@@ -783,3 +804,3 @@

* @param {any} elem is in form: { b: { type: 'cds.String' } }
* @param {any} artifact is: { kind: 'entity', elements: { a: { type: 'cds.Integer' } ... } }
* @param {CSN.Artifact} artifact is: { kind: 'entity', elements: { a: { type: 'cds.Integer' } ... } }
* @param {string} [artifactName] Name of the artifact in `csn.definitions[]`.

@@ -816,3 +837,3 @@ * @returns {void}

* @param {object} elem
* @param {object} artifact
* @param {CSN.Artifact} artifact
* @param {string} artifactName

@@ -875,3 +896,3 @@ * @param {string} elementName

* In form of `{ myAction: { kind: 'action', returns ... } }`
* @param {object} artifact Artifact in the form of `{ kind: 'entity', elements: ... }`
* @param {CSN.Artifact} artifact Artifact in the form of `{ kind: 'entity', elements: ... }`
* @param {string} artifactName Name of the artifact (used for error locations).

@@ -928,7 +949,7 @@ **/

*
* @param {any} annoName Annotation name
* @param {any} element Element to be checked
* @param {any[]} path
* @param {any} artifact Artifact
* @returns {Boolean} True if no errors
* @param {string} annoName Annotation name
* @param {object} element Element to be checked
* @param {string[]} path
* @param {CSN.Artifact} artifact
* @returns {boolean} True if no errors
*/

@@ -951,3 +972,3 @@ function checkAssignment(annoName, element, path, artifact) {

* @param {any} annoName Name of the annotation
* @param {any} artifact Root artifact containing the elements
* @param {CSN.Artifact} artifact Root artifact containing the elements
* @param {string} artifactName Name of the root artifact

@@ -969,5 +990,5 @@ * @param {boolean} [err=true] Down-grade to a warning if set to false

*
* @param artifact the artifact
* @param path path to get to `artifact` (mainly used for error messages)
* @param callback the callback to be called
* @param {CSN.Artifact} artifact the artifact
* @param {string[]} path path to get to `artifact` (mainly used for error messages)
* @param {(art: CSN.Artifact, path: string[]) => any} callback Function called for each element recursively.
*/

@@ -974,0 +995,0 @@ function recurseElements(artifact, path, callback) {

@@ -1,1 +0,1 @@

{"bin":{"cdsc":"bin/cdsc.js","cdshi":"bin/cdshi.js","cdsse":"bin/cdsse.js"},"bundleDependencies":false,"dependencies":{"antlr4":"4.7.1","resolve":"1.8.1","sax":"^1.2.4"},"deprecated":false,"description":"CDS (Core Data Services) compiler and backends","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","version":"1.24.4","license":"SEE LICENSE IN developer-license-3.1.txt"}
{"bin":{"cdsc":"bin/cdsc.js","cdshi":"bin/cdshi.js","cdsse":"bin/cdsse.js"},"bundleDependencies":false,"dependencies":{"antlr4":"4.7.1","resolve":"1.8.1","sax":"^1.2.4"},"deprecated":false,"description":"CDS (Core Data Services) compiler and backends","keywords":["CDS"],"main":"lib/main.js","name":"@sap/cds-compiler","version":"1.26.2","license":"SEE LICENSE IN developer-license-3.1.txt"}

@@ -8,3 +8,5 @@ # Getting started

[Installation and Usage](#installation-and-usage)
[Command invocation](#command-invocation)
[Command invocation](#command-invocation)
[Build from source](#build-from-source)
[Documentation](#documentation)

@@ -41,1 +43,22 @@ ## Installation and Usage

* `2`: commmand invocation error (invalid options, repeated file name)
### Build from source
We recommend to install cds-compiler using npm. However, if you want to use
the latest master (e.g. for testing purposes) then you need to set up the
compiler first:
```sh
git clone git@github.wdf.sap.corp:cdx/cds-compiler.git
cd cds-compiler
npm install
npm run download # Downloads Antlr (Java Dependency)
npm run gen # Generates the parser
./bin/cdsc.js --help
```
## Documentation
Please refer to the [official CDS documentation][capire].
[capire]: https://cap.cloud.sap/docs/cds/

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc