@limetech/lime-crm-building-blocks 1.101.0 → 1.102.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 (113) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/lime-crm-building-blocks.cjs.js +1 -1
  3. package/dist/cjs/lime-query-validation-6be10fa7.js +558 -0
  4. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +4 -514
  5. package/dist/cjs/{limebb-lime-query-filter-builder_4.cjs.entry.js → limebb-lime-query-filter-builder_3.cjs.entry.js} +1 -243
  6. package/dist/cjs/limebb-lime-query-filter-comparison_2.cjs.entry.js +1 -1
  7. package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +2 -2
  8. package/dist/cjs/limebb-lime-query-order-by-item.cjs.entry.js +2 -2
  9. package/dist/cjs/limebb-lime-query-response-format-builder.cjs.entry.js +242 -0
  10. package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +322 -0
  11. package/dist/cjs/limebb-live-docs-info.cjs.entry.js +2 -2
  12. package/dist/cjs/limebb-locale-picker.cjs.entry.js +1 -1
  13. package/dist/cjs/limebb-mention-group-counter.cjs.entry.js +2 -2
  14. package/dist/cjs/limebb-navigation-button_2.cjs.entry.js +3 -3
  15. package/dist/cjs/limebb-notification-item.cjs.entry.js +1 -1
  16. package/dist/cjs/limebb-percentage-visualizer.cjs.entry.js +2 -2
  17. package/dist/cjs/limebb-text-editor.cjs.entry.js +1 -1
  18. package/dist/cjs/limebb-trend-indicator.cjs.entry.js +1 -1
  19. package/dist/cjs/loader.cjs.js +1 -1
  20. package/dist/collection/collection-manifest.json +3 -2
  21. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-comparison.js +1 -1
  22. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-group.js +1 -1
  23. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-not.js +1 -1
  24. package/dist/collection/components/lime-query-builder/lime-query-builder.js +1 -1
  25. package/dist/collection/components/lime-query-builder/lime-query-response-format-builder.css +91 -0
  26. package/dist/collection/components/lime-query-builder/lime-query-response-format-builder.js +355 -0
  27. package/dist/collection/components/lime-query-builder/lime-query-validation.js +40 -0
  28. package/dist/collection/components/lime-query-builder/limetype-field/limetype-field.js +1 -1
  29. package/dist/collection/components/lime-query-builder/order-by/order-by-item.js +2 -2
  30. package/dist/collection/components/lime-query-builder/{response-format-editor.css → response-format/response-format-editor.css} +1 -1
  31. package/dist/collection/components/lime-query-builder/{response-format-editor.js → response-format/response-format-editor.js} +5 -5
  32. package/dist/collection/components/lime-query-builder/response-format/response-format-helpers.js +92 -0
  33. package/dist/collection/components/lime-query-builder/{response-format-item.css → response-format/response-format-item.css} +1 -1
  34. package/dist/collection/components/lime-query-builder/{response-format-item.js → response-format/response-format-item.js} +6 -6
  35. package/dist/collection/components/limeobject/file-viewer/live-docs-info.js +2 -2
  36. package/dist/collection/components/locale-picker/locale-picker.js +1 -1
  37. package/dist/collection/components/notification-list/notification-item/notification-item.js +1 -1
  38. package/dist/collection/components/percentage-visualizer/percentage-visualizer.js +2 -2
  39. package/dist/collection/components/summary-popover/summary-popover.js +3 -3
  40. package/dist/collection/components/text-editor/mention-group-counter.js +2 -2
  41. package/dist/collection/components/text-editor/text-editor.js +1 -1
  42. package/dist/collection/components/trend-indicator/trend-indicator.js +1 -1
  43. package/dist/components/lime-query-filter-comparison.js +1 -1
  44. package/dist/components/lime-query-filter-expression.js +2 -2
  45. package/dist/components/lime-query-validation.js +555 -0
  46. package/dist/components/limebb-lime-query-builder.js +14 -524
  47. package/dist/components/limebb-lime-query-response-format-builder.d.ts +11 -0
  48. package/dist/components/limebb-lime-query-response-format-builder.js +283 -0
  49. package/dist/components/limebb-lime-query-response-format-editor.d.ts +11 -0
  50. package/dist/components/{limebb-response-format-editor.js → limebb-lime-query-response-format-editor.js} +2 -2
  51. package/dist/components/limebb-lime-query-response-format-item.d.ts +11 -0
  52. package/dist/components/{limebb-response-format-item.js → limebb-lime-query-response-format-item.js} +2 -2
  53. package/dist/components/limebb-locale-picker.js +1 -1
  54. package/dist/components/limebb-mention-group-counter.js +2 -2
  55. package/dist/components/limebb-percentage-visualizer.js +2 -2
  56. package/dist/components/limebb-text-editor.js +1 -1
  57. package/dist/components/limebb-trend-indicator.js +1 -1
  58. package/dist/components/limetype-field.js +1 -1
  59. package/dist/components/live-docs-info.js +2 -2
  60. package/dist/components/notification-item.js +1 -1
  61. package/dist/components/order-by-item.js +2 -2
  62. package/dist/components/response-format-editor.js +11 -11
  63. package/dist/components/response-format-item.js +9 -9
  64. package/dist/components/summary-popover.js +3 -3
  65. package/dist/esm/lime-crm-building-blocks.js +1 -1
  66. package/dist/esm/lime-query-validation-573223a5.js +555 -0
  67. package/dist/esm/limebb-lime-query-builder.entry.js +4 -514
  68. package/dist/esm/{limebb-lime-query-filter-builder_4.entry.js → limebb-lime-query-filter-builder_3.entry.js} +2 -243
  69. package/dist/esm/limebb-lime-query-filter-comparison_2.entry.js +1 -1
  70. package/dist/esm/limebb-lime-query-filter-group_3.entry.js +2 -2
  71. package/dist/esm/limebb-lime-query-order-by-item.entry.js +2 -2
  72. package/dist/esm/limebb-lime-query-response-format-builder.entry.js +238 -0
  73. package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +317 -0
  74. package/dist/esm/limebb-live-docs-info.entry.js +2 -2
  75. package/dist/esm/limebb-locale-picker.entry.js +1 -1
  76. package/dist/esm/limebb-mention-group-counter.entry.js +2 -2
  77. package/dist/esm/limebb-navigation-button_2.entry.js +3 -3
  78. package/dist/esm/limebb-notification-item.entry.js +1 -1
  79. package/dist/esm/limebb-percentage-visualizer.entry.js +2 -2
  80. package/dist/esm/limebb-text-editor.entry.js +1 -1
  81. package/dist/esm/limebb-trend-indicator.entry.js +1 -1
  82. package/dist/esm/loader.js +1 -1
  83. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  84. package/dist/lime-crm-building-blocks/{p-5cf4898d.entry.js → p-0de79b7f.entry.js} +1 -1
  85. package/dist/lime-crm-building-blocks/{p-8c2fb1c9.entry.js → p-0f7135ff.entry.js} +1 -1
  86. package/dist/lime-crm-building-blocks/{p-6aa216ec.entry.js → p-186e9f1a.entry.js} +1 -1
  87. package/dist/lime-crm-building-blocks/p-289ce8b9.entry.js +1 -0
  88. package/dist/lime-crm-building-blocks/p-3351395b.entry.js +1 -0
  89. package/dist/lime-crm-building-blocks/p-33e6d0ec.entry.js +1 -0
  90. package/dist/lime-crm-building-blocks/{p-a1ee8990.entry.js → p-3d1be1c9.entry.js} +1 -1
  91. package/dist/lime-crm-building-blocks/{p-92dfc5f8.entry.js → p-577d8909.entry.js} +1 -1
  92. package/dist/lime-crm-building-blocks/{p-ccf34631.entry.js → p-6579412e.entry.js} +1 -1
  93. package/dist/lime-crm-building-blocks/{p-d84874dc.entry.js → p-6f6fed59.entry.js} +1 -1
  94. package/dist/lime-crm-building-blocks/{p-0cd036ed.entry.js → p-7e5528f6.entry.js} +1 -1
  95. package/dist/lime-crm-building-blocks/{p-8601eab5.entry.js → p-a9ac501f.entry.js} +1 -1
  96. package/dist/lime-crm-building-blocks/p-be845252.entry.js +1 -0
  97. package/dist/lime-crm-building-blocks/{p-2725671e.entry.js → p-cb338753.entry.js} +1 -1
  98. package/dist/lime-crm-building-blocks/{p-425eaba2.entry.js → p-d0721b22.entry.js} +1 -1
  99. package/dist/lime-crm-building-blocks/p-fa2da6bc.js +1 -0
  100. package/dist/types/components/lime-query-builder/lime-query-response-format-builder.d.ts +102 -0
  101. package/dist/types/components/lime-query-builder/lime-query-validation.d.ts +13 -0
  102. package/dist/types/components/lime-query-builder/{response-format-editor.d.ts → response-format/response-format-editor.d.ts} +2 -2
  103. package/dist/types/components/lime-query-builder/response-format/response-format-helpers.d.ts +42 -0
  104. package/dist/types/components/lime-query-builder/{response-format-item.d.ts → response-format/response-format-item.d.ts} +2 -2
  105. package/dist/types/components.d.ts +394 -222
  106. package/package.json +1 -1
  107. package/dist/cjs/limebb-response-format-item.cjs.entry.js +0 -80
  108. package/dist/components/limebb-response-format-editor.d.ts +0 -11
  109. package/dist/components/limebb-response-format-item.d.ts +0 -11
  110. package/dist/esm/limebb-response-format-item.entry.js +0 -76
  111. package/dist/lime-crm-building-blocks/p-244ee55b.entry.js +0 -1
  112. package/dist/lime-crm-building-blocks/p-67c174d0.entry.js +0 -1
  113. package/dist/lime-crm-building-blocks/p-f9efca1d.entry.js +0 -1
