@sap/cds-dk 9.9.2 → 9.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/bin/import.js +6 -1
- package/lib/import/common.js +3 -2
- package/lib/import/importapi.js +1 -1
- package/lib/import/odata/annotationParser.js +316 -0
- package/lib/import/odata/v2parser.js +217 -0
- package/lib/import/odata/v4parser.js +8 -232
- package/lib/watch/watcher.js +27 -1
- package/npm-shrinkwrap.json +55 -42
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
8
8
|
|
|
9
|
+
## Version 9.9.3 - 2026-06-29
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `cds import` for OData v2 now can import annotations.
|
|
14
|
+
- `cds watch` on Windows no longer restarts the server for read-only file opens (antivirus, Windows Search indexer, TypeScript loaders like tsx). A mtime guard now confirms a file was actually written before emitting a `change` event.
|
|
15
|
+
|
|
9
16
|
## Version 9.9.2 - 2026-05-28
|
|
10
17
|
|
|
11
18
|
### Fixed
|
package/bin/import.js
CHANGED
|
@@ -2,7 +2,7 @@ module.exports = Object.assign(_import, {
|
|
|
2
2
|
_name: 'import',
|
|
3
3
|
options: [ '--services', '--for', '--out', '--as', '--name', '--include-namespaces', '--dependencies', '--from', '--config', '--destination', '--group', '--into-namespace' ],
|
|
4
4
|
shortcuts: [ '-s', '-4', '-o', '-as', '-n', '-ns', '-d',undefined, '-c', undefined, undefined, '-2', '-', '-f', '-b'],
|
|
5
|
-
flags: [ '--dry', '--force', '--resolve-bindings', '--no-copy', '--no-save', '--keep-namespace', '--data-product', '--edmx', '--rfc', '--odata', '--openapi', '--asyncapi' ],
|
|
5
|
+
flags: [ '--dry', '--force', '--resolve-bindings', '--no-copy', '--no-save', '--keep-namespace', '--data-product', '--edmx', '--rfc', '--odata', '--openapi', '--asyncapi', '--odata-v2-annotations' ],
|
|
6
6
|
help: `
|
|
7
7
|
# SYNOPSIS
|
|
8
8
|
|
|
@@ -79,6 +79,11 @@ module.exports = Object.assign(_import, {
|
|
|
79
79
|
Keeps the original namespace from the edmx content instead of using
|
|
80
80
|
the filename by default.
|
|
81
81
|
|
|
82
|
+
*--odata-v2-annotations*
|
|
83
|
+
Imports OData annotations into the CSN output. For OData V2, annotations
|
|
84
|
+
are not imported by default and must be explicitly enabled with this flag.
|
|
85
|
+
For OData V4, annotations are always imported.
|
|
86
|
+
|
|
82
87
|
*-ns | --include-namespaces <string_of_namespaces> or* "\x2A"
|
|
83
88
|
Imports only entities matching to the given list of namespaces.
|
|
84
89
|
For OData V2 attributes with the namespace "sap" & "m" are captured by
|
package/lib/import/common.js
CHANGED
|
@@ -15,10 +15,11 @@ let optionsMapping = {
|
|
|
15
15
|
'keep-namespace': 'keepNamespace',
|
|
16
16
|
'include-namespaces': 'includeNamespaces',
|
|
17
17
|
'no-copy': 'no_copy',
|
|
18
|
-
'no-save': 'no_save'
|
|
18
|
+
'no-save': 'no_save',
|
|
19
|
+
'odata-v2-annotations': 'odataV2Annotations'
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
let apiOptions = ['includeNamespaces', 'keepNamespace'];
|
|
22
|
+
let apiOptions = ['includeNamespaces', 'keepNamespace', 'odataV2Annotations'];
|
|
22
23
|
let commonOptions = ['for', 'no_copy', 'no_save', 'out', 'dry', 'as', 'from', 'force', 'config', 'destination', 'group', 'name'];
|
|
23
24
|
let usingStatements = [];
|
|
24
25
|
|
package/lib/import/importapi.js
CHANGED
|
@@ -64,7 +64,7 @@ async function cds_import_from_edmx(filepath, options = { keepNamespace: false }
|
|
|
64
64
|
filepath = await common.isValidInputFile(path.resolve(process.cwd(), filepath));
|
|
65
65
|
const src = await common.getContentFromFile(filepath);
|
|
66
66
|
// takes the api specific options and reads it from env
|
|
67
|
-
let odataOptions = { include_namespaces: options.includeNamespaces, keep_namespace: options.keepNamespace};
|
|
67
|
+
let odataOptions = { include_namespaces: options.includeNamespaces, keep_namespace: options.keepNamespace, odataV2Annotations: options.odataV2Annotations};
|
|
68
68
|
if (cds.env.import) {
|
|
69
69
|
common.readEnvVariables(odataOptions, false);
|
|
70
70
|
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// annotation parsing funtions common for v2parser.js and v4parser.js
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { warn } = require("../../util/term");
|
|
6
|
+
|
|
7
|
+
const known_vocabularies = {
|
|
8
|
+
'Org.OData.Authorization.V1': 'Authorization',
|
|
9
|
+
'Org.OData.Aggregation.V1': 'Aggregation',
|
|
10
|
+
'Org.OData.Core.V1': 'Core',
|
|
11
|
+
'Org.OData.Capabilities.V1': 'Capabilities',
|
|
12
|
+
'Org.OData.Validation.V1': 'Validation',
|
|
13
|
+
'Org.OData.Measures.V1': 'Measures',
|
|
14
|
+
'Org.OData.JSON.V1': 'JSON',
|
|
15
|
+
'Org.OData.Repeatability.V1': 'Repeatability',
|
|
16
|
+
|
|
17
|
+
'com.sap.vocabularies.Analytics.v1': 'Analytics',
|
|
18
|
+
'com.sap.vocabularies.CodeList.v1': 'CodeList',
|
|
19
|
+
'com.sap.vocabularies.Common.v1': 'Common',
|
|
20
|
+
'com.sap.vocabularies.Communication.v1': 'Communication',
|
|
21
|
+
'com.sap.vocabularies.DataIntegration.v1': 'DataIntegration',
|
|
22
|
+
'com.sap.vocabularies.Graph.v1': 'Graph',
|
|
23
|
+
'com.sap.vocabularies.Hierarchy.v1': 'Hierarchy',
|
|
24
|
+
'com.sap.vocabularies.HTML5.v1': 'HTML5',
|
|
25
|
+
'com.sap.vocabularies.ODM.v1': 'ODM',
|
|
26
|
+
'com.sap.vocabularies.Offline.v1': 'Offline',
|
|
27
|
+
'com.sap.vocabularies.PDF.v1': 'PDF',
|
|
28
|
+
'com.sap.vocabularies.PersonalData.v1': 'PersonalData',
|
|
29
|
+
'com.sap.vocabularies.Session.v1': 'Session',
|
|
30
|
+
'com.sap.vocabularies.UI.v1': 'UI'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function _replaceSpecialCharacters(text) {
|
|
34
|
+
return text.replace(/(?:\\[rn]|[\r\n]+)+/gm, "\n").replace(/\s+/g, ' ').replace(/"/g, """).trim();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetObject) {
|
|
38
|
+
/**
|
|
39
|
+
* For scenarios like:
|
|
40
|
+
* <Annotation Term="Core.Computed" />
|
|
41
|
+
* Here, 'annotationObject' would be null but in CSN we need to treat it as:
|
|
42
|
+
* @Core.Computed : true
|
|
43
|
+
*/
|
|
44
|
+
if (annotationObject === null) {
|
|
45
|
+
annotationObject = true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* if annotationObject is empty list or empty object, we won't capture it.
|
|
49
|
+
* eg., <Annotation Term="Core.OptimisticConcurrency"> <Collection /> </Annotation>
|
|
50
|
+
* TODO: we might have to revisit it later for nested scenario.
|
|
51
|
+
*/
|
|
52
|
+
if ((annotationObject instanceof Array && annotationObject.length === 0) ||
|
|
53
|
+
(annotationObject instanceof Object && Object.keys(annotationObject).length === 0)
|
|
54
|
+
) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* <Annotation Term="Common.Text" Path="name"/> --> annotationTermObject = { "=": annotationTermObject.$Path };
|
|
59
|
+
*/
|
|
60
|
+
if (annotationObject.$Path) {
|
|
61
|
+
annotationObject = { "=": annotationObject.$Path };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Annotations need to be flattened in CSN. E.g.
|
|
66
|
+
*
|
|
67
|
+
* @Core.ContentDisposition : {
|
|
68
|
+
* Type : 'inline',
|
|
69
|
+
* Filename: typedImageFileName
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* is represented as:
|
|
73
|
+
*
|
|
74
|
+
* @Core.ContentDisposition.Type : 'inline',
|
|
75
|
+
* @Core.ContentDisposition.Filename : {'=': 'typedImageFileName'},
|
|
76
|
+
*/
|
|
77
|
+
if (annotationObject instanceof Object && !(annotationObject instanceof Array) && !(Object.keys(annotationObject).length === 1 && (Object.keys(annotationObject)[0] === '=' || Object.keys(annotationObject)[0] === 'xpr' || Object.keys(annotationObject)[0] === '#'))) {
|
|
78
|
+
Object.keys(annotationObject).forEach(innerTargetString => {
|
|
79
|
+
captureAnnotationInCSN(targetString + '.' + innerTargetString, annotationObject[innerTargetString], csnDefsTargetObject);
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
csnDefsTargetObject[targetString] = annotationObject;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function flattenAnnotationObject(annotationPrefix, annotationObject) {
|
|
87
|
+
let flattenedResult = [];
|
|
88
|
+
|
|
89
|
+
Object.entries(annotationObject).forEach(annotation => {
|
|
90
|
+
let currentResult = [];
|
|
91
|
+
/**
|
|
92
|
+
* if annotation looks like:
|
|
93
|
+
* { "#" : <value> } or { "=" : <value> }
|
|
94
|
+
* don't flatten because it is for the `EnumMember` & `Path` use cases respectively
|
|
95
|
+
*/
|
|
96
|
+
if (annotation[0] === '#' || annotation[0] === '=') {
|
|
97
|
+
currentResult[0] = annotationPrefix;
|
|
98
|
+
currentResult[1] = annotationObject;
|
|
99
|
+
} else {
|
|
100
|
+
currentResult[0] = annotationPrefix + '.' + annotation[0];
|
|
101
|
+
currentResult[1] = annotation[1];
|
|
102
|
+
}
|
|
103
|
+
flattenedResult.push(currentResult);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return flattenedResult;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function removeTypeInAnnotation(annotationObject) {
|
|
110
|
+
Object.keys(annotationObject).forEach(key => {
|
|
111
|
+
if (key === "$Type") delete annotationObject[key];
|
|
112
|
+
else if (annotationObject[key] instanceof Object) {
|
|
113
|
+
removeTypeInAnnotation(annotationObject[key]);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function parseCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
119
|
+
if (annotationTerm === "@Core.Description" || annotationTerm === "@Core.LongDescription") {
|
|
120
|
+
// we don't capture `null` value for doc
|
|
121
|
+
if (annotationTermObject === null) return;
|
|
122
|
+
|
|
123
|
+
annotationTermObject = _replaceSpecialCharacters(annotationTermObject);
|
|
124
|
+
|
|
125
|
+
if (csnDefsTargetObject.doc) {
|
|
126
|
+
if (annotationTerm === "@Core.Description") {
|
|
127
|
+
annotationTermObject = annotationTermObject + '\n\n' + csnDefsTargetObject.doc;
|
|
128
|
+
} else {
|
|
129
|
+
annotationTermObject = csnDefsTargetObject.doc + '\n\n' + annotationTermObject;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return captureAnnotationInCSN('doc', annotationTermObject, csnDefsTargetObject);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* <Annotation Term="Core.XYZ"/>
|
|
137
|
+
* For such scenario, mark the `annotationTermObject` as `true`
|
|
138
|
+
*/
|
|
139
|
+
if (annotationTermObject === null) annotationTermObject = true;
|
|
140
|
+
|
|
141
|
+
if (annotationTerm === "@Core.OptionalParameter") {
|
|
142
|
+
if (annotationTermObject && annotationTermObject.DefaultValue !== undefined) {
|
|
143
|
+
csnDefsTargetObject.default = { val: annotationTermObject.DefaultValue };
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
151
|
+
let targetString;
|
|
152
|
+
/**
|
|
153
|
+
* <Annotation Term="Capabilities.KeyAsSegmentSupported" />
|
|
154
|
+
* For such scenario, mark the `annotationTermObject` as `true`
|
|
155
|
+
*/
|
|
156
|
+
if (annotationTermObject === null) annotationTermObject = true;
|
|
157
|
+
|
|
158
|
+
removeTypeInAnnotation(annotationTermObject);
|
|
159
|
+
/**
|
|
160
|
+
* For entries like:
|
|
161
|
+
* <Annotation Term="Capabilities.FilterFunctions"> <Collection> <String>eq</String> <String>ne</String> </Collection> </Annotation>
|
|
162
|
+
* <Annotation Term="Capabilities.DeleteRestrictions.Deletable" Bool="true"/>
|
|
163
|
+
*
|
|
164
|
+
* simply preserve the value as it is
|
|
165
|
+
*/
|
|
166
|
+
if (annotationTermObject instanceof Array || !(annotationTermObject instanceof Object)) {
|
|
167
|
+
captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
Object.keys(annotationTermObject).forEach(annoKey => {
|
|
171
|
+
let currentAnnoValue = annotationTermObject[annoKey];
|
|
172
|
+
// if the currentAnnoValue is an Object, we need to flatten it
|
|
173
|
+
if (currentAnnoValue instanceof Object && !(currentAnnoValue instanceof Array)) {
|
|
174
|
+
targetString = annotationTerm + '.' + annoKey;
|
|
175
|
+
let flattenedResultList = flattenAnnotationObject(targetString, currentAnnoValue);
|
|
176
|
+
|
|
177
|
+
for (let idx = 0; idx < flattenedResultList.length; idx++) {
|
|
178
|
+
captureAnnotationInCSN(flattenedResultList[idx][0], flattenedResultList[idx][1], csnDefsTargetObject);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// if the currentAnnoValue is a list or direct value, we preserve as it is
|
|
182
|
+
else {
|
|
183
|
+
targetString = annotationTerm + '.' + annoKey;
|
|
184
|
+
captureAnnotationInCSN(targetString, currentAnnoValue, csnDefsTargetObject);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
191
|
+
const resolveCastFor = ["Minimum", "Maximum", "MaxItems"];
|
|
192
|
+
const term = annotationTerm.split('.')[1];
|
|
193
|
+
|
|
194
|
+
if (resolveCastFor.includes(term) && annotationTermObject.$Cast) {
|
|
195
|
+
annotationTermObject = Number(annotationTermObject.$Cast);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Capture all the annotation cases within an Element
|
|
203
|
+
*
|
|
204
|
+
* UnKnown term: <Annotation Term="Org.OData.ASDF.V1.qwe"/>
|
|
205
|
+
* Known term: <Annotation Term="Org.OData.Measures.V1.Unit" String="Centimeters"/>
|
|
206
|
+
*
|
|
207
|
+
* if "--include-namespaces *" then we also capture for ABS and QWE
|
|
208
|
+
* <TypeDefinition Name="TD_2" UnderlyingType="Edm.Int32" ABS:asdf="testing" QWE:qwe="testing"/>
|
|
209
|
+
*/
|
|
210
|
+
function parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
211
|
+
const term = annotationTerm.split('.');
|
|
212
|
+
const vocabAlias = term[0];
|
|
213
|
+
|
|
214
|
+
switch (vocabAlias) {
|
|
215
|
+
case "@Core":
|
|
216
|
+
parseCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
217
|
+
break;
|
|
218
|
+
case "@Capabilities":
|
|
219
|
+
parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
220
|
+
break;
|
|
221
|
+
case "@Validation":
|
|
222
|
+
parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
223
|
+
break;
|
|
224
|
+
default:
|
|
225
|
+
captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function validateAnnotationNamespace(annotationTerm, vocabNamespaceToAlias) {
|
|
231
|
+
let aliasOrNamespace = annotationTerm.substring(1, annotationTerm.lastIndexOf('.'));
|
|
232
|
+
|
|
233
|
+
// This check is for ignoring the @cds annotations given for special cases, e.g. @cds.validate = false for collided action/function
|
|
234
|
+
if (aliasOrNamespace === "cds") {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (let alias of Object.values(known_vocabularies)) {
|
|
239
|
+
if (alias === aliasOrNamespace) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (let [namespace, alias] of Object.entries(vocabNamespaceToAlias || {})) {
|
|
245
|
+
if (alias === aliasOrNamespace || namespace === aliasOrNamespace) {
|
|
246
|
+
console.log(warn(annotationTerm + " AnnotationTerm belongs to an unknown namespace: " + namespace));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log(warn(annotationTerm + ": Reference of this AnnotationTerm is not present in the edmx file."));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function resolveAnnotationTerm(schemaDataAnnotation, parserContext) {
|
|
255
|
+
for (const [namespace, alias] of Object.entries(parserContext.vocabNamespaceToAlias || {})) {
|
|
256
|
+
if (known_vocabularies[namespace]) {
|
|
257
|
+
// Case 1: Namespace belongs to known vocabularies. Replacing the namespace/alias of the doc with standard alias
|
|
258
|
+
if (schemaDataAnnotation.includes('@' + alias + '.')) {
|
|
259
|
+
schemaDataAnnotation = schemaDataAnnotation.split('@' + alias + '.').join('@' + known_vocabularies[namespace] + '.');
|
|
260
|
+
}
|
|
261
|
+
if (schemaDataAnnotation.includes('@' + namespace + '.')) {
|
|
262
|
+
schemaDataAnnotation = schemaDataAnnotation.split('@' + namespace + '.').join('@' + known_vocabularies[namespace] + '.');
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
// Case 2: Namespace doesn't belong to known vocabularies. Replacing the namespace with alias(if provided) in the doc.
|
|
266
|
+
if (schemaDataAnnotation.includes('@' + namespace + '.') && alias) {
|
|
267
|
+
schemaDataAnnotation = schemaDataAnnotation.split('@' + namespace + '.').join('@' + alias + '.');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Case 3: Namespace doesn't have edmx:Reference. But it is present in known_vocabularies.
|
|
273
|
+
* Replacing it with standard alias.
|
|
274
|
+
*/
|
|
275
|
+
for (const [namespace, alias] of Object.entries(known_vocabularies)) {
|
|
276
|
+
if (schemaDataAnnotation.includes(namespace)) {
|
|
277
|
+
schemaDataAnnotation = schemaDataAnnotation.split(namespace).join(alias);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let index = schemaDataAnnotation.indexOf('"@');
|
|
282
|
+
while (index != -1) {
|
|
283
|
+
let i = schemaDataAnnotation.indexOf('"', index + 1);
|
|
284
|
+
let annotationTerm = schemaDataAnnotation.substring(index + 1, i);
|
|
285
|
+
validateAnnotationNamespace(annotationTerm, parserContext.vocabNamespaceToAlias);
|
|
286
|
+
index = schemaDataAnnotation.indexOf('"@', i + 1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return JSON.parse(schemaDataAnnotation);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseAnnotations(schemaAnnotations, csnDefs, parserContext, resolveTarget) {
|
|
293
|
+
Object.keys(schemaAnnotations || {}).forEach(annotationTargetPath => {
|
|
294
|
+
let csnDefsTargetObject = resolveTarget(annotationTargetPath, csnDefs, parserContext);
|
|
295
|
+
if (csnDefsTargetObject) {
|
|
296
|
+
let annotationTerms = schemaAnnotations[annotationTargetPath];
|
|
297
|
+
Object.keys(annotationTerms).forEach(annotationTerm => {
|
|
298
|
+
let annotationTermObject = schemaAnnotations[annotationTargetPath][annotationTerm];
|
|
299
|
+
parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
module.exports = {
|
|
306
|
+
captureAnnotationInCSN,
|
|
307
|
+
flattenAnnotationObject,
|
|
308
|
+
removeTypeInAnnotation,
|
|
309
|
+
parseCoreTerm,
|
|
310
|
+
parseCapabilitiesTerm,
|
|
311
|
+
parseValidationTerm,
|
|
312
|
+
parseAnnotationTerms,
|
|
313
|
+
validateAnnotationNamespace,
|
|
314
|
+
resolveAnnotationTerm,
|
|
315
|
+
parseAnnotations
|
|
316
|
+
};
|
|
@@ -8,6 +8,7 @@ let messages = require("../message").getMessages();
|
|
|
8
8
|
let common = require("../common");
|
|
9
9
|
let versionInfo = require('../../../package.json').version;
|
|
10
10
|
const { warn } = require('../../util/term');
|
|
11
|
+
const annotationParser = require("./annotationParser");
|
|
11
12
|
|
|
12
13
|
const edmxncdsdatatype = {
|
|
13
14
|
"Edm.String": "cds.String",
|
|
@@ -78,6 +79,7 @@ function _initialize(parserContext) {
|
|
|
78
79
|
parserContext.allowedNamespaces = [];
|
|
79
80
|
parserContext.allowAllNamespaces = false;
|
|
80
81
|
parserContext.mockServerUc = true;
|
|
82
|
+
parserContext.vocabNamespaceToAlias = {};
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// Function to extract allowed namespace attributes from given set of attributes
|
|
@@ -1673,6 +1675,25 @@ function _generateEDMXJSON2CSN(jsonObj, ignorePersistenceSkip, parserContext) {
|
|
|
1673
1675
|
csn.$version = '2.0';
|
|
1674
1676
|
csn.definitions = {};
|
|
1675
1677
|
|
|
1678
|
+
// Parse edmx:Reference elements to build vocabNamespaceToAlias mapping
|
|
1679
|
+
const references = jsonObj["edmx:Edmx"]["edmx:Reference"];
|
|
1680
|
+
if (references) {
|
|
1681
|
+
const refArray = Array.isArray(references) ? references : [references];
|
|
1682
|
+
refArray.forEach(ref => {
|
|
1683
|
+
const include = ref["edmx:Include"];
|
|
1684
|
+
if (include) {
|
|
1685
|
+
const includeArray = Array.isArray(include) ? include : [include];
|
|
1686
|
+
includeArray.forEach(inc => {
|
|
1687
|
+
const namespace = inc._attributes?.Namespace;
|
|
1688
|
+
const alias = inc._attributes?.Alias;
|
|
1689
|
+
if (namespace && alias) {
|
|
1690
|
+
parserContext.vocabNamespaceToAlias[namespace] = alias;
|
|
1691
|
+
}
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1676
1697
|
// Extract associations
|
|
1677
1698
|
const associations = jsonObj["edmx:Edmx"]["edmx:DataServices"].Schema.Association;
|
|
1678
1699
|
if (associations) {
|
|
@@ -1742,12 +1763,204 @@ function _generateEDMXJSON2CSN(jsonObj, ignorePersistenceSkip, parserContext) {
|
|
|
1742
1763
|
|
|
1743
1764
|
try {
|
|
1744
1765
|
csn.definitions = JSON.parse(`{${definitions.join(",")}}`);
|
|
1766
|
+
|
|
1767
|
+
// Parse annotations after definitions are populated
|
|
1768
|
+
// Note: V2 annotations are opt-in (disabled by default for backward compatibility)
|
|
1769
|
+
// and require --odata-v2-annotations flag. V4 always imports annotations.
|
|
1770
|
+
const schemaAnnotations = jsonObj["edmx:Edmx"]["edmx:DataServices"].Schema.Annotations;
|
|
1771
|
+
if (schemaAnnotations && parserContext.annotations) {
|
|
1772
|
+
_parseV2SchemaAnnotations(schemaAnnotations, csn.definitions, parserContext);
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1745
1775
|
return JSON.stringify(csn, null, 4);
|
|
1746
1776
|
} catch {
|
|
1747
1777
|
throw new Error("Unable to convert to csn file.");
|
|
1748
1778
|
}
|
|
1749
1779
|
}
|
|
1750
1780
|
|
|
1781
|
+
function _toArray(node) {
|
|
1782
|
+
if (!node) return [];
|
|
1783
|
+
return Array.isArray(node) ? node : [node];
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
function _readAnnotationPrimitive(attributes) {
|
|
1787
|
+
if (attributes == null) return null;
|
|
1788
|
+
|
|
1789
|
+
if (attributes.Bool !== undefined) return attributes.Bool === 'true';
|
|
1790
|
+
if (attributes.String !== undefined) return attributes.String;
|
|
1791
|
+
if (attributes.Int !== undefined) return Number(attributes.Int);
|
|
1792
|
+
if (attributes.Decimal !== undefined) return Number(attributes.Decimal);
|
|
1793
|
+
if (attributes.Path !== undefined) return { '=': attributes.Path };
|
|
1794
|
+
if (attributes.PropertyPath !== undefined) return { '=': attributes.PropertyPath };
|
|
1795
|
+
if (attributes.NavigationPropertyPath !== undefined) return { '=': attributes.NavigationPropertyPath };
|
|
1796
|
+
if (attributes.AnnotationPath !== undefined) return { '=': attributes.AnnotationPath };
|
|
1797
|
+
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
function _parseV2AnnotationNode(node) {
|
|
1802
|
+
if (node === null || node === undefined) return null;
|
|
1803
|
+
if (Array.isArray(node)) return node.map(item => _parseV2AnnotationNode(item));
|
|
1804
|
+
if (typeof node !== 'object') return node;
|
|
1805
|
+
|
|
1806
|
+
if (node._attributes) {
|
|
1807
|
+
const primitiveValue = _readAnnotationPrimitive(node._attributes);
|
|
1808
|
+
const childKeys = Object.keys(node).filter(key => key !== '_attributes');
|
|
1809
|
+
if (primitiveValue !== null && childKeys.length === 0) {
|
|
1810
|
+
return primitiveValue;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
if (node.Collection) return _parseV2Collection(node.Collection);
|
|
1815
|
+
if (node.Record) return _parseV2Record(node.Record);
|
|
1816
|
+
|
|
1817
|
+
const result = {};
|
|
1818
|
+
Object.keys(node).forEach(key => {
|
|
1819
|
+
if (key === '_attributes') return;
|
|
1820
|
+
if (key === 'PropertyValue') {
|
|
1821
|
+
_toArray(node[key]).forEach(propertyValue => {
|
|
1822
|
+
Object.assign(result, _parseV2PropertyValue(propertyValue));
|
|
1823
|
+
});
|
|
1824
|
+
} else if (key === 'Collection') {
|
|
1825
|
+
return _parseV2Collection(node[key]);
|
|
1826
|
+
} else if (key === 'Record') {
|
|
1827
|
+
Object.assign(result, _parseV2Record(node[key]));
|
|
1828
|
+
} else {
|
|
1829
|
+
const parsedValue = _parseV2AnnotationNode(node[key]);
|
|
1830
|
+
if (parsedValue !== null && parsedValue !== undefined) {
|
|
1831
|
+
result[key] = parsedValue;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1836
|
+
return Object.keys(result).length ? result : _readAnnotationPrimitive(node._attributes);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
function _parseV2PropertyValue(propertyValueNode) {
|
|
1840
|
+
const attributes = propertyValueNode._attributes || {};
|
|
1841
|
+
const propertyName = attributes.Property;
|
|
1842
|
+
const childKeys = Object.keys(propertyValueNode).filter(key => key !== '_attributes');
|
|
1843
|
+
let propertyValue = null;
|
|
1844
|
+
|
|
1845
|
+
if (childKeys.length === 0) {
|
|
1846
|
+
propertyValue = _readAnnotationPrimitive(attributes);
|
|
1847
|
+
} else if (childKeys.length === 1) {
|
|
1848
|
+
const childKey = childKeys[0];
|
|
1849
|
+
if (childKey === 'Collection') {
|
|
1850
|
+
propertyValue = _parseV2Collection(propertyValueNode[childKey]);
|
|
1851
|
+
} else if (childKey === 'Record') {
|
|
1852
|
+
propertyValue = _parseV2Record(propertyValueNode[childKey]);
|
|
1853
|
+
} else {
|
|
1854
|
+
propertyValue = _parseV2AnnotationNode(propertyValueNode[childKey]);
|
|
1855
|
+
}
|
|
1856
|
+
} else {
|
|
1857
|
+
propertyValue = {};
|
|
1858
|
+
childKeys.forEach(childKey => {
|
|
1859
|
+
if (childKey === 'PropertyValue') {
|
|
1860
|
+
_toArray(propertyValueNode[childKey]).forEach(item => {
|
|
1861
|
+
Object.assign(propertyValue, _parseV2PropertyValue(item));
|
|
1862
|
+
});
|
|
1863
|
+
} else {
|
|
1864
|
+
propertyValue[childKey] = _parseV2AnnotationNode(propertyValueNode[childKey]);
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
return { [propertyName]: propertyValue };
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
function _parseV2Record(recordNode) {
|
|
1873
|
+
if (recordNode === null || recordNode === undefined) return null;
|
|
1874
|
+
if (Array.isArray(recordNode)) return recordNode.map(item => _parseV2Record(item));
|
|
1875
|
+
|
|
1876
|
+
const attributes = recordNode._attributes || {};
|
|
1877
|
+
const result = {};
|
|
1878
|
+
|
|
1879
|
+
if (attributes.Type) {
|
|
1880
|
+
result.$Type = attributes.Type;
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
Object.keys(recordNode).forEach(key => {
|
|
1884
|
+
if (key === '_attributes') return;
|
|
1885
|
+
if (key === 'PropertyValue') {
|
|
1886
|
+
_toArray(recordNode[key]).forEach(propertyValueNode => {
|
|
1887
|
+
Object.assign(result, _parseV2PropertyValue(propertyValueNode));
|
|
1888
|
+
});
|
|
1889
|
+
} else {
|
|
1890
|
+
result[key] = _parseV2AnnotationNode(recordNode[key]);
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1894
|
+
return result;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function _parseV2Collection(collectionNode) {
|
|
1898
|
+
if (collectionNode === null || collectionNode === undefined) return null;
|
|
1899
|
+
if (Array.isArray(collectionNode)) return collectionNode.map(item => _parseV2AnnotationNode(item));
|
|
1900
|
+
|
|
1901
|
+
const values = [];
|
|
1902
|
+
Object.keys(collectionNode).forEach(key => {
|
|
1903
|
+
if (key === '_attributes') return;
|
|
1904
|
+
_toArray(collectionNode[key]).forEach(item => {
|
|
1905
|
+
values.push(_parseV2AnnotationNode(item));
|
|
1906
|
+
});
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
return values;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function _parseV2AnnotationTerm(annotationNode) {
|
|
1913
|
+
const attributes = annotationNode._attributes || {};
|
|
1914
|
+
const annotationTerm = '@' + attributes.Term;
|
|
1915
|
+
let annotationTermObject = null;
|
|
1916
|
+
|
|
1917
|
+
Object.keys(annotationNode).forEach(key => {
|
|
1918
|
+
if (key === '_attributes') return;
|
|
1919
|
+
annotationTermObject = _parseV2AnnotationNode(annotationNode[key]);
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
return [annotationTerm, annotationTermObject];
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
function _resolveV2AnnotationTarget(targetPath, csnDefs, parserContext) {
|
|
1926
|
+
const normalizedTargetPath = targetPath.replace(/\.$/, '');
|
|
1927
|
+
const targetSegments = normalizedTargetPath.split('/');
|
|
1928
|
+
const entityContainerPrefix = parserContext.primarySchema + '.EntityContainer';
|
|
1929
|
+
|
|
1930
|
+
if (targetSegments.length > 1 && targetSegments[0] === entityContainerPrefix) {
|
|
1931
|
+
return csnDefs[parserContext.primarySchema + '.' + targetSegments[1]];
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
if (csnDefs[normalizedTargetPath]) {
|
|
1935
|
+
return csnDefs[normalizedTargetPath];
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
const directTarget = parserContext.primarySchema + '.' + targetSegments[targetSegments.length - 1];
|
|
1939
|
+
if (csnDefs[directTarget]) {
|
|
1940
|
+
return csnDefs[directTarget];
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
function _parseV2SchemaAnnotations(schemaAnnotations, csnDefs, parserContext) {
|
|
1945
|
+
const parsedAnnotations = {};
|
|
1946
|
+
|
|
1947
|
+
_toArray(schemaAnnotations).forEach(annotationsNode => {
|
|
1948
|
+
const targetPath = annotationsNode._attributes && annotationsNode._attributes.Target;
|
|
1949
|
+
if (!targetPath) return;
|
|
1950
|
+
|
|
1951
|
+
if (!parsedAnnotations[targetPath]) {
|
|
1952
|
+
parsedAnnotations[targetPath] = {};
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
_toArray(annotationsNode.Annotation).forEach(annotationNode => {
|
|
1956
|
+
const [annotationTerm, annotationTermObject] = _parseV2AnnotationTerm(annotationNode);
|
|
1957
|
+
parsedAnnotations[targetPath][annotationTerm] = annotationTermObject;
|
|
1958
|
+
});
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
annotationParser.parseAnnotations(parsedAnnotations, csnDefs, parserContext, _resolveV2AnnotationTarget);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1751
1964
|
function getEdmxv2CSN(edmx2jsonModel, ignorePersistenceSkip, mockServerUc, context) {
|
|
1752
1965
|
let csnDataModel;
|
|
1753
1966
|
let isValid;
|
|
@@ -1762,11 +1975,15 @@ function getEdmxv2CSN(edmx2jsonModel, ignorePersistenceSkip, mockServerUc, conte
|
|
|
1762
1975
|
else if (context.include_all_namespaces) {
|
|
1763
1976
|
parserContext.allowAllNamespaces = true;
|
|
1764
1977
|
}
|
|
1978
|
+
// V2 annotations are opt-in (disabled by default for backward compatibility)
|
|
1979
|
+
// Controlled via --odata-v2-annotations CLI flag
|
|
1980
|
+
parserContext.annotations = !!context.odataV2Annotations;
|
|
1765
1981
|
|
|
1766
1982
|
isValid = _isValidEDMXProvided(edmx2jsonModel);
|
|
1767
1983
|
_settingMockServerUc(edmx2jsonModel, parserContext)
|
|
1768
1984
|
if (isValid) {
|
|
1769
1985
|
parserContext.serviceNamespace = _getServiceNameSpace(edmx2jsonModel);
|
|
1986
|
+
parserContext.primarySchema = parserContext.serviceNamespace;
|
|
1770
1987
|
context.schemaNamespace = parserContext.serviceNamespace; // required for `--keep-namespace` scenario
|
|
1771
1988
|
let namespaceAlias = edmx2jsonModel["edmx:Edmx"]["edmx:DataServices"].Schema._attributes.Alias;
|
|
1772
1989
|
// replace the namespace alias value with original namespace value
|
|
@@ -11,6 +11,7 @@ const ParserContext = require("./ParserContext");
|
|
|
11
11
|
const ignoreObjects = ["$Reference"]; // add all the objects that needs to be ignored for parsing.
|
|
12
12
|
const { warn, info } = require("../../util/term");
|
|
13
13
|
const MetadataConverterFactory = require("./metadataconverter/MetadataConverterFactory");
|
|
14
|
+
const annotationParser = require("./annotationParser");
|
|
14
15
|
|
|
15
16
|
const edmPrimitiveTypes = [
|
|
16
17
|
'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset',
|
|
@@ -56,33 +57,6 @@ const edmToCdsTypeSpecialHandling = {
|
|
|
56
57
|
'Edm.Stream': 'cds.LargeBinary'
|
|
57
58
|
};
|
|
58
59
|
|
|
59
|
-
const known_vocabularies = {
|
|
60
|
-
'Org.OData.Authorization.V1': 'Authorization',
|
|
61
|
-
'Org.OData.Aggregation.V1': 'Aggregation',
|
|
62
|
-
'Org.OData.Core.V1': 'Core',
|
|
63
|
-
'Org.OData.Capabilities.V1': 'Capabilities',
|
|
64
|
-
'Org.OData.Validation.V1': 'Validation',
|
|
65
|
-
'Org.OData.Measures.V1': 'Measures',
|
|
66
|
-
'Org.OData.JSON.V1': 'JSON',
|
|
67
|
-
'Org.OData.Repeatability.V1': 'Repeatability',
|
|
68
|
-
|
|
69
|
-
'com.sap.vocabularies.Analytics.v1': 'Analytics',
|
|
70
|
-
'com.sap.vocabularies.CodeList.v1': 'CodeList',
|
|
71
|
-
'com.sap.vocabularies.Common.v1': 'Common',
|
|
72
|
-
'com.sap.vocabularies.Communication.v1': 'Communication',
|
|
73
|
-
'com.sap.vocabularies.DataIntegration.v1': 'DataIntegration',
|
|
74
|
-
'com.sap.vocabularies.Graph.v1': 'Graph',
|
|
75
|
-
'com.sap.vocabularies.Hierarchy.v1': 'Hierarchy',
|
|
76
|
-
'com.sap.vocabularies.HTML5.v1': 'HTML5',
|
|
77
|
-
'com.sap.vocabularies.ODM.v1': 'ODM',
|
|
78
|
-
'com.sap.vocabularies.Offline.v1': 'Offline',
|
|
79
|
-
'com.sap.vocabularies.PDF.v1': 'PDF',
|
|
80
|
-
'com.sap.vocabularies.PersonalData.v1': 'PersonalData',
|
|
81
|
-
'com.sap.vocabularies.Session.v1': 'Session',
|
|
82
|
-
'com.sap.vocabularies.UI.v1': 'UI',
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
|
|
86
60
|
function _isJson(edmx) {
|
|
87
61
|
let isJson = false;
|
|
88
62
|
try {
|
|
@@ -235,52 +209,7 @@ function _updateDataTypeObjectForSpecialHandling(dataTypeObj, propertyDataType,
|
|
|
235
209
|
}
|
|
236
210
|
|
|
237
211
|
function _captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetObject) {
|
|
238
|
-
|
|
239
|
-
* For scenarios like:
|
|
240
|
-
* <Annotation Term="Core.Computed" />
|
|
241
|
-
* Here, 'annotationObject' would be null but in CSN we need to treat it as:
|
|
242
|
-
* @Core.Computed : true
|
|
243
|
-
*/
|
|
244
|
-
if (annotationObject === null) {
|
|
245
|
-
annotationObject = true;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* if anntationObject is empty list or empty object, we won't capture it.
|
|
249
|
-
* eg., <Annotation Term="Core.OptimisticConcurrency"> <Collection /> </Annotation>
|
|
250
|
-
* TODO: we might have to revisit it later for nested scenario.
|
|
251
|
-
*/
|
|
252
|
-
if ((annotationObject instanceof Array && annotationObject.length === 0) ||
|
|
253
|
-
(annotationObject instanceof Object && Object.keys(annotationObject).length === 0)
|
|
254
|
-
) {
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* <Annotation Term="Common.Text" Path="name"/> --> annotationTermObject = { "=": annotationTermObject.$Path };
|
|
259
|
-
*/
|
|
260
|
-
if (annotationObject.$Path) {
|
|
261
|
-
annotationObject = { "=": annotationObject.$Path };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Annotations need to be flattened in CSN. E.g.
|
|
266
|
-
*
|
|
267
|
-
* @Core.ContentDisposition : {
|
|
268
|
-
* Type : 'inline',
|
|
269
|
-
* Filename: typedImageFileName
|
|
270
|
-
* }
|
|
271
|
-
*
|
|
272
|
-
* is represented as:
|
|
273
|
-
*
|
|
274
|
-
* @Core.ContentDisposition.Type : 'inline',
|
|
275
|
-
* @Core.ContentDisposition.Filename : {'=': 'typedImageFileName'},
|
|
276
|
-
*/
|
|
277
|
-
if (annotationObject instanceof Object && !(annotationObject instanceof Array) && !(Object.keys(annotationObject).length === 1 && (Object.keys(annotationObject)[0] === '=' || Object.keys(annotationObject)[0] === 'xpr' || Object.keys(annotationObject)[0] === '#'))) {
|
|
278
|
-
Object.keys(annotationObject).forEach(innerTargetString => {
|
|
279
|
-
_captureAnnotationInCSN(targetString + '.' + innerTargetString, annotationObject[innerTargetString], csnDefsTargetObject);
|
|
280
|
-
})
|
|
281
|
-
} else {
|
|
282
|
-
csnDefsTargetObject[targetString] = annotationObject;
|
|
283
|
-
}
|
|
212
|
+
annotationParser.captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetObject);
|
|
284
213
|
}
|
|
285
214
|
|
|
286
215
|
function _replaceSpecialCharacters(text) {
|
|
@@ -316,170 +245,17 @@ function _resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObj
|
|
|
316
245
|
}
|
|
317
246
|
}
|
|
318
247
|
|
|
319
|
-
function _flattenAnnotationObject(annotationPrefix, annotationObject) {
|
|
320
|
-
let flattenedResult = [];
|
|
321
|
-
|
|
322
|
-
Object.entries(annotationObject).forEach(annotation => {
|
|
323
|
-
let currentResult = [];
|
|
324
|
-
/**
|
|
325
|
-
* if annotation looks like:
|
|
326
|
-
* { "#" : <value> } or { "=" : <value> }
|
|
327
|
-
* don't flatten because it is for the `EnumMember` & `Path` use cases respectively
|
|
328
|
-
*/
|
|
329
|
-
if (annotation[0] === '#' || annotation[0] === '=') {
|
|
330
|
-
currentResult[0] = annotationPrefix;
|
|
331
|
-
currentResult[1] = annotationObject;
|
|
332
|
-
} else {
|
|
333
|
-
currentResult[0] = annotationPrefix + '.' + annotation[0];
|
|
334
|
-
currentResult[1] = annotation[1];
|
|
335
|
-
}
|
|
336
|
-
flattenedResult.push(currentResult);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
return flattenedResult;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function _removeTypeInAnnotation(annotationObject) {
|
|
343
|
-
Object.keys(annotationObject).forEach(key => {
|
|
344
|
-
if (key === "$Type") delete annotationObject[key];
|
|
345
|
-
else if (annotationObject[key] instanceof Object) {
|
|
346
|
-
_removeTypeInAnnotation(annotationObject[key]);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function _parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
352
|
-
let targetString;
|
|
353
|
-
/**
|
|
354
|
-
* <Annotation Term="Capabilities.KeyAsSegmentSupported" />
|
|
355
|
-
* For such scenario, mark the `anntationTermObject` as `true`
|
|
356
|
-
*/
|
|
357
|
-
if (annotationTermObject === null) annotationTermObject = true;
|
|
358
|
-
|
|
359
|
-
_removeTypeInAnnotation(annotationTermObject);
|
|
360
|
-
/**
|
|
361
|
-
* For entries like:
|
|
362
|
-
* <Annotation Term="Capabilities.FilterFunctions"> <Collection> <String>eq</String> <String>ne</String> </Collection> </Annotation>
|
|
363
|
-
* <Annotation Term="Capabilities.DeleteRestrictions.Deletable" Bool="true"/>
|
|
364
|
-
*
|
|
365
|
-
* simply preserve the value as it is
|
|
366
|
-
*/
|
|
367
|
-
if (annotationTermObject instanceof Array || !(annotationTermObject instanceof Object)) {
|
|
368
|
-
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
Object.keys(annotationTermObject).forEach(annoKey => {
|
|
372
|
-
let currentAnnoValue = annotationTermObject[annoKey];
|
|
373
|
-
// if the currentAnnoValue is an Object, we need to flatten it
|
|
374
|
-
if (currentAnnoValue instanceof Object && !(currentAnnoValue instanceof Array)) {
|
|
375
|
-
targetString = annotationTerm + '.' + annoKey;
|
|
376
|
-
let flattenedResultList = _flattenAnnotationObject(targetString, currentAnnoValue);
|
|
377
|
-
|
|
378
|
-
for (let idx = 0; idx < flattenedResultList.length; idx++) {
|
|
379
|
-
_captureAnnotationInCSN(flattenedResultList[idx][0], flattenedResultList[idx][1], csnDefsTargetObject);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// if the currentAnnoValue is a list or direct value, we preserve as it is
|
|
383
|
-
else {
|
|
384
|
-
targetString = annotationTerm + '.' + annoKey;
|
|
385
|
-
_captureAnnotationInCSN(targetString, currentAnnoValue, csnDefsTargetObject);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function _parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
392
|
-
const resolveCastFor = ["Minimum", "Maximum", "MaxItems"];
|
|
393
|
-
const term = annotationTerm.split('.')[1];
|
|
394
|
-
|
|
395
|
-
if (resolveCastFor.includes(term) && annotationTermObject.$Cast) {
|
|
396
|
-
annotationTermObject = Number(annotationTermObject.$Cast);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
248
|
function _parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
_resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
409
|
-
break;
|
|
410
|
-
case "@Capabilities":
|
|
411
|
-
_parseCapabilitiesTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
412
|
-
break;
|
|
413
|
-
case "@Validation":
|
|
414
|
-
_parseValidationTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
415
|
-
break;
|
|
416
|
-
default:
|
|
417
|
-
_captureAnnotationInCSN(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function _validateAnnotationNamespace(annotationTerm, vocabNamespaceToAlias) {
|
|
423
|
-
let aliasOrNamespace = annotationTerm.substring(1, annotationTerm.lastIndexOf('.'));
|
|
424
|
-
|
|
425
|
-
// This check is for ignoring the @cds annotations given for special cases, e.g. @cds.validate = false for collided action/function
|
|
426
|
-
if (aliasOrNamespace === "cds") {
|
|
427
|
-
return
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
for (let alias of Object.values(known_vocabularies)) {
|
|
431
|
-
if (alias === aliasOrNamespace) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
for (let [namespace, alias] of Object.entries(vocabNamespaceToAlias)) {
|
|
437
|
-
if (alias === aliasOrNamespace || namespace === aliasOrNamespace) {
|
|
438
|
-
console.log(warn(annotationTerm + " AnnotationTerm belongs to an unknown namespace: " + namespace));
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
249
|
+
// @Core needs special v4 handling for @Core.OptionalParameter (uses _updateElementObjectForDefaultValue)
|
|
250
|
+
if (annotationTerm.startsWith('@Core.')) {
|
|
251
|
+
_resolveCoreTerm(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
252
|
+
} else {
|
|
253
|
+
annotationParser.parseAnnotationTerms(annotationTerm, annotationTermObject, csnDefsTargetObject);
|
|
441
254
|
}
|
|
442
|
-
|
|
443
|
-
console.log(warn(annotationTerm + ": Reference of this AnnotationTerm is not present in the edmx file."));
|
|
444
255
|
}
|
|
445
256
|
|
|
446
257
|
function _resolveAnnotationTerm(schemaDataAnnotation, parserContext) {
|
|
447
|
-
|
|
448
|
-
for (const [namespace, alias] of Object.entries(parserContext.vocabNamespaceToAlias)) {
|
|
449
|
-
if (known_vocabularies[namespace]) {
|
|
450
|
-
// Case 1: Namespace belongs to known vocabularies. Replacing the namespace/alias of the doc with standard alias
|
|
451
|
-
if (schemaDataAnnotation.includes('@' + alias + '.')) {
|
|
452
|
-
schemaDataAnnotation = schemaDataAnnotation.split('@' + alias + '.').join('@' + known_vocabularies[namespace] + '.');
|
|
453
|
-
}
|
|
454
|
-
if (schemaDataAnnotation.includes('@' + namespace + '.')) {
|
|
455
|
-
schemaDataAnnotation = schemaDataAnnotation.split('@' + namespace + '.').join('@' + known_vocabularies[namespace] + '.');
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
// Case 2: Namespace doesn't belong to known vocabularies. Replacing the namespace with alias(if provided) in the doc.
|
|
459
|
-
if (schemaDataAnnotation.includes('@' + namespace + '.') && alias) {
|
|
460
|
-
schemaDataAnnotation = schemaDataAnnotation.split('@' + namespace + '.').join('@' + alias + '.');
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Case 3: Namespace doesn't have edmx:Reference. But it is present in known_vocabularies.
|
|
466
|
-
* Replacing it with standard alias.
|
|
467
|
-
*/
|
|
468
|
-
for (const [namespace, alias] of Object.entries(known_vocabularies)) {
|
|
469
|
-
if (schemaDataAnnotation.includes(namespace)) {
|
|
470
|
-
schemaDataAnnotation = schemaDataAnnotation.split(namespace).join(alias);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
let index = schemaDataAnnotation.indexOf('"@');
|
|
475
|
-
while (index != -1) {
|
|
476
|
-
let i = schemaDataAnnotation.indexOf('"', index + 1);
|
|
477
|
-
let annotationTerm = schemaDataAnnotation.substring(index + 1, i);
|
|
478
|
-
_validateAnnotationNamespace(annotationTerm, parserContext.vocabNamespaceToAlias);
|
|
479
|
-
index = schemaDataAnnotation.indexOf('"@', i + 1);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
return JSON.parse(schemaDataAnnotation);
|
|
258
|
+
return annotationParser.resolveAnnotationTerm(schemaDataAnnotation, parserContext);
|
|
483
259
|
}
|
|
484
260
|
|
|
485
261
|
/**
|
package/lib/watch/watcher.js
CHANGED
|
@@ -46,6 +46,32 @@ module.exports = function watch(paths, { filter, ignore, hotReload } = {}) {
|
|
|
46
46
|
|
|
47
47
|
const useNativeRecursive = process.platform === 'darwin' || process.platform === 'win32'
|
|
48
48
|
if (useNativeRecursive) {
|
|
49
|
+
// On Windows, ReadDirectoryChangesW fires FILE_NOTIFY_CHANGE events for mere
|
|
50
|
+
// file opens (antivirus, search indexer, TypeScript loaders) without a write
|
|
51
|
+
// having occurred, causing spurious server restarts. Guard by confirming
|
|
52
|
+
// mtime actually changed before emitting 'change'. The cache is seeded on
|
|
53
|
+
// the first event for each path so buffered pre-startup events are suppressed.
|
|
54
|
+
// Mac uses kqueue/FSEvents which only fires on real writes — no guard needed.
|
|
55
|
+
const _mtimes = process.platform === 'win32' ? new Map() : null
|
|
56
|
+
const _watcherStart = _mtimes && Date.now()
|
|
57
|
+
const _changedOnDisk = _mtimes
|
|
58
|
+
? async fullPath => {
|
|
59
|
+
try {
|
|
60
|
+
const { mtimeMs } = await fs.promises.stat(fullPath)
|
|
61
|
+
const prev = _mtimes.get(fullPath)
|
|
62
|
+
_mtimes.set(fullPath, mtimeMs)
|
|
63
|
+
if (prev === undefined) return false // first event: seed cache, suppress
|
|
64
|
+
if (prev === mtimeMs) return false // mtime unchanged: read-only open
|
|
65
|
+
if (mtimeMs < _watcherStart) return false // older than watcher: buffered OS event
|
|
66
|
+
return true
|
|
67
|
+
} catch (err) {
|
|
68
|
+
_mtimes.delete(fullPath)
|
|
69
|
+
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') throw err
|
|
70
|
+
return true // stat failed (deleted file): let it through
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
: () => true // non-Windows: always emit
|
|
74
|
+
|
|
49
75
|
const _watch = async root => {
|
|
50
76
|
try {
|
|
51
77
|
if (!fs.existsSync(root)) return
|
|
@@ -56,7 +82,7 @@ module.exports = function watch(paths, { filter, ignore, hotReload } = {}) {
|
|
|
56
82
|
const fullPath = resolve(root, name)
|
|
57
83
|
if (_hotReload(fullPath)) {
|
|
58
84
|
ee.emit('refreshModel', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
59
|
-
} else if (_allow(fullPath) && _narrowJSONreloads(fullPath)) {
|
|
85
|
+
} else if (_allow(fullPath) && _narrowJSONreloads(fullPath) && await _changedOnDisk(fullPath)) {
|
|
60
86
|
ee.emit('change', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
61
87
|
}
|
|
62
88
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "9.9.
|
|
3
|
+
"version": "9.9.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sap/cds-dk",
|
|
9
|
-
"version": "9.9.
|
|
9
|
+
"version": "9.9.3",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@cap-js/asyncapi": "^1.0.0",
|
|
13
13
|
"@cap-js/openapi": "^1.0.0",
|
|
14
|
-
"@sap/cds": "^
|
|
15
|
-
"@sap/cds-mtxs": "^
|
|
14
|
+
"@sap/cds": "^9",
|
|
15
|
+
"@sap/cds-mtxs": "^3",
|
|
16
16
|
"@sap/hdi-deploy": "^5",
|
|
17
17
|
"express": "^4.22.1 || ^5",
|
|
18
18
|
"hdb": "^2.0.0",
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
"cds-tsx": "bin/cds-tsx.js"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"@cap-js/sqlite": "
|
|
31
|
+
"@cap-js/sqlite": "^1 || ^2"
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"node_modules/@cap-js/asyncapi": {
|
|
35
|
-
"version": "1.0
|
|
36
|
-
"integrity": "sha512-
|
|
37
|
-
"license": "
|
|
35
|
+
"version": "1.1.0",
|
|
36
|
+
"integrity": "sha512-ywO5NfGwoeEx6/jneOMpNMs4PFmuueet8HOEg13VAiXpu4W5juAZQ/Jv2xE0QcW9m1qiCmI1ymypmz7UI+YGbw==",
|
|
37
|
+
"license": "Apache-2.0",
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@sap/cds": ">=7.6"
|
|
40
40
|
}
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"node_modules/@cap-js/openapi": {
|
|
55
|
-
"version": "1.
|
|
56
|
-
"integrity": "sha512-
|
|
55
|
+
"version": "1.5.0",
|
|
56
|
+
"integrity": "sha512-JDzENNC80vnP9W31Skz6PfgPnpaxNTgIP8CtDK3bJ0FCbr5sXshRqes1pc17kNMXDbJAtEshS0dN7GYBW2THWA==",
|
|
57
57
|
"license": "Apache-2.0",
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"pluralize": "^8.0.0"
|
|
@@ -129,8 +129,8 @@
|
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
"node_modules/@sap/cds-compiler": {
|
|
132
|
-
"version": "6.9.
|
|
133
|
-
"integrity": "sha512-
|
|
132
|
+
"version": "6.9.3",
|
|
133
|
+
"integrity": "sha512-8fEeM6D059ruqLWoJqzFSNLkGoUCMnycx7Zv4OP4nA9zZeHzFC3nAe6s3ak2kASSiub1xAMMPzDE/9EnSXQKLw==",
|
|
134
134
|
"license": "SEE LICENSE IN LICENSE",
|
|
135
135
|
"bin": {
|
|
136
136
|
"cdsc": "bin/cdsc.js",
|
|
@@ -150,8 +150,8 @@
|
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
152
|
"node_modules/@sap/cds-mtxs": {
|
|
153
|
-
"version": "3.9.
|
|
154
|
-
"integrity": "sha512-
|
|
153
|
+
"version": "3.9.4",
|
|
154
|
+
"integrity": "sha512-A3FRCGCMNhml8MLpGv+OUHwDhAUgvAiE38qdTL87/KQ932CTNZ1frohQtT24iszoamB7NI8lp9RR3BFD6HR/FQ==",
|
|
155
155
|
"license": "SEE LICENSE IN LICENSE",
|
|
156
156
|
"dependencies": {
|
|
157
157
|
"@sap/hdi-deploy": "^5"
|
|
@@ -188,15 +188,15 @@
|
|
|
188
188
|
}
|
|
189
189
|
},
|
|
190
190
|
"node_modules/@sap/hdi-deploy": {
|
|
191
|
-
"version": "5.
|
|
192
|
-
"integrity": "sha512
|
|
193
|
-
"license": "
|
|
191
|
+
"version": "5.7.0",
|
|
192
|
+
"integrity": "sha512-ENBCtCBnJEKBlvC5mQT/G7OAteOENo7ygSeRJgVdz7AHikcHUxegtavXkJtFCJ/PSjpjytFKfGRmG8peUVAmNQ==",
|
|
193
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
194
194
|
"dependencies": {
|
|
195
|
-
"@sap/hdi": "^4.8.
|
|
195
|
+
"@sap/hdi": "^4.8.1",
|
|
196
196
|
"@sap/xsenv": "^6.0.0",
|
|
197
197
|
"async": "^3.2.6",
|
|
198
198
|
"dotenv": "^16.4.5",
|
|
199
|
-
"handlebars": "^4.7.
|
|
199
|
+
"handlebars": "^4.7.9",
|
|
200
200
|
"micromatch": "^4.0.8"
|
|
201
201
|
},
|
|
202
202
|
"engines": {
|
|
@@ -274,8 +274,8 @@
|
|
|
274
274
|
"optional": true
|
|
275
275
|
},
|
|
276
276
|
"node_modules/better-sqlite3": {
|
|
277
|
-
"version": "12.
|
|
278
|
-
"integrity": "sha512-
|
|
277
|
+
"version": "12.11.1",
|
|
278
|
+
"integrity": "sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==",
|
|
279
279
|
"hasInstallScript": true,
|
|
280
280
|
"license": "MIT",
|
|
281
281
|
"optional": true,
|
|
@@ -308,20 +308,32 @@
|
|
|
308
308
|
}
|
|
309
309
|
},
|
|
310
310
|
"node_modules/body-parser": {
|
|
311
|
-
"version": "2.
|
|
312
|
-
"integrity": "sha512-
|
|
311
|
+
"version": "2.3.0",
|
|
312
|
+
"integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
|
|
313
313
|
"license": "MIT",
|
|
314
314
|
"dependencies": {
|
|
315
315
|
"bytes": "^3.1.2",
|
|
316
|
-
"content-type": "^
|
|
316
|
+
"content-type": "^2.0.0",
|
|
317
317
|
"debug": "^4.4.3",
|
|
318
|
-
"http-errors": "^2.0.
|
|
319
|
-
"iconv-lite": "^0.7.
|
|
318
|
+
"http-errors": "^2.0.1",
|
|
319
|
+
"iconv-lite": "^0.7.2",
|
|
320
320
|
"on-finished": "^2.4.1",
|
|
321
|
-
"qs": "^6.
|
|
322
|
-
"raw-body": "^3.0.
|
|
323
|
-
"type-is": "^2.0
|
|
321
|
+
"qs": "^6.15.2",
|
|
322
|
+
"raw-body": "^3.0.2",
|
|
323
|
+
"type-is": "^2.1.0"
|
|
324
|
+
},
|
|
325
|
+
"engines": {
|
|
326
|
+
"node": ">=18"
|
|
324
327
|
},
|
|
328
|
+
"funding": {
|
|
329
|
+
"type": "opencollective",
|
|
330
|
+
"url": "https://opencollective.com/express"
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
"node_modules/body-parser/node_modules/content-type": {
|
|
334
|
+
"version": "2.0.0",
|
|
335
|
+
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
|
336
|
+
"license": "MIT",
|
|
325
337
|
"engines": {
|
|
326
338
|
"node": ">=18"
|
|
327
339
|
},
|
|
@@ -817,8 +829,8 @@
|
|
|
817
829
|
}
|
|
818
830
|
},
|
|
819
831
|
"node_modules/hasown": {
|
|
820
|
-
"version": "2.0.
|
|
821
|
-
"integrity": "sha512-
|
|
832
|
+
"version": "2.0.4",
|
|
833
|
+
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
|
822
834
|
"license": "MIT",
|
|
823
835
|
"dependencies": {
|
|
824
836
|
"function-bind": "^1.1.2"
|
|
@@ -828,8 +840,8 @@
|
|
|
828
840
|
}
|
|
829
841
|
},
|
|
830
842
|
"node_modules/hdb": {
|
|
831
|
-
"version": "2.
|
|
832
|
-
"integrity": "sha512-
|
|
843
|
+
"version": "2.29.4",
|
|
844
|
+
"integrity": "sha512-9L7/xjx4NU2YWatqLSHXhuEIuvNlV/4eRFg7u/L63U1Dc5LocP4yWeH8UGKTLBCJkt548OQ+A7PqqimZwEuqdw==",
|
|
833
845
|
"license": "Apache-2.0",
|
|
834
846
|
"dependencies": {
|
|
835
847
|
"iconv-lite": "0.7.0"
|
|
@@ -1212,11 +1224,12 @@
|
|
|
1212
1224
|
}
|
|
1213
1225
|
},
|
|
1214
1226
|
"node_modules/qs": {
|
|
1215
|
-
"version": "6.15.
|
|
1216
|
-
"integrity": "sha512-
|
|
1227
|
+
"version": "6.15.3",
|
|
1228
|
+
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
|
|
1217
1229
|
"license": "BSD-3-Clause",
|
|
1218
1230
|
"dependencies": {
|
|
1219
|
-
"
|
|
1231
|
+
"es-define-property": "^1.0.1",
|
|
1232
|
+
"side-channel": "^1.1.1"
|
|
1220
1233
|
},
|
|
1221
1234
|
"engines": {
|
|
1222
1235
|
"node": ">=0.6"
|
|
@@ -1325,8 +1338,8 @@
|
|
|
1325
1338
|
}
|
|
1326
1339
|
},
|
|
1327
1340
|
"node_modules/semver": {
|
|
1328
|
-
"version": "7.8.
|
|
1329
|
-
"integrity": "sha512-
|
|
1341
|
+
"version": "7.8.5",
|
|
1342
|
+
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
|
1330
1343
|
"devOptional": true,
|
|
1331
1344
|
"license": "ISC",
|
|
1332
1345
|
"bin": {
|
|
@@ -1385,13 +1398,13 @@
|
|
|
1385
1398
|
"license": "ISC"
|
|
1386
1399
|
},
|
|
1387
1400
|
"node_modules/side-channel": {
|
|
1388
|
-
"version": "1.1.
|
|
1389
|
-
"integrity": "sha512-
|
|
1401
|
+
"version": "1.1.1",
|
|
1402
|
+
"integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
|
|
1390
1403
|
"license": "MIT",
|
|
1391
1404
|
"dependencies": {
|
|
1392
1405
|
"es-errors": "^1.3.0",
|
|
1393
|
-
"object-inspect": "^1.13.
|
|
1394
|
-
"side-channel-list": "^1.0.
|
|
1406
|
+
"object-inspect": "^1.13.4",
|
|
1407
|
+
"side-channel-list": "^1.0.1",
|
|
1395
1408
|
"side-channel-map": "^1.0.1",
|
|
1396
1409
|
"side-channel-weakmap": "^1.0.2"
|
|
1397
1410
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "9.9.
|
|
3
|
+
"version": "9.9.3",
|
|
4
4
|
"description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"keywords": [
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@cap-js/asyncapi": "^1.0.0",
|
|
21
21
|
"@cap-js/openapi": "^1.0.0",
|
|
22
|
-
"@sap/cds": "^
|
|
23
|
-
"@sap/cds-mtxs": "^
|
|
22
|
+
"@sap/cds": "^9",
|
|
23
|
+
"@sap/cds-mtxs": "^3",
|
|
24
24
|
"@sap/hdi-deploy": "^5",
|
|
25
25
|
"express": "^4.22.1 || ^5",
|
|
26
26
|
"hdb": "^2.0.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"yaml": "^2"
|
|
32
32
|
},
|
|
33
33
|
"optionalDependencies": {
|
|
34
|
-
"@cap-js/sqlite": "
|
|
34
|
+
"@cap-js/sqlite": "^1 || ^2"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"bin/",
|