@makehq/forman-schema 1.2.2 → 1.2.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 CHANGED
@@ -32,6 +32,9 @@ function noEmpty(text) {
32
32
  function isObject(value) {
33
33
  return typeof value === "object" && value !== null && !Array.isArray(value);
34
34
  }
35
+ function isOptionGroup(value) {
36
+ return "options" in value && Array.isArray(value.options);
37
+ }
35
38
 
36
39
  // src/forman.ts
37
40
  var SchemaConversionError = class extends Error {
@@ -241,41 +244,52 @@ function handleArrayType(field, result, context) {
241
244
  return result;
242
245
  }
243
246
  function handleSelectType(field, result, context) {
244
- const options = isObject(field.options) ? field.options.store : field.options;
247
+ const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
245
248
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
246
249
  const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
247
- if (typeof options === "string") {
250
+ if (typeof optionsOrGroups === "string") {
248
251
  Object.defineProperty(result, "x-fetch", {
249
252
  configurable: true,
250
253
  enumerable: true,
251
254
  writable: true,
252
- value: appendQueryString(options, context.domain, context.tail)
253
- });
254
- } else if (options?.some((option) => option.label || option.nested)) {
255
- result.oneOf = (options || []).map((option) => {
256
- const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
257
- const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
258
- if (localNested) {
259
- context.addConditionalFields(
260
- field.name,
261
- option.value,
262
- typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
263
- { type: "collection", spec: localNested },
264
- {
265
- ...context,
266
- domain: localDomain,
267
- tail: [...context.tail, field.name]
268
- }
269
- )
270
- );
271
- }
272
- return {
273
- title: noEmpty(option.label),
274
- const: option.value
275
- };
255
+ value: appendQueryString(optionsOrGroups, context.domain, context.tail)
276
256
  });
277
257
  } else {
278
- result.enum = (options || []).map((option) => option.value);
258
+ const options = optionsOrGroups?.some(isOptionGroup) ? optionsOrGroups.flatMap(
259
+ (group) => group.options.map((option) => ({
260
+ ...option,
261
+ label: `${group.label}: ${option.label || option.value}`
262
+ }))
263
+ ) : optionsOrGroups || [];
264
+ if (options?.some((option) => {
265
+ if (isOptionGroup(option)) return option.options.some((option2) => option2.label || option2.nested);
266
+ return option.label || option.nested;
267
+ })) {
268
+ result.oneOf = (options || []).map((option) => {
269
+ const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
270
+ const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
271
+ if (localNested) {
272
+ context.addConditionalFields(
273
+ field.name,
274
+ option.value,
275
+ typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
276
+ { type: "collection", spec: localNested },
277
+ {
278
+ ...context,
279
+ domain: localDomain,
280
+ tail: [...context.tail, field.name]
281
+ }
282
+ )
283
+ );
284
+ }
285
+ return {
286
+ title: noEmpty(option.label),
287
+ const: option.value
288
+ };
289
+ });
290
+ } else {
291
+ result.enum = (options || []).map((option) => option.value);
292
+ }
279
293
  }
280
294
  if (nested && domain && domain !== context.domain) {
281
295
  if (typeof nested === "string") {
package/dist/index.d.cts CHANGED
@@ -34,7 +34,7 @@ type FormanSchemaField = {
34
34
  /** Default value for the field */
35
35
  default?: FormanSchemaValue;
36
36
  /** Available options for select type fields */
37
- options?: FormanSchemaOption[] | FormanSchemaExtendedOptions | string;
37
+ options?: FormanSchemaOption[] | FormanSchemaOptionGroup[] | FormanSchemaExtendedOptions | string;
38
38
  /** Help text or description for the field */
39
39
  help?: string;
40
40
  /** Sub-fields specification for collection or array types */
@@ -47,7 +47,46 @@ type FormanSchemaField = {
47
47
  nested?: FormanSchemaNested;
48
48
  /** Validation rules */
49
49
  validate?: FormanSchemaValidation;
50
+ /** Whether the field is disabled (`false` by default) */
51
+ disabled?: boolean;
52
+ /** Whether the field is mappable */
53
+ mappable?: boolean;
54
+ /** Whether the user will be able to insert new lines in GUI (a textarea will be displayed instead of the text field) */
55
+ multiline?: boolean;
56
+ /** Specifies how to treat HTML tags in the field (text only) */
57
+ tags?: 'strip' | 'stripall' | 'escape';
58
+ /** Allowed extension or array of allowed extensions. (filename only) */
59
+ extension?: string | string[];
60
+ /** Semantic type for the field */
61
+ semantic?: string;
62
+ /** Whether to allow time selection (date only, `true` by default) */
63
+ time?: boolean;
64
+ /** Whether the properties of the object will be in the same order as they are defined in the spec (collection only) */
65
+ sequence?: boolean;
66
+ /** Codepage for the field (buffer only) */
67
+ codepage?: string;
68
+ /** Whether the field is grouped (select only) */
69
+ grouped?: boolean;
70
+ /** Whether a mapped value in the select should be validated against the option values. If true, the value is treated as a dynamic and validation is disabled. The value is set to `true` automatically if select options are generated using RPC. */
71
+ dynamic?: boolean;
72
+ /** Mode for the field (select only) */
73
+ mode?: 'edit' | 'choose';
74
+ /** Sort order for the field (select only) */
75
+ sort?: string;
76
+ /** 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. */
77
+ rpc?: FormanSchemaRPCButton;
50
78
  } & Record<`x-${string}`, unknown>;
79
+ /**
80
+ * RPC button allows for dynamic value retrieval from an external source
81
+ */
82
+ type FormanSchemaRPCButton = {
83
+ /** RPC button label */
84
+ label: string;
85
+ /** RPC button URL */
86
+ url: string;
87
+ /** RPC button parameters */
88
+ parameters: FormanSchemaField[];
89
+ };
51
90
  /**
52
91
  * Valid Forman Schema values
53
92
  */
@@ -60,15 +99,26 @@ type FormanSchemaOption = {
60
99
  value: FormanSchemaValue;
61
100
  /** Option label */
62
101
  label?: string;
102
+ /** Whether the option is the default */
103
+ default?: boolean;
63
104
  /** Nested fields for this option */
64
105
  nested?: FormanSchemaNested;
65
106
  };
107
+ /**
108
+ * Option group for a select field
109
+ */
110
+ type FormanSchemaOptionGroup = {
111
+ /** Group label */
112
+ label: string;
113
+ /** Group options */
114
+ options: FormanSchemaOption[];
115
+ };
66
116
  /**
67
117
  * Extended options for a select field
68
118
  */
69
119
  type FormanSchemaExtendedOptions = {
70
120
  /** Store for the options */
71
- store: FormanSchemaOption[] | string;
121
+ store: FormanSchemaOption[] | FormanSchemaOptionGroup[] | string;
72
122
  /** Nested fields for every option */
73
123
  nested?: FormanSchemaNested;
74
124
  };
@@ -100,4 +150,4 @@ declare function toFormanSchema(field: JSONSchema7): FormanSchemaField;
100
150
  */
101
151
  declare function toJSONSchema(field: FormanSchemaField): JSONSchema7;
102
152
 
103
- export { type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaValue, toFormanSchema, toJSONSchema };
153
+ export { type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaOptionGroup, type FormanSchemaRPCButton, type FormanSchemaValue, toFormanSchema, toJSONSchema };
package/dist/index.d.ts CHANGED
@@ -34,7 +34,7 @@ type FormanSchemaField = {
34
34
  /** Default value for the field */
35
35
  default?: FormanSchemaValue;
36
36
  /** Available options for select type fields */
37
- options?: FormanSchemaOption[] | FormanSchemaExtendedOptions | string;
37
+ options?: FormanSchemaOption[] | FormanSchemaOptionGroup[] | FormanSchemaExtendedOptions | string;
38
38
  /** Help text or description for the field */
39
39
  help?: string;
40
40
  /** Sub-fields specification for collection or array types */
@@ -47,7 +47,46 @@ type FormanSchemaField = {
47
47
  nested?: FormanSchemaNested;
48
48
  /** Validation rules */
49
49
  validate?: FormanSchemaValidation;
50
+ /** Whether the field is disabled (`false` by default) */
51
+ disabled?: boolean;
52
+ /** Whether the field is mappable */
53
+ mappable?: boolean;
54
+ /** Whether the user will be able to insert new lines in GUI (a textarea will be displayed instead of the text field) */
55
+ multiline?: boolean;
56
+ /** Specifies how to treat HTML tags in the field (text only) */
57
+ tags?: 'strip' | 'stripall' | 'escape';
58
+ /** Allowed extension or array of allowed extensions. (filename only) */
59
+ extension?: string | string[];
60
+ /** Semantic type for the field */
61
+ semantic?: string;
62
+ /** Whether to allow time selection (date only, `true` by default) */
63
+ time?: boolean;
64
+ /** Whether the properties of the object will be in the same order as they are defined in the spec (collection only) */
65
+ sequence?: boolean;
66
+ /** Codepage for the field (buffer only) */
67
+ codepage?: string;
68
+ /** Whether the field is grouped (select only) */
69
+ grouped?: boolean;
70
+ /** Whether a mapped value in the select should be validated against the option values. If true, the value is treated as a dynamic and validation is disabled. The value is set to `true` automatically if select options are generated using RPC. */
71
+ dynamic?: boolean;
72
+ /** Mode for the field (select only) */
73
+ mode?: 'edit' | 'choose';
74
+ /** Sort order for the field (select only) */
75
+ sort?: string;
76
+ /** 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. */
77
+ rpc?: FormanSchemaRPCButton;
50
78
  } & Record<`x-${string}`, unknown>;
79
+ /**
80
+ * RPC button allows for dynamic value retrieval from an external source
81
+ */
82
+ type FormanSchemaRPCButton = {
83
+ /** RPC button label */
84
+ label: string;
85
+ /** RPC button URL */
86
+ url: string;
87
+ /** RPC button parameters */
88
+ parameters: FormanSchemaField[];
89
+ };
51
90
  /**
52
91
  * Valid Forman Schema values
53
92
  */
@@ -60,15 +99,26 @@ type FormanSchemaOption = {
60
99
  value: FormanSchemaValue;
61
100
  /** Option label */
62
101
  label?: string;
102
+ /** Whether the option is the default */
103
+ default?: boolean;
63
104
  /** Nested fields for this option */
64
105
  nested?: FormanSchemaNested;
65
106
  };
107
+ /**
108
+ * Option group for a select field
109
+ */
110
+ type FormanSchemaOptionGroup = {
111
+ /** Group label */
112
+ label: string;
113
+ /** Group options */
114
+ options: FormanSchemaOption[];
115
+ };
66
116
  /**
67
117
  * Extended options for a select field
68
118
  */
69
119
  type FormanSchemaExtendedOptions = {
70
120
  /** Store for the options */
71
- store: FormanSchemaOption[] | string;
121
+ store: FormanSchemaOption[] | FormanSchemaOptionGroup[] | string;
72
122
  /** Nested fields for every option */
73
123
  nested?: FormanSchemaNested;
74
124
  };
@@ -100,4 +150,4 @@ declare function toFormanSchema(field: JSONSchema7): FormanSchemaField;
100
150
  */
101
151
  declare function toJSONSchema(field: FormanSchemaField): JSONSchema7;
102
152
 
103
- export { type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaValue, toFormanSchema, toJSONSchema };
153
+ export { type FormanSchemaExtendedNested, type FormanSchemaExtendedOptions, type FormanSchemaField, type FormanSchemaFieldType, type FormanSchemaNested, type FormanSchemaOption, type FormanSchemaOptionGroup, type FormanSchemaRPCButton, type FormanSchemaValue, toFormanSchema, toJSONSchema };
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ function noEmpty(text) {
5
5
  function isObject(value) {
6
6
  return typeof value === "object" && value !== null && !Array.isArray(value);
7
7
  }
8
+ function isOptionGroup(value) {
9
+ return "options" in value && Array.isArray(value.options);
10
+ }
8
11
 
9
12
  // src/forman.ts
10
13
  var SchemaConversionError = class extends Error {
@@ -214,41 +217,52 @@ function handleArrayType(field, result, context) {
214
217
  return result;
215
218
  }
216
219
  function handleSelectType(field, result, context) {
217
- const options = isObject(field.options) ? field.options.store : field.options;
220
+ const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
218
221
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
219
222
  const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
220
- if (typeof options === "string") {
223
+ if (typeof optionsOrGroups === "string") {
221
224
  Object.defineProperty(result, "x-fetch", {
222
225
  configurable: true,
223
226
  enumerable: true,
224
227
  writable: true,
225
- value: appendQueryString(options, context.domain, context.tail)
226
- });
227
- } else if (options?.some((option) => option.label || option.nested)) {
228
- result.oneOf = (options || []).map((option) => {
229
- const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
230
- const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
231
- if (localNested) {
232
- context.addConditionalFields(
233
- field.name,
234
- option.value,
235
- typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
236
- { type: "collection", spec: localNested },
237
- {
238
- ...context,
239
- domain: localDomain,
240
- tail: [...context.tail, field.name]
241
- }
242
- )
243
- );
244
- }
245
- return {
246
- title: noEmpty(option.label),
247
- const: option.value
248
- };
228
+ value: appendQueryString(optionsOrGroups, context.domain, context.tail)
249
229
  });
250
230
  } else {
251
- result.enum = (options || []).map((option) => option.value);
231
+ const options = optionsOrGroups?.some(isOptionGroup) ? optionsOrGroups.flatMap(
232
+ (group) => group.options.map((option) => ({
233
+ ...option,
234
+ label: `${group.label}: ${option.label || option.value}`
235
+ }))
236
+ ) : optionsOrGroups || [];
237
+ if (options?.some((option) => {
238
+ if (isOptionGroup(option)) return option.options.some((option2) => option2.label || option2.nested);
239
+ return option.label || option.nested;
240
+ })) {
241
+ result.oneOf = (options || []).map((option) => {
242
+ const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
243
+ const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
244
+ if (localNested) {
245
+ context.addConditionalFields(
246
+ field.name,
247
+ option.value,
248
+ typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
249
+ { type: "collection", spec: localNested },
250
+ {
251
+ ...context,
252
+ domain: localDomain,
253
+ tail: [...context.tail, field.name]
254
+ }
255
+ )
256
+ );
257
+ }
258
+ return {
259
+ title: noEmpty(option.label),
260
+ const: option.value
261
+ };
262
+ });
263
+ } else {
264
+ result.enum = (options || []).map((option) => option.value);
265
+ }
252
266
  }
253
267
  if (nested && domain && domain !== context.domain) {
254
268
  if (typeof nested === "string") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",