@formspec/build 0.1.0-alpha.16 → 0.1.0-alpha.17

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 (35) hide show
  1. package/dist/__tests__/fixtures/example-numeric-extension.d.ts +20 -0
  2. package/dist/__tests__/fixtures/example-numeric-extension.d.ts.map +1 -0
  3. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts +1 -0
  4. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
  5. package/dist/__tests__/numeric-extension.integration.test.d.ts +2 -0
  6. package/dist/__tests__/numeric-extension.integration.test.d.ts.map +1 -0
  7. package/dist/analyzer/class-analyzer.d.ts +5 -4
  8. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  9. package/dist/analyzer/jsdoc-constraints.d.ts +3 -2
  10. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  11. package/dist/analyzer/tsdoc-parser.d.ts +18 -2
  12. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  13. package/dist/browser.cjs +199 -4
  14. package/dist/browser.cjs.map +1 -1
  15. package/dist/browser.js +199 -4
  16. package/dist/browser.js.map +1 -1
  17. package/dist/build.d.ts +28 -2
  18. package/dist/cli.cjs +547 -84
  19. package/dist/cli.cjs.map +1 -1
  20. package/dist/cli.js +547 -84
  21. package/dist/cli.js.map +1 -1
  22. package/dist/extensions/registry.d.ts +25 -1
  23. package/dist/extensions/registry.d.ts.map +1 -1
  24. package/dist/generators/class-schema.d.ts +4 -4
  25. package/dist/generators/class-schema.d.ts.map +1 -1
  26. package/dist/index.cjs +546 -84
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +546 -84
  29. package/dist/index.js.map +1 -1
  30. package/dist/internals.cjs +645 -73
  31. package/dist/internals.cjs.map +1 -1
  32. package/dist/internals.js +643 -71
  33. package/dist/internals.js.map +1 -1
  34. package/dist/validate/constraint-validator.d.ts.map +1 -1
  35. package/package.json +3 -3
@@ -0,0 +1,20 @@
1
+ import { type ConstraintTagRegistration, type CustomTypeRegistration } from "@formspec/core";
2
+ export declare const NUMERIC_EXTENSION_ID = "x-formspec/example-numeric";
3
+ export declare const DECIMAL_TYPE_ID = "x-formspec/example-numeric/Decimal";
4
+ export declare const BIGINT_TYPE_ID = "x-formspec/example-numeric/BigInt";
5
+ export interface DecimalValue {
6
+ readonly coefficient: bigint;
7
+ readonly scale: number;
8
+ }
9
+ export declare function parseDecimal(raw: string): DecimalValue;
10
+ export declare function formatDecimal(value: DecimalValue): string;
11
+ export declare function compareDecimal(left: DecimalValue, right: DecimalValue): -1 | 0 | 1;
12
+ export declare function addDecimal(left: DecimalValue, right: DecimalValue): DecimalValue;
13
+ export declare function subtractDecimal(left: DecimalValue, right: DecimalValue): DecimalValue;
14
+ export declare const decimalType: CustomTypeRegistration;
15
+ export declare const bigIntType: CustomTypeRegistration;
16
+ export declare const maxSigFigTag: ConstraintTagRegistration;
17
+ export declare const maxDecimalPlacesTag: ConstraintTagRegistration;
18
+ export declare const numericExtension: import("@formspec/core").ExtensionDefinition;
19
+ export declare function createNumericExtensionRegistry(): import("../../extensions/registry.js").ExtensionRegistry;
20
+ //# sourceMappingURL=example-numeric-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-numeric-extension.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/example-numeric-extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,yBAAyB,EAE9B,KAAK,sBAAsB,EAG5B,MAAM,gBAAgB,CAAC;AAGxB,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,eAAe,uCAAoC,CAAC;AACjE,eAAO,MAAM,cAAc,sCAAmC,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAMD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAetD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAazD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CASlF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAQhF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAQrF;AAgDD,eAAO,MAAM,WAAW,EAAE,sBAkCxB,CAAC;AAEH,eAAO,MAAM,UAAU,EAAE,sBAOvB,CAAC;AA6FH,eAAO,MAAM,YAAY,EAAE,yBAKzB,CAAC;AAEH,eAAO,MAAM,mBAAmB,EAAE,yBAKhC,CAAC;AAEH,eAAO,MAAM,gBAAgB,8CAa3B,CAAC;AAEH,wBAAgB,8BAA8B,6DAE7C"}
@@ -27,4 +27,5 @@ export declare const duplicateShippingAddressOverlays: import("@formspec/core").
27
27
  export declare const unknownShippingAddressOverlays: import("@formspec/core").FormSpec<readonly [import("@formspec/core").DynamicEnumField<"region", "regions">]>;
