@open-rap/compiler 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -2
  3. package/dist/agent/server.d.ts +35 -0
  4. package/dist/agent/server.js +408 -0
  5. package/dist/agent/server.js.map +1 -0
  6. package/dist/authoring/abapgit.d.ts +38 -0
  7. package/dist/authoring/abapgit.js +242 -0
  8. package/dist/authoring/abapgit.js.map +1 -0
  9. package/dist/authoring/catalog.d.ts +48 -0
  10. package/dist/authoring/catalog.js +117 -0
  11. package/dist/authoring/catalog.js.map +1 -0
  12. package/dist/authoring/floorplan.d.ts +28 -0
  13. package/dist/authoring/floorplan.js +31 -0
  14. package/dist/authoring/floorplan.js.map +1 -0
  15. package/dist/authoring/generate.d.ts +57 -0
  16. package/dist/authoring/generate.js +630 -0
  17. package/dist/authoring/generate.js.map +1 -0
  18. package/dist/authoring/mcp.d.ts +15 -0
  19. package/dist/authoring/mcp.js +52 -0
  20. package/dist/authoring/mcp.js.map +1 -0
  21. package/dist/authoring/new.d.ts +35 -0
  22. package/dist/authoring/new.js +389 -0
  23. package/dist/authoring/new.js.map +1 -0
  24. package/dist/bdef/ast.d.ts +249 -0
  25. package/dist/bdef/ast.js +3 -0
  26. package/dist/bdef/ast.js.map +1 -0
  27. package/dist/bdef/lexer.d.ts +105 -0
  28. package/dist/bdef/lexer.js +148 -0
  29. package/dist/bdef/lexer.js.map +1 -0
  30. package/dist/bdef/parser.d.ts +22 -0
  31. package/dist/bdef/parser.js +1012 -0
  32. package/dist/bdef/parser.js.map +1 -0
  33. package/dist/build.d.ts +26 -0
  34. package/dist/build.js +62 -0
  35. package/dist/build.js.map +1 -0
  36. package/dist/cds/adapter.d.ts +91 -0
  37. package/dist/cds/adapter.js +381 -0
  38. package/dist/cds/adapter.js.map +1 -0
  39. package/dist/cds/expression-ast.d.ts +65 -0
  40. package/dist/cds/expression-ast.js +330 -0
  41. package/dist/cds/expression-ast.js.map +1 -0
  42. package/dist/cli.d.ts +9 -0
  43. package/dist/cli.js +485 -0
  44. package/dist/cli.js.map +1 -0
  45. package/dist/dcl/parser.d.ts +59 -0
  46. package/dist/dcl/parser.js +338 -0
  47. package/dist/dcl/parser.js.map +1 -0
  48. package/dist/ddic/catalog.d.ts +17 -0
  49. package/dist/ddic/catalog.js +211 -0
  50. package/dist/ddic/catalog.js.map +1 -0
  51. package/dist/ddic/cds-types.d.ts +47 -0
  52. package/dist/ddic/cds-types.js +131 -0
  53. package/dist/ddic/cds-types.js.map +1 -0
  54. package/dist/ddic/table-source.d.ts +70 -0
  55. package/dist/ddic/table-source.js +500 -0
  56. package/dist/ddic/table-source.js.map +1 -0
  57. package/dist/ddic/xml.d.ts +65 -0
  58. package/dist/ddic/xml.js +201 -0
  59. package/dist/ddic/xml.js.map +1 -0
  60. package/dist/dev.d.ts +128 -0
  61. package/dist/dev.js +434 -0
  62. package/dist/dev.js.map +1 -0
  63. package/dist/diagnostics.d.ts +33 -0
  64. package/dist/diagnostics.js +53 -0
  65. package/dist/diagnostics.js.map +1 -0
  66. package/dist/gen/abap/derived-types.d.ts +26 -0
  67. package/dist/gen/abap/derived-types.js +323 -0
  68. package/dist/gen/abap/derived-types.js.map +1 -0
  69. package/dist/gen/abap/eml.d.ts +118 -0
  70. package/dist/gen/abap/eml.js +362 -0
  71. package/dist/gen/abap/eml.js.map +1 -0
  72. package/dist/gen/abap/enums.d.ts +13 -0
  73. package/dist/gen/abap/enums.js +74 -0
  74. package/dist/gen/abap/enums.js.map +1 -0
  75. package/dist/gen/abap/framework-http.d.ts +14 -0
  76. package/dist/gen/abap/framework-http.js +1049 -0
  77. package/dist/gen/abap/framework-http.js.map +1 -0
  78. package/dist/gen/abap/framework-query.d.ts +14 -0
  79. package/dist/gen/abap/framework-query.js +225 -0
  80. package/dist/gen/abap/framework-query.js.map +1 -0
  81. package/dist/gen/abap/framework-utils.d.ts +29 -0
  82. package/dist/gen/abap/framework-utils.js +3707 -0
  83. package/dist/gen/abap/framework-utils.js.map +1 -0
  84. package/dist/gen/abap/framework.d.ts +12 -0
  85. package/dist/gen/abap/framework.js +480 -0
  86. package/dist/gen/abap/framework.js.map +1 -0
  87. package/dist/gen/abap/generate.d.ts +40 -0
  88. package/dist/gen/abap/generate.js +238 -0
  89. package/dist/gen/abap/generate.js.map +1 -0
  90. package/dist/gen/abap/rewrite.d.ts +82 -0
  91. package/dist/gen/abap/rewrite.js +957 -0
  92. package/dist/gen/abap/rewrite.js.map +1 -0
  93. package/dist/gen/abap/shadow.d.ts +25 -0
  94. package/dist/gen/abap/shadow.js +175 -0
  95. package/dist/gen/abap/shadow.js.map +1 -0
  96. package/dist/gen/abap/tokens.d.ts +33 -0
  97. package/dist/gen/abap/tokens.js +87 -0
  98. package/dist/gen/abap/tokens.js.map +1 -0
  99. package/dist/gen/app/index.d.ts +25 -0
  100. package/dist/gen/app/index.js +322 -0
  101. package/dist/gen/app/index.js.map +1 -0
  102. package/dist/gen/csdl.d.ts +11 -0
  103. package/dist/gen/csdl.js +562 -0
  104. package/dist/gen/csdl.js.map +1 -0
  105. package/dist/gen/deploy.d.ts +12 -0
  106. package/dist/gen/deploy.js +53 -0
  107. package/dist/gen/deploy.js.map +1 -0
  108. package/dist/gen/docs/index.d.ts +35 -0
  109. package/dist/gen/docs/index.js +267 -0
  110. package/dist/gen/docs/index.js.map +1 -0
  111. package/dist/gen/docs/markdown.d.ts +23 -0
  112. package/dist/gen/docs/markdown.js +187 -0
  113. package/dist/gen/docs/markdown.js.map +1 -0
  114. package/dist/gen/docs/site.d.ts +17 -0
  115. package/dist/gen/docs/site.js +106 -0
  116. package/dist/gen/docs/site.js.map +1 -0
  117. package/dist/gen/keycloak.d.ts +21 -0
  118. package/dist/gen/keycloak.js +90 -0
  119. package/dist/gen/keycloak.js.map +1 -0
  120. package/dist/gen/launchpad/index.d.ts +48 -0
  121. package/dist/gen/launchpad/index.js +302 -0
  122. package/dist/gen/launchpad/index.js.map +1 -0
  123. package/dist/gen/launchpad/ui.d.ts +14 -0
  124. package/dist/gen/launchpad/ui.js +350 -0
  125. package/dist/gen/launchpad/ui.js.map +1 -0
  126. package/dist/gen/mcp.d.ts +23 -0
  127. package/dist/gen/mcp.js +244 -0
  128. package/dist/gen/mcp.js.map +1 -0
  129. package/dist/gen/sql-expressions.d.ts +12 -0
  130. package/dist/gen/sql-expressions.js +136 -0
  131. package/dist/gen/sql-expressions.js.map +1 -0
  132. package/dist/gen/sql.d.ts +38 -0
  133. package/dist/gen/sql.js +612 -0
  134. package/dist/gen/sql.js.map +1 -0
  135. package/dist/gen/ui/index.d.ts +7 -0
  136. package/dist/gen/ui/index.js +27 -0
  137. package/dist/gen/ui/index.js.map +1 -0
  138. package/dist/gen/ui/layout.d.ts +230 -0
  139. package/dist/gen/ui/layout.js +511 -0
  140. package/dist/gen/ui/layout.js.map +1 -0
  141. package/dist/gen/ui/static.d.ts +55 -0
  142. package/dist/gen/ui/static.js +2901 -0
  143. package/dist/gen/ui/static.js.map +1 -0
  144. package/dist/gen/ui/views.d.ts +10 -0
  145. package/dist/gen/ui/views.js +511 -0
  146. package/dist/gen/ui/views.js.map +1 -0
  147. package/dist/gen/vendor.d.ts +23 -0
  148. package/dist/gen/vendor.js +68 -0
  149. package/dist/gen/vendor.js.map +1 -0
  150. package/dist/gen/xml.d.ts +9 -0
  151. package/dist/gen/xml.js +42 -0
  152. package/dist/gen/xml.js.map +1 -0
  153. package/dist/index.d.ts +20 -0
  154. package/dist/index.js +20 -0
  155. package/dist/index.js.map +1 -0
  156. package/dist/input/project.d.ts +50 -0
  157. package/dist/input/project.js +128 -0
  158. package/dist/input/project.js.map +1 -0
  159. package/dist/ir/access-controls.d.ts +11 -0
  160. package/dist/ir/access-controls.js +148 -0
  161. package/dist/ir/access-controls.js.map +1 -0
  162. package/dist/ir/authorizations.d.ts +25 -0
  163. package/dist/ir/authorizations.js +178 -0
  164. package/dist/ir/authorizations.js.map +1 -0
  165. package/dist/ir/behaviors.d.ts +16 -0
  166. package/dist/ir/behaviors.js +548 -0
  167. package/dist/ir/behaviors.js.map +1 -0
  168. package/dist/ir/builder.d.ts +20 -0
  169. package/dist/ir/builder.js +505 -0
  170. package/dist/ir/builder.js.map +1 -0
  171. package/dist/ir/config.d.ts +25 -0
  172. package/dist/ir/config.js +130 -0
  173. package/dist/ir/config.js.map +1 -0
  174. package/dist/ir/dictionary.d.ts +54 -0
  175. package/dist/ir/dictionary.js +328 -0
  176. package/dist/ir/dictionary.js.map +1 -0
  177. package/dist/ir/entities.d.ts +58 -0
  178. package/dist/ir/entities.js +676 -0
  179. package/dist/ir/entities.js.map +1 -0
  180. package/dist/ir/expressions.d.ts +33 -0
  181. package/dist/ir/expressions.js +327 -0
  182. package/dist/ir/expressions.js.map +1 -0
  183. package/dist/ir/handlers.d.ts +63 -0
  184. package/dist/ir/handlers.js +398 -0
  185. package/dist/ir/handlers.js.map +1 -0
  186. package/dist/ir/number-ranges.d.ts +24 -0
  187. package/dist/ir/number-ranges.js +74 -0
  188. package/dist/ir/number-ranges.js.map +1 -0
  189. package/dist/ir/seed.d.ts +23 -0
  190. package/dist/ir/seed.js +78 -0
  191. package/dist/ir/seed.js.map +1 -0
  192. package/dist/ir/service.d.ts +21 -0
  193. package/dist/ir/service.js +96 -0
  194. package/dist/ir/service.js.map +1 -0
  195. package/dist/launchpad/config.d.ts +47 -0
  196. package/dist/launchpad/config.js +321 -0
  197. package/dist/launchpad/config.js.map +1 -0
  198. package/dist/launchpad/workspace.d.ts +48 -0
  199. package/dist/launchpad/workspace.js +166 -0
  200. package/dist/launchpad/workspace.js.map +1 -0
  201. package/dist/srvd/parser.d.ts +36 -0
  202. package/dist/srvd/parser.js +193 -0
  203. package/dist/srvd/parser.js.map +1 -0
  204. package/dist/standard/data.d.ts +18 -0
  205. package/dist/standard/data.js +534 -0
  206. package/dist/standard/data.js.map +1 -0
  207. package/dist/standard/index.d.ts +36 -0
  208. package/dist/standard/index.js +507 -0
  209. package/dist/standard/index.js.map +1 -0
  210. package/dist/version.d.ts +2 -0
  211. package/dist/version.js +4 -0
  212. package/dist/version.js.map +1 -0
  213. package/docs/AGENTS.md +79 -0
  214. package/docs/ANNOTATIONS.md +76 -0
  215. package/docs/DECISIONS.md +1526 -0
  216. package/docs/DEPLOYMENT.md +113 -0
  217. package/docs/DIAGNOSTICS.md +116 -0
  218. package/docs/FEATURES.md +287 -0
  219. package/docs/GUIDE.md +560 -0
  220. package/docs/KNOWN_GAPS.md +216 -0
  221. package/docs/MCP.md +84 -0
  222. package/docs/PLAN.md +690 -0
  223. package/docs/README.md +148 -0
  224. package/docs/REMOTE_APIS.md +121 -0
  225. package/docs/ROADMAP.md +35 -0
  226. package/docs/UTILITIES.md +95 -0
  227. package/package.json +49 -3