@@ -0,0 +1,555 @@
1
+ import { Z as Zt } from './index.esm.js';
2
+ import { g as getPropertyFromPath, a as getNormalizedProperties } from './property-selector.js';
3
+
4
+ /**
5
+ * Dynamic filter values and placeholders that are valid in Lime Query
6
+ */
7
+ const VALID_DYNAMIC_VALUES = new Set([
8
+ // Temporal
9
+ '$yesterday',
10
+ '$now',
11
+ '$today',
12
+ '$tomorrow',
13
+ // Ranges
14
+ '$this_week',
15
+ '$this_month',
16
+ '$this_quarter',
17
+ '$this_year',
18
+ ]);
19
+ /**
20
+ * Pattern for relative date functions: $previous_day(x), $next_month(x), etc.
21
+ */
22
+ const RELATIVE_DATE_PATTERN = /^\$(previous|next)_(day|week|month|quarter|year)\(\d+\)$/;
23
+ /**
24
+ * Pattern for user context variables: $me, $me.office.name, etc.
25
+ */
26
+ const USER_CONTEXT_PATTERN = /^\$me(\.\w+)*$/;
27
+ /**
28
+ * Check if a value is a valid dynamic value or placeholder
29
+ * @param value
30
+ */
31
+ function isValidDynamicValue(value) {
32
+ if (typeof value !== 'string') {
33
+ return false;
34
+ }
35
+ return (VALID_DYNAMIC_VALUES.has(value) ||
36
+ RELATIVE_DATE_PATTERN.test(value) ||
37
+ USER_CONTEXT_PATTERN.test(value));
38
+ }
39
+ /**
40
+ * Validate a placeholder value
41
+ * @param value The value to check (might be a placeholder)
42
+ * @param activeLimetype The limetype of the active object
43
+ * @param limetypes Record of all available limetypes
44
+ * @returns Validation result with error message if invalid
45
+ */
46
+ function validatePlaceholder(value, activeLimetype, limetypes) {
47
+ // Check if it's a valid dynamic value ($today, $me, etc.)
48
+ if (isValidDynamicValue(value)) {
49
+ return { valid: true };
50
+ }
51
+ // Not a placeholder, no validation needed
52
+ if (typeof value !== 'string' || !value.startsWith('%activeObject%')) {
53
+ return { valid: true };
54
+ }
55
+ // Placeholder used but no active limetype specified
56
+ // This is always valid - activeLimetype is optional and only used for validation
57
+ if (!activeLimetype) {
58
+ return { valid: true };
59
+ }
60
+ // Extract property path from placeholder
61
+ const propertyPath = value.replace(/^%activeObject%\.?/, '');
62
+ // %activeObject% without property path is valid (references the ID)
63
+ if (!propertyPath) {
64
+ return { valid: true };
65
+ }
66
+ // Validate the property path exists on the active limetype
67
+ try {
68
+ const property = getPropertyFromPath(limetypes, activeLimetype, propertyPath);
69
+ if (!property) {
70
+ return {
71
+ valid: false,
72
+ error: `Property path '${propertyPath}' does not exist on limetype '${activeLimetype}'`,
73
+ };
74
+ }
75
+ // Path validation is already done by getPropertyFromPath
76
+ // If we can traverse the path successfully, it means all intermediate
77
+ // properties are valid single relations (belongsTo/hasOne).
78
+ // hasMany relations cannot be traversed in paths.
79
+ return { valid: true };
80
+ }
81
+ catch (error) {
82
+ return {
83
+ valid: false,
84
+ error: `Invalid placeholder path: ${error.message}`,
85
+ };
86
+ }
87
+ }
88
+ /**
89
+ * Validate a response format against limetype schemas
90
+ * Throws errors for invalid property references
91
+ * Returns GUI limitations for features not yet supported in GUI
92
+ * @param responseFormat
93
+ * @param limetypes Record of all available limetypes
94
+ * @param limetype The root limetype for this query
95
+ * @param guiModeEnabled Whether GUI mode is enabled
96
+ * @returns GUI limitations found (if any)
97
+ */
98
+ function validateResponseFormat(responseFormat, limetypes, limetype, guiModeEnabled = true) {
99
+ const guiLimitations = [];
100
+ // Check for GUI-unsupported features
101
+ if (guiModeEnabled && responseFormat.aggregates) {
102
+ guiLimitations.push('responseFormat.aggregates is not yet supported in GUI mode');
103
+ }
104
+ // Validate object properties (throws on invalid properties)
105
+ if (responseFormat.object) {
106
+ const objectLimitations = validatePropertySelection(responseFormat.object, limetypes, limetype, guiModeEnabled);
107
+ guiLimitations.push(...objectLimitations);
108
+ }
109
+ return guiLimitations;
110
+ }
111
+ /**
112
+ * Extract non-metadata keys from a property value object
113
+ * Filters out _alias and all # properties (which are treated as comments)
114
+ * @param propValue Property value object
115
+ * @param propName Property name (for error messages)
116
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
117
+ * @returns Object with non-metadata keys and any GUI limitations found
118
+ */
119
+ function extractNonMetadataKeys(propValue, propName, guiModeEnabled = true) {
120
+ const keys = Object.keys(propValue);
121
+ const guiLimitations = [];
122
+ // Check for # properties other than #description (GUI limitation)
123
+ if (guiModeEnabled) {
124
+ const unsupportedHashProps = keys.filter((k) => k.startsWith('#') && k !== '#description');
125
+ if (unsupportedHashProps.length > 0) {
126
+ guiLimitations.push(`Property '${propName}' contains # properties not supported in GUI: ${unsupportedHashProps.join(', ')}`);
127
+ }
128
+ }
129
+ // Filter out _alias and all # properties (they are metadata/comments)
130
+ const nonMetadataKeys = keys.filter((k) => k !== '_alias' && !k.startsWith('#'));
131
+ return { keys: nonMetadataKeys, guiLimitations };
132
+ }
133
+ /**
134
+ * Validate a relation property value
135
+ * @param propName Property name
136
+ * @param propValue Property value
137
+ * @param limetypes Record of all available limetypes
138
+ * @param property
139
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
140
+ * @returns GUI limitations found (if any)
141
+ */
142
+ function validateRelationProperty(propName, propValue, limetypes, property, guiModeEnabled = true) {
143
+ // null is valid - just return the relation ID
144
+ if (propValue === null) {
145
+ return [];
146
+ }
147
+ if (typeof propValue !== 'object') {
148
+ throw new TypeError(`Relation property '${propName}' must be null or an object`);
149
+ }
150
+ const propValueObj = propValue;
151
+ const { keys: otherKeys, guiLimitations } = extractNonMetadataKeys(propValueObj, propName, guiModeEnabled);
152
+ // If it's just {} or { _alias: "...", "#...": "..." }, that's valid
153
+ if (otherKeys.length === 0) {
154
+ return guiLimitations;
155
+ }
156
+ // Otherwise, validate nested properties
157
+ const relatedLimetype = property.relation.getLimetype();
158
+ if (!relatedLimetype) {
159
+ throw new Error(`Could not determine related limetype for property '${propName}'`);
160
+ }
161
+ // Build a clean PropertySelection without metadata for recursive validation
162
+ const cleanSelection = {};
163
+ for (const key of otherKeys) {
164
+ cleanSelection[key] = propValueObj[key];
165
+ }
166
+ const nestedLimitations = validatePropertySelection(cleanSelection, limetypes, relatedLimetype.name, guiModeEnabled);
167
+ return [...guiLimitations, ...nestedLimitations];
168
+ }
169
+ /**
170
+ * Validate a non-relation property value
171
+ * @param propName Property name
172
+ * @param propValue Property value
173
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
174
+ * @returns GUI limitations found (if any)
175
+ */
176
+ function validateNonRelationProperty(propName, propValue, guiModeEnabled = true) {
177
+ // null is valid
178
+ if (propValue === null) {
179
+ return [];
180
+ }
181
+ // Allow empty object {} or object with only metadata (_alias, #...)
182
+ if (typeof propValue === 'object') {
183
+ const { keys: nonMetadataKeys, guiLimitations } = extractNonMetadataKeys(propValue, propName, guiModeEnabled);
184
+ if (nonMetadataKeys.length === 0) {
185
+ // {} or { _alias: "...", "#...": "..." } is valid
186
+ return guiLimitations;
187
+ }
188
+ throw new Error(`Non-relation property '${propName}' cannot have nested properties other than _alias or # properties (got: ${nonMetadataKeys.join(', ')})`);
189
+ }
190
+ throw new Error(`Non-relation property '${propName}' must be null or an object (got ${typeof propValue})`);
191
+ }
192
+ /**
193
+ * Validate a single property entry
194
+ * @param propName Property name
195
+ * @param propValue Property value
196
+ * @param normalizedProperties Normalized properties of the limetype
197
+ * @param limetypes Record of all available limetypes
198
+ * @param limetype Current limetype name
199
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
200
+ * @returns GUI limitations found (if any)
201
+ */
202
+ function validateSinglePropertyEntry(propName, propValue, normalizedProperties, limetypes, limetype, guiModeEnabled = true) {
203
+ // Allow empty string (editing state in GUI)
204
+ if (propName === '') {
205
+ // Only validate empty property name in GUI mode
206
+ if (guiModeEnabled && propValue !== null) {
207
+ throw new Error('Empty property name must have null value');
208
+ }
209
+ return [];
210
+ }
211
+ // Validate property exists on limetype
212
+ const property = normalizedProperties[propName];
213
+ if (!property) {
214
+ throw new Error(`Property '${propName}' does not exist on limetype '${limetype}'`);
215
+ }
216
+ // Validate value based on property type
217
+ if (property.relation) {
218
+ return validateRelationProperty(propName, propValue, limetypes, property, guiModeEnabled);
219
+ }
220
+ else {
221
+ return validateNonRelationProperty(propName, propValue, guiModeEnabled);
222
+ }
223
+ }
224
+ /**
225
+ * Validate a property selection object against a limetype schema
226
+ * Recursively validates nested property selections for relations
227
+ * @param selection
228
+ * @param limetypes Record of all available limetypes
229
+ * @param limetype The limetype for this level of the selection
230
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
231
+ * @returns GUI limitations found (if any)
232
+ */
233
+ function validatePropertySelection(selection, limetypes, limetype, guiModeEnabled = true) {
234
+ const limetypeObj = limetypes[limetype];
235
+ if (!limetypeObj) {
236
+ throw new Error(`Unknown limetype: ${limetype}`);
237
+ }
238
+ const normalizedProperties = getNormalizedProperties(limetypeObj);
239
+ const allGuiLimitations = [];
240
+ for (const [propName, propValue] of Object.entries(selection)) {
241
+ // Skip # properties (comments/metadata that backend accepts via unknown=INCLUDE)
242
+ // Note: _alias only appears inside property value objects, not at this level
243
+ if (propName.startsWith('#')) {
244
+ continue;
245
+ }
246
+ const limitations = validateSinglePropertyEntry(propName, propValue, normalizedProperties, limetypes, limetype, guiModeEnabled);
247
+ allGuiLimitations.push(...limitations);
248
+ }
249
+ return allGuiLimitations;
250
+ }
251
+ /**
252
+ * Validate a comparison expression (has 'key' property)
253
+ * @param filter
254
+ * @param activeLimetype
255
+ * @param limetypes
256
+ */
257
+ function validateComparisonExpression(filter, activeLimetype, limetypes) {
258
+ // Validate operator
259
+ const allValidOperators = Object.values(Zt);
260
+ if (!allValidOperators.includes(filter.op)) {
261
+ throw new Error(`Unsupported filter operator: ${filter.op}`);
262
+ }
263
+ // Validate placeholder
264
+ const result = validatePlaceholder(filter.exp, activeLimetype, limetypes);
265
+ if (!result.valid) {
266
+ throw new Error(`Invalid placeholder in filter '${filter.key}': ${result.error}`);
267
+ }
268
+ }
269
+ /**
270
+ * Validate a group expression (AND/OR/NOT)
271
+ * @param filter
272
+ * @param activeLimetype
273
+ * @param limetypes
274
+ * @param guiModeEnabled
275
+ */
276
+ function validateGroupExpression(filter, activeLimetype, limetypes, guiModeEnabled) {
277
+ // Validate operator
278
+ if (filter.op !== Zt.AND &&
279
+ filter.op !== Zt.OR &&
280
+ filter.op !== Zt.NOT) {
281
+ throw new Error(`Unsupported group operator: ${filter.op}`);
282
+ }
283
+ // Recursively validate children
284
+ if (filter.op === Zt.NOT) {
285
+ validateFilterPlaceholders(filter.exp, activeLimetype, limetypes, guiModeEnabled);
286
+ }
287
+ else if (filter.op === Zt.AND || filter.op === Zt.OR) {
288
+ const expressions = filter.exp;
289
+ for (const expr of expressions) {
290
+ validateFilterPlaceholders(expr, activeLimetype, limetypes, guiModeEnabled);
291
+ }
292
+ }
293
+ }
294
+ /**
295
+ * Validate placeholders in a filter expression
296
+ * @param filter Filter expression to validate
297
+ * @param activeLimetype The limetype of the active object
298
+ * @param limetypes Record of all available limetypes
299
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
300
+ */
301
+ function validateFilterPlaceholders(filter, activeLimetype, limetypes, guiModeEnabled = true) {
302
+ if (!filter) {
303
+ return;
304
+ }
305
+ if ('key' in filter) {
306
+ validateComparisonExpression(filter, activeLimetype, limetypes);
307
+ return;
308
+ }
309
+ if ('exp' in filter) {
310
+ validateGroupExpression(filter, activeLimetype, limetypes, guiModeEnabled);
311
+ }
312
+ }
313
+ /**
314
+ * Validate Lime Query filter and collect errors
315
+ * @param filter The filter expression or group to validate
316
+ * @param activeLimetype Optional active object limetype for placeholder validation
317
+ * @param limetypes Record of all available limetypes
318
+ * @param guiModeEnabled Whether GUI mode is enabled
319
+ * @returns Array of validation error messages
320
+ */
321
+ function validateLimeQueryFilterInternal(filter, activeLimetype, limetypes, guiModeEnabled) {
322
+ const errors = [];
323
+ try {
324
+ validateFilterPlaceholders(filter, activeLimetype, limetypes, guiModeEnabled);
325
+ }
326
+ catch (error) {
327
+ errors.push(`Invalid filter: ${error.message}`);
328
+ }
329
+ return errors;
330
+ }
331
+ /**
332
+ * Validate orderBy specification
333
+ * @param orderBy Array of orderBy items to validate
334
+ * @param limetypes Record of all available limetypes
335
+ * @param limetype The limetype for this Lime Query
336
+ * @returns Array of validation error messages
337
+ */
338
+ function validateOrderBy(orderBy, limetypes, limetype) {
339
+ const errors = [];
340
+ if (!Array.isArray(orderBy)) {
341
+ errors.push('orderBy must be an array');
342
+ return errors;
343
+ }
344
+ if (!limetype || !limetypes[limetype]) {
345
+ // Can't validate property paths without limetype
346
+ return errors;
347
+ }
348
+ for (const [index, item] of orderBy.entries()) {
349
+ const itemErrors = validateOrderByItem(item, index, limetypes, limetype);
350
+ errors.push(...itemErrors);
351
+ }
352
+ return errors;
353
+ }
354
+ /**
355
+ * Validate a single orderBy item
356
+ * @param item The orderBy item to validate
357
+ * @param index The index of the item in the array (for error messages)
358
+ * @param limetypes Record of all available limetypes
359
+ * @param limetype The limetype for this Lime Query
360
+ * @returns Array of validation error messages for this item
361
+ */
362
+ function validateOrderByItem(item, index, limetypes, limetype) {
363
+ const errors = [];
364
+ const objectError = validateOrderByItemIsObject(item, index);
365
+ if (objectError) {
366
+ return [objectError];
367
+ }
368
+ const keys = Object.keys(item);
369
+ const keyError = validateOrderByItemHasSingleKey(keys, index);
370
+ if (keyError) {
371
+ return [keyError];
372
+ }
373
+ const propertyPath = keys[0];
374
+ const direction = item[propertyPath];
375
+ const directionError = validateOrderByDirection(direction, index);
376
+ if (directionError) {
377
+ errors.push(directionError);
378
+ }
379
+ const pathError = validateOrderByPropertyPath(propertyPath, limetypes, limetype, index);
380
+ if (pathError) {
381
+ errors.push(pathError);
382
+ }
383
+ return errors;
384
+ }
385
+ /**
386
+ * Validate that the orderBy item is an object
387
+ * @param item
388
+ * @param index
389
+ */
390
+ function validateOrderByItemIsObject(item, index) {
391
+ if (typeof item !== 'object' || item === null) {
392
+ return `orderBy[${index}] must be an object`;
393
+ }
394
+ return null;
395
+ }
396
+ /**
397
+ * Validate that the orderBy item has exactly one property key
398
+ * @param keys
399
+ * @param index
400
+ */
401
+ function validateOrderByItemHasSingleKey(keys, index) {
402
+ if (keys.length === 0) {
403
+ return `orderBy[${index}] must have a property path`;
404
+ }
405
+ if (keys.length > 1) {
406
+ return `orderBy[${index}] must have exactly one property, got ${keys.length}`;
407
+ }
408
+ return null;
409
+ }
410
+ /**
411
+ * Validate that the sort direction is either ASC or DESC
412
+ * @param direction
413
+ * @param index
414
+ */
415
+ function validateOrderByDirection(direction, index) {
416
+ if (direction !== 'ASC' && direction !== 'DESC') {
417
+ return `orderBy[${index}]: direction must be 'ASC' or 'DESC', got '${direction}'`;
418
+ }
419
+ return null;
420
+ }
421
+ /**
422
+ * Validate that the property path exists on the limetype
423
+ * @param propertyPath
424
+ * @param limetypes
425
+ * @param limetype
426
+ * @param index
427
+ */
428
+ function validateOrderByPropertyPath(propertyPath, limetypes, limetype, index) {
429
+ if (!propertyPath || propertyPath === '') {
430
+ return null;
431
+ }
432
+ const property = getPropertyFromPath(limetypes, limetype, propertyPath);
433
+ if (!property) {
434
+ return `orderBy[${index}]: property path '${propertyPath}' does not exist on limetype '${limetype}'`;
435
+ }
436
+ return null;
437
+ }
438
+ /**
439
+ * Validate Lime Query response format and collect errors/limitations
440
+ * @param responseFormat The response format to validate
441
+ * @param limetypes Record of all available limetypes
442
+ * @param limetype The limetype for this Lime Query
443
+ * @param guiModeEnabled Whether GUI mode is enabled
444
+ * @returns Object with validation errors and GUI limitations
445
+ */
446
+ function validateLimeQueryResponseFormatInternal(responseFormat, limetypes, limetype, guiModeEnabled) {
447
+ const errors = [];
448
+ const limitations = [];
449
+ try {
450
+ const formatLimitations = validateResponseFormat(responseFormat, limetypes, limetype, guiModeEnabled);
451
+ limitations.push(...formatLimitations);
452
+ }
453
+ catch (error) {
454
+ errors.push(`Invalid responseFormat: ${error.message}`);
455
+ }
456
+ return { errors, limitations };
457
+ }
458
+ /**
459
+ * Validate a Lime Query
460
+ * Returns validation result with separate arrays for validity errors and GUI limitations
461
+ * @param limeQuery The Lime Query to validate
462
+ * @param limetypes Record of all available limetypes
463
+ * @param activeLimetype Optional active object limetype for placeholder validation
464
+ * @param guiModeEnabled Whether GUI mode is enabled (affects validation)
465
+ * @returns LimeQueryValidationResult with validity status and any errors/limitations
466
+ */
467
+ function isLimeQuerySupported(limeQuery, limetypes, activeLimetype, guiModeEnabled = true) {
468
+ // Handle empty/undefined Lime Query
469
+ if (!limeQuery) {
470
+ return {
471
+ valid: true,
472
+ guiSupported: true,
473
+ validationErrors: [],
474
+ guiLimitations: [],
475
+ };
476
+ }
477
+ const validationErrors = [];
478
+ const guiLimitations = [];
479
+ // Validate limetype exists
480
+ if (limeQuery.limetype && !limetypes[limeQuery.limetype]) {
481
+ validationErrors.push(`Unknown limetype: ${limeQuery.limetype}`);
482
+ }
483
+ // Check for offset without orderBy (Lime Query requirement)
484
+ if (limeQuery.offset !== undefined && !limeQuery.orderBy) {
485
+ validationErrors.push('offset requires orderBy to be specified');
486
+ }
487
+ // Validate orderBy
488
+ if (limeQuery.orderBy) {
489
+ const orderByErrors = validateOrderBy(limeQuery.orderBy, limetypes, limeQuery.limetype);
490
+ validationErrors.push(...orderByErrors);
491
+ }
492
+ // Check for GUI-unsupported top-level properties
493
+ if (guiModeEnabled && limeQuery.offset !== undefined) {
494
+ guiLimitations.push('offset is not yet supported in GUI mode');
495
+ }
496
+ // Validate filter
497
+ if (limeQuery.filter) {
498
+ const filterErrors = validateLimeQueryFilterInternal(limeQuery.filter, activeLimetype, limetypes, guiModeEnabled);
499
+ validationErrors.push(...filterErrors);
500
+ }
501
+ // Validate responseFormat
502
+ if (limeQuery.responseFormat) {
503
+ const { errors, limitations } = validateLimeQueryResponseFormatInternal(limeQuery.responseFormat, limetypes, limeQuery.limetype, guiModeEnabled);
504
+ validationErrors.push(...errors);
505
+ guiLimitations.push(...limitations);
506
+ }
507
+ return {
508
+ valid: validationErrors.length === 0,
509
+ guiSupported: guiLimitations.length === 0,
510
+ validationErrors,
511
+ guiLimitations,
512
+ };
513
+ }
514
+ // ============================================================================
515
+ // Specialized Validators for Modular Builders
516
+ // ============================================================================
517
+ /**
518
+ * Validate a ResponseFormat in isolation (for response-format-builder)
519
+ *
520
+ * This validator is designed for the response-format-builder component,
521
+ * which only handles ResponseFormat editing without the full LimeQuery context.
522
+ *
523
+ * @param responseFormat - The response format to validate
524
+ * @param limetypes - Record of all available limetypes
525
+ * @param limetype - The limetype context for validation
526
+ * @param guiModeEnabled - Whether GUI mode is enabled (affects validation)
527
+ * @returns Validation result with errors and GUI limitations
528
+ */
529
+ function validateResponseFormatOnly(responseFormat, limetypes, limetype, guiModeEnabled = true) {
530
+ const validationErrors = [];
531
+ const guiLimitations = [];
532
+ // Validate limetype exists
533
+ if (!limetypes[limetype]) {
534
+ validationErrors.push(`Unknown limetype: ${limetype}`);
535
+ // Can't proceed with property validation if limetype is unknown
536
+ return {
537
+ valid: false,
538
+ guiSupported: false,
539
+ validationErrors,
540
+ guiLimitations,
541
+ };
542
+ }
543
+ // Use internal validation logic
544
+ const { errors, limitations } = validateLimeQueryResponseFormatInternal(responseFormat, limetypes, limetype, guiModeEnabled);
545
+ validationErrors.push(...errors);
546
+ guiLimitations.push(...limitations);
547
+ return {
548
+ valid: validationErrors.length === 0,
549
+ guiSupported: guiLimitations.length === 0,
550
+ validationErrors,
551
+ guiLimitations,
552
+ };
553
+ }
554
+
555
+ export { isLimeQuerySupported as i, validateResponseFormatOnly as v };