@formspec/build 0.1.0-alpha.4 → 0.1.0-alpha.41

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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +238 -93
  3. package/dist/analyzer/class-analyzer.d.ts +135 -0
  4. package/dist/analyzer/class-analyzer.d.ts.map +1 -0
  5. package/dist/analyzer/jsdoc-constraints.d.ts +53 -0
  6. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -0
  7. package/dist/analyzer/program.d.ts +94 -0
  8. package/dist/analyzer/program.d.ts.map +1 -0
  9. package/dist/analyzer/tsdoc-parser.d.ts +126 -0
  10. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -0
  11. package/dist/browser.cjs +2113 -0
  12. package/dist/browser.cjs.map +1 -0
  13. package/dist/browser.d.ts +74 -0
  14. package/dist/browser.d.ts.map +1 -0
  15. package/dist/browser.js +2070 -0
  16. package/dist/browser.js.map +1 -0
  17. package/dist/build-alpha.d.ts +1501 -0
  18. package/dist/build-beta.d.ts +1501 -0
  19. package/dist/build-internal.d.ts +1501 -0
  20. package/dist/build.d.ts +1194 -43
  21. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts +22 -0
  22. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -0
  23. package/dist/canonicalize/index.d.ts +8 -0
  24. package/dist/canonicalize/index.d.ts.map +1 -0
  25. package/dist/canonicalize/tsdoc-canonicalizer.d.ts +38 -0
  26. package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -0
  27. package/dist/cli.cjs +6715 -0
  28. package/dist/cli.cjs.map +1 -0
  29. package/dist/cli.js +6687 -103
  30. package/dist/cli.js.map +1 -1
  31. package/dist/extensions/index.d.ts +8 -0
  32. package/dist/extensions/index.d.ts.map +1 -0
  33. package/dist/extensions/registry.d.ts +83 -0
  34. package/dist/extensions/registry.d.ts.map +1 -0
  35. package/dist/generators/class-schema.d.ts +347 -0
  36. package/dist/generators/class-schema.d.ts.map +1 -0
  37. package/dist/generators/discovered-schema.d.ts +152 -0
  38. package/dist/generators/discovered-schema.d.ts.map +1 -0
  39. package/dist/generators/method-schema.d.ts +72 -0
  40. package/dist/generators/method-schema.d.ts.map +1 -0
  41. package/dist/generators/mixed-authoring.d.ts +52 -0
  42. package/dist/generators/mixed-authoring.d.ts.map +1 -0
  43. package/dist/index.cjs +6412 -0
  44. package/dist/index.cjs.map +1 -0
  45. package/dist/index.d.ts +50 -8
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +6387 -107
  48. package/dist/index.js.map +1 -1
  49. package/dist/internals.cjs +5573 -0
  50. package/dist/internals.cjs.map +1 -0
  51. package/dist/internals.d.ts +32 -0
  52. package/dist/internals.d.ts.map +1 -0
  53. package/dist/internals.js +5554 -0
  54. package/dist/internals.js.map +1 -0
  55. package/dist/json-schema/generator.d.ts +32 -6
  56. package/dist/json-schema/generator.d.ts.map +1 -1
  57. package/dist/json-schema/ir-generator.d.ts +149 -0
  58. package/dist/json-schema/ir-generator.d.ts.map +1 -0
  59. package/dist/json-schema/schema.d.ts +23 -0
  60. package/dist/json-schema/schema.d.ts.map +1 -0
  61. package/dist/json-schema/types.d.ts +76 -2
  62. package/dist/json-schema/types.d.ts.map +1 -1
  63. package/dist/metadata/collision-guards.d.ts +3 -0
  64. package/dist/metadata/collision-guards.d.ts.map +1 -0
  65. package/dist/metadata/index.d.ts +7 -0
  66. package/dist/metadata/index.d.ts.map +1 -0
  67. package/dist/metadata/policy.d.ts +12 -0
  68. package/dist/metadata/policy.d.ts.map +1 -0
  69. package/dist/metadata/resolve.d.ts +21 -0
  70. package/dist/metadata/resolve.d.ts.map +1 -0
  71. package/dist/static-build.d.ts +61 -0
  72. package/dist/static-build.d.ts.map +1 -0
  73. package/dist/ui-schema/generator.d.ts +18 -2
  74. package/dist/ui-schema/generator.d.ts.map +1 -1
  75. package/dist/ui-schema/ir-generator.d.ts +54 -0
  76. package/dist/ui-schema/ir-generator.d.ts.map +1 -0
  77. package/dist/ui-schema/schema.d.ts +429 -0
  78. package/dist/ui-schema/schema.d.ts.map +1 -0
  79. package/dist/ui-schema/types.d.ts +179 -35
  80. package/dist/ui-schema/types.d.ts.map +1 -1
  81. package/dist/validate/constraint-validator.d.ts +85 -0
  82. package/dist/validate/constraint-validator.d.ts.map +1 -0
  83. package/dist/validate/index.d.ts +9 -0
  84. package/dist/validate/index.d.ts.map +1 -0
  85. package/package.json +31 -11
  86. package/dist/__tests__/cli.test.d.ts +0 -2
  87. package/dist/__tests__/cli.test.d.ts.map +0 -1
  88. package/dist/__tests__/cli.test.js +0 -178
  89. package/dist/__tests__/cli.test.js.map +0 -1
  90. package/dist/__tests__/edge-cases.test.d.ts +0 -7
  91. package/dist/__tests__/edge-cases.test.d.ts.map +0 -1
  92. package/dist/__tests__/edge-cases.test.js +0 -217
  93. package/dist/__tests__/edge-cases.test.js.map +0 -1
  94. package/dist/__tests__/generator.test.d.ts +0 -2
  95. package/dist/__tests__/generator.test.d.ts.map +0 -1
  96. package/dist/__tests__/generator.test.js +0 -225
  97. package/dist/__tests__/generator.test.js.map +0 -1
  98. package/dist/__tests__/integration.test.d.ts +0 -8
  99. package/dist/__tests__/integration.test.d.ts.map +0 -1
  100. package/dist/__tests__/integration.test.js +0 -163
  101. package/dist/__tests__/integration.test.js.map +0 -1
  102. package/dist/__tests__/write-schemas.test.d.ts +0 -2
  103. package/dist/__tests__/write-schemas.test.d.ts.map +0 -1
  104. package/dist/__tests__/write-schemas.test.js +0 -196
  105. package/dist/__tests__/write-schemas.test.js.map +0 -1
  106. package/dist/json-schema/generator.js +0 -161
  107. package/dist/json-schema/generator.js.map +0 -1
  108. package/dist/json-schema/types.js +0 -7
  109. package/dist/json-schema/types.js.map +0 -1
  110. package/dist/ui-schema/generator.js +0 -150
  111. package/dist/ui-schema/generator.js.map +0 -1
  112. package/dist/ui-schema/types.js +0 -8
  113. package/dist/ui-schema/types.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mike North
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,138 +1,283 @@
1
1
  # @formspec/build
