@formspec/build 0.1.0-alpha.30 → 0.1.0-alpha.32

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 CHANGED
@@ -19,11 +19,11 @@ Most app code can use `formspec`, but use `@formspec/build` directly when you ne
19
19
 
20
20
  ## Public Entry Points
21
21
 
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 |
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 |
27
27
 
28
28
  ## Chain DSL Generation
29
29
 
@@ -63,6 +63,83 @@ const result = generateSchemasFromClass({
63
63
  });
64
64
  ```
65
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
+ - `generateSchemasFromDeclaration(...)` - Generate from a resolved schema-source declaration.
78
+ - `generateSchemasFromParameter(...)` - Generate from a method or function parameter declaration.
79
+ - `generateSchemasFromReturnType(...)` - Generate from a method or function return type.
80
+ - `generateSchemasFromType(...)` - Generate directly from a resolved `ts.Type`.
81
+
82
+ Use `resolveModuleExportDeclaration(...)` when your tooling wants to hand a resolved
83
+ declaration straight to `generateSchemasFromDeclaration(...)`. Use `resolveModuleExport(...)`
84
+ when you need lower-level TypeScript access first, for example to inspect a function
85
+ export and then generate schemas from one of its signature types.
86
+
87
+ ```ts
88
+ import * as ts from "typescript";
89
+ import {
90
+ createStaticBuildContext,
91
+ generateSchemasFromDeclaration,
92
+ generateSchemasFromParameter,
93
+ generateSchemasFromReturnType,
94
+ resolveModuleExport,
95
+ resolveModuleExportDeclaration,
96
+ } from "@formspec/build";
97
+
98
+ const context = createStaticBuildContext("./src/service.ts");
99
+ const serviceDeclaration = resolveModuleExportDeclaration(context, "PaymentService");
100
+
101
+ if (serviceDeclaration && ts.isClassDeclaration(serviceDeclaration)) {
102
+ const submitMethod = serviceDeclaration.members.find(
103
+ (member): member is ts.MethodDeclaration =>
104
+ ts.isMethodDeclaration(member) &&
105
+ ts.isIdentifier(member.name) &&
106
+ member.name.text === "submit"
107
+ );
108
+
109
+ if (submitMethod?.parameters[0]) {
110
+ const inputSchemas = generateSchemasFromParameter({
111
+ context,
112
+ parameter: submitMethod.parameters[0],
113
+ });
114
+ }
115
+ }
116
+
117
+ const inputDeclaration = resolveModuleExportDeclaration(context, "SubmitInput");
118
+ if (inputDeclaration) {
119
+ const inputSchemas = generateSchemasFromDeclaration({
120
+ context,
121
+ declaration: inputDeclaration,
122
+ });
123
+ }
124
+
125
+ const paymentSymbol = resolveModuleExport(context, "submitPayment");
126
+ const paymentDeclaration = paymentSymbol?.declarations?.find(ts.isFunctionDeclaration);
127
+ if (paymentDeclaration) {
128
+ const outputSchemas = generateSchemasFromReturnType({
129
+ context,
130
+ declaration: paymentDeclaration,
131
+ });
132
+ }
133
+ ```
134
+
135
+ If you already own a `ts.Program`, use `createStaticBuildContextFromProgram(program, filePath)`
136
+ instead of letting FormSpec create one. If your tool has already resolved a raw
137
+ `ts.Type` or signature declaration, use `generateSchemasFromType(...)` or
138
+ `generateSchemasFromReturnType(...)` directly.
139
+
140
+ This is the supported public path for build-time analysis workflows that used to
141
+ require `@formspec/build/internals`.
142
+
66
143
  ### Supported TSDoc Examples
67
144
 
68
145
  ```ts
@@ -57,7 +57,13 @@ export type AnalyzeTypeAliasToIRResult = {
57
57
  readonly ok: false;
58
58
  readonly error: string;
59
59
  };