28
28
  export declare const constrainedShippingAddressOverlays: import("@formspec/core").FormSpec<readonly [import("@formspec/core").TextField<"city">]>;
29
29
  export declare const requiredShippingAddressOverlays: import("@formspec/core").FormSpec<readonly [import("@formspec/core").TextField<"postalCode">]>;
30
+ export declare const requiredCountryShippingAddressOverlays: import("@formspec/core").FormSpec<readonly [import("@formspec/core").TextField<"country">]>;
30
31
  //# sourceMappingURL=mixed-authoring-shipping-address.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mixed-authoring-shipping-address.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/mixed-authoring-shipping-address.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,2BAA2B;IAC3B,OAAO,EAAG,MAAM,CAAC;IAEjB,wBAAwB;IACxB,IAAI,EAAG,MAAM,CAAC;IAEd,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,2GAInC,CAAC;AAEF,qBAAa,2BAA2B;IACtC,QAAQ,EAAG,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mCAAmC,+GAE/C,CAAC;AAEF,qBAAa,0BAA0B;IACrC,OAAO,EAAG;QACR,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,eAAO,MAAM,6BAA6B,uKAEzC,CAAC;AAEF,eAAO,MAAM,gCAAgC,+KAG5C,CAAC;AAEF,eAAO,MAAM,8BAA8B,8GAAmD,CAAC;AAE/F,eAAO,MAAM,kCAAkC,0FAE9C,CAAC;AAEF,eAAO,MAAM,+BAA+B,gGAE3C,CAAC"}
1
+ {"version":3,"file":"mixed-authoring-shipping-address.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/mixed-authoring-shipping-address.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,2BAA2B;IAC3B,OAAO,EAAG,MAAM,CAAC;IAEjB,wBAAwB;IACxB,IAAI,EAAG,MAAM,CAAC;IAEd,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,2GAInC,CAAC;AAEF,qBAAa,2BAA2B;IACtC,QAAQ,EAAG,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mCAAmC,+GAE/C,CAAC;AAEF,qBAAa,0BAA0B;IACrC,OAAO,EAAG;QACR,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,eAAO,MAAM,6BAA6B,uKAEzC,CAAC;AAEF,eAAO,MAAM,gCAAgC,+KAG5C,CAAC;AAEF,eAAO,MAAM,8BAA8B,8GAAmD,CAAC;AAE/F,eAAO,MAAM,kCAAkC,0FAE9C,CAAC;AAEF,eAAO,MAAM,+BAA+B,gGAE3C,CAAC;AAEF,eAAO,MAAM,sCAAsC,6FAElD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=numeric-extension.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numeric-extension.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/numeric-extension.integration.test.ts"],"names":[],"mappings":""}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import * as ts from "typescript";
9
9
  import type { FieldNode, TypeNode, AnnotationNode, TypeDefinition, JsonValue } from "@formspec/core";