2
2
 
3
- Build tools to compile FormSpec forms into JSON Schema and JSON Forms UI Schema.
3
+ Build-time schema generation for FormSpec.
4
4
 
5
- ## Installation
5
+ This package covers:
6
+
7
+ - Chain DSL to JSON Schema / UI Schema compilation
8
+ - Static analysis of TypeScript classes, interfaces, and type aliases with TSDoc tags
9
+ - Mixed-authoring schema generation
10
+ - Extension-aware schema generation with custom vendor keywords
11
+
12
+ ## Install
6
13
 
7
14
  ```bash
8
- npm install @formspec/build
9
- # or
10
15
  pnpm add @formspec/build
11
16
  ```
12
17
 
13
- > **Note:** Most users should install the `formspec` umbrella package instead, which re-exports everything from this package.
18
+ Most app code can use `formspec`, but use `@formspec/build` directly when you need static analysis or lower-level generation APIs.
14
19
 
15
- ## Requirements
20
+ ## Public Entry Points
16
21
 
17
- This package is ESM-only and requires:
22
+ | Entry point | Purpose |
23
+ | --------------------------- | ------------------------------------- |
24
+ | `@formspec/build` | Public build APIs |
25
+ | `@formspec/build/browser` | Browser-safe chain-DSL and IR surface |
26
+ | `@formspec/build/internals` | Unstable low-level IR/analyzer APIs |
18
27
 
