@formspec/build 0.1.0-alpha.36 → 0.1.0-alpha.37

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.
@@ -106,6 +106,8 @@ export declare function generateSchemasFromParameter(options: GenerateSchemasFro
106
106
  /**
107
107
  * Generates schemas for a method or function return type.
108
108
  *
109
+ * Awaited `Promise<T>`-style return types are unwrapped before generation.
110
+ *
109
111
  * @public
110
112
  */
111
113
  export declare function generateSchemasFromReturnType(options: GenerateSchemasFromReturnTypeOptions): DiscoveredTypeSchemas;
@@ -1 +1 @@
1
- {"version":3,"file":"discovered-schema.d.ts","sourceRoot":"","sources":["../../src/generators/discovered-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAQjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAU7D,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAK/F;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAC/B,EAAE,CAAC,gBAAgB,GACnB,EAAE,CAAC,oBAAoB,GACvB,EAAE,CAAC,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,WAAW,qCAAsC,SAAQ,6BAA6B;IAC1F,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,WAAW,8BAA+B,SAAQ,6BAA6B;IACnF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,SAAS,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,mCAAoC,SAAQ,6BAA6B;IACxF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,oBAAoB,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,WAAW,oCAAqC,SAAQ,6BAA6B;IACzF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,oBAAoB,CAAC;CAC/C;AAwSD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,qCAAqC,GAC7C,qBAAqB,CA+EvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,qBAAqB,CAEvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,qBAAqB,CAOvB;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,qBAAqB,CAkBvB"}
1
+ {"version":3,"file":"discovered-schema.d.ts","sourceRoot":"","sources":["../../src/generators/discovered-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAQjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAU7D,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAK/F;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAC/B,EAAE,CAAC,gBAAgB,GACnB,EAAE,CAAC,oBAAoB,GACvB,EAAE,CAAC,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,WAAW,qCAAsC,SAAQ,6BAA6B;IAC1F,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,WAAW,8BAA+B,SAAQ,6BAA6B;IACnF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,SAAS,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,mCAAoC,SAAQ,6BAA6B;IACxF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,oBAAoB,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,WAAW,oCAAqC,SAAQ,6BAA6B;IACzF,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,oBAAoB,CAAC;CAC/C;AAwSD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,qCAAqC,GAC7C,qBAAqB,CA+EvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,qBAAqB,CAEvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,qBAAqB,CAOvB;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,qBAAqB,CAyBvB"}
package/dist/index.cjs CHANGED
@@ -2925,6 +2925,21 @@ function isObjectType(type) {
2925
2925
  function isIntersectionType(type) {
2926
2926
  return !!(type.flags & ts3.TypeFlags.Intersection);
2927
2927
  }
2928
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
2929
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2930
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
2931
+ }
2932
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
2933
+ return true;
2934
+ }
2935
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
2936
+ }
2937
+ function isSemanticallyPlainObjectLikeType(type, checker) {
2938
+ if (isIntersectionType(type)) {
2939
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
2940
+ }
2941
+ return isObjectType(type) && checker.getSignaturesOfType(type, ts3.SignatureKind.Call).length === 0 && checker.getSignaturesOfType(type, ts3.SignatureKind.Construct).length === 0 && !checker.isArrayType(type) && !checker.isTupleType(type);
2942
+ }
2928
2943
  function isTypeReference(type) {
2929
2944
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
2930
2945
  }
@@ -3182,6 +3197,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3182
3197
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3183
3198
  return {
3184
3199
  ok: false,
3200
+ kind: "not-object-like",
3185
3201
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3186
3202
  };
3187
3203
  }
@@ -3196,6 +3212,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3196
3212
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3197
3213
  return {
3198
3214
  ok: false,
3215
+ kind: "duplicate-properties",
3199
3216
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3200
3217
  };
3201
3218
  }
@@ -3892,10 +3909,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3892
3909
  return [...duplicates].sort();
3893
3910
  }
3894
3911
  function getAnalyzableObjectLikePropertyName(name) {
3895
- if (!ts3.isIdentifier(name)) {
3896
- return null;
3912
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
3913
+ return name.text;
3897
3914
  }
3898
- return name.text;
3915
+ return null;
3899
3916
  }
3900
3917
  function applyEnumMemberDisplayNames(type, annotations) {
3901
3918
  if (!annotations.some(
@@ -4105,6 +4122,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4105
4122
  diagnostics
4106
4123
  );
4107
4124
  }
4125
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
4126
+ return resolveObjectType(
4127
+ type,
4128
+ checker,
4129
+ file,
4130
+ typeRegistry,
4131
+ visiting,
4132
+ sourceNode,
4133
+ metadataPolicy,
4134
+ extensionRegistry,
4135
+ diagnostics
4136
+ );
4137
+ }
4108
4138
  }
