@makehq/forman-schema 1.5.0 → 1.6.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/README.md CHANGED
@@ -185,6 +185,7 @@ const result = await validateFormanWithDomains(
185
185
  - email → string
186
186
  - file → string
187
187
  - filename → string
188
+ - filter → array
188
189
  - folder → string
189
190
  - hidden → string
190
191
  - hook → number
package/dist/index.cjs CHANGED
@@ -152,6 +152,67 @@ function buildRestoreStructure(items) {
152
152
  }
153
153
  return result;
154
154
  }
155
+ var IML_FILTER_ENTRY_TYPES = ["null", "boolean", "number", "string"];
156
+ var IML_UNARY_FILTER_OPERATORS = ["exist", "notexist"];
157
+ var IML_BINARY_FILTER_OPERATORS = [
158
+ "text:equal",
159
+ "text:equal:ci",
160
+ "text:notequal",
161
+ "text:notequal:ci",
162
+ "text:contain",
163
+ "text:contain:ci",
164
+ "text:notcontain",
165
+ "text:notcontain:ci",
166
+ "text:startwith",
167
+ "text:startwith:ci",
168
+ "text:notstartwith",
169
+ "text:notstartwith:ci",
170
+ "text:endwith",
171
+ "text:endwith:ci",
172
+ "text:notendwith",
173
+ "text:notendwith:ci",
174
+ "text:pattern",
175
+ "text:pattern:ci",
176
+ "text:notpattern",
177
+ "text:notpattern:ci",
178
+ "number:equal",
179
+ "number:notequal",
180
+ "number:greater",
181
+ "number:less",
182
+ "number:greaterorequal",
183
+ "number:lessorequal",
184
+ "date:equal",
185
+ "date:notequal",
186
+ "date:greater",
187
+ "date:less",
188
+ "date:greaterorequal",
189
+ "date:lessorequal",
190
+ "time:equal",
191
+ "time:notequal",
192
+ "time:greater",
193
+ "time:less",
194
+ "time:greaterorequal",
195
+ "time:lessorequal",
196
+ "semver:equal",
197
+ "semver:notequal",
198
+ "semver:greater",
199
+ "semver:less",
200
+ "semver:greaterorequal",
201
+ "semver:lessorequal",
202
+ "array:contain",
203
+ "array:contain:ci",
204
+ "array:notcontain",
205
+ "array:notcontain:ci",
206
+ "array:equal",
207
+ "array:notequal",
208
+ "array:greater",
209
+ "array:less",
210
+ "array:greaterorequal",
211
+ "array:lessorequal",
212
+ "boolean:equal",
213
+ "boolean:notequal"
214
+ ];
215
+ var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
155
216
 
156
217
  // src/forman.ts