10
+ import type { ExtensionRegistry } from "../extensions/index.js";
10
11
  /**
11
12
  * Layout metadata extracted from `@Group` and `@ShowWhen` TSDoc tags.
12
13
  * One entry per field, in the same order as `fields`.
@@ -52,19 +53,19 @@ export type AnalyzeTypeAliasToIRResult = {
52
53
  /**
53
54
  * Analyzes a class declaration and produces canonical IR FieldNodes.
54
55
  */
55
- export declare function analyzeClassToIR(classDecl: ts.ClassDeclaration, checker: ts.TypeChecker, file?: string): IRClassAnalysis;
56
+ export declare function analyzeClassToIR(classDecl: ts.ClassDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry): IRClassAnalysis;
56
57
  /**
57
58
  * Analyzes an interface declaration and produces canonical IR FieldNodes.
58
59
  */
59
- export declare function analyzeInterfaceToIR(interfaceDecl: ts.InterfaceDeclaration, checker: ts.TypeChecker, file?: string): IRClassAnalysis;
60
+ export declare function analyzeInterfaceToIR(interfaceDecl: ts.InterfaceDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry): IRClassAnalysis;
60
61
  /**
61
62
  * Analyzes a type alias declaration and produces canonical IR FieldNodes.
62
63
  */
63
- export declare function analyzeTypeAliasToIR(typeAlias: ts.TypeAliasDeclaration, checker: ts.TypeChecker, file?: string): AnalyzeTypeAliasToIRResult;
64
+ export declare function analyzeTypeAliasToIR(typeAlias: ts.TypeAliasDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry): AnalyzeTypeAliasToIRResult;
64
65
  /**
65
66
  * Resolves a TypeScript type to a canonical IR TypeNode.
66
67
  */
67
- export declare function resolveTypeNode(type: ts.Type, checker: ts.TypeChecker, file: string, typeRegistry: Record<string, TypeDefinition>, visiting: Set<ts.Type>, sourceNode?: ts.Node): TypeNode;
68
+ export declare function resolveTypeNode(type: ts.Type, checker: ts.TypeChecker, file: string, typeRegistry: Record<string, TypeDefinition>, visiting: Set<ts.Type>, sourceNode?: ts.Node, extensionRegistry?: ExtensionRegistry): TypeNode;
68
69
  /**
69
70
  * Analyzed method information.
70
71
  */
