@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 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 filterItems = {
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: { type: IML_FILTER_ENTRY_TYPES },
420
- o: { enum: IML_UNARY_FILTER_OPERATORS }
421
- },
422
- required: ["a", "o"]
423
- },
424
- {
425
- type: "object",
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) ? filterItems : {
498
+ result.items = ["and", "or"].includes(logic) ? filterDefinition : {
437
499
  type: "array",
438
- items: filterItems
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 = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
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
- if (nested && domain && domain !== context.domain) {
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
- name: "a",
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 filterItems = {
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: { type: IML_FILTER_ENTRY_TYPES },
391
- o: { enum: IML_UNARY_FILTER_OPERATORS }
392
- },
393
- required: ["a", "o"]
394
- },
395
- {
396
- type: "object",
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) ? filterItems : {
469
+ result.items = ["and", "or"].includes(logic) ? filterDefinition : {
408
470
  type: "array",
409
- items: filterItems
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 = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
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
- if (nested && domain && domain !== context.domain) {
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
- name: "a",
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",