@qoretechnologies/reqraft 0.10.39-pr.99.gdbafa34 → 0.10.39

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 (110) hide show
  1. package/.claude/CLAUDE.md +12 -0
  2. package/dist/components/codeSize/index.d.ts +37 -0
  3. package/dist/components/codeSize/index.d.ts.map +1 -0
  4. package/dist/components/codeSize/index.js +87 -0
  5. package/dist/components/codeSize/index.js.map +1 -0
  6. package/dist/components/form/engine/CompactRow.d.ts +20 -1
  7. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  8. package/dist/components/form/engine/CompactRow.js +365 -30
  9. package/dist/components/form/engine/CompactRow.js.map +1 -1
  10. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  11. package/dist/components/form/engine/CompactToolbar.js +6 -8
  12. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  13. package/dist/components/form/engine/FormEngine.d.ts +24 -45
  14. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  15. package/dist/components/form/engine/FormEngine.js +211 -84
  16. package/dist/components/form/engine/FormEngine.js.map +1 -1
  17. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts +37 -0
  18. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -0
  19. package/dist/components/form/engine/_structuredData/SchemaDataView.js +335 -0
  20. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -0
  21. package/dist/components/form/engine/compactRowStyles.d.ts +13 -0
  22. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  23. package/dist/components/form/engine/compactRowStyles.js +30 -5
  24. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  25. package/dist/components/form/engine/compactToolbarContext.d.ts +0 -8
  26. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  27. package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
  28. package/dist/components/form/engine/readFirst.d.ts +73 -1
  29. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  30. package/dist/components/form/engine/readFirst.js +175 -5
  31. package/dist/components/form/engine/readFirst.js.map +1 -1
  32. package/dist/components/form/fields/Field.d.ts.map +1 -1
  33. package/dist/components/form/fields/Field.js +1 -1
  34. package/dist/components/form/fields/Field.js.map +1 -1
  35. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +12 -2
  36. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  37. package/dist/components/form/fields/allowed-values/AllowedValues.js +16 -6
  38. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  39. package/dist/components/form/fields/array/ArrayAuto.d.ts.map +1 -1
  40. package/dist/components/form/fields/array/ArrayAuto.js +51 -21
  41. package/dist/components/form/fields/array/ArrayAuto.js.map +1 -1
  42. package/dist/components/form/fields/array/ArrayAutoField.d.ts.map +1 -1
  43. package/dist/components/form/fields/array/ArrayAutoField.js +42 -25
  44. package/dist/components/form/fields/array/ArrayAutoField.js.map +1 -1
  45. package/dist/components/form/fields/auto/AutoFormField.d.ts +4 -0
  46. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  47. package/dist/components/form/fields/auto/AutoFormField.js +29 -4
  48. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  49. package/dist/components/form/fields/long-string/LongString.d.ts +9 -1
  50. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  51. package/dist/components/form/fields/long-string/LongString.js +25 -4
  52. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  53. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  54. package/dist/components/form/fields/template/TemplateField.js +12 -2
  55. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  56. package/dist/helpers/common.d.ts +8 -1
  57. package/dist/helpers/common.d.ts.map +1 -1
  58. package/dist/helpers/common.js +9 -1
  59. package/dist/helpers/common.js.map +1 -1
  60. package/dist/helpers/optionUiTypes.d.ts +30 -0
  61. package/dist/helpers/optionUiTypes.d.ts.map +1 -0
  62. package/dist/helpers/optionUiTypes.js +148 -0
  63. package/dist/helpers/optionUiTypes.js.map +1 -0
  64. package/dist/helpers/options.d.ts +19 -0
  65. package/dist/helpers/options.d.ts.map +1 -1
  66. package/dist/helpers/options.js +25 -1
  67. package/dist/helpers/options.js.map +1 -1
  68. package/dist/helpers/providerValue.d.ts +68 -0
  69. package/dist/helpers/providerValue.d.ts.map +1 -0
  70. package/dist/helpers/providerValue.js +50 -0
  71. package/dist/helpers/providerValue.js.map +1 -0
  72. package/dist/helpers/singleLineString.d.ts +37 -0
  73. package/dist/helpers/singleLineString.d.ts.map +1 -0
  74. package/dist/helpers/singleLineString.js +66 -0
  75. package/dist/helpers/singleLineString.js.map +1 -0
  76. package/dist/helpers/validations.d.ts +85 -2
  77. package/dist/helpers/validations.d.ts.map +1 -1
  78. package/dist/helpers/validations.js +802 -76
  79. package/dist/helpers/validations.js.map +1 -1
  80. package/dist/index.d.ts +1 -0
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +1 -0
  83. package/dist/index.js.map +1 -1
  84. package/package.json +1 -1
  85. package/src/components/codeSize/index.tsx +79 -0
  86. package/src/components/form/engine/CompactRow.tsx +450 -31
  87. package/src/components/form/engine/CompactToolbar.tsx +16 -21
  88. package/src/components/form/engine/FormEngine.stories.tsx +804 -17
  89. package/src/components/form/engine/FormEngine.tsx +207 -105
  90. package/src/components/form/engine/_structuredData/SchemaDataView.stories.tsx +233 -0
  91. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +630 -0
  92. package/src/components/form/engine/compactRowStyles.ts +44 -2
  93. package/src/components/form/engine/compactToolbarContext.ts +0 -8
  94. package/src/components/form/engine/readFirst.ts +201 -5
  95. package/src/components/form/fields/Field.tsx +3 -1
  96. package/src/components/form/fields/allowed-values/AllowedValues.tsx +24 -2
  97. package/src/components/form/fields/array/ArrayAuto.tsx +35 -4
  98. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +134 -0
  99. package/src/components/form/fields/array/ArrayAutoField.tsx +22 -4
  100. package/src/components/form/fields/auto/AutoFormField.tsx +30 -1
  101. package/src/components/form/fields/long-string/LongString.tsx +46 -5
  102. package/src/components/form/fields/template/TemplateField.tsx +16 -1
  103. package/src/helpers/common.ts +11 -1
  104. package/src/helpers/optionUiTypes.ts +153 -0
  105. package/src/helpers/options.ts +23 -0
  106. package/src/helpers/providerValue.ts +125 -0
  107. package/src/helpers/singleLineString.ts +61 -0
  108. package/src/helpers/validations.ts +1105 -80
  109. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +23 -2
  110. 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 = void 0;
