@makehq/forman-schema 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +126 -20
- package/dist/index.d.cts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +126 -20
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,22 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
29
|
|
|
30
30
|
// src/utils.ts
|
|
31
31
|
var FORMAN_VISUAL_TYPES = ["banner", "markdown", "html", "separator"];
|
|
32
|
+
function isVisualType(type) {
|
|
33
|
+
return FORMAN_VISUAL_TYPES.includes(type);
|
|
34
|
+
}
|
|
35
|
+
var FORMAN_REFERENCE_TYPES = [
|
|
36
|
+
"account",
|
|
37
|
+
"hook",
|
|
38
|
+
"device",
|
|
39
|
+
"keychain",
|
|
40
|
+
"datastore",
|
|
41
|
+
"aiagent",
|
|
42
|
+
"udt",
|
|
43
|
+
"scenario"
|
|
44
|
+
];
|
|
45
|
+
function isReferenceType(type) {
|
|
46
|
+
return FORMAN_REFERENCE_TYPES.includes(type);
|
|
47
|
+
}
|
|
32
48
|
function noEmpty(text) {
|
|
33
49
|
return text?.trim() || void 0;
|
|
34
50
|
}
|
|
@@ -42,13 +58,19 @@ function containsIMLExpression(value) {
|
|
|
42
58
|
if (typeof value !== "string") return false;
|
|
43
59
|
return value.indexOf("{{") > -1 && value.indexOf("}}") > -1;
|
|
44
60
|
}
|
|
61
|
+
function isPrimitiveIMLExpression(value) {
|
|
62
|
+
if (typeof value !== "string") return false;
|
|
63
|
+
return value.lastIndexOf("{{") === 0 && value.indexOf("}}") === value.length - 2;
|
|
64
|
+
}
|
|
45
65
|
var API_ENDPOINTS = {
|
|
46
66
|
account: "api://connections/{{kind}}",
|
|
47
67
|
aiagent: "api://ai-agents/v1/agents",
|
|
48
68
|
datastore: "api://data-stores",
|
|
49
69
|
hook: "api://hooks/{{kind}}",
|
|
70
|
+
device: "api://devices",
|
|
50
71
|
keychain: "api://keys/{{kind}}",
|
|
51
|
-
udt: "api://data-structures"
|
|
72
|
+
udt: "api://data-structures",
|
|
73
|
+
scenario: "api://scenario-list"
|
|
52
74
|
};
|
|
53
75
|
function normalizeFormanFieldType(field) {
|
|
54
76
|
const [type, kind] = field.type.split(":");
|
|
@@ -67,6 +89,69 @@ function normalizeFormanFieldType(field) {
|
|
|
67
89
|
}
|
|
68
90
|
};
|
|
69
91
|
}
|
|
92
|
+
function buildRestoreStructure(items) {
|
|
93
|
+
const result = {};
|
|
94
|
+
for (const item of items) {
|
|
95
|
+
const { domain, path, state } = item;
|
|
96
|
+
if (!result[domain]) {
|
|
97
|
+
result[domain] = {};
|
|
98
|
+
}
|
|
99
|
+
let current = result[domain];
|
|
100
|
+
for (const [index, key] of path.entries()) {
|
|
101
|
+
const nextKey = path[index + 1];
|
|
102
|
+
const isLastElement = index === path.length - 1;
|
|
103
|
+
const nextIsNumber = typeof nextKey === "number";
|
|
104
|
+
if (Array.isArray(current)) {
|
|
105
|
+
if (typeof key !== "number") {
|
|
106
|
+
throw new Error("Invalid path");
|
|
107
|
+
}
|
|
108
|
+
if (!current[key]) {
|
|
109
|
+
current[key] = {};
|
|
110
|
+
}
|
|
111
|
+
if (nextIsNumber) {
|
|
112
|
+
if (!current[key].value) {
|
|
113
|
+
current[key].value = {
|
|
114
|
+
mode: "chose",
|
|
115
|
+
items: []
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (!current[key].value.items) {
|
|
119
|
+
current[key].value.items = [];
|
|
120
|
+
}
|
|
121
|
+
current = current[key].value.items;
|
|
122
|
+
} else if (isLastElement) {
|
|
123
|
+
Object.assign(current[key], state);
|
|
124
|
+
} else {
|
|
125
|
+
current = current[key];
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
if (typeof key !== "string") {
|
|
129
|
+
throw new Error("Invalid path");
|
|
130
|
+
}
|
|
131
|
+
if (!current[key]) {
|
|
132
|
+
current[key] = {};
|
|
133
|
+
}
|
|
134
|
+
if (isLastElement) {
|
|
135
|
+
Object.assign(current[key], state);
|
|
136
|
+
} else {
|
|
137
|
+
if (nextIsNumber) {
|
|
138
|
+
if (!current[key].items) {
|
|
139
|
+
current[key].items = [];
|
|
140
|
+
current[key].mode = "chose";
|
|
141
|
+
}
|
|
142
|
+
current = current[key].items;
|
|
143
|
+
} else {
|
|
144
|
+
if (!current[key].nested) {
|
|
145
|
+
current[key].nested = {};
|
|
146
|
+
}
|
|
147
|
+
current = current[key].nested;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
70
155
|
|
|
71
156
|
// src/forman.ts
|
|
72
157
|
var SchemaConversionError = class extends Error {
|
|
@@ -178,7 +263,7 @@ function handleCollectionType(field, result, context) {
|
|
|
178
263
|
required: []
|
|
179
264
|
});
|
|
180
265
|
function addField(subField, tail) {
|
|
181
|
-
if (
|
|
266
|
+
if (isVisualType(subField.type)) {
|
|
182
267
|
return;
|
|
183
268
|
}
|
|
184
269
|
if (!subField.name) return;
|
|
@@ -423,6 +508,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
423
508
|
(acc, domain) => {
|
|
424
509
|
acc[domain] = {
|
|
425
510
|
seenFields: /* @__PURE__ */ new Set(),
|
|
511
|
+
fieldStates: [],
|
|
426
512
|
validateFields: (fields, context) => {
|
|
427
513
|
return validateFormanValue(
|
|
428
514
|
domains[domain].values,
|
|
@@ -480,11 +566,14 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
480
566
|
}
|
|
481
567
|
return {
|
|
482
568
|
valid: errors.length === 0,
|
|
483
|
-
errors
|
|
569
|
+
errors,
|
|
570
|
+
states: errors.length === 0 && options?.states ? buildRestoreStructure(
|
|
571
|
+
Object.keys(domains).map((domain) => roots[domain].fieldStates.map((state) => ({ domain, ...state }))).flat()
|
|
572
|
+
) : void 0
|
|
484
573
|
};
|
|
485
574
|
}
|
|
486
575
|
async function validateFormanValue(value, field, context) {
|
|
487
|
-
if (
|
|
576
|
+
if (isVisualType(field.type)) {
|
|
488
577
|
return {
|
|
489
578
|
valid: true,
|
|
490
579
|
errors: []
|
|
@@ -512,7 +601,7 @@ async function validateFormanValue(value, field, context) {
|
|
|
512
601
|
const expectedType = FORMAN_TYPE_MAP2[normalizedField.type];
|
|
513
602
|
let actualType = typeof value;
|
|
514
603
|
if (actualType === "object" && Array.isArray(value)) actualType = "array";
|
|
515
|
-
if (expectedType && expectedType !== actualType) {
|
|
604
|
+
if (expectedType && expectedType !== actualType && !isPrimitiveIMLExpression(value)) {
|
|
516
605
|
return {
|
|
517
606
|
valid: false,
|
|
518
607
|
errors: [
|
|
@@ -524,6 +613,24 @@ async function validateFormanValue(value, field, context) {
|
|
|
524
613
|
]
|
|
525
614
|
};
|
|
526
615
|
}
|
|
616
|
+
if (containsIMLExpression(value)) {
|
|
617
|
+
if (normalizedField.mappable === false) {
|
|
618
|
+
return {
|
|
619
|
+
valid: false,
|
|
620
|
+
errors: [
|
|
621
|
+
{
|
|
622
|
+
domain: context.domain,
|
|
623
|
+
path: context.path.join("."),
|
|
624
|
+
message: "Value contains prohibited IML expression."
|
|
625
|
+
}
|
|
626
|
+
]
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
valid: true,
|
|
631
|
+
errors: []
|
|
632
|
+
};
|
|
633
|
+
}
|
|
527
634
|
switch (normalizedField.type) {
|
|
528
635
|
case "collection":
|
|
529
636
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -532,10 +639,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
532
639
|
case "select":
|
|
533
640
|
case "account":
|
|
534
641
|
case "hook":
|
|
642
|
+
case "device":
|
|
535
643
|
case "keychain":
|
|
536
644
|
case "datastore":
|
|
537
645
|
case "aiagent":
|
|
538
646
|
case "udt":
|
|
647
|
+
case "scenario":
|
|
539
648
|
case "file":
|
|
540
649
|
case "folder":
|
|
541
650
|
return handleSelectType2(value, normalizedField, context);
|
|
@@ -575,7 +684,7 @@ async function handleCollectionType2(value, field, context) {
|
|
|
575
684
|
};
|
|
576
685
|
}
|
|
577
686
|
}
|
|
578
|
-
if (
|
|
687
|
+
if (isVisualType(subField.type)) {
|
|
579
688
|
continue;
|
|
580
689
|
}
|
|
581
690
|
if (!subField.name) {
|
|
@@ -592,7 +701,7 @@ async function handleCollectionType2(value, field, context) {
|
|
|
592
701
|
path: [...path, subField.name],
|
|
593
702
|
validateNestedFields: async (fields, context2) => {
|
|
594
703
|
for (const subField2 of fields) {
|
|
595
|
-
if (
|
|
704
|
+
if (isVisualType(subField2.type)) {
|
|
596
705
|
continue;
|
|
597
706
|
}
|
|
598
707
|
if (!subField2.name) {
|
|
@@ -639,7 +748,7 @@ async function handleArrayType2(value, field, context) {
|
|
|
639
748
|
const result = await validateFormanValue(
|
|
640
749
|
item,
|
|
641
750
|
Array.isArray(field.spec) ? { name: index.toString(), type: "collection", spec: field.spec } : Object.assign({}, field.spec, { name: index.toString() }),
|
|
642
|
-
{ ...context, path: [...context.path, index
|
|
751
|
+
{ ...context, path: [...context.path, index] }
|
|
643
752
|
);
|
|
644
753
|
errors.push(...result.errors);
|
|
645
754
|
}
|
|
@@ -743,6 +852,15 @@ async function handleSelectType2(value, field, context) {
|
|
|
743
852
|
]
|
|
744
853
|
};
|
|
745
854
|
}
|
|
855
|
+
if (item.label) {
|
|
856
|
+
context.roots[context.domain].fieldStates.push({
|
|
857
|
+
path: context.path,
|
|
858
|
+
state: {
|
|
859
|
+
mode: isReferenceType(field.type) ? void 0 : "chose",
|
|
860
|
+
label: item.label
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}
|
|
746
864
|
if (item.nested) nested = item.nested;
|
|
747
865
|
}
|
|
748
866
|
if (nested) {
|
|
@@ -811,18 +929,6 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
811
929
|
}
|
|
812
930
|
async function handlePrimitiveType2(value, field, context) {
|
|
813
931
|
const errors = [];
|
|
814
|
-
if (containsIMLExpression(value)) {
|
|
815
|
-
return {
|
|
816
|
-
valid: true,
|
|
817
|
-
errors
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
if (errors.length > 0) {
|
|
821
|
-
return {
|
|
822
|
-
valid: false,
|
|
823
|
-
errors
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
932
|
if (field.validate) {
|
|
827
933
|
if (typeof value === "string") {
|
|
828
934
|
if (field.validate.pattern && !new RegExp(
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
3
3
|
/**
|
|
4
4
|
* Valid Forman Schema field types
|
|
5
5
|
*/
|
|
6
|
-
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'keychain' | 'datastore' | 'udt' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
6
|
+
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
7
7
|
/**
|
|
8
8
|
* Validation configuration for Forman Schema fields
|
|
9
9
|
*/
|
|
@@ -163,10 +163,21 @@ type FormanValidationResult = {
|
|
|
163
163
|
/** Error message */
|
|
164
164
|
message: string;
|
|
165
165
|
}[];
|
|
166
|
+
/** States of fields grouped by domain */
|
|
167
|
+
states?: Record<string, FormanSchemaFieldState>;
|
|
168
|
+
};
|
|
169
|
+
type FormanSchemaFieldState = {
|
|
170
|
+
mode?: 'chose' | 'edit';
|
|
171
|
+
label?: string;
|
|
172
|
+
data?: Record<string, unknown>;
|
|
173
|
+
nested?: Record<string, FormanSchemaFieldState>;
|
|
174
|
+
items?: Record<string, FormanSchemaFieldState>[];
|
|
166
175
|
};
|
|
167
176
|
type FormanValidationOptions = {
|
|
168
177
|
/** Unknown fields are not allowed when strict is true */
|
|
169
178
|
strict?: boolean;
|
|
179
|
+
/** Whether to generate states for fields */
|
|
180
|
+
states?: boolean;
|
|
170
181
|
/** Remote resource resolver */
|
|
171
182
|
resolveRemote?(path: string, data: Record<string, unknown>): Promise<unknown>;
|
|
172
183
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
3
3
|
/**
|
|
4
4
|
* Valid Forman Schema field types
|
|
5
5
|
*/
|
|
6
|
-
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'keychain' | 'datastore' | 'udt' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
6
|
+
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
7
7
|
/**
|
|
8
8
|
* Validation configuration for Forman Schema fields
|
|
9
9
|
*/
|
|
@@ -163,10 +163,21 @@ type FormanValidationResult = {
|
|
|
163
163
|
/** Error message */
|
|
164
164
|
message: string;
|
|
165
165
|
}[];
|
|
166
|
+
/** States of fields grouped by domain */
|
|
167
|
+
states?: Record<string, FormanSchemaFieldState>;
|
|
168
|
+
};
|
|
169
|
+
type FormanSchemaFieldState = {
|
|
170
|
+
mode?: 'chose' | 'edit';
|
|
171
|
+
label?: string;
|
|
172
|
+
data?: Record<string, unknown>;
|
|
173
|
+
nested?: Record<string, FormanSchemaFieldState>;
|
|
174
|
+
items?: Record<string, FormanSchemaFieldState>[];
|
|
166
175
|
};
|
|
167
176
|
type FormanValidationOptions = {
|
|
168
177
|
/** Unknown fields are not allowed when strict is true */
|
|
169
178
|
strict?: boolean;
|
|
179
|
+
/** Whether to generate states for fields */
|
|
180
|
+
states?: boolean;
|
|
170
181
|
/** Remote resource resolver */
|
|
171
182
|
resolveRemote?(path: string, data: Record<string, unknown>): Promise<unknown>;
|
|
172
183
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
// src/utils.ts
|
|
2
2
|
var FORMAN_VISUAL_TYPES = ["banner", "markdown", "html", "separator"];
|
|
3
|
+
function isVisualType(type) {
|
|
4
|
+
return FORMAN_VISUAL_TYPES.includes(type);
|
|
5
|
+
}
|
|
6
|
+
var FORMAN_REFERENCE_TYPES = [
|
|
7
|
+
"account",
|
|
8
|
+
"hook",
|
|
9
|
+
"device",
|
|
10
|
+
"keychain",
|
|
11
|
+
"datastore",
|
|
12
|
+
"aiagent",
|
|
13
|
+
"udt",
|
|
14
|
+
"scenario"
|
|
15
|
+
];
|
|
16
|
+
function isReferenceType(type) {
|
|
17
|
+
return FORMAN_REFERENCE_TYPES.includes(type);
|
|
18
|
+
}
|
|
3
19
|
function noEmpty(text) {
|
|
4
20
|
return text?.trim() || void 0;
|
|
5
21
|
}
|
|
@@ -13,13 +29,19 @@ function containsIMLExpression(value) {
|
|
|
13
29
|
if (typeof value !== "string") return false;
|
|
14
30
|
return value.indexOf("{{") > -1 && value.indexOf("}}") > -1;
|
|
15
31
|
}
|
|
32
|
+
function isPrimitiveIMLExpression(value) {
|
|
33
|
+
if (typeof value !== "string") return false;
|
|
34
|
+
return value.lastIndexOf("{{") === 0 && value.indexOf("}}") === value.length - 2;
|
|
35
|
+
}
|
|
16
36
|
var API_ENDPOINTS = {
|
|
17
37
|
account: "api://connections/{{kind}}",
|
|
18
38
|
aiagent: "api://ai-agents/v1/agents",
|
|
19
39
|
datastore: "api://data-stores",
|
|
20
40
|
hook: "api://hooks/{{kind}}",
|
|
41
|
+
device: "api://devices",
|
|
21
42
|
keychain: "api://keys/{{kind}}",
|
|
22
|
-
udt: "api://data-structures"
|
|
43
|
+
udt: "api://data-structures",
|
|
44
|
+
scenario: "api://scenario-list"
|
|
23
45
|
};
|
|
24
46
|
function normalizeFormanFieldType(field) {
|
|
25
47
|
const [type, kind] = field.type.split(":");
|
|
@@ -38,6 +60,69 @@ function normalizeFormanFieldType(field) {
|
|
|
38
60
|
}
|
|
39
61
|
};
|
|
40
62
|
}
|
|
63
|
+
function buildRestoreStructure(items) {
|
|
64
|
+
const result = {};
|
|
65
|
+
for (const item of items) {
|
|
66
|
+
const { domain, path, state } = item;
|
|
67
|
+
if (!result[domain]) {
|
|
68
|
+
result[domain] = {};
|
|
69
|
+
}
|
|
70
|
+
let current = result[domain];
|
|
71
|
+
for (const [index, key] of path.entries()) {
|
|
72
|
+
const nextKey = path[index + 1];
|
|
73
|
+
const isLastElement = index === path.length - 1;
|
|
74
|
+
const nextIsNumber = typeof nextKey === "number";
|
|
75
|
+
if (Array.isArray(current)) {
|
|
76
|
+
if (typeof key !== "number") {
|
|
77
|
+
throw new Error("Invalid path");
|
|
78
|
+
}
|
|
79
|
+
if (!current[key]) {
|
|
80
|
+
current[key] = {};
|
|
81
|
+
}
|
|
82
|
+
if (nextIsNumber) {
|
|
83
|
+
if (!current[key].value) {
|
|
84
|
+
current[key].value = {
|
|
85
|
+
mode: "chose",
|
|
86
|
+
items: []
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!current[key].value.items) {
|
|
90
|
+
current[key].value.items = [];
|
|
91
|
+
}
|
|
92
|
+
current = current[key].value.items;
|
|
93
|
+
} else if (isLastElement) {
|
|
94
|
+
Object.assign(current[key], state);
|
|
95
|
+
} else {
|
|
96
|
+
current = current[key];
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (typeof key !== "string") {
|
|
100
|
+
throw new Error("Invalid path");
|
|
101
|
+
}
|
|
102
|
+
if (!current[key]) {
|
|
103
|
+
current[key] = {};
|
|
104
|
+
}
|
|
105
|
+
if (isLastElement) {
|
|
106
|
+
Object.assign(current[key], state);
|
|
107
|
+
} else {
|
|
108
|
+
if (nextIsNumber) {
|
|
109
|
+
if (!current[key].items) {
|
|
110
|
+
current[key].items = [];
|
|
111
|
+
current[key].mode = "chose";
|
|
112
|
+
}
|
|
113
|
+
current = current[key].items;
|
|
114
|
+
} else {
|
|
115
|
+
if (!current[key].nested) {
|
|
116
|
+
current[key].nested = {};
|
|
117
|
+
}
|
|
118
|
+
current = current[key].nested;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
41
126
|
|
|
42
127
|
// src/forman.ts
|
|
43
128
|
var SchemaConversionError = class extends Error {
|
|
@@ -149,7 +234,7 @@ function handleCollectionType(field, result, context) {
|
|
|
149
234
|
required: []
|
|
150
235
|
});
|
|
151
236
|
function addField(subField, tail) {
|
|
152
|
-
if (
|
|
237
|
+
if (isVisualType(subField.type)) {
|
|
153
238
|
return;
|
|
154
239
|
}
|
|
155
240
|
if (!subField.name) return;
|
|
@@ -394,6 +479,7 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
394
479
|
(acc, domain) => {
|
|
395
480
|
acc[domain] = {
|
|
396
481
|
seenFields: /* @__PURE__ */ new Set(),
|
|
482
|
+
fieldStates: [],
|
|
397
483
|
validateFields: (fields, context) => {
|
|
398
484
|
return validateFormanValue(
|
|
399
485
|
domains[domain].values,
|
|
@@ -451,11 +537,14 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
451
537
|
}
|
|
452
538
|
return {
|
|
453
539
|
valid: errors.length === 0,
|
|
454
|
-
errors
|
|
540
|
+
errors,
|
|
541
|
+
states: errors.length === 0 && options?.states ? buildRestoreStructure(
|
|
542
|
+
Object.keys(domains).map((domain) => roots[domain].fieldStates.map((state) => ({ domain, ...state }))).flat()
|
|
543
|
+
) : void 0
|
|
455
544
|
};
|
|
456
545
|
}
|
|
457
546
|
async function validateFormanValue(value, field, context) {
|
|
458
|
-
if (
|
|
547
|
+
if (isVisualType(field.type)) {
|
|
459
548
|
return {
|
|
460
549
|
valid: true,
|
|
461
550
|
errors: []
|
|
@@ -483,7 +572,7 @@ async function validateFormanValue(value, field, context) {
|
|
|
483
572
|
const expectedType = FORMAN_TYPE_MAP2[normalizedField.type];
|
|
484
573
|
let actualType = typeof value;
|
|
485
574
|
if (actualType === "object" && Array.isArray(value)) actualType = "array";
|
|
486
|
-
if (expectedType && expectedType !== actualType) {
|
|
575
|
+
if (expectedType && expectedType !== actualType && !isPrimitiveIMLExpression(value)) {
|
|
487
576
|
return {
|
|
488
577
|
valid: false,
|
|
489
578
|
errors: [
|
|
@@ -495,6 +584,24 @@ async function validateFormanValue(value, field, context) {
|
|
|
495
584
|
]
|
|
496
585
|
};
|
|
497
586
|
}
|
|
587
|
+
if (containsIMLExpression(value)) {
|
|
588
|
+
if (normalizedField.mappable === false) {
|
|
589
|
+
return {
|
|
590
|
+
valid: false,
|
|
591
|
+
errors: [
|
|
592
|
+
{
|
|
593
|
+
domain: context.domain,
|
|
594
|
+
path: context.path.join("."),
|
|
595
|
+
message: "Value contains prohibited IML expression."
|
|
596
|
+
}
|
|
597
|
+
]
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
valid: true,
|
|
602
|
+
errors: []
|
|
603
|
+
};
|
|
604
|
+
}
|
|
498
605
|
switch (normalizedField.type) {
|
|
499
606
|
case "collection":
|
|
500
607
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -503,10 +610,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
503
610
|
case "select":
|
|
504
611
|
case "account":
|
|
505
612
|
case "hook":
|
|
613
|
+
case "device":
|
|
506
614
|
case "keychain":
|
|
507
615
|
case "datastore":
|
|
508
616
|
case "aiagent":
|
|
509
617
|
case "udt":
|
|
618
|
+
case "scenario":
|
|
510
619
|
case "file":
|
|
511
620
|
case "folder":
|
|
512
621
|
return handleSelectType2(value, normalizedField, context);
|
|
@@ -546,7 +655,7 @@ async function handleCollectionType2(value, field, context) {
|
|
|
546
655
|
};
|
|
547
656
|
}
|
|
548
657
|
}
|
|
549
|
-
if (
|
|
658
|
+
if (isVisualType(subField.type)) {
|
|
550
659
|
continue;
|
|
551
660
|
}
|
|
552
661
|
if (!subField.name) {
|
|
@@ -563,7 +672,7 @@ async function handleCollectionType2(value, field, context) {
|
|
|
563
672
|
path: [...path, subField.name],
|
|
564
673
|
validateNestedFields: async (fields, context2) => {
|
|
565
674
|
for (const subField2 of fields) {
|
|
566
|
-
if (
|
|
675
|
+
if (isVisualType(subField2.type)) {
|
|
567
676
|
continue;
|
|
568
677
|
}
|
|
569
678
|
if (!subField2.name) {
|
|
@@ -610,7 +719,7 @@ async function handleArrayType2(value, field, context) {
|
|
|
610
719
|
const result = await validateFormanValue(
|
|
611
720
|
item,
|
|
612
721
|
Array.isArray(field.spec) ? { name: index.toString(), type: "collection", spec: field.spec } : Object.assign({}, field.spec, { name: index.toString() }),
|
|
613
|
-
{ ...context, path: [...context.path, index
|
|
722
|
+
{ ...context, path: [...context.path, index] }
|
|
614
723
|
);
|
|
615
724
|
errors.push(...result.errors);
|
|
616
725
|
}
|
|
@@ -714,6 +823,15 @@ async function handleSelectType2(value, field, context) {
|
|
|
714
823
|
]
|
|
715
824
|
};
|
|
716
825
|
}
|
|
826
|
+
if (item.label) {
|
|
827
|
+
context.roots[context.domain].fieldStates.push({
|
|
828
|
+
path: context.path,
|
|
829
|
+
state: {
|
|
830
|
+
mode: isReferenceType(field.type) ? void 0 : "chose",
|
|
831
|
+
label: item.label
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
}
|
|
717
835
|
if (item.nested) nested = item.nested;
|
|
718
836
|
}
|
|
719
837
|
if (nested) {
|
|
@@ -782,18 +900,6 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
782
900
|
}
|
|
783
901
|
async function handlePrimitiveType2(value, field, context) {
|
|
784
902
|
const errors = [];
|
|
785
|
-
if (containsIMLExpression(value)) {
|
|
786
|
-
return {
|
|
787
|
-
valid: true,
|
|
788
|
-
errors
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
if (errors.length > 0) {
|
|
792
|
-
return {
|
|
793
|
-
valid: false,
|
|
794
|
-
errors
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
903
|
if (field.validate) {
|
|
798
904
|
if (typeof value === "string") {
|
|
799
905
|
if (field.validate.pattern && !new RegExp(
|