@qoretechnologies/reqraft 0.10.46-pr.95.g68dab3c → 0.10.46

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 (147) hide show
  1. package/.claude/CLAUDE.md +12 -0
  2. package/.tasks/INDEX.md +1 -0
  3. package/.tasks/TEMPLATE_TOKEN_GRAMMAR.md +72 -0
  4. package/dist/components/composer/Composer.d.ts.map +1 -1
  5. package/dist/components/composer/Composer.js +5 -1
  6. package/dist/components/composer/Composer.js.map +1 -1
  7. package/dist/components/form/engine/CompactRow.d.ts +14 -0
  8. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  9. package/dist/components/form/engine/CompactRow.js +199 -41
  10. package/dist/components/form/engine/CompactRow.js.map +1 -1
  11. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  12. package/dist/components/form/engine/CompactToolbar.js +72 -72
  13. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  14. package/dist/components/form/engine/FormEngine.d.ts +65 -1
  15. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  16. package/dist/components/form/engine/FormEngine.js +276 -108
  17. package/dist/components/form/engine/FormEngine.js.map +1 -1
  18. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  19. package/dist/components/form/engine/_structuredData/SchemaDataView.js +55 -13
  20. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  21. package/dist/components/form/engine/_structuredData/structuredData.d.ts +15 -0
  22. package/dist/components/form/engine/_structuredData/structuredData.d.ts.map +1 -1
  23. package/dist/components/form/engine/_structuredData/structuredData.js +22 -1
  24. package/dist/components/form/engine/_structuredData/structuredData.js.map +1 -1
  25. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  26. package/dist/components/form/engine/compactRowStyles.js +15 -3
  27. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  28. package/dist/components/form/engine/compactToolbarContext.d.ts +8 -0
  29. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  30. package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
  31. package/dist/components/form/engine/readFirst.d.ts +24 -0
  32. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  33. package/dist/components/form/engine/readFirst.js +84 -6
  34. package/dist/components/form/engine/readFirst.js.map +1 -1
  35. package/dist/components/form/fields/Field.d.ts.map +1 -1
  36. package/dist/components/form/fields/Field.js +1 -1
  37. package/dist/components/form/fields/Field.js.map +1 -1
  38. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +12 -2
  39. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  40. package/dist/components/form/fields/allowed-values/AllowedValues.js +16 -6
  41. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  42. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  43. package/dist/components/form/fields/auto/AutoFormField.js +64 -14
  44. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  45. package/dist/components/form/fields/long-string/LongString.d.ts +9 -1
  46. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  47. package/dist/components/form/fields/long-string/LongString.js +25 -4
  48. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  49. package/dist/components/form/fields/template/ExampleValueModal.d.ts +29 -0
  50. package/dist/components/form/fields/template/ExampleValueModal.d.ts.map +1 -0
  51. package/dist/components/form/fields/template/ExampleValueModal.js +122 -0
  52. package/dist/components/form/fields/template/ExampleValueModal.js.map +1 -0
  53. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  54. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -3
  55. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  56. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  57. package/dist/components/form/fields/template/TemplateField.js +81 -27
  58. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  59. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +2 -0
  60. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +1 -0
  61. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +9 -0
  62. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +1 -0
  63. package/dist/components/form/index.d.ts +1 -0
  64. package/dist/components/form/index.d.ts.map +1 -1
  65. package/dist/components/form/index.js +1 -0
  66. package/dist/components/form/index.js.map +1 -1
  67. package/dist/components/searchFilterBar/SearchFilterBar.d.ts +12 -1
  68. package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -1
  69. package/dist/components/searchFilterBar/SearchFilterBar.js +2 -2
  70. package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -1
  71. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
  72. package/dist/components/ticketThread/TicketThread.js +1 -1
  73. package/dist/components/ticketThread/TicketThread.js.map +1 -1
  74. package/dist/helpers/common.d.ts +8 -1
  75. package/dist/helpers/common.d.ts.map +1 -1
  76. package/dist/helpers/common.js +9 -1
  77. package/dist/helpers/common.js.map +1 -1
  78. package/dist/helpers/expressions.d.ts.map +1 -1
  79. package/dist/helpers/expressions.js +15 -2
  80. package/dist/helpers/expressions.js.map +1 -1
  81. package/dist/helpers/optionUiTypes.d.ts +30 -0
  82. package/dist/helpers/optionUiTypes.d.ts.map +1 -0
  83. package/dist/helpers/optionUiTypes.js +156 -0
  84. package/dist/helpers/optionUiTypes.js.map +1 -0
  85. package/dist/helpers/options.d.ts +19 -0
  86. package/dist/helpers/options.d.ts.map +1 -1
  87. package/dist/helpers/options.js +25 -1
  88. package/dist/helpers/options.js.map +1 -1
  89. package/dist/helpers/providerValue.d.ts +68 -0
  90. package/dist/helpers/providerValue.d.ts.map +1 -0
  91. package/dist/helpers/providerValue.js +50 -0
  92. package/dist/helpers/providerValue.js.map +1 -0
  93. package/dist/helpers/singleLineString.d.ts +37 -0
  94. package/dist/helpers/singleLineString.d.ts.map +1 -0
  95. package/dist/helpers/singleLineString.js +66 -0
  96. package/dist/helpers/singleLineString.js.map +1 -0
  97. package/dist/helpers/templates.d.ts +100 -0
  98. package/dist/helpers/templates.d.ts.map +1 -1
  99. package/dist/helpers/templates.js +237 -4
  100. package/dist/helpers/templates.js.map +1 -1
  101. package/dist/helpers/validations.d.ts +89 -2
  102. package/dist/helpers/validations.d.ts.map +1 -1
  103. package/dist/helpers/validations.js +801 -89
  104. package/dist/helpers/validations.js.map +1 -1
  105. package/dist/index.d.ts +1 -1
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +2 -1
  108. package/dist/index.js.map +1 -1
  109. package/package.json +1 -1
  110. package/src/components/composer/Composer.tsx +7 -3
  111. package/src/components/form/engine/CompactRow.tsx +279 -33
  112. package/src/components/form/engine/CompactToolbar.tsx +105 -98
  113. package/src/components/form/engine/FormEngine.stories.tsx +995 -12
  114. package/src/components/form/engine/FormEngine.tsx +377 -94
  115. package/src/components/form/engine/_structuredData/SchemaDataView.stories.tsx +104 -0
  116. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +158 -101
  117. package/src/components/form/engine/_structuredData/structuredData.ts +20 -0
  118. package/src/components/form/engine/compactRowStyles.ts +55 -1
  119. package/src/components/form/engine/compactToolbarContext.ts +8 -0
  120. package/src/components/form/engine/readFirst.ts +125 -35
  121. package/src/components/form/fields/Field.tsx +3 -1
  122. package/src/components/form/fields/allowed-values/AllowedValues.tsx +24 -2
  123. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +81 -0
  124. package/src/components/form/fields/auto/AutoFormField.stories.tsx +47 -1
  125. package/src/components/form/fields/auto/AutoFormField.tsx +73 -11
  126. package/src/components/form/fields/long-string/LongString.tsx +46 -5
  127. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +5 -0
  128. package/src/components/form/fields/template/ExampleValueModal.stories.tsx +114 -0
  129. package/src/components/form/fields/template/ExampleValueModal.tsx +99 -0
  130. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +8 -4
  131. package/src/components/form/fields/template/TemplateField.stories.tsx +389 -1
  132. package/src/components/form/fields/template/TemplateField.tsx +100 -35
  133. package/src/components/form/fields/template/__fixtures__/longExampleValue.ts +7 -0
  134. package/src/components/form/index.tsx +1 -0
  135. package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +62 -0
  136. package/src/components/searchFilterBar/SearchFilterBar.tsx +13 -0
  137. package/src/components/ticketThread/TicketThread.tsx +7 -0
  138. package/src/helpers/common.ts +11 -1
  139. package/src/helpers/expressions.ts +16 -2
  140. package/src/helpers/optionUiTypes.ts +161 -0
  141. package/src/helpers/options.ts +23 -0
  142. package/src/helpers/providerValue.ts +125 -0
  143. package/src/helpers/singleLineString.ts +61 -0
  144. package/src/helpers/templates.ts +272 -3
  145. package/src/helpers/validations.ts +1118 -107
  146. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +23 -2
  147. package/src/index.tsx +1 -0