26
+ exports.hasAllDependenciesFullfilled = 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,9 +35,74 @@ 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");
44
+ /** The five cron fields, in order, as a schedule hash may name them. */
45
+ var CRON_FIELDS = [
46
+ ['minutes', 'minute'],
47
+ ['hours', 'hour'],
48
+ ['days', 'day'],
49
+ ['months', 'month'],
50
+ ['dow', 'weekday'],
51
+ ];
52
+ /**
53
+ * A five-field cron expression, from either representation a caller may hold.
54
+ *
55
+ * A schedule reaches the form in one of two shapes, and both are legitimate: a
56
+ * joined string (`"0 0 1 1 *"`), or the hash a Qorus job carries
57
+ * (`{minutes, hours, days, months, dow}` — also accepted under the singular
58
+ * `minute`/`hour`/`day`/`month`/`weekday` spellings). The renderer already
59
+ * accepted both; validation did not, and fed the hash straight to
60
+ * `isValidCron`, which calls `.trim()` on it.
61
+ *
62
+ * Returns `undefined` for anything that is neither, so the caller reports an
63
+ * invalid value rather than throwing.
64
+ */
65
+ var cronExpressionFromValue = function (value) {
66
+ if (typeof value === 'string') {
67
+ return value;
68
+ }
69
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
70
+ return undefined;
71
+ }
72
+ var hash = value;
73
+ var parts = CRON_FIELDS.map(function (_a) {
74
+ var _b;
75
+ var plural = _a[0], singular = _a[1];
76
+ var field = (_b = hash[plural]) !== null && _b !== void 0 ? _b : hash[singular];
77
+ return field === undefined || field === null || field === '' ? '*' : String(field);
78
+ });
79
+ // An object with none of the five keys is not a schedule — every part
80
+ // defaulting to `*` would otherwise turn any object at all into "* * * * *"
81
+ return parts.every(function (part) { return part === '*'; }) &&
82
+ !CRON_FIELDS.some(function (_a) {
83
+ var plural = _a[0], singular = _a[1];
84
+ return plural in hash || singular in hash;
85
+ })
86
+ ? undefined
87
+ : parts.join(' ');
88
+ };
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
+ };
41
106
  var validResult = function () { return ({ isValid: true, reasons: [] }); };
