@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 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
- function toJSONSchemaInternal(field, context = createDefaultContext()) {
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
- optionsOrGroups = await context.resolveRemote(optionsOrGroups, context);
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
- resolvedStore.push(...await context.resolveRemote(item, context));
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
- return toJSONSchemaInternal(field);
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
- function toJSONSchemaInternal(field, context = createDefaultContext()) {
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
- optionsOrGroups = await context.resolveRemote(optionsOrGroups, context);
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
- resolvedStore.push(...await context.resolveRemote(item, context));
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
- return toJSONSchemaInternal(field);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.8.3",
3
+ "version": "1.8.4",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",