19
- ```json
20
- // package.json
21
- {
22
- "type": "module"
23
- }
28
+ ## Chain DSL Generation
29
+
30
+ ```ts
31
+ import { buildFormSchemas } from "@formspec/build";
32
+ import { field, formspec } from "@formspec/dsl";
33
+
34
+ const form = formspec(
35
+ field.text("name", { required: true }),
36
+ field.enum("status", ["draft", "published"] as const)
37
+ );
38
+
39
+ const { jsonSchema, uiSchema } = buildFormSchemas(form);
24
40
  ```
25
41
 
26
- ```json
27
- // tsconfig.json
28
- {
29
- "compilerOptions": {
30
- "module": "NodeNext",
31
- "moduleResolution": "NodeNext"
42
+ ## Static Analysis
43
+
44
+ `generateSchemas()` is the main entry point for TSDoc-backed generation.
45
+
46
+ ```ts
47
+ import { generateSchemas } from "@formspec/build";
48
+
49
+ const { jsonSchema, uiSchema } = generateSchemas({
50
+ filePath: "./src/forms.ts",
51
+ typeName: "ProductConfig",
52
+ });
53
+ ```
54
+
55
+ `generateSchemasFromClass()` remains available when the input is definitely a class declaration.
56
+
57
+ ```ts
58
+ import { generateSchemasFromClass } from "@formspec/build";
59
+
60
+ const result = generateSchemasFromClass({
61
+ filePath: "./src/forms.ts",
62
+ className: "ProductConfig",
63
+ });
64
+ ```
65
+
66
+ ### Static Build Context
67
+
68
+ Use the static build context APIs when you need to inspect exports, declarations,
69
+ or method signatures before deciding what schemas to generate.
70
+
71
+ Public helpers in this workflow:
72
+
73
+ - `createStaticBuildContext(filePath)` - Create a reusable compiler-backed context from a file.
74
+ - `createStaticBuildContextFromProgram(program, filePath)` - Reuse a host-owned `ts.Program`.
75
+ - `resolveModuleExport(context, exportName?)` - Resolve any exported symbol, including functions and other non-schema declarations.
76
+ - `resolveModuleExportDeclaration(context, exportName?)` - Resolve only schema-source declarations (`class`, `interface`, `type` alias).
77
+ - `resolveDeclarationMetadata(...)` - Resolve metadata for named types, methods, and properties using FormSpec's active metadata policy.
78
+ - `generateSchemasFromDeclaration(...)` - Generate from a resolved schema-source declaration.
79
+ - `generateSchemasFromParameter(...)` - Generate from a method or function parameter declaration.
80
+ - `generateSchemasFromReturnType(...)` - Generate from a method or function return type, unwrapping awaited `Promise<T>`-style returns before generation.
81
+ - `generateSchemasFromType(...)` - Generate directly from a resolved `ts.Type`.
82
+
83
+ Use `resolveModuleExportDeclaration(...)` when your tooling wants to hand a resolved
84
+ declaration straight to `generateSchemasFromDeclaration(...)`. Use `resolveModuleExport(...)`
85
+ when you need lower-level TypeScript access first, for example to inspect a function
86
+ export and then generate schemas from one of its signature types.
87
+
88
+ ```ts
89
+ import * as ts from "typescript";
90
+ import {
91
+ createStaticBuildContext,
92
+ generateSchemasFromDeclaration,
93
+ generateSchemasFromParameter,
94
+ generateSchemasFromReturnType,
95
+ resolveDeclarationMetadata,
96
+ resolveModuleExport,
97
+ resolveModuleExportDeclaration,
98
+ } from "@formspec/build";
99
+
100
+ const context = createStaticBuildContext("./src/service.ts");
101
+ const serviceDeclaration = resolveModuleExportDeclaration(context, "PaymentService");
102
+
103
+ if (serviceDeclaration && ts.isClassDeclaration(serviceDeclaration)) {
104
+ const submitMethod = serviceDeclaration.members.find(
105
+ (member): member is ts.MethodDeclaration =>
106
+ ts.isMethodDeclaration(member) &&
107
+ ts.isIdentifier(member.name) &&
108
+ member.name.text === "submit"
109
+ );
110
+
111
+ if (submitMethod?.parameters[0]) {
112
+ const methodMetadata = resolveDeclarationMetadata({
113
+ context,
114
+ declaration: submitMethod,
115
+ });
116
+ const inputSchemas = generateSchemasFromParameter({
117
+ context,
118
+ parameter: submitMethod.parameters[0],
119
+ });
32
120
  }
33
121
  }
122
+
123
+ const inputDeclaration = resolveModuleExportDeclaration(context, "SubmitInput");
124
+ if (inputDeclaration) {
125
+ const inputSchemas = generateSchemasFromDeclaration({
126
+ context,
127
+ declaration: inputDeclaration,
128
+ });
129
+ }
130
+
131
+ const paymentSymbol = resolveModuleExport(context, "submitPayment");
132
+ const paymentDeclaration = paymentSymbol?.declarations?.find(ts.isFunctionDeclaration);
133
+ if (paymentDeclaration) {
134
+ const outputSchemas = generateSchemasFromReturnType({
135
+ context,
136
+ declaration: paymentDeclaration,
137
+ });
138
+ }
34
139
  ```
35
140
 
36
- ## Usage
141
+ If you already own a `ts.Program`, use `createStaticBuildContextFromProgram(program, filePath)`
142
+ instead of letting FormSpec create one. If your tool has already resolved a raw
143
+ `ts.Type` or signature declaration, use `generateSchemasFromType(...)` or
144
+ `generateSchemasFromReturnType(...)` directly.
37
145
 
38
- ### Generate Schemas in Memory
146
+ This is the supported public path for build-time analysis workflows that used to
147
+ require `@formspec/build/internals`.
39
148
 
40
- ```typescript
41
- import { buildFormSchemas } from "@formspec/build";
42
- import { formspec, field, group } from "@formspec/dsl";
149
+ ### Supported TSDoc Examples
43
150
 
44
- const ContactForm = formspec(
45
- field.text("name", { label: "Name", required: true }),
46
- field.text("email", { label: "Email", required: true }),
47
- field.enum("subject", ["General", "Support", "Sales"]),
48
- );
151
+ ```ts
152
+ export interface ProductConfig {
153
+ /** @displayName Product Name @minLength 1 */
154
+ name: string;
49
155
 
50
- const { jsonSchema, uiSchema } = buildFormSchemas(ContactForm);
156
+ /** @format email */
157
+ supportEmail?: string;
51
158
 
52
- // Use with JSON Forms renderer
53
- // <JsonForms schema={jsonSchema} uischema={uiSchema} data={formData} />
159
+ /** @placeholder Search products */
160
+ query?: string;
161
+
162
+ /** @minimum 0 @maximum 9999.99 */
163
+ price: number;
164
+
165
+ /** @uniqueItems */
166
+ tags: string[];
167
+ }
54
168
  ```
55
169
 
56
- ### Write Schemas to Disk
170
+ ## Extension-Aware Generation
57
171
 
58
- ```typescript
59
- import { writeSchemas } from "@formspec/build";
172
+ Static-analysis and mixed-authoring generation APIs accept `extensionRegistry` and `vendorPrefix`. Chain DSL generation accepts `vendorPrefix`, but not `extensionRegistry`.
60
173
 
61
- const result = writeSchemas(ContactForm, {
62
- outDir: "./generated",
63
- name: "contact-form",
64
- indent: 2,
65
- });
174
+ ```ts
175
+ import { createExtensionRegistry, generateSchemas } from "@formspec/build";
176
+
177
+ const registry = createExtensionRegistry([myExtension]);
66
178
 
67
- console.log(`JSON Schema: ${result.jsonSchemaPath}`);
68
- console.log(`UI Schema: ${result.uiSchemaPath}`);
69
- // JSON Schema: ./generated/contact-form-schema.json
70
- // UI Schema: ./generated/contact-form-uischema.json
179
+ const result = generateSchemas({
180
+ filePath: "./src/forms.ts",
181
+ typeName: "Invoice",
182
+ extensionRegistry: registry,
183
+ vendorPrefix: "x-acme",
184
+ });
71
185
  ```
72
186
 
73
- ### Use Individual Generators
187
+ Generation validates canonical IR before emitting schemas. Invalid inputs now fail generation with structured diagnostic codes surfaced in the thrown error.
74
188
 
75
- ```typescript
76
- import { generateJsonSchema, generateUiSchema } from "@formspec/build";
189
+ ### Handling Generation Failures
77
190
 
78
- const jsonSchema = generateJsonSchema(ContactForm);
79
- const uiSchema = generateUiSchema(ContactForm);
80
- ```
191
+ Static generation now uses explicit error reporting on the main entry points:
192
+
193
+ - `generateSchemas({ ..., errorReporting: "throw" })` and `generateSchemasFromProgram({ ..., errorReporting: "throw" })` keep the simple throw-on-error contract.
194
+ - `generateSchemas({ ..., errorReporting: "diagnostics" })` and `generateSchemasFromProgram({ ..., errorReporting: "diagnostics" })` return structured diagnostics instead of throwing for analysis and validation failures.
195
+ - `generateSchemasBatch()` and `generateSchemasBatchFromProgram()` continue to return per-target diagnostics across multiple targets.
196
+
197
+ Use the `"throw"` mode when you want "schema or failure" ergonomics. Use the `"diagnostics"` mode when you want to surface as much feedback as possible in one pass, especially in editor, CI, or migration tooling. The older `generateSchemasDetailed()` and `generateSchemasFromProgramDetailed()` wrappers remain available only as deprecated compatibility shims.
198
+
199
+ ```ts
200
+ import { generateSchemas, generateSchemasBatch } from "@formspec/build";
201
+
202
+ try {
203
+ const { jsonSchema, uiSchema } = generateSchemas({
204
+ filePath: "./src/forms.ts",
205
+ typeName: "Invoice",
206
+ errorReporting: "throw",
207
+ });
208
+ } catch (error) {
209
+ const message = error instanceof Error ? error.message : String(error);
210
+
211
+ if (message.includes("UNSUPPORTED_CUSTOM_TYPE_OVERRIDE")) {
212
+ // An extension tried to override a TS global built-in such as Date or Array
213
+ } else if (message.includes("SYNTHETIC_SETUP_FAILURE")) {
214
+ // Extension setup failed before tag type-checking could run
215
+ } else if (message.includes("TYPE_MISMATCH")) {
216
+ // A tag was applied to an incompatible field or target type
217
+ }
81
218
 
82
- ## Generated Output
83
-
84
- ### JSON Schema (Draft-07)
85
-
86
- ```json
87
- {
88
- "$schema": "https://json-schema.org/draft-07/schema#",
89
- "type": "object",
90
- "properties": {
91
- "name": { "type": "string", "title": "Name" },
92
- "email": { "type": "string", "title": "Email" },
93
- "subject": {
94
- "type": "string",
95
- "enum": ["General", "Support", "Sales"]
96
- }
97
- },
98
- "required": ["name", "email"]
219
+ throw error;
220
+ }
221
+
222
+ const detailed = generateSchemas({
223
+ filePath: "./src/forms.ts",
224
+ typeName: "Invoice",
225
+ errorReporting: "diagnostics",
226
+ });
227
+
228
+ if (!detailed.ok) {
229
+ for (const diagnostic of detailed.diagnostics) {
230
+ console.error(`${diagnostic.code}: ${diagnostic.message}`);
231
+ }
99
232
  }
100
- ```
101
233
 
102
- ### JSON Forms UI Schema
234
+ const batch = generateSchemasBatch({
235
+ targets: [
236
+ { filePath: "./src/forms.ts", typeName: "Invoice" },
237
+ { filePath: "./src/forms.ts", typeName: "PaymentTerms" },
238
+ ],
239
+ });
103
240
 
104
- ```json
105
- {
106
- "type": "VerticalLayout",
107
- "elements": [
108
- { "type": "Control", "scope": "#/properties/name", "label": "Name" },
109
- { "type": "Control", "scope": "#/properties/email", "label": "Email" },
110
- { "type": "Control", "scope": "#/properties/subject" }
111
- ]
241
+ for (const result of batch) {
242
+ if (!result.ok) {
243
+ console.error(
244
+ `${result.typeName} failed: ${result.diagnostics.map((diagnostic) => diagnostic.code).join(", ")}`
245
+ );
246
+ }
112
247
  }
113
248
  ```
114
249
 
115
- ## API Reference
250
+ The most relevant codes for extension-backed static analysis failures are:
251
+
252
+ - `UNSUPPORTED_CUSTOM_TYPE_OVERRIDE` for extension custom types that conflict with unsupported TypeScript global built-ins.
253
+ - `SYNTHETIC_SETUP_FAILURE` for invalid or conflicting extension custom type registrations and other synthetic compiler setup failures.
254
+ - `TYPE_MISMATCH` for normal tag-on-type incompatibilities in author source.
255
+ - `TYPE_NOT_FOUND` when a requested exported target cannot be resolved.
256
+ - `UNSUPPORTED_ROOT_TYPE` and `DUPLICATE_ROOT_PROPERTIES` when a requested type alias cannot be treated as a schema root.
257
+ - `PROGRAM_CONTEXT_FAILURE` when the package cannot create or reuse a TypeScript program for the requested file.
258
+
259
+ As a rule of thumb, `UNSUPPORTED_CUSTOM_TYPE_OVERRIDE` and `SYNTHETIC_SETUP_FAILURE` indicate extension configuration problems, while `TYPE_MISMATCH` usually indicates an authoring error in the analyzed source.
260
+
261
+ ## Internal Entry Point
116
262
 
117
- ### Functions
263
+ Low-level canonical IR generators, analyzer primitives, and validation helpers are intentionally no longer exported from the package root. If you need those unstable internals inside the monorepo, import them from `@formspec/build/internals`.
118
264
 
119
- | Function | Description |
120
- |----------|-------------|
121
- | `buildFormSchemas(form)` | Generate both JSON Schema and UI Schema |
122
- | `generateJsonSchema(form)` | Generate only JSON Schema |
123
- | `generateUiSchema(form)` | Generate only UI Schema |
124
- | `writeSchemas(form, options)` | Build and write schemas to disk |
265
+ ## Main Exports
125
266
 
126
- ### Types
267
+ - `buildFormSchemas(form, options?)`
268
+ - `generateJsonSchema(form, options?)`
269
+ - `generateUiSchema(form)`
270
+ - `writeSchemas(form, options)`
271
+ - `generateSchemas(options)`
272
+ - `generateSchemasFromClass(options)`
273
+ - `generateSchemasFromProgram(options)`
274
+ - `generateSchemasBatch(options)`
275
+ - `generateSchemasBatchFromProgram(options)`
276
+ - `buildMixedAuthoringSchemas(options)`
277
+ - `createExtensionRegistry(extensions)`
127
278
 
128
- | Type | Description |
129
- |------|-------------|
130
- | `BuildResult` | Return type of `buildFormSchemas` |
131
- | `WriteSchemasOptions` | Options for `writeSchemas` |
132
- | `WriteSchemasResult` | Return type of `writeSchemas` |
133
- | `JSONSchema7` | JSON Schema Draft-07 type |
134
- | `UISchema` | JSON Forms UI Schema type |
279
+ `writeSchemas()` is the chain-DSL convenience wrapper for writing emitted files. Extension registries apply to the static-analysis and mixed-authoring generation flows above, not to `writeSchemas()`.
135
280
 
136
281
  ## License
137
282
 
138
- UNLICENSED
283
+ This package is part of the FormSpec monorepo and is released under the MIT License. See [LICENSE](./LICENSE) for details.
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Class analyzer for extracting fields, types, and JSDoc constraints.
3
+ *
4
+ * Produces `IRClassAnalysis` containing `FieldNode[]` and `typeRegistry`
5
+ * directly from class, interface, or type alias declarations.
6
+ * All downstream generation routes through the canonical FormIR.
7
+ */
8
+ import * as ts from "typescript";
9
+ import { type ConstraintSemanticDiagnostic } from "@formspec/analysis/internal";
10
+ import type { FieldNode, TypeNode, AnnotationNode, TypeDefinition, JsonValue, ResolvedMetadata } from "@formspec/core/internals";
11
+ import type { ExtensionRegistry } from "../extensions/index.js";
12
+ import type { MetadataPolicyInput } from "@formspec/core";
13
+ import { normalizeMetadataPolicy } from "../metadata/index.js";
14
+ export declare function isResolvableObjectLikeAliasTypeNode(typeNode: ts.TypeNode): boolean;
15
+ /**
16
+ * Layout metadata extracted from `@Group` and `@ShowWhen` TSDoc tags.
17
+ * One entry per field, in the same order as `fields`.
18
+ */
19
+ export interface FieldLayoutMetadata {
20
+ /** Group label from `@Group("label")`, or undefined if ungrouped. */
21
+ readonly groupLabel?: string;
22
+ /** ShowWhen condition from `@ShowWhen({ field, value })`, or undefined if always visible. */
23
+ readonly showWhen?: {
24
+ readonly field: string;
25
+ readonly value: JsonValue;
26
+ };
27
+ }
28
+ /**
29
+ * Result of analyzing a class/interface/type alias into canonical IR.
30
+ */
31
+ export interface IRClassAnalysis {
32
+ /** Type name */
33
+ readonly name: string;
34
+ /** Root-level metadata for the analyzed declaration. */
35
+ readonly metadata?: ResolvedMetadata;
36
+ /** Analyzed fields as canonical IR FieldNodes */
37
+ readonly fields: readonly FieldNode[];
38
+ /** Layout metadata per field (same order/length as `fields`). */
39
+ readonly fieldLayouts: readonly FieldLayoutMetadata[];
40
+ /** Named type definitions referenced by fields */
41
+ readonly typeRegistry: Record<string, TypeDefinition>;
42
+ /** Root-level metadata for the analyzed declaration. */
43
+ readonly annotations?: readonly AnnotationNode[];
44
+ /** Extraction-time diagnostics surfaced before IR validation. */
45
+ readonly diagnostics?: readonly ConstraintSemanticDiagnostic[];
46
+ /** Instance methods (retained for downstream method-schema generation) */
47
+ readonly instanceMethods: readonly MethodInfo[];
48
+ /** Static methods */
49
+ readonly staticMethods: readonly MethodInfo[];
50
+ }
51
+ export type AnalyzeTypeAliasToIRFailureKind = "duplicate-properties" | "not-object-like";
52
+ /**
53
+ * Result of analyzing a type alias into IR — either success or error.
54
+ */
55
+ export type AnalyzeTypeAliasToIRResult = {
56
+ readonly ok: true;
57
+ readonly analysis: IRClassAnalysis;
58
+ } | {
59
+ readonly ok: false;
60
+ readonly kind: AnalyzeTypeAliasToIRFailureKind;
61
+ readonly error: string;
62
+ };
63
+ export interface DeclarationRootInfo {
64
+ readonly metadata?: ResolvedMetadata;
65
+ readonly annotations: readonly AnnotationNode[];
66
+ readonly diagnostics: readonly ConstraintSemanticDiagnostic[];
67
+ }
68
+ /**
69
+ * Discriminator-specific schema generation options.
70
+ *
71
+ * @public
72
+ */
73
+ export interface DiscriminatorResolutionOptions {
74
+ /**
75
+ * Optional prefix applied only to metadata-derived discriminator values.
76
+ *
77
+ * Literal discriminator identities taken directly from a bound type remain
78
+ * unchanged.
79
+ */
80
+ readonly apiNamePrefix?: string | undefined;
81
+ }
82
+ interface AnalyzerMetadataPolicy {
83
+ readonly raw: MetadataPolicyInput | undefined;
84
+ readonly normalized: ReturnType<typeof normalizeMetadataPolicy>;
85
+ readonly discriminator: DiscriminatorResolutionOptions | undefined;
86
+ }
87
+ export declare function createAnalyzerMetadataPolicy(input?: MetadataPolicyInput, discriminator?: DiscriminatorResolutionOptions): AnalyzerMetadataPolicy;
88
+ export declare function analyzeDeclarationRootInfo(declaration: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeAliasDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry, metadataPolicy?: MetadataPolicyInput): DeclarationRootInfo;
89
+ /**
90
+ * Analyzes a class declaration and produces canonical IR FieldNodes.
91
+ */
92
+ export declare function analyzeClassToIR(classDecl: ts.ClassDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry, metadataPolicy?: MetadataPolicyInput, discriminatorOptions?: DiscriminatorResolutionOptions): IRClassAnalysis;
93
+ /**
94
+ * Analyzes an interface declaration and produces canonical IR FieldNodes.
95
+ */
96
+ export declare function analyzeInterfaceToIR(interfaceDecl: ts.InterfaceDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry, metadataPolicy?: MetadataPolicyInput, discriminatorOptions?: DiscriminatorResolutionOptions): IRClassAnalysis;
97
+ /**
98
+ * Analyzes a type alias declaration and produces canonical IR FieldNodes.
99
+ */
100
+ export declare function analyzeTypeAliasToIR(typeAlias: ts.TypeAliasDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry, metadataPolicy?: MetadataPolicyInput, discriminatorOptions?: DiscriminatorResolutionOptions): AnalyzeTypeAliasToIRResult;
101
+ export declare function getAnalyzableObjectLikePropertyName(name: ts.PropertyName): string | null;
102
+ /**
103
+ * Resolves a TypeScript type to a canonical IR TypeNode.
104
+ */
105
+ export declare function resolveTypeNode(type: ts.Type, checker: ts.TypeChecker, file: string, typeRegistry: Record<string, TypeDefinition>, visiting: Set<ts.Type>, sourceNode?: ts.Node, metadataPolicy?: AnalyzerMetadataPolicy, extensionRegistry?: ExtensionRegistry, diagnostics?: ConstraintSemanticDiagnostic[]): TypeNode;
106
+ /**
107
+ * Analyzed method information.
108
+ */
109
+ export interface MethodInfo {
110
+ /** Method name */
111
+ name: string;
112
+ /** Method parameters */
113
+ parameters: ParameterInfo[];
114
+ /** Return type node */
115
+ returnTypeNode: ts.TypeNode | undefined;
116
+ /** Resolved return type */
117
+ returnType: ts.Type;
118
+ }
119
+ /**
120
+ * Analyzed parameter information.
121
+ */
122
+ export interface ParameterInfo {
123
+ /** Parameter name */
124
+ name: string;
125
+ /** TypeScript type node */
126
+ typeNode: ts.TypeNode | undefined;
127
+ /** Resolved type */
128
+ type: ts.Type;
129
+ /** If this is InferSchema<typeof X>, the export name X */
130
+ formSpecExportName: string | null;
131
+ /** Whether the parameter is optional (has ? or default value) */
132
+ optional: boolean;
133
+ }
134
+ export {};
135
+ //# sourceMappingURL=class-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * JSDoc constraint and annotation extractor.
3
+ *
4
+ * Extracts constraints and annotation tags from JSDoc comments on
5
+ * class/interface fields and returns canonical IR nodes directly:
6
+ * - {@link ConstraintNode} for set-influencing tags (@minimum, @pattern, etc.)
7
+ * - {@link AnnotationNode} for value-influencing tags (@displayName, etc.)
8
+ *
9
+ * The IR extraction path uses the official `@microsoft/tsdoc` parser for
10
+ * all canonical tags.
11
+ *
12
+ * Supported constraints correspond to the built-in FormSpec constraint tags
13
+ * (e.g., `@minimum`, `@maximum`, `@pattern`).
14
+ */
15
+ import * as ts from "typescript";
16
+ import type { ConstraintNode, AnnotationNode } from "@formspec/core/internals";
17
+ import { type ParseTSDocOptions, type TSDocParseResult } from "./tsdoc-parser.js";
18
+ export declare function extractJSDocParseResult(node: ts.Node, file?: string, options?: ParseTSDocOptions): TSDocParseResult;
19
+ /**
20
+ * Extracts constraints from JSDoc comments on a TypeScript AST node and returns
21
+ * canonical {@link ConstraintNode} objects.
22
+ *
23
+ * Uses the official `@microsoft/tsdoc` parser for structured tag extraction.
24
+ * Constraints are registered as custom block tags in the TSDoc configuration.
25
+ *
26
+ * @param node - The AST node to inspect for JSDoc tags
27
+ * @param file - Absolute path to the source file for provenance
28
+ * @returns Canonical constraint nodes for each valid constraint tag
29
+ */
30
+ export declare function extractJSDocConstraintNodes(node: ts.Node, file?: string, options?: ParseTSDocOptions): ConstraintNode[];
31
+ /**
32
+ * Extracts canonical annotation tags from a node and returns
33
+ * {@link AnnotationNode} objects.
34
+ *
35
+ * @param node - The AST node to inspect for annotation tags
36
+ * @param file - Absolute path to the source file for provenance
37
+ * @returns Canonical annotation nodes
38
+ */
39
+ export declare function extractJSDocAnnotationNodes(node: ts.Node, file?: string, options?: ParseTSDocOptions): AnnotationNode[];
40
+ /**
41
+ * Checks if a node has a TSDoc `@deprecated` tag.
42
+ *
43
+ * Uses the TSDoc parser for structured detection.
44
+ */
45
+ export declare function hasDeprecatedTag(node: ts.Node): boolean;
46
+ /**
47
+ * Extracts a default value from a property initializer and returns a
48
+ * {@link DefaultValueAnnotationNode} if present.
49
+ *
50
+ * Only extracts literal values (strings, numbers, booleans, null).
51
+ */
52
+ export declare function extractDefaultValueAnnotation(initializer: ts.Expression | undefined, file?: string): AnnotationNode | null;
53
+ //# sourceMappingURL=jsdoc-constraints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdoc-constraints.d.ts","sourceRoot":"","sources":["../../src/analyzer/jsdoc-constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAa,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAM3B,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,IAAI,SAAK,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CAElB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,IAAI,SAAK,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC1B,cAAc,EAAE,CAGlB;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,IAAI,SAAK,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC1B,cAAc,EAAE,CAGlB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,EAAE,CAAC,UAAU,GAAG,SAAS,EACtC,IAAI,SAAK,GACR,cAAc,GAAG,IAAI,CAwCvB"}