60
+ export interface DeclarationRootInfo {
61
+ readonly metadata?: ResolvedMetadata;
62
+ readonly annotations: readonly AnnotationNode[];
63
+ readonly diagnostics: readonly ConstraintSemanticDiagnostic[];
64
+ }
60
65
  type AnalyzerMetadataPolicy = ReturnType<typeof normalizeMetadataPolicy>;
66
+ export declare function analyzeDeclarationRootInfo(declaration: ts.ClassDeclaration | ts.InterfaceDeclaration | ts.TypeAliasDeclaration, checker: ts.TypeChecker, file?: string, extensionRegistry?: ExtensionRegistry, metadataPolicy?: MetadataPolicyInput): DeclarationRootInfo;
61
67
  /**
62
68
  * Analyzes a class declaration and produces canonical IR FieldNodes.
63
69
  */
@@ -1 +1 @@
1
- {"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/class-analyzer.ts"],"names":[],"mappings":"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"}
package/dist/browser.cjs CHANGED
@@ -757,9 +757,9 @@ function collectFields(elements, properties, required, ctx) {
757
757
  for (const element of elements) {
758
758
  switch (element.kind) {
759
759
  case "field":
760
- properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
760
+ properties[getSerializedFieldName(element)] = generateFieldSchema(element, ctx);
761
761
  if (element.required) {
762
- required.push(getSerializedName(element.name, element.metadata));
762
+ required.push(getSerializedFieldName(element));
763
763
  }
764
764
  break;
765
765
  case "group":
@@ -830,19 +830,21 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
830
830
  schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
831
831
  return schema;
832
832
  }
833
- const byTarget = /* @__PURE__ */ new Map();
834
- for (const c of pathConstraints) {
835
- const target = c.path?.segments[0];
836
- if (!target) continue;
837
- const group = byTarget.get(target) ?? [];
838
- group.push(c);
839
- byTarget.set(target, group);
840
- }
841
- const propertyOverrides = {};
842
- for (const [target, constraints] of byTarget) {
843
- const subSchema = {};
844
- applyConstraints(subSchema, constraints, ctx);
845
- propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
833
+ const propertyOverrides = buildPropertyOverrides(pathConstraints, typeNode, ctx);
834
+ const nullableValueBranch = getNullableUnionValueSchema(schema);
835
+ if (nullableValueBranch !== void 0) {
836
+ const updatedNullableValueBranch = applyPathTargetedConstraints(
837
+ nullableValueBranch,
838
+ pathConstraints,
839
+ ctx,
840
+ resolveTraversableTypeNode(typeNode, ctx)
841
+ );
842
+ if (schema.oneOf !== void 0) {
843
+ schema.oneOf = schema.oneOf.map(
844
+ (branch) => branch === nullableValueBranch ? updatedNullableValueBranch : branch
845
+ );
846
+ }
847
+ return schema;
846
848
  }
847
849
  if (schema.$ref) {
848
850
  const { $ref, ...rest } = schema;
@@ -857,7 +859,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
857
859
  const missingOverrides = {};
858
860
  for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
859
861
  if (schema.properties[target]) {
860
- Object.assign(schema.properties[target], overrideSchema);
862
+ mergeSchemaOverride(schema.properties[target], overrideSchema);
861
863
  } else {
862
864
  missingOverrides[target] = overrideSchema;
863
865
  }
@@ -931,7 +933,7 @@ function generateObjectType(type, ctx) {
931
933
  const properties = {};
932
934
  const required = [];
933
935
  for (const prop of type.properties) {
934
- const propertyName = getSerializedName(prop.name, prop.metadata);
936
+ const propertyName = getSerializedObjectPropertyName(prop);
935
937
  properties[propertyName] = generatePropertySchema(prop, ctx);
936
938
  if (!prop.optional) {
937
939
  required.push(propertyName);
@@ -985,7 +987,16 @@ function isNullableUnion(type) {
985
987
  return nullCount === 1;
986
988
  }
987
989
  function generateReferenceType(type, ctx) {
988
- return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
990
+ return { $ref: `#/$defs/${getSerializedTypeName(type.name, ctx)}` };
991
+ }
992
+ function getSerializedFieldName(field) {
993
+ return getSerializedName(field.name, field.metadata);
994
+ }
995
+ function getSerializedObjectPropertyName(property) {
996
+ return getSerializedName(property.name, property.metadata);
997
+ }
998
+ function getSerializedTypeName(logicalName, ctx) {
999
+ return ctx.typeNameMap[logicalName] ?? logicalName;
989
1000
  }
990
1001
  function applyResolvedMetadata(schema, metadata) {
991
1002
  const displayName = getDisplayName(metadata);
@@ -996,17 +1007,148 @@ function applyResolvedMetadata(schema, metadata) {
996
1007
  function resolveReferencedType(type, ctx) {
997
1008
  return ctx.typeRegistry[type.name]?.type;
998
1009
  }
1010
+ function dereferenceTypeNode(typeNode, ctx) {
1011
+ if (typeNode?.kind !== "reference") {
1012
+ return typeNode;
1013
+ }
1014
+ return resolveReferencedType(typeNode, ctx);
1015
+ }
1016
+ function unwrapNullableTypeNode(typeNode) {
1017
+ if (typeNode?.kind !== "union" || !isNullableUnion(typeNode)) {
1018
+ return typeNode;
1019
+ }
1020
+ return typeNode.members.find(
1021
+ (member) => !(member.kind === "primitive" && member.primitiveKind === "null")
1022
+ );
1023
+ }
1024
+ function resolveTraversableTypeNode(typeNode, ctx) {
1025
+ const dereferenced = dereferenceTypeNode(typeNode, ctx);
1026
+ const unwrapped = unwrapNullableTypeNode(dereferenced);
1027
+ if (unwrapped !== dereferenced) {
1028
+ return resolveTraversableTypeNode(unwrapped, ctx);
1029
+ }
1030
+ return dereferenced;
1031
+ }
999
1032
  function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1000
- if (typeNode?.kind === "object") {
1001
- const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1002
- return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1033
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1034
+ if (effectiveType?.kind === "array") {
1035
+ return resolveSerializedPropertyName(logicalName, effectiveType.items, ctx);
1003
1036
  }
1004
- if (typeNode?.kind === "reference") {
1005
- const referencedType = resolveReferencedType(typeNode, ctx);
1006
- return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1037
+ if (effectiveType?.kind === "object") {
1038
+ const property = effectiveType.properties.find((candidate) => candidate.name === logicalName);
1039
+ return property === void 0 ? logicalName : getSerializedObjectPropertyName(property);
1007
1040
  }
1008
1041
  return logicalName;
1009
1042
  }
1043
+ function resolveTargetTypeNode(logicalName, typeNode, ctx) {
1044
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1045
+ if (effectiveType?.kind === "array") {
1046
+ return resolveTargetTypeNode(logicalName, effectiveType.items, ctx);
1047
+ }
1048
+ if (effectiveType?.kind !== "object") {
1049
+ return void 0;
1050
+ }
1051
+ return effectiveType.properties.find((candidate) => candidate.name === logicalName)?.type;
1052
+ }
1053
+ function buildPropertyOverrides(pathConstraints, typeNode, ctx) {
1054
+ const byTarget = /* @__PURE__ */ new Map();
1055
+ for (const constraint of pathConstraints) {
1056
+ const target = constraint.path?.segments[0];
1057
+ if (!target) {
1058
+ continue;
1059
+ }
1060
+ const grouped = byTarget.get(target) ?? [];
1061
+ grouped.push(constraint);
1062
+ byTarget.set(target, grouped);
1063
+ }
1064
+ const overrides = {};
1065
+ for (const [target, constraints] of byTarget) {
1066
+ overrides[resolveSerializedPropertyName(target, typeNode, ctx)] = buildPathOverrideSchema(
1067
+ constraints.map(stripLeadingPathSegment),
1068
+ resolveTargetTypeNode(target, typeNode, ctx),
1069
+ ctx
1070
+ );
1071
+ }
1072
+ return overrides;
1073
+ }
1074
+ function buildPathOverrideSchema(constraints, typeNode, ctx) {
1075
+ const schema = {};
1076
+ const directConstraints = [];
1077
+ const nestedConstraints = [];
1078
+ for (const constraint of constraints) {
1079
+ if (constraint.path === void 0 || constraint.path.segments.length === 0) {
1080
+ directConstraints.push(constraint);
1081
+ } else {
1082
+ nestedConstraints.push(constraint);
1083
+ }
1084
+ }
1085
+ applyConstraints(schema, directConstraints, ctx);
1086
+ if (nestedConstraints.length === 0) {
1087
+ return schema;
1088
+ }
1089
+ const effectiveType = resolveTraversableTypeNode(typeNode, ctx);
1090
+ if (effectiveType?.kind === "array") {
1091
+ schema.items = buildPathOverrideSchema(nestedConstraints, effectiveType.items, ctx);
1092
+ return schema;
1093
+ }
1094
+ schema.properties = buildPropertyOverrides(nestedConstraints, effectiveType, ctx);
1095
+ return schema;
1096
+ }
1097
+ function mergeSchemaOverride(target, override) {
1098
+ const nullableValueBranch = getNullableUnionValueSchema(target);
1099
+ if (nullableValueBranch !== void 0) {
1100
+ mergeSchemaOverride(nullableValueBranch, override);
1101
+ return;
1102
+ }
1103
+ if (override.properties !== void 0) {
1104
+ const mergedProperties = target.properties ?? {};
1105
+ for (const [name, propertyOverride] of Object.entries(override.properties)) {
1106
+ const existing = mergedProperties[name];
1107
+ if (existing === void 0) {
1108
+ mergedProperties[name] = propertyOverride;
1109
+ } else {
1110
+ mergeSchemaOverride(existing, propertyOverride);
1111
+ }
1112
+ }
1113
+ target.properties = mergedProperties;
1114
+ }
1115
+ if (override.items !== void 0) {
1116
+ if (target.items === void 0) {
1117
+ target.items = override.items;
1118
+ } else {
1119
+ mergeSchemaOverride(target.items, override.items);
1120
+ }
1121
+ }
1122
+ for (const [key, value] of Object.entries(override)) {
1123
+ if (key === "properties" || key === "items") {
1124
+ continue;
1125
+ }
1126
+ target[key] = value;
1127
+ }
1128
+ }
1129
+ function stripLeadingPathSegment(constraint) {
1130
+ const segments = constraint.path?.segments;
1131
+ if (segments === void 0 || segments.length === 0) {
1132
+ return constraint;
1133
+ }
1134
+ const [, ...rest] = segments;
1135
+ if (rest.length === 0) {
1136
+ const { path: _path, ...stripped } = constraint;
1137
+ return stripped;
1138
+ }
1139
+ return {
1140
+ ...constraint,
1141
+ path: { segments: rest }
1142
+ };
1143
+ }
1144
+ function getNullableUnionValueSchema(schema) {
1145
+ if (schema.oneOf?.length !== 2) {
1146
+ return void 0;
1147
+ }
1148
+ const valueSchema = schema.oneOf.find((branch) => branch.type !== "null");
1149
+ const nullSchema = schema.oneOf.find((branch) => branch.type === "null");
1150
+ return valueSchema !== void 0 && nullSchema !== void 0 ? valueSchema : void 0;
1151
+ }
1010
1152
  function generateDynamicType(type) {
1011
1153
  if (type.dynamicKind === "enum") {
1012
1154
  const schema = {