@makehq/forman-schema 1.8.1 → 1.8.3
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 +165 -69
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +165 -69
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -250,6 +250,7 @@ var FORMAN_TYPE_MAP = {
|
|
|
250
250
|
editor: "string",
|
|
251
251
|
number: "number",
|
|
252
252
|
boolean: "boolean",
|
|
253
|
+
checkbox: "boolean",
|
|
253
254
|
date: "string",
|
|
254
255
|
json: "string",
|
|
255
256
|
buffer: "string",
|
|
@@ -411,31 +412,92 @@ function handleArrayType(field, result, context) {
|
|
|
411
412
|
return result;
|
|
412
413
|
}
|
|
413
414
|
function handleFilterType(field, result, context) {
|
|
414
|
-
const
|
|
415
|
+
const operandOptions = isObject(field.options) ? field.options.store : field.options;
|
|
416
|
+
const operandSchema = {
|
|
417
|
+
title: "Operand A",
|
|
418
|
+
description: "The first operand of the filter."
|
|
419
|
+
};
|
|
420
|
+
if (Array.isArray(operandOptions)) {
|
|
421
|
+
operandSchema.type = "string";
|
|
422
|
+
operandSchema.oneOf = operandOptions.flatMap((optionOrGroup) => {
|
|
423
|
+
if (isOptionGroup(optionOrGroup)) {
|
|
424
|
+
return optionOrGroup.options.map((option) => ({
|
|
425
|
+
title: `${optionOrGroup.label}: ${option.label || option.value}`,
|
|
426
|
+
const: option.value
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
title: optionOrGroup.label || String(optionOrGroup.value),
|
|
431
|
+
const: optionOrGroup.value
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
} else if (typeof operandOptions === "string") {
|
|
435
|
+
operandSchema.type = "string";
|
|
436
|
+
Object.defineProperty(operandSchema, "x-fetch", {
|
|
437
|
+
configurable: true,
|
|
438
|
+
enumerable: true,
|
|
439
|
+
writable: true,
|
|
440
|
+
value: appendQueryString(operandOptions, context.domain, context.tail)
|
|
441
|
+
});
|
|
442
|
+
} else {
|
|
443
|
+
operandSchema.type = IML_FILTER_ENTRY_TYPES;
|
|
444
|
+
}
|
|
445
|
+
const operatorOptions = isObject(field.options) ? field.options.operators : void 0;
|
|
446
|
+
const operatorSchema = {
|
|
447
|
+
title: "Operator",
|
|
448
|
+
description: "The operator of the filter."
|
|
449
|
+
};
|
|
450
|
+
if (Array.isArray(operatorOptions)) {
|
|
451
|
+
operatorSchema.type = "string";
|
|
452
|
+
operatorSchema.oneOf = operatorOptions.flatMap((optionOrGroup) => {
|
|
453
|
+
if (isOptionGroup(optionOrGroup)) {
|
|
454
|
+
return optionOrGroup.options.map((option) => ({
|
|
455
|
+
title: `${optionOrGroup.label}: ${option.label || option.value}`,
|
|
456
|
+
const: option.value
|
|
457
|
+
}));
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
title: optionOrGroup.label || String(optionOrGroup.value),
|
|
461
|
+
const: optionOrGroup.value
|
|
462
|
+
};
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
operatorSchema.enum = IML_BINARY_FILTER_OPERATORS;
|
|
466
|
+
}
|
|
467
|
+
const filterDefinition = {
|
|
415
468
|
oneOf: [
|
|
416
469
|
{
|
|
417
470
|
type: "object",
|
|
418
471
|
properties: {
|
|
419
|
-
a:
|
|
420
|
-
o:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
properties: {
|
|
427
|
-
a: { type: IML_FILTER_ENTRY_TYPES },
|
|
428
|
-
b: { type: IML_FILTER_ENTRY_TYPES },
|
|
429
|
-
o: { enum: IML_BINARY_FILTER_OPERATORS }
|
|
472
|
+
a: operandSchema,
|
|
473
|
+
o: operatorSchema,
|
|
474
|
+
b: {
|
|
475
|
+
title: "Operand B",
|
|
476
|
+
description: "The second operand of the filter.",
|
|
477
|
+
type: IML_FILTER_ENTRY_TYPES
|
|
478
|
+
}
|
|
430
479
|
},
|
|
431
480
|
required: ["a", "b", "o"]
|
|
432
481
|
}
|
|
433
482
|
]
|
|
434
483
|
};
|
|
484
|
+
if (!operatorOptions) {
|
|
485
|
+
filterDefinition.oneOf?.push({
|
|
486
|
+
type: "object",
|
|
487
|
+
properties: {
|
|
488
|
+
a: operandSchema,
|
|
489
|
+
o: {
|
|
490
|
+
...operatorSchema,
|
|
491
|
+
enum: IML_UNARY_FILTER_OPERATORS
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
required: ["a", "o"]
|
|
495
|
+
});
|
|
496
|
+
}
|
|
435
497
|
const logic = field.logic ?? "default";
|
|
436
|
-
result.items = ["and", "or"].includes(logic) ?
|
|
498
|
+
result.items = ["and", "or"].includes(logic) ? filterDefinition : {
|
|
437
499
|
type: "array",
|
|
438
|
-
items:
|
|
500
|
+
items: filterDefinition
|
|
439
501
|
};
|
|
440
502
|
Object.defineProperty(result, "x-filter", {
|
|
441
503
|
configurable: true,
|
|
@@ -461,9 +523,7 @@ function handleSelectOrPathType(field, result, context) {
|
|
|
461
523
|
}
|
|
462
524
|
});
|
|
463
525
|
}
|
|
464
|
-
const nested
|
|
465
|
-
const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
|
|
466
|
-
const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
|
|
526
|
+
const { nested, domain } = extractNestedAndDomain(field);
|
|
467
527
|
if (typeof optionsOrGroups === "string") {
|
|
468
528
|
Object.defineProperty(result, "x-fetch", {
|
|
469
529
|
configurable: true,
|
|
@@ -532,46 +592,7 @@ function handleSelectOrPathType(field, result, context) {
|
|
|
532
592
|
result.enum = (options || []).map((option) => option.value);
|
|
533
593
|
}
|
|
534
594
|
}
|
|
535
|
-
|
|
536
|
-
const normalizedNested = typeof nested === "string" ? [nested] : nested;
|
|
537
|
-
let root = context.roots[domain];
|
|
538
|
-
if (!root) {
|
|
539
|
-
const buffer = [];
|
|
540
|
-
root = context.roots[domain] = {
|
|
541
|
-
buffer,
|
|
542
|
-
addFields: (nested2, tail) => {
|
|
543
|
-
buffer.push(...nested2.map((field2) => ({ field: field2, tail })));
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
root.addFields(normalizedNested, [...context.tail, field.name]);
|
|
548
|
-
} else if (nested) {
|
|
549
|
-
Object.defineProperty(result, "x-nested", {
|
|
550
|
-
configurable: true,
|
|
551
|
-
enumerable: true,
|
|
552
|
-
writable: true,
|
|
553
|
-
value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
|
|
554
|
-
type: "object",
|
|
555
|
-
allOf: nested.map(
|
|
556
|
-
(item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
|
|
557
|
-
{ type: "collection", spec: [item] },
|
|
558
|
-
{
|
|
559
|
-
...context,
|
|
560
|
-
domain: domain || context.domain,
|
|
561
|
-
tail: [...context.tail, field.name]
|
|
562
|
-
}
|
|
563
|
-
)
|
|
564
|
-
)
|
|
565
|
-
} : toJSONSchemaInternal(
|
|
566
|
-
{ type: "collection", spec: nested },
|
|
567
|
-
{
|
|
568
|
-
...context,
|
|
569
|
-
domain: domain || context.domain,
|
|
570
|
-
tail: [...context.tail, field.name]
|
|
571
|
-
}
|
|
572
|
-
)
|
|
573
|
-
});
|
|
574
|
-
}
|
|
595
|
+
result = handleNestedWithDomain(field, nested, domain, result, context);
|
|
575
596
|
if (field.rpc) result = processRpcDirective(field, result, context);
|
|
576
597
|
return result;
|
|
577
598
|
}
|
|
@@ -594,6 +615,10 @@ function handlePrimitiveType(field, result, context) {
|
|
|
594
615
|
}
|
|
595
616
|
}
|
|
596
617
|
if (field.rpc) result = processRpcDirective(field, result, context);
|
|
618
|
+
if (field.nested) {
|
|
619
|
+
const { nested, domain } = extractNestedAndDomain(field);
|
|
620
|
+
result = handleNestedWithDomain(field, nested, domain, result, context);
|
|
621
|
+
}
|
|
597
622
|
return result;
|
|
598
623
|
}
|
|
599
624
|
function processRpcDirective(field, result, context) {
|
|
@@ -610,6 +635,60 @@ function processRpcDirective(field, result, context) {
|
|
|
610
635
|
});
|
|
611
636
|
return result;
|
|
612
637
|
}
|
|
638
|
+
function extractNestedAndDomain(field) {
|
|
639
|
+
const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : isObject(field.nested) ? field.nested.store : field.nested;
|
|
640
|
+
const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : isObject(field.nested) && field.nested.domain ? field.nested.domain : void 0;
|
|
641
|
+
return { nested, domain };
|
|
642
|
+
}
|
|
643
|
+
function handleNestedWithDomain(field, nested, domain, result, context) {
|
|
644
|
+
if (!nested) return result;
|
|
645
|
+
if (domain && domain !== context.domain) {
|
|
646
|
+
const normalizedNested = typeof nested === "string" ? [nested] : nested;
|
|
647
|
+
let root = context.roots[domain];
|
|
648
|
+
if (!root) {
|
|
649
|
+
const buffer = [];
|
|
650
|
+
root = context.roots[domain] = {
|
|
651
|
+
buffer,
|
|
652
|
+
addFields: (nested2, tail) => {
|
|
653
|
+
buffer.push(...nested2.map((field2) => ({ field: field2, tail })));
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
root.addFields(normalizedNested, [...context.tail, field.name]);
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
return processNestedDirective(field, nested, domain, result, context);
|
|
661
|
+
}
|
|
662
|
+
function processNestedDirective(field, nested, domain, result, context) {
|
|
663
|
+
if (!nested) return result;
|
|
664
|
+
const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
|
|
665
|
+
Object.defineProperty(result, "x-nested", {
|
|
666
|
+
configurable: true,
|
|
667
|
+
enumerable: true,
|
|
668
|
+
writable: true,
|
|
669
|
+
value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
|
|
670
|
+
type: "object",
|
|
671
|
+
allOf: nested.map(
|
|
672
|
+
(item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
|
|
673
|
+
{ type: "collection", spec: [item] },
|
|
674
|
+
{
|
|
675
|
+
...context,
|
|
676
|
+
domain: domain || context.domain,
|
|
677
|
+
tail: [...context.tail, field.name]
|
|
678
|
+
}
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
} : toJSONSchemaInternal(
|
|
682
|
+
{ type: "collection", spec: nested },
|
|
683
|
+
{
|
|
684
|
+
...context,
|
|
685
|
+
domain: domain || context.domain,
|
|
686
|
+
tail: [...context.tail, field.name]
|
|
687
|
+
}
|
|
688
|
+
)
|
|
689
|
+
});
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
613
692
|
|
|
614
693
|
// src/validator.ts
|
|
615
694
|
var FORMAN_TYPE_MAP2 = {
|
|
@@ -625,6 +704,7 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
625
704
|
text: "string",
|
|
626
705
|
number: "number",
|
|
627
706
|
boolean: "boolean",
|
|
707
|
+
checkbox: "boolean",
|
|
628
708
|
date: "string",
|
|
629
709
|
json: "string",
|
|
630
710
|
buffer: "string",
|
|
@@ -930,24 +1010,40 @@ async function handleArrayType2(value, field, context) {
|
|
|
930
1010
|
};
|
|
931
1011
|
}
|
|
932
1012
|
async function handleFilterType2(value, field, context) {
|
|
1013
|
+
const operandOptions = isObject(field.options) ? field.options.store : field.options;
|
|
1014
|
+
const operandSpec = {
|
|
1015
|
+
name: "a",
|
|
1016
|
+
type: "any",
|
|
1017
|
+
required: true
|
|
1018
|
+
};
|
|
1019
|
+
if (Array.isArray(operandOptions)) {
|
|
1020
|
+
operandSpec.type = "select";
|
|
1021
|
+
operandSpec.options = operandOptions;
|
|
1022
|
+
}
|
|
1023
|
+
const operatorOptions = isObject(field.options) ? field.options.operators : void 0;
|
|
1024
|
+
const operatorSpec = {
|
|
1025
|
+
name: "o",
|
|
1026
|
+
type: "any",
|
|
1027
|
+
required: true
|
|
1028
|
+
};
|
|
1029
|
+
if (Array.isArray(operatorOptions)) {
|
|
1030
|
+
operatorSpec.type = "select";
|
|
1031
|
+
operatorSpec.grouped = true;
|
|
1032
|
+
operatorSpec.options = operatorOptions;
|
|
1033
|
+
} else {
|
|
1034
|
+
operatorSpec.type = "text";
|
|
1035
|
+
operatorSpec.validate = {
|
|
1036
|
+
enum: IML_FILTER_OPERATORS
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
933
1039
|
const filterEntry = {
|
|
934
1040
|
type: "collection",
|
|
935
1041
|
spec: [
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
type: "any",
|
|
939
|
-
required: true
|
|
940
|
-
},
|
|
1042
|
+
operandSpec,
|
|
1043
|
+
operatorSpec,
|
|
941
1044
|
{
|
|
942
1045
|
name: "b",
|
|
943
1046
|
type: "any"
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
name: "o",
|
|
947
|
-
type: "text",
|
|
948
|
-
validate: {
|
|
949
|
-
enum: IML_FILTER_OPERATORS
|
|
950
|
-
}
|
|
951
1047
|
}
|
|
952
1048
|
]
|
|
953
1049
|
};
|
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' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | `device:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
6
|
+
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'checkbox' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | `device:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
7
7
|
/**
|
|
8
8
|
* Validation configuration for Forman Schema fields
|
|
9
9
|
*/
|
|
@@ -145,6 +145,10 @@ type FormanSchemaOptionGroup = {
|
|
|
145
145
|
/** Group options */
|
|
146
146
|
options: FormanSchemaOption[];
|
|
147
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Helper type for store of select, where options can be either plain options, or groups
|
|
150
|
+
*/
|
|
151
|
+
type FormanSchemaSelectOptionsStore = (FormanSchemaOption | FormanSchemaOptionGroup)[];
|
|
148
152
|
/**
|
|
149
153
|
* Extended options for a select field
|
|
150
154
|
*/
|
|
@@ -162,6 +166,8 @@ type FormanSchemaExtendedOptions = {
|
|
|
162
166
|
label: string;
|
|
163
167
|
nested?: FormanSchemaNested;
|
|
164
168
|
};
|
|
169
|
+
/** Definition of field operators, applicable on Filter fields. */
|
|
170
|
+
operators?: FormanSchemaSelectOptionsStore;
|
|
165
171
|
};
|
|
166
172
|
/**
|
|
167
173
|
* Nested fields
|
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' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | `device:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
6
|
+
type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'checkbox' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | `device:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
|
|
7
7
|
/**
|
|
8
8
|
* Validation configuration for Forman Schema fields
|
|
9
9
|
*/
|
|
@@ -145,6 +145,10 @@ type FormanSchemaOptionGroup = {
|
|
|
145
145
|
/** Group options */
|
|
146
146
|
options: FormanSchemaOption[];
|
|
147
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Helper type for store of select, where options can be either plain options, or groups
|
|
150
|
+
*/
|
|
151
|
+
type FormanSchemaSelectOptionsStore = (FormanSchemaOption | FormanSchemaOptionGroup)[];
|
|
148
152
|
/**
|
|
149
153
|
* Extended options for a select field
|
|
150
154
|
*/
|
|
@@ -162,6 +166,8 @@ type FormanSchemaExtendedOptions = {
|
|
|
162
166
|
label: string;
|
|
163
167
|
nested?: FormanSchemaNested;
|
|
164
168
|
};
|
|
169
|
+
/** Definition of field operators, applicable on Filter fields. */
|
|
170
|
+
operators?: FormanSchemaSelectOptionsStore;
|
|
165
171
|
};
|
|
166
172
|
/**
|
|
167
173
|
* Nested fields
|
package/dist/index.js
CHANGED
|
@@ -221,6 +221,7 @@ var FORMAN_TYPE_MAP = {
|
|
|
221
221
|
editor: "string",
|
|
222
222
|
number: "number",
|
|
223
223
|
boolean: "boolean",
|
|
224
|
+
checkbox: "boolean",
|
|
224
225
|
date: "string",
|
|
225
226
|
json: "string",
|
|
226
227
|
buffer: "string",
|
|
@@ -382,31 +383,92 @@ function handleArrayType(field, result, context) {
|
|
|
382
383
|
return result;
|
|
383
384
|
}
|
|
384
385
|
function handleFilterType(field, result, context) {
|
|
385
|
-
const
|
|
386
|
+
const operandOptions = isObject(field.options) ? field.options.store : field.options;
|
|
387
|
+
const operandSchema = {
|
|
388
|
+
title: "Operand A",
|
|
389
|
+
description: "The first operand of the filter."
|
|
390
|
+
};
|
|
391
|
+
if (Array.isArray(operandOptions)) {
|
|
392
|
+
operandSchema.type = "string";
|
|
393
|
+
operandSchema.oneOf = operandOptions.flatMap((optionOrGroup) => {
|
|
394
|
+
if (isOptionGroup(optionOrGroup)) {
|
|
395
|
+
return optionOrGroup.options.map((option) => ({
|
|
396
|
+
title: `${optionOrGroup.label}: ${option.label || option.value}`,
|
|
397
|
+
const: option.value
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
title: optionOrGroup.label || String(optionOrGroup.value),
|
|
402
|
+
const: optionOrGroup.value
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
} else if (typeof operandOptions === "string") {
|
|
406
|
+
operandSchema.type = "string";
|
|
407
|
+
Object.defineProperty(operandSchema, "x-fetch", {
|
|
408
|
+
configurable: true,
|
|
409
|
+
enumerable: true,
|
|
410
|
+
writable: true,
|
|
411
|
+
value: appendQueryString(operandOptions, context.domain, context.tail)
|
|
412
|
+
});
|
|
413
|
+
} else {
|
|
414
|
+
operandSchema.type = IML_FILTER_ENTRY_TYPES;
|
|
415
|
+
}
|
|
416
|
+
const operatorOptions = isObject(field.options) ? field.options.operators : void 0;
|
|
417
|
+
const operatorSchema = {
|
|
418
|
+
title: "Operator",
|
|
419
|
+
description: "The operator of the filter."
|
|
420
|
+
};
|
|
421
|
+
if (Array.isArray(operatorOptions)) {
|
|
422
|
+
operatorSchema.type = "string";
|
|
423
|
+
operatorSchema.oneOf = operatorOptions.flatMap((optionOrGroup) => {
|
|
424
|
+
if (isOptionGroup(optionOrGroup)) {
|
|
425
|
+
return optionOrGroup.options.map((option) => ({
|
|
426
|
+
title: `${optionOrGroup.label}: ${option.label || option.value}`,
|
|
427
|
+
const: option.value
|
|
428
|
+
}));
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
title: optionOrGroup.label || String(optionOrGroup.value),
|
|
432
|
+
const: optionOrGroup.value
|
|
433
|
+
};
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
operatorSchema.enum = IML_BINARY_FILTER_OPERATORS;
|
|
437
|
+
}
|
|
438
|
+
const filterDefinition = {
|
|
386
439
|
oneOf: [
|
|
387
440
|
{
|
|
388
441
|
type: "object",
|
|
389
442
|
properties: {
|
|
390
|
-
a:
|
|
391
|
-
o:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
properties: {
|
|
398
|
-
a: { type: IML_FILTER_ENTRY_TYPES },
|
|
399
|
-
b: { type: IML_FILTER_ENTRY_TYPES },
|
|
400
|
-
o: { enum: IML_BINARY_FILTER_OPERATORS }
|
|
443
|
+
a: operandSchema,
|
|
444
|
+
o: operatorSchema,
|
|
445
|
+
b: {
|
|
446
|
+
title: "Operand B",
|
|
447
|
+
description: "The second operand of the filter.",
|
|
448
|
+
type: IML_FILTER_ENTRY_TYPES
|
|
449
|
+
}
|
|
401
450
|
},
|
|
402
451
|
required: ["a", "b", "o"]
|
|
403
452
|
}
|
|
404
453
|
]
|
|
405
454
|
};
|
|
455
|
+
if (!operatorOptions) {
|
|
456
|
+
filterDefinition.oneOf?.push({
|
|
457
|
+
type: "object",
|
|
458
|
+
properties: {
|
|
459
|
+
a: operandSchema,
|
|
460
|
+
o: {
|
|
461
|
+
...operatorSchema,
|
|
462
|
+
enum: IML_UNARY_FILTER_OPERATORS
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
required: ["a", "o"]
|
|
466
|
+
});
|
|
467
|
+
}
|
|
406
468
|
const logic = field.logic ?? "default";
|
|
407
|
-
result.items = ["and", "or"].includes(logic) ?
|
|
469
|
+
result.items = ["and", "or"].includes(logic) ? filterDefinition : {
|
|
408
470
|
type: "array",
|
|
409
|
-
items:
|
|
471
|
+
items: filterDefinition
|
|
410
472
|
};
|
|
411
473
|
Object.defineProperty(result, "x-filter", {
|
|
412
474
|
configurable: true,
|
|
@@ -432,9 +494,7 @@ function handleSelectOrPathType(field, result, context) {
|
|
|
432
494
|
}
|
|
433
495
|
});
|
|
434
496
|
}
|
|
435
|
-
const nested
|
|
436
|
-
const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
|
|
437
|
-
const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
|
|
497
|
+
const { nested, domain } = extractNestedAndDomain(field);
|
|
438
498
|
if (typeof optionsOrGroups === "string") {
|
|
439
499
|
Object.defineProperty(result, "x-fetch", {
|
|
440
500
|
configurable: true,
|
|
@@ -503,46 +563,7 @@ function handleSelectOrPathType(field, result, context) {
|
|
|
503
563
|
result.enum = (options || []).map((option) => option.value);
|
|
504
564
|
}
|
|
505
565
|
}
|
|
506
|
-
|
|
507
|
-
const normalizedNested = typeof nested === "string" ? [nested] : nested;
|
|
508
|
-
let root = context.roots[domain];
|
|
509
|
-
if (!root) {
|
|
510
|
-
const buffer = [];
|
|
511
|
-
root = context.roots[domain] = {
|
|
512
|
-
buffer,
|
|
513
|
-
addFields: (nested2, tail) => {
|
|
514
|
-
buffer.push(...nested2.map((field2) => ({ field: field2, tail })));
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
root.addFields(normalizedNested, [...context.tail, field.name]);
|
|
519
|
-
} else if (nested) {
|
|
520
|
-
Object.defineProperty(result, "x-nested", {
|
|
521
|
-
configurable: true,
|
|
522
|
-
enumerable: true,
|
|
523
|
-
writable: true,
|
|
524
|
-
value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
|
|
525
|
-
type: "object",
|
|
526
|
-
allOf: nested.map(
|
|
527
|
-
(item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
|
|
528
|
-
{ type: "collection", spec: [item] },
|
|
529
|
-
{
|
|
530
|
-
...context,
|
|
531
|
-
domain: domain || context.domain,
|
|
532
|
-
tail: [...context.tail, field.name]
|
|
533
|
-
}
|
|
534
|
-
)
|
|
535
|
-
)
|
|
536
|
-
} : toJSONSchemaInternal(
|
|
537
|
-
{ type: "collection", spec: nested },
|
|
538
|
-
{
|
|
539
|
-
...context,
|
|
540
|
-
domain: domain || context.domain,
|
|
541
|
-
tail: [...context.tail, field.name]
|
|
542
|
-
}
|
|
543
|
-
)
|
|
544
|
-
});
|
|
545
|
-
}
|
|
566
|
+
result = handleNestedWithDomain(field, nested, domain, result, context);
|
|
546
567
|
if (field.rpc) result = processRpcDirective(field, result, context);
|
|
547
568
|
return result;
|
|
548
569
|
}
|
|
@@ -565,6 +586,10 @@ function handlePrimitiveType(field, result, context) {
|
|
|
565
586
|
}
|
|
566
587
|
}
|
|
567
588
|
if (field.rpc) result = processRpcDirective(field, result, context);
|
|
589
|
+
if (field.nested) {
|
|
590
|
+
const { nested, domain } = extractNestedAndDomain(field);
|
|
591
|
+
result = handleNestedWithDomain(field, nested, domain, result, context);
|
|
592
|
+
}
|
|
568
593
|
return result;
|
|
569
594
|
}
|
|
570
595
|
function processRpcDirective(field, result, context) {
|
|
@@ -581,6 +606,60 @@ function processRpcDirective(field, result, context) {
|
|
|
581
606
|
});
|
|
582
607
|
return result;
|
|
583
608
|
}
|
|
609
|
+
function extractNestedAndDomain(field) {
|
|
610
|
+
const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : isObject(field.nested) ? field.nested.store : field.nested;
|
|
611
|
+
const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : isObject(field.nested) && field.nested.domain ? field.nested.domain : void 0;
|
|
612
|
+
return { nested, domain };
|
|
613
|
+
}
|
|
614
|
+
function handleNestedWithDomain(field, nested, domain, result, context) {
|
|
615
|
+
if (!nested) return result;
|
|
616
|
+
if (domain && domain !== context.domain) {
|
|
617
|
+
const normalizedNested = typeof nested === "string" ? [nested] : nested;
|
|
618
|
+
let root = context.roots[domain];
|
|
619
|
+
if (!root) {
|
|
620
|
+
const buffer = [];
|
|
621
|
+
root = context.roots[domain] = {
|
|
622
|
+
buffer,
|
|
623
|
+
addFields: (nested2, tail) => {
|
|
624
|
+
buffer.push(...nested2.map((field2) => ({ field: field2, tail })));
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
root.addFields(normalizedNested, [...context.tail, field.name]);
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
return processNestedDirective(field, nested, domain, result, context);
|
|
632
|
+
}
|
|
633
|
+
function processNestedDirective(field, nested, domain, result, context) {
|
|
634
|
+
if (!nested) return result;
|
|
635
|
+
const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
|
|
636
|
+
Object.defineProperty(result, "x-nested", {
|
|
637
|
+
configurable: true,
|
|
638
|
+
enumerable: true,
|
|
639
|
+
writable: true,
|
|
640
|
+
value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
|
|
641
|
+
type: "object",
|
|
642
|
+
allOf: nested.map(
|
|
643
|
+
(item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
|
|
644
|
+
{ type: "collection", spec: [item] },
|
|
645
|
+
{
|
|
646
|
+
...context,
|
|
647
|
+
domain: domain || context.domain,
|
|
648
|
+
tail: [...context.tail, field.name]
|
|
649
|
+
}
|
|
650
|
+
)
|
|
651
|
+
)
|
|
652
|
+
} : toJSONSchemaInternal(
|
|
653
|
+
{ type: "collection", spec: nested },
|
|
654
|
+
{
|
|
655
|
+
...context,
|
|
656
|
+
domain: domain || context.domain,
|
|
657
|
+
tail: [...context.tail, field.name]
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
});
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
584
663
|
|
|
585
664
|
// src/validator.ts
|
|
586
665
|
var FORMAN_TYPE_MAP2 = {
|
|
@@ -596,6 +675,7 @@ var FORMAN_TYPE_MAP2 = {
|
|
|
596
675
|
text: "string",
|
|
597
676
|
number: "number",
|
|
598
677
|
boolean: "boolean",
|
|
678
|
+
checkbox: "boolean",
|
|
599
679
|
date: "string",
|
|
600
680
|
json: "string",
|
|
601
681
|
buffer: "string",
|
|
@@ -901,24 +981,40 @@ async function handleArrayType2(value, field, context) {
|
|
|
901
981
|
};
|
|
902
982
|
}
|
|
903
983
|
async function handleFilterType2(value, field, context) {
|
|
984
|
+
const operandOptions = isObject(field.options) ? field.options.store : field.options;
|
|
985
|
+
const operandSpec = {
|
|
986
|
+
name: "a",
|
|
987
|
+
type: "any",
|
|
988
|
+
required: true
|
|
989
|
+
};
|
|
990
|
+
if (Array.isArray(operandOptions)) {
|
|
991
|
+
operandSpec.type = "select";
|
|
992
|
+
operandSpec.options = operandOptions;
|
|
993
|
+
}
|
|
994
|
+
const operatorOptions = isObject(field.options) ? field.options.operators : void 0;
|
|
995
|
+
const operatorSpec = {
|
|
996
|
+
name: "o",
|
|
997
|
+
type: "any",
|
|
998
|
+
required: true
|
|
999
|
+
};
|
|
1000
|
+
if (Array.isArray(operatorOptions)) {
|
|
1001
|
+
operatorSpec.type = "select";
|
|
1002
|
+
operatorSpec.grouped = true;
|
|
1003
|
+
operatorSpec.options = operatorOptions;
|
|
1004
|
+
} else {
|
|
1005
|
+
operatorSpec.type = "text";
|
|
1006
|
+
operatorSpec.validate = {
|
|
1007
|
+
enum: IML_FILTER_OPERATORS
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
904
1010
|
const filterEntry = {
|
|
905
1011
|
type: "collection",
|
|
906
1012
|
spec: [
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
type: "any",
|
|
910
|
-
required: true
|
|
911
|
-
},
|
|
1013
|
+
operandSpec,
|
|
1014
|
+
operatorSpec,
|
|
912
1015
|
{
|
|
913
1016
|
name: "b",
|
|
914
1017
|
type: "any"
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
name: "o",
|
|
918
|
-
type: "text",
|
|
919
|
-
validate: {
|
|
920
|
-
enum: IML_FILTER_OPERATORS
|
|
921
|
-
}
|
|
922
1018
|
}
|
|
923
1019
|
]
|
|
924
1020
|
};
|