@@ -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,EAIR,cAAc,EAId,cAAc,EACd,SAAS,EACV,MAAM,gBAAgB,CAAC;AAgDxB;;;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,wDAAwD;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD,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,CAuCjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,EAAE,CAAC,oBAAoB,EACtC,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,SAAK,GACR,eAAe,CA0BjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,EAAE,CAAC,oBAAoB,EAClC,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,SAAK,GACR,0BAA0B,CAuC5B;AA+ND;;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,EACtB,UAAU,CAAC,EAAE,EAAE,CAAC,IAAI,GACnB,QAAQ,CAoDV;AAqjBD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,uBAAuB;IACvB,cAAc,EAAE,EAAE,CAAC,QAAQ,GAAG,SAAS,CAAC;IACxC,2BAA2B;IAC3B,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,QAAQ,EAAE,EAAE,CAAC,QAAQ,GAAG,SAAS,CAAC;IAClC,oBAAoB;IACpB,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,0DAA0D;IAC1D,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;CACnB"}
1
+ {"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import * as ts from "typescript";
16
16
  import type { ConstraintNode, AnnotationNode } from "@formspec/core";
17
+ import { type ParseTSDocOptions } from "./tsdoc-parser.js";
17
18
  /**
18
19
  * Extracts constraints from JSDoc comments on a TypeScript AST node and returns
19
20
  * canonical {@link ConstraintNode} objects.
@@ -25,7 +26,7 @@ import type { ConstraintNode, AnnotationNode } from "@formspec/core";
25
26
  * @param file - Absolute path to the source file for provenance
26
27
  * @returns Canonical constraint nodes for each valid constraint tag
27
28
  */
28
- export declare function extractJSDocConstraintNodes(node: ts.Node, file?: string): ConstraintNode[];
29
+ export declare function extractJSDocConstraintNodes(node: ts.Node, file?: string, options?: ParseTSDocOptions): ConstraintNode[];
29
30
  /**
30
31
  * Extracts canonical annotation tags from a node and returns
31
32
  * {@link AnnotationNode} objects.
@@ -34,7 +35,7 @@ export declare function extractJSDocConstraintNodes(node: ts.Node, file?: string
34
35
  * @param file - Absolute path to the source file for provenance
35
36
  * @returns Canonical annotation nodes
36
37
  */
37
- export declare function extractJSDocAnnotationNodes(node: ts.Node, file?: string): AnnotationNode[];
38
+ export declare function extractJSDocAnnotationNodes(node: ts.Node, file?: string, options?: ParseTSDocOptions): AnnotationNode[];
38
39
  /**
39
40
  * Checks if a node has a TSDoc `@deprecated` tag.
40
41
  *
@@ -1 +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,gBAAgB,CAAC;AAOhF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,SAAK,GAAG,cAAc,EAAE,CAGtF;AAED;;;;;;;GAOG;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"}
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,gBAAgB,CAAC;AAChF,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAM3B;;;;;;;;;;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"}
@@ -28,7 +28,8 @@
28
28
  * verbatim.
29
29
  */
30
30
  import * as ts from "typescript";
31
- import { type ConstraintNode, type AnnotationNode, type PathTarget } from "@formspec/core";
31
+ import { type ConstraintNode, type AnnotationNode, type PathTarget, type TypeNode } from "@formspec/core";
32
+ import type { ExtensionRegistry } from "../extensions/index.js";
32
33
  /**
33
34
  * Result of parsing a single JSDoc comment attached to a TS AST node.
34
35
  */
@@ -38,6 +39,21 @@ export interface TSDocParseResult {
38
39
  /** Annotation IR nodes extracted from canonical TSDoc block tags. */
39
40
  readonly annotations: readonly AnnotationNode[];
40
41
  }
42
+ /**
43
+ * Optional extension-aware parsing inputs for TSDoc extraction.
44
+ */
45
+ export interface ParseTSDocOptions {
46
+ /**
47
+ * Extension registry used to resolve custom tags and custom-type-specific
48
+ * broadening of built-in constraint tags.
49
+ */
50
+ readonly extensionRegistry?: ExtensionRegistry;
51
+ /**
52
+ * Effective field/type node for the declaration being parsed. Required when
53
+ * built-in tags may broaden onto a custom type.
54
+ */
55
+ readonly fieldType?: TypeNode;
56
+ }
41
57
  /**
42
58
  * Display-name metadata extracted from a node's JSDoc tags.
43
59
  *
@@ -62,7 +78,7 @@ export interface DisplayNameMetadata {
62
78
  * @param file - Absolute source file path for provenance
63
79
  * @returns Parsed constraint and annotation nodes
64
80
  */
65
- export declare function parseTSDocTags(node: ts.Node, file?: string): TSDocParseResult;
81
+ export declare function parseTSDocTags(node: ts.Node, file?: string, options?: ParseTSDocOptions): TSDocParseResult;
66
82
  /**
67
83
  * Checks if a TS AST node has a `@deprecated` tag using the TSDoc parser.
68
84
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tsdoc-parser.d.ts","sourceRoot":"","sources":["../../src/analyzer/tsdoc-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAYjC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EAInB,KAAK,UAAU,EAEhB,MAAM,gBAAgB,CAAC;AAyFxB;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;IAChD,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;CACjD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,SAAK,GAAG,gBAAgB,CAiKzE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAsB5D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,mBAAmB,CA2B7E;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"}
1
+ {"version":3,"file":"tsdoc-parser.d.ts","sourceRoot":"","sources":["../../src/analyzer/tsdoc-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAYjC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EAInB,KAAK,UAAU,EAEf,KAAK,QAAQ,EACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AA+GhE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;IAChD,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,IAAI,SAAK,EACT,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CAiKlB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAsB5D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,mBAAmB,CA2B7E;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
@@ -754,7 +754,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
754
754
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
755
755
  );
756
756
  }
757
- Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
757
+ assignVendorPrefixedExtensionKeywords(
758
+ schema,
759
+ registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
760
+ ctx.vendorPrefix,
761
+ `custom constraint "${constraint.constraintId}"`
762
+ );
758
763
  }
759
764
  function applyCustomAnnotation(schema, annotation, ctx) {
760
765
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -766,7 +771,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
766
771
  if (registration.toJsonSchema === void 0) {
767
772
  return;
768
773
  }
769
- Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
774
+ assignVendorPrefixedExtensionKeywords(
775
+ schema,
776
+ registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
777
+ ctx.vendorPrefix,
778
+ `custom annotation "${annotation.annotationId}"`
779
+ );
780
+ }
781
+ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
782
+ for (const [key, value] of Object.entries(extensionSchema)) {
783
+ if (!key.startsWith(`${vendorPrefix}-`)) {
784
+ throw new Error(
785
+ `Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
786
+ );
787
+ }
788
+ schema[key] = value;
789
+ }
770
790
  }
771
791
 
772
792
  // src/json-schema/generator.ts
@@ -1015,7 +1035,10 @@ function getSchemaExtension(schema, key) {
1015
1035
  // src/extensions/registry.ts
1016
1036
  function createExtensionRegistry(extensions) {
1017
1037
  const typeMap = /* @__PURE__ */ new Map();
1038
+ const typeNameMap = /* @__PURE__ */ new Map();
1018
1039
  const constraintMap = /* @__PURE__ */ new Map();
1040
+ const constraintTagMap = /* @__PURE__ */ new Map();
1041
+ const builtinBroadeningMap = /* @__PURE__ */ new Map();
1019
1042
  const annotationMap = /* @__PURE__ */ new Map();
1020
1043
  for (const ext of extensions) {
1021
1044
  if (ext.types !== void 0) {
@@ -1025,6 +1048,27 @@ function createExtensionRegistry(extensions) {
1025
1048
  throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1026
1049
  }
1027
1050
  typeMap.set(qualifiedId, type);
1051
+ for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
1052
+ if (typeNameMap.has(sourceTypeName)) {
1053
+ throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
1054
+ }
1055
+ typeNameMap.set(sourceTypeName, {
1056
+ extensionId: ext.extensionId,
1057
+ registration: type
1058
+ });
1059
+ }
1060
+ if (type.builtinConstraintBroadenings !== void 0) {
1061
+ for (const broadening of type.builtinConstraintBroadenings) {
1062
+ const key = `${qualifiedId}:${broadening.tagName}`;
1063
+ if (builtinBroadeningMap.has(key)) {
1064
+ throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
1065
+ }
1066
+ builtinBroadeningMap.set(key, {
1067
+ extensionId: ext.extensionId,
1068
+ registration: broadening
1069
+ });
1070
+ }
1071
+ }
1028
1072
  }
1029
1073
  }