@@ -23,7 +23,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
23
23
  return (mod && mod.__esModule) ? mod : { "default": mod };
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.hasAllDependenciesFullfilled = exports.validateOptionWithRequiredGroups = exports.getTypeFromValue = exports.getValueOrDefaultValue = exports.isValueSet = exports.maybeParseYaml = exports.validateField = exports.validateFieldWithResult = exports.readArgValue = exports._validateField = exports.cronExpressionFromValue = void 0;
26
+ exports.hasAllDependenciesFullfilled = exports.parseDependency = exports.getUnresolvedRequiredOptions = exports.getUnresolvedRequiredOptionReason = exports.validateOptionWithRequiredGroups = exports.getTypeFromValue = exports.getValueOrDefaultValue = exports.isValueSet = exports.maybeParseYaml = exports.validateField = exports.validateFieldWithResult = exports.readArgValue = exports._validateField = exports.setExpressionCatalogueReader = exports.cronExpressionFromValue = void 0;
27
27
  var colors_1 = require("@qoretechnologies/reqore/dist/helpers/colors");
28
28
  var cron_validator_1 = require("cron-validator");
29
29
  var js_yaml_1 = __importDefault(require("js-yaml"));
@@ -35,8 +35,11 @@ var isNaN_1 = __importDefault(require("lodash/isNaN"));
35
35
  var isNumber_1 = __importDefault(require("lodash/isNumber"));
36
36
  var isPlainObject_1 = __importDefault(require("lodash/isPlainObject"));
37
37
  var size_1 = __importDefault(require("lodash/size"));
38
+ var uniqWith_1 = __importDefault(require("lodash/uniqWith"));
38
39
  var common_1 = require("./common");
40
+ var optionUiTypes_1 = require("./optionUiTypes");
39
41
  var options_1 = require("./options");
42
+ var providerValue_1 = require("./providerValue");
40
43
  var templates_1 = require("./templates");
41
44
  /** The five cron fields, in order, as a schedule hash may name them. */
