| File: TypeScriptApiModel.cs | Web Access |
| Project: src\src\Aspire.Hosting.CodeGeneration.TypeScript\Aspire.Hosting.CodeGeneration.TypeScript.csproj (Aspire.Hosting.CodeGeneration.TypeScript) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.TypeSystem; namespace Aspire.Hosting.CodeGeneration.TypeScript; /// <summary> /// The kind of a symbol in the canonical TypeScript API export. /// </summary> internal enum TypeScriptApiItemKind { /// <summary>A generated wrapper interface for a handle type.</summary> Interface, /// <summary>A generated enum.</summary> Enum, /// <summary>A generated interface for an <c>[AspireDto]</c> type.</summary> Dto, /// <summary>A generated options bag interface for a method's optional parameters.</summary> Options, /// <summary>A namespace containing immutable exported values.</summary> Namespace, /// <summary>An immutable exported value.</summary> Constant, /// <summary> /// The members this package contributes to an interface another package owns. The owning package /// publishes the type itself, so this is deliberately not a second page for that type. /// </summary> Augmentation, /// <summary>A method on a generated interface, or a module-level entry point function.</summary> Method, /// <summary>A property on a generated interface.</summary> Property, } /// <summary> /// The exact package identity a canonical export was produced for. /// </summary> /// <param name="Name">The package name, for example <c>Aspire.Hosting.Redis</c>.</param> /// <param name="Version">The exact package version, for example <c>13.5.0</c>.</param> internal sealed record TypeScriptApiPackageIdentity(string Name, string Version); /// <summary> /// Identifies the code generator that produced a canonical export. /// </summary> /// <param name="Name">The code-generation assembly name.</param> /// <param name="Version">The code-generation assembly informational version.</param> internal sealed record TypeScriptApiGeneratorIdentity(string Name, string Version); /// <summary> /// A single parameter of a resolved TypeScript signature. /// </summary> internal sealed record TypeScriptApiParameter { /// <summary>Gets the parameter name as it appears in the generated signature.</summary> public required string Name { get; init; } /// <summary>Gets the final TypeScript type text for the parameter.</summary> public required string DeclaredType { get; init; } /// <summary>Gets a value indicating whether the parameter is optional.</summary> public required bool IsOptional { get; init; } /// <summary>Gets the documentation summary for the parameter, if any.</summary> public string? Summary { get; init; } } /// <summary> /// A documented member of an exported item. /// </summary> internal sealed record TypeScriptApiMember { /// <summary>Gets the stable, generator-owned identifier for the member.</summary> public required string Id { get; init; } /// <summary>Gets the member kind.</summary> public required TypeScriptApiItemKind Kind { get; init; } /// <summary>Gets the member name.</summary> public required string Name { get; init; } /// <summary> /// Gets the final TypeScript declaration string, for example /// <c>withPersistence(options?: WithPersistenceOptions): TestRedisResourceBuilderPromise</c>. /// </summary> public required string Declaration { get; init; } /// <summary>Gets the documentation summary.</summary> public string? Summary { get; init; } /// <summary>Gets the documentation remarks.</summary> public string? Remarks { get; init; } /// <summary>Gets the documentation examples.</summary> public IReadOnlyList<string> Examples { get; init; } = []; /// <summary>Gets the deprecation message, or <see langword="null"/> when the member is not deprecated.</summary> public string? DeprecationMessage { get; init; } /// <summary>Gets the ATS capability that produced this member, used as source metadata.</summary> public string? CapabilityId { get; init; } /// <summary> /// Gets the assembly that declares this member, which is not always the assembly that owns the /// type it hangs off: a package can add extension methods to another package's resource. /// </summary> public string? OwningAssemblyName { get; init; } /// <summary>Gets the resolved parameters of the member.</summary> public IReadOnlyList<TypeScriptApiParameter> Parameters { get; init; } = []; /// <summary>Gets the final TypeScript return type text, if the member has one.</summary> public string? ReturnType { get; init; } } /// <summary> /// A documented, package-owned top-level symbol. /// </summary> internal sealed record TypeScriptApiItem { /// <summary>Gets the stable, generator-owned identifier for the item.</summary> public required string Id { get; init; } /// <summary>Gets the ATS type identifier the item was projected from, when it has one.</summary> public required string TypeId { get; init; } /// <summary>Gets the item kind.</summary> public required TypeScriptApiItemKind Kind { get; init; } /// <summary>Gets the generated TypeScript name.</summary> public required string Name { get; init; } /// <summary>Gets the final TypeScript declaration header for the item.</summary> public required string Declaration { get; init; } /// <summary>Gets the assembly that owns the item.</summary> public required string OwningAssemblyName { get; init; } /// <summary>Gets the documentation summary.</summary> public string? Summary { get; init; } /// <summary>Gets the documentation remarks.</summary> public string? Remarks { get; init; } /// <summary>Gets the documentation examples.</summary> public IReadOnlyList<string> Examples { get; init; } = []; /// <summary>Gets the interfaces this item extends, for relationship rendering.</summary> public IReadOnlyList<string> Extends { get; init; } = []; /// <summary>Gets the documented members of the item.</summary> public IReadOnlyList<TypeScriptApiMember> Members { get; init; } = []; } /// <summary> /// A module of package-owned documentation symbols. /// </summary> internal sealed record TypeScriptApiModule { /// <summary>Gets the module name.</summary> public required string Name { get; init; } /// <summary>Gets the module summary.</summary> public string? Summary { get; init; } /// <summary>Gets the package-owned items in the module.</summary> public required IReadOnlyList<TypeScriptApiItem> Items { get; init; } } /// <summary> /// A fully rendered exported-value namespace shared by source generation and canonical projection. /// </summary> internal sealed record TypeScriptExportedValueNamespace { /// <summary>Gets the namespace name.</summary> public required string Name { get; init; } /// <summary>Gets the complete TypeScript namespace declaration.</summary> public required string Content { get; init; } /// <summary>Gets the namespace and constant members exposed for canonical documentation.</summary> public required IReadOnlyList<TypeScriptApiMember> Members { get; init; } } /// <summary> /// A generator-owned TypeScript declaration fragment. /// </summary> /// <remarks> /// Declaration IDs are scoped to the containing package export. The canonical identity of a /// declaration is the tuple <c>(package.name, package.version, declaration.id)</c>; declarations /// from separate package exports cannot be flattened into one global declaration set because /// package-local TypeScript names may intentionally overlap. The complete declaration list in one /// export must type-check on its own. /// </remarks> internal sealed record TypeScriptApiDeclaration { private readonly string _content = string.Empty; /// <summary>Gets the stable, generator-owned identifier within the containing package export.</summary> public required string Id { get; init; } /// <summary>Gets the TypeScript declaration text.</summary> /// <remarks> /// Line endings are normalized to <c>\n</c>. Some fragments come from raw string literals, which /// carry whatever line endings the source file was checked out with, so the same package export /// would otherwise differ between a CLI built on Windows and one built on Linux. /// </remarks> public required string Content { get => _content; init => _content = value.ReplaceLineEndings("\n"); } /// <summary>Gets the assembly that owns the declared symbol.</summary> public required string OwningAssemblyName { get; init; } } /// <summary> /// The canonical TypeScript API export model for one package. /// </summary> internal sealed record TypeScriptApiModel { /// <summary>Gets the export schema version.</summary> public required int SchemaVersion { get; init; } /// <summary>Gets the export language, always <c>typescript</c>.</summary> public required string Language { get; init; } /// <summary>Gets the code generator identity that produced this export.</summary> public required TypeScriptApiGeneratorIdentity Generator { get; init; } /// <summary>Gets the exact package identity this export was produced for.</summary> public required TypeScriptApiPackageIdentity Package { get; init; } /// <summary>Gets the package-owned documentation modules.</summary> public required IReadOnlyList<TypeScriptApiModule> Modules { get; init; } /// <summary> /// Gets the package-scoped declaration fragments needed to type-check the exported surface. /// </summary> public required IReadOnlyList<TypeScriptApiDeclaration> Declarations { get; init; } } /// <summary> /// A method signature resolved once and shared by the source emitter and the canonical exporter. /// </summary> /// <remarks> /// Both emitters must render the same text. Reconstructing signatures separately is what caused /// documented TypeScript signatures to drift from the generated SDK (microsoft/aspire#17608). /// </remarks> internal sealed record TypeScriptApiMethodSignature { /// <summary>Gets the generated method name.</summary> public required string MethodName { get; init; } /// <summary>Gets the final TypeScript return type text.</summary> public required string ReturnType { get; init; } /// <summary>Gets the parameters exactly as they appear in the public TypeScript signature.</summary> public required IReadOnlyList<TypeScriptApiParameter> Parameters { get; init; } /// <summary>Gets the rendered public parameter list, without the surrounding parentheses.</summary> public string ParameterList => string.Join( ", ", Parameters.Select(parameter => $"{parameter.Name}{(parameter.IsOptional ? "?" : string.Empty)}: {parameter.DeclaredType}")); /// <summary>Gets the required parameters, in declaration order.</summary> public required IReadOnlyList<AtsParameterInfo> RequiredParameters { get; init; } /// <summary>Gets the resolved options bag parameter, when the method exposes one.</summary> public TypeScriptApiParameter? OptionsParameter { get; init; } /// <summary>Gets the cancellation token emitted separately after a direct options DTO.</summary> public TypeScriptApiParameter? TrailingCancellationToken { get; init; } /// <summary>Gets the full declaration string, for example <c>addRedis(name: string): RedisResourceBuilderPromise</c>.</summary> public string Declaration => $"{MethodName}({ParameterList}): {ReturnType}"; } /// <summary> /// The result of resolving an <see cref="AtsContext"/> into TypeScript-specific decisions. /// </summary> internal sealed record TypeScriptResolvedModel { /// <summary>Gets the ATS context the model was resolved from.</summary> public required AtsContext Context { get; init; } /// <summary>Gets every builder model discovered from the context.</summary> public required List<BuilderModel> Builders { get; init; } /// <summary>Gets the builders that represent resource builders.</summary> public required List<BuilderModel> ResourceBuilders { get; init; } /// <summary>Gets the builders that represent context and wrapper type classes.</summary> public required List<BuilderModel> TypeClasses { get; init; } /// <summary>Gets the entry point capabilities that hang off the client rather than a type.</summary> public required List<AtsCapabilityInfo> ClientMethods { get; init; } /// <summary>Gets the type IDs that need generated handle aliases.</summary> public required HashSet<string> HandleTypeIds { get; init; } }