157
218
  var SchemaConversionError = class extends Error {
@@ -187,6 +248,7 @@ var FORMAN_TYPE_MAP = {
187
248
  email: "string",
188
249
  filename: "string",
189
250
  file: "string",
251
+ filter: "array",
190
252
  folder: "string",
191
253
  hidden: void 0,
192
254
  integer: "number",
@@ -253,6 +315,8 @@ function toJSONSchemaInternal(field, context = createDefaultContext()) {
253
315
  case "file":
254
316
  case "folder":
255
317
  return handleSelectType(normalizedField, result, context);
318
+ case "filter":
319
+ return handleFilterType(normalizedField, result, context);
256
320
  default:
257
321
  return handlePrimitiveType(normalizedField, result);
258
322
  }
@@ -329,6 +393,41 @@ function handleArrayType(field, result, context) {
329
393
  }
330
394
  return result;
331
395
  }
396
+ function handleFilterType(field, result, context) {
397
+ const filterItems = {
398
+ oneOf: [
399
+ {
400
+ type: "object",
401
+ properties: {
402
+ a: { type: IML_FILTER_ENTRY_TYPES },
403
+ o: { enum: IML_UNARY_FILTER_OPERATORS }
404
+ },
405
+ required: ["a", "o"]
406
+ },
407
+ {
408
+ type: "object",
409
+ properties: {
410
+ a: { type: IML_FILTER_ENTRY_TYPES },
411
+ b: { type: IML_FILTER_ENTRY_TYPES },
412
+ o: { enum: IML_BINARY_FILTER_OPERATORS }
413
+ },
414
+ required: ["a", "b", "o"]
415
+ }
416
+ ]
417
+ };
418
+ const logic = field.logic ?? "default";
419
+ result.items = ["and", "or"].includes(logic) ? filterItems : {
420
+ type: "array",
421
+ items: filterItems
422
+ };
423
+ Object.defineProperty(result, "x-filter", {
424
+ configurable: true,
425
+ enumerable: true,
426
+ writable: true,
427
+ value: logic
428
+ });
429
+ return result;
430
+ }
332
431
  function handleSelectType(field, result, context) {
333
432
  const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
334
433
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
@@ -485,6 +584,7 @@ var FORMAN_TYPE_MAP2 = {
485
584
  email: "string",
486
585
  filename: "string",
487
586
  file: "string",
587
+ filter: "array",
488
588
  folder: "string",
489
589
  hidden: void 0,
490
590
  integer: "number",
@@ -648,6 +748,8 @@ async function validateFormanValue(value, field, context) {
648
748
  case "file":
649
749
  case "folder":
650
750
  return handleSelectType2(value, normalizedField, context);
751
+ case "filter":
752
+ return handleFilterType2(value, normalizedField, context);
651
753
  default:
652
754
  return handlePrimitiveType2(value, normalizedField, context);
653
755
  }
@@ -774,6 +876,42 @@ async function handleArrayType2(value, field, context) {
774
876
  errors
775
877
  };
776
878
  }
879
+ async function handleFilterType2(value, field, context) {
880
+ const filterEntry = {
881
+ type: "collection",
882
+ spec: [
883
+ {
884
+ name: "a",
885
+ type: "any",
886
+ required: true
887
+ },
888
+ {
889
+ name: "b",
890
+ type: "any"
891
+ },
892
+ {
893
+ name: "o",
894
+ type: "text",
895
+ validate: {
896
+ enum: IML_FILTER_OPERATORS
897
+ }
898
+ }
899
+ ]
900
+ };
901
+ const inlineSchema = ["and", "or"].includes(field.logic ?? "default") ? {
902
+ name: field.name,
903
+ type: "array",
904
+ spec: filterEntry
905
+ } : {
906
+ name: field.name,
907
+ type: "array",
908
+ spec: {
909
+ type: "array",
910
+ spec: filterEntry
911
+ }
912
+ };
913
+ return handleArrayType2(value, inlineSchema, context);
914
+ }
777
915
  async function handleSelectType2(value, field, context) {
778
916
  const errors = [];
779
917
  let optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
@@ -1018,6 +1156,15 @@ function toFormanSchema(field) {
1018
1156
  spec
1019
1157
  };
1020
1158
  case "array":
1159
+ const filterLogic = Object.getOwnPropertyDescriptor(field, "x-filter");
1160
+ if (filterLogic) {
1161
+ return {
1162
+ type: "filter",
1163
+ label: noEmpty(field.title),
1164
+ help: noEmpty(field.description),
1165
+ logic: filterLogic.value === "default" ? void 0 : filterLogic.value
1166
+ };
1167
+ }
1021
1168
  const items = field.items && isObject(field.items) ? field.items : void 0;
1022
1169
  const formanSchema = {
1023
1170
  type: "array",
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' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
6
+ type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
7
7
  /**
8
8
  * Validation configuration for Forman Schema fields
9
9
  */
@@ -79,6 +79,8 @@ type FormanSchemaField = {
79
79
  sort?: string;
80
80
  /** Adds an extra button to the field which opens an extra form. When the form is submitted, a specified RPC is called and the result is set as a new value of the parameter. */
81
81
  rpc?: FormanSchemaRPCButton;
82
+ /** Definition of boolean logic to apply (filter only) */
83
+ logic?: 'and' | 'or' | 'reverse';
82
84
  } & Record<`x-${string}`, unknown>;
83
85
  /**
84
86
  * RPC button allows for dynamic value retrieval from an external source
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' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
6
+ type FormanSchemaFieldType = 'aiagent' | 'account' | 'hook' | 'device' | 'keychain' | 'datastore' | 'udt' | 'scenario' | 'array' | 'collection' | 'text' | 'number' | 'boolean' | 'date' | 'json' | 'buffer' | 'cert' | 'color' | 'email' | 'filename' | 'file' | 'filter' | 'folder' | 'hidden' | 'integer' | 'uinteger' | 'path' | 'pkey' | 'port' | 'select' | 'time' | 'timestamp' | 'timezone' | 'url' | 'uuid' | `account:${string}` | `hook:${string}` | `keychain:${string}` | 'banner' | 'markdown' | 'html' | 'separator' | string;
7
7
  /**
8
8
  * Validation configuration for Forman Schema fields
9
9
  */
@@ -79,6 +79,8 @@ type FormanSchemaField = {
79
79
  sort?: string;
80
80
  /** Adds an extra button to the field which opens an extra form. When the form is submitted, a specified RPC is called and the result is set as a new value of the parameter. */
81
81
  rpc?: FormanSchemaRPCButton;
82
+ /** Definition of boolean logic to apply (filter only) */
83
+ logic?: 'and' | 'or' | 'reverse';
82
84
  } & Record<`x-${string}`, unknown>;
83
85
  /**
84
86
  * RPC button allows for dynamic value retrieval from an external source
package/dist/index.js CHANGED
@@ -123,6 +123,67 @@ function buildRestoreStructure(items) {
123
123
  }
124
124
  return result;
125
125
  }
126
+ var IML_FILTER_ENTRY_TYPES = ["null", "boolean", "number", "string"];
127
+ var IML_UNARY_FILTER_OPERATORS = ["exist", "notexist"];
128
+ var IML_BINARY_FILTER_OPERATORS = [
129
+ "text:equal",
130
+ "text:equal:ci",
131
+ "text:notequal",
132
+ "text:notequal:ci",
133
+ "text:contain",
134
+ "text:contain:ci",
135
+ "text:notcontain",
136
+ "text:notcontain:ci",
137
+ "text:startwith",
138
+ "text:startwith:ci",
139
+ "text:notstartwith",
140
+ "text:notstartwith:ci",
141
+ "text:endwith",
142
+ "text:endwith:ci",
143
+ "text:notendwith",
144
+ "text:notendwith:ci",
145
+ "text:pattern",
146
+ "text:pattern:ci",
147
+ "text:notpattern",
148
+ "text:notpattern:ci",
149
+ "number:equal",
150
+ "number:notequal",
151
+ "number:greater",
152
+ "number:less",
153
+ "number:greaterorequal",
154
+ "number:lessorequal",
155
+ "date:equal",
156
+ "date:notequal",
157
+ "date:greater",
158
+ "date:less",
159
+ "date:greaterorequal",
160
+ "date:lessorequal",
161
+ "time:equal",
162
+ "time:notequal",
163
+ "time:greater",
164
+ "time:less",
165
+ "time:greaterorequal",
166
+ "time:lessorequal",
167
+ "semver:equal",
168
+ "semver:notequal",
169
+ "semver:greater",
170
+ "semver:less",
171
+ "semver:greaterorequal",
172
+ "semver:lessorequal",
173
+ "array:contain",
174
+ "array:contain:ci",
175
+ "array:notcontain",
176
+ "array:notcontain:ci",
177
+ "array:equal",
178
+ "array:notequal",
179
+ "array:greater",
180
+ "array:less",
181
+ "array:greaterorequal",
182
+ "array:lessorequal",
183
+ "boolean:equal",
184
+ "boolean:notequal"
185
+ ];
186
+ var IML_FILTER_OPERATORS = [...IML_UNARY_FILTER_OPERATORS, ...IML_BINARY_FILTER_OPERATORS];
126
187
 
127
188
  // src/forman.ts
128
189
  var SchemaConversionError = class extends Error {
@@ -158,6 +219,7 @@ var FORMAN_TYPE_MAP = {
158
219
  email: "string",
159
220
  filename: "string",
160
221
  file: "string",
222
+ filter: "array",
161
223
  folder: "string",
162
224
  hidden: void 0,
163
225
  integer: "number",
@@ -224,6 +286,8 @@ function toJSONSchemaInternal(field, context = createDefaultContext()) {
224
286
  case "file":
225
287
  case "folder":
226
288
  return handleSelectType(normalizedField, result, context);
289
+ case "filter":
290
+ return handleFilterType(normalizedField, result, context);
227
291
  default:
228
292
  return handlePrimitiveType(normalizedField, result);
229
293
  }
@@ -300,6 +364,41 @@ function handleArrayType(field, result, context) {
300
364
  }
301
365
  return result;
302
366
  }
367
+ function handleFilterType(field, result, context) {
368
+ const filterItems = {
369
+ oneOf: [
370
+ {
371
+ type: "object",
372
+ properties: {
373
+ a: { type: IML_FILTER_ENTRY_TYPES },
374
+ o: { enum: IML_UNARY_FILTER_OPERATORS }
375
+ },
376
+ required: ["a", "o"]
377
+ },
378
+ {
379
+ type: "object",
380
+ properties: {
381
+ a: { type: IML_FILTER_ENTRY_TYPES },
382
+ b: { type: IML_FILTER_ENTRY_TYPES },
383
+ o: { enum: IML_BINARY_FILTER_OPERATORS }
384
+ },
385
+ required: ["a", "b", "o"]
386
+ }
387
+ ]
388
+ };
389
+ const logic = field.logic ?? "default";
390
+ result.items = ["and", "or"].includes(logic) ? filterItems : {
391
+ type: "array",
392
+ items: filterItems
393
+ };
394
+ Object.defineProperty(result, "x-filter", {
395
+ configurable: true,
396
+ enumerable: true,
397
+ writable: true,
398
+ value: logic
399
+ });
400
+ return result;
401
+ }
303
402
  function handleSelectType(field, result, context) {
304
403
  const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
305
404
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
@@ -456,6 +555,7 @@ var FORMAN_TYPE_MAP2 = {
456
555
  email: "string",
457
556
  filename: "string",
458
557
  file: "string",
558
+ filter: "array",
459
559
  folder: "string",
460
560
  hidden: void 0,
461
561
  integer: "number",
@@ -619,6 +719,8 @@ async function validateFormanValue(value, field, context) {
619
719
  case "file":
620
720
  case "folder":
621
721
  return handleSelectType2(value, normalizedField, context);
722
+ case "filter":
723
+ return handleFilterType2(value, normalizedField, context);
622
724
  default:
623
725
  return handlePrimitiveType2(value, normalizedField, context);
624
726
  }
@@ -745,6 +847,42 @@ async function handleArrayType2(value, field, context) {
745
847
  errors
746
848
  };
747
849
  }
850
+ async function handleFilterType2(value, field, context) {
851
+ const filterEntry = {
852
+ type: "collection",
853
+ spec: [
854
+ {
855
+ name: "a",
856
+ type: "any",
857
+ required: true
858
+ },
859
+ {
860
+ name: "b",
861
+ type: "any"
862
+ },
863
+ {
864
+ name: "o",
865
+ type: "text",
866
+ validate: {
867
+ enum: IML_FILTER_OPERATORS
868
+ }
869
+ }
870
+ ]
871
+ };
872
+ const inlineSchema = ["and", "or"].includes(field.logic ?? "default") ? {
873
+ name: field.name,
874
+ type: "array",
875
+ spec: filterEntry
876
+ } : {
877
+ name: field.name,
878
+ type: "array",
879
+ spec: {
880
+ type: "array",
881
+ spec: filterEntry
882
+ }
883
+ };
884
+ return handleArrayType2(value, inlineSchema, context);
885
+ }
748
886
  async function handleSelectType2(value, field, context) {
749
887
  const errors = [];
750
888
  let optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
@@ -989,6 +1127,15 @@ function toFormanSchema(field) {
989
1127
  spec
990
1128
  };
991
1129
  case "array":
1130
+ const filterLogic = Object.getOwnPropertyDescriptor(field, "x-filter");
1131
+ if (filterLogic) {
1132
+ return {
1133
+ type: "filter",
1134
+ label: noEmpty(field.title),
1135
+ help: noEmpty(field.description),
1136
+ logic: filterLogic.value === "default" ? void 0 : filterLogic.value
1137
+ };
1138
+ }
992
1139
  const items = field.items && isObject(field.items) ? field.items : void 0;
993
1140
  const formanSchema = {
994
1141
  type: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",