@sap/ux-specification 1.136.15 → 1.136.17

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 (244) hide show
  1. package/CHANGELOG.md +258 -165
  2. package/dist/documentation/v2/v2-AnalyticalListPage.html +2 -2
  3. package/dist/documentation/v2/v2-ApplicationV2.html +2 -2
  4. package/dist/documentation/v2/v2-ListReport.html +2 -2
  5. package/dist/documentation/v2/v2-ListReportNew.html +2 -2
  6. package/dist/documentation/v2/v2-ObjectPage.html +2 -2
  7. package/dist/documentation/v2/v2-OverviewPage.html +2 -2
  8. package/dist/documentation/v4/v4-ApplicationV4.html +2 -2
  9. package/dist/documentation/v4/v4-BuildingBlocks.html +2 -2
  10. package/dist/documentation/v4/v4-FreestylePage.html +2 -2
  11. package/dist/documentation/v4/v4-ListReport.html +2 -2
  12. package/dist/documentation/v4/v4-ObjectPage.html +2 -2
  13. package/dist/index-min.js +280 -274
  14. package/dist/index-min.js.map +4 -4
  15. package/dist/schemas/v2/AnalyticalListPageConfig.json +3 -3
  16. package/dist/schemas/v2/ListReportConfig.json +5 -5
  17. package/dist/schemas/v2/ListReportNewConfig.json +1 -1
  18. package/dist/schemas/v2/ObjectPageConfig.json +5 -5
  19. package/dist/schemas/v4/ApplicationV4.json +1 -1
  20. package/dist/schemas/v4/BuildingBlocksConfig.json +100 -10
  21. package/dist/schemas/v4/ListReportConfig.json +24 -6
  22. package/dist/schemas/v4/ObjectPageConfig.json +32 -8
  23. package/dist/specification/package.json +14 -12
  24. package/dist/specification/scripts/extractDocu.js +18 -1
  25. package/dist/specification/scripts/extractDocu.js.map +1 -1
  26. package/dist/specification/scripts/generate-validity-report.d.ts +15 -0
  27. package/dist/specification/scripts/generate-validity-report.d.ts.map +1 -0
  28. package/dist/specification/scripts/generate-validity-report.js +367 -0
  29. package/dist/specification/scripts/generate-validity-report.js.map +1 -0
  30. package/dist/specification/scripts/macros/corrections.d.ts +40 -0
  31. package/dist/specification/scripts/macros/corrections.d.ts.map +1 -1
  32. package/dist/specification/scripts/macros/corrections.js +114 -7
  33. package/dist/specification/scripts/macros/corrections.js.map +1 -1
  34. package/dist/specification/scripts/macros/schema.d.ts.map +1 -1
  35. package/dist/specification/scripts/macros/schema.js +14 -1
  36. package/dist/specification/scripts/macros/schema.js.map +1 -1
  37. package/dist/specification/scripts/macros/types.d.ts +1 -0
  38. package/dist/specification/scripts/macros/types.d.ts.map +1 -1
  39. package/dist/specification/scripts/schema/to-json-schema.d.ts.map +1 -1
  40. package/dist/specification/scripts/schema/to-json-schema.js +2 -13
  41. package/dist/specification/scripts/schema/to-json-schema.js.map +1 -1
  42. package/dist/specification/src/api.js +1 -1
  43. package/dist/specification/src/i18n/i18n.d.ts.map +1 -1
  44. package/dist/specification/src/i18n/i18n.js +2 -1
  45. package/dist/specification/src/i18n/i18n.js.map +1 -1
  46. package/dist/specification/src/sync/common/ManifestDrivenSchemaProcessor.d.ts +8 -1
  47. package/dist/specification/src/sync/common/ManifestDrivenSchemaProcessor.d.ts.map +1 -1
  48. package/dist/specification/src/sync/common/ManifestDrivenSchemaProcessor.js +10 -6
  49. package/dist/specification/src/sync/common/ManifestDrivenSchemaProcessor.js.map +1 -1
  50. package/dist/specification/src/sync/common/decoration/decorator-paths.d.ts +503 -0
  51. package/dist/specification/src/sync/common/decoration/decorator-paths.d.ts.map +1 -0
  52. package/dist/specification/src/sync/common/decoration/decorator-paths.js +497 -0
  53. package/dist/specification/src/sync/common/decoration/decorator-paths.js.map +1 -0
  54. package/dist/specification/src/sync/common/decoration/decorators.d.ts +116 -24
  55. package/dist/specification/src/sync/common/decoration/decorators.d.ts.map +1 -1
  56. package/dist/specification/src/sync/common/decoration/decorators.js +308 -78
  57. package/dist/specification/src/sync/common/decoration/decorators.js.map +1 -1
  58. package/dist/specification/src/sync/common/decoration/index.d.ts +1 -0
  59. package/dist/specification/src/sync/common/decoration/index.d.ts.map +1 -1
  60. package/dist/specification/src/sync/common/decoration/index.js +1 -0
  61. package/dist/specification/src/sync/common/decoration/index.js.map +1 -1
  62. package/dist/specification/src/sync/common/dist_tag.json +8 -3
  63. package/dist/specification/src/sync/common/generate/utils.d.ts +2 -1
  64. package/dist/specification/src/sync/common/generate/utils.d.ts.map +1 -1
  65. package/dist/specification/src/sync/common/generate/utils.js +5 -1
  66. package/dist/specification/src/sync/common/generate/utils.js.map +1 -1
  67. package/dist/specification/src/sync/common/i18n.json +2 -1
  68. package/dist/specification/src/sync/common/rules.d.ts +20 -0
  69. package/dist/specification/src/sync/common/rules.d.ts.map +1 -1
  70. package/dist/specification/src/sync/common/rules.js +22 -2
  71. package/dist/specification/src/sync/common/rules.js.map +1 -1
  72. package/dist/specification/src/sync/common/utils.d.ts +22 -6
  73. package/dist/specification/src/sync/common/utils.d.ts.map +1 -1
  74. package/dist/specification/src/sync/common/utils.js +122 -18
  75. package/dist/specification/src/sync/common/utils.js.map +1 -1
  76. package/dist/specification/src/sync/v2/generate/analyticalListReport.d.ts.map +1 -1
  77. package/dist/specification/src/sync/v2/generate/analyticalListReport.js +9 -7
  78. package/dist/specification/src/sync/v2/generate/analyticalListReport.js.map +1 -1
  79. package/dist/specification/src/sync/v2/generate/listReport.d.ts.map +1 -1
  80. package/dist/specification/src/sync/v2/generate/listReport.js +3 -1
  81. package/dist/specification/src/sync/v2/generate/listReport.js.map +1 -1
  82. package/dist/specification/src/sync/v2/generate/objectPage.d.ts.map +1 -1
  83. package/dist/specification/src/sync/v2/generate/objectPage.js +58 -2
  84. package/dist/specification/src/sync/v2/generate/objectPage.js.map +1 -1
  85. package/dist/specification/src/sync/v2/generate/overviewPage.d.ts +1 -9
  86. package/dist/specification/src/sync/v2/generate/overviewPage.d.ts.map +1 -1
  87. package/dist/specification/src/sync/v2/generate/overviewPage.js +30 -1
  88. package/dist/specification/src/sync/v2/generate/overviewPage.js.map +1 -1
  89. package/dist/specification/src/sync/v2/import/app/appProvider.d.ts.map +1 -1
  90. package/dist/specification/src/sync/v2/import/app/appProvider.js +2 -0
  91. package/dist/specification/src/sync/v2/import/app/appProvider.js.map +1 -1
  92. package/dist/specification/src/sync/v2/import/pages/analyticalListPage.d.ts +2 -1
  93. package/dist/specification/src/sync/v2/import/pages/analyticalListPage.d.ts.map +1 -1
  94. package/dist/specification/src/sync/v2/import/pages/analyticalListPage.js +5 -0
  95. package/dist/specification/src/sync/v2/import/pages/analyticalListPage.js.map +1 -1
  96. package/dist/specification/src/sync/v2/import/pages/listReport.d.ts +2 -1
  97. package/dist/specification/src/sync/v2/import/pages/listReport.d.ts.map +1 -1
  98. package/dist/specification/src/sync/v2/import/pages/listReport.js +5 -0
  99. package/dist/specification/src/sync/v2/import/pages/listReport.js.map +1 -1
  100. package/dist/specification/src/sync/v2/import/pages/objectPage.d.ts.map +1 -1
  101. package/dist/specification/src/sync/v2/import/pages/objectPage.js +2 -1
  102. package/dist/specification/src/sync/v2/import/pages/objectPage.js.map +1 -1
  103. package/dist/specification/src/sync/v2/import/utils.d.ts +28 -1
  104. package/dist/specification/src/sync/v2/import/utils.d.ts.map +1 -1
  105. package/dist/specification/src/sync/v2/import/utils.js +37 -0
  106. package/dist/specification/src/sync/v2/import/utils.js.map +1 -1
  107. package/dist/specification/src/sync/v2/utils.d.ts +12 -2
  108. package/dist/specification/src/sync/v2/utils.d.ts.map +1 -1
  109. package/dist/specification/src/sync/v2/utils.js +19 -2
  110. package/dist/specification/src/sync/v2/utils.js.map +1 -1
  111. package/dist/specification/src/sync/v4/application.d.ts +31 -2
  112. package/dist/specification/src/sync/v4/application.d.ts.map +1 -1
  113. package/dist/specification/src/sync/v4/application.js +44 -4
  114. package/dist/specification/src/sync/v4/application.js.map +1 -1
  115. package/dist/specification/src/sync/v4/export/controls/FilterBar.d.ts +2 -1
  116. package/dist/specification/src/sync/v4/export/controls/FilterBar.d.ts.map +1 -1
  117. package/dist/specification/src/sync/v4/export/controls/FilterBar.js +2 -1
  118. package/dist/specification/src/sync/v4/export/controls/FilterBar.js.map +1 -1
  119. package/dist/specification/src/sync/v4/export/controls/Header.d.ts +4 -3
  120. package/dist/specification/src/sync/v4/export/controls/Header.d.ts.map +1 -1
  121. package/dist/specification/src/sync/v4/export/controls/Header.js +7 -3
  122. package/dist/specification/src/sync/v4/export/controls/Header.js.map +1 -1
  123. package/dist/specification/src/sync/v4/export/controls/HeaderAction.d.ts +2 -1
  124. package/dist/specification/src/sync/v4/export/controls/HeaderAction.d.ts.map +1 -1
  125. package/dist/specification/src/sync/v4/export/controls/HeaderAction.js +7 -0
  126. package/dist/specification/src/sync/v4/export/controls/HeaderAction.js.map +1 -1
  127. package/dist/specification/src/sync/v4/export/controls/ObjectPageCustomSection.d.ts +2 -1
  128. package/dist/specification/src/sync/v4/export/controls/ObjectPageCustomSection.d.ts.map +1 -1
  129. package/dist/specification/src/sync/v4/export/controls/ObjectPageCustomSection.js +2 -1
  130. package/dist/specification/src/sync/v4/export/controls/ObjectPageCustomSection.js.map +1 -1
  131. package/dist/specification/src/sync/v4/export/controls/ObjectPageTable.d.ts +1 -1
  132. package/dist/specification/src/sync/v4/export/controls/ObjectPageTable.d.ts.map +1 -1
  133. package/dist/specification/src/sync/v4/export/controls/ObjectPageTable.js +0 -13
  134. package/dist/specification/src/sync/v4/export/controls/ObjectPageTable.js.map +1 -1
  135. package/dist/specification/src/sync/v4/export/controls/Table.d.ts +32 -17
  136. package/dist/specification/src/sync/v4/export/controls/Table.d.ts.map +1 -1
  137. package/dist/specification/src/sync/v4/export/controls/Table.js +84 -58
  138. package/dist/specification/src/sync/v4/export/controls/Table.js.map +1 -1
  139. package/dist/specification/src/sync/v4/export/controls/TableColumn.d.ts.map +1 -1
  140. package/dist/specification/src/sync/v4/export/controls/TableColumn.js +2 -2
  141. package/dist/specification/src/sync/v4/export/controls/TableColumn.js.map +1 -1
  142. package/dist/specification/src/sync/v4/export/controls/ToolBarAction.d.ts.map +1 -1
  143. package/dist/specification/src/sync/v4/export/controls/ToolBarAction.js +2 -2
  144. package/dist/specification/src/sync/v4/export/controls/ToolBarAction.js.map +1 -1
  145. package/dist/specification/src/sync/v4/export/export.d.ts.map +1 -1
  146. package/dist/specification/src/sync/v4/export/export.js +25 -3
  147. package/dist/specification/src/sync/v4/export/export.js.map +1 -1
  148. package/dist/specification/src/sync/v4/export/pages/ListReport.js +1 -1
  149. package/dist/specification/src/sync/v4/export/pages/ListReport.js.map +1 -1
  150. package/dist/specification/src/sync/v4/generate/actions.d.ts +1 -1
  151. package/dist/specification/src/sync/v4/generate/actions.d.ts.map +1 -1
  152. package/dist/specification/src/sync/v4/generate/fpm-custom-page/annotations.d.ts.map +1 -1
  153. package/dist/specification/src/sync/v4/generate/fpm-custom-page/annotations.js +20 -12
  154. package/dist/specification/src/sync/v4/generate/fpm-custom-page/annotations.js.map +1 -1
  155. package/dist/specification/src/sync/v4/generate/fpm-custom-page/generator.d.ts.map +1 -1
  156. package/dist/specification/src/sync/v4/generate/fpm-custom-page/generator.js +16 -1
  157. package/dist/specification/src/sync/v4/generate/fpm-custom-page/generator.js.map +1 -1
  158. package/dist/specification/src/sync/v4/generate/fpm-custom-page/utils.d.ts.map +1 -1
  159. package/dist/specification/src/sync/v4/generate/fpm-custom-page/utils.js +10 -1
  160. package/dist/specification/src/sync/v4/generate/fpm-custom-page/utils.js.map +1 -1
  161. package/dist/specification/src/sync/v4/generate/index.d.ts +1 -1
  162. package/dist/specification/src/sync/v4/generate/index.d.ts.map +1 -1
  163. package/dist/specification/src/sync/v4/generate/index.js +1 -1
  164. package/dist/specification/src/sync/v4/generate/index.js.map +1 -1
  165. package/dist/specification/src/sync/v4/generate/list-report/ListReportUtils.d.ts +73 -0
  166. package/dist/specification/src/sync/v4/generate/list-report/ListReportUtils.d.ts.map +1 -0
  167. package/dist/specification/src/sync/v4/generate/list-report/ListReportUtils.js +354 -0
  168. package/dist/specification/src/sync/v4/generate/list-report/ListReportUtils.js.map +1 -0
  169. package/dist/specification/src/sync/v4/generate/{listReport.d.ts → list-report/listReport.d.ts} +27 -16
  170. package/dist/specification/src/sync/v4/generate/list-report/listReport.d.ts.map +1 -0
  171. package/dist/specification/src/sync/v4/generate/{listReport.js → list-report/listReport.js} +191 -163
  172. package/dist/specification/src/sync/v4/generate/list-report/listReport.js.map +1 -0
  173. package/dist/specification/src/sync/v4/generate/objectPage.d.ts.map +1 -1
  174. package/dist/specification/src/sync/v4/generate/objectPage.js +145 -44
  175. package/dist/specification/src/sync/v4/generate/objectPage.js.map +1 -1
  176. package/dist/specification/src/sync/v4/import/pages/listReport.d.ts.map +1 -1
  177. package/dist/specification/src/sync/v4/import/pages/listReport.js +2 -1
  178. package/dist/specification/src/sync/v4/import/pages/listReport.js.map +1 -1
  179. package/dist/specification/src/sync/v4/import/utils.js +5 -3
  180. package/dist/specification/src/sync/v4/import/utils.js.map +1 -1
  181. package/dist/specification/src/sync/v4/sync-rules/App.d.ts +9 -7
  182. package/dist/specification/src/sync/v4/sync-rules/App.d.ts.map +1 -1
  183. package/dist/specification/src/sync/v4/sync-rules/App.js +4 -8
  184. package/dist/specification/src/sync/v4/sync-rules/App.js.map +1 -1
  185. package/dist/specification/src/sync/v4/sync-rules/AppSchema.d.ts +2 -9
  186. package/dist/specification/src/sync/v4/sync-rules/AppSchema.d.ts.map +1 -1
  187. package/dist/specification/src/sync/v4/sync-rules/AppSchema.js +2 -11
  188. package/dist/specification/src/sync/v4/sync-rules/AppSchema.js.map +1 -1
  189. package/dist/specification/src/sync/v4/sync-rules/DecoratorClass.d.ts +219 -96
  190. package/dist/specification/src/sync/v4/sync-rules/DecoratorClass.d.ts.map +1 -1
  191. package/dist/specification/src/sync/v4/sync-rules/DecoratorClass.js +557 -211
  192. package/dist/specification/src/sync/v4/sync-rules/DecoratorClass.js.map +1 -1
  193. package/dist/specification/src/sync/v4/sync-rules/Page.d.ts +17 -22
  194. package/dist/specification/src/sync/v4/sync-rules/Page.d.ts.map +1 -1
  195. package/dist/specification/src/sync/v4/sync-rules/Page.js +15 -22
  196. package/dist/specification/src/sync/v4/sync-rules/Page.js.map +1 -1
  197. package/dist/specification/src/sync/v4/sync-rules/controllerExtensions.d.ts +1 -0
  198. package/dist/specification/src/sync/v4/sync-rules/controllerExtensions.d.ts.map +1 -1
  199. package/dist/specification/src/sync/v4/sync-rules/controllerExtensions.js +17 -9
  200. package/dist/specification/src/sync/v4/sync-rules/controllerExtensions.js.map +1 -1
  201. package/dist/specification/src/sync/v4/sync-rules/index.d.ts +5 -0
  202. package/dist/specification/src/sync/v4/sync-rules/index.d.ts.map +1 -0
  203. package/dist/specification/src/sync/v4/sync-rules/index.js +21 -0
  204. package/dist/specification/src/sync/v4/sync-rules/index.js.map +1 -0
  205. package/dist/specification/src/sync/v4/sync-rules/utils.d.ts.map +1 -1
  206. package/dist/specification/src/sync/v4/sync-rules/utils.js +4 -0
  207. package/dist/specification/src/sync/v4/sync-rules/utils.js.map +1 -1
  208. package/dist/specification/src/sync/v4/utils/index.d.ts +2 -0
  209. package/dist/specification/src/sync/v4/utils/index.d.ts.map +1 -0
  210. package/dist/specification/src/sync/v4/utils/index.js +18 -0
  211. package/dist/specification/src/sync/v4/utils/index.js.map +1 -0
  212. package/dist/specification/src/sync/v4/utils/utils.d.ts +9 -1
  213. package/dist/specification/src/sync/v4/utils/utils.d.ts.map +1 -1
  214. package/dist/specification/src/sync/v4/utils/utils.js +17 -2
  215. package/dist/specification/src/sync/v4/utils/utils.js.map +1 -1
  216. package/dist/specification/test/test-projects/v2sttaProdMan2/webapp/manifest.json +550 -0
  217. package/dist/specification/test/test-projects/v4new/webapp/manifest.json +147 -0
  218. package/dist/specification/test/test-utils/utils.d.ts +71 -0
  219. package/dist/specification/test/test-utils/utils.d.ts.map +1 -0
  220. package/dist/specification/test/test-utils/utils.js +400 -0
  221. package/dist/specification/test/test-utils/utils.js.map +1 -0
  222. package/dist/specification/test/unit/decorators/validity-test-utils.d.ts +131 -0
  223. package/dist/specification/test/unit/decorators/validity-test-utils.d.ts.map +1 -0
  224. package/dist/specification/test/unit/decorators/validity-test-utils.js +661 -0
  225. package/dist/specification/test/unit/decorators/validity-test-utils.js.map +1 -0
  226. package/dist/types/src/apiTypes.d.ts +2 -3
  227. package/dist/types/src/apiTypes.d.ts.map +1 -1
  228. package/dist/types/src/apiTypes.js.map +1 -1
  229. package/dist/types/src/common/types.d.ts +119 -13
  230. package/dist/types/src/common/types.d.ts.map +1 -1
  231. package/dist/types/src/common/types.js.map +1 -1
  232. package/dist/types/src/v2/controls/ObjectPageTable.d.ts +1 -1
  233. package/dist/types/src/v2/controls/Table.d.ts +1 -1
  234. package/dist/types/src/v4/controls/ObjectPageTable.d.ts +1 -1
  235. package/dist/types/src/v4/controls/ObjectPageTable.d.ts.map +1 -1
  236. package/dist/types/src/v4/controls/Table.d.ts +3 -3
  237. package/dist/types/src/v4/controls/Table.d.ts.map +1 -1
  238. package/package.json +14 -12
  239. package/dist/specification/src/sync/v4/generate/ListReportUtils.d.ts +0 -107
  240. package/dist/specification/src/sync/v4/generate/ListReportUtils.d.ts.map +0 -1
  241. package/dist/specification/src/sync/v4/generate/ListReportUtils.js +0 -394
  242. package/dist/specification/src/sync/v4/generate/ListReportUtils.js.map +0 -1
  243. package/dist/specification/src/sync/v4/generate/listReport.d.ts.map +0 -1
  244. package/dist/specification/src/sync/v4/generate/listReport.js.map +0 -1
