@makehq/forman-schema 1.8.3 → 1.8.4
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 +323 -4
- package/dist/index.js +323 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -223,6 +223,248 @@ var IML_BINARY_FILTER_OPERATORS = [
|
|
|
223
223
|
];
|
|
224
224
|
var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
|
|
225
225
|
|
|
226
|
+
// src/composites/udttype.ts
|
|
227
|
+
function udttypeExpand(field) {
|
|
228
|
+
field.type = "select";
|
|
229
|
+
field.options = {
|
|
230
|
+
store: [
|
|
231
|
+
{
|
|
232
|
+
label: "Array",
|
|
233
|
+
value: "array",
|
|
234
|
+
nested: [
|
|
235
|
+
{
|
|
236
|
+
name: "spec",
|
|
237
|
+
type: "collection",
|
|
238
|
+
label: "Array Item Specification",
|
|
239
|
+
spec: [
|
|
240
|
+
{
|
|
241
|
+
name: "type",
|
|
242
|
+
label: "Type",
|
|
243
|
+
type: "udttype",
|
|
244
|
+
required: true,
|
|
245
|
+
default: "text"
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
mappable: false
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "required",
|
|
252
|
+
label: "Required",
|
|
253
|
+
type: "boolean",
|
|
254
|
+
required: true,
|
|
255
|
+
default: false,
|
|
256
|
+
mappable: false
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
label: "Collection",
|
|
262
|
+
value: "collection",
|
|
263
|
+
nested: [
|
|
264
|
+
{
|
|
265
|
+
name: "spec",
|
|
266
|
+
label: "Specification",
|
|
267
|
+
type: "udtspec"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "sequence",
|
|
271
|
+
label: "Preserve the order of object keys",
|
|
272
|
+
type: "boolean",
|
|
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: "Date",
|
|
287
|
+
value: "date",
|
|
288
|
+
nested: [
|
|
289
|
+
{
|
|
290
|
+
name: "required",
|
|
291
|
+
label: "Required",
|
|
292
|
+
type: "boolean",
|
|
293
|
+
required: true,
|
|
294
|
+
default: false,
|
|
295
|
+
mappable: false
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
label: "Text",
|
|
301
|
+
value: "text",
|
|
302
|
+
nested: [
|
|
303
|
+
{
|
|
304
|
+
name: "default",
|
|
305
|
+
label: "Default value",
|
|
306
|
+
placeholder: "Enter default value",
|
|
307
|
+
type: "text"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: "required",
|
|
311
|
+
label: "Required",
|
|
312
|
+
type: "boolean",
|
|
313
|
+
required: true,
|
|
314
|
+
default: false,
|
|
315
|
+
mappable: false
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: "multiline",
|
|
319
|
+
label: "Multi-line",
|
|
320
|
+
type: "boolean",
|
|
321
|
+
required: true,
|
|
322
|
+
default: false,
|
|
323
|
+
mappable: false
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
label: "Number",
|
|
329
|
+
value: "number",
|
|
330
|
+
nested: [
|
|
331
|
+
{
|
|
332
|
+
name: "default",
|
|
333
|
+
label: "Default value",
|
|
334
|
+
placeholder: "Enter default value",
|
|
335
|
+
type: "number"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: "required",
|
|
339
|
+
label: "Required",
|
|
340
|
+
type: "boolean",
|
|
341
|
+
required: true,
|
|
342
|
+
default: false,
|
|
343
|
+
mappable: false
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
label: "Boolean",
|
|
349
|
+
value: "boolean",
|
|
350
|
+
nested: [
|
|
351
|
+
{
|
|
352
|
+
name: "default",
|
|
353
|
+
label: "Default value",
|
|
354
|
+
placeholder: "Enter default value",
|
|
355
|
+
type: "boolean",
|
|
356
|
+
mappable: false
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "required",
|
|
360
|
+
label: "Required",
|
|
361
|
+
type: "boolean",
|
|
362
|
+
required: true,
|
|
363
|
+
default: false,
|
|
364
|
+
mappable: false
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
label: "Binary Data",
|
|
370
|
+
value: "buffer",
|
|
371
|
+
nested: [
|
|
372
|
+
{
|
|
373
|
+
name: "required",
|
|
374
|
+
label: "Required",
|
|
375
|
+
type: "boolean",
|
|
376
|
+
required: true,
|
|
377
|
+
default: false,
|
|
378
|
+
mappable: false
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "codepage",
|
|
382
|
+
label: "Codepage",
|
|
383
|
+
type: "text",
|
|
384
|
+
help: "Possible values: `binary`, `utf8`. Leave empty if you're not sure."
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
};
|
|
390
|
+
return field;
|
|
391
|
+
}
|
|
392
|
+
function udttypeExtractInner(schema) {
|
|
393
|
+
const { title, description, ...inner } = schema;
|
|
394
|
+
return inner;
|
|
395
|
+
}
|
|
396
|
+
function udttypeWrapRef(ref, field) {
|
|
397
|
+
return {
|
|
398
|
+
allOf: [{ $ref: ref }],
|
|
399
|
+
title: noEmpty(field.label),
|
|
400
|
+
description: noEmpty(field.help),
|
|
401
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function udttypeCollapse(field) {
|
|
405
|
+
return {
|
|
406
|
+
type: "udttype",
|
|
407
|
+
label: noEmpty(field.title),
|
|
408
|
+
help: noEmpty(field.description),
|
|
409
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/composites/udtspec.ts
|
|
414
|
+
function udtspecExpand(field) {
|
|
415
|
+
field.type = "array";
|
|
416
|
+
field.spec = [
|
|
417
|
+
{
|
|
418
|
+
name: "name",
|
|
419
|
+
label: "Name",
|
|
420
|
+
placeholder: "Enter name",
|
|
421
|
+
type: "text",
|
|
422
|
+
required: true
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: "label",
|
|
426
|
+
label: "Label",
|
|
427
|
+
help: "Display name for better readability.",
|
|
428
|
+
type: "text",
|
|
429
|
+
advanced: true
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: "help",
|
|
433
|
+
label: "Description",
|
|
434
|
+
type: "text",
|
|
435
|
+
multiline: true,
|
|
436
|
+
required: false,
|
|
437
|
+
placeholder: "Enter description"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: "type",
|
|
441
|
+
label: "Type",
|
|
442
|
+
type: "udttype",
|
|
443
|
+
required: true,
|
|
444
|
+
default: "text"
|
|
445
|
+
}
|
|
446
|
+
];
|
|
447
|
+
return field;
|
|
448
|
+
}
|
|
449
|
+
function udtspecExtractInner(schema) {
|
|
450
|
+
return schema.items;
|
|
451
|
+
}
|
|
452
|
+
function udtspecWrapRef(ref, field) {
|
|
453
|
+
return {
|
|
454
|
+
type: "array",
|
|
455
|
+
title: noEmpty(field.label),
|
|
456
|
+
description: noEmpty(field.help),
|
|
457
|
+
items: { $ref: ref }
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function udtspecCollapse(field) {
|
|
461
|
+
return {
|
|
462
|
+
type: "udtspec",
|
|
463
|
+
label: noEmpty(field.title),
|
|
464
|
+
help: noEmpty(field.description)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
226
468
|
// src/forman.ts
|
|
227
469
|
var SchemaConversionError = class extends Error {
|
|
228
470
|
/** Field that caused the error */
|
|
@@ -269,6 +511,8 @@ var FORMAN_TYPE_MAP = {
|
|
|
269
511
|
pkey: "string",
|
|
270
512
|
port: "number",
|
|
271
513
|
select: "string",
|
|
514
|
+
udttype: "string",
|
|
515
|
+
udtspec: "array",
|
|
272
516
|
time: "string",
|
|
273
517
|
timestamp: "string",
|
|
274
518
|
timezone: "string",
|
|
@@ -298,14 +542,52 @@ function createDefaultContext() {
|
|
|
298
542
|
tail: [],
|
|
299
543
|
path: [],
|
|
300
544
|
roots: {},
|
|
545
|
+
definitions: {},
|
|
301
546
|
addConditionalFields: () => {
|
|
302
547
|
throw new SchemaConversionError("Cannot serialize nested fields without parent field.");
|
|
303
548
|
}
|
|
304
549
|
};
|
|
305
550
|
}
|
|
306
|
-
|
|
551
|
+
var compositeHandlers = {
|
|
552
|
+
udtspec: { expand: udtspecExpand, extractInner: udtspecExtractInner, wrapRef: udtspecWrapRef },
|
|
553
|
+
udttype: { expand: udttypeExpand, extractInner: udttypeExtractInner, wrapRef: udttypeWrapRef }
|
|
554
|
+
};
|
|
555
|
+
function toJSONSchemaInternal(field, context) {
|
|
307
556
|
validateFormanField(field);
|
|
308
557
|
const normalizedField = normalizeFormanFieldType(field);
|
|
558
|
+
const handler = compositeHandlers[normalizedField.type];
|
|
559
|
+
if (handler) {
|
|
560
|
+
const type = normalizedField.type;
|
|
561
|
+
const ref = `#/definitions/${type}`;
|
|
562
|
+
if (!context.definitions?.[type]) {
|
|
563
|
+
if (context.definitions) {
|
|
564
|
+
context.definitions[type] = {};
|
|
565
|
+
}
|
|
566
|
+
const expandField = { ...normalizedField };
|
|
567
|
+
delete expandField.label;
|
|
568
|
+
delete expandField.help;
|
|
569
|
+
const expanded = handler.expand(expandField);
|
|
570
|
+
const expandedResult = toJSONSchemaInternal(expanded, context);
|
|
571
|
+
const inner = handler.extractInner(expandedResult);
|
|
572
|
+
Object.defineProperty(inner, "x-composite", {
|
|
573
|
+
configurable: true,
|
|
574
|
+
enumerable: true,
|
|
575
|
+
writable: true,
|
|
576
|
+
value: type
|
|
577
|
+
});
|
|
578
|
+
if (context.definitions) {
|
|
579
|
+
context.definitions[type] = inner;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const wrapper = handler.wrapRef(ref, normalizedField);
|
|
583
|
+
Object.defineProperty(wrapper, "x-composite", {
|
|
584
|
+
configurable: true,
|
|
585
|
+
enumerable: true,
|
|
586
|
+
writable: true,
|
|
587
|
+
value: type
|
|
588
|
+
});
|
|
589
|
+
return wrapper;
|
|
590
|
+
}
|
|
309
591
|
const result = {
|
|
310
592
|
type: FORMAN_TYPE_MAP[normalizedField.type],
|
|
311
593
|
title: noEmpty(normalizedField.label),
|
|
@@ -723,6 +1005,8 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
723
1005
|
pkey: "string",
|
|
724
1006
|
port: "number",
|
|
725
1007
|
select: void 0,
|
|
1008
|
+
udttype: "string",
|
|
1009
|
+
udtspec: "array",
|
|
726
1010
|
time: "string",
|
|
727
1011
|
timestamp: "string",
|
|
728
1012
|
timezone: "string",
|
|
@@ -863,6 +1147,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
863
1147
|
errors: []
|
|
864
1148
|
};
|
|
865
1149
|
}
|
|
1150
|
+
if (normalizedField.type === "udttype") {
|
|
1151
|
+
return validateFormanValue(value, udttypeExpand({ ...normalizedField }), context);
|
|
1152
|
+
}
|
|
1153
|
+
if (normalizedField.type === "udtspec") {
|
|
1154
|
+
return validateFormanValue(value, udtspecExpand({ ...normalizedField }), context);
|
|
1155
|
+
}
|
|
866
1156
|
switch (normalizedField.type) {
|
|
867
1157
|
case "collection":
|
|
868
1158
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -1212,7 +1502,18 @@ async function handleSelectType(value, field, context) {
|
|
|
1212
1502
|
let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
|
|
1213
1503
|
if (typeof optionsOrGroups === "string") {
|
|
1214
1504
|
try {
|
|
1215
|
-
|
|
1505
|
+
const resolved = await context.resolveRemote(optionsOrGroups, context);
|
|
1506
|
+
if (resolved == null || typeof resolved !== "object") {
|
|
1507
|
+
return {
|
|
1508
|
+
valid: false,
|
|
1509
|
+
errors: [...errors, {
|
|
1510
|
+
domain: context.domain,
|
|
1511
|
+
path: context.path.join("."),
|
|
1512
|
+
message: `Remote resource ${optionsOrGroups} returned no data.`
|
|
1513
|
+
}]
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
optionsOrGroups = Array.isArray(resolved) ? resolved : [resolved];
|
|
1216
1517
|
} catch (error) {
|
|
1217
1518
|
return {
|
|
1218
1519
|
valid: false,
|
|
@@ -1322,7 +1623,12 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
1322
1623
|
for (const item of store) {
|
|
1323
1624
|
if (typeof item === "string") {
|
|
1324
1625
|
try {
|
|
1325
|
-
|
|
1626
|
+
const resolved = await context.resolveRemote(item, context);
|
|
1627
|
+
if (Array.isArray(resolved)) {
|
|
1628
|
+
resolvedStore.push(...resolved);
|
|
1629
|
+
} else if (resolved && typeof resolved === "object") {
|
|
1630
|
+
resolvedStore.push(resolved);
|
|
1631
|
+
}
|
|
1326
1632
|
} catch (error) {
|
|
1327
1633
|
return {
|
|
1328
1634
|
valid: false,
|
|
@@ -1430,6 +1736,9 @@ var JSON_PRIMITIVE_TYPE_MAP = {
|
|
|
1430
1736
|
boolean: "boolean"
|
|
1431
1737
|
};
|
|
1432
1738
|
function toFormanSchema(field) {
|
|
1739
|
+
const compositeType = Object.getOwnPropertyDescriptor(field, "x-composite")?.value;
|
|
1740
|
+
if (compositeType === "udttype") return udttypeCollapse(field);
|
|
1741
|
+
if (compositeType === "udtspec") return udtspecCollapse(field);
|
|
1433
1742
|
switch (field.type) {
|
|
1434
1743
|
case "object":
|
|
1435
1744
|
if (!field.properties || !Object.entries(field.properties).length) {
|
|
@@ -1552,7 +1861,17 @@ function handleSearchDirective(formanField, directive) {
|
|
|
1552
1861
|
|
|
1553
1862
|
// src/index.ts
|
|
1554
1863
|
function toJSONSchema(field) {
|
|
1555
|
-
|
|
1864
|
+
const context = createDefaultContext();
|
|
1865
|
+
const result = toJSONSchemaInternal(field, context);
|
|
1866
|
+
if (Object.keys(context.definitions ?? {}).length > 0) {
|
|
1867
|
+
Object.defineProperty(result, "definitions", {
|
|
1868
|
+
configurable: true,
|
|
1869
|
+
enumerable: true,
|
|
1870
|
+
writable: true,
|
|
1871
|
+
value: context.definitions
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
return result;
|
|
1556
1875
|
}
|
|
1557
1876
|
function validateFormanWithDomains(domains, options) {
|
|
1558
1877
|
return validateFormanWithDomainsInternal(domains, options);
|
package/dist/index.js
CHANGED
|
@@ -194,6 +194,248 @@ var IML_BINARY_FILTER_OPERATORS = [
|
|
|
194
194
|
];
|
|
195
195
|
var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
|
|
196
196
|
|
|
197
|
+
// src/composites/udttype.ts
|
|
198
|
+
function udttypeExpand(field) {
|
|
199
|
+
field.type = "select";
|
|
200
|
+
field.options = {
|
|
201
|
+
store: [
|
|
202
|
+
{
|
|
203
|
+
label: "Array",
|
|
204
|
+
value: "array",
|
|
205
|
+
nested: [
|
|
206
|
+
{
|
|
207
|
+
name: "spec",
|
|
208
|
+
type: "collection",
|
|
209
|
+
label: "Array Item Specification",
|
|
210
|
+
spec: [
|
|
211
|
+
{
|
|
212
|
+
name: "type",
|
|
213
|
+
label: "Type",
|
|
214
|
+
type: "udttype",
|
|
215
|
+
required: true,
|
|
216
|
+
default: "text"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
mappable: false
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "required",
|
|
223
|
+
label: "Required",
|
|
224
|
+
type: "boolean",
|
|
225
|
+
required: true,
|
|
226
|
+
default: false,
|
|
227
|
+
mappable: false
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
label: "Collection",
|
|
233
|
+
value: "collection",
|
|
234
|
+
nested: [
|
|
235
|
+
{
|
|
236
|
+
name: "spec",
|
|
237
|
+
label: "Specification",
|
|
238
|
+
type: "udtspec"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "sequence",
|
|
242
|
+
label: "Preserve the order of object keys",
|
|
243
|
+
type: "boolean",
|
|
244
|
+
mappable: false
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "required",
|
|
248
|
+
label: "Required",
|
|
249
|
+
type: "boolean",
|
|
250
|
+
required: true,
|
|
251
|
+
default: false,
|
|
252
|
+
mappable: false
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: "Date",
|
|
258
|
+
value: "date",
|
|
259
|
+
nested: [
|
|
260
|
+
{
|
|
261
|
+
name: "required",
|
|
262
|
+
label: "Required",
|
|
263
|
+
type: "boolean",
|
|
264
|
+
required: true,
|
|
265
|
+
default: false,
|
|
266
|
+
mappable: false
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
label: "Text",
|
|
272
|
+
value: "text",
|
|
273
|
+
nested: [
|
|
274
|
+
{
|
|
275
|
+
name: "default",
|
|
276
|
+
label: "Default value",
|
|
277
|
+
placeholder: "Enter default value",
|
|
278
|
+
type: "text"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: "required",
|
|
282
|
+
label: "Required",
|
|
283
|
+
type: "boolean",
|
|
284
|
+
required: true,
|
|
285
|
+
default: false,
|
|
286
|
+
mappable: false
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "multiline",
|
|
290
|
+
label: "Multi-line",
|
|
291
|
+
type: "boolean",
|
|
292
|
+
required: true,
|
|
293
|
+
default: false,
|
|
294
|
+
mappable: false
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
label: "Number",
|
|
300
|
+
value: "number",
|
|
301
|
+
nested: [
|
|
302
|
+
{
|
|
303
|
+
name: "default",
|
|
304
|
+
label: "Default value",
|
|
305
|
+
placeholder: "Enter default value",
|
|
306
|
+
type: "number"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "required",
|
|
310
|
+
label: "Required",
|
|
311
|
+
type: "boolean",
|
|
312
|
+
required: true,
|
|
313
|
+
default: false,
|
|
314
|
+
mappable: false
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
label: "Boolean",
|
|
320
|
+
value: "boolean",
|
|
321
|
+
nested: [
|
|
322
|
+
{
|
|
323
|
+
name: "default",
|
|
324
|
+
label: "Default value",
|
|
325
|
+
placeholder: "Enter default value",
|
|
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: "Binary Data",
|
|
341
|
+
value: "buffer",
|
|
342
|
+
nested: [
|
|
343
|
+
{
|
|
344
|
+
name: "required",
|
|
345
|
+
label: "Required",
|
|
346
|
+
type: "boolean",
|
|
347
|
+
required: true,
|
|
348
|
+
default: false,
|
|
349
|
+
mappable: false
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: "codepage",
|
|
353
|
+
label: "Codepage",
|
|
354
|
+
type: "text",
|
|
355
|
+
help: "Possible values: `binary`, `utf8`. Leave empty if you're not sure."
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
};
|
|
361
|
+
return field;
|
|
362
|
+
}
|
|
363
|
+
function udttypeExtractInner(schema) {
|
|
364
|
+
const { title, description, ...inner } = schema;
|
|
365
|
+
return inner;
|
|
366
|
+
}
|
|
367
|
+
function udttypeWrapRef(ref, field) {
|
|
368
|
+
return {
|
|
369
|
+
allOf: [{ $ref: ref }],
|
|
370
|
+
title: noEmpty(field.label),
|
|
371
|
+
description: noEmpty(field.help),
|
|
372
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function udttypeCollapse(field) {
|
|
376
|
+
return {
|
|
377
|
+
type: "udttype",
|
|
378
|
+
label: noEmpty(field.title),
|
|
379
|
+
help: noEmpty(field.description),
|
|
380
|
+
...field.default !== "" && field.default != null ? { default: field.default } : {}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/composites/udtspec.ts
|
|
385
|
+
function udtspecExpand(field) {
|
|
386
|
+
field.type = "array";
|
|
387
|
+
field.spec = [
|
|
388
|
+
{
|
|
389
|
+
name: "name",
|
|
390
|
+
label: "Name",
|
|
391
|
+
placeholder: "Enter name",
|
|
392
|
+
type: "text",
|
|
393
|
+
required: true
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "label",
|
|
397
|
+
label: "Label",
|
|
398
|
+
help: "Display name for better readability.",
|
|
399
|
+
type: "text",
|
|
400
|
+
advanced: true
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "help",
|
|
404
|
+
label: "Description",
|
|
405
|
+
type: "text",
|
|
406
|
+
multiline: true,
|
|
407
|
+
required: false,
|
|
408
|
+
placeholder: "Enter description"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "type",
|
|
412
|
+
label: "Type",
|
|
413
|
+
type: "udttype",
|
|
414
|
+
required: true,
|
|
415
|
+
default: "text"
|
|
416
|
+
}
|
|
417
|
+
];
|
|
418
|
+
return field;
|
|
419
|
+
}
|
|
420
|
+
function udtspecExtractInner(schema) {
|
|
421
|
+
return schema.items;
|
|
422
|
+
}
|
|
423
|
+
function udtspecWrapRef(ref, field) {
|
|
424
|
+
return {
|
|
425
|
+
type: "array",
|
|
426
|
+
title: noEmpty(field.label),
|
|
427
|
+
description: noEmpty(field.help),
|
|
428
|
+
items: { $ref: ref }
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function udtspecCollapse(field) {
|
|
432
|
+
return {
|
|
433
|
+
type: "udtspec",
|
|
434
|
+
label: noEmpty(field.title),
|
|
435
|
+
help: noEmpty(field.description)
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
197
439
|
// src/forman.ts
|
|
198
440
|
var SchemaConversionError = class extends Error {
|
|
199
441
|
/** Field that caused the error */
|
|
@@ -240,6 +482,8 @@ var FORMAN_TYPE_MAP = {
|
|
|
240
482
|
pkey: "string",
|
|
241
483
|
port: "number",
|
|
242
484
|
select: "string",
|
|
485
|
+
udttype: "string",
|
|
486
|
+
udtspec: "array",
|
|
243
487
|
time: "string",
|
|
244
488
|
timestamp: "string",
|
|
245
489
|
timezone: "string",
|
|
@@ -269,14 +513,52 @@ function createDefaultContext() {
|
|
|
269
513
|
tail: [],
|
|
270
514
|
path: [],
|
|
271
515
|
roots: {},
|
|
516
|
+
definitions: {},
|
|
272
517
|
addConditionalFields: () => {
|
|
273
518
|
throw new SchemaConversionError("Cannot serialize nested fields without parent field.");
|
|
274
519
|
}
|
|
275
520
|
};
|
|
276
521
|
}
|
|
277
|
-
|
|
522
|
+
var compositeHandlers = {
|
|
523
|
+
udtspec: { expand: udtspecExpand, extractInner: udtspecExtractInner, wrapRef: udtspecWrapRef },
|
|
524
|
+
udttype: { expand: udttypeExpand, extractInner: udttypeExtractInner, wrapRef: udttypeWrapRef }
|
|
525
|
+
};
|
|
526
|
+
function toJSONSchemaInternal(field, context) {
|
|
278
527
|
validateFormanField(field);
|
|
279
528
|
const normalizedField = normalizeFormanFieldType(field);
|
|
529
|
+
const handler = compositeHandlers[normalizedField.type];
|
|
530
|
+
if (handler) {
|
|
531
|
+
const type = normalizedField.type;
|
|
532
|
+
const ref = `#/definitions/${type}`;
|
|
533
|
+
if (!context.definitions?.[type]) {
|
|
534
|
+
if (context.definitions) {
|
|
535
|
+
context.definitions[type] = {};
|
|
536
|
+
}
|
|
537
|
+
const expandField = { ...normalizedField };
|
|
538
|
+
delete expandField.label;
|
|
539
|
+
delete expandField.help;
|
|
540
|
+
const expanded = handler.expand(expandField);
|
|
541
|
+
const expandedResult = toJSONSchemaInternal(expanded, context);
|
|
542
|
+
const inner = handler.extractInner(expandedResult);
|
|
543
|
+
Object.defineProperty(inner, "x-composite", {
|
|
544
|
+
configurable: true,
|
|
545
|
+
enumerable: true,
|
|
546
|
+
writable: true,
|
|
547
|
+
value: type
|
|
548
|
+
});
|
|
549
|
+
if (context.definitions) {
|
|
550
|
+
context.definitions[type] = inner;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const wrapper = handler.wrapRef(ref, normalizedField);
|
|
554
|
+
Object.defineProperty(wrapper, "x-composite", {
|
|
555
|
+
configurable: true,
|
|
556
|
+
enumerable: true,
|
|
557
|
+
writable: true,
|
|
558
|
+
value: type
|
|
559
|
+
});
|
|
560
|
+
return wrapper;
|
|
561
|
+
}
|
|
280
562
|
const result = {
|
|
281
563
|
type: FORMAN_TYPE_MAP[normalizedField.type],
|
|
282
564
|
title: noEmpty(normalizedField.label),
|
|
@@ -694,6 +976,8 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
694
976
|
pkey: "string",
|
|
695
977
|
port: "number",
|
|
696
978
|
select: void 0,
|
|
979
|
+
udttype: "string",
|
|
980
|
+
udtspec: "array",
|
|
697
981
|
time: "string",
|
|
698
982
|
timestamp: "string",
|
|
699
983
|
timezone: "string",
|
|
@@ -834,6 +1118,12 @@ async function validateFormanValue(value, field, context) {
|
|
|
834
1118
|
errors: []
|
|
835
1119
|
};
|
|
836
1120
|
}
|
|
1121
|
+
if (normalizedField.type === "udttype") {
|
|
1122
|
+
return validateFormanValue(value, udttypeExpand({ ...normalizedField }), context);
|
|
1123
|
+
}
|
|
1124
|
+
if (normalizedField.type === "udtspec") {
|
|
1125
|
+
return validateFormanValue(value, udtspecExpand({ ...normalizedField }), context);
|
|
1126
|
+
}
|
|
837
1127
|
switch (normalizedField.type) {
|
|
838
1128
|
case "collection":
|
|
839
1129
|
return handleCollectionType2(value, normalizedField, context);
|
|
@@ -1183,7 +1473,18 @@ async function handleSelectType(value, field, context) {
|
|
|
1183
1473
|
let nested = field.nested ? field.nested : isObject(field.options) ? field.options.nested : void 0;
|
|
1184
1474
|
if (typeof optionsOrGroups === "string") {
|
|
1185
1475
|
try {
|
|
1186
|
-
|
|
1476
|
+
const resolved = await context.resolveRemote(optionsOrGroups, context);
|
|
1477
|
+
if (resolved == null || typeof resolved !== "object") {
|
|
1478
|
+
return {
|
|
1479
|
+
valid: false,
|
|
1480
|
+
errors: [...errors, {
|
|
1481
|
+
domain: context.domain,
|
|
1482
|
+
path: context.path.join("."),
|
|
1483
|
+
message: `Remote resource ${optionsOrGroups} returned no data.`
|
|
1484
|
+
}]
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
optionsOrGroups = Array.isArray(resolved) ? resolved : [resolved];
|
|
1187
1488
|
} catch (error) {
|
|
1188
1489
|
return {
|
|
1189
1490
|
valid: false,
|
|
@@ -1293,7 +1594,12 @@ async function handleNestedFields(nested, value, field, context) {
|
|
|
1293
1594
|
for (const item of store) {
|
|
1294
1595
|
if (typeof item === "string") {
|
|
1295
1596
|
try {
|
|
1296
|
-
|
|
1597
|
+
const resolved = await context.resolveRemote(item, context);
|
|
1598
|
+
if (Array.isArray(resolved)) {
|
|
1599
|
+
resolvedStore.push(...resolved);
|
|
1600
|
+
} else if (resolved && typeof resolved === "object") {
|
|
1601
|
+
resolvedStore.push(resolved);
|
|
1602
|
+
}
|
|
1297
1603
|
} catch (error) {
|
|
1298
1604
|
return {
|
|
1299
1605
|
valid: false,
|
|
@@ -1401,6 +1707,9 @@ var JSON_PRIMITIVE_TYPE_MAP = {
|
|
|
1401
1707
|
boolean: "boolean"
|
|
1402
1708
|
};
|
|
1403
1709
|
function toFormanSchema(field) {
|
|
1710
|
+
const compositeType = Object.getOwnPropertyDescriptor(field, "x-composite")?.value;
|
|
1711
|
+
if (compositeType === "udttype") return udttypeCollapse(field);
|
|
1712
|
+
if (compositeType === "udtspec") return udtspecCollapse(field);
|
|
1404
1713
|
switch (field.type) {
|
|
1405
1714
|
case "object":
|
|
1406
1715
|
if (!field.properties || !Object.entries(field.properties).length) {
|
|
@@ -1523,7 +1832,17 @@ function handleSearchDirective(formanField, directive) {
|
|
|
1523
1832
|
|
|
1524
1833
|
// src/index.ts
|
|
1525
1834
|
function toJSONSchema(field) {
|
|
1526
|
-
|
|
1835
|
+
const context = createDefaultContext();
|
|
1836
|
+
const result = toJSONSchemaInternal(field, context);
|
|
1837
|
+
if (Object.keys(context.definitions ?? {}).length > 0) {
|
|
1838
|
+
Object.defineProperty(result, "definitions", {
|
|
1839
|
+
configurable: true,
|
|
1840
|
+
enumerable: true,
|
|
1841
|
+
writable: true,
|
|
1842
|
+
value: context.definitions
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
return result;
|
|
1527
1846
|
}
|
|
1528
1847
|
function validateFormanWithDomains(domains, options) {
|
|
1529
1848
|
return validateFormanWithDomainsInternal(domains, options);
|