@formspec/decorators 0.1.0-alpha.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.js ADDED
@@ -0,0 +1,694 @@
1
+ /**
2
+ * @formspec/decorators
3
+ *
4
+ * Decorators for FormSpec form definitions.
5
+ *
6
+ * These decorators work in two modes:
7
+ *
8
+ * 1. **Build-time (CLI generate)**: The FormSpec CLI reads decorators through
9
+ * static analysis and generates JSON Schema + UI Schema files directly.
10
+ *
11
+ * 2. **Runtime (with CLI codegen)**: Run `formspec codegen` to generate a type
12
+ * metadata file, then use `toFormSpec()` to generate specs at runtime.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { Label, Min, Max, EnumOptions, toFormSpec } from '@formspec/decorators';
17
+ *
18
+ * class UserForm {
19
+ * @Label("Full Name")
20
+ * name!: string;
21
+ *
22
+ * @Label("Age")
23
+ * @Min(18)
24
+ * @Max(120)
25
+ * age?: number;
26
+ *
27
+ * @Label("Country")
28
+ * @EnumOptions([
29
+ * { id: "us", label: "United States" },
30
+ * { id: "ca", label: "Canada" }
31
+ * ])
32
+ * country!: "us" | "ca";
33
+ * }
34
+ *
35
+ * // After running: formspec codegen ./forms.ts -o ./__formspec_types__.ts
36
+ * // Import the generated file and use toFormSpec:
37
+ * import './__formspec_types__';
38
+ * const spec = toFormSpec(UserForm);
39
+ * ```
40
+ */
41
+ // =============================================================================
42
+ // Runtime Metadata Storage
43
+ // =============================================================================
44
+ /**
45
+ * Storage for decorator metadata, keyed by constructor.
46
+ * This is a regular Map (not WeakMap) since class constructors
47
+ * live for the lifetime of the application.
48
+ */
49
+ const decoratorMetadata = new Map();
50
+ /**
51
+ * Gets or creates the metadata map for a class constructor.
52
+ */
53
+ function getFieldMetadata(
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ ctor, propertyKey) {
56
+ if (!decoratorMetadata.has(ctor)) {
57
+ decoratorMetadata.set(ctor, new Map());
58
+ }
59
+ const classMetadata = decoratorMetadata.get(ctor);
60
+ if (!classMetadata.has(propertyKey)) {
61
+ classMetadata.set(propertyKey, {});
62
+ }
63
+ return classMetadata.get(propertyKey);
64
+ }
65
+ // =============================================================================
66
+ // Field Metadata Decorators
67
+ // =============================================================================
68
+ /**
69
+ * Sets the display label for a field.
70
+ *
71
+ * @param text - The label text to display
72
+ * @example
73
+ * ```typescript
74
+ * @Label("Email Address")
75
+ * email!: string;
76
+ * ```
77
+ */
78
+ export function Label(text) {
79
+ return function (target, propertyKey) {
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ const ctor = target.constructor;
82
+ getFieldMetadata(ctor, propertyKey).label = text;
83
+ };
84
+ }
85
+ /**
86
+ * Sets placeholder text for input fields.
87
+ *
88
+ * @param text - The placeholder text
89
+ * @example
90
+ * ```typescript
91
+ * @Placeholder("Enter your email...")
92
+ * email!: string;
93
+ * ```
94
+ */
95
+ export function Placeholder(text) {
96
+ return function (target, propertyKey) {
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ const ctor = target.constructor;
99
+ getFieldMetadata(ctor, propertyKey).placeholder = text;
100
+ };
101
+ }
102
+ /**
103
+ * Sets a description or help text for a field.
104
+ *
105
+ * @param text - The description text
106
+ * @example
107
+ * ```typescript
108
+ * @Description("We'll never share your email with anyone")
109
+ * email!: string;
110
+ * ```
111
+ */
112
+ export function Description(text) {
113
+ return function (target, propertyKey) {
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ const ctor = target.constructor;
116
+ getFieldMetadata(ctor, propertyKey).description = text;
117
+ };
118
+ }
119
+ // =============================================================================
120
+ // Numeric Constraint Decorators
121
+ // =============================================================================
122
+ /**
123
+ * Sets the minimum allowed value for a numeric field.
124
+ *
125
+ * @param value - The minimum value
126
+ * @example
127
+ * ```typescript
128
+ * @Min(0)
129
+ * quantity!: number;
130
+ * ```
131
+ */
132
+ export function Min(value) {
133
+ return function (target, propertyKey) {
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
+ const ctor = target.constructor;
136
+ getFieldMetadata(ctor, propertyKey).min = value;
137
+ };
138
+ }
139
+ /**
140
+ * Sets the maximum allowed value for a numeric field.
141
+ *
142
+ * @param value - The maximum value
143
+ * @example
144
+ * ```typescript
145
+ * @Max(100)
146
+ * percentage!: number;
147
+ * ```
148
+ */
149
+ export function Max(value) {
150
+ return function (target, propertyKey) {
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ const ctor = target.constructor;
153
+ getFieldMetadata(ctor, propertyKey).max = value;
154
+ };
155
+ }
156
+ /**
157
+ * Sets the step increment for a numeric field.
158
+ *
159
+ * @param value - The step value
160
+ * @example
161
+ * ```typescript
162
+ * @Step(0.01)
163
+ * price!: number;
164
+ * ```
165
+ */
166
+ export function Step(value) {
167
+ return function (target, propertyKey) {
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
+ const ctor = target.constructor;
170
+ getFieldMetadata(ctor, propertyKey).step = value;
171
+ };
172
+ }
173
+ // =============================================================================
174
+ // String Constraint Decorators
175
+ // =============================================================================
176
+ /**
177
+ * Sets the minimum length for a string field.
178
+ *
179
+ * @param value - The minimum character count
180
+ * @example
181
+ * ```typescript
182
+ * @MinLength(1)
183
+ * name!: string;
184
+ * ```
185
+ */
186
+ export function MinLength(value) {
187
+ return function (target, propertyKey) {
188
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
+ const ctor = target.constructor;
190
+ getFieldMetadata(ctor, propertyKey).minLength = value;
191
+ };
192
+ }
193
+ /**
194
+ * Sets the maximum length for a string field.
195
+ *
196
+ * @param value - The maximum character count
197
+ * @example
198
+ * ```typescript
199
+ * @MaxLength(255)
200
+ * bio!: string;
201
+ * ```
202
+ */
203
+ export function MaxLength(value) {
204
+ return function (target, propertyKey) {
205
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
206
+ const ctor = target.constructor;
207
+ getFieldMetadata(ctor, propertyKey).maxLength = value;
208
+ };
209
+ }
210
+ /**
211
+ * Sets a regex pattern for string validation.
212
+ *
213
+ * @param regex - The regex pattern as a string
214
+ * @example
215
+ * ```typescript
216
+ * @Pattern("^[a-z]+$")
217
+ * username!: string;
218
+ * ```
219
+ */
220
+ export function Pattern(regex) {
221
+ return function (target, propertyKey) {
222
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
223
+ const ctor = target.constructor;
224
+ getFieldMetadata(ctor, propertyKey).pattern = regex;
225
+ };
226
+ }
227
+ // =============================================================================
228
+ // Array Constraint Decorators
229
+ // =============================================================================
230
+ /**
231
+ * Sets the minimum number of items for an array field.
232
+ *
233
+ * @param value - The minimum item count
234
+ * @example
235
+ * ```typescript
236
+ * @MinItems(1)
237
+ * tags!: string[];
238
+ * ```
239
+ */
240
+ export function MinItems(value) {
241
+ return function (target, propertyKey) {
242
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
243
+ const ctor = target.constructor;
244
+ getFieldMetadata(ctor, propertyKey).minItems = value;
245
+ };
246
+ }
247
+ /**
248
+ * Sets the maximum number of items for an array field.
249
+ *
250
+ * @param value - The maximum item count
251
+ * @example
252
+ * ```typescript
253
+ * @MaxItems(10)
254
+ * tags!: string[];
255
+ * ```
256
+ */
257
+ export function MaxItems(value) {
258
+ return function (target, propertyKey) {
259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
+ const ctor = target.constructor;
261
+ getFieldMetadata(ctor, propertyKey).maxItems = value;
262
+ };
263
+ }
264
+ // =============================================================================
265
+ // Enum and Options Decorators
266
+ // =============================================================================
267
+ /**
268
+ * Provides custom options for enum fields with labels.
269
+ *
270
+ * Use this to provide human-readable labels for enum values,
271
+ * or to customize the order and display of options.
272
+ *
273
+ * @param options - Array of option values or {id, label} objects
274
+ * @example
275
+ * ```typescript
276
+ * @EnumOptions([
277
+ * { id: "us", label: "United States" },
278
+ * { id: "ca", label: "Canada" },
279
+ * { id: "uk", label: "United Kingdom" }
280
+ * ])
281
+ * country!: "us" | "ca" | "uk";
282
+ * ```
283
+ */
284
+ export function EnumOptions(options) {
285
+ return function (target, propertyKey) {
286
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
287
+ const ctor = target.constructor;
288
+ getFieldMetadata(ctor, propertyKey).options = options;
289
+ };
290
+ }
291
+ // =============================================================================
292
+ // Conditional and Layout Decorators
293
+ // =============================================================================
294
+ /**
295
+ * Makes a field conditionally visible based on another field's value.
296
+ *
297
+ * @param condition - Object specifying the field and value to match
298
+ * @example
299
+ * ```typescript
300
+ * @ShowWhen({ field: "contactMethod", value: "email" })
301
+ * emailAddress!: string;
302
+ *
303
+ * @ShowWhen({ field: "contactMethod", value: "phone" })
304
+ * phoneNumber!: string;
305
+ * ```
306
+ */
307
+ export function ShowWhen(condition) {
308
+ return function (target, propertyKey) {
309
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
310
+ const ctor = target.constructor;
311
+ getFieldMetadata(ctor, propertyKey).showWhen = condition;
312
+ };
313
+ }
314
+ /**
315
+ * Groups fields together under a named section.
316
+ *
317
+ * @param name - The group name
318
+ * @example
319
+ * ```typescript
320
+ * @Group("Personal Information")
321
+ * @Label("First Name")
322
+ * firstName!: string;
323
+ *
324
+ * @Group("Personal Information")
325
+ * @Label("Last Name")
326
+ * lastName!: string;
327
+ *
328
+ * @Group("Contact Details")
329
+ * @Label("Email")
330
+ * email!: string;
331
+ * ```
332
+ */
333
+ export function Group(name) {
334
+ return function (target, propertyKey) {
335
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
336
+ const ctor = target.constructor;
337
+ getFieldMetadata(ctor, propertyKey).group = name;
338
+ };
339
+ }
340
+ // =============================================================================
341
+ // Runtime API
342
+ // =============================================================================
343
+ /**
344
+ * The name of the static property containing type metadata.
345
+ * This is set by the `formspec codegen` command at build time.
346
+ */
347
+ const FORMSPEC_TYPES_KEY = "__formspec_types__";
348
+ /**
349
+ * Maps type metadata type to FormSpec field type.
350
+ */
351
+ function mapTypeToFieldType(type) {
352
+ switch (type) {
353
+ case "string":
354
+ return "text";
355
+ case "number":
356
+ return "number";
357
+ case "boolean":
358
+ return "boolean";
359
+ case "enum":
360
+ return "enum";
361
+ case "array":
362
+ return "array";
363
+ case "object":
364
+ return "object";
365
+ default:
366
+ return "text";
367
+ }
368
+ }
369
+ /**
370
+ * Creates a FormSpec field from type and decorator metadata.
371
+ */
372
+ function createField(fieldName, typeInfo, decoratorInfo) {
373
+ const field = {
374
+ _field: mapTypeToFieldType(typeInfo.type),
375
+ id: fieldName,
376
+ };
377
+ // Required if not optional and not nullable
378
+ if (!typeInfo.optional && !typeInfo.nullable) {
379
+ field.required = true;
380
+ }
381
+ // Apply decorator metadata
382
+ if (decoratorInfo.label)
383
+ field.label = decoratorInfo.label;
384
+ if (decoratorInfo.placeholder)
385
+ field.placeholder = decoratorInfo.placeholder;
386
+ if (decoratorInfo.description)
387
+ field.description = decoratorInfo.description;
388
+ if (decoratorInfo.min !== undefined)
389
+ field.min = decoratorInfo.min;
390
+ if (decoratorInfo.max !== undefined)
391
+ field.max = decoratorInfo.max;
392
+ if (decoratorInfo.step !== undefined)
393
+ field.step = decoratorInfo.step;
394
+ if (decoratorInfo.minLength !== undefined)
395
+ field.minLength = decoratorInfo.minLength;
396
+ if (decoratorInfo.maxLength !== undefined)
397
+ field.maxLength = decoratorInfo.maxLength;
398
+ if (decoratorInfo.minItems !== undefined)
399
+ field.minItems = decoratorInfo.minItems;
400
+ if (decoratorInfo.maxItems !== undefined)
401
+ field.maxItems = decoratorInfo.maxItems;
402
+ if (decoratorInfo.pattern)
403
+ field.pattern = decoratorInfo.pattern;
404
+ if (decoratorInfo.showWhen)
405
+ field.showWhen = decoratorInfo.showWhen;
406
+ if (decoratorInfo.group)
407
+ field.group = decoratorInfo.group;
408
+ // Options: prefer decorator options, fall back to type values
409
+ if (decoratorInfo.options) {
410
+ field.options = decoratorInfo.options;
411
+ }
412
+ else if (typeInfo.values) {
413
+ // Convert simple values to strings
414
+ field.options = typeInfo.values.map((v) => String(v));
415
+ }
416
+ // Handle nested object properties
417
+ if (typeInfo.type === "object" && typeInfo.properties) {
418
+ field.fields = Object.entries(typeInfo.properties).map(([propName, propType]) => createField(propName, propType, {}));
419
+ }
420
+ return field;
421
+ }
422
+ /**
423
+ * Generates a UI Schema from a decorated class at runtime.
424
+ *
425
+ * **Requirements:**
426
+ * - Run `formspec codegen` to generate type metadata for the class
427
+ * - Decorators must be applied to store field metadata
428
+ *
429
+ * @param ctor - The class constructor
430
+ * @returns FormSpec output with elements array
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * import { Label, toFormSpec } from '@formspec/decorators';
435
+ *
436
+ * class MyForm {
437
+ * @Label("Name")
438
+ * name!: string;
439
+ *
440
+ * @Label("Country")
441
+ * country!: "us" | "ca";
442
+ * }
443
+ *
444
+ * const spec = toFormSpec(MyForm);
445
+ * // { elements: [{ _field: "text", id: "name", label: "Name", required: true }, ...] }
446
+ * ```
447
+ */
448
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
449
+ export function toFormSpec(ctor) {
450
+ // Get type metadata from codegen (if available)
451
+ const typeMetadata =
452
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
453
+ ctor[FORMSPEC_TYPES_KEY] ?? {};
454
+ // Get decorator metadata
455
+ const classDecoratorMeta = decoratorMetadata.get(ctor) ?? new Map();
456
+ // Combine to create elements
457
+ const elements = [];
458
+ // Process all fields from type metadata
459
+ for (const [fieldName, typeInfo] of Object.entries(typeMetadata)) {
460
+ const decoratorInfo = classDecoratorMeta.get(fieldName) ?? {};
461
+ elements.push(createField(fieldName, typeInfo, decoratorInfo));
462
+ }
463
+ // If no type metadata, fall back to decorator metadata only
464
+ // (limited functionality - no type information)
465
+ if (Object.keys(typeMetadata).length === 0) {
466
+ for (const [fieldName, decoratorInfo] of classDecoratorMeta.entries()) {
467
+ elements.push(createField(fieldName, { type: "unknown" }, decoratorInfo));
468
+ }
469
+ }
470
+ return { elements };
471
+ }
472
+ /**
473
+ * Gets the raw decorator metadata for a class.
474
+ *
475
+ * Useful for debugging or custom processing.
476
+ *
477
+ * @param ctor - The class constructor
478
+ * @returns Map of field names to decorator metadata
479
+ */
480
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
481
+ export function getDecoratorMetadata(ctor) {
482
+ return decoratorMetadata.get(ctor) ?? new Map();
483
+ }
484
+ /**
485
+ * Gets the raw type metadata for a class.
486
+ *
487
+ * Useful for debugging or custom processing.
488
+ *
489
+ * @param ctor - The class constructor
490
+ * @returns Record of field names to type metadata, or empty object if not transformed
491
+ */
492
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
493
+ export function getTypeMetadata(ctor) {
494
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
495
+ return ctor[FORMSPEC_TYPES_KEY] ?? {};
496
+ }
497
+ // =============================================================================
498
+ // Schema Generation
499
+ // =============================================================================
500
+ /**
501
+ * Maps FormSpecField type to JSON Schema type.
502
+ */
503
+ function fieldTypeToJsonSchemaType(fieldType) {
504
+ switch (fieldType) {
505
+ case "text":
506
+ return "string";
507
+ case "number":
508
+ return "number";
509
+ case "boolean":
510
+ return "boolean";
511
+ case "enum":
512
+ return "string";
513
+ case "array":
514
+ return "array";
515
+ case "object":
516
+ return "object";
517
+ default:
518
+ return "string";
519
+ }
520
+ }
521
+ /**
522
+ * Converts a FormSpecField to JSON Schema.
523
+ */
524
+ function fieldToJsonSchema(field) {
525
+ const schema = {};
526
+ if (field.label) {
527
+ schema.title = field.label;
528
+ }
529
+ const jsonType = fieldTypeToJsonSchemaType(field._field);
530
+ schema.type = jsonType;
531
+ // Number constraints
532
+ if (field.min !== undefined)
533
+ schema.minimum = field.min;
534
+ if (field.max !== undefined)
535
+ schema.maximum = field.max;
536
+ // String constraints
537
+ if (field.minLength !== undefined)
538
+ schema.minLength = field.minLength;
539
+ if (field.maxLength !== undefined)
540
+ schema.maxLength = field.maxLength;
541
+ if (field.pattern)
542
+ schema.pattern = field.pattern;
543
+ // Enum options
544
+ if (field._field === "enum" && field.options) {
545
+ const hasLabels = field.options.some((opt) => typeof opt === "object" && opt !== null && "id" in opt);
546
+ if (hasLabels) {
547
+ schema.oneOf = field.options.map((opt) => {
548
+ if (typeof opt === "object" && "id" in opt) {
549
+ return { const: opt.id, title: opt.label };
550
+ }
551
+ return { const: opt, title: String(opt) };
552
+ });
553
+ }
554
+ else {
555
+ schema.enum = field.options.map((opt) => typeof opt === "string" ? opt : opt.id);
556
+ }
557
+ }
558
+ // Array items
559
+ if (field._field === "array" && field.fields) {
560
+ const itemProperties = {};
561
+ const itemRequired = [];
562
+ for (const nested of field.fields) {
563
+ itemProperties[nested.id] = fieldToJsonSchema(nested);
564
+ if (nested.required)
565
+ itemRequired.push(nested.id);
566
+ }
567
+ schema.items = {
568
+ type: "object",
569
+ properties: itemProperties,
570
+ ...(itemRequired.length > 0 && { required: itemRequired }),
571
+ };
572
+ if (field.minItems !== undefined)
573
+ schema.minItems = field.minItems;
574
+ if (field.maxItems !== undefined)
575
+ schema.maxItems = field.maxItems;
576
+ }
577
+ // Object properties
578
+ if (field._field === "object" && field.fields) {
579
+ const objProperties = {};
580
+ const objRequired = [];
581
+ for (const nested of field.fields) {
582
+ objProperties[nested.id] = fieldToJsonSchema(nested);
583
+ if (nested.required)
584
+ objRequired.push(nested.id);
585
+ }
586
+ schema.properties = objProperties;
587
+ if (objRequired.length > 0)
588
+ schema.required = objRequired;
589
+ }
590
+ return schema;
591
+ }
592
+ /**
593
+ * Generates JSON Schema from FormSpecField elements.
594
+ */
595
+ function generateJsonSchemaFromElements(elements) {
596
+ const properties = {};
597
+ const required = [];
598
+ for (const element of elements) {
599
+ properties[element.id] = fieldToJsonSchema(element);
600
+ if (element.required) {
601
+ required.push(element.id);
602
+ }
603
+ }
604
+ return {
605
+ $schema: "https://json-schema.org/draft-07/schema#",
606
+ type: "object",
607
+ properties,
608
+ ...(required.length > 0 && { required }),
609
+ };
610
+ }
611
+ /**
612
+ * Converts a field name to a JSON Pointer scope.
613
+ */
614
+ function fieldToScope(fieldName) {
615
+ return `#/properties/${fieldName}`;
616
+ }
617
+ /**
618
+ * Converts FormSpecField elements to UI Schema elements.
619
+ */
620
+ function elementsToUiSchema(elements) {
621
+ const result = [];
622
+ for (const element of elements) {
623
+ const control = {
624
+ type: "Control",
625
+ scope: fieldToScope(element.id),
626
+ };
627
+ if (element.label) {
628
+ control.label = element.label;
629
+ }
630
+ // Add conditional visibility rule
631
+ if (element.showWhen) {
632
+ control.rule = {
633
+ effect: "SHOW",
634
+ condition: {
635
+ scope: fieldToScope(element.showWhen.field),
636
+ schema: { const: element.showWhen.value },
637
+ },
638
+ };
639
+ }
640
+ result.push(control);
641
+ }
642
+ return result;
643
+ }
644
+ /**
645
+ * Generates UI Schema from FormSpecField elements.
646
+ */
647
+ function generateUiSchemaFromElements(elements) {
648
+ return {
649
+ type: "VerticalLayout",
650
+ elements: elementsToUiSchema(elements),
651
+ };
652
+ }
653
+ /**
654
+ * Builds both JSON Schema and UI Schema from a decorated class at runtime.
655
+ *
656
+ * This function provides the same API as `buildFormSchemas` from `@formspec/build`,
657
+ * allowing consistent usage across both Chain DSL and Decorator DSL.
658
+ *
659
+ * **Requirements:**
660
+ * - Run `formspec codegen` to generate type metadata for the class
661
+ * - Decorators must be applied to store field metadata
662
+ *
663
+ * @param ctor - The class constructor
664
+ * @returns Object containing both jsonSchema and uiSchema
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * import { Label, buildFormSchemas } from '@formspec/decorators';
669
+ *
670
+ * class MyForm {
671
+ * @Label("Name")
672
+ * name!: string;
673
+ *
674
+ * @Label("Country")
675
+ * country!: "us" | "ca";
676
+ * }
677
+ *
678
+ * // After running: formspec codegen ./forms.ts -o ./__formspec_types__.ts
679
+ * // And importing: import './__formspec_types__';
680
+ *
681
+ * const { jsonSchema, uiSchema } = buildFormSchemas(MyForm);
682
+ * // jsonSchema: { $schema: "...", type: "object", properties: {...}, required: [...] }
683
+ * // uiSchema: { type: "VerticalLayout", elements: [...] }
684
+ * ```
685
+ */
686
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
687
+ export function buildFormSchemas(ctor) {
688
+ const { elements } = toFormSpec(ctor);
689
+ return {
690
+ jsonSchema: generateJsonSchemaFromElements(elements),
691
+ uiSchema: generateUiSchemaFromElements(elements),
692
+ };
693
+ }
694
+ //# sourceMappingURL=index.js.map