@makehq/forman-schema 1.8.3 → 1.9.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 +401 -6
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +401 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -98,6 +98,60 @@ function findValueInSelectOptions(field, value, optionsAndGroups) {
|
|
|
98
98
|
const found = optionsAndGroups.find((option) => "value" in option && option.value === value);
|
|
99
99
|
return found;
|
|
100
100
|
}
|
|
101
|
+
function pathToString(path) {
|
|
102
|
+
let result = "";
|
|
103
|
+
for (const key of path) {
|
|
104
|
+
if (typeof key === "number") {
|
|
105
|
+
result += `[${key}]`;
|
|
106
|
+
} else {
|
|
107
|
+
if (result.length > 0) {
|
|
108
|
+
result += ".";
|
|
109
|
+
}
|
|
110
|
+
if (key.includes("`")) {
|
|
111
|
+
throw new Error(`Invalid path key: backticks are not allowed in key "${key}"`);
|
|
112
|
+
}
|
|
113
|
+
result += key.includes(".") ? `\`${key}\`` : key;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function stringToPath(str) {
|
|
119
|
+
const path = [];
|
|
120
|
+
let i = 0;
|
|
121
|
+
while (i < str.length) {
|
|
122
|
+
if (str[i] === ".") {
|
|
123
|
+
i++;
|
|
124
|
+
if (i >= str.length) break;
|
|
125
|
+
}
|
|
126
|
+
if (str[i] === "[") {
|
|
127
|
+
const end = str.indexOf("]", i);
|
|
128
|
+
if (end === -1) {
|
|
129
|
+
throw new Error(`Invalid path: missing closing bracket in "${str}"`);
|
|
130
|
+
}
|
|
131
|
+
const num = Number(str.slice(i + 1, end));
|
|
132
|
+
if (!Number.isInteger(num) || num < 0) {
|
|
133
|
+
throw new Error(`Invalid path: non-numeric or negative index in "${str}"`);
|
|
134
|
+
}
|
|
135
|
+
path.push(num);
|
|
136
|
+
i = end + 1;
|
|
137
|
+
} else if (str[i] === "`") {
|
|
138
|
+
const end = str.indexOf("`", i + 1);
|
|
139
|
+
if (end === -1) {
|
|
140
|
+
throw new Error(`Invalid path: missing closing backtick in "${str}"`);
|
|
141
|
+
}
|
|
142
|
+
path.push(str.slice(i + 1, end));
|
|
143
|
+
i = end + 1;
|
|
144
|
+
} else {
|
|
145
|
+
let end = i;
|
|
146
|
+
while (end < str.length && str[end] !== "." && str[end] !== "[") {
|
|
147
|
+
end++;
|
|
148
|
+
}
|
|
149
|
+
path.push(str.slice(i, end));
|
|
150
|
+
i = end;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return path;
|
|
154
|
+
}
|
|
101
155
|
function buildRestoreStructure(items) {
|
|
102
156
|
const result = {};
|
|
103
157
|
for (const item of items) {
|
|
@@ -223,6 +277,248 @@ var IML_BINARY_FILTER_OPERATORS = [
|
|
|
223
277
|
];
|
|
224
278
|
var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
|
|
225
279
|
|
|
280
|
+
// src/composites/udttype.ts
|
|
281
|
+
function udttypeExpand(field) {
|
|
282
|
+
field.type = "select";
|
|
283
|
+
field.options = {
|
|
284
|
+
store: [
|
|
285
|
+
{
|
|
286
|
+
label: "Array",
|
|
287
|
+
value: "array",
|
|
288
|
+
nested: [
|
|
289
|
+
{
|
|
290
|
+
name: "spec",
|
|
291
|
+
type: "collection",
|
|
292
|
+
label: "Array Item Specification",
|
|
293
|
+
spec: [
|
|
294
|
+
{
|
|
295
|
+
name: "type",
|
|
296
|
+
label: "Type",
|
|
297
|
+
type: "udttype",
|
|
298
|
+
required: true,
|
|
299
|
+
default: "text"
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
mappable: false
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "required",
|
|
306
|
+
label: "Required",
|
|
307
|
+
type: "boolean",
|
|
308
|
+
required: true,
|
|
309
|
+
default: false,
|
|
310
|
+
mappable: false
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
label: "Collection",
|
|
316
|
+
value: "collection",
|
|
317
|
+
nested: [
|
|
318
|
+
{
|
|
319
|
+
name: "spec",
|
|
320
|
+
label: "Specification",
|
|
321
|
+
type: "udtspec"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "sequence",
|
|
325
|
+
label: "Preserve the order of object keys",
|
|
326
|
+
type: "boolean",
|
|
327
|
+
mappable: false
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "required",
|
|
331
|
+
label: "Required",
|
|
332
|
+
type: "boolean",
|
|
333
|
+
required: true,
|
|
334
|
+
default: false,
|
|
335
|
+
mappable: false
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
label: "Date",
|
|
341
|
+
value: "date",
|
|
342
|
+
nested: [
|
|
343
|
+
{
|
|
344
|
+
name: "required",
|
|
345
|
+
label: "Required",
|
|
346
|
+
type: "boolean",
|
|
347
|
+
required: true,
|
|
348
|
+
default: false,
|
|
349
|
+
mappable: false
|
|
350
|
+
}
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
label: "Text",
|
|
355
|
+
value: "text",
|
|
356
|
+
nested: [
|
|
357
|
+
{
|
|
358
|
+
name: "default",
|
|
359
|
+
label: "Default value",
|
|
360
|
+
placeholder: "Enter default value",
|
|
361
|
+
type: "text"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "required",
|
|
365
|
+
label: "Required",
|
|
366
|
+
type: "boolean",
|
|
367
|
+
required: true,
|
|
368
|
+
default: false,
|
|
369
|
+
mappable: false
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "multiline",
|
|
373
|
+
label: "Multi-line",
|
|
374
|
+
type: "boolean",
|
|
375
|
+
required: true,
|
|
376
|
+
default: false,
|
|
377
|
+
mappable: false
|
|
378
|
+
}
|
|
379
|
+
]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
label: "Number",
|
|
383
|
+
value: "number",
|
|
384
|
+
nested: [
|
|
385
|
+
{
|
|
386
|
+
name: "default",
|
|
387
|
+
label: "Default value",
|
|
388
|
+
placeholder: "Enter default value",
|
|
389
|
+
type: "number"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "required",
|
|
393
|
+
label: "Required",
|
|
394
|
+
type: "boolean",
|
|
395
|
+
required: true,
|
|
396
|
+
default: false,
|
|
397
|
+
mappable: false
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
label: "Boolean",
|
|
403
|
+
value: "boolean",
|
|
404
|
+
nested: [
|
|
405
|
+
{
|
|
406
|
+
name: "default",
|
|
407
|
+
label: "Default value",
|
|
408
|
+
placeholder: "Enter default value",
|
|
409
|
+
type: "boolean",
|
|
410
|
+
mappable: false
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: "required",
|
|
414
|
+
label: "Required",
|
|
415
|
+
type: "boolean",
|
|
416
|
+
required: true,
|
|
417
|
+
default: false,
|
|
418
|
+
mappable: false
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
label: "Binary Data",
|
|
424
|
+
value: "buffer",
|
|
425
|
+
nested: [
|
|
426
|
+
{
|
|
427
|
+
name: "required",
|
|
428
|
+
label: "Required",
|
|
429
|
+
type: "boolean",
|
|
430
|
+
required: true,
|
|
431
|
+
default: false,
|
|
432
|
+
mappable: false
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "codepage",
|
|
436
|
+
label: "Codepage",
|
|
437
|
+
type: "text",
|
|
438
|
+
help: "Possible values: `binary`, `utf8`. Leave empty if you're not sure."
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
}
|
|
442
|
+
]
|
|
443
|
+
};
|
|
444
|
+
return field;
|
|
445
|
+
}
|
|
446
|
+
function udttypeExtractInner(schema) {
|
|
447
|
+
const { title, description, ...inner } = schema;
|
|
448
|
+
return inner;
|
|
449
|
+
}
|
|
450
|
+
function udttypeWrapRef(ref, field) {
|
|
451
|
+
return {
|
|
452
|
+
allOf: [{ $ref: ref }],
|
|
453
|
+
title: noEmpty(field.label),
|
|
454
|
+
description: noEmpty(field.help),
|
|
455
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function udttypeCollapse(field) {
|
|
459
|
+
return {
|
|
460
|
+
type: "udttype",
|
|
461
|
+
label: noEmpty(field.title),
|
|
462
|
+
help: noEmpty(field.description),
|
|
463
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/composites/udtspec.ts
|
|
468
|
+
function udtspecExpand(field) {
|
|
469
|
+
field.type = "array";
|
|
470
|
+
field.spec = [
|
|
471
|
+
{
|
|
472
|
+
name: "name",
|
|
473
|
+
label: "Name",
|
|
474
|
+
placeholder: "Enter name",
|
|
475
|
+
type: "text",
|
|
476
|
+
required: true
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "label",
|
|
480
|
+
label: "Label",
|
|
481
|
+
help: "Display name for better readability.",
|
|
482
|
+
type: "text",
|
|
483
|
+
advanced: true
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "help",
|
|
487
|
+
label: "Description",
|
|
488
|
+
type: "text",
|
|
489
|
+
multiline: true,
|
|
490
|
+
required: false,
|
|
491
|
+
placeholder: "Enter description"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
name: "type",
|
|
495
|
+
label: "Type",
|
|
496
|
+
type: "udttype",
|
|
497
|
+
required: true,
|
|
498
|
+
default: "text"
|
|
499
|
+
}
|
|
500
|
+
];
|
|
501
|
+
return field;
|
|
502
|
+
}
|
|
503
|
+
function udtspecExtractInner(schema) {
|
|
504
|
+
return schema.items;
|
|
505
|
+
}
|
|
506
|
+
function udtspecWrapRef(ref, field) {
|
|
507
|
+
return {
|
|
508
|
+
type: "array",
|
|
509
|
+
title: noEmpty(field.label),
|
|
510
|
+
description: noEmpty(field.help),
|
|
511
|
+
items: { $ref: ref }
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function udtspecCollapse(field) {
|
|
515
|
+
return {
|
|
516
|
+
type: "udtspec",
|
|
517
|
+
label: noEmpty(field.title),
|
|
518
|
+
help: noEmpty(field.description)
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
226
522
|
// src/forman.ts
|
|
227
523
|
var SchemaConversionError = class extends Error {
|
|
228
524
|
/** Field that caused the error */
|
|
@@ -269,6 +565,8 @@ var FORMAN_TYPE_MAP = {
|
|
|
269
565
|
pkey: "string",
|
|
270
566
|
port: "number",
|
|
271
567
|
select: "string",
|
|
568
|
+
udttype: "string",
|
|
569
|
+
udtspec: "array",
|
|
272
570
|
time: "string",
|
|
273
571
|
timestamp: "string",
|
|
274
572
|
timezone: "string",
|
|
@@ -298,14 +596,52 @@ function createDefaultContext() {
|
|
|
298
596
|
tail: [],
|
|
299
597
|
path: [],
|
|
300
598
|
roots: {},
|
|
599
|
+
definitions: {},
|
|
301
600
|
addConditionalFields: () => {
|
|
302
601
|
throw new SchemaConversionError("Cannot serialize nested fields without parent field.");
|
|
303
602
|
}
|
|
304
603
|
};
|
|
305
604
|
}
|
|
306
|
-
|
|
605
|
+
var compositeHandlers = {
|
|
606
|
+
udtspec: { expand: udtspecExpand, extractInner: udtspecExtractInner, wrapRef: udtspecWrapRef },
|
|
607
|
+
udttype: { expand: udttypeExpand, extractInner: udttypeExtractInner, wrapRef: udttypeWrapRef }
|
|
608
|
+
};
|
|
609
|
+
function toJSONSchemaInternal(field, context) {
|
|
307
610
|
validateFormanField(field);
|
|
308
611
|
const normalizedField = normalizeFormanFieldType(field);
|
|
612
|
+
const handler = compositeHandlers[normalizedField.type];
|
|
613
|
+
if (handler) {
|
|
614
|
+
const type = normalizedField.type;
|
|
615
|
+
const ref = `#/definitions/${type}`;
|
|
616
|
+
if (!context.definitions?.[type]) {
|
|
617
|
+
if (context.definitions) {
|
|
618
|
+
context.definitions[type] = {};
|
|
619
|
+
}
|
|
620
|
+
const expandField = { ...normalizedField };
|
|
621
|
+
delete expandField.label;
|
|
622
|
+
delete expandField.help;
|
|
623
|
+
const expanded = handler.expand(expandField);
|
|
624
|
+
const expandedResult = toJSONSchemaInternal(expanded, context);
|
|
625
|
+
const inner = handler.extractInner(expandedResult);
|
|
626
|
+
Object.defineProperty(inner, "x-composite", {
|
|
627
|
+
configurable: true,
|
|
628
|
+
enumerable: true,
|
|
629
|
+
writable: true,
|
|
630
|
+
value: type
|
|
631
|
+
});
|
|
632
|
+
if (context.definitions) {
|
|
633
|
+
context.definitions[type] = inner;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const wrapper = handler.wrapRef(ref, normalizedField);
|
|
637
|
+
Object.defineProperty(wrapper, "x-composite", {
|
|
638
|
+
configurable: true,
|
|
639
|
+
enumerable: true,
|
|
640
|
+
writable: true,
|
|
641
|
+
value: type
|
|
642
|
+
});
|
|
643
|
+
return wrapper;
|
|
644
|
+
}
|
|
309
645
|
const result = {
|
|
310
646
|
type: FORMAN_TYPE_MAP[normalizedField.type],
|
|
311
647
|
title: noEmpty(normalizedField.label),
|
|
@@ -723,6 +1059,8 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
723
1059
|
pkey: "string",
|
|
724
1060
|
port: "number",
|
|
725
1061
|
select: void 0,
|
|
1062
|
+
udttype: "string",
|
|
1063
|
+
udtspec: "array",
|
|
726
1064
|
time: "string",
|
|
727
1065
|
timestamp: "string",
|
|
728
1066
|
timezone: "string",
|
|
@@ -795,6 +1133,25 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
795
1133
|
}
|
|
796
1134
|
);
|
|
797
1135
|
errors.push(...result.errors);
|
|
1136
|
+
const restoreExtras = options?.states && domains[domain]?.restoreExtras;
|
|
1137
|
+
if (restoreExtras) {
|
|
1138
|
+
const fieldStateMap = new Map(
|
|
1139
|
+
roots[domain].fieldStates.map(({ path, state }) => {
|
|
1140
|
+
return [pathToString(path), state];
|
|
1141
|
+
})
|
|
1142
|
+
);
|
|
1143
|
+
for (const [fieldPath, extra] of Object.entries(restoreExtras)) {
|
|
1144
|
+
const state = fieldStateMap.get(fieldPath);
|
|
1145
|
+
if (state) {
|
|
1146
|
+
state.extra = extra;
|
|
1147
|
+
} else {
|
|
1148
|
+
roots[domain].fieldStates.push({
|
|
1149
|
+
path: stringToPath(fieldPath),
|
|
1150
|
+
state: { extra }
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
798
1155
|
}
|
|
799
1156
|
return {
|
|
800
1157
|
valid: errors.length === 0,
|
|
@@ -863,6 +1220,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
863
1220
|
errors: []
|
|
864
1221
|
};
|
|
865
1222
|
}
|
|
1223
|
+
if (normalizedField.type === "udttype") {
|
|
1224
|
+
return validateFormanValue(value, udttypeExpand({ ...normalizedField }), context);
|
|
1225
|
+
}
|
|
1226
|
+
if (normalizedField.type === "udtspec") {
|
|
1227
|
+
return validateFormanValue(value, udtspecExpand({ ...normalizedField }), context);
|
|
1228
|
+
}
|
|
866
1229
|
switch (normalizedField.type) {
|
|
867
1230
|
case "collection":
|
|
868
1231
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -1212,7 +1575,21 @@ async function handleSelectType(value, field, context) {
|
|
|
1212
1575
|
let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
|
|
1213
1576
|
if (typeof optionsOrGroups === "string") {
|
|
1214
1577
|
try {
|
|
1215
|
-
|
|
1578
|
+
const resolved = await context.resolveRemote(optionsOrGroups, context);
|
|
1579
|
+
if (resolved == null || typeof resolved !== "object") {
|
|
1580
|
+
return {
|
|
1581
|
+
valid: false,
|
|
1582
|
+
errors: [
|
|
1583
|
+
...errors,
|
|
1584
|
+
{
|
|
1585
|
+
domain: context.domain,
|
|
1586
|
+
path: context.path.join("."),
|
|
1587
|
+
message: `Remote resource ${optionsOrGroups} returned no data.`
|
|
1588
|
+
}
|
|
1589
|
+
]
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
optionsOrGroups = Array.isArray(resolved) ? resolved : [resolved];
|
|
1216
1593
|
} catch (error) {
|
|
1217
1594
|
return {
|
|
1218
1595
|
valid: false,
|
|
@@ -1322,7 +1699,12 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
1322
1699
|
for (const item of store) {
|
|
1323
1700
|
if (typeof item === "string") {
|
|
1324
1701
|
try {
|
|
1325
|
-
|
|
1702
|
+
const resolved = await context.resolveRemote(item, context);
|
|
1703
|
+
if (Array.isArray(resolved)) {
|
|
1704
|
+
resolvedStore.push(...resolved);
|
|
1705
|
+
} else if (resolved && typeof resolved === "object") {
|
|
1706
|
+
resolvedStore.push(resolved);
|
|
1707
|
+
}
|
|
1326
1708
|
} catch (error) {
|
|
1327
1709
|
return {
|
|
1328
1710
|
valid: false,
|
|
@@ -1430,6 +1812,9 @@ var JSON_PRIMITIVE_TYPE_MAP = {
|
|
|
1430
1812
|
boolean: "boolean"
|
|
1431
1813
|
};
|
|
1432
1814
|
function toFormanSchema(field) {
|
|
1815
|
+
const compositeType = Object.getOwnPropertyDescriptor(field, "x-composite")?.value;
|
|
1816
|
+
if (compositeType === "udttype") return udttypeCollapse(field);
|
|
1817
|
+
if (compositeType === "udtspec") return udtspecCollapse(field);
|
|
1433
1818
|
switch (field.type) {
|
|
1434
1819
|
case "object":
|
|
1435
1820
|
if (!field.properties || !Object.entries(field.properties).length) {
|
|
@@ -1552,13 +1937,23 @@ function handleSearchDirective(formanField, directive) {
|
|
|
1552
1937
|
|
|
1553
1938
|
// src/index.ts
|
|
1554
1939
|
function toJSONSchema(field) {
|
|
1555
|
-
|
|
1940
|
+
const context = createDefaultContext();
|
|
1941
|
+
const result = toJSONSchemaInternal(field, context);
|
|
1942
|
+
if (Object.keys(context.definitions ?? {}).length > 0) {
|
|
1943
|
+
Object.defineProperty(result, "definitions", {
|
|
1944
|
+
configurable: true,
|
|
1945
|
+
enumerable: true,
|
|
1946
|
+
writable: true,
|
|
1947
|
+
value: context.definitions
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
return result;
|
|
1556
1951
|
}
|
|
1557
1952
|
function validateFormanWithDomains(domains, options) {
|
|
1558
1953
|
return validateFormanWithDomainsInternal(domains, options);
|
|
1559
1954
|
}
|
|
1560
|
-
function validateForman(values, schema, options) {
|
|
1561
|
-
return validateFormanWithDomains({ default: { values, schema } }, options);
|
|
1955
|
+
function validateForman(values, schema, options, restoreExtras) {
|
|
1956
|
+
return validateFormanWithDomains({ default: { values, schema, restoreExtras } }, options);
|
|
1562
1957
|
}
|
|
1563
1958
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1564
1959
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -205,6 +205,7 @@ type FormanSchemaFieldState = {
|
|
|
205
205
|
label?: string;
|
|
206
206
|
path?: Array<string>;
|
|
207
207
|
data?: Record<string, unknown>;
|
|
208
|
+
extra?: Record<string, unknown>;
|
|
208
209
|
nested?: Record<string, FormanSchemaFieldState>;
|
|
209
210
|
items?: Record<string, FormanSchemaFieldState>[];
|
|
210
211
|
};
|
|
@@ -239,14 +240,19 @@ declare function toJSONSchema(field: FormanSchemaField): JSONSchema7;
|
|
|
239
240
|
declare function validateFormanWithDomains(domains: Record<string, {
|
|
240
241
|
values: Record<string, unknown>;
|
|
241
242
|
schema: FormanSchemaField[];
|
|
243
|
+
/** Extra values injected into restore states, keyed by string path (dot notation, `[index]`, backtick-escaping). */
|
|
244
|
+
restoreExtras?: Record<string, Record<string, unknown>>;
|
|
242
245
|
}>, options?: FormanValidationOptions): Promise<FormanValidationResult>;
|
|
243
246
|
/**
|
|
244
247
|
* Validates a simple Forman values against a schema
|
|
245
248
|
* @param values The values to validate
|
|
246
249
|
* @param schema The schema to validate against
|
|
247
250
|
* @param options The validation options
|
|
251
|
+
* @param restoreExtras Values to be injected into restore objects of particular fields.
|
|
252
|
+
* Keyed by string path using dot notation for nested keys, `[index]` for array indices,
|
|
253
|
+
* and backtick-escaping for keys containing dots (e.g. `"a.b[0].c"`, `` "`dotted.key`.child" ``).
|
|
248
254
|
* @returns The validation result
|
|
249
255
|
*/
|
|
250
|
-
declare function validateForman(values: Record<string, unknown>, schema: FormanSchemaField[], options?: FormanValidationOptions): Promise<FormanValidationResult>;
|
|
256
|
+
declare function validateForman(values: Record<string, unknown>, schema: FormanSchemaField[], options?: FormanValidationOptions, restoreExtras?: Record<string, Record<string, unknown>>): Promise<FormanValidationResult>;
|
|
251
257
|
|
|
252
258
|
export { type FormanSchemaDirectoryOption, type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaOptionGroup, type FormanSchemaPathExtendedOptions, type FormanSchemaRPCButton, type FormanSchemaValue, toFormanSchema, toJSONSchema, validateForman, validateFormanWithDomains };
|
package/dist/index.d.ts
CHANGED
|
@@ -205,6 +205,7 @@ type FormanSchemaFieldState = {
|
|
|
205
205
|
label?: string;
|
|
206
206
|
path?: Array<string>;
|
|
207
207
|
data?: Record<string, unknown>;
|
|
208
|
+
extra?: Record<string, unknown>;
|
|
208
209
|
nested?: Record<string, FormanSchemaFieldState>;
|
|
209
210
|
items?: Record<string, FormanSchemaFieldState>[];
|
|
210
211
|
};
|
|
@@ -239,14 +240,19 @@ declare function toJSONSchema(field: FormanSchemaField): JSONSchema7;
|
|
|
239
240
|
declare function validateFormanWithDomains(domains: Record<string, {
|
|
240
241
|
values: Record<string, unknown>;
|
|
241
242
|
schema: FormanSchemaField[];
|
|
243
|
+
/** Extra values injected into restore states, keyed by string path (dot notation, `[index]`, backtick-escaping). */
|
|
244
|
+
restoreExtras?: Record<string, Record<string, unknown>>;
|
|
242
245
|
}>, options?: FormanValidationOptions): Promise<FormanValidationResult>;
|
|
243
246
|
/**
|
|
244
247
|
* Validates a simple Forman values against a schema
|
|
245
248
|
* @param values The values to validate
|
|
246
249
|
* @param schema The schema to validate against
|
|
247
250
|
* @param options The validation options
|
|
251
|
+
* @param restoreExtras Values to be injected into restore objects of particular fields.
|
|
252
|
+
* Keyed by string path using dot notation for nested keys, `[index]` for array indices,
|
|
253
|
+
* and backtick-escaping for keys containing dots (e.g. `"a.b[0].c"`, `` "`dotted.key`.child" ``).
|
|
248
254
|
* @returns The validation result
|
|
249
255
|
*/
|
|
250
|
-
declare function validateForman(values: Record<string, unknown>, schema: FormanSchemaField[], options?: FormanValidationOptions): Promise<FormanValidationResult>;
|
|
256
|
+
declare function validateForman(values: Record<string, unknown>, schema: FormanSchemaField[], options?: FormanValidationOptions, restoreExtras?: Record<string, Record<string, unknown>>): Promise<FormanValidationResult>;
|
|
251
257
|
|
|
252
258
|
export { type FormanSchemaDirectoryOption, type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaOptionGroup, type FormanSchemaPathExtendedOptions, type FormanSchemaRPCButton, type FormanSchemaValue, toFormanSchema, toJSONSchema, validateForman, validateFormanWithDomains };
|
package/dist/index.js
CHANGED
|
@@ -69,6 +69,60 @@ function findValueInSelectOptions(field, value, optionsAndGroups) {
|
|
|
69
69
|
const found = optionsAndGroups.find((option) => "value" in option && option.value === value);
|
|
70
70
|
return found;
|
|
71
71
|
}
|
|
72
|
+
function pathToString(path) {
|
|
73
|
+
let result = "";
|
|
74
|
+
for (const key of path) {
|
|
75
|
+
if (typeof key === "number") {
|
|
76
|
+
result += `[${key}]`;
|
|
77
|
+
} else {
|
|
78
|
+
if (result.length > 0) {
|
|
79
|
+
result += ".";
|
|
80
|
+
}
|
|
81
|
+
if (key.includes("`")) {
|
|
82
|
+
throw new Error(`Invalid path key: backticks are not allowed in key "${key}"`);
|
|
83
|
+
}
|
|
84
|
+
result += key.includes(".") ? `\`${key}\`` : key;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
function stringToPath(str) {
|
|
90
|
+
const path = [];
|
|
91
|
+
let i = 0;
|
|
92
|
+
while (i < str.length) {
|
|
93
|
+
if (str[i] === ".") {
|
|
94
|
+
i++;
|
|
95
|
+
if (i >= str.length) break;
|
|
96
|
+
}
|
|
97
|
+
if (str[i] === "[") {
|
|
98
|
+
const end = str.indexOf("]", i);
|
|
99
|
+
if (end === -1) {
|
|
100
|
+
throw new Error(`Invalid path: missing closing bracket in "${str}"`);
|
|
101
|
+
}
|
|
102
|
+
const num = Number(str.slice(i + 1, end));
|
|
103
|
+
if (!Number.isInteger(num) || num < 0) {
|
|
104
|
+
throw new Error(`Invalid path: non-numeric or negative index in "${str}"`);
|
|
105
|
+
}
|
|
106
|
+
path.push(num);
|
|
107
|
+
i = end + 1;
|
|
108
|
+
} else if (str[i] === "`") {
|
|
109
|
+
const end = str.indexOf("`", i + 1);
|
|
110
|
+
if (end === -1) {
|
|
111
|
+
throw new Error(`Invalid path: missing closing backtick in "${str}"`);
|
|
112
|
+
}
|
|
113
|
+
path.push(str.slice(i + 1, end));
|
|
114
|
+
i = end + 1;
|
|
115
|
+
} else {
|
|
116
|
+
let end = i;
|
|
117
|
+
while (end < str.length && str[end] !== "." && str[end] !== "[") {
|
|
118
|
+
end++;
|
|
119
|
+
}
|
|
120
|
+
path.push(str.slice(i, end));
|
|
121
|
+
i = end;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return path;
|
|
125
|
+
}
|
|
72
126
|
function buildRestoreStructure(items) {
|
|
73
127
|
const result = {};
|
|
74
128
|
for (const item of items) {
|
|
@@ -194,6 +248,248 @@ var IML_BINARY_FILTER_OPERATORS = [
|
|
|
194
248
|
];
|
|
195
249
|
var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
|
|
196
250
|
|
|
251
|
+
// src/composites/udttype.ts
|
|
252
|
+
function udttypeExpand(field) {
|
|
253
|
+
field.type = "select";
|
|
254
|
+
field.options = {
|
|
255
|
+
store: [
|
|
256
|
+
{
|
|
257
|
+
label: "Array",
|
|
258
|
+
value: "array",
|
|
259
|
+
nested: [
|
|
260
|
+
{
|
|
261
|
+
name: "spec",
|
|
262
|
+
type: "collection",
|
|
263
|
+
label: "Array Item Specification",
|
|
264
|
+
spec: [
|
|
265
|
+
{
|
|
266
|
+
name: "type",
|
|
267
|
+
label: "Type",
|
|
268
|
+
type: "udttype",
|
|
269
|
+
required: true,
|
|
270
|
+
default: "text"
|
|
271
|
+
}
|
|
272
|
+
],
|
|
273
|
+
mappable: false
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "required",
|
|
277
|
+
label: "Required",
|
|
278
|
+
type: "boolean",
|
|
279
|
+
required: true,
|
|
280
|
+
default: false,
|
|
281
|
+
mappable: false
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
label: "Collection",
|
|
287
|
+
value: "collection",
|
|
288
|
+
nested: [
|
|
289
|
+
{
|
|
290
|
+
name: "spec",
|
|
291
|
+
label: "Specification",
|
|
292
|
+
type: "udtspec"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: "sequence",
|
|
296
|
+
label: "Preserve the order of object keys",
|
|
297
|
+
type: "boolean",
|
|
298
|
+
mappable: false
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
name: "required",
|
|
302
|
+
label: "Required",
|
|
303
|
+
type: "boolean",
|
|
304
|
+
required: true,
|
|
305
|
+
default: false,
|
|
306
|
+
mappable: false
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
label: "Date",
|
|
312
|
+
value: "date",
|
|
313
|
+
nested: [
|
|
314
|
+
{
|
|
315
|
+
name: "required",
|
|
316
|
+
label: "Required",
|
|
317
|
+
type: "boolean",
|
|
318
|
+
required: true,
|
|
319
|
+
default: false,
|
|
320
|
+
mappable: false
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
label: "Text",
|
|
326
|
+
value: "text",
|
|
327
|
+
nested: [
|
|
328
|
+
{
|
|
329
|
+
name: "default",
|
|
330
|
+
label: "Default value",
|
|
331
|
+
placeholder: "Enter default value",
|
|
332
|
+
type: "text"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "required",
|
|
336
|
+
label: "Required",
|
|
337
|
+
type: "boolean",
|
|
338
|
+
required: true,
|
|
339
|
+
default: false,
|
|
340
|
+
mappable: false
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "multiline",
|
|
344
|
+
label: "Multi-line",
|
|
345
|
+
type: "boolean",
|
|
346
|
+
required: true,
|
|
347
|
+
default: false,
|
|
348
|
+
mappable: false
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
label: "Number",
|
|
354
|
+
value: "number",
|
|
355
|
+
nested: [
|
|
356
|
+
{
|
|
357
|
+
name: "default",
|
|
358
|
+
label: "Default value",
|
|
359
|
+
placeholder: "Enter default value",
|
|
360
|
+
type: "number"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "required",
|
|
364
|
+
label: "Required",
|
|
365
|
+
type: "boolean",
|
|
366
|
+
required: true,
|
|
367
|
+
default: false,
|
|
368
|
+
mappable: false
|
|
369
|
+
}
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
label: "Boolean",
|
|
374
|
+
value: "boolean",
|
|
375
|
+
nested: [
|
|
376
|
+
{
|
|
377
|
+
name: "default",
|
|
378
|
+
label: "Default value",
|
|
379
|
+
placeholder: "Enter default value",
|
|
380
|
+
type: "boolean",
|
|
381
|
+
mappable: false
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: "required",
|
|
385
|
+
label: "Required",
|
|
386
|
+
type: "boolean",
|
|
387
|
+
required: true,
|
|
388
|
+
default: false,
|
|
389
|
+
mappable: false
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
label: "Binary Data",
|
|
395
|
+
value: "buffer",
|
|
396
|
+
nested: [
|
|
397
|
+
{
|
|
398
|
+
name: "required",
|
|
399
|
+
label: "Required",
|
|
400
|
+
type: "boolean",
|
|
401
|
+
required: true,
|
|
402
|
+
default: false,
|
|
403
|
+
mappable: false
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: "codepage",
|
|
407
|
+
label: "Codepage",
|
|
408
|
+
type: "text",
|
|
409
|
+
help: "Possible values: `binary`, `utf8`. Leave empty if you're not sure."
|
|
410
|
+
}
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
]
|
|
414
|
+
};
|
|
415
|
+
return field;
|
|
416
|
+
}
|
|
417
|
+
function udttypeExtractInner(schema) {
|
|
418
|
+
const { title, description, ...inner } = schema;
|
|
419
|
+
return inner;
|
|
420
|
+
}
|
|
421
|
+
function udttypeWrapRef(ref, field) {
|
|
422
|
+
return {
|
|
423
|
+
allOf: [{ $ref: ref }],
|
|
424
|
+
title: noEmpty(field.label),
|
|
425
|
+
description: noEmpty(field.help),
|
|
426
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function udttypeCollapse(field) {
|
|
430
|
+
return {
|
|
431
|
+
type: "udttype",
|
|
432
|
+
label: noEmpty(field.title),
|
|
433
|
+
help: noEmpty(field.description),
|
|
434
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/composites/udtspec.ts
|
|
439
|
+
function udtspecExpand(field) {
|
|
440
|
+
field.type = "array";
|
|
441
|
+
field.spec = [
|
|
442
|
+
{
|
|
443
|
+
name: "name",
|
|
444
|
+
label: "Name",
|
|
445
|
+
placeholder: "Enter name",
|
|
446
|
+
type: "text",
|
|
447
|
+
required: true
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "label",
|
|
451
|
+
label: "Label",
|
|
452
|
+
help: "Display name for better readability.",
|
|
453
|
+
type: "text",
|
|
454
|
+
advanced: true
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: "help",
|
|
458
|
+
label: "Description",
|
|
459
|
+
type: "text",
|
|
460
|
+
multiline: true,
|
|
461
|
+
required: false,
|
|
462
|
+
placeholder: "Enter description"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "type",
|
|
466
|
+
label: "Type",
|
|
467
|
+
type: "udttype",
|
|
468
|
+
required: true,
|
|
469
|
+
default: "text"
|
|
470
|
+
}
|
|
471
|
+
];
|
|
472
|
+
return field;
|
|
473
|
+
}
|
|
474
|
+
function udtspecExtractInner(schema) {
|
|
475
|
+
return schema.items;
|
|
476
|
+
}
|
|
477
|
+
function udtspecWrapRef(ref, field) {
|
|
478
|
+
return {
|
|
479
|
+
type: "array",
|
|
480
|
+
title: noEmpty(field.label),
|
|
481
|
+
description: noEmpty(field.help),
|
|
482
|
+
items: { $ref: ref }
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function udtspecCollapse(field) {
|
|
486
|
+
return {
|
|
487
|
+
type: "udtspec",
|
|
488
|
+
label: noEmpty(field.title),
|
|
489
|
+
help: noEmpty(field.description)
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
197
493
|
// src/forman.ts
|
|
198
494
|
var SchemaConversionError = class extends Error {
|
|
199
495
|
/** Field that caused the error */
|
|
@@ -240,6 +536,8 @@ var FORMAN_TYPE_MAP = {
|
|
|
240
536
|
pkey: "string",
|
|
241
537
|
port: "number",
|
|
242
538
|
select: "string",
|
|
539
|
+
udttype: "string",
|
|
540
|
+
udtspec: "array",
|
|
243
541
|
time: "string",
|
|
244
542
|
timestamp: "string",
|
|
245
543
|
timezone: "string",
|
|
@@ -269,14 +567,52 @@ function createDefaultContext() {
|
|
|
269
567
|
tail: [],
|
|
270
568
|
path: [],
|
|
271
569
|
roots: {},
|
|
570
|
+
definitions: {},
|
|
272
571
|
addConditionalFields: () => {
|
|
273
572
|
throw new SchemaConversionError("Cannot serialize nested fields without parent field.");
|
|
274
573
|
}
|
|
275
574
|
};
|
|
276
575
|
}
|
|
277
|
-
|
|
576
|
+
var compositeHandlers = {
|
|
577
|
+
udtspec: { expand: udtspecExpand, extractInner: udtspecExtractInner, wrapRef: udtspecWrapRef },
|
|
578
|
+
udttype: { expand: udttypeExpand, extractInner: udttypeExtractInner, wrapRef: udttypeWrapRef }
|
|
579
|
+
};
|
|
580
|
+
function toJSONSchemaInternal(field, context) {
|
|
278
581
|
validateFormanField(field);
|
|
279
582
|
const normalizedField = normalizeFormanFieldType(field);
|
|
583
|
+
const handler = compositeHandlers[normalizedField.type];
|
|
584
|
+
if (handler) {
|
|
585
|
+
const type = normalizedField.type;
|
|
586
|
+
const ref = `#/definitions/${type}`;
|
|
587
|
+
if (!context.definitions?.[type]) {
|
|
588
|
+
if (context.definitions) {
|
|
589
|
+
context.definitions[type] = {};
|
|
590
|
+
}
|
|
591
|
+
const expandField = { ...normalizedField };
|
|
592
|
+
delete expandField.label;
|
|
593
|
+
delete expandField.help;
|
|
594
|
+
const expanded = handler.expand(expandField);
|
|
595
|
+
const expandedResult = toJSONSchemaInternal(expanded, context);
|
|
596
|
+
const inner = handler.extractInner(expandedResult);
|
|
597
|
+
Object.defineProperty(inner, "x-composite", {
|
|
598
|
+
configurable: true,
|
|
599
|
+
enumerable: true,
|
|
600
|
+
writable: true,
|
|
601
|
+
value: type
|
|
602
|
+
});
|
|
603
|
+
if (context.definitions) {
|
|
604
|
+
context.definitions[type] = inner;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const wrapper = handler.wrapRef(ref, normalizedField);
|
|
608
|
+
Object.defineProperty(wrapper, "x-composite", {
|
|
609
|
+
configurable: true,
|
|
610
|
+
enumerable: true,
|
|
611
|
+
writable: true,
|
|
612
|
+
value: type
|
|
613
|
+
});
|
|
614
|
+
return wrapper;
|
|
615
|
+
}
|
|
280
616
|
const result = {
|
|
281
617
|
type: FORMAN_TYPE_MAP[normalizedField.type],
|
|
282
618
|
title: noEmpty(normalizedField.label),
|
|
@@ -694,6 +1030,8 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
694
1030
|
pkey: "string",
|
|
695
1031
|
port: "number",
|
|
696
1032
|
select: void 0,
|
|
1033
|
+
udttype: "string",
|
|
1034
|
+
udtspec: "array",
|
|
697
1035
|
time: "string",
|
|
698
1036
|
timestamp: "string",
|
|
699
1037
|
timezone: "string",
|
|
@@ -766,6 +1104,25 @@ async function validateFormanWithDomainsInternal(domains, options) {
|
|
|
766
1104
|
}
|
|
767
1105
|
);
|
|
768
1106
|
errors.push(...result.errors);
|
|
1107
|
+
const restoreExtras = options?.states && domains[domain]?.restoreExtras;
|
|
1108
|
+
if (restoreExtras) {
|
|
1109
|
+
const fieldStateMap = new Map(
|
|
1110
|
+
roots[domain].fieldStates.map(({ path, state }) => {
|
|
1111
|
+
return [pathToString(path), state];
|
|
1112
|
+
})
|
|
1113
|
+
);
|
|
1114
|
+
for (const [fieldPath, extra] of Object.entries(restoreExtras)) {
|
|
1115
|
+
const state = fieldStateMap.get(fieldPath);
|
|
1116
|
+
if (state) {
|
|
1117
|
+
state.extra = extra;
|
|
1118
|
+
} else {
|
|
1119
|
+
roots[domain].fieldStates.push({
|
|
1120
|
+
path: stringToPath(fieldPath),
|
|
1121
|
+
state: { extra }
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
769
1126
|
}
|
|
770
1127
|
return {
|
|
771
1128
|
valid: errors.length === 0,
|
|
@@ -834,6 +1191,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
834
1191
|
errors: []
|
|
835
1192
|
};
|
|
836
1193
|
}
|
|
1194
|
+
if (normalizedField.type === "udttype") {
|
|
1195
|
+
return validateFormanValue(value, udttypeExpand({ ...normalizedField }), context);
|
|
1196
|
+
}
|
|
1197
|
+
if (normalizedField.type === "udtspec") {
|
|
1198
|
+
return validateFormanValue(value, udtspecExpand({ ...normalizedField }), context);
|
|
1199
|
+
}
|
|
837
1200
|
switch (normalizedField.type) {
|
|
838
1201
|
case "collection":
|
|
839
1202
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -1183,7 +1546,21 @@ async function handleSelectType(value, field, context) {
|
|
|
1183
1546
|
let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
|
|
1184
1547
|
if (typeof optionsOrGroups === "string") {
|
|
1185
1548
|
try {
|
|
1186
|
-
|
|
1549
|
+
const resolved = await context.resolveRemote(optionsOrGroups, context);
|
|
1550
|
+
if (resolved == null || typeof resolved !== "object") {
|
|
1551
|
+
return {
|
|
1552
|
+
valid: false,
|
|
1553
|
+
errors: [
|
|
1554
|
+
...errors,
|
|
1555
|
+
{
|
|
1556
|
+
domain: context.domain,
|
|
1557
|
+
path: context.path.join("."),
|
|
1558
|
+
message: `Remote resource ${optionsOrGroups} returned no data.`
|
|
1559
|
+
}
|
|
1560
|
+
]
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
optionsOrGroups = Array.isArray(resolved) ? resolved : [resolved];
|
|
1187
1564
|
} catch (error) {
|
|
1188
1565
|
return {
|
|
1189
1566
|
valid: false,
|
|
@@ -1293,7 +1670,12 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
1293
1670
|
for (const item of store) {
|
|
1294
1671
|
if (typeof item === "string") {
|
|
1295
1672
|
try {
|
|
1296
|
-
|
|
1673
|
+
const resolved = await context.resolveRemote(item, context);
|
|
1674
|
+
if (Array.isArray(resolved)) {
|
|
1675
|
+
resolvedStore.push(...resolved);
|
|
1676
|
+
} else if (resolved && typeof resolved === "object") {
|
|
1677
|
+
resolvedStore.push(resolved);
|
|
1678
|
+
}
|
|
1297
1679
|
} catch (error) {
|
|
1298
1680
|
return {
|
|
1299
1681
|
valid: false,
|
|
@@ -1401,6 +1783,9 @@ var JSON_PRIMITIVE_TYPE_MAP = {
|
|
|
1401
1783
|
boolean: "boolean"
|
|
1402
1784
|
};
|
|
1403
1785
|
function toFormanSchema(field) {
|
|
1786
|
+
const compositeType = Object.getOwnPropertyDescriptor(field, "x-composite")?.value;
|
|
1787
|
+
if (compositeType === "udttype") return udttypeCollapse(field);
|
|
1788
|
+
if (compositeType === "udtspec") return udtspecCollapse(field);
|
|
1404
1789
|
switch (field.type) {
|
|
1405
1790
|
case "object":
|
|
1406
1791
|
if (!field.properties || !Object.entries(field.properties).length) {
|
|
@@ -1523,13 +1908,23 @@ function handleSearchDirective(formanField, directive) {
|
|
|
1523
1908
|
|
|
1524
1909
|
// src/index.ts
|
|
1525
1910
|
function toJSONSchema(field) {
|
|
1526
|
-
|
|
1911
|
+
const context = createDefaultContext();
|
|
1912
|
+
const result = toJSONSchemaInternal(field, context);
|
|
1913
|
+
if (Object.keys(context.definitions ?? {}).length > 0) {
|
|
1914
|
+
Object.defineProperty(result, "definitions", {
|
|
1915
|
+
configurable: true,
|
|
1916
|
+
enumerable: true,
|
|
1917
|
+
writable: true,
|
|
1918
|
+
value: context.definitions
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
return result;
|
|
1527
1922
|
}
|
|
1528
1923
|
function validateFormanWithDomains(domains, options) {
|
|
1529
1924
|
return validateFormanWithDomainsInternal(domains, options);
|
|
1530
1925
|
}
|
|
1531
|
-
function validateForman(values, schema, options) {
|
|
1532
|
-
return validateFormanWithDomains({ default: { values, schema } }, options);
|
|
1926
|
+
function validateForman(values, schema, options, restoreExtras) {
|
|
1927
|
+
return validateFormanWithDomains({ default: { values, schema, restoreExtras } }, options);
|
|
1533
1928
|
}
|
|
1534
1929
|
export {
|
|
1535
1930
|
toFormanSchema,
|