42
107
  var invalidResult = function (reason, childReasons) {
43
108
  if (childReasons === void 0) { childReasons = []; }
@@ -74,7 +139,7 @@ var isValueDefined = function (type, value) {
74
139
  return value !== undefined && value !== '';
75
140
  };
76
141
  var _validateField = function (type, value, field, canBeNull) {
77
- 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;
78
143
  if (!type) {
79
144
  if (value === undefined || value === null) {
80
145
  return invalidResult('Missing value');
@@ -90,13 +155,6 @@ var _validateField = function (type, value, field, canBeNull) {
90
155
  if (canBeNull && (value === null || value === undefined)) {
91
156
  return validResult();
92
157
  }
93
- // Expression values (`is_expression`) carry the AST `{ exp, args }` instead
94
- // of a typed literal — validate that an expression is chosen, not the
95
- // base type. (`isFunction` is set by FormEngine for expression options.)
96
- if (field === null || field === void 0 ? void 0 : field.isFunction) {
97
- var ast = (_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : value;
98
- return resultFromBoolean(!!(ast === null || ast === void 0 ? void 0 : ast.exp), 'Expression operation is required');
99
- }
100
158
  // Strip type parameters e.g. hash<string, int> → hash
101
159
  var pos = type.indexOf('<');
102
160
  if (pos > 0) {
@@ -108,6 +166,16 @@ var _validateField = function (type, value, field, canBeNull) {
108
166
  var hasValue = !!(0, templates_1.getTemplateValue)(value);
109
167
  return resultFromBoolean(hasKey && hasValue, 'Template must include key and value');
110
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
+ }
111
179
  // Check if the field has required groups
112
180
  if (field === null || field === void 0 ? void 0 : field.required_groups) {
113
181
  if (!isValueDefined(type, value) &&
@@ -115,6 +183,12 @@ var _validateField = function (type, value, field, canBeNull) {
115
183
  return validResult();
116
184
  }
117
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
+ }
118
192
  switch (type) {
119
193
  case 'richtext': {
120
194
  if (!value || !(0, size_1.default)(value)) {
@@ -151,28 +225,53 @@ var _validateField = function (type, value, field, canBeNull) {
151
225
  }
152
226
  return withContext((0, exports.validateFieldWithResult)('string', value, field), 'Connection string validation failed');
153
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
+ }
154
245
  case 'binary': {
155
246
  if (typeof value !== 'string') {
156
247
  return invalidResult('Binary value must be a string');
157
248
  }
158
- var hex = value.trim().replace(/^0x/i, '');
159
- if (hex.length === 0) {
249
+ var trimmed = value.trim();
250
+ if (trimmed.length === 0) {
160
251
  return invalidResult('Binary value is empty');
161
252
  }
162
- if (hex.length % 2 !== 0) {
163
- 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();
164
257
  }
165
- if (!/^[0-9a-fA-F]+$/.test(hex)) {
166
- 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');
167
261
  }
168
- 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');
169
263
  }
170
264
  case 'string':
171
265
  case 'mapper':
172
266
  case 'workflow':
173
267
  case 'service':
174
268
  case 'job':
269
+ case 'softstring':
270
+ case 'select-string':
271
+ case 'file-string':
272
+ case 'file-as-string':
175
273
  case 'long-string':
274
+ case 'method-name':
176
275
  case 'code-editor':
177
276
  case 'data': {
178
277
  if (value === undefined || value === null || value === '' || typeof value !== 'string') {
@@ -234,6 +333,109 @@ var _validateField = function (type, value, field, canBeNull) {
234
333
  : withContext(contentResult, 'File content is invalid').reasons), true);
235
334
  return reasons.length ? invalidResult(reasons) : validResult();
236
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
+ }
237
439
  case 'number': {
238
440
  return resultFromBoolean(!(0, isNaN_1.default)(value) && ((0, exports.getTypeFromValue)(value) === 'float' || (0, exports.getTypeFromValue)(value) === 'int'), 'Value must be a number');
239
441
  }
@@ -241,15 +443,25 @@ var _validateField = function (type, value, field, canBeNull) {
241
443
  return resultFromBoolean(!Number.isNaN(value) && (0, exports.getTypeFromValue)(value) === 'int', 'Value must be an integer');
242
444
  case 'float':
243
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':
244
447
  case 'multi-select':
245
448
  case 'array':
246
449
  case 'file-tree':
247
450
  return resultFromBoolean(value && value.length > 0, 'At least one value is required');
248
- case 'cron':
451
+ case 'cron': {
249
452
  if (!value) {
250
453
  return invalidResult('Cron value is required');
251
454
  }
252
- return resultFromBoolean((0, cron_validator_1.isValidCron)(value, { alias: true }), 'Cron expression is invalid');
455
+ var expression = (0, exports.cronExpressionFromValue)(value);
456
+ // A value that is neither a cron string nor a schedule hash is INVALID,
457
+ // not a crash: `isValidCron` calls `.trim()` on whatever it is given, so
458
+ // passing it a hash threw a TypeError out of validation and took the
459
+ // whole form down through the host's error boundary.
460
+ if (expression === undefined) {
461
+ return invalidResult('Cron expression is invalid');
462
+ }
463
+ return resultFromBoolean((0, cron_validator_1.isValidCron)(expression, { alias: true }), 'Cron expression is invalid');
464
+ }
253
465
  case 'date':
254
466
  return resultFromBoolean(value !== undefined &&
255
467
  value !== null &&
@@ -293,7 +505,14 @@ var _validateField = function (type, value, field, canBeNull) {
293
505
  }
294
506
  if ((field === null || field === void 0 ? void 0 : field.ui_element_type) || (field === null || field === void 0 ? void 0 : field.element_type)) {
295
507
  for (var i = 0; i < parsedValue.length; i++) {
296
- 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);
297
516
  if (!itemResult.isValid) {
298
517
  return withContext(itemResult, "List item ".concat(i, " is invalid"));
299
518
  }
@@ -301,27 +520,409 @@ var _validateField = function (type, value, field, canBeNull) {
301
520
  }
302
521
  return validResult();
303
522
  }
304
- case 'auto':
305
- case 'any': {
306
- var yamlCorrect = true;
307
- var parsedData = void 0;
308
- try {
309
- 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');
550
+ }
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');
569
+ }
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
+ }
310
743
  }
311
- catch (e) {
312
- yamlCorrect = false;
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');
313
749
  }
314
- if (!yamlCorrect) {
315
- return invalidResult('Value is not valid YAML');
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
+ }
316
783
  }
317
784
  if (parsedData) {
318
785
  return withContext((0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(parsedData), value), 'Auto-detected type is invalid');
319
786
  }
320
787
  return invalidResult('Value is empty');
321
788
  }
322
- 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': {
323
924
  var getIsValid = function (options, optionSchema) {
324
- var _a, _b, _c, _d, _e, _f;
925
+ var _a, _b, _c, _d;
325
926
  if (optionSchema === void 0) { optionSchema = {}; }
326
927
  if (!options || (0, size_1.default)(options) === 0) {
327
928
  if (!canBeNull) {
@@ -329,35 +930,31 @@ var _validateField = function (type, value, field, canBeNull) {
329
930
  }
330
931
  return validResult();
331
932
  }
332
- if (optionSchema) {
333
- for (var _i = 0, _g = Object.entries(optionSchema); _i < _g.length; _i++) {
334
- var _h = _g[_i], option = _h[0], optionData = _h[1];
335
- if (optionData.required &&
336
- (!(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)) {
337
- return invalidResult("Option ".concat(option, " is required"));
338
- }
339
- if (!((_b = options === null || options === void 0 ? void 0 : options[option]) === null || _b === void 0 ? void 0 : _b.value) &&
340
- optionData.required_groups &&
341
- !(0, exports.validateOptionWithRequiredGroups)(options, optionSchema, optionData.required_groups)) {
342
- return invalidResult("Option ".concat(option, " requires a value"));
343
- }
344
- }
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));
345
939
  }
346
- for (var _j = 0, _k = Object.keys(options); _j < _k.length; _j++) {
347
- var option = _k[_j];
348
- 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) &&
349
952
  !(0, exports.hasAllDependenciesFullfilled)(optionSchema[option].depends_on, options, optionSchema)) {
350
953
  return invalidResult("Option ".concat(option, " dependencies are not fulfilled"));
351
954
  }
352
- if (((_d = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _d === void 0 ? void 0 : _d.preselected) &&
353
- !((_e = optionSchema === null || optionSchema === void 0 ? void 0 : optionSchema[option]) === null || _e === void 0 ? void 0 : _e.required) &&
354
- !((_f = options[option]) === null || _f === void 0 ? void 0 : _f.value)) {
355
- continue;
356
- }
357
- var optionData = options[option];
358
955
  var optionResult = typeof optionData !== 'object'
359
956
  ? (0, exports.validateFieldWithResult)((0, exports.getTypeFromValue)(optionData), optionData)
360
- : (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 }));
361
958
  if (!optionResult.isValid) {
362
959
  return withContext(optionResult, "Option ".concat(option, " is invalid"));
363
960
  }
@@ -375,12 +972,45 @@ var _validateField = function (type, value, field, canBeNull) {
375
972
  }
376
973
  return getIsValid(value, field === null || field === void 0 ? void 0 : field.optionSchema);
377
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
+ }
378
1010
  case 'byte-size': {
379
- // Ported from qorus-ide validations (`sizeUnit` instead of the IDE's
380
- // `size`, which would shadow the lodash import here).
381
1011
  if (typeof value !== 'string')
382
1012
  return invalidResult('Byte size must be a string');
383
- 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];
384
1014
  var bytesResult = (0, exports.validateFieldWithResult)('number', bytes);
385
1015
  if (!bytesResult.isValid) {
386
1016
  return withContext(bytesResult, 'Byte size numeric part is invalid');
@@ -400,7 +1030,8 @@ var _validateField = function (type, value, field, canBeNull) {
400
1030
  }
401
1031
  return validResult();
402
1032
  }
403
- case 'url': {
1033
+ case 'url':
1034
+ case 'uri': {
404
1035
  var protocolResult = (0, exports.validateFieldWithResult)('string', (0, common_1.getProtocol)(value));
405
1036
  if (!protocolResult.isValid) {
406
1037
  return withContext(protocolResult, 'URL protocol is invalid');
@@ -412,8 +1043,7 @@ var _validateField = function (type, value, field, canBeNull) {
412
1043
  return validResult();
413
1044
  }
414
1045
  case 'schema-definition': {
415
- // No IDE counterpart (the editor validates inline there); this is the
416
- // minimal NEW_FIELD.md shape check so malformed definitions can't
1046
+ // The minimal NEW_FIELD.md shape check, so malformed definitions can't
417
1047
  // silently save through FormEngine.
418
1048
  if (!(0, isPlainObject_1.default)(value)) {
419
1049
  return invalidResult('Schema definition must be an object');
@@ -424,33 +1054,52 @@ var _validateField = function (type, value, field, canBeNull) {
424
1054
  return validResult();
425
1055
  }
426
1056
  case 'expression': {
427
- // Ported from qorus-ide validations `'expression'` case. The catalogue
428
- // is supplied via `field.expressions` (the ExpressionBuilder passes it),
429
- // not a global store as in the IDE.
430
1057
  var castedValue_1 = value;
431
- var expressions = (_d = field === null || field === void 0 ? void 0 : field.expressions) !== null && _d !== void 0 ? _d : [];
432
- // Expressions not loaded yet skip validation until they're available.
433
- if (!(0, size_1.default)(expressions)) {
434
- return validResult();
435
- }
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.
436
1073
  if (!castedValue_1) {
437
1074
  return invalidResult('Expression value is empty');
438
1075
  }
439
- 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)) {
440
1077
  return invalidResult('Expression operation is required');
441
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
442
1087
  var expressionDefinition = expressions.find(function (expr) { var _a; return expr.name === ((_a = castedValue_1.value) === null || _a === void 0 ? void 0 : _a.exp); });
443
1088
  if (!expressionDefinition) {
444
- 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"));
445
1090
  }
446
- var hasRequiredArgs = (_g = expressionDefinition.args) === null || _g === void 0 ? void 0 : _g.some(function (arg) { return arg.required; });
447
- 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) {
448
1094
  return validResult();
449
1095
  }
450
- 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)) {
451
1099
  return invalidResult('Not enough arguments provided');
452
1100
  }
453
- 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 : [];
454
1103
  for (var index = 0; index < args.length; index++) {
455
1104
  var argValue = args[index];
456
1105
  var argDefinition = expressionDefinition.varargs
@@ -488,7 +1137,6 @@ var _validateField = function (type, value, field, canBeNull) {
488
1137
  }
489
1138
  };
490
1139
  exports._validateField = _validateField;
491
- // Memoized version
492
1140
  /**
493
1141
  * Read one arg out of a hash, whichever of the two legitimate shapes it is in.
494
1142
  *
@@ -509,6 +1157,7 @@ var readArgValue = function (arg) {
509
1157
  return (0, isPlainObject_1.default)(arg) && 'value' in arg ? arg.value : arg;
510
1158
  };
511
1159
  exports.readArgValue = readArgValue;
1160
+ // Memoized version
512
1161
  exports.validateFieldWithResult = (0, lodash_1.memoize)(exports._validateField, function (type, value, field, canBeNull) {
513
1162
  return "".concat(type, "-").concat(JSON.stringify(value), "-").concat(JSON.stringify(field), "-").concat(canBeNull);
514
1163
  });
@@ -566,6 +1215,9 @@ var getValueOrDefaultValue = function (value, defaultValue, canBeNull) {
566
1215
  return undefined;
567
1216
  };
568
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. */
569
1221
  var getTypeFromValue = function (value) {
570
1222
  switch (true) {
571
1223
  case (0, lodash_1.isNull)(value):
@@ -595,12 +1247,86 @@ var validateOptionWithRequiredGroups = function (options, schema, groups) {
595
1247
  var optionsInGroups = (0, options_1.getOptionsFromRequiredGroups)(schema, groups);
596
1248
  return optionsInGroups.some(function (option) {
597
1249
  var _a, _b, _c;
598
- 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);
599
1252
  });
600
1253
  }
601
1254
  return true;
602
1255
  };
603
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;
604
1330
  var hasAllDependenciesFullfilled = function (dependencies, options, optionsSchema) {
605
1331
  if ((0, size_1.default)(dependencies) === 0) {
606
1332
  return true;