1030
1074
  if (ext.constraints !== void 0) {
@@ -1036,6 +1080,17 @@ function createExtensionRegistry(extensions) {
1036
1080
  constraintMap.set(qualifiedId, constraint);
1037
1081
  }
1038
1082
  }
1083
+ if (ext.constraintTags !== void 0) {
1084
+ for (const tag of ext.constraintTags) {
1085
+ if (constraintTagMap.has(tag.tagName)) {
1086
+ throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
1087
+ }
1088
+ constraintTagMap.set(tag.tagName, {
1089
+ extensionId: ext.extensionId,
1090
+ registration: tag
1091
+ });
1092
+ }
1093
+ }
1039
1094
  if (ext.annotations !== void 0) {
1040
1095
  for (const annotation of ext.annotations) {
1041
1096
  const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
@@ -1049,7 +1104,10 @@ function createExtensionRegistry(extensions) {
1049
1104
  return {
1050
1105
  extensions,
1051
1106
  findType: (typeId) => typeMap.get(typeId),
1107
+ findTypeByName: (typeName) => typeNameMap.get(typeName),
1052
1108
  findConstraint: (constraintId) => constraintMap.get(constraintId),
1109
+ findConstraintTag: (tagName) => constraintTagMap.get(tagName),
1110
+ findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
1053
1111
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
1054
1112
  };
1055
1113
  }
@@ -1117,6 +1175,7 @@ var jsonSchema7Schema = import_zod3.z.lazy(
1117
1175
  );
1118
1176
 
1119
1177
  // src/validate/constraint-validator.ts
1178
+ var import_core3 = require("@formspec/core");
1120
1179
  function addContradiction(ctx, message, primary, related) {
1121
1180
  ctx.diagnostics.push({
1122
1181
  code: "CONTRADICTING_CONSTRAINTS",
@@ -1162,6 +1221,13 @@ function addConstraintBroadening(ctx, message, primary, related) {
1162
1221
  relatedLocations: [related]
1163
1222
  });
1164
1223
  }
1224
+ function getExtensionIdFromConstraintId(constraintId) {
1225
+ const separator = constraintId.lastIndexOf("/");
1226
+ if (separator <= 0) {
1227
+ return null;
1228
+ }
1229
+ return constraintId.slice(0, separator);
1230
+ }
1165
1231
  function findNumeric(constraints, constraintKind) {
1166
1232
  return constraints.find((c) => c.constraintKind === constraintKind);
1167
1233
  }
@@ -1332,6 +1398,112 @@ function checkConstraintBroadening(ctx, fieldName, constraints) {
1332
1398
  strongestByKey.set(key, constraint);
1333
1399
  }
1334
1400
  }
1401
+ function compareCustomConstraintStrength(current, previous) {
1402
+ const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
1403
+ const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
1404
+ switch (current.role.bound) {
1405
+ case "lower":
1406
+ return equalPayloadTiebreaker;
1407
+ case "upper":
1408
+ return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
1409
+ case "exact":
1410
+ return order === 0 ? 0 : Number.NaN;
1411
+ default: {
1412
+ const _exhaustive = current.role.bound;
1413
+ return _exhaustive;
1414
+ }
1415
+ }
1416
+ }
1417
+ function compareSemanticInclusivity(currentInclusive, previousInclusive) {
1418
+ if (currentInclusive === previousInclusive) {
1419
+ return 0;
1420
+ }
1421
+ return currentInclusive ? -1 : 1;
1422
+ }
1423
+ function customConstraintsContradict(lower, upper) {
1424
+ const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
1425
+ if (order > 0) {
1426
+ return true;
1427
+ }
1428
+ if (order < 0) {
1429
+ return false;
1430
+ }
1431
+ return !lower.role.inclusive || !upper.role.inclusive;
1432
+ }
1433
+ function describeCustomConstraintTag(constraint) {
1434
+ return constraint.provenance.tagName ?? constraint.constraintId;
1435
+ }
1436
+ function checkCustomConstraintSemantics(ctx, fieldName, constraints) {
1437
+ if (ctx.extensionRegistry === void 0) {
1438
+ return;
1439
+ }
1440
+ const strongestByKey = /* @__PURE__ */ new Map();
1441
+ const lowerByFamily = /* @__PURE__ */ new Map();
1442
+ const upperByFamily = /* @__PURE__ */ new Map();
1443
+ for (const constraint of constraints) {
1444
+ if (constraint.constraintKind !== "custom") {
1445
+ continue;
1446
+ }
1447
+ const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
1448
+ if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
1449
+ continue;
1450
+ }
1451
+ const entry = {
1452
+ constraint,
1453
+ comparePayloads: registration.comparePayloads,
1454
+ role: registration.semanticRole
1455
+ };
1456
+ const familyKey = `${registration.semanticRole.family}:${pathKey(constraint)}`;
1457
+ const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
1458
+ const previous = strongestByKey.get(boundKey);
1459
+ if (previous !== void 0) {
1460
+ const strength = compareCustomConstraintStrength(entry, previous);
1461
+ if (Number.isNaN(strength)) {
1462
+ addContradiction(
1463
+ ctx,
1464
+ `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
1465
+ constraint.provenance,
1466
+ previous.constraint.provenance
1467
+ );
1468
+ continue;
1469
+ }
1470
+ if (strength < 0) {
1471
+ addConstraintBroadening(
1472
+ ctx,
1473
+ `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
1474
+ constraint.provenance,
1475
+ previous.constraint.provenance
1476
+ );
1477
+ continue;
1478
+ }
1479
+ if (strength > 0) {
1480
+ strongestByKey.set(boundKey, entry);
1481
+ }
1482
+ } else {
1483
+ strongestByKey.set(boundKey, entry);
1484
+ }
1485
+ if (registration.semanticRole.bound === "lower") {
1486
+ lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
1487
+ } else if (registration.semanticRole.bound === "upper") {
1488
+ upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
1489
+ }
1490
+ }
1491
+ for (const [familyKey, lower] of lowerByFamily) {
1492
+ const upper = upperByFamily.get(familyKey);
1493
+ if (upper === void 0) {
1494
+ continue;
1495
+ }
1496
+ if (!customConstraintsContradict(lower, upper)) {
1497
+ continue;
1498
+ }
1499
+ addContradiction(
1500
+ ctx,
1501
+ `Field "${formatPathTargetFieldName(fieldName, lower.constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
1502
+ lower.constraint.provenance,
1503
+ upper.constraint.provenance
1504
+ );
1505
+ }
1506
+ }
1335
1507
  function checkNumericContradictions(ctx, fieldName, constraints) {
1336
1508
  const min = findNumeric(constraints, "minimum");
1337
1509
  const max = findNumeric(constraints, "maximum");
@@ -1643,8 +1815,30 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
1643
1815
  );
1644
1816
  return;
1645
1817
  }
1646
- if (registration.applicableTypes === null) return;
1647
- if (!registration.applicableTypes.includes(type.kind)) {
1818
+ const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core3.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
1819
+ if (normalizedTagName !== void 0) {
1820
+ const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
1821
+ const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
1822
+ if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && tagRegistration.registration.isApplicableToType?.(type) === false) {
1823
+ addTypeMismatch(
1824
+ ctx,
1825
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
1826
+ constraint.provenance
1827
+ );
1828
+ return;
1829
+ }
1830
+ }
1831
+ if (registration.applicableTypes === null) {
1832
+ if (registration.isApplicableToType?.(type) === false) {
1833
+ addTypeMismatch(
1834
+ ctx,
1835
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
1836
+ constraint.provenance
1837
+ );
1838
+ }
1839
+ return;
1840
+ }
1841
+ if (!registration.applicableTypes.includes(type.kind) || registration.isApplicableToType?.(type) === false) {
1648
1842
  addTypeMismatch(
1649
1843
  ctx,
1650
1844
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -1675,6 +1869,7 @@ function validateConstraints(ctx, name, type, constraints) {
1675
1869
  checkAllowedMembersContradiction(ctx, name, constraints);
1676
1870
  checkConstContradictions(ctx, name, constraints);
1677
1871
  checkConstraintBroadening(ctx, name, constraints);
1872
+ checkCustomConstraintSemantics(ctx, name, constraints);
1678
1873
  checkTypeApplicability(ctx, name, type, constraints);
1679
1874
  }
1680
1875
  function validateElement(ctx, element) {