42
45
  var CRON_FIELDS = [
@@ -84,6 +87,22 @@ var cronExpressionFromValue = function (value) {
84
87
  : parts.join(' ');
85
88
  };
86
89
  exports.cronExpressionFromValue = cronExpressionFromValue;
90
+ var expressionCatalogueReader;
91
+ var setExpressionCatalogueReader = function (reader) {
92
+ expressionCatalogueReader = reader;
93
+ };
94
+ exports.setExpressionCatalogueReader = setExpressionCatalogueReader;
95
+ var readExpressionCatalogue = function (field) { var _a, _b; return (_b = (_a = field === null || field === void 0 ? void 0 : field.expressions) !== null && _a !== void 0 ? _a : expressionCatalogueReader === null || expressionCatalogueReader === void 0 ? void 0 : expressionCatalogueReader()) !== null && _b !== void 0 ? _b : []; };
96
+ // permissive patterns for the semantic string formats — mirror the qore DataProvider
97
+ // QoreStringFormatDataType server backstop (email/uuid/hostname/ipv4/ipv6/phone)
98
+ var SEMANTIC_FORMAT_PATTERNS = {
99
+ email: /^[^@\s]+@[^@\s]+$/,
100
+ uuid: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,
101
+ hostname: /^[A-Za-z0-9.-]+$/,
102
+ ipv4: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
103
+ ipv6: /^([0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}$/,
104
+ phone: /^\+?[0-9 ().-]{3,}$/,
105
+ };
87
106
  var validResult = function () { return ({ isValid: true, reasons: [] }); };
88
107
  var invalidResult = function (reason, childReasons) {
89
108
  if (childReasons === void 0) { childReasons = []; }
@@ -120,7 +139,7 @@ var isValueDefined = function (type, value) {
120
139
  return value !== undefined && value !== '';
121
140
  };
122
141
  var _validateField = function (type, value, field, canBeNull) {
123
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
142
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
124
143
  if (!type) {
125
144
  if (value === undefined || value === null) {
126
145
  return invalidResult('Missing value');
@@ -136,13 +155,6 @@ var _validateField = function (type, value, field, canBeNull) {
136
155
  if (canBeNull && (value === null || value === undefined)) {
137
156
  return validResult();
138
157
  }
139
- // Expression values (`is_expression`) carry the AST `{ exp, args }` instead
140
- // of a typed literal — validate that an expression is chosen, not the
141
- // base type. (`isFunction` is set by FormEngine for expression options.)
142
- if (field === null || field === void 0 ? void 0 : field.isFunction) {
143
- var ast = (_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : value;
144
- return resultFromBoolean(!!(ast === null || ast === void 0 ? void 0 : ast.exp), 'Expression operation is required');
145
- }
146
158
  // Strip type parameters e.g. hash<string, int> → hash
147
159
  var pos = type.indexOf('<');
148
160
  if (pos > 0) {
@@ -154,6 +166,16 @@ var _validateField = function (type, value, field, canBeNull) {
154
166
  var hasValue = !!(0, templates_1.getTemplateValue)(value);
155
167
  return resultFromBoolean(hasKey && hasValue, 'Template must include key and value');
156
168
  }
169
+ // Expression values (`is_expression`) carry the AST `{ exp, args }` instead of
170
+ // a typed literal, so the declared type says nothing about them — validate
171
+ // them as the expression they are, arguments and all. (`isFunction` is set by
172
+ // FormEngine, and by the option recursion below, for expression options.)
173
+ if (field === null || field === void 0 ? void 0 : field.isFunction) {
174
+ // The AST is normally the value itself; a caller that still holds the
175
+ // option envelope hands over `{ value: ast }`, so accept both.
176
+ var ast = (_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : value;
177
+ return (0, exports.validateFieldWithResult)('expression', { value: ast }, (0, lodash_1.omit)(field, ['isFunction']), canBeNull);
178
+ }
157
179
  // Check if the field has required groups
158
180
  if (field === null || field === void 0 ? void 0 : field.required_groups) {
159
181
  if (!isValueDefined(type, value) &&
@@ -161,6 +183,12 @@ var _validateField = function (type, value, field, canBeNull) {
161
183
  return validResult();
162
184
  }
163
185
  }
186
+ // An interface type names another Qorus object by its name, so a non-empty
187
+ // string is the whole contract. `connection` is the exception: it carries
188
+ // enablement and authentication state of its own, checked below.
189
+ if ((0, optionUiTypes_1.isOptionInterfaceUiType)(type) && type !== 'connection') {
190
+ return (0, exports.validateFieldWithResult)('string', value, field);
191
+ }
164
192
  switch (type) {
165
193
  case 'richtext': {
166
194
  if (!value || !(0, size_1.default)(value)) {
@@ -197,28 +225,53 @@ var _validateField = function (type, value, field, canBeNull) {
197
225
  }
198
226
  return withContext((0, exports.validateFieldWithResult)('string', value, field), 'Connection string validation failed');
199
227
  }
228
+ // semantic string formats — permissive patterns mirror the qore DataProvider
229
+ // QoreStringFormatDataType backstop; a server-supplied validation_regex, if
230
+ // present, is also enforced
231
+ case 'email':
232
+ case 'uuid':
233
+ case 'hostname':
234
+ case 'ipv4':
235
+ case 'ipv6':
236
+ case 'phone': {
237
+ if (value === undefined || value === null || value === '' || typeof value !== 'string') {
238
+ return invalidResult('Value must be a non-empty text');
239
+ }
240
+ if ((field === null || field === void 0 ? void 0 : field.validation_regex) && !value.match(field.validation_regex)) {
241
+ return invalidResult('Value does not match the required format');
242
+ }
243
+ return resultFromBoolean(SEMANTIC_FORMAT_PATTERNS[type].test(value), "Value is not a valid ".concat(type));
244
+ }
200
245
  case 'binary': {
201
246
  if (typeof value !== 'string') {
202
247
  return invalidResult('Binary value must be a string');
203
248
  }
204
- var hex = value.trim().replace(/^0x/i, '');
205
- if (hex.length === 0) {
249
+ var trimmed = value.trim();
250
+ if (trimmed.length === 0) {
206
251
  return invalidResult('Binary value is empty');
207
252
  }
208
- if (hex.length % 2 !== 0) {
209
- return invalidResult('Binary hex length must be even');
253
+ // the canonical wire form is base64 (matching the server decode); a "data:<mime>;base64,..." URL
254
+ // and legacy "0x"-prefixed hex are also accepted
255
+ if (/^data:[^;]+;base64,/.test(trimmed)) {
256
+ return validResult();
210
257
  }
211
- if (!/^[0-9a-fA-F]+$/.test(hex)) {
212
- return invalidResult('Binary value must contain only hex characters');
258
+ if (/^0x/i.test(trimmed)) {
259
+ var hex = trimmed.replace(/^0x/i, '');
260
+ return resultFromBoolean(hex.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(hex), 'Binary hex value is invalid');
213
261
  }
214
- return validResult();
262
+ return resultFromBoolean(/^[A-Za-z0-9+/]+={0,2}$/.test(trimmed), 'Binary value must be base64, a data: URL, or 0x-prefixed hex');
215
263
  }
216
264
  case 'string':
217
265
  case 'mapper':
218
266
  case 'workflow':
219
267
  case 'service':
220
268
  case 'job':
269
+ case 'softstring':
270
+ case 'select-string':
271
+ case 'file-string':
272
+ case 'file-as-string':
221
273
  case 'long-string':
274
+ case 'method-name':
222
275
  case 'code-editor':
223
276
  case 'data': {
224
277
  if (value === undefined || value === null || value === '' || typeof value !== 'string') {
@@ -280,6 +333,109 @@ var _validateField = function (type, value, field, canBeNull) {
280
333
  : withContext(contentResult, 'File content is invalid').reasons), true);
281
334
  return reasons.length ? invalidResult(reasons) : validResult();
282
335
  }
336
+ case 'collection-documents': {
337
+ // The value is an array of rows in mixed lifecycle states.
338
+ // A row is valid when it's either:
339
+ // 1. Server-managed: has `ai_documentid` (number) + `name`.
340
+ // 2. Client-managed: has `name` + `content` (base64) — i.e.
341
+ // a fresh upload that hasn't landed yet.
342
+ // Failed rows count as valid for form-level purposes (the
343
+ // user can Retry; the form shouldn't block save just because
344
+ // an upload errored). Empty list is valid here — the
345
+ // top-level required check (`field.required`) handles "must
346
+ // pick at least one" gating.
347
+ if (value === undefined || value === null) {
348
+ return validResult();
349
+ }
350
+ if (!Array.isArray(value)) {
351
+ return invalidResult('Value must be a list of documents');
352
+ }
353
+ var reasons_1 = [];
354
+ value.forEach(function (row, idx) {
355
+ if (!row || typeof row !== 'object') {
356
+ reasons_1.push("Row ".concat(idx + 1, " is not an object"));
357
+ return;
358
+ }
359
+ if (typeof row.name !== 'string' || !row.name) {
360
+ reasons_1.push("Row ".concat(idx + 1, " is missing a name"));
361
+ return;
362
+ }
363
+ var hasServerId = typeof row.ai_documentid === 'number';
364
+ var hasContent = typeof row.content === 'string' && !!row.content;
365
+ if (!hasServerId && !hasContent) {
366
+ reasons_1.push("Row ".concat(idx + 1, " (\"").concat(row.name, "\") has neither uploaded ") +
367
+ "content nor a server document id");
368
+ }
369
+ });
370
+ return reasons_1.length ? invalidResult(undefined, reasons_1) : validResult();
371
+ }
372
+ case 'array-of-pairs': {
373
+ var valid = true;
374
+ var reasons = [];
375
+ // Check if every pair has key & value
376
+ // assigned properly
377
+ if (!Array.isArray(value)) {
378
+ return invalidResult('Value must be a list');
379
+ }
380
+ if (!(value === null || value === void 0 ? void 0 : value.every(function (pair) {
381
+ return pair[field.fields[0]] !== '' && pair[field.fields[1]] !== '';
382
+ }))) {
383
+ valid = false;
384
+ reasons.push('All pairs must include both fields');
385
+ }
386
+ // Get a list of unique values
387
+ var uniqueValues = (0, uniqWith_1.default)(value, function (cur, prev) { return cur[field.fields[0]] === prev[field.fields[0]]; });
388
+ // Check if there are any duplicates
389
+ if ((0, size_1.default)(uniqueValues) !== (0, size_1.default)(value)) {
390
+ valid = false;
391
+ reasons.push('Pairs must use unique keys');
392
+ }
393
+ return valid ? validResult() : invalidResult(undefined, reasons);
394
+ }
395
+ case 'class-connectors': {
396
+ if (!Array.isArray(value) || value.length === 0) {
397
+ return invalidResult('At least one connector is required');
398
+ }
399
+ var valid = true;
400
+ var reasons = [];
401
+ // Check if every pair has name, input method and output method
402
+ // assigned properly
403
+ if (!(value === null || value === void 0 ? void 0 : value.every(function (pair) {
404
+ return !!pair.name && pair.name !== '' && !!pair.method && pair.method !== '';
405
+ }))) {
406
+ valid = false;
407
+ reasons.push('Each connector must have a name and method');
408
+ }
409
+ // Get a list of unique values
410
+ var uniqueValues = (0, uniqWith_1.default)(value, function (cur, prev) { return cur.name === prev.name; });
411
+ // Check if there are any duplicates
412
+ if ((0, size_1.default)(uniqueValues) !== (0, size_1.default)(value)) {
413
+ valid = false;
414
+ reasons.push('Connector names must be unique');
415
+ }
416
+ return valid ? validResult() : invalidResult(undefined, reasons);
417
+ }
418
+ // Classes check
419
+ case 'class-array': {
420
+ if (!(Array.isArray(value) && value.length > 0)) {
421
+ return invalidResult('At least one class must be provided');
422
+ }
423
+ var valid = true;
424
+ var reasons = [];
425
+ // Check if the fields are not empty
426
+ if (!(value === null || value === void 0 ? void 0 : value.every(function (pair) { return !!pair.name && pair.name !== ''; }))) {
427
+ valid = false;
428
+ reasons.push('Each class must have a name');
429
+ }
430
+ // Get a list of unique values
431
+ var uniqueValues = (0, uniqWith_1.default)(value, function (cur, prev) { return "".concat(cur.prefix).concat(cur.name) === "".concat(prev.prefix).concat(prev.name); });
432
+ // Check if there are any duplicates
433
+ if ((0, size_1.default)(uniqueValues) !== (0, size_1.default)(value)) {
434
+ valid = false;
435
+ reasons.push('Class names must be unique');
436
+ }
437
+ return valid ? validResult() : invalidResult(undefined, reasons);
438
+ }
283
439
  case 'number': {
284
440
  return resultFromBoolean(!(0, isNaN_1.default)(value) && ((0, exports.getTypeFromValue)(value) === 'float' || (0, exports.getTypeFromValue)(value) === 'int'), 'Value must be a number');
285
441
  }
@@ -287,6 +443,7 @@ var _validateField = function (type, value, field, canBeNull) {
287
443
  return resultFromBoolean(!Number.isNaN(value) && (0, exports.getTypeFromValue)(value) === 'int', 'Value must be an integer');
288
444
  case 'float':
289
445
  return resultFromBoolean(!(0, isNaN_1.default)(value) && ((0, exports.getTypeFromValue)(value) === 'float' || (0, exports.getTypeFromValue)(value) === 'int'), 'Value must be a float');
446
+ case 'select-array':
290
447
  case 'multi-select':
291
448
  case 'array':
292
449
  case 'file-tree':
@@ -348,7 +505,14 @@ var _validateField = function (type, value, field, canBeNull) {
348
505
  }
349
506
  if ((field === null || field === void 0 ? void 0 : field.ui_element_type) || (field === null || field === void 0 ? void 0 : field.element_type)) {
350
507
  for (var i = 0; i < parsedValue.length; i++) {
351
- var itemResult = (0, exports.validateFieldWithResult)(field.ui_element_type || field.element_type, parsedValue[i].value, field);
508
+ var itemResult = (0, exports.validateFieldWithResult)(field.ui_element_type || field.element_type,
509
+ // An element arrives bare OR in a `{value, type}` envelope: the
510
+ // envelope is what the editor writes, the bare form is what storage
511
+ // holds. Reaching straight for `.value` failed every stored element
512
+ // as empty, so a reloaded alert rule reported its delivery action
513
+ // invalid while showing all six options set -- and threw outright on
514
+ // a null element.
515
+ (0, options_1.getListElementValue)(parsedValue[i]), field);
352
516
  if (!itemResult.isValid) {
353
517
  return withContext(itemResult, "List item ".concat(i, " is invalid"));
354
518
  }
@@ -356,27 +520,409 @@ var _validateField = function (type, value, field, canBeNull) {
356
520
  }
357
521
  return validResult();
358
522
  }
359
- case 'auto':
360
- case 'any': {
361
- var yamlCorrect = true;
362
- var parsedData = void 0;
363
- try {
364
- parsedData = js_yaml_1.default.load(value);
523
+ case 'list-of-hashes': {
524
+ var parsedValue = (0, exports.maybeParseYaml)(value);
525
+ if (!parsedValue || !(0, isArray_1.default)(parsedValue)) {
526
+ return invalidResult('Value must be a list of hashes');
527
+ }
528
+ for (var i = 0; i < parsedValue.length; i++) {
529
+ var item = parsedValue[i];
530
+ if (!(0, size_1.default)(item)) {
531
+ return invalidResult("Hash at index ".concat(i, " must not be empty"));
532
+ }
533
+ var itemResult = (0, exports.validateFieldWithResult)('hash', item);
534
+ if (!itemResult.isValid) {
535
+ return withContext(itemResult, "Hash at index ".concat(i, " is invalid"));
536
+ }
537
+ }
538
+ return validResult();
539
+ }
540
+ case 'mapper-code': {
541
+ if (!value) {
542
+ return invalidResult('Mapper code is required');
543
+ }
544
+ // Split the value
545
+ var _r = value.split('::'), code = _r[0], method = _r[1];
546
+ // Both fields need to be strings & filled
547
+ var codeResult = (0, exports.validateFieldWithResult)('string', code);
548
+ if (!codeResult.isValid) {
549
+ return withContext(codeResult, 'Mapper code is invalid');
365
550
  }
366
- catch (e) {
367
- yamlCorrect = false;
551
+ var methodResult = (0, exports.validateFieldWithResult)('string', method);
552
+ if (!methodResult.isValid) {
553
+ return withContext(methodResult, 'Mapper method is invalid');
554
+ }
555
+ return validResult();
556
+ }
557
+ case 'var-action': {
558
+ var varAction = value;
559
+ if ((varAction === null || varAction === void 0 ? void 0 : varAction.var_type) !== 'localvar' &&
560
+ (varAction === null || varAction === void 0 ? void 0 : varAction.var_type) !== 'globalvar' &&
561
+ (varAction === null || varAction === void 0 ? void 0 : varAction.var_type) !== 'autovar') {
562
+ return invalidResult('Variable type is invalid');
563
+ }
564
+ var nameResult = (0, exports.validateFieldWithResult)('string', varAction.var_name, {
565
+ has_to_have_value: true,
566
+ });
567
+ if (!nameResult.isValid) {
568
+ return withContext(nameResult, 'Variable name is invalid');
368
569
  }
369
- if (!yamlCorrect) {
370
- return invalidResult('Value is not valid YAML');
570
+ var actionResult = (0, exports.validateFieldWithResult)('string', varAction.action_type, {
571
+ has_to_have_value: true,
572
+ });
573
+ if (!actionResult.isValid) {
574
+ return withContext(actionResult, 'Variable action type is invalid');
575
+ }
576
+ // If the action type is transaction, the transaction_action needs to be set
577
+ if (varAction.action_type === 'transaction') {
578
+ var transactionResult = (0, exports.validateFieldWithResult)('string', varAction.transaction_action, {
579
+ has_to_have_value: true,
580
+ });
581
+ if (!transactionResult.isValid) {
582
+ return withContext(transactionResult, 'Transaction action is invalid');
583
+ }
584
+ return validResult();
585
+ }
586
+ // If the variable data is missing
587
+ if (!((_d = field === null || field === void 0 ? void 0 : field.variableData) === null || _d === void 0 ? void 0 : _d.value)) {
588
+ return invalidResult('Variable data is missing');
589
+ }
590
+ // Get the variable data
591
+ var variableData = __assign(__assign({}, value), field.variableData.value);
592
+ return withContext((0, exports.validateFieldWithResult)(varAction.action_type, variableData, field), 'Variable action is invalid');
593
+ }
594
+ case 'type-selector':
595
+ case 'data-provider':
596
+ case 'api-call':
597
+ case 'search-single':
598
+ case 'send-message':
599
+ case 'search':
600
+ case 'update':
601
+ case 'delete':
602
+ case 'create': {
603
+ var newValue = (0, providerValue_1.maybeBuildOptionProvider)(value);
604
+ if (!newValue) {
605
+ return invalidResult('Provider value is invalid');
606
+ }
607
+ // Api call only supports requests / response
608
+ if (type === 'api-call' && !value.supports_request) {
609
+ return invalidResult('API call must support requests');
610
+ }
611
+ // If the provider is from FSM variables, it needs pass this
612
+ if ((field === null || field === void 0 ? void 0 : field.isVariable) &&
613
+ !(newValue.supports_read ||
614
+ newValue.supports_create ||
615
+ newValue.supports_update ||
616
+ newValue.supports_delete ||
617
+ newValue.supports_request ||
618
+ newValue.supports_messages ||
619
+ newValue.transaction_management)) {
620
+ return invalidResult('Variable provider must support at least one action');
621
+ }
622
+ // Send message only supports messages
623
+ if (type === 'send-message' &&
624
+ (!newValue.supports_messages || !newValue.message_id || !newValue.message)) {
625
+ return invalidResult('Send message provider must include message id and content');
626
+ }
627
+ if (newValue.message_id) {
628
+ var messageIdResult = (0, exports.validateFieldWithResult)('string', newValue.message_id);
629
+ if (!messageIdResult.isValid) {
630
+ return withContext(messageIdResult, 'Message id is invalid');
631
+ }
632
+ if (!newValue.message) {
633
+ return invalidResult('Message content is missing');
634
+ }
635
+ var messageResult = (0, exports.validateFieldWithResult)(newValue.message.type, newValue.message.value);
636
+ if (!messageResult.isValid) {
637
+ return withContext(messageResult, 'Message content is invalid');
638
+ }
639
+ }
640
+ if (newValue.use_args) {
641
+ if (((_e = newValue.args) === null || _e === void 0 ? void 0 : _e.type) !== 'nothing') {
642
+ if (!newValue.args) {
643
+ return invalidResult('Arguments are missing');
644
+ }
645
+ var argsResult = (0, exports.validateFieldWithResult)(newValue.args.type === 'hash' ? 'system-options' : newValue.args.type, newValue.args.value);
646
+ if (!argsResult.isValid) {
647
+ return withContext(argsResult, 'Arguments are invalid');
648
+ }
649
+ }
650
+ }
651
+ if ((type === 'search-single' || type === 'search') &&
652
+ (0, size_1.default)(newValue.search_args) !== 0 &&
653
+ !(0, exports.validateFieldWithResult)('system-options-with-operators', newValue.search_args).isValid) {
654
+ return invalidResult('Search arguments are invalid');
655
+ }
656
+ var isUpdateOrCreate = type === 'update' || type === 'create';
657
+ if (isUpdateOrCreate) {
658
+ var areNormalArgsInvalid = "".concat(type, "_args") in newValue &&
659
+ ((0, size_1.default)(newValue["".concat(type, "_args")]) === 0 ||
660
+ !(0, exports.validateFieldWithResult)('system-options', newValue["".concat(type, "_args")]).isValid);
661
+ var areFreeFormArgsInvalid = "".concat(type, "_args_freeform") in newValue &&
662
+ ((0, size_1.default)(newValue["".concat(type, "_args_freeform")]) === 0 ||
663
+ !(0, exports.validateFieldWithResult)('list-of-hashes', newValue["".concat(type, "_args_freeform")]).isValid);
664
+ if ("".concat(type, "_args") in newValue && areNormalArgsInvalid && areFreeFormArgsInvalid) {
665
+ return invalidResult('Update/create arguments are invalid');
666
+ }
667
+ if ("".concat(type, "_args_freeform") in newValue && areFreeFormArgsInvalid && areNormalArgsInvalid) {
668
+ return invalidResult('Update/create arguments are invalid');
669
+ }
670
+ }
671
+ if ((newValue === null || newValue === void 0 ? void 0 : newValue.type) === 'factory') {
672
+ if (newValue.optionsChanged) {
673
+ return invalidResult('Factory options are not saved');
674
+ }
675
+ var options = true;
676
+ if (newValue.options) {
677
+ options = (0, exports.validateFieldWithResult)('system-options', newValue.options).isValid;
678
+ }
679
+ // Type path and name are required
680
+ return resultFromBoolean(!!(newValue.type && newValue.name && options), 'Factory provider is incomplete');
681
+ }
682
+ if (newValue.record_requires_search_options && newValue.searchOptionsChanged) {
683
+ return invalidResult('Search options must be saved');
684
+ }
685
+ if (newValue.search_options &&
686
+ !(0, exports.validateFieldWithResult)('system-options', newValue.search_options).isValid) {
687
+ return invalidResult('Search options are invalid');
688
+ }
689
+ return resultFromBoolean(!!(newValue.type && newValue.name), 'Provider type and name required');
690
+ }
691
+ case 'context-selector': {
692
+ if ((0, lodash_1.isString)(value)) {
693
+ var cont = value.split(':');
694
+ var contextResult = (0, exports.validateFieldWithResult)('string', cont[0]);
695
+ if (!contextResult.isValid) {
696
+ return withContext(contextResult, 'Context selector prefix is invalid');
697
+ }
698
+ var nameResult = (0, exports.validateFieldWithResult)('string', cont[1]);
699
+ if (!nameResult.isValid) {
700
+ return withContext(nameResult, 'Context selector name is invalid');
701
+ }
702
+ return validResult();
703
+ }
704
+ return resultFromBoolean(!!(value === null || value === void 0 ? void 0 : value.iface_kind) && !!(value === null || value === void 0 ? void 0 : value.name), 'Context selector requires iface_kind and name');
705
+ }
706
+ case 'service-event': {
707
+ var typeResult = (0, exports.validateFieldWithResult)('type-selector', value);
708
+ if (!typeResult.isValid) {
709
+ return withContext(typeResult, 'Service event type is invalid');
710
+ }
711
+ if (!(0, size_1.default)(value.handlers) ||
712
+ !(0, every_1.default)(value.handlers, function (handler) { return (handler.type === 'fsm' || handler.type === 'method') && handler.value; })) {
713
+ return invalidResult('Service event handlers are invalid');
714
+ }
715
+ return validResult();
716
+ }
717
+ case 'service-events': {
718
+ if (!(0, isArray_1.default)(value) || !(0, size_1.default)(value)) {
719
+ return invalidResult('At least one service event is required');
720
+ }
721
+ for (var i = 0; i < value.length; i++) {
722
+ var serviceEventResult = (0, exports.validateFieldWithResult)('service-event', value[i]);
723
+ if (!serviceEventResult.isValid) {
724
+ return withContext(serviceEventResult, "Service event ".concat(i, " is invalid"));
725
+ }
726
+ }
727
+ return validResult();
728
+ }
729
+ case 'service-webhook': {
730
+ if (!(0, exports.validateFieldWithResult)('string', value === null || value === void 0 ? void 0 : value.name).isValid ||
731
+ !(0, exports.validateFieldWithResult)('string', value === null || value === void 0 ? void 0 : value['rest-method']).isValid ||
732
+ !(0, exports.validateFieldWithResult)('string', value === null || value === void 0 ? void 0 : value.auth).isValid) {
733
+ return invalidResult('Webhook name, method and auth are required');
734
+ }
735
+ if (value.handler) {
736
+ if (value.handler.type !== 'fsm' && value.handler.type !== 'method') {
737
+ return invalidResult('Webhook handler type is invalid');
738
+ }
739
+ var handlerResult = (0, exports.validateFieldWithResult)('string', value.handler.value);
740
+ if (!handlerResult.isValid) {
741
+ return withContext(handlerResult, 'Webhook handler value is invalid');
742
+ }
743
+ }
744
+ return validResult();
745
+ }
746
+ case 'service-webhooks': {
747
+ if (!(0, isArray_1.default)(value) || !(0, size_1.default)(value)) {
748
+ return invalidResult('At least one webhook is required');
749
+ }
750
+ for (var i = 0; i < value.length; i++) {
751
+ var webhookResult = (0, exports.validateFieldWithResult)('service-webhook', value[i]);
752
+ if (!webhookResult.isValid) {
753
+ return withContext(webhookResult, "Service webhook ".concat(i, " is invalid"));
754
+ }
755
+ }
756
+ return validResult();
757
+ }
758
+ case 'auto':
759
+ case 'any': {
760
+ // Only a STRING has YAML in it. An `auto` field's value is usually
761
+ // already the structure it describes -- a hash the form's hash editor
762
+ // wrote, a list from a list editor -- and `jsyaml.load()` stringifies
763
+ // whatever it is handed first: an object becomes `"[object Object]"`,
764
+ // which YAML then reads as the flow sequence `["object Object"]`. So the
765
+ // auto-detected type came back `list`, the value was validated as a list,
766
+ // and every hash stored in an `auto` field was reported *"Value must be a
767
+ // list"* -- an invalid marker on a value that is fine. A list fared no
768
+ // better: `[1, 2]` stringified to `"1,2"` and detected as a string.
769
+ //
770
+ // Every other branch that accepts either shape already parses through
771
+ // `maybeParseYaml`, which passes a non-string straight through; this one
772
+ // reached for `jsyaml` directly. It cannot use `maybeParseYaml` as-is
773
+ // because that reports a YAML error as an absent value, and "not valid
774
+ // YAML" and "empty" are different answers here.
775
+ var parsedData = value;
776
+ if ((0, lodash_1.isString)(value)) {
777
+ try {
778
+ parsedData = js_yaml_1.default.load(value);
779
+ }
780
+ catch (e) {
781
+ return invalidResult('Value is not valid YAML');
782
+ }
371
783
  }
372
784
  if (parsedData) {
373
785
  return withContext((0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(parsedData), value), 'Auto-detected type is invalid');
374
786
  }
375
787
  return invalidResult('Value is empty');
376
788
  }
377
- case 'options': {
789
+ case 'processor': {
790
+ if (!value || !value['processor-input-type'] || !value['processor-output-type']) {
791
+ return invalidResult('Processor input and output types are required');
792
+ }
793
+ // Validate the input and output types
794
+ if (value === null || value === void 0 ? void 0 : value['processor-input-type']) {
795
+ var inputResult = (0, exports.validateFieldWithResult)('type-selector', value === null || value === void 0 ? void 0 : value['processor-input-type']);
796
+ if (!inputResult.isValid) {
797
+ return withContext(inputResult, 'Processor input type is invalid');
798
+ }
799
+ }
800
+ if (value === null || value === void 0 ? void 0 : value['processor-output-type']) {
801
+ var outputResult = (0, exports.validateFieldWithResult)('type-selector', value === null || value === void 0 ? void 0 : value['processor-output-type']);
802
+ if (!outputResult.isValid) {
803
+ return withContext(outputResult, 'Processor output type is invalid');
804
+ }
805
+ }
806
+ return validResult();
807
+ }
808
+ case 'processor-mappings': {
809
+ // processor-mappings is an array of field mappings
810
+ // Each mapping must have an outputPath (the target field being mapped)
811
+ // and either an inputPath (source field) or options configured
812
+ if (!(0, isArray_1.default)(value)) {
813
+ return canBeNull ? validResult() : invalidResult('Processor mappings must be a list');
814
+ }
815
+ for (var i = 0; i < value.length; i++) {
816
+ var mapping = value[i];
817
+ if (!mapping || !(0, isPlainObject_1.default)(mapping)) {
818
+ return invalidResult("Mapping at index ".concat(i, " is invalid"));
819
+ }
820
+ // outputPath is always required - it identifies which output field is being mapped
821
+ var outputPathResult = (0, exports.validateFieldWithResult)('string', mapping.outputPath);
822
+ if (!outputPathResult.isValid) {
823
+ return withContext(outputPathResult, "Mapping ".concat(i, " output path is invalid"));
824
+ }
825
+ // A mapping must have either an inputPath or options (or both)
826
+ var hasInputPath = mapping.inputPath && mapping.inputPath !== '';
827
+ var hasOptions = mapping.options && (0, size_1.default)(mapping.options) > 0;
828
+ if (!hasInputPath && !hasOptions) {
829
+ return invalidResult("Mapping for \"".concat(mapping.outputPath, "\" must have an input path or options configured"));
830
+ }
831
+ }
832
+ return validResult();
833
+ }
834
+ case 'tool-catalog': {
835
+ // tool-catalog is a flat string[] of selectors; each selector is one of:
836
+ // "*" — all tools from every source
837
+ // "system:*" — all Qorus system tools
838
+ // "system:<tool>" — a specific system tool
839
+ // "<connection>" — all tools from a connection
840
+ // "<connection>/<tool>" — a specific tool from a connection
841
+ if (!(0, isArray_1.default)(value)) {
842
+ return canBeNull ? validResult() : invalidResult('Tool catalog must be a list');
843
+ }
844
+ for (var i = 0; i < value.length; i++) {
845
+ var selector = value[i];
846
+ if (typeof selector !== 'string' || selector.length === 0) {
847
+ return invalidResult("Tool selector at index ".concat(i, " must be a non-empty string"));
848
+ }
849
+ }
850
+ return validResult();
851
+ }
852
+ case 'fsm-list': {
853
+ if (!(0, isArray_1.default)(value)) {
854
+ return invalidResult('FSM list must be an array');
855
+ }
856
+ for (var i = 0; i < value.length; i++) {
857
+ var itemResult = (0, exports.validateFieldWithResult)('string', (_f = value[i]) === null || _f === void 0 ? void 0 : _f.name);
858
+ if (!itemResult.isValid) {
859
+ return withContext(itemResult, "FSM entry ".concat(i, " name is invalid"));
860
+ }
861
+ }
862
+ return validResult();
863
+ }
864
+ case 'api-manager': {
865
+ if (!value) {
866
+ return invalidResult('API manager value is missing');
867
+ }
868
+ var factoryResult = (0, exports.validateFieldWithResult)('string', value.factory);
869
+ if (!factoryResult.isValid) {
870
+ return withContext(factoryResult, 'API manager factory is invalid');
871
+ }
872
+ var endpointsResult = (0, exports.validateFieldWithResult)('api-endpoints', value.endpoints);
873
+ if (!endpointsResult.isValid) {
874
+ return withContext(endpointsResult, 'API manager endpoints are invalid');
875
+ }
876
+ return validResult();
877
+ }
878
+ case 'api-endpoints': {
879
+ if (!(0, isArray_1.default)(value) || !(0, size_1.default)(value)) {
880
+ return invalidResult('At least one API endpoint is required');
881
+ }
882
+ for (var i = 0; i < value.length; i++) {
883
+ var endpointResult = (0, exports.validateFieldWithResult)('string', (_g = value[i]) === null || _g === void 0 ? void 0 : _g.value);
884
+ if (!endpointResult.isValid) {
885
+ return withContext(endpointResult, "Endpoint ".concat(i, " value is invalid"));
886
+ }
887
+ var authorizationResult = (0, exports.validateFieldWithResult)('api-endpoint-authorization', (_h = value[i]) === null || _h === void 0 ? void 0 : _h.authorization, undefined, true);
888
+ if (!authorizationResult.isValid) {
889
+ return withContext(authorizationResult, "Endpoint ".concat(i, " authorization is invalid"));
890
+ }
891
+ }
892
+ return validResult();
893
+ }
894
+ case 'api-endpoint-authorization': {
895
+ if (!value) {
896
+ return canBeNull ? validResult() : invalidResult('Authorization override is missing');
897
+ }
898
+ if (typeof value !== 'object' || (0, isArray_1.default)(value)) {
899
+ return invalidResult('Authorization override must be an object');
900
+ }
901
+ if (value.mode !== undefined && !['merge', 'replace'].includes(value.mode)) {
902
+ return invalidResult('Authorization mode must be merge or replace');
903
+ }
904
+ if (value.allow_anonymous !== undefined && typeof value.allow_anonymous !== 'boolean') {
905
+ return invalidResult('Authorization allow_anonymous must be a boolean');
906
+ }
907
+ if (value.roles !== undefined) {
908
+ if (!(0, isArray_1.default)(value.roles)) {
909
+ return invalidResult('Authorization roles must be a list');
910
+ }
911
+ for (var i = 0; i < value.roles.length; i++) {
912
+ var roleResult = (0, exports.validateFieldWithResult)('string', value.roles[i]);
913
+ if (!roleResult.isValid) {
914
+ return withContext(roleResult, "Authorization role ".concat(i, " is invalid"));
915
+ }
916
+ }
917
+ }
918
+ return validResult();
919
+ }
920
+ case 'options':
921
+ case 'pipeline-options':
922
+ case 'mapper-options':
923
+ case 'system-options': {
378
924
  var getIsValid = function (options, optionSchema) {
379
- var _a, _b, _c, _d, _e, _f;
925
+ var _a, _b, _c, _d;
380
926
  if (optionSchema === void 0) { optionSchema = {}; }
381
927
  if (!options || (0, size_1.default)(options) === 0) {
382
928
  if (!canBeNull) {
@@ -384,35 +930,31 @@ var _validateField = function (type, value, field, canBeNull) {
384
930
  }
385
931
  return validResult();
386
932
  }
387
- if (optionSchema) {
388
- for (var _i = 0, _g = Object.entries(optionSchema); _i < _g.length; _i++) {
389
- var _h = _g[_i], option = _h[0], optionData = _h[1];
390
- if (optionData.required &&
391
- (!(options === null || options === void 0 ? void 0 : options[option]) || ((_a = options === null || options === void 0 ? void 0 : options[option]) === null || _a === void 0 ? void 0 : _a.value) === undefined)) {
392
- return invalidResult("Option ".concat(option, " is required"));
393
- }
394
- if (!((_b = options === null || options === void 0 ? void 0 : options[option]) === null || _b === void 0 ? void 0 : _b.value) &&
395
- optionData.required_groups &&
396
- !(0, exports.validateOptionWithRequiredGroups)(options, optionSchema, optionData.required_groups)) {
397
- return invalidResult("Option ".concat(option, " requires a value"));
398
- }
399
- }
933
+ // Check if all required options are resolved by the current values
934
+ var unresolvedRequiredOption = (0, exports.getUnresolvedRequiredOptions)(optionSchema, options)[0];
935
+ if (unresolvedRequiredOption) {
936
+ return unresolvedRequiredOption.validation
937
+ ? withContext(unresolvedRequiredOption.validation, "Option ".concat(unresolvedRequiredOption.name, " is invalid"))
938
+ : invalidResult((0, exports.getUnresolvedRequiredOptionReason)(unresolvedRequiredOption, optionSchema));
400
939
  }
401
- for (var _j = 0, _k = Object.keys(options); _j < _k.length; _j++) {
402
- var option = _k[_j];
403
- if (((_c = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _c === void 0 ? void 0 : _c.depends_on) &&
940
+ for (var _i = 0, _e = Object.keys(options); _i < _e.length; _i++) {
941
+ var option = _e[_i];
942
+ var optionData = options[option];
943
+ var optionType = ((typeof optionData === 'object' ? optionData === null || optionData === void 0 ? void 0 : optionData.type : undefined) ||
944
+ ((_a = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _a === void 0 ? void 0 : _a.ui_type) ||
945
+ ((_b = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _b === void 0 ? void 0 : _b.type));
946
+ var optionValue = optionData && typeof optionData === 'object' ? optionData.value : optionData;
947
+ if (!((_c = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _c === void 0 ? void 0 : _c.required) &&
948
+ !isValueDefined(optionType, optionValue)) {
949
+ continue;
950
+ }
951
+ if (((_d = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _d === void 0 ? void 0 : _d.depends_on) &&
404
952
  !(0, exports.hasAllDependenciesFullfilled)(optionSchema[option].depends_on, options, optionSchema)) {
405
953
  return invalidResult("Option ".concat(option, " dependencies are not fulfilled"));
406
954
  }
407
- if (((_d = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _d === void 0 ? void 0 : _d.preselected) &&
408
- !((_e = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _e === void 0 ? void 0 : _e.required) &&
409
- !((_f = options[option]) === null || _f === void 0 ? void 0 : _f.value)) {
410
- continue;
411
- }
412
- var optionData = options[option];
413
955
  var optionResult = typeof optionData !== 'object'
414
956
  ? (0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(optionData), optionData)
415
- : (0, exports.validateFieldWithResult)(optionData.type, optionData.value, __assign(__assign({}, (0, lodash_1.omit)(optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option], [])), { optionSchema: optionSchema, options: options, isFunction: optionData.is_expression }));
957
+ : (0, exports.validateFieldWithResult)(optionData.type, optionData.value, __assign(__assign({}, optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]), { optionSchema: optionSchema, options: options, isFunction: optionData.is_expression }));
416
958
  if (!optionResult.isValid) {
417
959
  return withContext(optionResult, "Option ".concat(option, " is invalid"));
418
960
  }
@@ -430,12 +972,45 @@ var _validateField = function (type, value, field, canBeNull) {
430
972
  }
431
973
  return getIsValid(value, field === null || field === void 0 ? void 0 : field.optionSchema);
432
974
  }
975
+ case 'system-options-with-operators': {
976
+ var isValid = function (val) {
977
+ if (!val || (0, size_1.default)(val) === 0) {
978
+ if (canBeNull) {
979
+ return validResult();
980
+ }
981
+ return invalidResult('Options with operators are required');
982
+ }
983
+ for (var _i = 0, _a = Object.keys(val); _i < _a.length; _i++) {
984
+ var option = _a[_i];
985
+ var optionData = val[option];
986
+ var optionResult = typeof optionData !== 'object'
987
+ ? (0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(optionData), optionData)
988
+ : (0, exports.validateFieldWithResult)(optionData.type, optionData.value);
989
+ if (!optionResult.isValid) {
990
+ return withContext(optionResult, "Option ".concat(option, " is invalid"));
991
+ }
992
+ if (!optionData.op ||
993
+ !(0, common_1.fixOperatorValue)(optionData.op).every(function (operator) { return (0, exports.validateFieldWithResult)('string', operator).isValid; })) {
994
+ return invalidResult("Operators for option ".concat(option, " are invalid"));
995
+ }
996
+ }
997
+ return validResult();
998
+ };
999
+ if ((0, isArray_1.default)(value)) {
1000
+ for (var i = 0; i < value.length; i++) {
1001
+ var optionResult = isValid(value[i]);
1002
+ if (!optionResult.isValid) {
1003
+ return withContext(optionResult, "Option set ".concat(i, " is invalid"));
1004
+ }
1005
+ }
1006
+ return validResult();
1007
+ }
1008
+ return isValid(value);
1009
+ }
433
1010
  case 'byte-size': {
434
- // Ported from qorus-ide validations (`sizeUnit` instead of the IDE's
435
- // `size`, which would shadow the lodash import here).
436
1011
  if (typeof value !== 'string')
437
1012
  return invalidResult('Byte size must be a string');
438
- var _m = (0, common_1.splitByteSize)(value), bytes = _m[0], sizeUnit = _m[1];
1013
+ var _s = (0, common_1.splitByteSize)(value), bytes = _s[0], sizeUnit = _s[1];
439
1014
  var bytesResult = (0, exports.validateFieldWithResult)('number', bytes);
440
1015
  if (!bytesResult.isValid) {
441
1016
  return withContext(bytesResult, 'Byte size numeric part is invalid');
@@ -455,7 +1030,8 @@ var _validateField = function (type, value, field, canBeNull) {
455
1030
  }
456
1031
  return validResult();
457
1032
  }
458
- case 'url': {
1033
+ case 'url':
1034
+ case 'uri': {
459
1035
  var protocolResult = (0, exports.validateFieldWithResult)('string', (0, common_1.getProtocol)(value));
460
1036
  if (!protocolResult.isValid) {
461
1037
  return withContext(protocolResult, 'URL protocol is invalid');
@@ -467,8 +1043,7 @@ var _validateField = function (type, value, field, canBeNull) {
467
1043
  return validResult();
468
1044
  }
469
1045
  case 'schema-definition': {
470
- // No IDE counterpart (the editor validates inline there); this is the
471
- // minimal NEW_FIELD.md shape check so malformed definitions can't
1046
+ // The minimal NEW_FIELD.md shape check, so malformed definitions can't
472
1047
  // silently save through FormEngine.
473
1048
  if (!(0, isPlainObject_1.default)(value)) {
474
1049
  return invalidResult('Schema definition must be an object');
@@ -479,33 +1054,52 @@ var _validateField = function (type, value, field, canBeNull) {
479
1054
  return validResult();
480
1055
  }
481
1056
  case 'expression': {
482
- // Ported from qorus-ide validations `'expression'` case. The catalogue
483
- // is supplied via `field.expressions` (the ExpressionBuilder passes it),
484
- // not a global store as in the IDE.
485
1057
  var castedValue_1 = value;
486
- var expressions = (_d = field === null || field === void 0 ? void 0 : field.expressions) !== null && _d !== void 0 ? _d : [];
487
- // Expressions not loaded yet — skip validation until they're available.
488
- if (!(0, size_1.default)(expressions)) {
489
- return validResult();
490
- }
1058
+ var expressions = readExpressionCatalogue(field);
1059
+ // Expressions validation is complicated and needs to follow the following rules
1060
+ // 1. If the expression is empty, it is invalid
1061
+ // 2. If the expression has a type, it means its a normal argument (sub expressions don't have type)
1062
+ // 3. If the expression has no type and `exp` is present, it is a sub expression and must be validated as such
1063
+ // 4. If there are no `args` we need to check if the expression actually has any required arguments or what is the `min_args` property
1064
+ // 1. If the expression is empty
1065
+ //
1066
+ // Checked BEFORE the catalogue guard below, and deliberately: an
1067
+ // expression with no value, or with no operation chosen, is incomplete on
1068
+ // its own terms. Nothing in the catalogue can make it valid, so making
1069
+ // these two answers wait for a network fetch meant the same option
1070
+ // reported valid or invalid depending on whether that fetch had landed —
1071
+ // the field's own "show invalid only" filter listed a different set of
1072
+ // rows on a fast machine than on a slow one.
491
1073
  if (!castedValue_1) {
492
1074
  return invalidResult('Expression value is empty');
493
1075
  }
494
- if (!((_e = castedValue_1.value) === null || _e === void 0 ? void 0 : _e.exp)) {
1076
+ if (!((_j = castedValue_1.value) === null || _j === void 0 ? void 0 : _j.exp)) {
495
1077
  return invalidResult('Expression operation is required');
496
1078
  }
1079
+ // Everything past this point needs the expression's definition, which
1080
+ // only the catalogue can supply. Until it loads there is nothing to check
1081
+ // against, so an expression that names an operation is left alone rather
1082
+ // than reported against a catalogue we do not have.
1083
+ if (!(0, size_1.default)(expressions)) {
1084
+ return validResult();
1085
+ }
1086
+ // Get the expression definition from the catalogue
497
1087
  var expressionDefinition = expressions.find(function (expr) { var _a; return expr.name === ((_a = castedValue_1.value) === null || _a === void 0 ? void 0 : _a.exp); });
498
1088
  if (!expressionDefinition) {
499
- return invalidResult("Expression definition ".concat(String((_f = castedValue_1.value) === null || _f === void 0 ? void 0 : _f.exp), " not found"));
1089
+ return invalidResult("Expression definition ".concat(String((_k = castedValue_1.value) === null || _k === void 0 ? void 0 : _k.exp), " not found"));
500
1090
  }
501
- var hasRequiredArgs = (_g = expressionDefinition.args) === null || _g === void 0 ? void 0 : _g.some(function (arg) { return arg.required; });
502
- if (!hasRequiredArgs && (0, size_1.default)(((_h = castedValue_1.value) === null || _h === void 0 ? void 0 : _h.args) || []) === 0) {
1091
+ // If the expression has no required args, we can consider it valid
1092
+ var hasRequiredArgs = (_l = expressionDefinition.args) === null || _l === void 0 ? void 0 : _l.some(function (arg) { return arg.required; });
1093
+ if (!hasRequiredArgs && (0, size_1.default)(((_m = castedValue_1.value) === null || _m === void 0 ? void 0 : _m.args) || []) === 0) {
503
1094
  return validResult();
504
1095
  }
505
- if ((0, size_1.default)(((_j = castedValue_1.value) === null || _j === void 0 ? void 0 : _j.args) || []) < (expressionDefinition.min_args || 1)) {
1096
+ // If there are no args provided but the expression requires some (normal expressions require at least 1 arg
1097
+ // because we checked for required args earlier), or min_args is set
1098
+ if ((0, size_1.default)(((_o = castedValue_1.value) === null || _o === void 0 ? void 0 : _o.args) || []) < (expressionDefinition.min_args || 1)) {
506
1099
  return invalidResult('Not enough arguments provided');
507
1100
  }
508
- var args = (_l = (_k = castedValue_1.value) === null || _k === void 0 ? void 0 : _k.args) !== null && _l !== void 0 ? _l : [];
1101
+ // Now we need to validate each argument, either as a normal value or as a sub-expression
1102
+ var args = (_q = (_p = castedValue_1.value) === null || _p === void 0 ? void 0 : _p.args) !== null && _q !== void 0 ? _q : [];
509
1103
  for (var index = 0; index < args.length; index++) {
510
1104
  var argValue = args[index];
511
1105
  var argDefinition = expressionDefinition.varargs
@@ -543,7 +1137,6 @@ var _validateField = function (type, value, field, canBeNull) {
543
1137
  }
544
1138
  };
545
1139
  exports._validateField = _validateField;
546
- // Memoized version
547
1140
  /**
548
1141
  * Read one arg out of a hash, whichever of the two legitimate shapes it is in.
549
1142
  *
@@ -564,6 +1157,7 @@ var readArgValue = function (arg) {
564
1157
  return (0, isPlainObject_1.default)(arg) && 'value' in arg ? arg.value : arg;
565
1158
  };
566
1159
  exports.readArgValue = readArgValue;
1160
+ // Memoized version
567
1161
  exports.validateFieldWithResult = (0, lodash_1.memoize)(exports._validateField, function (type, value, field, canBeNull) {
568
1162
  return "".concat(type, "-").concat(JSON.stringify(value), "-").concat(JSON.stringify(field), "-").concat(canBeNull);
569
1163
  });
@@ -621,6 +1215,9 @@ var getValueOrDefaultValue = function (value, defaultValue, canBeNull) {
621
1215
  return undefined;
622
1216
  };
623
1217
  exports.getValueOrDefaultValue = getValueOrDefaultValue;
1218
+ /** Deliberately un-annotated: the inferred literal union is what callers assign
1219
+ * into their own narrower type aliases, and widening it to `string` breaks
1220
+ * them. */
624
1221
  var getTypeFromValue = function (value) {
625
1222
  switch (true) {
626
1223
  case (0, lodash_1.isNull)(value):
@@ -650,36 +1247,151 @@ var validateOptionWithRequiredGroups = function (options, schema, groups) {
650
1247
  var optionsInGroups = (0, options_1.getOptionsFromRequiredGroups)(schema, groups);
651
1248
  return optionsInGroups.some(function (option) {
652
1249
  var _a, _b, _c;
653
- return isValueDefined(((_a = schema[option]) === null || _a === void 0 ? void 0 : _a.ui_type) || ((_b = schema[option]) === null || _b === void 0 ? void 0 : _b.type), (_c = options === null || options === void 0 ? void 0 : options[option]) === null || _c === void 0 ? void 0 : _c.value);
1250
+ return isValueDefined(((_a = schema[option]) === null || _a === void 0 ? void 0 : _a.ui_type) ||
1251
+ ((_b = schema[option]) === null || _b === void 0 ? void 0 : _b.type), (_c = options === null || options === void 0 ? void 0 : options[option]) === null || _c === void 0 ? void 0 : _c.value);
654
1252
  });
655
1253
  }
656
1254
  return true;
657
1255
  };
658
1256
  exports.validateOptionWithRequiredGroups = validateOptionWithRequiredGroups;
1257
+ var getUnresolvedRequiredOptionReason = function (_a, optionSchema) {
1258
+ var _b;
1259
+ var name = _a.name, reason = _a.reason;
1260
+ switch (reason) {
1261
+ case 'dependency':
1262
+ return "Option ".concat(name, " dependencies are not fulfilled");
1263
+ case 'invalid':
1264
+ return "Option ".concat(name, " is invalid");
1265
+ default:
1266
+ return ((_b = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[name]) === null || _b === void 0 ? void 0 : _b.required)
1267
+ ? "Option ".concat(name, " is required")
1268
+ : "Option ".concat(name, " requires a value");
1269
+ }
1270
+ };
1271
+ exports.getUnresolvedRequiredOptionReason = getUnresolvedRequiredOptionReason;
1272
+ /**
1273
+ * List every required option that the given values do not resolve.
1274
+ *
1275
+ * `required` on an option schema means "the effective configuration must carry
1276
+ * this option" — NOT "the user has to type it in". An option whose schema
1277
+ * default or preselected value already resolves it needs no user input at all,
1278
+ * which is why callers pass values that have been through `fixOptions()`
1279
+ * (defaults, preselected values and rich-text default envelopes applied)
1280
+ * rather than the raw values the server returned.
1281
+ *
1282
+ * This is the single owner of the "which required options still need input"
1283
+ * question: `validateField('options', …)` uses it for form validity, and
1284
+ * qorus-ide's `ConnectionManagementModal` uses it to decide whether a
1285
+ * connection can be created — and its OAuth2 flow started — without showing a
1286
+ * form at all.
1287
+ */
1288
+ var getUnresolvedRequiredOptions = function (optionSchema, options) {
1289
+ if (optionSchema === void 0) { optionSchema = {}; }
1290
+ if (options === void 0) { options = {}; }
1291
+ var unresolved = [];
1292
+ for (var _i = 0, _a = Object.entries(optionSchema !== null && optionSchema !== void 0 ? optionSchema : {}); _i < _a.length; _i++) {
1293
+ var _b = _a[_i], name_1 = _b[0], schemaEntry = _b[1];
1294
+ var optionData = schemaEntry;
1295
+ if (!(optionData === null || optionData === void 0 ? void 0 : optionData.required) && !(0, size_1.default)(optionData === null || optionData === void 0 ? void 0 : optionData.required_groups)) {
1296
+ continue;
1297
+ }
1298
+ var optionField = options === null || options === void 0 ? void 0 : options[name_1];
1299
+ var optionValue = optionField && typeof optionField === 'object' ? optionField.value : optionField;
1300
+ var optionType = ((typeof optionField === 'object' ? optionField === null || optionField === void 0 ? void 0 : optionField.type : undefined) ||
1301
+ optionData.ui_type ||
1302
+ optionData.type);
1303
+ // A dependent option cannot be considered resolved while its dependencies
1304
+ // are not: the schema it will be validated against — and with it any
1305
+ // default the server would send — is not final yet.
1306
+ if (optionData.depends_on &&
1307
+ !(0, exports.hasAllDependenciesFullfilled)(optionData.depends_on, options, optionSchema)) {
1308
+ unresolved.push({ name: name_1, reason: 'dependency' });
1309
+ continue;
1310
+ }
1311
+ if (!isValueDefined(optionType, optionValue)) {
1312
+ // An option required only through a group is resolved by any sibling in
1313
+ // that group carrying a value.
1314
+ if (optionData.required ||
1315
+ !(0, exports.validateOptionWithRequiredGroups)(options, optionSchema, optionData.required_groups)) {
1316
+ unresolved.push({ name: name_1, reason: 'missing' });
1317
+ }
1318
+ continue;
1319
+ }
1320
+ var validation = typeof optionField !== 'object'
1321
+ ? (0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(optionField), optionField)
1322
+ : (0, exports.validateFieldWithResult)(optionField.type, optionField.value, __assign(__assign({}, optionData), { optionSchema: optionSchema, options: options, isFunction: optionField.is_expression }));
1323
+ if (!validation.isValid) {
1324
+ unresolved.push({ name: name_1, reason: 'invalid', validation: validation });
1325
+ }
1326
+ }
1327
+ return unresolved;
1328
+ };
1329
+ exports.getUnresolvedRequiredOptions = getUnresolvedRequiredOptions;
1330
+ /**
1331
+ * Split a dependency entry into the sibling it names and how its value is compared.
1332
+ *
1333
+ * Three forms: `name` (the sibling must simply have a value), `name=value` (it must
1334
+ * have exactly that value) and `name!=value` (it must have a value, and not that
1335
+ * one). The negative form is what lets a field say it applies to every variant of a
1336
+ * record but one — spelling that as an `any-of` list of every other variant is both
1337
+ * unreadable in the "Depends on" lock and silently wrong the moment a variant is
1338
+ * added.
1339
+ *
1340
+ * `!=` deliberately requires the sibling to be answered: "not X" is a statement about
1341
+ * an answer, so an unanswered sibling does not satisfy it and the field stays locked.
1342
+ */
1343
+ var parseDependency = function (dependency) {
1344
+ var neqIdx = dependency.indexOf('!=');
1345
+ if (neqIdx !== -1) {
1346
+ return {
1347
+ name: dependency.substring(0, neqIdx),
1348
+ op: '!=',
1349
+ value: dependency.substring(neqIdx + 2),
1350
+ };
1351
+ }
1352
+ var eqIdx = dependency.indexOf('=');
1353
+ if (eqIdx !== -1) {
1354
+ return {
1355
+ name: dependency.substring(0, eqIdx),
1356
+ op: '=',
1357
+ value: dependency.substring(eqIdx + 1),
1358
+ };
1359
+ }
1360
+ return { name: dependency };
1361
+ };
1362
+ exports.parseDependency = parseDependency;
1363
+ /** Whether one dependency entry holds against the current form values. */
1364
+ var isDependencyFulfilled = function (dependency, options, optionsSchema) {
1365
+ var _a;
1366
+ var _b = (0, exports.parseDependency)(dependency), name = _b.name, op = _b.op, value = _b.value;
1367
+ if (op) {
1368
+ var optValue = (_a = options === null || options === void 0 ? void 0 : options[name]) === null || _a === void 0 ? void 0 : _a.value;
1369
+ if (optValue == null) {
1370
+ return false;
1371
+ }
1372
+ return op === '=' ? String(optValue) === value : String(optValue) !== value;
1373
+ }
1374
+ return (options === null || options === void 0 ? void 0 : options[name])
1375
+ ? (0, exports.validateField)(options[name].type, options[name].value, __assign(__assign({}, optionsSchema === null || optionsSchema === void 0 ? void 0 : optionsSchema[name]), { options: options, optionSchema: optionsSchema }))
1376
+ : true;
1377
+ };
659
1378
  var hasAllDependenciesFullfilled = function (dependencies, options, optionsSchema) {
660
1379
  if ((0, size_1.default)(dependencies) === 0) {
661
1380
  return true;
662
1381
  }
663
1382
  return dependencies.every(function (dependency) {
664
- var _a;
1383
+ // A nested list is an ANY. It used to look each entry up as a whole form-field
1384
+ // name, so a `name=value` entry inside one found no field and returned `true`
1385
+ // unconditionally — the any-of group was satisfied by anything at all, while
1386
+ // CompactRow's lock rendered those same entries as real comparisons. Both halves
1387
+ // now go through one parser, so the grammar cannot mean two things.
665
1388
  if ((0, isArray_1.default)(dependency)) {
666
1389
  return dependency.some(function (dep) {
667
- return (options === null || options === void 0 ? void 0 : options[dep])
668
- ? (0, exports.validateField)(options[dep].type, options[dep].value, __assign(__assign({}, optionsSchema === null || optionsSchema === void 0 ? void 0 : optionsSchema[dep]), { options: options, optionSchema: optionsSchema }))
669
- : true;
1390
+ return isDependencyFulfilled(dep, options, optionsSchema);
670
1391
  });
671
1392
  }
672
- if ((0, lodash_1.isString)(dependency)) {
673
- var eqIdx = dependency.indexOf('=');
674
- if (eqIdx !== -1) {
675
- var depName = dependency.substring(0, eqIdx);
676
- var depValue = dependency.substring(eqIdx + 1);
677
- var optValue = (_a = options === null || options === void 0 ? void 0 : options[depName]) === null || _a === void 0 ? void 0 : _a.value;
678
- return optValue != null && String(optValue) === depValue;
679
- }
680
- }
681
- return (options === null || options === void 0 ? void 0 : options[dependency])
682
- ? (0, exports.validateField)(options[dependency].type, options[dependency].value, __assign(__assign({}, optionsSchema === null || optionsSchema === void 0 ? void 0 : optionsSchema[dependency]), { options: options, optionSchema: optionsSchema }))
1393
+ return (0, lodash_1.isString)(dependency)
1394
+ ? isDependencyFulfilled(dependency, options, optionsSchema)
683
1395
  : true;
684
1396
  });
685
1397
  };