@formspec/build 0.1.0-alpha.12 → 0.1.0-alpha.14
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/README.md +20 -20
- package/dist/__tests__/alias-chain-propagation.test.d.ts +9 -0
- package/dist/__tests__/alias-chain-propagation.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/alias-chains.d.ts +37 -0
- package/dist/__tests__/fixtures/alias-chains.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +7 -7
- package/dist/__tests__/fixtures/example-interface-types.d.ts +17 -17
- package/dist/__tests__/guards.test.d.ts +2 -0
- package/dist/__tests__/guards.test.d.ts.map +1 -0
- package/dist/__tests__/json-utils.test.d.ts +5 -0
- package/dist/__tests__/json-utils.test.d.ts.map +1 -0
- package/dist/__tests__/path-target-parser.test.d.ts +9 -0
- package/dist/__tests__/path-target-parser.test.d.ts.map +1 -0
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +2 -2
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/json-utils.d.ts +22 -0
- package/dist/analyzer/json-utils.d.ts.map +1 -0
- package/dist/analyzer/tsdoc-parser.d.ts +18 -4
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +115 -8
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +115 -8
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +179 -42
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +184 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +173 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +178 -42
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +186 -47
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +191 -48
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +1 -0
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -106,11 +106,11 @@ The analyzer extracts type information and JSDoc constraint tags (e.g., `/** @Mi
|
|
|
106
106
|
|
|
107
107
|
### Entry Points
|
|
108
108
|
|
|
109
|
-
| Entry Point
|
|
110
|
-
|
|
|
111
|
-
| `@formspec/build`
|
|
112
|
-
| `@formspec/build/browser`
|
|
113
|
-
| `@formspec/build/internals` | CLI (unstable)
|
|
109
|
+
| Entry Point | Audience | Description |
|
|
110
|
+
| --------------------------- | -------------------- | --------------------------------------------------------------------------------- |
|
|
111
|
+
| `@formspec/build` | Public API | `buildFormSchemas`, `writeSchemas`, `generateSchemasFromClass`, schema generators |
|
|
112
|
+
| `@formspec/build/browser` | Browser (playground) | Schema generators without Node.js `fs`/`path` — safe for bundlers |
|
|
113
|
+
| `@formspec/build/internals` | CLI (unstable) | Internal APIs: `createProgramContext`, `analyzeClass`, `generateClassSchemas` |
|
|
114
114
|
|
|
115
115
|
## Generated Output
|
|
116
116
|
|
|
@@ -149,25 +149,25 @@ The analyzer extracts type information and JSDoc constraint tags (e.g., `/** @Mi
|
|
|
149
149
|
|
|
150
150
|
### Functions
|
|
151
151
|
|
|
152
|
-
| Function
|
|
153
|
-
|
|
|
154
|
-
| `buildFormSchemas(form)`
|
|
155
|
-
| `generateJsonSchema(form)`
|
|
156
|
-
| `generateUiSchema(form)`
|
|
157
|
-
| `writeSchemas(form, options)`
|
|
158
|
-
| `generateSchemasFromClass(options)`
|
|
159
|
-
| `generateSchemas(options)`
|
|
152
|
+
| Function | Description |
|
|
153
|
+
| ----------------------------------- | ------------------------------------------------------------ |
|
|
154
|
+
| `buildFormSchemas(form)` | Generate both JSON Schema and UI Schema |
|
|
155
|
+
| `generateJsonSchema(form)` | Generate only JSON Schema |
|
|
156
|
+
| `generateUiSchema(form)` | Generate only UI Schema |
|
|
157
|
+
| `writeSchemas(form, options)` | Build and write schemas to disk |
|
|
158
|
+
| `generateSchemasFromClass(options)` | Generate schemas from a TypeScript class via static analysis |
|
|
159
|
+
| `generateSchemas(options)` | Generate schemas from a TypeScript type via static analysis |
|
|
160
160
|
|
|
161
161
|
### Types
|
|
162
162
|
|
|
163
|
-
| Type
|
|
164
|
-
|
|
|
165
|
-
| `BuildResult`
|
|
166
|
-
| `WriteSchemasOptions`
|
|
167
|
-
| `WriteSchemasResult`
|
|
168
|
-
| `JsonSchema2020`
|
|
163
|
+
| Type | Description |
|
|
164
|
+
| -------------------------- | -------------------------------------- |
|
|
165
|
+
| `BuildResult` | Return type of `buildFormSchemas` |
|
|
166
|
+
| `WriteSchemasOptions` | Options for `writeSchemas` |
|
|
167
|
+
| `WriteSchemasResult` | Return type of `writeSchemas` |
|
|
168
|
+
| `JsonSchema2020` | JSON Schema 2020-12 type |
|
|
169
169
|
| `GenerateFromClassOptions` | Options for `generateSchemasFromClass` |
|
|
170
|
-
| `UISchema`
|
|
170
|
+
| `UISchema` | JSON Forms UI Schema type |
|
|
171
171
|
|
|
172
172
|
## License
|
|
173
173
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for transitive type alias constraint propagation.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that constraints declared on type aliases propagate transitively
|
|
5
|
+
* through alias chains (e.g., Integer → Percentage → field), and that
|
|
6
|
+
* excessively deep chains throw a clear error.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=alias-chain-propagation.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alias-chain-propagation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/alias-chain-propagation.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** @multipleOf 1 */
|
|
2
|
+
type Integer = number;
|
|
3
|
+
/** @minimum 0 @maximum 100 */
|
|
4
|
+
type Percentage = Integer;
|
|
5
|
+
export declare class TwoLevelChain {
|
|
6
|
+
/** @minimum 10 */
|
|
7
|
+
cpuUsage: Percentage;
|
|
8
|
+
memoryUsage: Percentage;
|
|
9
|
+
}
|
|
10
|
+
/** @minimum 0 */
|
|
11
|
+
type Base = number;
|
|
12
|
+
/** @maximum 1000 */
|
|
13
|
+
type Mid = Base;
|
|
14
|
+
/** @multipleOf 5 */
|
|
15
|
+
type Leaf = Mid;
|
|
16
|
+
export declare class ThreeLevelChain {
|
|
17
|
+
value: Leaf;
|
|
18
|
+
}
|
|
19
|
+
export declare class NoAlias {
|
|
20
|
+
/** @minimum 0 */
|
|
21
|
+
count: number;
|
|
22
|
+
}
|
|
23
|
+
type D0 = number;
|
|
24
|
+
type D1 = D0;
|
|
25
|
+
type D2 = D1;
|
|
26
|
+
type D3 = D2;
|
|
27
|
+
type D4 = D3;
|
|
28
|
+
type D5 = D4;
|
|
29
|
+
type D6 = D5;
|
|
30
|
+
type D7 = D6;
|
|
31
|
+
type D8 = D7;
|
|
32
|
+
type D9 = D8;
|
|
33
|
+
export declare class ExceedsMaxDepth {
|
|
34
|
+
value: D9;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=alias-chains.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alias-chains.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/alias-chains.ts"],"names":[],"mappings":"AAIA,oBAAoB;AACpB,KAAK,OAAO,GAAG,MAAM,CAAC;AAEtB,8BAA8B;AAC9B,KAAK,UAAU,GAAG,OAAO,CAAC;AAE1B,qBAAa,aAAa;IACxB,kBAAkB;IAClB,QAAQ,EAAG,UAAU,CAAC;IACtB,WAAW,EAAG,UAAU,CAAC;CAC1B;AAID,iBAAiB;AACjB,KAAK,IAAI,GAAG,MAAM,CAAC;AAEnB,oBAAoB;AACpB,KAAK,GAAG,GAAG,IAAI,CAAC;AAEhB,oBAAoB;AACpB,KAAK,IAAI,GAAG,GAAG,CAAC;AAEhB,qBAAa,eAAe;IAC1B,KAAK,EAAG,IAAI,CAAC;CACd;AAID,qBAAa,OAAO;IAClB,iBAAiB;IACjB,KAAK,EAAG,MAAM,CAAC;CAChB;AAOD,KAAK,EAAE,GAAG,MAAM,CAAC;AACjB,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AACb,KAAK,EAAE,GAAG,EAAE,CAAC;AAEb,qBAAa,eAAe;IAC1B,KAAK,EAAG,EAAE,CAAC;CACZ"}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
export declare class ExampleAForm {
|
|
2
2
|
/** @Field_displayName Full Name
|
|
3
3
|
* @Field_description Your legal name
|
|
4
|
-
* @
|
|
5
|
-
* @
|
|
4
|
+
* @minLength 2
|
|
5
|
+
* @maxLength 100
|
|
6
6
|
*/
|
|
7
7
|
name: string;
|
|
8
8
|
/** @Field_displayName Age
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
9
|
+
* @minimum 0
|
|
10
|
+
* @maximum 150
|
|
11
11
|
*/
|
|
12
12
|
age: number;
|
|
13
13
|
/** @Field_displayName Score
|
|
14
|
-
* @
|
|
14
|
+
* @exclusiveMinimum 0
|
|
15
15
|
*/
|
|
16
16
|
score: number;
|
|
17
17
|
/** @Field_displayName Email
|
|
18
|
-
* @
|
|
18
|
+
* @pattern ^[^@]+@[^@]+$
|
|
19
19
|
*/
|
|
20
20
|
email?: string;
|
|
21
21
|
/** @Field_displayName Country
|
|
22
|
-
* @
|
|
22
|
+
* @enumOptions [{"id":"us","label":"United States"},{"id":"ca","label":"Canada"}]
|
|
23
23
|
*/
|
|
24
24
|
country: "us" | "ca";
|
|
25
25
|
state?: string;
|
|
@@ -8,13 +8,13 @@ export interface SimpleConfig {
|
|
|
8
8
|
/**
|
|
9
9
|
* @Field_displayName Full Name
|
|
10
10
|
* @Field_description The user's legal name
|
|
11
|
-
* @
|
|
12
|
-
* @
|
|
11
|
+
* @minLength 1
|
|
12
|
+
* @maxLength 200
|
|
13
13
|
*/
|
|
14
14
|
name: string;
|
|
15
|
-
/** @Field_displayName Age @
|
|
15
|
+
/** @Field_displayName Age @minimum 0 @maximum 150 */
|
|
16
16
|
age: number;
|
|
17
|
-
/** @Field_displayName Email @
|
|
17
|
+
/** @Field_displayName Email @pattern ^[^@]+@[^@]+$ */
|
|
18
18
|
email?: string;
|
|
19
19
|
/** @Field_displayName Active */
|
|
20
20
|
active: boolean;
|
|
@@ -22,12 +22,12 @@ export interface SimpleConfig {
|
|
|
22
22
|
export interface WithEnumOptions {
|
|
23
23
|
/**
|
|
24
24
|
* @Field_displayName Status
|
|
25
|
-
* @
|
|
25
|
+
* @enumOptions ["draft","active","archived"]
|
|
26
26
|
*/
|
|
27
27
|
status: "draft" | "active" | "archived";
|
|
28
28
|
/**
|
|
29
29
|
* @Field_displayName Priority
|
|
30
|
-
* @
|
|
30
|
+
* @enumOptions [{"id":"low","label":"Low Priority"},{"id":"high","label":"High Priority"}]
|
|
31
31
|
*/
|
|
32
32
|
priority: "low" | "high";
|
|
33
33
|
}
|
|
@@ -47,9 +47,9 @@ export interface DeprecatedFieldInterface {
|
|
|
47
47
|
fullName: string;
|
|
48
48
|
}
|
|
49
49
|
export type SimpleTypeAlias = {
|
|
50
|
-
/** @Field_displayName Label @
|
|
50
|
+
/** @Field_displayName Label @minLength 1 */
|
|
51
51
|
label: string;
|
|
52
|
-
/** @Field_displayName Count @
|
|
52
|
+
/** @Field_displayName Count @minimum 0 */
|
|
53
53
|
count: number;
|
|
54
54
|
/** @Field_displayName Description */
|
|
55
55
|
description?: string;
|
|
@@ -57,17 +57,17 @@ export type SimpleTypeAlias = {
|
|
|
57
57
|
export type TypeAliasWithEnumOptions = {
|
|
58
58
|
/**
|
|
59
59
|
* @Field_displayName Color
|
|
60
|
-
* @
|
|
60
|
+
* @enumOptions ["red","green","blue"]
|
|
61
61
|
*/
|
|
62
62
|
color: "red" | "green" | "blue";
|
|
63
63
|
};
|
|
64
64
|
export type StringAlias = string;
|
|
65
65
|
export type UnionAlias = "a" | "b" | "c";
|
|
66
|
-
/** @
|
|
66
|
+
/** @minimum 0 @maximum 100 */
|
|
67
67
|
export type Percent = number;
|
|
68
|
-
/** @
|
|
68
|
+
/** @minLength 1 @maxLength 255 @pattern ^[^@]+@[^@]+$ */
|
|
69
69
|
export type Email = string;
|
|
70
|
-
/** @Field_displayName Discount Rate @Field_description Percentage discount applied @
|
|
70
|
+
/** @Field_displayName Discount Rate @Field_description Percentage discount applied @minimum 0 @maximum 100 */
|
|
71
71
|
export type AnnotatedPercent = number;
|
|
72
72
|
export interface ConfigWithAliasedTypes {
|
|
73
73
|
/** @Field_displayName Discount */
|
|
@@ -78,17 +78,17 @@ export interface ConfigWithAliasedTypes {
|
|
|
78
78
|
taxRate: AnnotatedPercent;
|
|
79
79
|
}
|
|
80
80
|
export interface Address {
|
|
81
|
-
/** @Field_displayName Street @
|
|
81
|
+
/** @Field_displayName Street @minLength 1 @maxLength 200 */
|
|
82
82
|
street: string;
|
|
83
|
-
/** @Field_displayName City @
|
|
83
|
+
/** @Field_displayName City @minLength 1 */
|
|
84
84
|
city: string;
|
|
85
|
-
/** @Field_displayName Zip @
|
|
85
|
+
/** @Field_displayName Zip @pattern ^[0-9]{5}$ */
|
|
86
86
|
zip?: string;
|
|
87
87
|
}
|
|
88
88
|
export type ContactInfo = {
|
|
89
|
-
/** @Field_displayName Email @
|
|
89
|
+
/** @Field_displayName Email @pattern ^[^@]+@[^@]+$ */
|
|
90
90
|
email: string;
|
|
91
|
-
/** @Field_displayName Phone @
|
|
91
|
+
/** @Field_displayName Phone @maxLength 20 */
|
|
92
92
|
phone?: string;
|
|
93
93
|
};
|
|
94
94
|
export interface NestedConfig {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/guards.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/json-utils.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for path-target extraction in TSDoc constraint tags.
|
|
3
|
+
*
|
|
4
|
+
* Covers both the low-level `extractPathTarget` helper and integration
|
|
5
|
+
* via `parseTSDocTags` to confirm that ConstraintNode objects carry the
|
|
6
|
+
* parsed `path` field when a `:identifier` prefix is present.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=path-target-parser.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-target-parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/path-target-parser.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EAKR,cAAc,EACd,SAAS,EACV,MAAM,gBAAgB,CAAC;AAmCxB;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAC;CAC3E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gBAAgB;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACtD,kDAAkD;IAClD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACtD,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,SAAS,UAAU,EAAE,CAAC;IAChD,qBAAqB;IACrB,QAAQ,CAAC,aAAa,EAAE,SAAS,UAAU,EAAE,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAMnD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAC9B,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,SAAK,GACR,eAAe,CA8BjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,EAAE,CAAC,oBAAoB,EACtC,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,SAAK,GACR,eAAe,CAiBjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,EAAE,CAAC,oBAAoB,EAClC,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,SAAK,GACR,0BAA0B,CAqC5B;AAoHD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC5C,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GACrB,QAAQ,CAoDV;
|
|
1
|
+
{"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Extracts constraints and annotation tags from JSDoc comments on
|
|
5
5
|
* class/interface fields and returns canonical IR nodes directly:
|
|
6
|
-
* - {@link ConstraintNode} for set-influencing tags (@
|
|
6
|
+
* - {@link ConstraintNode} for set-influencing tags (@minimum, @pattern, etc.)
|
|
7
7
|
* - {@link AnnotationNode} for value-influencing tags (@Field_displayName, etc.)
|
|
8
8
|
*
|
|
9
9
|
* The IR extraction path uses the official `@microsoft/tsdoc` parser for
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `@Field_displayName`).
|
|
13
13
|
*
|
|
14
14
|
* Supported constraints correspond to keys in {@link BUILTIN_CONSTRAINT_DEFINITIONS}
|
|
15
|
-
* from `@formspec/core` (e.g., `@
|
|
15
|
+
* from `@formspec/core` (e.g., `@minimum`, `@maximum`, `@pattern`).
|
|
16
16
|
*/
|
|
17
17
|
import * as ts from "typescript";
|
|
18
18
|
import { type ConstraintNode, type AnnotationNode } from "@formspec/core";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsdoc-constraints.d.ts","sourceRoot":"","sources":["../../src/analyzer/jsdoc-constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,
|
|
1
|
+
{"version":3,"file":"jsdoc-constraints.d.ts","sourceRoot":"","sources":["../../src/analyzer/jsdoc-constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EAEpB,MAAM,gBAAgB,CAAC;AAQxB;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,aAAa,EAAE,GACf;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,sEAAsE;IACtE,IAAI,EAAE,EAAE,CAAC,SAAS,GAAG,SAAS,CAAC;CAChC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,SAAK,GAAG,cAAc,EAAE,CAGtF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,SAAK,GAAG,cAAc,EAAE,CAGtF;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;AAMD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,cAAc,EAAE,CA0CvE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,cAAc,GAAG,IAAI,CAgC9E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON parsing utilities for the analyzer pipeline.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Attempts to parse `text` as JSON.
|
|
6
|
+
*
|
|
7
|
+
* Returns the parsed value on success, or `null` if the input is not valid
|
|
8
|
+
* JSON. This is the canonical "try-parse" wrapper used by the constraint tag
|
|
9
|
+
* parsers so that every `JSON.parse` call in this package is consistent and
|
|
10
|
+
* centrally tested.
|
|
11
|
+
*
|
|
12
|
+
* Note: when the input is the literal string `"null"`, the return value is
|
|
13
|
+
* also `null` (valid JSON). This helper therefore cannot distinguish parse
|
|
14
|
+
* failure from a successfully-parsed JSON `null`. Callers that need to
|
|
15
|
+
* distinguish these cases should use a different API (for example, a wrapper
|
|
16
|
+
* that returns a discriminated result such as `{ ok: boolean, value?: unknown }`).
|
|
17
|
+
*
|
|
18
|
+
* @param text - Raw string to parse
|
|
19
|
+
* @returns The parsed value, or `null` on parse failure
|
|
20
|
+
*/
|
|
21
|
+
export declare function tryParseJson(text: string): unknown;
|
|
22
|
+
//# sourceMappingURL=json-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-utils.d.ts","sourceRoot":"","sources":["../../src/analyzer/json-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMlD"}
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* The parser recognises two categories of tags:
|
|
9
9
|
*
|
|
10
10
|
* 1. **Constraint tags** (all alphanumeric, TSDoc-compliant):
|
|
11
|
-
* `@
|
|
12
|
-
* `@
|
|
11
|
+
* `@minimum`, `@maximum`, `@exclusiveMinimum`, `@exclusiveMaximum`,
|
|
12
|
+
* `@multipleOf`, `@minLength`, `@maxLength`, `@minItems`, `@maxItems`,
|
|
13
|
+
* `@pattern`, `@enumOptions`
|
|
13
14
|
* — Parsed via TSDocParser as custom block tags.
|
|
15
|
+
* Both camelCase and PascalCase forms are accepted (e.g., `@Minimum`).
|
|
14
16
|
*
|
|
15
17
|
* 2. **Annotation tags** (`@Field_displayName`, `@Field_description`):
|
|
16
18
|
* These contain underscores which are not valid in TSDoc tag names.
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
* verbatim.
|
|
28
30
|
*/
|
|
29
31
|
import * as ts from "typescript";
|
|
30
|
-
import { type ConstraintNode, type AnnotationNode } from "@formspec/core";
|
|
32
|
+
import { type ConstraintNode, type AnnotationNode, type PathTarget } from "@formspec/core";
|
|
31
33
|
/**
|
|
32
34
|
* Result of parsing a single JSDoc comment attached to a TS AST node.
|
|
33
35
|
*/
|
|
@@ -42,7 +44,7 @@ export interface TSDocParseResult {
|
|
|
42
44
|
* official TSDoc parser and returns canonical IR constraint and annotation
|
|
43
45
|
* nodes.
|
|
44
46
|
*
|
|
45
|
-
* For constraint tags (`@
|
|
47
|
+
* For constraint tags (`@minimum`, `@pattern`, `@enumOptions`, etc.),
|
|
46
48
|
* the structured TSDoc parser is used. For annotation tags that contain
|
|
47
49
|
* underscores (`@Field_displayName`, `@Field_description`), the TypeScript
|
|
48
50
|
* compiler JSDoc API is used as a fallback.
|
|
@@ -58,4 +60,16 @@ export declare function parseTSDocTags(node: ts.Node, file?: string): TSDocParse
|
|
|
58
60
|
* Falls back to the TS compiler API for nodes without doc comments.
|
|
59
61
|
*/
|
|
60
62
|
export declare function hasDeprecatedTagTSDoc(node: ts.Node): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Extracts a path-target prefix (`:fieldName`) from constraint tag text.
|
|
65
|
+
* Returns the parsed PathTarget and remaining text, or null if no path target.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* extractPathTarget(":value 0") // → { path: { segments: ["value"] }, remainingText: "0" }
|
|
69
|
+
* extractPathTarget("42") // → null
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractPathTarget(text: string): {
|
|
72
|
+
path: PathTarget;
|
|
73
|
+
remainingText: string;
|
|
74
|
+
} | null;
|
|
61
75
|
//# sourceMappingURL=tsdoc-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsdoc-parser.d.ts","sourceRoot":"","sources":["../../src/analyzer/tsdoc-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tsdoc-parser.d.ts","sourceRoot":"","sources":["../../src/analyzer/tsdoc-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAYjC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EAInB,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AA6ExB;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;IAChD,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;CACjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,SAAK,GAAG,gBAAgB,CAuHzE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAsB5D;AAMD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQpD"}
|
package/dist/browser.cjs
CHANGED
|
@@ -114,11 +114,40 @@ function canonicalizeField(field) {
|
|
|
114
114
|
}
|
|
115
115
|
function canonicalizeTextField(field) {
|
|
116
116
|
const type = { kind: "primitive", primitiveKind: "string" };
|
|
117
|
+
const constraints = [];
|
|
118
|
+
if (field.minLength !== void 0) {
|
|
119
|
+
const c = {
|
|
120
|
+
kind: "constraint",
|
|
121
|
+
constraintKind: "minLength",
|
|
122
|
+
value: field.minLength,
|
|
123
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
124
|
+
};
|
|
125
|
+
constraints.push(c);
|
|
126
|
+
}
|
|
127
|
+
if (field.maxLength !== void 0) {
|
|
128
|
+
const c = {
|
|
129
|
+
kind: "constraint",
|
|
130
|
+
constraintKind: "maxLength",
|
|
131
|
+
value: field.maxLength,
|
|
132
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
133
|
+
};
|
|
134
|
+
constraints.push(c);
|
|
135
|
+
}
|
|
136
|
+
if (field.pattern !== void 0) {
|
|
137
|
+
const c = {
|
|
138
|
+
kind: "constraint",
|
|
139
|
+
constraintKind: "pattern",
|
|
140
|
+
pattern: field.pattern,
|
|
141
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
142
|
+
};
|
|
143
|
+
constraints.push(c);
|
|
144
|
+
}
|
|
117
145
|
return buildFieldNode(
|
|
118
146
|
field.name,
|
|
119
147
|
type,
|
|
120
148
|
field.required,
|
|
121
|
-
buildAnnotations(field.label, field.placeholder)
|
|
149
|
+
buildAnnotations(field.label, field.placeholder),
|
|
150
|
+
constraints
|
|
122
151
|
);
|
|
123
152
|
}
|
|
124
153
|
function canonicalizeNumberField(field) {
|
|
@@ -142,6 +171,15 @@ function canonicalizeNumberField(field) {
|
|
|
142
171
|
};
|
|
143
172
|
constraints.push(c);
|
|
144
173
|
}
|
|
174
|
+
if (field.multipleOf !== void 0) {
|
|
175
|
+
const c = {
|
|
176
|
+
kind: "constraint",
|
|
177
|
+
constraintKind: "multipleOf",
|
|
178
|
+
value: field.multipleOf,
|
|
179
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
180
|
+
};
|
|
181
|
+
constraints.push(c);
|
|
182
|
+
}
|
|
145
183
|
return buildFieldNode(
|
|
146
184
|
field.name,
|
|
147
185
|
type,
|
|
@@ -370,8 +408,70 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
370
408
|
}
|
|
371
409
|
function generateFieldSchema(field, ctx) {
|
|
372
410
|
const schema = generateTypeNode(field.type, ctx);
|
|
373
|
-
|
|
411
|
+
const directConstraints = [];
|
|
412
|
+
const pathConstraints = [];
|
|
413
|
+
for (const c of field.constraints) {
|
|
414
|
+
if (c.path) {
|
|
415
|
+
pathConstraints.push(c);
|
|
416
|
+
} else {
|
|
417
|
+
directConstraints.push(c);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
applyConstraints(schema, directConstraints);
|
|
374
421
|
applyAnnotations(schema, field.annotations);
|
|
422
|
+
if (pathConstraints.length === 0) {
|
|
423
|
+
return schema;
|
|
424
|
+
}
|
|
425
|
+
return applyPathTargetedConstraints(schema, pathConstraints);
|
|
426
|
+
}
|
|
427
|
+
function applyPathTargetedConstraints(schema, pathConstraints) {
|
|
428
|
+
if (schema.type === "array" && schema.items) {
|
|
429
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
|
|
430
|
+
return schema;
|
|
431
|
+
}
|
|
432
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
433
|
+
for (const c of pathConstraints) {
|
|
434
|
+
const target = c.path?.segments[0];
|
|
435
|
+
if (!target) continue;
|
|
436
|
+
const group = byTarget.get(target) ?? [];
|
|
437
|
+
group.push(c);
|
|
438
|
+
byTarget.set(target, group);
|
|
439
|
+
}
|
|
440
|
+
const propertyOverrides = {};
|
|
441
|
+
for (const [target, constraints] of byTarget) {
|
|
442
|
+
const subSchema = {};
|
|
443
|
+
applyConstraints(subSchema, constraints);
|
|
444
|
+
propertyOverrides[target] = subSchema;
|
|
445
|
+
}
|
|
446
|
+
if (schema.$ref) {
|
|
447
|
+
const { $ref, ...rest } = schema;
|
|
448
|
+
const refPart = { $ref };
|
|
449
|
+
const overridePart = {
|
|
450
|
+
properties: propertyOverrides,
|
|
451
|
+
...rest
|
|
452
|
+
};
|
|
453
|
+
return { allOf: [refPart, overridePart] };
|
|
454
|
+
}
|
|
455
|
+
if (schema.type === "object" && schema.properties) {
|
|
456
|
+
const missingOverrides = {};
|
|
457
|
+
for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
|
|
458
|
+
if (schema.properties[target]) {
|
|
459
|
+
Object.assign(schema.properties[target], overrideSchema);
|
|
460
|
+
} else {
|
|
461
|
+
missingOverrides[target] = overrideSchema;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (Object.keys(missingOverrides).length === 0) {
|
|
465
|
+
return schema;
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
allOf: [schema, { properties: missingOverrides }]
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (schema.allOf) {
|
|
472
|
+
schema.allOf = [...schema.allOf, { properties: propertyOverrides }];
|
|
473
|
+
return schema;
|
|
474
|
+
}
|
|
375
475
|
return schema;
|
|
376
476
|
}
|
|
377
477
|
function generateTypeNode(type, ctx) {
|
|
@@ -891,14 +991,10 @@ function addUnknownExtension(ctx, message, primary) {
|
|
|
891
991
|
});
|
|
892
992
|
}
|
|
893
993
|
function findNumeric(constraints, constraintKind) {
|
|
894
|
-
return constraints.find(
|
|
895
|
-
(c) => c.constraintKind === constraintKind
|
|
896
|
-
);
|
|
994
|
+
return constraints.find((c) => c.constraintKind === constraintKind);
|
|
897
995
|
}
|
|
898
996
|
function findLength(constraints, constraintKind) {
|
|
899
|
-
return constraints.find(
|
|
900
|
-
(c) => c.constraintKind === constraintKind
|
|
901
|
-
);
|
|
997
|
+
return constraints.find((c) => c.constraintKind === constraintKind);
|
|
902
998
|
}
|
|
903
999
|
function findAllowedMembers(constraints) {
|
|
904
1000
|
return constraints.filter(
|
|
@@ -1022,6 +1118,17 @@ function checkTypeApplicability(ctx, fieldName, type, constraints) {
|
|
|
1022
1118
|
const isEnum = type.kind === "enum";
|
|
1023
1119
|
const label = typeLabel(type);
|
|
1024
1120
|
for (const constraint of constraints) {
|
|
1121
|
+
if (constraint.path) {
|
|
1122
|
+
const isTraversable = type.kind === "object" || type.kind === "array" || type.kind === "reference";
|
|
1123
|
+
if (!isTraversable) {
|
|
1124
|
+
addTypeMismatch(
|
|
1125
|
+
ctx,
|
|
1126
|
+
`Field "${fieldName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${label}" cannot be traversed`,
|
|
1127
|
+
constraint.provenance
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1025
1132
|
const ck = constraint.constraintKind;
|
|
1026
1133
|
switch (ck) {
|
|
1027
1134
|
case "minimum":
|