@sap/cds-dk 9.9.2 → 9.9.4

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 CHANGED
@@ -6,6 +6,20 @@ 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.4 - 2026-07-31
10
+
11
+ ### Changed
12
+
13
+ - `cds init` uses Maven Java archetype version 4.9.1 for creating Java projects.
14
+ - Other dependency updates
15
+
16
+ ## Version 9.9.3 - 2026-06-29
17
+
18
+ ### Fixed
19
+
20
+ - `cds import` for OData v2 now can import annotations.
21
+ - `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.
22
+
9
23
  ## Version 9.9.2 - 2026-05-28
10
24
 
11
25
  ### 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
@@ -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
 
@@ -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, "&quot;").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
- const term = annotationTerm.split('.');
404
- const vocabAlias = term[0];
405
-
406
- switch (vocabAlias) {
407
- case "@Core":
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
  /**
@@ -5,7 +5,7 @@ module.exports = {
5
5
  COMMAND_ADD: 'add',
6
6
 
7
7
  JAVA_LTS_VERSIONS: [17, 21],
8
- MAVEN_ARCHETYPE_VERSION: '4.8.1',
8
+ MAVEN_ARCHETYPE_VERSION: '4.9.1',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
@@ -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
  }
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.9.2",
3
+ "version": "9.9.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "9.9.2",
9
+ "version": "9.9.4",
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": "^8.3 || ^9",
15
- "@sap/cds-mtxs": "^2 || ^3",
14
+ "@sap/cds": "^9",
15
+ "@sap/cds-compiler": "^6",
16
+ "@sap/cds-mtxs": "^3",
16
17
  "@sap/hdi-deploy": "^5",
17
18
  "express": "^4.22.1 || ^5",
18
19
  "hdb": "^2.0.0",
@@ -28,13 +29,13 @@
28
29
  "cds-tsx": "bin/cds-tsx.js"
29
30
  },
30
31
  "optionalDependencies": {
31
- "@cap-js/sqlite": ">=1"
32
+ "@cap-js/sqlite": "^1 || ^2"
32
33
  }
33
34
  },
34
35
  "node_modules/@cap-js/asyncapi": {
35
- "version": "1.0.3",
36
- "integrity": "sha512-vZSWKAe+3qfvZDXV5SSFiObGWmqyS9MDyEADb5PLVT8kzO39qGaSDPv/GzI/gwvRfCayGAjU4ThiBKrFA7Gclg==",
37
- "license": "SEE LICENSE IN LICENSE",
36
+ "version": "1.1.0",
37
+ "integrity": "sha512-ywO5NfGwoeEx6/jneOMpNMs4PFmuueet8HOEg13VAiXpu4W5juAZQ/Jv2xE0QcW9m1qiCmI1ymypmz7UI+YGbw==",
38
+ "license": "Apache-2.0",
38
39
  "peerDependencies": {
39
40
  "@sap/cds": ">=7.6"
40
41
  }
@@ -52,8 +53,8 @@
52
53
  }
53
54
  },
54
55
  "node_modules/@cap-js/openapi": {
55
- "version": "1.4.2",
56
- "integrity": "sha512-Mi02XsSlLy+h9tBLPX1/re2Obsmb1rnIj18AUb1jD5dLvbfedfDojtsuZIQHQImdYNeD6nMgaBQw0g1JRHTdow==",
56
+ "version": "1.5.0",
57
+ "integrity": "sha512-JDzENNC80vnP9W31Skz6PfgPnpaxNTgIP8CtDK3bJ0FCbr5sXshRqes1pc17kNMXDbJAtEshS0dN7GYBW2THWA==",
57
58
  "license": "Apache-2.0",
58
59
  "dependencies": {
59
60
  "pluralize": "^8.0.0"
@@ -102,8 +103,8 @@
102
103
  }
103
104
  },
104
105
  "node_modules/@sap/cds": {
105
- "version": "9.9.1",
106
- "integrity": "sha512-GqdsBsRkZThhpOyzj8ihf/jDmf/2zprZFgaun6ZymUw4/ahzjK/bbdd6eQ8txDuv88pnUl2HPFjvUVq3O/6hCA==",
106
+ "version": "9.9.3",
107
+ "integrity": "sha512-uSbggU5fycRhipYDUsfYwgeahx3E+B3ILq2bomm6rW22gzi/dO6OJNPyVnWDI6XDU/Q5Sd1v/9DT5fRI79p5tQ==",
107
108
  "license": "SEE LICENSE IN LICENSE",
108
109
  "dependencies": {
109
110
  "@sap/cds-compiler": "^6.4",
@@ -129,8 +130,8 @@
129
130
  }
130
131
  },
131
132
  "node_modules/@sap/cds-compiler": {
132
- "version": "6.9.2",
133
- "integrity": "sha512-Qv7Zb3RhG92WVm1AjHEJaYbOi3tNT051/EWPYTsYdUe5epYXbR4dJfGpD1eEgo82ThrKCFx0BZfT0b28t0/vqg==",
133
+ "version": "6.9.4",
134
+ "integrity": "sha512-gz7PeE3vjl0OdnK+XWH/aslQ6WNsqA1EDwqKOXho0tTDwqXSgpe3BT85PKy49ozytRkUGh/YU1/3ztO+dWeUvQ==",
134
135
  "license": "SEE LICENSE IN LICENSE",
135
136
  "bin": {
136
137
  "cdsc": "bin/cdsc.js",
@@ -150,8 +151,8 @@
150
151
  }
151
152
  },
152
153
  "node_modules/@sap/cds-mtxs": {
153
- "version": "3.9.3",
154
- "integrity": "sha512-NdL+ctu1YmxRMFFk8ip3PZHXxIrDv93gK+ZUE+e5B+9MNDvalQcav4QllqwiGJ/CJcfJROWGTt+a554A81dxow==",
154
+ "version": "3.9.5",
155
+ "integrity": "sha512-R7SLGJ5eCIGiR5uX6QWBG+SVDvCj3OpjxhpcHF1NwhJ0mqMHC3FCrHrutIpX4slc0ZTYyoJnEyL7gaPhrjK50g==",
155
156
  "license": "SEE LICENSE IN LICENSE",
156
157
  "dependencies": {
157
158
  "@sap/hdi-deploy": "^5"
@@ -188,15 +189,15 @@
188
189
  }
189
190
  },
190
191
  "node_modules/@sap/hdi-deploy": {
191
- "version": "5.6.1",
192
- "integrity": "sha512-+qQ7qwG8lko303L5yRj2dud/nDAVuVblV/mmzJT44oPbF0Nry18eD2LUS23hFeuxjRa7rYK5YKQ8ffGgWxVrYQ==",
193
- "license": "See LICENSE file",
192
+ "version": "5.7.0",
193
+ "integrity": "sha512-ENBCtCBnJEKBlvC5mQT/G7OAteOENo7ygSeRJgVdz7AHikcHUxegtavXkJtFCJ/PSjpjytFKfGRmG8peUVAmNQ==",
194
+ "license": "SEE LICENSE IN LICENSE",
194
195
  "dependencies": {
195
- "@sap/hdi": "^4.8.0",
196
+ "@sap/hdi": "^4.8.1",
196
197
  "@sap/xsenv": "^6.0.0",
197
198
  "async": "^3.2.6",
198
199
  "dotenv": "^16.4.5",
199
- "handlebars": "^4.7.8",
200
+ "handlebars": "^4.7.9",
200
201
  "micromatch": "^4.0.8"
201
202
  },
202
203
  "engines": {
@@ -274,8 +275,8 @@
274
275
  "optional": true
275
276
  },
276
277
  "node_modules/better-sqlite3": {
277
- "version": "12.10.0",
278
- "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==",
278
+ "version": "12.11.1",
279
+ "integrity": "sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==",
279
280
  "hasInstallScript": true,
280
281
  "license": "MIT",
281
282
  "optional": true,
@@ -308,19 +309,19 @@
308
309
  }
309
310
  },
310
311
  "node_modules/body-parser": {
311
- "version": "2.2.2",
312
- "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
312
+ "version": "2.3.0",
313
+ "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
313
314
  "license": "MIT",
314
315
  "dependencies": {
315
316
  "bytes": "^3.1.2",
316
- "content-type": "^1.0.5",
317
+ "content-type": "^2.0.0",
317
318
  "debug": "^4.4.3",
318
- "http-errors": "^2.0.0",
319
- "iconv-lite": "^0.7.0",
319
+ "http-errors": "^2.0.1",
320
+ "iconv-lite": "^0.7.2",
320
321
  "on-finished": "^2.4.1",
321
- "qs": "^6.14.1",
322
- "raw-body": "^3.0.1",
323
- "type-is": "^2.0.1"
322
+ "qs": "^6.15.2",
323
+ "raw-body": "^3.0.2",
324
+ "type-is": "^2.1.0"
324
325
  },
325
326
  "engines": {
326
327
  "node": ">=18"
@@ -330,6 +331,18 @@
330
331
  "url": "https://opencollective.com/express"
331
332
  }
332
333
  },
334
+ "node_modules/body-parser/node_modules/content-type": {
335
+ "version": "2.0.0",
336
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
337
+ "license": "MIT",
338
+ "engines": {
339
+ "node": ">=18"
340
+ },
341
+ "funding": {
342
+ "type": "opencollective",
343
+ "url": "https://opencollective.com/express"
344
+ }
345
+ },
333
346
  "node_modules/braces": {
334
347
  "version": "3.0.3",
335
348
  "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
@@ -817,8 +830,8 @@
817
830
  }
818
831
  },
819
832
  "node_modules/hasown": {
820
- "version": "2.0.3",
821
- "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
833
+ "version": "2.0.4",
834
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
822
835
  "license": "MIT",
823
836
  "dependencies": {
824
837
  "function-bind": "^1.1.2"
@@ -828,8 +841,8 @@
828
841
  }
829
842
  },
830
843
  "node_modules/hdb": {
831
- "version": "2.27.1",
832
- "integrity": "sha512-xYL/W+fq2TyGHyzm8muolQnw8tdh4+2NQ8mQP2FpLSuhfJ8l0jQNSUZoAXic7NfMEan1Jvf8V1L4blwkgTc6+A==",
844
+ "version": "2.29.5",
845
+ "integrity": "sha512-3DFwpAdURCKz7mI4Ahsu12dVUCev4Epd+OfgR6L13K36c0yyU3Tf7i9Q1bZt9wN9sBczpmUf0hHnshqygtBIsw==",
833
846
  "license": "Apache-2.0",
834
847
  "dependencies": {
835
848
  "iconv-lite": "0.7.0"
@@ -876,8 +889,8 @@
876
889
  }
877
890
  },
878
891
  "node_modules/iconv-lite": {
879
- "version": "0.7.2",
880
- "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
892
+ "version": "0.7.3",
893
+ "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
881
894
  "license": "MIT",
882
895
  "dependencies": {
883
896
  "safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -962,11 +975,15 @@
962
975
  }
963
976
  },
964
977
  "node_modules/media-typer": {
965
- "version": "1.1.0",
966
- "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
978
+ "version": "1.1.1",
979
+ "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==",
967
980
  "license": "MIT",
968
981
  "engines": {
969
982
  "node": ">= 0.8"
983
+ },
984
+ "funding": {
985
+ "type": "opencollective",
986
+ "url": "https://opencollective.com/express"
970
987
  }
971
988
  },
972
989
  "node_modules/merge-descriptors": {
@@ -1085,8 +1102,8 @@
1085
1102
  "license": "MIT"
1086
1103
  },
1087
1104
  "node_modules/node-abi": {
1088
- "version": "3.92.0",
1089
- "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==",
1105
+ "version": "3.94.0",
1106
+ "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==",
1090
1107
  "license": "MIT",
1091
1108
  "optional": true,
1092
1109
  "dependencies": {
@@ -1212,11 +1229,12 @@
1212
1229
  }
1213
1230
  },
1214
1231
  "node_modules/qs": {
1215
- "version": "6.15.2",
1216
- "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
1232
+ "version": "6.15.3",
1233
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
1217
1234
  "license": "BSD-3-Clause",
1218
1235
  "dependencies": {
1219
- "side-channel": "^1.1.0"
1236
+ "es-define-property": "^1.0.1",
1237
+ "side-channel": "^1.1.1"
1220
1238
  },
1221
1239
  "engines": {
1222
1240
  "node": ">=0.6"
@@ -1226,11 +1244,15 @@
1226
1244
  }
1227
1245
  },
1228
1246
  "node_modules/range-parser": {
1229
- "version": "1.2.1",
1230
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1247
+ "version": "1.3.0",
1248
+ "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
1231
1249
  "license": "MIT",
1232
1250
  "engines": {
1233
1251
  "node": ">= 0.6"
1252
+ },
1253
+ "funding": {
1254
+ "type": "opencollective",
1255
+ "url": "https://opencollective.com/express"
1234
1256
  }
1235
1257
  },
1236
1258
  "node_modules/raw-body": {
@@ -1317,16 +1339,16 @@
1317
1339
  "license": "MIT"
1318
1340
  },
1319
1341
  "node_modules/sax": {
1320
- "version": "1.6.0",
1321
- "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
1342
+ "version": "1.6.1",
1343
+ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
1322
1344
  "license": "BlueOak-1.0.0",
1323
1345
  "engines": {
1324
1346
  "node": ">=11.0.0"
1325
1347
  }
1326
1348
  },
1327
1349
  "node_modules/semver": {
1328
- "version": "7.8.1",
1329
- "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
1350
+ "version": "7.8.5",
1351
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
1330
1352
  "devOptional": true,
1331
1353
  "license": "ISC",
1332
1354
  "bin": {
@@ -1385,13 +1407,13 @@
1385
1407
  "license": "ISC"
1386
1408
  },
1387
1409
  "node_modules/side-channel": {
1388
- "version": "1.1.0",
1389
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1410
+ "version": "1.1.1",
1411
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
1390
1412
  "license": "MIT",
1391
1413
  "dependencies": {
1392
1414
  "es-errors": "^1.3.0",
1393
- "object-inspect": "^1.13.3",
1394
- "side-channel-list": "^1.0.0",
1415
+ "object-inspect": "^1.13.4",
1416
+ "side-channel-list": "^1.0.1",
1395
1417
  "side-channel-map": "^1.0.1",
1396
1418
  "side-channel-weakmap": "^1.0.2"
1397
1419
  },
@@ -1532,8 +1554,8 @@
1532
1554
  }
1533
1555
  },
1534
1556
  "node_modules/tar-fs": {
1535
- "version": "2.1.4",
1536
- "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
1557
+ "version": "2.1.5",
1558
+ "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
1537
1559
  "license": "MIT",
1538
1560
  "optional": true,
1539
1561
  "dependencies": {
@@ -1677,8 +1699,8 @@
1677
1699
  "license": "ISC"
1678
1700
  },
1679
1701
  "node_modules/ws": {
1680
- "version": "8.21.0",
1681
- "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
1702
+ "version": "8.21.1",
1703
+ "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
1682
1704
  "license": "MIT",
1683
1705
  "engines": {
1684
1706
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.9.2",
3
+ "version": "9.9.4",
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,9 @@
19
19
  "dependencies": {
20
20
  "@cap-js/asyncapi": "^1.0.0",
21
21
  "@cap-js/openapi": "^1.0.0",
22
- "@sap/cds": "^8.3 || ^9",
23
- "@sap/cds-mtxs": "^2 || ^3",
22
+ "@sap/cds": "^9",
23
+ "@sap/cds-compiler": "^6",
24
+ "@sap/cds-mtxs": "^3",
24
25
  "@sap/hdi-deploy": "^5",
25
26
  "express": "^4.22.1 || ^5",
26
27
  "hdb": "^2.0.0",
@@ -31,7 +32,7 @@
31
32
  "yaml": "^2"
32
33
  },
33
34
  "optionalDependencies": {
34
- "@cap-js/sqlite": ">=1"
35
+ "@cap-js/sqlite": "^1 || ^2"
35
36
  },
36
37
  "files": [
37
38
  "bin/",