4109
4139
  if (isObjectType(type)) {
4110
4140
  return resolveObjectType(
@@ -5012,6 +5042,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
5012
5042
  function findTypeAliasByName(sourceFile, aliasName) {
5013
5043
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
5014
5044
  }
5045
+ function getResolvedObjectRootType(rootType, typeRegistry) {
5046
+ if (rootType.kind === "object") {
5047
+ return rootType;
5048
+ }
5049
+ if (rootType.kind !== "reference") {
5050
+ return null;
5051
+ }
5052
+ const definition = typeRegistry[rootType.name];
5053
+ return definition?.type.kind === "object" ? definition.type : null;
5054
+ }
5055
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
5056
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
5057
+ if (resolvedRootType === null) {
5058
+ return null;
5059
+ }
5060
+ const fields = resolvedRootType.properties.map((property) => ({
5061
+ kind: "field",
5062
+ name: property.name,
5063
+ ...property.metadata !== void 0 && { metadata: property.metadata },
5064
+ type: property.type,
5065
+ required: !property.optional,
5066
+ constraints: property.constraints,
5067
+ annotations: property.annotations,
5068
+ provenance: property.provenance
5069
+ }));
5070
+ return {
5071
+ name,
5072
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
5073
+ fields,
5074
+ fieldLayouts: fields.map(() => ({})),
5075
+ typeRegistry,
5076
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
5077
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
5078
+ instanceMethods: [],
5079
+ staticMethods: []
5080
+ };
5081
+ }
5082
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
5083
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5084
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
5085
+ }
5086
+ if (ts4.isTypeReferenceNode(typeNode)) {
5087
+ return true;
5088
+ }
5089
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
5090
+ }
5091
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
5092
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5093
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
5094
+ }
5095
+ if (ts4.isTypeLiteralNode(typeNode)) {
5096
+ const propertyNames = [];
5097
+ for (const member of typeNode.members) {
5098
+ if (!ts4.isPropertySignature(member)) {
5099
+ continue;
5100
+ }
5101
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
5102
+ if (propertyName !== null) {
5103
+ propertyNames.push(propertyName);
5104
+ }
5105
+ }
5106
+ return propertyNames;
5107
+ }
5108
+ if (ts4.isTypeReferenceNode(typeNode)) {
5109
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
5110
+ }
5111
+ return null;
5112
+ }
5113
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
5114
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
5115
+ return [];
5116
+ }
5117
+ const seen = /* @__PURE__ */ new Set();
5118
+ const duplicates = /* @__PURE__ */ new Set();
5119
+ for (const member of typeNode.types) {
5120
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
5121
+ if (propertyNames === null) {
5122
+ continue;
5123
+ }
5124
+ for (const propertyName of propertyNames) {
5125
+ if (seen.has(propertyName)) {
5126
+ duplicates.add(propertyName);
5127
+ } else {
5128
+ seen.add(propertyName);
5129
+ }
5130
+ }
5131
+ }
5132
+ return [...duplicates].sort();
5133
+ }
5015
5134
  function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
5016
5135
  const ctx = createProgramContext(filePath);
5017
5136
  return analyzeNamedTypeToIRFromProgramContext(
@@ -5060,6 +5179,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
5060
5179
  if (result.ok) {
5061
5180
  return result.analysis;
5062
5181
  }
5182
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
5183
+ if (!fallbackEligible) {
5184
+ throw new Error(result.error);
5185
+ }
5186
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
5187
+ typeAlias.type,
5188
+ ctx.checker
5189
+ );
5190
+ if (duplicatePropertyNames.length > 0) {
5191
+ const sourceFile = typeAlias.getSourceFile();
5192
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
5193
+ throw new Error(
5194
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
5195
+ );
5196
+ }
5197
+ const rootInfo = analyzeDeclarationRootInfo(
5198
+ typeAlias,
5199
+ ctx.checker,
5200
+ analysisFilePath,
5201
+ extensionRegistry,
5202
+ metadataPolicy
5203
+ );
5204
+ const diagnostics = [...rootInfo.diagnostics];
5205
+ const typeRegistry = {};
5206
+ const rootType = resolveTypeNode(
5207
+ ctx.checker.getTypeAtLocation(typeAlias),
5208
+ ctx.checker,
5209
+ analysisFilePath,
5210
+ typeRegistry,
5211
+ /* @__PURE__ */ new Set(),
5212
+ typeAlias,
5213
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
5214
+ extensionRegistry,
5215
+ diagnostics
5216
+ );
5217
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
5218
+ typeAlias.name.text,
5219
+ rootType,
5220
+ typeRegistry,
5221
+ rootInfo,
5222
+ diagnostics
5223
+ );
5224
+ if (fallbackAnalysis !== null) {
5225
+ return fallbackAnalysis;
5226
+ }
5063
5227
  throw new Error(result.error);
5064
5228
  }
5065
5229
  throw new Error(
@@ -5563,15 +5727,36 @@ function generateSchemasFromParameter(options) {
5563
5727
  }
5564
5728
  function generateSchemasFromReturnType(options) {
5565
5729
  const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
5566
- const type = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5730
+ const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5731
+ const type = unwrapPromiseType(options.context.checker, returnType);
5732
+ const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
5567
5733
  const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
5568
5734
  return generateSchemasFromResolvedType({
5569
5735
  ...options,
5570
5736
  type,
5571
- sourceNode: options.declaration.type ?? options.declaration,
5737
+ sourceNode,
5572
5738
  name: fallbackName
5573
5739
  });
5574
5740
  }
5741
+ function unwrapPromiseType(checker, type) {
5742
+ if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
5743
+ return type;
5744
+ }
5745
+ return checker.getAwaitedType(type) ?? type;
5746
+ }
5747
+ function unwrapPromiseTypeNode(typeNode) {
5748
+ if (typeNode === void 0) {
5749
+ return void 0;
5750
+ }
5751
+ if (ts7.isParenthesizedTypeNode(typeNode)) {
5752
+ const unwrapped = unwrapPromiseTypeNode(typeNode.type);
5753
+ return unwrapped ?? typeNode;
5754
+ }
5755
+ return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
5756
+ }
5757
+ function isPromiseTypeReferenceNode(typeNode) {
5758
+ return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
5759
+ }
5575
5760
 
5576
5761
  // src/generators/mixed-authoring.ts
5577
5762
  function buildMixedAuthoringSchemas(options) {