@@ -3,19 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Decorator = exports.BaseClass = void 0;
6
+ exports.Decorator = exports.BaseClass = exports.BaseConstruct = void 0;
7
7
  const ux_specification_types_1 = require("@sap/ux-specification-types");
8
8
  const decoration_1 = require("../../common/decoration");
9
9
  const i18next_1 = __importDefault(require("i18next"));
10
+ const jsonpath_plus_1 = require("jsonpath-plus");
10
11
  const utils_1 = require("./utils");
11
12
  /**
12
- * Adds a message to a schema element.
13
- * Inlined to avoid circular dependency issues.
13
+ * Appends a message to a schema element's message array.
14
+ * Inlined here to avoid circular dependency issues.
14
15
  *
15
- * @param element - The schema element to add the message to
16
- * @param message - The message parameters
17
- * @param message.text - The message text
18
- * @param message.deletable - Whether the message is deletable (default: false)
16
+ * @param element - The schema element that will receive the message
17
+ * @param message - The message content and options
18
+ * @param message.text - The translated message text to display
19
+ * @param message.deletable - Whether the user can dismiss the message (default: false)
19
20
  */
20
21
  function addMessageToSchema(element, { text, deletable = false }) {
21
22
  if (!element[ux_specification_types_1.SchemaTag.messages]) {
@@ -23,77 +24,103 @@ function addMessageToSchema(element, { text, deletable = false }) {
23
24
  }
24
25
  element[ux_specification_types_1.SchemaTag.messages].push({ text, deletable });
25
26
  }
26
- class BaseClass {
27
+ class BaseConstruct {
27
28
  /**
28
- * Creates an instance of BaseClass.
29
+ * Creates the base construct with access to the app schema, app/page configuration, and logger.
29
30
  *
30
- * @param appSchema - The application schema definition
31
+ * @param settings - Shared context (appSchema, app, page, logger) passed to all decorator classes
31
32
  */
32
- constructor(appSchema) {
33
- // Define appSchema as non-enumerable to hide it from serialization and Object.keys()
33
+ constructor(settings) {
34
+ const { app, appSchema, page, logger } = settings || {};
35
+ // Define properties as non-enumerable to hide them from serialization and Object.keys()
34
36
  Object.defineProperty(this, 'appSchema', {
35
37
  value: appSchema,
36
- writable: true,
38
+ writable: false,
39
+ enumerable: false,
40
+ configurable: true
41
+ });
42
+ Object.defineProperty(this, 'app', {
43
+ value: app,
44
+ writable: false,
45
+ enumerable: false,
46
+ configurable: true
47
+ });
48
+ Object.defineProperty(this, 'page', {
49
+ value: page,
50
+ writable: false,
51
+ enumerable: false,
52
+ configurable: true
53
+ });
54
+ Object.defineProperty(this, 'logger', {
55
+ value: logger,
56
+ writable: false,
37
57
  enumerable: false,
38
58
  configurable: true
39
59
  });
60
+ }
61
+ /**
62
+ * Returns the shared context (appSchema, app, page, logger) for use by subclasses or callers.
63
+ *
64
+ * @returns The current settings object
65
+ */
66
+ getSettings() {
67
+ return {
68
+ appSchema: this.appSchema,
69
+ app: this.app,
70
+ page: this.page,
71
+ logger: this.logger
72
+ };
73
+ }
74
+ }
75
+ exports.BaseConstruct = BaseConstruct;
76
+ class BaseClass extends BaseConstruct {
77
+ /**
78
+ * Looks up the matching schema definition by class name and stores it as the base.
79
+ *
80
+ * @param settings - Shared context (appSchema, app, page, logger) passed to all decorator classes
81
+ */
82
+ constructor(settings) {
83
+ super(settings);
40
84
  const name = this.getClassName();
41
- const definitions = appSchema?.definitions;
42
85
  // Define base as non-enumerable to hide it from serialization and Object.keys()
43
86
  Object.defineProperty(this, 'base', {
44
- value: { name, definition: definitions?.[name] },
87
+ value: { name, definition: this.appSchema?.get().definitions?.[name] },
45
88
  writable: true,
46
89
  enumerable: false,
47
90
  configurable: true
48
91
  });
49
92
  }
50
93
  /**
51
- * Gets the class name.
94
+ * Returns the class name used to look up the matching schema definition.
52
95
  *
53
- * @returns The name of the class
96
+ * @returns The constructor name of this class
54
97
  */
55
98
  getClassName() {
56
99
  return this.constructor.name;
57
100
  }
58
101
  /**
59
- * Gets the base name.
102
+ * Returns the name stored at construction time for the schema definition lookup.
60
103
  *
61
- * @returns The base name
104
+ * @returns The base definition name
62
105
  */
63
106
  getBaseName() {
64
107
  return this.base.name;
65
108
  }
66
109
  /**
67
- * Gets the base definition.
110
+ * Returns the schema definition that decorators are applied to.
68
111
  *
69
- * @returns The base definition
112
+ * @returns The JSON schema definition for this class
70
113
  */
71
114
  getBase() {
72
115
  return this.base.definition;
73
116
  }
74
117
  /**
75
- * Sets the base definition and name.
118
+ * Builds an annotation path like `/<EntityType>/@<Term>#<Qualifier>` and stores it on the schema definition.
76
119
  *
77
- * @param base - The base object containing name and definition
78
- */
79
- setBase(base) {
80
- this.base = base;
81
- }
82
- /**
83
- * Gets the definitions from the app schema.
84
- *
85
- * @returns The definitions object or undefined
86
- */
87
- getDefinitions() {
88
- return this.appSchema?.definitions;
89
- }
90
- /**
91
- * Creates an annotation path for schema based on entity type, term, and qualifier.
92
- *
93
- * @param entityTypeName - Entity type name.
94
- * @param term - Annotation term.
95
- * @param qualifier - Annotation qualifier (optional).
96
- * @returns Annotation path based on received params
120
+ * @param entityTypeName - The OData entity type name (e.g. "SalesOrderItem")
121
+ * @param term - The annotation term (e.g. "com.sap.vocabularies.UI.v1.LineItem")
122
+ * @param qualifier - Optional qualifier to disambiguate multiple annotations of the same term
123
+ * @returns The built annotation path, or undefined if entityTypeName is empty
97
124
  */
98
125
  createAnnotationPath(entityTypeName, term, qualifier) {
99
126
  if (!entityTypeName) {
@@ -110,70 +137,116 @@ class BaseClass {
110
137
  exports.BaseClass = BaseClass;
111
138
  class Decorator extends BaseClass {
112
139
  /**
113
- * Sets the parent decorator instance for this object.
114
- * This enables nested decorators to access parent properties using the '../' prefix in @dependsOn.
140
+ * Sets up the decorator context as non-enumerable so it stays hidden from serialization.
115
141
  *
116
- * @param parent - The parent Decorator instance
117
- * @returns this (for method chaining)
142
+ * @param settings - Shared context (appSchema, app, page, logger) passed to all decorator classes
118
143
  */
119
- setParent(parent) {
120
- this.__parent__ = parent;
121
- return this;
144
+ constructor(settings) {
145
+ super(settings);
146
+ // Define decoratorContext as non-enumerable to hide from serialization
147
+ Object.defineProperty(this, 'decoratorContext', {
148
+ value: {},
149
+ writable: true,
150
+ enumerable: false,
151
+ configurable: true
152
+ });
122
153
  }
123
154
  /**
124
- * Gets the parent decorator instance if it exists.
155
+ * Builds the decorator context from app, page, and custom values, then applies all
156
+ * decorators (enums, message, hide, readonly) to the schema definition.
157
+ *
158
+ * @param customContext - Specific values (e.g. table state, section state) used for condition evaluation
159
+ * @param definition - Override the schema definition to decorate (defaults to getBase())
160
+ * @example
161
+ * ```typescript
162
+ * // Basic initialization (app and page auto-injected)
163
+ * decorator.init();
164
+ *
165
+ * // With custom context for table-specific conditions
166
+ * decorator.init({
167
+ * table: { type: 'GridTable', views: [] }
168
+ * });
125
169
  *
126
- * @returns The parent Decorator instance or undefined
170
+ * // With custom definition (for dynamically created definitions)
171
+ * decorator.init({ table: { type: 'GridTable' } }, customTableDefinition);
172
+ * ```
127
173
  */
128
- getParent() {
129
- return this.__parent__;
174
+ init(customContext, definition) {
175
+ this.decoratorContext = {
176
+ app: this.app?.config,
177
+ page: this.page?.config,
178
+ custom: customContext
179
+ };
180
+ // @ToDo activate for actual use of minUI5Version.
181
+ // const minUi5Version = this.app?.getMinUI5Version();
182
+ this.applyDecorators(undefined, undefined, definition);
130
183
  }
131
184
  /**
132
- * Helper method to get property value from an object using a property path.
133
- * Supports simple property names (e.g., 'visualFilters'), nested paths (e.g., 'settings.enabled'),
134
- * and parent traversal (e.g., '../type' to access parent object's type property).
185
+ * Resolves a dotted property path (e.g. `page.isALP`, `custom.items[0].name`) against
186
+ * the decorator context using JSONPath. The path must start with a known source prefix
187
+ * (`app`, `page`, or `custom`).
135
188
  *
136
- * @param syncInstance - The object instance to retrieve the property value from
137
- * @param propertyPath - The property path (e.g., 'visualFilters', 'settings.enabled', or '../type')
138
- * @returns Object containing the final key and its value, or undefined if not found
189
+ * @param propertyPath - Dotted path with source prefix, supports full JSONPath syntax
190
+ * @returns The resolved `{ key, value }` pair, or undefined if the path does not resolve
191
+ * @example
192
+ * ```typescript
193
+ * // With decoratorContext = {
194
+ * // app: { manifest: { 'sap.ui5': { routing: {...} } } },
195
+ * // page: { isALP: true },
196
+ * // custom: { type: 'GridTable', items: [{ name: 'Item1' }, { name: 'Item2' }] }
197
+ * // }
198
+ * getPropertyKeyValue('page.isALP') // { key: 'isALP', value: true }
199
+ * getPropertyKeyValue('custom.items[0]') // { key: '0', value: { name: 'Item1' } }
200
+ * getPropertyKeyValue('custom.items[1].name') // { key: 'name', value: 'Item2' }
201
+ * getPropertyKeyValue('app.manifest["sap.ui5"]') // Access property with dot in name
202
+ * getPropertyKeyValue('$.app.manifest["sap.ui5"].routing') // JSONPath with root reference
203
+ * getPropertyKeyValue('$app["manifest"]["sap.ui5"]') // Alternative JSONPath format
204
+ * ```
139
205
  */
140
- getPropertyKeyValue(syncInstance, propertyPath) {
141
- if (!syncInstance || !propertyPath) {
206
+ getPropertyKeyValue(propertyPath) {
207
+ if (!propertyPath || !this.decoratorContext) {
142
208
  return undefined;
143
209
  }
144
- // Handle parent traversal (../)
145
- if (propertyPath.startsWith('../')) {
146
- const parent = this.getParent();
147
- if (!parent) {
210
+ try {
211
+ // Ensure path starts with $ for JSONPath
212
+ const jsonPath = propertyPath.startsWith('$') ? propertyPath : `$.${propertyPath}`;
213
+ // Use JSONPath to query with resultType 'all' to get both value and metadata
214
+ const results = (0, jsonpath_plus_1.JSONPath)({
215
+ path: jsonPath,
216
+ json: this.decoratorContext,
217
+ resultType: 'all'
218
+ });
219
+ // JSONPath with resultType 'all' returns an array of result objects
220
+ if (!results || results.length === 0) {
148
221
  return undefined;
149
222
  }
150
- // Remove '../' and get the property from parent
151
- const parentPath = propertyPath.substring(3);
152
- return this.getPropertyKeyValue(parent, parentPath);
153
- }
154
- const keys = propertyPath.split('.');
155
- let value = syncInstance;
156
- for (const key of keys) {
157
- if (value === null || value === undefined) {
158
- return undefined;
159
- }
160
- value = value[key];
223
+ // Get the first result (wrap: false behavior)
224
+ const result = results[0];
225
+ // parentProperty contains the last key in the path
226
+ const key = String(result.parentProperty);
227
+ const value = result.value;
228
+ return { key, value };
229
+ }
230
+ catch {
231
+ // If JSONPath fails, return undefined
232
+ return undefined;
161
233
  }
162
- const key = keys[keys.length - 1];
163
- return { key, value };
164
234
  }
165
235
  /**
166
- * Evaluates a single dependency condition.
167
- *
168
- * @param condition - The single condition to evaluate
169
- * @param condition.path - The property path to check
170
- * @param condition.dependsOn - Optional custom condition function
171
- * @param condition.expectedValue - Optional expected value for equality check
172
- * @param syncRuleProviderInstance - The sync rule provider instance
173
- * @returns Object containing whether condition passed and the dependency value
236
+ * Evaluates one condition: resolves the path, applies the check (custom function,
237
+ * equality, or truthy), and optionally negates the result (for `not()` conditions).
238
+ *
239
+ * @param condition - A single condition with path, expected value, and optional negate flag
240
+ * @returns Whether the condition passed along with the resolved value and key
174
241
  */
175
- evaluateSingleCondition(condition, syncRuleProviderInstance) {
176
- const { key, value } = this.getPropertyKeyValue(syncRuleProviderInstance, condition.path);
242
+ evaluateSingleCondition(condition) {
243
+ // Handle special __always__ path (from @hide(true))
244
+ if (condition.path === '__always__') {
245
+ return { passed: true, value: true, key: '__always__' };
246
+ }
247
+ const result = this.getPropertyKeyValue(condition.path);
248
+ const key = result?.key ?? condition.path;
249
+ const value = result?.value;
177
250
  let passed = false;
178
251
  if (condition.dependsOn) {
179
252
  // Use custom condition function
@@ -187,29 +260,82 @@ class Decorator extends BaseClass {
187
260
  // Default: truthy check
188
261
  passed = !!value;
189
262
  }
263
+ // Apply negation if negate flag is set (from not() helper)
264
+ if (condition.negate) {
265
+ passed = !passed;
266
+ }
190
267
  return { passed, value, key };
191
268
  }
192
269
  /**
193
- * Helper method to add a message to the schema property based on value condition.
194
- *
195
- * @param condition - EnumValueCondition object containing condition details
196
- * @param syncRuleProviderInstance - The instance of the sync rule provider
197
- * @param definition - The schema property definition
198
- * @param i18nProperties - i18n properties for message translation
199
- * @param i18nProperties.propertyName - The property name for the message
200
- * @param i18nProperties.context - The context for the message
270
+ * Translates and attaches a message to a schema property.
271
+ * Supports both the new `MessageConfig` format (from `msg()` helper) and the legacy `DependsOnMessage` format.
272
+ *
273
+ * @param condition - The decorator metadata or enum value condition containing the message definition
274
+ * @param decoratedClass - The decorated class instance, used for legacy function-based message text
275
+ * @param definition - The schema property definition to attach the message to
276
+ * @param i18nProperties - Context for i18n translation (propertyName, evaluation context)
277
+ * @param i18nProperties.propertyName - The property name used as i18n parameter
278
+ * @param i18nProperties.context - The evaluation context string used as i18n parameter
201
279
  */
202
- addConditionalMessage(condition, syncRuleProviderInstance, definition, i18nProperties) {
203
- const messageText = typeof condition.message.text === 'function'
204
- ? condition.message.text(syncRuleProviderInstance)
205
- : i18next_1.default.t('PROPERTY_NOT_ALLOWED', { ...i18nProperties });
280
+ addConditionalMessage(condition, decoratedClass, definition, i18nProperties) {
281
+ // Handle new MessageConfig format (from msg() helper)
282
+ const metadata = condition;
283
+ if (metadata.messageConfig && (0, decoration_1.isMessageConfig)(metadata.messageConfig)) {
284
+ // Resolve any PathNode values in the params
285
+ const resolvedParams = this.resolveMessageParams(metadata.messageConfig.params);
286
+ const messageText = i18next_1.default.t(metadata.messageConfig.i18nKey, resolvedParams);
287
+ addMessageToSchema(definition, { text: messageText, deletable: metadata.messageConfig.deletable });
288
+ return;
289
+ }
290
+ // Handle legacy DependsOnMessage format
291
+ if (!condition.message) {
292
+ return;
293
+ }
294
+ let messageText;
295
+ if (typeof condition.message.text === 'function') {
296
+ messageText = condition.message.text(decoratedClass);
297
+ }
298
+ else if (typeof condition.message.text === 'string') {
299
+ messageText = condition.message.text;
300
+ }
301
+ else if (condition.message.text === true) {
302
+ // true means use default i18n translation
303
+ messageText = i18next_1.default.t('PROPERTY_NOT_ALLOWED', { ...i18nProperties });
304
+ }
305
+ else {
306
+ messageText = i18next_1.default.t('PROPERTY_NOT_ALLOWED', { ...i18nProperties });
307
+ }
206
308
  addMessageToSchema(definition, { text: messageText, deletable: condition.message.deletable });
207
309
  }
208
310
  /**
209
- * Gets the context message from evaluation results.
311
+ * Resolves any `PathNode` values in message params to their actual values from the decorator context.
210
312
  *
211
- * @param results - The evaluation results
212
- * @returns The context message string
313
+ * @param params - Message parameters that may contain PathNode references
314
+ * @returns A new params object with PathNode values replaced by their resolved context values
315
+ */
316
+ resolveMessageParams(params) {
317
+ if (!params) {
318
+ return params;
319
+ }
320
+ const resolved = {};
321
+ for (const [key, value] of Object.entries(params)) {
322
+ if ((0, decoration_1.isPathNode)(value)) {
323
+ // Resolve PathNode to actual value
324
+ const result = this.getPropertyKeyValue((0, decoration_1.getPath)(value));
325
+ resolved[key] = result?.value ?? '';
326
+ }
327
+ else {
328
+ resolved[key] = value;
329
+ }
330
+ }
331
+ return resolved;
332
+ }
333
+ /**
334
+ * Builds a human-readable context string from the evaluation results that did not pass.
335
+ * Used for diagnostic messages showing which conditions were unmet.
336
+ *
337
+ * @param results - The condition evaluation results to summarize
338
+ * @returns A comma-separated string of `key: value` pairs for failed conditions
213
339
  */
214
340
  getContextForMessage(results) {
215
341
  return results
@@ -218,53 +344,234 @@ class Decorator extends BaseClass {
218
344
  .join(', ');
219
345
  }
220
346
  /**
221
- * Applies the dependsOn decorator to the schema definition.
347
+ * Evaluates one item inside an AND group — either a single condition or a nested OR group.
348
+ *
349
+ * @param conditionItem - A single condition or a nested `{ __orConditions }` group
350
+ * @returns Whether the item passed and the detailed evaluation results
351
+ */
352
+ evaluateAndConditionItem(conditionItem) {
353
+ // Check if this is a nested OR group
354
+ if ((0, decoration_1.isOrConditionGroup)(conditionItem)) {
355
+ // Evaluate the nested OR - at least one must pass
356
+ return this.evaluateOrConditions(conditionItem.__orConditions);
357
+ }
358
+ // It's a single condition
359
+ const result = this.evaluateSingleCondition(conditionItem);
360
+ return { passed: result.passed, results: [result] };
361
+ }
362
+ /**
363
+ * Evaluates OR conditions — passes when at least one item (single condition or nested AND group) matches.
364
+ * Short-circuits on the first passing item.
365
+ *
366
+ * @param orConditions - Array of conditions or nested AND groups to evaluate
367
+ * @returns Whether any condition passed and the collected evaluation results
368
+ */
369
+ evaluateOrConditions(orConditions) {
370
+ const allResults = [];
371
+ for (const conditionItem of orConditions) {
372
+ // Check if this is a nested AND group
373
+ if ((0, decoration_1.isAndConditionGroup)(conditionItem)) {
374
+ // Evaluate all conditions in the AND group - all must pass
375
+ const andResults = [];
376
+ let allAndPassed = true;
377
+ for (const andItem of conditionItem.__andConditions) {
378
+ const { passed, results } = this.evaluateAndConditionItem(andItem);
379
+ andResults.push(...results);
380
+ if (!passed) {
381
+ allAndPassed = false;
382
+ // Don't break - we want all results for context
383
+ }
384
+ }
385
+ allResults.push(...andResults);
386
+ if (allAndPassed) {
387
+ // Short-circuit: if this AND group passes, the OR passes
388
+ return { passed: true, results: allResults };
389
+ }
390
+ }
391
+ else {
392
+ // Single condition (symmetric with AND handling)
393
+ const result = this.evaluateSingleCondition(conditionItem);
394
+ allResults.push(result);
395
+ if (result.passed) {
396
+ // Short-circuit: if any condition passes, the OR passes
397
+ return { passed: true, results: allResults };
398
+ }
399
+ }
400
+ }
401
+ return { passed: false, results: allResults };
402
+ }
403
+ /**
404
+ * Evaluates AND conditions — passes only when every item (single condition or nested OR group) matches.
405
+ * Continues even after a failure to collect all results for diagnostic context.
222
406
  *
223
- * @param schemaDefinition - The schema definition to apply the decorator to
224
- * @param syncRuleProviderInstance - The sync rule provider instance
407
+ * @param andConditions - Array of conditions or nested OR groups that must all pass
408
+ * @returns Whether all conditions passed and the collected evaluation results
225
409
  */
226
- applyDependsOnDecorator(schemaDefinition, syncRuleProviderInstance) {
227
- if (!schemaDefinition?.properties || !syncRuleProviderInstance) {
410
+ evaluateAndConditions(andConditions) {
411
+ const allResults = [];
412
+ let allPassed = true;
413
+ for (const conditionItem of andConditions) {
414
+ const { passed, results } = this.evaluateAndConditionItem(conditionItem);
415
+ allResults.push(...results);
416
+ if (!passed) {
417
+ allPassed = false;
418
+ // Continue to collect all results for context
419
+ }
420
+ }
421
+ return { passed: allPassed, results: allResults };
422
+ }
423
+ /**
424
+ * Entry point for condition evaluation. Dispatches to OR, AND, or single evaluation
425
+ * depending on the shape of the condition metadata.
426
+ *
427
+ * @param conditionInfo - The decorator condition metadata (single, AND, or OR)
428
+ * @returns Whether the condition passed and a diagnostic context string
429
+ */
430
+ evaluateCondition(conditionInfo) {
431
+ let passed = false;
432
+ let context = '';
433
+ if (conditionInfo.orConditions && Array.isArray(conditionInfo.orConditions)) {
434
+ // OR logic: At least one condition object must match
435
+ const result = this.evaluateOrConditions(conditionInfo.orConditions);
436
+ passed = result.passed;
437
+ context = this.getContextForMessage(result.results);
438
+ }
439
+ else if (conditionInfo.conditions && Array.isArray(conditionInfo.conditions)) {
440
+ // Multi-condition: ALL must be met (AND logic)
441
+ // Now supports nested OR groups via evaluateAndConditions
442
+ const result = this.evaluateAndConditions(conditionInfo.conditions);
443
+ passed = result.passed;
444
+ context = this.getContextForMessage(result.results);
445
+ }
446
+ else if (conditionInfo.path) {
447
+ // Single condition
448
+ const result = this.evaluateSingleCondition(conditionInfo);
449
+ passed = result.passed;
450
+ context = this.getContextForMessage([result]);
451
+ }
452
+ return { passed, context };
453
+ }
454
+ /**
455
+ * Yields each property from a schema definition for decorator processing.
456
+ * Returns early if the definition has no properties or the target is falsy.
457
+ *
458
+ * @param schemaDefinition - The schema definition whose properties to iterate
459
+ * @param target - Guard object — iteration is skipped if falsy
460
+ * @yields Property name and its definition for each property in the schema
461
+ */
462
+ *iterateProperties(schemaDefinition, target) {
463
+ if (!schemaDefinition?.properties || !target) {
228
464
  return;
229
465
  }
230
466
  for (const propertyName in schemaDefinition.properties) {
231
467
  const property = schemaDefinition.properties[propertyName];
232
- const dependsOnInfo = (0, decoration_1.getDependsOnMetadata)(syncRuleProviderInstance, propertyName);
233
- if (dependsOnInfo) {
234
- let shouldHide = false;
235
- let context = '';
236
- // Check if this is multi-condition or single condition
237
- if (dependsOnInfo.conditions && Array.isArray(dependsOnInfo.conditions)) {
238
- // Multi-condition: ALL must be met (AND logic)
239
- const results = dependsOnInfo.conditions.map((condition) => this.evaluateSingleCondition(condition, syncRuleProviderInstance));
240
- // All conditions must pass
241
- shouldHide = !results.every((result) => result.passed);
242
- context = this.getContextForMessage(results);
243
- }
244
- else {
245
- const result = this.evaluateSingleCondition(dependsOnInfo, syncRuleProviderInstance);
246
- context = this.getContextForMessage([result]);
247
- shouldHide = !result.passed;
468
+ yield { propertyName, property };
469
+ }
470
+ }
471
+ /**
472
+ * Hides properties whose `@hide` conditions pass.
473
+ * Multiple `@hide` decorators use OR semantics — any passing condition hides the property.
474
+ * Skipped when the `@message` decorator already added messages to the property.
475
+ *
476
+ * @param schemaDefinition - The schema definition to process
477
+ * @param decoratedClass - The decorated class instance carrying `@hide` metadata
478
+ */
479
+ applyHideDecorator(schemaDefinition, decoratedClass) {
480
+ for (const { propertyName, property } of this.iterateProperties(schemaDefinition, decoratedClass)) {
481
+ const hideConditions = Reflect.getMetadata(decoration_1.metadataKeys.hide, decoratedClass, propertyName);
482
+ if (hideConditions) {
483
+ // Hide when ANY condition IS met (OR semantics across multiple @hide decorators)
484
+ // But only if no messages were added by the message decorator
485
+ const hasMessages = Array.isArray(property[ux_specification_types_1.SchemaTag.messages]) && property[ux_specification_types_1.SchemaTag.messages].length > 0;
486
+ for (const condition of hideConditions) {
487
+ const { passed } = this.evaluateCondition(condition);
488
+ if (passed && !hasMessages) {
489
+ property[ux_specification_types_1.SchemaTag.hidden] = true;
490
+ break;
491
+ }
248
492
  }
249
- if (shouldHide) {
250
- if (dependsOnInfo.message) {
251
- this.addConditionalMessage(dependsOnInfo, syncRuleProviderInstance, property, {
493
+ }
494
+ }
495
+ }
496
+ /**
497
+ * Shows messages on properties whose `@message` conditions pass.
498
+ * Must run before `applyHideDecorator` so hide can detect existing messages.
499
+ *
500
+ * @param schemaDefinition - The schema definition to process
501
+ * @param decoratedClass - The decorated class instance carrying `@message` metadata
502
+ */
503
+ applyMessageDecorator(schemaDefinition, decoratedClass) {
504
+ for (const { propertyName, property } of this.iterateProperties(schemaDefinition, decoratedClass)) {
505
+ const messageConditions = Reflect.getMetadata(decoration_1.metadataKeys.message, decoratedClass, propertyName);
506
+ if (messageConditions) {
507
+ for (const condition of messageConditions) {
508
+ const { passed, context } = this.evaluateCondition(condition);
509
+ if (passed && (condition.message || condition.messageConfig)) {
510
+ this.addConditionalMessage(condition, decoratedClass, property, {
252
511
  propertyName,
253
512
  context
254
513
  });
255
514
  }
256
- // Alternative to hide: delete schemaDefinition.properties[propertyName];
257
- property[ux_specification_types_1.SchemaTag.hidden] = true;
258
515
  }
259
516
  }
260
517
  }
261
518
  }
262
519
  /**
263
- * Gets the validity metadata for a property.
520
+ * Marks properties as read-only whose `@readonly` conditions pass.
521
+ * Multiple `@readonly` decorators use OR semantics — any passing condition makes the property read-only.
522
+ *
523
+ * @param schemaDefinition - The schema definition to process
524
+ * @param decoratedClass - The decorated class instance carrying `@readonly` metadata
525
+ */
526
+ applyReadonlyDecorator(schemaDefinition, decoratedClass) {
527
+ for (const { propertyName, property } of this.iterateProperties(schemaDefinition, decoratedClass)) {
528
+ const readonlyConditions = Reflect.getMetadata(decoration_1.metadataKeys.readonly, decoratedClass, propertyName);
529
+ if (readonlyConditions) {
530
+ // Readonly when ANY condition IS met (OR semantics across multiple @readonly decorators)
531
+ for (const condition of readonlyConditions) {
532
+ const { passed } = this.evaluateCondition(condition);
533
+ if (passed) {
534
+ property.readOnly = true;
535
+ break;
536
+ }
537
+ }
538
+ }
539
+ }
540
+ }
541
+ /**
542
+ * Restricts enum values on properties whose `@enums` conditions pass.
543
+ * When multiple `@enums` decorators exist on the same property, the first matching condition wins.
544
+ *
545
+ * @param schemaDefinition - The schema definition to process
546
+ * @param decoratedClass - The decorated class instance carrying `@enums` metadata
547
+ */
548
+ applyEnumsDecorator(schemaDefinition, decoratedClass) {
549
+ for (const { propertyName, property } of this.iterateProperties(schemaDefinition, decoratedClass)) {
550
+ const enumsConditions = Reflect.getMetadata(decoration_1.metadataKeys.enums, decoratedClass, propertyName);
551
+ if (!enumsConditions?.length) {
552
+ continue;
553
+ }
554
+ // Find first matching condition (first match wins)
555
+ for (const condition of enumsConditions) {
556
+ const { passed } = this.evaluateCondition(condition);
557
+ if (passed) {
558
+ const currentEnumValues = this.resolveEnumFromProperty(property);
559
+ if (currentEnumValues) {
560
+ // Filter to only allowed values that exist in original enum
561
+ const filteredValues = condition.allowedValues.filter((v) => currentEnumValues.includes(v));
562
+ this.applyFilteredEnumToProperty(property, filteredValues);
563
+ }
564
+ break; // First match wins, stop processing
565
+ }
566
+ }
567
+ }
568
+ }
569
+ /**
570
+ * Reads the `@validity` metadata stored on a property of the decorated class.
264
571
  *
265
- * @param target - The target object
266
- * @param propertyName - The property name
267
- * @returns The validity metadata or undefined
572
+ * @param target - The decorated class instance
573
+ * @param propertyName - The property to read validity metadata from
574
+ * @returns The validity constraints (since, enum restrictions), or undefined if none
268
575
  */
269
576
  getValidityMetadata(target, propertyName) {
270
577
  let validityInfo;
@@ -274,66 +581,121 @@ class Decorator extends BaseClass {
274
581
  return validityInfo;
275
582
  }
276
583
  /**
277
- * Applies the validity decorator to the schema definition based on the sync rule provider instance and minimum UI5 version.
584
+ * Returns the enum values from a property definition, resolving `$ref` to a shared enum definition if needed.
278
585
  *
279
- * @param schemaDefinition The schema definition to apply the validity decorator to
280
- * @param syncRuleProviderInstance The instance of the sync rule provider
281
- * @param minUI5Version The minimum UI5 version to consider for validity checks
586
+ * @param property - The schema property definition (may have inline `enum` or a `$ref`)
587
+ * @returns The enum values array, or undefined if the property is not an enum
282
588
  */
283
- applyValidityDecorator(schemaDefinition, syncRuleProviderInstance, minUI5Version) {
284
- if (!schemaDefinition?.properties || !syncRuleProviderInstance || !minUI5Version) {
589
+ resolveEnumFromProperty(property) {
590
+ // Check for inline enum
591
+ if (property?.enum && Array.isArray(property.enum)) {
592
+ return property.enum;
593
+ }
594
+ // Check for $ref to an enum definition
595
+ if (property?.$ref) {
596
+ const refName = property.$ref.replace('#/definitions/', '');
597
+ const schema = this.appSchema?.get();
598
+ const refDef = schema?.definitions?.[refName];
599
+ if (refDef?.enum && Array.isArray(refDef.enum)) {
600
+ return refDef.enum;
601
+ }
602
+ }
603
+ return undefined;
604
+ }
605
+ /**
606
+ * Writes filtered enum values to a property definition.
607
+ * For `$ref` properties, inlines the enum, copies the type, and removes the `$ref`.
608
+ *
609
+ * @param property - The schema property definition to update
610
+ * @param filteredEnum - The allowed enum values after filtering
611
+ */
612
+ applyFilteredEnumToProperty(property, filteredEnum) {
613
+ if (property.$ref) {
614
+ // For $ref properties, resolve the referenced definition to get the type
615
+ const refName = property.$ref.replace('#/definitions/', '');
616
+ const schema = this.appSchema?.get();
617
+ const refDef = schema?.definitions?.[refName];
618
+ // Copy the type from the referenced definition if it exists
619
+ if (refDef?.type) {
620
+ property.type = refDef.type;
621
+ }
622
+ // Remove the $ref and inline the filtered enum
623
+ delete property.$ref;
624
+ property.enum = filteredEnum;
625
+ }
626
+ else {
627
+ // For inline enum properties, update directly
628
+ property.enum = filteredEnum;
629
+ }
630
+ }
631
+ /**
632
+ * Hides properties and filters enum values that require a UI5 version higher than the app's minimum.
633
+ *
634
+ * @param schemaDefinition - The schema definition to process
635
+ * @param decoratedClass - The decorated class instance carrying `@validity` metadata
636
+ * @param minUI5Version - The app's minimum UI5 version to check against
637
+ */
638
+ applyValidityDecorator(schemaDefinition, decoratedClass, minUI5Version) {
639
+ if (!schemaDefinition?.properties || !decoratedClass || !minUI5Version) {
285
640
  return;
286
641
  }
287
642
  for (const propertyName in schemaDefinition.properties) {
288
643
  const property = schemaDefinition.properties[propertyName];
289
- const validityInfo = this.getValidityMetadata(syncRuleProviderInstance, propertyName);
644
+ const validityInfo = this.getValidityMetadata(decoratedClass, propertyName);
290
645
  // Check if property has a 'since' requirement that exceeds the app's minUI5Version
291
646
  if (validityInfo?.since && !(0, utils_1.compareUI5Versions)(minUI5Version, validityInfo.since)) {
292
647
  property[ux_specification_types_1.SchemaTag.hidden] = true;
293
648
  // possible message?
294
649
  }
295
- // Process inline enum validity configuration from @validity decorator
296
- if (validityInfo?.enum && property?.enum && Array.isArray(property.enum)) {
297
- const filteredEnum = property.enum.filter((enumValue) => {
298
- const enumValueStr = String(enumValue);
299
- const condition = validityInfo.enum[enumValueStr];
300
- if (!condition) {
301
- return true; // Keep values without conditions
302
- }
303
- // Check UI5 version requirement (if specified)
304
- if (condition.since && minUI5Version) {
305
- if (!(0, utils_1.compareUI5Versions)(minUI5Version, condition.since)) {
306
- if (condition.message) {
307
- this.addConditionalMessage(condition, syncRuleProviderInstance, property, {
308
- propertyName: enumValueStr
309
- });
650
+ // Process enum validity configuration from @validity decorator
651
+ // Supports both inline enums and $ref to enum definitions
652
+ if (validityInfo?.enum) {
653
+ const enumValues = this.resolveEnumFromProperty(property);
654
+ if (enumValues) {
655
+ const filteredEnum = enumValues.filter((enumValue) => {
656
+ const enumValueStr = String(enumValue);
657
+ const condition = validityInfo.enum[enumValueStr];
658
+ if (!condition) {
659
+ return true; // Keep values without conditions
660
+ }
661
+ // Check UI5 version requirement (if specified)
662
+ if (condition.since && minUI5Version) {
663
+ if (!(0, utils_1.compareUI5Versions)(minUI5Version, condition.since)) {
664
+ if (condition.message) {
665
+ this.addConditionalMessage(condition, decoratedClass, property, {
666
+ propertyName: enumValueStr
667
+ });
668
+ }
669
+ return false; // Version requirement not met
310
670
  }
311
- return false; // Version requirement not met
312
671
  }
313
- }
314
- // Check property dependency using dependsOn function
315
- if (condition.dependsOn) {
316
- if (!condition.dependsOn(syncRuleProviderInstance)) {
317
- if (condition.message) {
318
- this.addConditionalMessage(condition, syncRuleProviderInstance, property, {
319
- propertyName: enumValueStr
320
- });
672
+ // Check property dependency using dependsOn function
673
+ if (condition.dependsOn) {
674
+ if (!condition.dependsOn(decoratedClass)) {
675
+ if (condition.message) {
676
+ this.addConditionalMessage(condition, decoratedClass, property, {
677
+ propertyName: enumValueStr
678
+ });
679
+ }
680
+ return false; // Dependency condition not met
321
681
  }
322
- return false; // Dependency condition not met
323
682
  }
683
+ return true;
684
+ });
685
+ // Only update if enum was actually filtered
686
+ if (filteredEnum.length !== enumValues.length) {
687
+ this.applyFilteredEnumToProperty(property, filteredEnum);
324
688
  }
325
- return true;
326
- });
327
- property.enum = filteredEnum;
689
+ }
328
690
  }
329
691
  }
330
692
  }
331
693
  /**
332
- * Applies the isViewNode decorator to the schema definition based on the target and property name.
694
+ * Marks the schema definition as a view node if the `@isViewNode` decorator is present on the target.
333
695
  *
334
- * @param schemaDefinition The schema definition to apply the isViewNode decorator to
335
- * @param target The target object or function
336
- * @param propertyName The property name (optional)
696
+ * @param schemaDefinition - The schema definition to tag
697
+ * @param target - The class constructor or prototype carrying the decorator
698
+ * @param propertyName - Optional property name for property-level decorators
337
699
  */
338
700
  applyIsViewNodeDecorator(schemaDefinition, target, propertyName) {
339
701
  const isViewNode = Reflect.getMetadata(decoration_1.metadataKeys.isViewNode, target, propertyName);
@@ -343,11 +705,11 @@ class Decorator extends BaseClass {
343
705
  }
344
706
  }
345
707
  /**
346
- * Applies the description decorator to the schema definition.
708
+ * Sets the schema `description` field from the `@description` decorator if present on the target.
347
709
  *
348
- * @param schemaDefinition - The schema definition to apply the decorator to
349
- * @param target - The target object or function
350
- * @param propertyName - The property name (optional)
710
+ * @param schemaDefinition - The schema definition to update
711
+ * @param target - The class constructor or prototype carrying the decorator
712
+ * @param propertyName - Optional property name for property-level decorators
351
713
  */
352
714
  applyDescriptionDecorator(schemaDefinition, target, propertyName) {
353
715
  const description = Reflect.getMetadata(decoration_1.metadataKeys.description, target, propertyName);
@@ -356,39 +718,17 @@ class Decorator extends BaseClass {
356
718
  }
357
719
  }
358
720
  /**
359
- * Applies the hidden decorator to the schema definition.
360
- *
361
- * @param schemaDefinition - The schema definition to apply the decorator to
362
- * @param target - The target object or function
363
- * @param propertyName - The property name (optional)
364
- */
365
- applyHiddenDecorator(schemaDefinition, target, propertyName) {
366
- const hiddenCondition = Reflect.getMetadata(decoration_1.metadataKeys.hidden, target, propertyName);
367
- if (typeof hiddenCondition === 'function') {
368
- const shouldHide = hiddenCondition(this);
369
- if (shouldHide) {
370
- schemaDefinition[ux_specification_types_1.SchemaTag.hidden] = true;
371
- }
372
- }
373
- else if (hiddenCondition === true) {
374
- schemaDefinition[ux_specification_types_1.SchemaTag.hidden] = true;
375
- }
376
- if (!schemaDefinition?.properties) {
377
- return;
378
- }
379
- for (const propertyName in schemaDefinition.properties) {
380
- this.applyHiddenDecorator(schemaDefinition.properties[propertyName], target, propertyName);
381
- }
382
- }
383
- /**
384
- * Applies all decorators to the schema definition.
721
+ * Applies all decorators to the schema in the correct order:
722
+ * description → isViewNode → validity → enums → message → hide → readonly.
723
+ * Message runs before hide so that hide can skip properties that already have messages.
385
724
  *
386
- * @param minUi5Version - The minimum UI5 version (optional)
387
- * @param propertyName - The property name (optional)
388
- * @returns Object containing the definition
725
+ * @param minUi5Version - The app's minimum UI5 version for validity checks
726
+ * @param propertyName - Optional property name when decorating a single property
727
+ * @param customDefinition - Override the schema definition (defaults to `getBase()`)
728
+ * @returns The decorated schema definition
389
729
  */
390
- applyDecorators(minUi5Version, propertyName) {
391
- const definition = this.getDefinitions()?.[this.getBaseName()];
730
+ applyDecorators(minUi5Version, propertyName, customDefinition) {
731
+ const definition = customDefinition ?? this.getBase();
392
732
  if (!definition) {
393
733
  return;
394
734
  }
@@ -399,10 +739,16 @@ class Decorator extends BaseClass {
399
739
  this.applyIsViewNodeDecorator(definition, target, propertyName);
400
740
  // Apply validity decorator (for properties)
401
741
  this.applyValidityDecorator(definition, this, minUi5Version);
402
- // Apply dependsOn decorator - check property dependencies
403
- this.applyDependsOnDecorator(definition, this);
404
- // Apply hidden decorator - evaluate condition function if provided
405
- this.applyHiddenDecorator(definition, this);
742
+ // Apply enums decorator - filter enum values based on conditions
743
+ this.applyEnumsDecorator(definition, this);
744
+ // Apply message decorator - show messages based on conditions
745
+ // NOTE: Message decorator must run before hide decorator so hide can check for messages
746
+ this.applyMessageDecorator(definition, this);
747
+ // Apply hide decorator - hide properties based on conditions
748
+ // NOTE: Only hides if message decorator did not add any messages
749
+ this.applyHideDecorator(definition, this);
750
+ // Apply readonly decorator - mark properties as readonly based on conditions
751
+ this.applyReadonlyDecorator(definition, this);
406
752
  return { definition };
407
753
  }
408
754
  }