@@ -0,0 +1,562 @@
1
+ /**
2
+ * $metadata (CSDL XML, OData V4) of the service, with the UI, Common, Core, Capabilities and
3
+ * Measures annotations derived from the CDS annotations and the behavior (PLAN.md §6.5).
4
+ */
5
+ import { buildODataService, CONTAINER_NAME, ENTITY_CONTROL, exposedBehavior, FIELD_CONTROL, fieldControlTypeName, findEntity, getAnnotation, valueHelpOf, getArray, OPERATION_CONTROL, operationControlTypeName, projectedFieldName, same, SCHEMA_ALIAS, } from "@open-rap/model";
6
+ import { el, renderXml } from "./xml.js";
7
+ const VOCABULARIES = [
8
+ { uri: "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml", namespace: "Org.OData.Core.V1", alias: "Core" },
9
+ { uri: "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml", namespace: "Org.OData.Capabilities.V1", alias: "Capabilities" },
10
+ { uri: "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml", namespace: "Org.OData.Measures.V1", alias: "Measures" },
11
+ { uri: "https://sap.github.io/odata-vocabularies/vocabularies/Common.xml", namespace: "com.sap.vocabularies.Common.v1", alias: "Common" },
12
+ { uri: "https://sap.github.io/odata-vocabularies/vocabularies/UI.xml", namespace: "com.sap.vocabularies.UI.v1", alias: "UI" },
13
+ { uri: "https://sap.github.io/odata-vocabularies/vocabularies/Communication.xml", namespace: "com.sap.vocabularies.Communication.v1", alias: "Communication" },
14
+ ];
15
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
16
+ const str = (v) => (typeof v === "string" ? v : typeof v === "number" ? String(v) : undefined);
17
+ /**
18
+ * The AllowedExpressions of a filter field (FilterExpressionRestrictions) from
19
+ * `@Consumption.filter.selectionType` and `multipleSelections` (D57, D68); undefined: any condition.
20
+ */
21
+ export function allowedFilterExpressions(annotations) {
22
+ const selection = str(getAnnotation(annotations, "Consumption.filter.selectionType"))?.replace(/^#/, "").toUpperCase();
23
+ const single = getAnnotation(annotations, "Consumption.filter.multipleSelections") === false;
24
+ if (selection === "SINGLE" || (single && selection === undefined))
25
+ return "SingleValue";
26
+ if (selection === "INTERVAL")
27
+ return single ? "SingleRange" : "MultiRange";
28
+ return selection === "RANGE" && single ? "SingleRange" : undefined;
29
+ }
30
+ const num = (v) => (typeof v === "number" ? v : Number.POSITIVE_INFINITY);
31
+ /** `#HIGH` → `High`, `#FOR_ACTION` → `ForAction`. */
32
+ function enumName(value) {
33
+ return (value ?? "")
34
+ .replace(/^#/, "")
35
+ .toLowerCase()
36
+ .split("_")
37
+ .map((p) => (p === "" ? "" : p[0]?.toUpperCase() + p.slice(1)))
38
+ .join("");
39
+ }
40
+ /** Terms a Web API binding leaves out (D60): UI information, value helps, side effects. */
41
+ const UI_ONLY = /^(UI\.|Common\.ValueList|Common\.SideEffects|Common\.FilterDefaultValue)/;
42
+ /** An annotation of a Web API: without the UI information, nested annotations included. */
43
+ function webApiAnnotation(element) {
44
+ const term = element.attributes?.["Term"];
45
+ if (element.name === "Annotation" && typeof term === "string" && UI_ONLY.test(term)) {
46
+ return undefined;
47
+ }
48
+ return {
49
+ ...element,
50
+ children: (element.children ?? []).map((c) => (typeof c === "object" && c !== null ? webApiAnnotation(c) : c)),
51
+ };
52
+ }
53
+ function edmAttributes(edm) {
54
+ if (edm.enum !== undefined) {
55
+ // an OData enumeration type of the enhanced mode (D55)
56
+ return { Type: `${SCHEMA_ALIAS}.${edm.enum.name}` };
57
+ }
58
+ const attributes = { Type: edm.type };
59
+ if (edm.maxLength !== undefined)
60
+ attributes["MaxLength"] = edm.maxLength;
61
+ if (edm.precision !== undefined)
62
+ attributes["Precision"] = edm.precision;
63
+ if (edm.type === "Edm.Decimal")
64
+ attributes["Scale"] = edm.scale ?? "variable";
65
+ return attributes;
66
+ }
67
+ const annotation = (term, attributes = {}, ...children) => el("Annotation", { Term: term, ...attributes }, ...children);
68
+ const pv = (property, attributes, ...children) => el("PropertyValue", { Property: property, ...attributes }, ...children);
69
+ const record = (type, ...children) => el("Record", type === undefined ? undefined : { Type: type }, ...children);
70
+ const collection = (...children) => el("Collection", undefined, ...children);
71
+ const propertyPath = (path) => el("PropertyPath", undefined, path);
72
+ /** The entries of an element-level array annotation (`@UI.lineItem`) over all fields, by position. */
73
+ function entriesOf(entity, path) {
74
+ const result = [];
75
+ for (const field of entity.fields) {
76
+ for (const entry of getArray(field.annotations, path) ?? []) {
77
+ if (isObject(entry)) {
78
+ result.push({ field: field.name, entry });
79
+ }
80
+ }
81
+ }
82
+ return result.sort((a, b) => num(a.entry["position"]) - num(b.entry["position"]));
83
+ }
84
+ class CsdlBuilder {
85
+ model;
86
+ service;
87
+ qualified;
88
+ annotationBlocks = [];
89
+ constructor(model, service) {
90
+ this.model = model;
91
+ this.service = service;
92
+ this.qualified = (name) => `${SCHEMA_ALIAS}.${name}`;
93
+ }
94
+ build() {
95
+ const schemaChildren = [];
96
+ for (const type of this.service.entityTypes) {
97
+ schemaChildren.push(this.entityType(type));
98
+ }
99
+ schemaChildren.push(...this.complexTypes());
100
+ for (const operation of this.service.operations) {
101
+ schemaChildren.push(this.operation(operation));
102
+ }
103
+ schemaChildren.push(this.container());
104
+ for (const type of this.service.entityTypes) {
105
+ const entity = findEntity(this.model, type.entity);
106
+ if (entity !== undefined) {
107
+ this.propertyAnnotations(type, entity);
108
+ this.typeAnnotations(type, entity);
109
+ this.setAnnotations(type);
110
+ }
111
+ }
112
+ for (const operation of this.service.operations) {
113
+ this.operationAnnotations(operation);
114
+ }
115
+ schemaChildren.push(...this.annotationBlocks);
116
+ const edmx = el("edmx:Edmx", { Version: "4.0", "xmlns:edmx": "http://docs.oasis-open.org/odata/ns/edmx" }, ...VOCABULARIES.map((v) => el("edmx:Reference", { Uri: v.uri }, el("edmx:Include", { Namespace: v.namespace, Alias: v.alias }))), el("edmx:DataServices", undefined, el("Schema", { Namespace: this.service.namespace, Alias: SCHEMA_ALIAS, xmlns: "http://docs.oasis-open.org/odata/ns/edm" }, ...schemaChildren)));
117
+ return renderXml(edmx);
118
+ }
119
+ // -------------------------------------------------------------------------------------------
120
+ // types
121
+ entityType(type) {
122
+ const children = [el("Key", undefined, ...type.keys.map((k) => el("PropertyRef", { Name: k })))];
123
+ for (const property of type.properties) {
124
+ children.push(el("Property", { Name: property.name, ...edmAttributes(property.edm), Nullable: property.nullable ? undefined : "false" }));
125
+ }
126
+ if (type.entityControl !== undefined) {
127
+ children.push(el("Property", { Name: ENTITY_CONTROL, Type: this.qualified("EntityControl") }));
128
+ }
129
+ if (type.operationControl.length > 0) {
130
+ children.push(el("Property", { Name: OPERATION_CONTROL, Type: this.qualified(operationControlTypeName(type)) }));
131
+ }
132
+ if (type.fieldControl.length > 0) {
133
+ children.push(el("Property", { Name: FIELD_CONTROL, Type: this.qualified(fieldControlTypeName(type)) }));
134
+ }
135
+ for (const navigation of type.navigations) {
136
+ const targetType = this.qualified(navigation.targetType);
137
+ children.push(el("NavigationProperty", {
138
+ Name: navigation.name,
139
+ Type: navigation.collection ? `Collection(${targetType})` : targetType,
140
+ Nullable: navigation.collection || navigation.nullable ? undefined : "false",
141
+ Partner: navigation.partner,
142
+ }, ...navigation.constraints.map((c) => el("ReferentialConstraint", { Property: c.property, ReferencedProperty: c.referencedProperty }))));
143
+ }
144
+ return el("EntityType", { Name: type.name }, ...children);
145
+ }
146
+ complexTypes() {
147
+ const result = [];
148
+ if (this.service.entityTypes.some((t) => t.entityControl !== undefined)) {
149
+ result.push(el("ComplexType", { Name: "EntityControl" }, el("Property", { Name: "Deletable", Type: "Edm.Boolean", Nullable: "false" }), el("Property", { Name: "Updatable", Type: "Edm.Boolean", Nullable: "false" })));
150
+ }
151
+ for (const type of this.service.entityTypes) {
152
+ if (type.operationControl.length > 0) {
153
+ result.push(el("ComplexType", { Name: operationControlTypeName(type) }, ...type.operationControl.map((o) => el("Property", { Name: o, Type: "Edm.Boolean", Nullable: "false" }))));
154
+ }
155
+ if (type.fieldControl.length > 0) {
156
+ result.push(el("ComplexType", { Name: fieldControlTypeName(type) }, ...type.fieldControl.map((f) => el("Property", { Name: f, Type: "Edm.Byte", Nullable: "false" }))));
157
+ }
158
+ }
159
+ for (const complex of this.service.complexTypes) {
160
+ result.push(el("ComplexType", { Name: complex.name }, ...complex.properties.map((p) => el("Property", { Name: p.name, ...edmAttributes(p.edm), Nullable: p.nullable ? undefined : "false" }))));
161
+ }
162
+ for (const enumeration of this.service.enumTypes ?? []) {
163
+ result.push(el("EnumType", { Name: enumeration.name, UnderlyingType: enumeration.underlyingType === "Edm.Int32" ? undefined : enumeration.underlyingType }, ...enumeration.members.map((m) => el("Member", { Name: m.name, Value: m.value }, m.label === undefined ? undefined : annotation("Core.Description", { String: m.label })))));
164
+ }
165
+ return result;
166
+ }
167
+ bindingType(operation) {
168
+ const type = this.qualified(operation.boundType);
169
+ return operation.collectionBound ? `Collection(${type})` : type;
170
+ }
171
+ operation(operation) {
172
+ const returnsSelf = operation.returnType?.entityType === true && operation.returnType.type === operation.boundType;
173
+ const children = [el("Parameter", { Name: "_it", Type: this.bindingType(operation), Nullable: "false" })];
174
+ for (const parameter of operation.parameters) {
175
+ if (parameter.complexType !== undefined) {
176
+ const type = `${SCHEMA_ALIAS}.${parameter.complexType}`;
177
+ children.push(el("Parameter", { Name: parameter.name, Type: parameter.collection === true ? `Collection(${type})` : type, Nullable: parameter.nullable ? undefined : "false" }));
178
+ continue;
179
+ }
180
+ children.push(el("Parameter", { Name: parameter.name, ...edmAttributes(parameter.edm), Nullable: parameter.nullable ? undefined : "false" }));
181
+ }
182
+ if (operation.returnType !== undefined) {
183
+ const type = this.qualified(operation.returnType.type);
184
+ children.push(el("ReturnType", { Type: operation.returnType.collection ? `Collection(${type})` : type, Nullable: operation.returnType.nullable ? undefined : "false" }));
185
+ }
186
+ return el(operation.kind === "action" ? "Action" : "Function", { Name: operation.name, IsBound: "true", EntitySetPath: returnsSelf ? "_it" : undefined }, ...children);
187
+ }
188
+ container() {
189
+ return el("EntityContainer", { Name: CONTAINER_NAME }, ...this.service.entitySets.map((set) => {
190
+ const type = this.service.entityTypes.find((t) => t.name === set.entityType);
191
+ return el("EntitySet", { Name: set.name, EntityType: this.qualified(set.entityType) }, ...(type?.navigations ?? []).map((n) => el("NavigationPropertyBinding", { Path: n.name, Target: n.targetSet })));
192
+ }));
193
+ }
194
+ // -------------------------------------------------------------------------------------------
195
+ // annotations
196
+ annotations(target, children) {
197
+ const list = children.filter((c) => c !== undefined).map((c) => (this.model.service.webApi === true ? webApiAnnotation(c) : c)).filter((c) => c !== undefined);
198
+ if (list.length > 0) {
199
+ this.annotationBlocks.push(el("Annotations", { Target: target }, ...list));
200
+ }
201
+ }
202
+ propertyAnnotations(type, entity) {
203
+ for (const property of type.properties) {
204
+ const field = entity.fields.find((f) => f.name === property.field);
205
+ if (field === undefined) {
206
+ continue;
207
+ }
208
+ const a = field.annotations;
209
+ const children = [];
210
+ const label = str(getAnnotation(a, "EndUserText.label"));
211
+ if (label !== undefined)
212
+ children.push(annotation("Common.Label", { String: label }));
213
+ const quickInfo = str(getAnnotation(a, "EndUserText.quickInfo"));
214
+ if (quickInfo !== undefined)
215
+ children.push(annotation("Common.QuickInfo", { String: quickInfo }));
216
+ if (property.computed)
217
+ children.push(annotation("Core.Computed"));
218
+ // D57: multi-line texts, e-mail addresses and phone numbers, default filter values
219
+ if (getAnnotation(a, "UI.multiLineText") === true)
220
+ children.push(annotation("UI.MultiLineText"));
221
+ if (getAnnotation(a, "Semantics.email.address") === true)
222
+ children.push(annotation("Communication.IsEmailAddress"));
223
+ if (getAnnotation(a, "Semantics.telephone.type") !== undefined)
224
+ children.push(annotation("Communication.IsPhoneNumber"));
225
+ const filterDefault = getAnnotation(a, "Consumption.filter.defaultValue");
226
+ if (typeof filterDefault === "string" || typeof filterDefault === "number")
227
+ children.push(annotation("Common.FilterDefaultValue", { String: String(filterDefault) }));
228
+ if (property.immutable)
229
+ children.push(annotation("Core.Immutable"));
230
+ if (property.dynamic) {
231
+ children.push(annotation("Common.FieldControl", { Path: `${FIELD_CONTROL}/${property.name}` }));
232
+ }
233
+ else if (property.mandatory) {
234
+ children.push(annotation("Common.FieldControl", { EnumMember: "Common.FieldControlType/Mandatory" }));
235
+ }
236
+ if (property.edm.digitSequence === true && property.edm.enum === undefined)
237
+ children.push(annotation("Common.IsDigitSequence"));
238
+ if (field.type.kind === "cuky")
239
+ children.push(annotation("Common.IsCurrency"));
240
+ if (field.type.kind === "unit")
241
+ children.push(annotation("Common.IsUnit"));
242
+ if (field.currencyCode !== undefined)
243
+ children.push(annotation("Measures.ISOCurrency", { Path: field.currencyCode }));
244
+ if (field.unitOfMeasure !== undefined)
245
+ children.push(annotation("Measures.Unit", { Path: field.unitOfMeasure }));
246
+ if (getAnnotation(a, "UI.hidden") === true)
247
+ children.push(annotation("UI.Hidden"));
248
+ const help = valueHelpOf(this.model, field);
249
+ if (help !== undefined) {
250
+ children.push(annotation("Common.ValueList", {}, record("Common.ValueListType", pv("Label", { String: help.label }), pv("CollectionPath", { String: help.set }), pv("SearchSupported", { Bool: this.service.entitySets.find((s) => s.name === help.set)?.searchable === true }), pv("Parameters", {}, collection(record("Common.ValueListParameterInOut", pv("LocalDataProperty", { PropertyPath: property.name }), pv("ValueListProperty", { String: help.element })),
251
+ // additionalBinding: In (filter), Out (result), InOut (both)
252
+ ...(help.bindings ?? []).map((b) => record(b.usage === "filter" ? "Common.ValueListParameterIn" : b.usage === "result" ? "Common.ValueListParameterOut" : "Common.ValueListParameterInOut", pv("LocalDataProperty", { PropertyPath: b.local }), pv("ValueListProperty", { String: b.element }))), ...help.columns
253
+ .slice(1)
254
+ .filter((c) => !(help.bindings ?? []).some((b) => b.element === c.property))
255
+ .map((c) => record("Common.ValueListParameterDisplayOnly", pv("ValueListProperty", { String: c.property }))))))));
256
+ if (help.validation === true)
257
+ children.push(annotation("Common.ValueListForValidation", { String: "" }));
258
+ if (help.fixedValues === true)
259
+ children.push(annotation("Common.ValueListWithFixedValues"));
260
+ }
261
+ const text = getArray(a, "ObjectModel.text.element")?.map(str).find((t) => t !== undefined);
262
+ if (text !== undefined) {
263
+ const arrangement = str(getAnnotation(a, "UI.textArrangement"));
264
+ children.push(annotation("Common.Text", { Path: text }, arrangement === undefined ? undefined : annotation("UI.TextArrangement", { EnumMember: `UI.TextArrangementType/${enumName(arrangement)}` })));
265
+ }
266
+ this.annotations(`${this.qualified(type.name)}/${property.name}`, children);
267
+ }
268
+ }
269
+ /** `@UI.dataPoint` of the elements: UI.DataPoint, qualified by its qualifier or the element name (D57). */
270
+ dataPoints(entity) {
271
+ const result = [];
272
+ for (const field of entity.fields) {
273
+ const point = getAnnotation(field.annotations, "UI.dataPoint");
274
+ if (!isObject(point))
275
+ continue;
276
+ const title = str(point["title"]) ?? str(getAnnotation(field.annotations, "EndUserText.label"));
277
+ const criticality = str(point["criticality"]);
278
+ const target = point["targetvalue"];
279
+ const visualization = str(point["visualization"]);
280
+ result.push(annotation("UI.DataPoint", { Qualifier: str(point["qualifier"]) ?? field.name }, record("UI.DataPointType", pv("Value", { Path: field.name }), title === undefined ? undefined : pv("Title", { String: title }), str(point["description"]) === undefined ? undefined : pv("Description", { String: str(point["description"]) }), typeof target === "number" ? pv("TargetValue", { Decimal: target }) : undefined, criticality === undefined ? undefined : pv("Criticality", { Path: criticality }), visualization === undefined ? undefined : pv("Visualization", { EnumMember: `UI.VisualizationType/${enumName(visualization)}` }))));
281
+ }
282
+ return result;
283
+ }
284
+ dataFields(entity, path) {
285
+ const result = [];
286
+ for (const { field, entry } of entriesOf(entity, path)) {
287
+ const kind = str(entry["type"]) ?? "#STANDARD";
288
+ const label = str(entry["label"]);
289
+ const importance = str(entry["importance"]);
290
+ const importanceAnnotation = importance === undefined ? undefined : annotation("UI.Importance", { EnumMember: `UI.ImportanceType/${enumName(importance)}` });
291
+ const hidden = entry["hidden"] === true ? annotation("UI.Hidden") : undefined;
292
+ if (kind === "#FOR_ACTION") {
293
+ const action = str(entry["dataaction"]);
294
+ if (action === undefined) {
295
+ continue;
296
+ }
297
+ const grouping = str(entry["invocationgrouping"]);
298
+ result.push(record("UI.DataFieldForAction", pv("Action", { String: `${this.service.namespace}.${action}` }), label === undefined ? undefined : pv("Label", { String: label }), grouping === undefined ? undefined : pv("InvocationGrouping", { EnumMember: `UI.OperationGroupingType/${enumName(grouping)}` }), entry["inline"] === true ? pv("Inline", { Bool: true }) : undefined, importanceAnnotation, hidden));
299
+ continue;
300
+ }
301
+ const criticality = str(entry["criticality"]);
302
+ const url = str(entry["url"]);
303
+ if (kind === "#AS_DATAPOINT") {
304
+ const point = getAnnotation(entity.fields.find((f) => f.name === field)?.annotations, "UI.dataPoint");
305
+ const qualifier = isObject(point) ? (str(point["qualifier"]) ?? field) : field;
306
+ result.push(record("UI.DataFieldForAnnotation", pv("Target", { AnnotationPath: `@UI.DataPoint#${qualifier}` }), label === undefined ? undefined : pv("Label", { String: label }), importanceAnnotation, hidden));
307
+ continue;
308
+ }
309
+ result.push(record(kind === "#WITH_URL" && url !== undefined ? "UI.DataFieldWithUrl" : "UI.DataField", pv("Value", { Path: field }), label === undefined ? undefined : pv("Label", { String: label }), kind === "#WITH_URL" && url !== undefined ? pv("Url", { Path: url }) : undefined, criticality === undefined ? undefined : pv("Criticality", { Path: criticality }), importanceAnnotation, hidden));
310
+ }
311
+ return result;
312
+ }
313
+ typeAnnotations(type, entity) {
314
+ const a = entity.annotations;
315
+ const children = [];
316
+ const header = getAnnotation(a, "UI.headerInfo");
317
+ if (isObject(header)) {
318
+ const dataField = (value) => {
319
+ if (!isObject(value)) {
320
+ return undefined;
321
+ }
322
+ const path = str(value["value"]);
323
+ return path === undefined ? undefined : record("UI.DataField", pv("Value", { Path: path }), str(value["label"]) === undefined ? undefined : pv("Label", { String: str(value["label"]) }));
324
+ };
325
+ const title = dataField(header["title"]);
326
+ const description = dataField(header["description"]);
327
+ children.push(annotation("UI.HeaderInfo", {}, record("UI.HeaderInfoType", pv("TypeName", { String: str(header["typename"]) ?? type.name.replace(/Type$/, "") }), pv("TypeNamePlural", { String: str(header["typenameplural"]) ?? type.name.replace(/Type$/, "") }), title === undefined ? undefined : pv("Title", {}, title), description === undefined ? undefined : pv("Description", {}, description), str(header["imageurl"]) === undefined ? undefined : pv("ImageUrl", { Path: str(header["imageurl"]) }))));
328
+ }
329
+ const lineItem = this.dataFields(entity, "UI.lineItem");
330
+ if (lineItem.length > 0)
331
+ children.push(annotation("UI.LineItem", {}, collection(...lineItem)));
332
+ const identification = this.dataFields(entity, "UI.identification");
333
+ if (identification.length > 0)
334
+ children.push(annotation("UI.Identification", {}, collection(...identification)));
335
+ const selection = entriesOf(entity, "UI.selectionField");
336
+ if (selection.length > 0)
337
+ children.push(annotation("UI.SelectionFields", {}, collection(...selection.map((s) => propertyPath(s.field)))));
338
+ const groups = new Map();
339
+ for (const item of entriesOf(entity, "UI.fieldGroup")) {
340
+ const qualifier = str(item.entry["qualifier"]) ?? "";
341
+ groups.set(qualifier, [...(groups.get(qualifier) ?? []), item]);
342
+ }
343
+ for (const [qualifier, items] of groups) {
344
+ const fields = items.map((i) => record("UI.DataField", pv("Value", { Path: i.field }), str(i.entry["label"]) === undefined ? undefined : pv("Label", { String: str(i.entry["label"]) })));
345
+ children.push(annotation("UI.FieldGroup", { Qualifier: qualifier === "" ? undefined : qualifier }, record("UI.FieldGroupType", pv("Data", {}, collection(...fields)))));
346
+ }
347
+ children.push(...this.facets(entity));
348
+ children.push(...this.dataPoints(entity));
349
+ const variant = getArray(a, "UI.presentationVariant")?.find(isObject);
350
+ if (variant !== undefined) {
351
+ const sortOrder = (getArray(variant, "sortOrder") ?? []).filter(isObject);
352
+ children.push(annotation("UI.PresentationVariant", {}, record("UI.PresentationVariantType", sortOrder.length === 0
353
+ ? undefined
354
+ : pv("SortOrder", {}, collection(...sortOrder.map((s) => record("Common.SortOrderType", pv("Property", { PropertyPath: str(s["by"]) ?? "" }), pv("Descending", { Bool: str(s["direction"]) === "#DESC" }))))), pv("Visualizations", {}, collection(el("AnnotationPath", undefined, "@UI.LineItem"))))));
355
+ }
356
+ const semanticKey = getArray(a, "ObjectModel.semanticKey")?.map(str).filter((k) => k !== undefined);
357
+ if (semanticKey !== undefined && semanticKey.length > 0) {
358
+ children.push(annotation("Common.SemanticKey", {}, collection(...semanticKey.map(propertyPath))));
359
+ }
360
+ children.push(...this.sideEffects(type, entity));
361
+ const label = str(getAnnotation(a, "EndUserText.label"));
362
+ if (label !== undefined)
363
+ children.push(annotation("Common.Label", { String: label }));
364
+ this.annotations(this.qualified(type.name), children);
365
+ }
366
+ facets(entity) {
367
+ const all = [];
368
+ for (const field of entity.fields) {
369
+ for (const facet of getArray(field.annotations, "UI.facet") ?? []) {
370
+ if (isObject(facet)) {
371
+ all.push(facet);
372
+ }
373
+ }
374
+ }
375
+ if (all.length === 0) {
376
+ return [];
377
+ }
378
+ const byPosition = (x, y) => num(x["position"]) - num(y["position"]);
379
+ const render = (facet) => {
380
+ const type = str(facet["type"]) ?? "#COLLECTION";
381
+ const id = str(facet["id"]);
382
+ const label = str(facet["label"]);
383
+ const common = [id === undefined ? undefined : pv("ID", { String: id }), label === undefined ? undefined : pv("Label", { String: label })];
384
+ const hidden = facet["hidden"] === true ? annotation("UI.Hidden") : undefined;
385
+ if (type === "#COLLECTION") {
386
+ const children = all.filter((f) => str(f["parentid"]) === id && id !== undefined).sort(byPosition).map(render);
387
+ return record("UI.CollectionFacet", ...common, pv("Facets", {}, collection(...children)), hidden);
388
+ }
389
+ const qualifier = str(facet["targetqualifier"]);
390
+ const suffix = qualifier === undefined ? "" : `#${qualifier}`;
391
+ let target;
392
+ switch (type) {
393
+ case "#IDENTIFICATION_REFERENCE":
394
+ target = `@UI.Identification${suffix}`;
395
+ break;
396
+ case "#FIELDGROUP_REFERENCE":
397
+ target = `@UI.FieldGroup${suffix}`;
398
+ break;
399
+ case "#LINEITEM_REFERENCE": {
400
+ const element = str(facet["targetelement"]);
401
+ target = element === undefined ? `@UI.LineItem${suffix}` : `${element}/@UI.LineItem${suffix}`;
402
+ break;
403
+ }
404
+ case "#DATAPOINT_REFERENCE":
405
+ target = `@UI.DataPoint${suffix}`;
406
+ break;
407
+ case "#PRESENTATIONVARIANT_REFERENCE":
408
+ target = `@UI.PresentationVariant${suffix}`;
409
+ break;
410
+ default:
411
+ return undefined;
412
+ }
413
+ return record("UI.ReferenceFacet", ...common, pv("Target", { AnnotationPath: target }), hidden);
414
+ };
415
+ const topLevel = (purpose) => all
416
+ .filter((f) => str(f["parentid"]) === undefined && (str(f["purpose"]) ?? "#STANDARD") === purpose)
417
+ .sort(byPosition)
418
+ .map(render)
419
+ .filter((x) => x !== undefined);
420
+ const result = [];
421
+ const standard = topLevel("#STANDARD");
422
+ if (standard.length > 0)
423
+ result.push(annotation("UI.Facets", {}, collection(...standard)));
424
+ const header = topLevel("#HEADER");
425
+ if (header.length > 0)
426
+ result.push(annotation("UI.HeaderFacets", {}, collection(...header)));
427
+ return result;
428
+ }
429
+ /** BDEF side effects of the base entity, renamed through the projection. */
430
+ sideEffects(type, entity) {
431
+ const behavior = exposedBehavior(this.model, entity).base;
432
+ const result = [];
433
+ let index = 0;
434
+ for (const effect of behavior?.sideEffects ?? []) {
435
+ if (effect.trigger.kind !== "field") {
436
+ continue;
437
+ }
438
+ const source = projectedFieldName(this.model, entity, effect.trigger.name);
439
+ if (source === undefined) {
440
+ continue;
441
+ }
442
+ const targets = this.sideEffectTargets(type, entity, effect.targets, "");
443
+ if (targets.properties.length + targets.entities.length === 0) {
444
+ continue;
445
+ }
446
+ index++;
447
+ result.push(annotation("Common.SideEffects", { Qualifier: `${source}${index > 1 ? index : ""}` }, record("Common.SideEffectsType", pv("SourceProperties", {}, collection(propertyPath(source))), targets.properties.length === 0 ? undefined : pv("TargetProperties", {}, collection(...targets.properties.map((p) => el("String", undefined, p)))), targets.entities.length === 0 ? undefined : pv("TargetEntities", {}, collection(...targets.entities.map((p) => el("NavigationPropertyPath", undefined, p)))))));
448
+ }
449
+ return result;
450
+ }
451
+ sideEffectTargets(type, entity, targets, prefix) {
452
+ const properties = [];
453
+ const entities = [];
454
+ for (const target of targets) {
455
+ const segments = target.path.split(".");
456
+ if (target.kind === "field" && segments.length === 1) {
457
+ const projected = projectedFieldName(this.model, entity, segments[0] ?? "");
458
+ if (projected !== undefined)
459
+ properties.push(`${prefix}${projected}`);
460
+ }
461
+ else if (target.kind === "field" && segments.length === 2) {
462
+ const navigation = type.navigations.find((n) => same(n.name, segments[0] ?? ""));
463
+ const targetEntity = navigation === undefined ? undefined : findEntity(this.model, navigation.targetEntity);
464
+ const projected = targetEntity === undefined ? undefined : projectedFieldName(this.model, targetEntity, segments[1] ?? "");
465
+ if (navigation !== undefined && projected !== undefined)
466
+ properties.push(`${prefix}${navigation.name}/${projected}`);
467
+ }
468
+ else if (target.kind === "entity") {
469
+ const navigation = type.navigations.find((n) => same(n.name, target.path));
470
+ if (navigation !== undefined)
471
+ entities.push(`${prefix}${navigation.name}`);
472
+ else if (target.path === "$self" || target.path === "")
473
+ entities.push(prefix === "" ? "" : prefix.replace(/\/$/, ""));
474
+ }
475
+ }
476
+ return { properties, entities };
477
+ }
478
+ /** `@Consumption.filter.mandatory` and `selectionType` / `multipleSelections` (D57). */
479
+ filterRestrictions(type) {
480
+ const entity = findEntity(this.model, type.entity);
481
+ if (entity === undefined)
482
+ return undefined;
483
+ const exposed = entity.fields.filter((f) => type.properties.some((p) => p.name === f.name));
484
+ const required = exposed.filter((f) => getAnnotation(f.annotations, "Consumption.filter.mandatory") === true).map((f) => f.name);
485
+ const expressions = exposed.flatMap((f) => {
486
+ const allowed = allowedFilterExpressions(f.annotations);
487
+ return allowed === undefined ? [] : [{ property: f.name, allowed }];
488
+ });
489
+ if (required.length === 0 && expressions.length === 0)
490
+ return undefined;
491
+ return annotation("Capabilities.FilterRestrictions", {}, record("Capabilities.FilterRestrictionsType", required.length === 0 ? undefined : pv("RequiredProperties", {}, collection(...required.map(propertyPath))), expressions.length === 0
492
+ ? undefined
493
+ : pv("FilterExpressionRestrictions", {}, collection(...expressions.map((e) => record("Capabilities.FilterExpressionRestrictionType", pv("Property", { PropertyPath: e.property }), pv("AllowedExpressions", { String: e.allowed })))))));
494
+ }
495
+ setAnnotations(type) {
496
+ const set = this.service.entitySets.find((s) => s.entityType === type.name);
497
+ if (set === undefined) {
498
+ return;
499
+ }
500
+ const children = [
501
+ annotation("Capabilities.InsertRestrictions", {}, record("Capabilities.InsertRestrictionsType", pv("Insertable", { Bool: set.insertable }))),
502
+ annotation("Capabilities.UpdateRestrictions", {}, record("Capabilities.UpdateRestrictionsType", pv("Updatable", type.entityControl?.updatable === true && set.updatable ? { Path: `${ENTITY_CONTROL}/Updatable` } : { Bool: set.updatable }))),
503
+ annotation("Capabilities.DeleteRestrictions", {}, record("Capabilities.DeleteRestrictionsType", pv("Deletable", type.entityControl?.deletable === true && set.deletable ? { Path: `${ENTITY_CONTROL}/Deletable` } : { Bool: set.deletable }))),
504
+ annotation("Capabilities.SearchRestrictions", {}, record("Capabilities.SearchRestrictionsType", pv("Searchable", { Bool: set.searchable }))),
505
+ this.filterRestrictions(type),
506
+ ];
507
+ const navigations = type.navigations.filter((n) => n.collection);
508
+ if (navigations.length > 0) {
509
+ children.push(annotation("Capabilities.NavigationRestrictions", {}, record("Capabilities.NavigationRestrictionsType", pv("RestrictedProperties", {}, collection(...navigations.map((n) => record("Capabilities.NavigationPropertyRestriction", pv("NavigationProperty", { NavigationPropertyPath: n.name }), pv("InsertRestrictions", {}, record("Capabilities.InsertRestrictionsType", pv("Insertable", n.insertableDynamic ? { Path: `${OPERATION_CONTROL}/${n.name}` } : { Bool: n.insertable }))))))))));
510
+ }
511
+ if (type.etag !== undefined) {
512
+ children.push(annotation("Core.OptimisticConcurrency", {}, collection(propertyPath(type.etag))));
513
+ }
514
+ else if (type.etagPath !== undefined) {
515
+ // etag dependent by: the ETag property of the master, through the to-one navigations
516
+ children.push(annotation("Core.OptimisticConcurrency", {}, collection(propertyPath([...type.etagPath.navigations, type.etagPath.property].join("/")))));
517
+ }
518
+ // draft (D54): the root and the nodes of a draft BO, with the actions of the draft
519
+ if (set.draft?.root === true) {
520
+ const action = (name) => `${this.service.namespace}.${name}`;
521
+ children.push(annotation("Common.DraftRoot", {}, record("Common.DraftRootType", pv("ActivationAction", { String: action("Activate") }), pv("EditAction", { String: action("Edit") }), pv("PreparationAction", { String: action("Prepare") }), pv("DiscardAction", { String: action("Discard") }), pv("ResumeAction", { String: action("Resume") }), set.draft.collaborative === true ? pv("ShareAction", { String: action("Share") }) : undefined)));
522
+ }
523
+ else if (set.draft !== undefined) {
524
+ children.push(annotation("Common.DraftNode", {}, record("Common.DraftNodeType", pv("PreparationAction", { String: `${this.service.namespace}.Prepare` }))));
525
+ }
526
+ this.annotations(`${SCHEMA_ALIAS}.${CONTAINER_NAME}/${set.name}`, children);
527
+ }
528
+ operationAnnotations(operation) {
529
+ const target = `${this.qualified(operation.name)}(${this.bindingType(operation)})`;
530
+ const children = [];
531
+ if (operation.dynamic && !operation.collectionBound) {
532
+ children.push(annotation("Core.OperationAvailable", { Path: `_it/${OPERATION_CONTROL}/${operation.name}` }));
533
+ }
534
+ const type = this.service.entityTypes.find((t) => t.name === operation.boundType);
535
+ const entity = type === undefined ? undefined : findEntity(this.model, type.entity);
536
+ const behavior = entity === undefined ? undefined : exposedBehavior(this.model, entity).base;
537
+ for (const effect of behavior?.sideEffects.filter((e) => e.trigger.kind === "action" && same(e.trigger.name, operation.action)) ?? []) {
538
+ if (type === undefined || entity === undefined) {
539
+ break;
540
+ }
541
+ const targets = this.sideEffectTargets(type, entity, effect.targets, "_it/");
542
+ if (targets.properties.length + targets.entities.length > 0) {
543
+ children.push(annotation("Common.SideEffects", {}, record("Common.SideEffectsType", targets.properties.length === 0 ? undefined : pv("TargetProperties", {}, collection(...targets.properties.map((p) => el("String", undefined, p)))), targets.entities.length === 0 ? undefined : pv("TargetEntities", {}, collection(...targets.entities.map((p) => el("NavigationPropertyPath", undefined, p)))))));
544
+ }
545
+ }
546
+ this.annotations(target, children);
547
+ // parameter labels
548
+ const action = behavior?.actions.find((a) => same(a.name, operation.action));
549
+ const parameterEntity = action?.parameter === undefined ? undefined : findEntity(this.model, action.parameter);
550
+ for (const parameter of operation.parameters) {
551
+ const field = parameterEntity?.fields.find((f) => f.name === parameter.name);
552
+ const label = str(getAnnotation(field?.annotations, "EndUserText.label"));
553
+ if (label !== undefined) {
554
+ this.annotations(`${target}/${parameter.name}`, [annotation("Common.Label", { String: label })]);
555
+ }
556
+ }
557
+ }
558
+ }
559
+ export function generateCsdl(model, service = buildODataService(model)) {
560
+ return new CsdlBuilder(model, service).build();
561
+ }
562
+ //# sourceMappingURL=csdl.js.map