@formspec/cli 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.
Files changed (66) hide show
  1. package/README.md +314 -0
  2. package/dist/__tests__/analyzer.test.d.ts +5 -0
  3. package/dist/__tests__/analyzer.test.d.ts.map +1 -0
  4. package/dist/__tests__/analyzer.test.js +141 -0
  5. package/dist/__tests__/analyzer.test.js.map +1 -0
  6. package/dist/__tests__/codegen.test.d.ts +5 -0
  7. package/dist/__tests__/codegen.test.d.ts.map +1 -0
  8. package/dist/__tests__/codegen.test.js +482 -0
  9. package/dist/__tests__/codegen.test.js.map +1 -0
  10. package/dist/__tests__/edge-cases.test.d.ts +14 -0
  11. package/dist/__tests__/edge-cases.test.d.ts.map +1 -0
  12. package/dist/__tests__/edge-cases.test.js +432 -0
  13. package/dist/__tests__/edge-cases.test.js.map +1 -0
  14. package/dist/__tests__/fixtures/edge-cases.d.ts +110 -0
  15. package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -0
  16. package/dist/__tests__/fixtures/edge-cases.js +135 -0
  17. package/dist/__tests__/fixtures/edge-cases.js.map +1 -0
  18. package/dist/__tests__/fixtures/sample-forms.d.ts +55 -0
  19. package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -0
  20. package/dist/__tests__/fixtures/sample-forms.js +78 -0
  21. package/dist/__tests__/fixtures/sample-forms.js.map +1 -0
  22. package/dist/__tests__/integration.test.d.ts +5 -0
  23. package/dist/__tests__/integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/integration.test.js +159 -0
  25. package/dist/__tests__/integration.test.js.map +1 -0
  26. package/dist/analyzer/class-analyzer.d.ts +75 -0
  27. package/dist/analyzer/class-analyzer.d.ts.map +1 -0
  28. package/dist/analyzer/class-analyzer.js +151 -0
  29. package/dist/analyzer/class-analyzer.js.map +1 -0
  30. package/dist/analyzer/decorator-extractor.d.ts +87 -0
  31. package/dist/analyzer/decorator-extractor.d.ts.map +1 -0
  32. package/dist/analyzer/decorator-extractor.js +193 -0
  33. package/dist/analyzer/decorator-extractor.js.map +1 -0
  34. package/dist/analyzer/program.d.ts +37 -0
  35. package/dist/analyzer/program.d.ts.map +1 -0
  36. package/dist/analyzer/program.js +89 -0
  37. package/dist/analyzer/program.js.map +1 -0
  38. package/dist/analyzer/type-converter.d.ts +97 -0
  39. package/dist/analyzer/type-converter.d.ts.map +1 -0
  40. package/dist/analyzer/type-converter.js +353 -0
  41. package/dist/analyzer/type-converter.js.map +1 -0
  42. package/dist/codegen/index.d.ts +74 -0
  43. package/dist/codegen/index.d.ts.map +1 -0
  44. package/dist/codegen/index.js +501 -0
  45. package/dist/codegen/index.js.map +1 -0
  46. package/dist/generators/class-schema.d.ts +43 -0
  47. package/dist/generators/class-schema.d.ts.map +1 -0
  48. package/dist/generators/class-schema.js +61 -0
  49. package/dist/generators/class-schema.js.map +1 -0
  50. package/dist/generators/method-schema.d.ts +57 -0
  51. package/dist/generators/method-schema.d.ts.map +1 -0
  52. package/dist/generators/method-schema.js +108 -0
  53. package/dist/generators/method-schema.js.map +1 -0
  54. package/dist/index.d.ts +19 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +282 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/output/writer.d.ts +82 -0
  59. package/dist/output/writer.d.ts.map +1 -0
  60. package/dist/output/writer.js +152 -0
  61. package/dist/output/writer.js.map +1 -0
  62. package/dist/runtime/formspec-loader.d.ts +80 -0
  63. package/dist/runtime/formspec-loader.d.ts.map +1 -0
  64. package/dist/runtime/formspec-loader.js +154 -0
  65. package/dist/runtime/formspec-loader.js.map +1 -0
  66. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # @formspec/cli
2
+
3
+ CLI tool for generating JSON Schema and JSON Forms UI Schema from TypeScript source files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @formspec/cli
9
+ # or
10
+ pnpm add @formspec/cli
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Generate schemas from a TypeScript class
17
+ formspec generate ./src/forms.ts MyClass -o ./generated
18
+
19
+ # Generate schemas from all FormSpec exports in a file (chain DSL)
20
+ formspec generate ./src/forms.ts -o ./generated
21
+ ```
22
+
23
+ ## When to Use
24
+
25
+ ### Decorators + CLI vs Chain DSL
26
+
27
+ FormSpec offers two approaches for defining forms:
28
+
29
+ | Approach | Best For | Schema Generation |
30
+ |----------|----------|-------------------|
31
+ | **Decorators + CLI** | Class-based data models, DTOs | Build-time (static analysis) or runtime (with codegen) |
32
+ | **Chain DSL** | Dynamic forms, runtime construction | Build-time or runtime (no code generation needed) |
33
+
34
+ **Use decorators + CLI when:**
35
+ - Your forms map directly to TypeScript classes or DTOs
36
+ - You prefer class-based modeling with validation constraints
37
+ - You want type information inferred from TypeScript (no redundant type hints)
38
+ - You need to generate schemas at build time for CI/CD pipelines
39
+
40
+ **Use Chain DSL (`@formspec/dsl`) when:**
41
+ - Forms are constructed dynamically at runtime
42
+ - Form definitions come from a database or API
43
+ - Forms don't correspond to specific TypeScript types
44
+ - You want runtime schema generation without a build step
45
+
46
+ Both approaches produce identical JSON Schema and UI Schema output.
47
+
48
+ ## Features
49
+
50
+ ### Static Type Analysis
51
+
52
+ The CLI uses the TypeScript Compiler API to statically analyze your source files. It automatically infers:
53
+
54
+ | TypeScript Type | JSON Schema | FormSpec Field |
55
+ |-----------------|-------------|----------------|
56
+ | `string` | `{ "type": "string" }` | `{ "_field": "text" }` |
57
+ | `number` | `{ "type": "number" }` | `{ "_field": "number" }` |
58
+ | `boolean` | `{ "type": "boolean" }` | `{ "_field": "boolean" }` |
59
+ | `"a" \| "b" \| "c"` | `{ "enum": ["a", "b", "c"] }` | `{ "_field": "enum", "options": [...] }` |
60
+ | `string[]` | `{ "type": "array", "items": {...} }` | `{ "_field": "array" }` |
61
+ | `{ a: string }` | `{ "type": "object", "properties": {...} }` | `{ "_field": "object" }` |
62
+ | `field?: T` | not in `required` array | `{ "required": false }` |
63
+
64
+ ### Decorator Recognition
65
+
66
+ The CLI recognizes decorators by name through static analysis. You don't need a specific decorator library - any decorator with a recognized name will work.
67
+
68
+ #### Supported Decorators
69
+
70
+ | Decorator | Purpose | Example |
71
+ |-----------|---------|---------|
72
+ | `@Label(text)` | Set field label | `@Label("Full Name")` |
73
+ | `@Placeholder(text)` | Set placeholder text | `@Placeholder("Enter name...")` |
74
+ | `@Description(text)` | Set field description | `@Description("Your legal name")` |
75
+ | `@Min(n)` | Set minimum value | `@Min(0)` |
76
+ | `@Max(n)` | Set maximum value | `@Max(100)` |
77
+ | `@MinLength(n)` | Set minimum string length | `@MinLength(1)` |
78
+ | `@MaxLength(n)` | Set maximum string length | `@MaxLength(255)` |
79
+ | `@MinItems(n)` | Set minimum array items | `@MinItems(1)` |
80
+ | `@MaxItems(n)` | Set maximum array items | `@MaxItems(10)` |
81
+ | `@Pattern(regex)` | Set validation pattern | `@Pattern("^[a-z]+$")` |
82
+ | `@EnumOptions(opts)` | Override enum options | `@EnumOptions([{id: "us", label: "United States"}])` |
83
+ | `@ShowWhen(cond)` | Conditional visibility | `@ShowWhen({ field: "type", value: "other" })` |
84
+ | `@Group(name)` | Group fields together | `@Group("Contact Info")` |
85
+
86
+ #### Using Decorators
87
+
88
+ Install the `@formspec/decorators` package:
89
+
90
+ ```bash
91
+ npm install @formspec/decorators
92
+ ```
93
+
94
+ Then use the decorators in your class:
95
+
96
+ ```typescript
97
+ // user-registration.ts
98
+ import { Label, Min, Max, EnumOptions } from "@formspec/decorators";
99
+
100
+ class UserRegistration {
101
+ @Label("Full Name")
102
+ name!: string;
103
+
104
+ @Label("Email Address")
105
+ email!: string;
106
+
107
+ @Label("Age")
108
+ @Min(18)
109
+ @Max(120)
110
+ age?: number;
111
+
112
+ @Label("Country")
113
+ @EnumOptions([
114
+ { id: "us", label: "United States" },
115
+ { id: "ca", label: "Canada" },
116
+ { id: "uk", label: "United Kingdom" },
117
+ ])
118
+ country!: "us" | "ca" | "uk";
119
+ }
120
+ ```
121
+
122
+ Run the CLI:
123
+
124
+ ```bash
125
+ formspec generate ./src/user-registration.ts UserRegistration -o ./generated
126
+ ```
127
+
128
+ > **Note**: The decorators are no-ops at runtime with zero overhead. The CLI reads them through static analysis of your TypeScript source code.
129
+
130
+ ### FormSpec Chain DSL Support
131
+
132
+ The CLI also supports the FormSpec chain DSL. Export your FormSpec definitions and the CLI will generate schemas for them:
133
+
134
+ ```typescript
135
+ // forms.ts
136
+ import { formspec, field } from "@formspec/dsl";
137
+
138
+ export const ContactForm = formspec(
139
+ field.text("name", { label: "Name", required: true }),
140
+ field.text("email", { label: "Email", required: true }),
141
+ field.text("message", { label: "Message" })
142
+ );
143
+ ```
144
+
145
+ ```bash
146
+ formspec generate ./src/forms.ts -o ./generated
147
+ ```
148
+
149
+ ### Method Parameter Analysis
150
+
151
+ The CLI can detect `InferSchema<typeof X>` or `InferFormSchema<typeof X>` patterns in method parameters and use the referenced FormSpec to generate parameter schemas:
152
+
153
+ ```typescript
154
+ import { formspec, field, type InferFormSchema } from "@formspec/dsl";
155
+
156
+ export const ActivateParams = formspec(
157
+ field.number("amount", { label: "Amount", min: 100 }),
158
+ field.number("installments", { min: 2, max: 12 })
159
+ );
160
+
161
+ class PaymentPlan {
162
+ status!: "active" | "paused" | "canceled";
163
+
164
+ activate(params: InferFormSchema<typeof ActivateParams>): boolean {
165
+ // ...
166
+ }
167
+ }
168
+ ```
169
+
170
+ The CLI will generate schemas for both the class fields and the method parameters.
171
+
172
+ ## Output Structure
173
+
174
+ ```
175
+ generated/
176
+ ├── ClassName/
177
+ │ ├── schema.json # JSON Schema for class fields
178
+ │ ├── ux_spec.json # JSON Forms UI Schema for form rendering
179
+ │ ├── instance_methods/
180
+ │ │ └── methodName/
181
+ │ │ ├── params.schema.json
182
+ │ │ ├── params.ux_spec.json # (if FormSpec-based params)
183
+ │ │ └── return_type.schema.json
184
+ │ └── static_methods/
185
+ │ └── methodName/
186
+ │ └── ...
187
+ └── formspecs/
188
+ └── ExportName/
189
+ ├── schema.json
190
+ └── ux_spec.json
191
+ ```
192
+
193
+ ## Example Output
194
+
195
+ Given this TypeScript class:
196
+
197
+ ```typescript
198
+ import { Label, Min, Max, EnumOptions } from "@formspec/decorators";
199
+
200
+ class ContactForm {
201
+ @Label("Full Name")
202
+ name!: string;
203
+
204
+ @Label("Email Address")
205
+ email!: string;
206
+
207
+ @Label("Age")
208
+ @Min(18)
209
+ @Max(120)
210
+ age?: number;
211
+
212
+ @Label("Country")
213
+ @EnumOptions([
214
+ { id: "us", label: "United States" },
215
+ { id: "ca", label: "Canada" }
216
+ ])
217
+ country!: "us" | "ca";
218
+ }
219
+ ```
220
+
221
+ Running `formspec generate ./contact-form.ts ContactForm` produces:
222
+
223
+ **schema.json:**
224
+ ```json
225
+ {
226
+ "type": "object",
227
+ "properties": {
228
+ "name": { "type": "string", "title": "Full Name" },
229
+ "email": { "type": "string", "title": "Email Address" },
230
+ "age": { "type": "number", "title": "Age", "minimum": 18, "maximum": 120 },
231
+ "country": { "enum": ["us", "ca"], "title": "Country" }
232
+ },
233
+ "required": ["name", "email", "country"]
234
+ }
235
+ ```
236
+
237
+ **ux_spec.json:**
238
+ ```json
239
+ {
240
+ "elements": [
241
+ { "_field": "text", "id": "name", "label": "Full Name", "required": true },
242
+ { "_field": "text", "id": "email", "label": "Email Address", "required": true },
243
+ { "_field": "number", "id": "age", "label": "Age", "min": 18, "max": 120 },
244
+ {
245
+ "_field": "enum",
246
+ "id": "country",
247
+ "label": "Country",
248
+ "required": true,
249
+ "options": [
250
+ { "id": "us", "label": "United States" },
251
+ { "id": "ca", "label": "Canada" }
252
+ ]
253
+ }
254
+ ]
255
+ }
256
+ ```
257
+
258
+ ## CLI Reference
259
+
260
+ ```
261
+ formspec generate <file> [className] [options]
262
+
263
+ Arguments:
264
+ <file> Path to TypeScript source file
265
+ [className] Optional class name (if omitted, generates from FormSpec exports only)
266
+
267
+ Options:
268
+ -o, --output <dir> Output directory (default: ./generated)
269
+ -c, --compiled <path> Path to compiled JS file (auto-detected if omitted)
270
+ -h, --help Show help message
271
+
272
+ Aliases:
273
+ formspec analyze Same as 'generate' (backwards compatibility)
274
+ ```
275
+
276
+ ## TypeScript Configuration
277
+
278
+ For decorator support, ensure your `tsconfig.json` includes:
279
+
280
+ ```json
281
+ {
282
+ "compilerOptions": {
283
+ "experimentalDecorators": true
284
+ }
285
+ }
286
+ ```
287
+
288
+ > **Note**: The `emitDecoratorMetadata` flag is not required. The CLI performs static analysis and reads decorators directly from the AST without using reflection.
289
+
290
+ ## Troubleshooting
291
+
292
+ ### "Could not load compiled module" Warning
293
+
294
+ This warning appears when the CLI cannot find a compiled JavaScript version of your TypeScript file. This is expected if you haven't compiled your TypeScript yet.
295
+
296
+ **The CLI will still work** - it uses static TypeScript analysis which doesn't require compiled output. The warning only affects method parameters that use `InferSchema<typeof X>`, which require the FormSpec to be loaded at runtime.
297
+
298
+ To suppress this warning, compile your TypeScript first:
299
+
300
+ ```bash
301
+ tsc
302
+ formspec generate ./src/forms.ts MyClass -o ./generated
303
+ ```
304
+
305
+ ### Decorators Not Being Recognized
306
+
307
+ Ensure:
308
+ 1. Decorator names match exactly (case-sensitive): `@Label`, not `@label`
309
+ 2. Decorators are function calls: `@Label("text")`, not `@Label`
310
+ 3. The decorator is imported (even if it's a stub)
311
+
312
+ ## License
313
+
314
+ UNLICENSED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unit tests for the analyzer module.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=analyzer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Unit tests for the analyzer module.
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import * as path from "node:path";
6
+ import { createProgramContext, findClassByName } from "../analyzer/program.js";
7
+ import { analyzeClass } from "../analyzer/class-analyzer.js";
8
+ import { convertType } from "../analyzer/type-converter.js";
9
+ const fixturesDir = path.join(__dirname, "fixtures");
10
+ const sampleFormsPath = path.join(fixturesDir, "sample-forms.ts");
11
+ describe("program", () => {
12
+ it("creates program context from TypeScript file", () => {
13
+ const ctx = createProgramContext(sampleFormsPath);
14
+ expect(ctx.program).toBeDefined();
15
+ expect(ctx.checker).toBeDefined();
16
+ expect(ctx.sourceFile).toBeDefined();
17
+ expect(ctx.sourceFile.fileName).toContain("sample-forms.ts");
18
+ });
19
+ it("throws for non-existent file", () => {
20
+ expect(() => createProgramContext("/non/existent/file.ts")).toThrow();
21
+ });
22
+ });
23
+ describe("findClassByName", () => {
24
+ it("finds InstallmentPlan class", () => {
25
+ const ctx = createProgramContext(sampleFormsPath);
26
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
27
+ expect(classDecl).not.toBeNull();
28
+ expect(classDecl?.name?.text).toBe("InstallmentPlan");
29
+ });
30
+ it("finds SimpleProduct class", () => {
31
+ const ctx = createProgramContext(sampleFormsPath);
32
+ const classDecl = findClassByName(ctx.sourceFile, "SimpleProduct");
33
+ expect(classDecl).not.toBeNull();
34
+ expect(classDecl?.name?.text).toBe("SimpleProduct");
35
+ });
36
+ it("returns null for non-existent class", () => {
37
+ const ctx = createProgramContext(sampleFormsPath);
38
+ const classDecl = findClassByName(ctx.sourceFile, "NonExistentClass");
39
+ expect(classDecl).toBeNull();
40
+ });
41
+ });
42
+ describe("analyzeClass", () => {
43
+ it("analyzes InstallmentPlan fields", () => {
44
+ const ctx = createProgramContext(sampleFormsPath);
45
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
46
+ const analysis = analyzeClass(classDecl, ctx.checker);
47
+ expect(analysis.name).toBe("InstallmentPlan");
48
+ expect(analysis.fields).toHaveLength(4);
49
+ const fieldNames = analysis.fields.map((f) => f.name);
50
+ expect(fieldNames).toContain("status");
51
+ expect(fieldNames).toContain("amount");
52
+ expect(fieldNames).toContain("customerEmail");
53
+ expect(fieldNames).toContain("installments");
54
+ });
55
+ it("detects optional fields", () => {
56
+ const ctx = createProgramContext(sampleFormsPath);
57
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
58
+ const analysis = analyzeClass(classDecl, ctx.checker);
59
+ const emailField = analysis.fields.find((f) => f.name === "customerEmail");
60
+ const amountField = analysis.fields.find((f) => f.name === "amount");
61
+ expect(emailField?.optional).toBe(true);
62
+ expect(amountField?.optional).toBe(false);
63
+ });
64
+ it("analyzes instance methods", () => {
65
+ const ctx = createProgramContext(sampleFormsPath);
66
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
67
+ const analysis = analyzeClass(classDecl, ctx.checker);
68
+ expect(analysis.instanceMethods).toHaveLength(2);
69
+ const methodNames = analysis.instanceMethods.map((m) => m.name);
70
+ expect(methodNames).toContain("activate");
71
+ expect(methodNames).toContain("cancelPlan");
72
+ });
73
+ it("analyzes static methods", () => {
74
+ const ctx = createProgramContext(sampleFormsPath);
75
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
76
+ const analysis = analyzeClass(classDecl, ctx.checker);
77
+ expect(analysis.staticMethods).toHaveLength(1);
78
+ expect(analysis.staticMethods[0]?.name).toBe("createStandard");
79
+ });
80
+ it("detects InferSchema references in method parameters", () => {
81
+ const ctx = createProgramContext(sampleFormsPath);
82
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
83
+ const analysis = analyzeClass(classDecl, ctx.checker);
84
+ const activateMethod = analysis.instanceMethods.find((m) => m.name === "activate");
85
+ expect(activateMethod?.parameters).toHaveLength(1);
86
+ expect(activateMethod?.parameters[0]?.formSpecExportName).toBe("ActivateParams");
87
+ const cancelMethod = analysis.instanceMethods.find((m) => m.name === "cancelPlan");
88
+ expect(cancelMethod?.parameters[0]?.formSpecExportName).toBe("CancelParams");
89
+ });
90
+ it("analyzes SimpleProduct without FormSpec references", () => {
91
+ const ctx = createProgramContext(sampleFormsPath);
92
+ const classDecl = findClassByName(ctx.sourceFile, "SimpleProduct");
93
+ const analysis = analyzeClass(classDecl, ctx.checker);
94
+ expect(analysis.name).toBe("SimpleProduct");
95
+ expect(analysis.fields).toHaveLength(4);
96
+ expect(analysis.instanceMethods).toHaveLength(1);
97
+ expect(analysis.staticMethods).toHaveLength(0);
98
+ // The update method shouldn't have a FormSpec reference
99
+ const updateMethod = analysis.instanceMethods[0];
100
+ expect(updateMethod?.parameters[0]?.formSpecExportName).toBeNull();
101
+ });
102
+ });
103
+ describe("convertType", () => {
104
+ it("converts string type", () => {
105
+ const ctx = createProgramContext(sampleFormsPath);
106
+ const classDecl = findClassByName(ctx.sourceFile, "SimpleProduct");
107
+ const analysis = analyzeClass(classDecl, ctx.checker);
108
+ const nameField = analysis.fields.find((f) => f.name === "name");
109
+ const result = convertType(nameField.type, ctx.checker);
110
+ expect(result.jsonSchema.type).toBe("string");
111
+ expect(result.formSpecFieldType).toBe("text");
112
+ });
113
+ it("converts number type", () => {
114
+ const ctx = createProgramContext(sampleFormsPath);
115
+ const classDecl = findClassByName(ctx.sourceFile, "SimpleProduct");
116
+ const analysis = analyzeClass(classDecl, ctx.checker);
117
+ const priceField = analysis.fields.find((f) => f.name === "price");
118
+ const result = convertType(priceField.type, ctx.checker);
119
+ // price is optional so it's number | undefined
120
+ expect(result.formSpecFieldType).toBe("number");
121
+ });
122
+ it("converts boolean type", () => {
123
+ const ctx = createProgramContext(sampleFormsPath);
124
+ const classDecl = findClassByName(ctx.sourceFile, "SimpleProduct");
125
+ const analysis = analyzeClass(classDecl, ctx.checker);
126
+ const activeField = analysis.fields.find((f) => f.name === "active");
127
+ const result = convertType(activeField.type, ctx.checker);
128
+ expect(result.jsonSchema.type).toBe("boolean");
129
+ expect(result.formSpecFieldType).toBe("boolean");
130
+ });
131
+ it("converts string literal union to enum", () => {
132
+ const ctx = createProgramContext(sampleFormsPath);
133
+ const classDecl = findClassByName(ctx.sourceFile, "InstallmentPlan");
134
+ const analysis = analyzeClass(classDecl, ctx.checker);
135
+ const statusField = analysis.fields.find((f) => f.name === "status");
136
+ const result = convertType(statusField.type, ctx.checker);
137
+ expect(result.jsonSchema.enum).toEqual(["active", "paused", "canceled"]);
138
+ expect(result.formSpecFieldType).toBe("enum");
139
+ });
140
+ });
141
+ //# sourceMappingURL=analyzer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.test.js","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for the codegen type generation functions.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=codegen.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/codegen.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}