From 47c82df5d48f56c8ff076c47c2a4963f2850b021 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 4 Mar 2026 12:02:00 -0800 Subject: [PATCH 1/3] Fix TypeScript codegen: merge options interface params across overloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RegisterOptionsInterface method used first-write-wins when the same method name appeared on multiple resource types. This caused parameter loss — e.g., WithDataVolumeOptions was missing isReadOnly because RedisInsightResource.WithDataVolume (name only) was registered before RedisResource.WithDataVolume (name + isReadOnly). Now merges all optional parameters across registrations by name, producing a union interface. Also regenerates base.ts (ReferenceExpression handle-mode constructor) across all polyglot playground apps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.aspire/settings.json | 2 +- .../ValidationAppHost/.modules/aspire.ts | 1 + .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../ValidationAppHost/.modules/base.ts | 40 ++++++++++++++----- .../AtsTypeScriptCodeGenerator.cs | 12 ++++++ 39 files changed, 1094 insertions(+), 361 deletions(-) diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.aspire/settings.json b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.aspire/settings.json index 33ec69a38c4..a5b4ee845c3 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.aspire/settings.json +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.aspire/settings.json @@ -4,4 +4,4 @@ "packages": { "Aspire.Hosting.Redis": "" } -} +} \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/aspire.ts index 2822da8ab04..15531fef14e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/aspire.ts @@ -306,6 +306,7 @@ export interface WithDataBindMountOptions { export interface WithDataVolumeOptions { name?: string; + isReadOnly?: boolean; } export interface WithDescriptionOptions { diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Redis/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/base.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/base.ts index d25499d7b82..7778b0f1737 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/base.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/base.ts @@ -39,12 +39,24 @@ export { AtsErrorCodes, isMarshalledHandle, isAtsError, wrapIfHandle } from './t * ``` */ export class ReferenceExpression { - private readonly _format: string; - private readonly _valueProviders: unknown[]; - - private constructor(format: string, valueProviders: unknown[]) { - this._format = format; - this._valueProviders = valueProviders; + // Expression mode fields + private readonly _format?: string; + private readonly _valueProviders?: unknown[]; + + // Handle mode fields (when wrapping a server-returned handle) + private readonly _handle?: Handle; + private readonly _client?: AspireClient; + + constructor(format: string, valueProviders: unknown[]); + constructor(handle: Handle, client: AspireClient); + constructor(handleOrFormat: Handle | string, clientOrValueProviders: AspireClient | unknown[]) { + if (typeof handleOrFormat === 'string') { + this._format = handleOrFormat; + this._valueProviders = clientOrValueProviders as unknown[]; + } else { + this._handle = handleOrFormat; + this._client = clientOrValueProviders as AspireClient; + } } /** @@ -72,13 +84,18 @@ export class ReferenceExpression { /** * Serializes the reference expression for JSON-RPC transport. - * Uses the $expr format recognized by the server. + * In template-literal mode, uses the $expr format. + * In handle mode, delegates to the handle's serialization. */ - toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } { + toJSON(): { $expr: { format: string; valueProviders?: unknown[] } } | MarshalledHandle { + if (this._handle) { + return this._handle.toJSON(); + } + return { $expr: { - format: this._format, - valueProviders: this._valueProviders.length > 0 ? this._valueProviders : undefined + format: this._format!, + valueProviders: this._valueProviders && this._valueProviders.length > 0 ? this._valueProviders : undefined } }; } @@ -87,6 +104,9 @@ export class ReferenceExpression { * String representation for debugging. */ toString(): string { + if (this._handle) { + return `ReferenceExpression(handle)`; + } return `ReferenceExpression(${this._format})`; } } diff --git a/src/Aspire.Hosting.CodeGeneration.TypeScript/AtsTypeScriptCodeGenerator.cs b/src/Aspire.Hosting.CodeGeneration.TypeScript/AtsTypeScriptCodeGenerator.cs index e53bda49992..877bb64782f 100644 --- a/src/Aspire.Hosting.CodeGeneration.TypeScript/AtsTypeScriptCodeGenerator.cs +++ b/src/Aspire.Hosting.CodeGeneration.TypeScript/AtsTypeScriptCodeGenerator.cs @@ -673,6 +673,18 @@ private void RegisterOptionsInterface(string methodName, List { _optionsInterfacesToGenerate[interfaceName] = optionalParams; } + else if (_optionsInterfacesToGenerate.TryGetValue(interfaceName, out var existing)) + { + // Merge: add any parameters from this registration that aren't already present. + var existingNames = new HashSet(existing.Select(p => p.Name)); + foreach (var param in optionalParams) + { + if (existingNames.Add(param.Name)) + { + existing.Add(param); + } + } + } } /// From 49e65e7941c929694885e4c2a36ca9288a48e42a Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 4 Mar 2026 12:08:25 -0800 Subject: [PATCH 2/3] Add test for options interface parameter merging across overloads Adds WithDataVolume methods on both TestRedisResource (name + isReadOnly) and TestDatabaseResource (name only) to reproduce the codegen bug where first-write-wins caused parameters to be lost. The test verifies that the generated WithDataVolumeOptions interface contains the union of all parameters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 8 +-- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 4 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 12 ++--- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 12 ++--- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 16 +++--- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 11 ++++ .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/aspire.ts | 12 ++--- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../ValidationAppHost/.modules/.codegen-hash | 2 +- .../ValidationAppHost/.modules/transport.ts | 21 +++++++- .../AtsTypeScriptCodeGeneratorTests.cs | 28 ++++++++++ .../Snapshots/AtsGeneratedAspire.verified.ts | 51 +++++++++++++++++++ ...TwoPassScanningGeneratedAspire.verified.ts | 29 +++++++++++ .../TestTypes/TestExtensions.cs | 28 ++++++++++ 80 files changed, 860 insertions(+), 135 deletions(-) diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/.codegen-hash index 693f53d00c7..29cb83962df 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -BBC4E06F9B0A1EED3C45E9F2D7F9252F4E5759D47A90F8507502F9A94CED3CFD \ No newline at end of file +E009AF9F9307C1F4CF27D6EDFDA0F4344DDF74E4CEA80F06641BB5E5EEED5D55 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/.codegen-hash index 508e43e71fa..a309c80a41e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -BFDBEE79E391368156C929FBA3E89038416E658D6130E0C7BE7C4AE69BF97E8D \ No newline at end of file +0B8E5E5C065C4224832806DB93EB742BF33571136DBB1ABECB65A683983B64A7 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/.codegen-hash index dfdaa9387e5..33b1aee17da 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -7EF947E3DB9CB01B5D6008ADF07D860AB6B45E6F611A401E305D8A2BB42EA1DF \ No newline at end of file +06CC658536930207FF3C36815931591FA76882599F17680297D44D6848FB6E5A \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/.codegen-hash index 2f719706a77..2ff2e66b5e3 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -31B8640EE31CD920C45A7A3F7C2ABBDCE9F0AA8304B58DC03F170971E1A1F61A \ No newline at end of file +2AC3FFFB60C637F746D87E10A987A202B058E6DDCB0754885D0F70363464CB8E \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/.codegen-hash index c364cbba82d..67fde4eba3b 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -DBC64F54832647886AB4623206DC96C317F3F0AA5DAAE89507866008AA49F823 \ No newline at end of file +7693135140FCC6D63AF5FB6D0EFB0199D12FAFB4F57B41BBC52BA11350EFB591 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/aspire.ts index b6db30c1003..50399dd0992 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/aspire.ts @@ -1008,7 +1008,7 @@ export class DistributedApplicationBuilder { return new ProjectResourcePromise(this._addProjectInternal(name, projectPath, launchProfileName)); } - /** Exports AddAzureContainerRegistry for polyglot app hosts. */ + /** Adds an Azure Container Registry resource to the distributed application model. */ /** @internal */ async _addAzureContainerRegistryInternal(name: string): Promise { const rpcArgs: Record = { builder: this._handle, name }; @@ -1068,7 +1068,7 @@ export class DistributedApplicationBuilderPromise implements PromiseLike obj.addProject(name, projectPath, launchProfileName))); } - /** Exports AddAzureContainerRegistry for polyglot app hosts. */ + /** Adds an Azure Container Registry resource to the distributed application model. */ addAzureContainerRegistry(name: string): AzureContainerRegistryResourcePromise { return new AzureContainerRegistryResourcePromise(this._promise.then(obj => obj.addAzureContainerRegistry(name))); } @@ -1317,7 +1317,7 @@ export class AzureContainerRegistryResource extends ResourceBuilderBase obj.getResourceName()); } - /** Exports WithPurgeTask for polyglot app hosts. */ + /** Configures a purge task for the Azure Container Registry resource. */ withPurgeTask(schedule: string, options?: WithPurgeTaskOptions): AzureContainerRegistryResourcePromise { return new AzureContainerRegistryResourcePromise(this._promise.then(obj => obj.withPurgeTask(schedule, options))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/.codegen-hash index 4291a6c4a65..a94ea62a4f7 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -DBB6280E35747875BBB87C22C11F32FFF7A4B731745CC2CEF04892281DDC801D \ No newline at end of file +1A45BEC9A2EA80B577646EA520B093405A3773C7E6672AA91CC64717FEF174AE \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/.codegen-hash index a2237b90dd6..5e990312877 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -32CD5EBFD8C6520E685BBC710C248F1BF6EB7FF87B6910BC1666577596FC393A \ No newline at end of file +18FDAC42D0B2D0605DD99DB11AB6EE2D16EC66E293DB88D71771CC986E3AD1E0 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/.codegen-hash index f6382ed6264..8722aa004a9 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -4583664B2F155D5899156C24E89CE7EB0A5773906B2515AFDFC177DA4E5215DD \ No newline at end of file +28DEFB344A362363F790483ED1BC4C0CC0C4C65EE1790A774C53F26EF5FD7496 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Functions/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/.codegen-hash index c62d9c6f159..0801c13955c 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -3756A8A6EB317891A7C6FEC55CE2DE7E54B55C7269B4FE25BFCB4EAE9F1FC295 \ No newline at end of file +E43ACA70D51A27E823FED8F34630CE07F500B5BD45746BDA883DE00054068F4F \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/.codegen-hash index 3d28e91bc42..58992fb5089 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -828EA0E4912A449EDC85CB10E214D028A5DA7C5B5145142A6E2707BDC8D60B13 \ No newline at end of file +457F405505FE892B0BEBCA33708DDB98ECC477FD0FA6FDBB44D0D55D99D99B2E \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/.codegen-hash index 26b040030ce..ba695b5541b 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -E22860DD4EA07C3C79C621504C85A93A3CE0670F055B14EE67C12FB8A2C830B4 \ No newline at end of file +92AEE915CF20FBFC178D41FD24FFA4CA1B7A47F9B6818C4BB3DE79D0C93A5F8F \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/.codegen-hash index 3875aedfe7c..2d6e11c5078 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -421C194505615774A34906AC3585B5CEBB899A8D33D3CAE9C80394DD07C85E12 \ No newline at end of file +F7E3E926DDA9F3139DBDB883A45B3A9D2CB839AFCCA3572E82CA4DE3069C339C \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Redis/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/.codegen-hash index 928c69770dd..ef086e1d10d 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -D052B842D8DE3E3B7D8FB14DF049572615640B38B9A1436C7433AFAD9081FF7B \ No newline at end of file +1769C72F78EBFFAAE10337FB58430AA4EE6DA87045D99932740A18A21AC537D3 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Search/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/.codegen-hash index 0ba681a3b2f..651c47f0334 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -F15FAABEAB595E99F4A4C2F5405F3C8EB3C6274C0638F27DB6C7622757EC88AD \ No newline at end of file +E848BC8C6BABD1398EECB7818914EFE6006836BBC218847A703D74FF9F2DE46E \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/.codegen-hash index 84c728aae1c..3bd3a61299c 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -A0FC5C221339ED77FC8317C4B1C9900918CF092D44BACDE39AA4FD68D1A8F0B9 \ No newline at end of file +755F836B25D731955E2A575C1F5AA8E7A8F13692F01C09CA7A04398165F88877 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/aspire.ts index 102000535ac..997cde69585 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/aspire.ts @@ -2382,7 +2382,7 @@ export class AzureSignalRResource extends ResourceBuilderBase obj.getResourceName()); } - /** Exports RunAsEmulator for polyglot app hosts. */ + /** Configures an Azure SignalR resource to be emulated. This resource requires an to be added to the application model. Please note that the resource will be emulated in Serverless mode. */ runAsEmulator(options?: RunAsEmulatorOptions): AzureSignalRResourcePromise { return new AzureSignalRResourcePromise(this._promise.then(obj => obj.runAsEmulator(options))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.SignalR/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/.codegen-hash index 8329a43dedb..0a02fa009e2 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -04E9319C84A98EA8E5760BBAD172E33CD057C956F3E1DA37290F122DE1732CC0 \ No newline at end of file +BA13C9B82ACEF1CDF45C00B85B9E1767233C714DFA288A6121CACBDC740F441C \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Sql/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/.codegen-hash index be7f31e02a8..1c1ea58c0a6 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -CFD37E4488EB6EF2211CF7C0D52F35951C3FAB2CA84DDC19BDFEDDE2AAC0BC67 \ No newline at end of file +5A54D1B73F3111D2E28EB70736BDB5D6A1F83F296C60422016E6779C3A8EB5C2 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.Storage/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/.codegen-hash index e461a07a860..bacb3da6b6d 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -2E6B733AC69A0F4B4766C0E528F658EBDFB5D251C2836486B2B217CBD0AB6C3C \ No newline at end of file +C496E7D3582DC1E0B7FFD028168567C37E4B79DA2BB364880CA38DDB13559842 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/.codegen-hash index 38c30f4f3ca..b445451512e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -86023D404399D568298B0CBF9086DA716B40E9C86EBC3F3410AF485AF2AB8350 \ No newline at end of file +F42E68D7F1DD2F346AEC5468489625C49348F3F3BF65679B6C37EAD07DDAC705 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.DevTunnels/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/.codegen-hash index 350967db366..fb4fa742adf 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -631BBDFB5C0199C5FFFC91CA20424E6F2B7E092C25EB887B9C5820C5FF3EB37A \ No newline at end of file +6FBE6E6918F401B5EFA2262887B7D452DA17D6BEB38BB9982DC5FA842070D751 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts index b98f1b70441..7493d322214 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts @@ -3268,7 +3268,7 @@ export class GarnetResource extends ResourceBuilderBase { return new GarnetResource(result, this._client); } - /** Exports WithDataVolume for polyglot app hosts. */ + /** Adds a persistent data volume to the Garnet resource. */ withDataVolume(options?: WithDataVolumeOptions): GarnetResourcePromise { const name = options?.name; const isReadOnly = options?.isReadOnly; @@ -3286,7 +3286,7 @@ export class GarnetResource extends ResourceBuilderBase { return new GarnetResource(result, this._client); } - /** Exports WithDataBindMount for polyglot app hosts. */ + /** Mounts a host directory as the Garnet data directory. */ withDataBindMount(source: string, options?: WithDataBindMountOptions): GarnetResourcePromise { const isReadOnly = options?.isReadOnly; return new GarnetResourcePromise(this._withDataBindMountInternal(source, isReadOnly)); @@ -3303,7 +3303,7 @@ export class GarnetResource extends ResourceBuilderBase { return new GarnetResource(result, this._client); } - /** Exports WithPersistence for polyglot app hosts. */ + /** Configures snapshot persistence for the Garnet resource. */ withPersistence(options?: WithPersistenceOptions): GarnetResourcePromise { const interval = options?.interval; return new GarnetResourcePromise(this._withPersistenceInternal(interval)); @@ -3521,17 +3521,17 @@ export class GarnetResourcePromise implements PromiseLike { return this._promise.then(obj => obj.getResourceName()); } - /** Exports WithDataVolume for polyglot app hosts. */ + /** Adds a persistent data volume to the Garnet resource. */ withDataVolume(options?: WithDataVolumeOptions): GarnetResourcePromise { return new GarnetResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); } - /** Exports WithDataBindMount for polyglot app hosts. */ + /** Mounts a host directory as the Garnet data directory. */ withDataBindMount(source: string, options?: WithDataBindMountOptions): GarnetResourcePromise { return new GarnetResourcePromise(this._promise.then(obj => obj.withDataBindMount(source, options))); } - /** Exports WithPersistence for polyglot app hosts. */ + /** Configures snapshot persistence for the Garnet resource. */ withPersistence(options?: WithPersistenceOptions): GarnetResourcePromise { return new GarnetResourcePromise(this._promise.then(obj => obj.withPersistence(options))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/.codegen-hash index aefe809b9a4..82dbba0bf01 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.GitHub.Models/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -C13880F095DBA862665782C34D611AD1BB6B65C79C0D482A6EB5C7CACE3CDB53 \ No newline at end of file +51CC903953FCE3216DF2934FF75156EB3E9B9C2ADB2592FC372049602C007C79 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/.codegen-hash index a903bfabfbf..cf0d6b8e286 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -E0062628DDBA6FB1887B6001ED2331FEBD7260AD4FD6C333B4B7960F9AF46F81 \ No newline at end of file +39D801434F6FB56F828BDAA106DAE5D665B2D3E925554CF07E1B075E74422377 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/.codegen-hash index 19ef9948bb9..6e458bc5bfa 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -9EFB61F16E182F3768B58F94109F169DE36E549231F384300B80FD166C7A010B \ No newline at end of file +96607CC26221B5F285F571BA7873F5E7D176F3A02EE8E52927D9FE92B0325393 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/.codegen-hash index b463c16cef5..e5a4ff9528a 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -47DB5A0B3644F6209B62B510AD6923A6216829A0C0420ECDA70F90FF03C686F7 \ No newline at end of file +753C29A2A05145D3912D48C7514BA2AB8D37804E8FE2A56FAB608E2C86714404 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/.codegen-hash index ce0ce5c46be..a372e83d5ad 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -C059C1DD2931ECF547C421DC89FBA611588AFAE7F282D848F2B58393737D1D37 \ No newline at end of file +7EB466C3E4154EC74DB2BFB07815DCE255B1A457ACDD54B6D056D26163C824C5 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/.codegen-hash index b18442cc6ec..ac525efdda3 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -BA82BCA5B4BE2904A61C1F7B2680F9143C797E08051DC6948D08D41A2F137904 \ No newline at end of file +C47D83EAB5C7D7B8CD066C32C9A226FD8D866A6976A925A632F603D09A5D6586 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts index 0911d135745..755d0e19342 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts @@ -3265,7 +3265,7 @@ export class NatsServerResource extends ResourceBuilderBase obj.getResourceName()); } - /** Exports WithJetStream for polyglot app hosts. */ + /** Configures the NATS resource to enable JetStream. */ withJetStream(): NatsServerResourcePromise { return new NatsServerResourcePromise(this._promise.then(obj => obj.withJetStream())); } - /** Exports WithDataVolume for polyglot app hosts. */ + /** Adds a persistent data volume to the NATS resource. */ withDataVolume(options?: WithDataVolumeOptions): NatsServerResourcePromise { return new NatsServerResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); } - /** Exports WithDataBindMount for polyglot app hosts. */ + /** Mounts a host directory as the NATS data directory. */ withDataBindMount(source: string, options?: WithDataBindMountOptions): NatsServerResourcePromise { return new NatsServerResourcePromise(this._promise.then(obj => obj.withDataBindMount(source, options))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/.codegen-hash index f6b9a8c9468..2cc90257871 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -039E22BEBD11F9200A87529D869A2C51F12C061E9B03BB26B6D96C3940A1D5C5 \ No newline at end of file +3F48A1579BD68DB4DB8050A6A15FAEECEB8E2002ECAD9ADA61B8BE15862050AD \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/aspire.ts index 21aaf648879..9e7b37a260f 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/aspire.ts @@ -1004,7 +1004,7 @@ export class DistributedApplicationBuilder { return new ProjectResourcePromise(this._addProjectInternal(name, projectPath, launchProfileName)); } - /** Exports AddOpenAI for polyglot app hosts. */ + /** Adds an OpenAI resource to the distributed application model. */ /** @internal */ async _addOpenAIInternal(name: string): Promise { const rpcArgs: Record = { builder: this._handle, name }; @@ -1064,7 +1064,7 @@ export class DistributedApplicationBuilderPromise implements PromiseLike obj.addProject(name, projectPath, launchProfileName))); } - /** Exports AddOpenAI for polyglot app hosts. */ + /** Adds an OpenAI resource to the distributed application model. */ addOpenAI(name: string): OpenAIResourcePromise { return new OpenAIResourcePromise(this._promise.then(obj => obj.addOpenAI(name))); } @@ -3117,7 +3117,7 @@ export class OpenAIResource extends ResourceBuilderBase { return new OpenAIModelResource(result, this._client); } - /** Exports AddModel for polyglot app hosts. */ + /** Adds an OpenAI model resource. */ addModel(name: string, model: string): OpenAIModelResourcePromise { return new OpenAIModelResourcePromise(this._addModelInternal(name, model)); } @@ -3132,7 +3132,7 @@ export class OpenAIResource extends ResourceBuilderBase { return new OpenAIResource(result, this._client); } - /** Exports WithEndpoint for polyglot app hosts. */ + /** Configures the endpoint URI for the OpenAI resource. */ withEndpoint(endpoint: string): OpenAIResourcePromise { return new OpenAIResourcePromise(this._withEndpointInternal(endpoint)); } @@ -3147,7 +3147,7 @@ export class OpenAIResource extends ResourceBuilderBase { return new OpenAIResource(result, this._client); } - /** Exports WithApiKey for polyglot app hosts. */ + /** Configures the API key for the OpenAI resource. */ withApiKey(apiKey: ParameterResource): OpenAIResourcePromise { return new OpenAIResourcePromise(this._withApiKeyInternal(apiKey)); } @@ -3214,17 +3214,17 @@ export class OpenAIResourcePromise implements PromiseLike { return this._promise.then(obj => obj.getResourceName()); } - /** Exports AddModel for polyglot app hosts. */ + /** Adds an OpenAI model resource. */ addModel(name: string, model: string): OpenAIModelResourcePromise { return new OpenAIModelResourcePromise(this._promise.then(obj => obj.addModel(name, model))); } - /** Exports WithEndpoint for polyglot app hosts. */ + /** Configures the endpoint URI for the OpenAI resource. */ withEndpoint(endpoint: string): OpenAIResourcePromise { return new OpenAIResourcePromise(this._promise.then(obj => obj.withEndpoint(endpoint))); } - /** Exports WithApiKey for polyglot app hosts. */ + /** Configures the API key for the OpenAI resource. */ withApiKey(apiKey: ParameterResource): OpenAIResourcePromise { return new OpenAIResourcePromise(this._promise.then(obj => obj.withApiKey(apiKey))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.OpenAI/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/.codegen-hash index 77246f8bf0f..7c60a65fc2b 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -3351878CB6DB6FB990F1DCDC7A36F7A49F12AF937D99C1F87A559F6AC3007B8A \ No newline at end of file +7FFAD4677A46F2886F05CE3650C2E2582DF96C39B6226E529DCBFFB99F2B38A6 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/.codegen-hash index 76fcf54066b..30e21edbe4e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -AF77BA19F270EAEF13D2B8C581B9D1103574C383AABD7AABB0D3927BF7801FC0 \ No newline at end of file +A2921DBBDABD3544002EACBA2573AE143FBB3FD3D7D0BFA2FEC8A11FC2E13866 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts index 9446ac6931e..a8868026843 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts @@ -4728,6 +4728,17 @@ export class PostgresDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/PostgresDatabaseResource.parent', + { context: this._handle } + ); + return new PostgresServerResource(handle, this._client); + }, + }; + /** Gets the DatabaseName property */ databaseName = { get: async (): Promise => { diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/.codegen-hash index e8bfafad6f2..4141bfa8922 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -98BC354B0DC55F4D19F751036068386535C90C0A8F0647F2C48D2AC90029ABC0 \ No newline at end of file +8ADF544AC385CCF07CF752C4E9F7A135B9F0CECA4635D12AF6EB15C737F2DE2C \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts index 53dbfcd983c..df5a794b523 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts @@ -1016,7 +1016,7 @@ export class DistributedApplicationBuilder { return new ProjectResourcePromise(this._addProjectInternal(name, projectPath, launchProfileName)); } - /** Exports AddQdrant for polyglot app hosts. */ + /** Adds a Qdrant resource to the application. A container is used for local development. */ /** @internal */ async _addQdrantInternal(name: string, apiKey?: ParameterResource, grpcPort?: number, httpPort?: number): Promise { const rpcArgs: Record = { builder: this._handle, name }; @@ -1082,7 +1082,7 @@ export class DistributedApplicationBuilderPromise implements PromiseLike obj.addProject(name, projectPath, launchProfileName))); } - /** Exports AddQdrant for polyglot app hosts. */ + /** Adds a Qdrant resource to the application. A container is used for local development. */ addQdrant(name: string, options?: AddQdrantOptions): QdrantServerResourcePromise { return new QdrantServerResourcePromise(this._promise.then(obj => obj.addQdrant(name, options))); } @@ -4250,7 +4250,7 @@ export class QdrantServerResource extends ResourceBuilderBase obj.getResourceName()); } - /** Exports WithDataVolume for polyglot app hosts. */ + /** Adds a named volume for the data folder to a Qdrant container resource. */ withDataVolume(options?: WithDataVolumeOptions): QdrantServerResourcePromise { return new QdrantServerResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); } - /** Exports WithDataBindMount for polyglot app hosts. */ + /** Adds a bind mount for the data folder to a Qdrant container resource. */ withDataBindMount(source: string, options?: WithDataBindMountOptions): QdrantServerResourcePromise { return new QdrantServerResourcePromise(this._promise.then(obj => obj.withDataBindMount(source, options))); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/.codegen-hash index 26717c19ebf..087e63640b0 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -D46E70A4332D618072CA12E59BDB1ADE9082D3D442FA6C9C2128953036128BBE \ No newline at end of file +CC54D2B8ADBA9E8FD2BDDA117218FE1F0ABF6B4FE976C5820F4B39DFC373A277 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/.codegen-hash index 2c9fdf6f0be..ceb25401862 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -1EC3B24A2350CB2FC0EC2A902C6B8CEC6496F4EF9290DF9B353851756FC0655D \ No newline at end of file +E063BD0399918036BD7F0299D3D39978681843C93FAC78DE5FA5D737736DB904 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/.codegen-hash index 0f9d9938bf1..0c0e67a5b2e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -4025E0CC985D0AE27323F2DD500D41BABFF46A65C6955E32F32639C8607E84CE \ No newline at end of file +32076E7F90672DBE77E9DF438AF4509906E4E43447CBFF5EADDC24D5DBAF8842 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/.codegen-hash index 01b65cfe95f..276190054c3 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -EDFBEC38EE7F0770429C21F0F2E7DB9AB092A5214A8C6ECD16CCD7A2C7C961AC \ No newline at end of file +5CEDDB7D0D546F29FDE53FB0BE683E7C54371B96868FC9621E1A9D07239776C8 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/.codegen-hash b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/.codegen-hash index a6791b3910a..3f500da6763 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/.codegen-hash +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/.codegen-hash @@ -1 +1 @@ -6A4C36EFAE4AA1BC45BD815CD4BA4047EC2213D97B566767BDB697E4E41603B7 \ No newline at end of file +513F5334C9946FFB8349794213DFF8EFFD049962CD2F39DD135991427C8F74E1 \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/transport.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/transport.ts index 6b9a4acae98..7bddd74beff 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/transport.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Yarp/ValidationAppHost/.modules/transport.ts @@ -263,10 +263,27 @@ export function registerCallback( if (argArray.length > 0) { // Spread positional arguments to callback - return await callback(...argArray); + const result = await callback(...argArray); + // DTO writeback protocol: when a void callback returns undefined, we + // return the original args object so the .NET host can detect property + // mutations made by the callback and apply them back to the original + // C# DTO objects. DTO args are plain JS objects (not Handle wrappers), + // so any property changes the callback made are reflected in args. + // + // Non-void callbacks (result !== undefined) return their actual result. + // The .NET side only activates writeback for void delegates whose + // parameters include [AspireDto] types — all other cases discard the + // returned args object, so the extra wire payload is harmless. + // + // IMPORTANT: callbacks that intentionally return undefined will also + // trigger this path. For non-void delegate types, the C# proxy uses + // a result-unmarshalling path (not writeback), so returning args will + // cause an unmarshal error. Void callbacks should never return a + // meaningful value; non-void callbacks should always return one. + return result !== undefined ? result : args; } - // No positional params found - call with no args + // No positional params found — nothing to write back return await callback(); } diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs index e0dd7001a6f..ca4a8a77714 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs @@ -1285,6 +1285,34 @@ public void Generate_MultiParamCallback_UsesPerPropertyTyping() Assert.Contains("{ p0: unknown, p1: unknown }", code); } + // ===== Options Interface Merging Tests ===== + + [Fact] + public void Generate_SameMethodNameOnDifferentTypes_MergesOptionsInterface() + { + // Regression test: When the same method name (e.g., withDataVolume) appears on + // multiple resource types with different optional parameters, the generated options + // interface must be the union of all parameters across all overloads. + // Previously, RegisterOptionsInterface used first-write-wins, so the interface + // only included parameters from whichever overload was registered first. + var code = GenerateTwoPassCode(); + + // There should be exactly one WithDataVolumeOptions interface + var interfaceCount = CountOccurrences(code, "export interface WithDataVolumeOptions"); + Assert.Equal(1, interfaceCount); + + // The interface must contain both 'name' (from both overloads) and 'isReadOnly' + // (only from the TestRedisResource overload) + var interfaceStart = code.IndexOf("export interface WithDataVolumeOptions", StringComparison.Ordinal); + Assert.True(interfaceStart >= 0, "WithDataVolumeOptions interface not found"); + + var interfaceEnd = code.IndexOf("}", interfaceStart, StringComparison.Ordinal); + var interfaceBody = code[interfaceStart..interfaceEnd]; + + Assert.Contains("name?:", interfaceBody); + Assert.Contains("isReadOnly?:", interfaceBody); + } + private static int CountOccurrences(string text, string pattern) { var count = 0; diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/AtsGeneratedAspire.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/AtsGeneratedAspire.verified.ts index a6be97c058f..036593c7b91 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/AtsGeneratedAspire.verified.ts +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/AtsGeneratedAspire.verified.ts @@ -130,6 +130,11 @@ export interface WaitForReadyAsyncOptions { cancellationToken?: AbortSignal; } +export interface WithDataVolumeOptions { + name?: string; + isReadOnly?: boolean; +} + export interface WithOptionalCallbackOptions { callback?: (arg: TestCallbackContext) => Promise; } @@ -753,6 +758,23 @@ export class TestDatabaseResource extends ResourceBuilderBase { + const rpcArgs: Record = { builder: this._handle }; + if (name !== undefined) rpcArgs.name = name; + const result = await this._client.invokeCapability( + 'Aspire.Hosting.CodeGeneration.TypeScript.Tests/withDataVolume', + rpcArgs + ); + return new TestDatabaseResource(result, this._client); + } + + /** Adds a data volume */ + withDataVolume(options?: WithDataVolumeOptions): TestDatabaseResourcePromise { + const name = options?.name; + return new TestDatabaseResourcePromise(this._withDataVolumeInternal(name)); + } + } /** @@ -845,6 +867,11 @@ export class TestDatabaseResourcePromise implements PromiseLike obj.withCancellableOperation(operation))); } + /** Adds a data volume */ + withDataVolume(options?: WithDataVolumeOptions): TestDatabaseResourcePromise { + return new TestDatabaseResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); + } + } // ============================================================================ @@ -1259,6 +1286,25 @@ export class TestRedisResource extends ResourceBuilderBase { + const rpcArgs: Record = { builder: this._handle }; + if (name !== undefined) rpcArgs.name = name; + if (isReadOnly !== undefined) rpcArgs.isReadOnly = isReadOnly; + const result = await this._client.invokeCapability( + 'Aspire.Hosting.CodeGeneration.TypeScript.Tests/withDataVolume', + rpcArgs + ); + return new TestRedisResource(result, this._client); + } + + /** Adds a data volume with persistence */ + withDataVolume(options?: WithDataVolumeOptions): TestRedisResourcePromise { + const name = options?.name; + const isReadOnly = options?.isReadOnly; + return new TestRedisResourcePromise(this._withDataVolumeInternal(name, isReadOnly)); + } + } /** @@ -1406,6 +1452,11 @@ export class TestRedisResourcePromise implements PromiseLike return new TestRedisResourcePromise(this._promise.then(obj => obj.withMultiParamHandleCallback(callback))); } + /** Adds a data volume with persistence */ + withDataVolume(options?: WithDataVolumeOptions): TestRedisResourcePromise { + return new TestRedisResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); + } + } // ============================================================================ diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts index 6e081485406..70bf1a3319f 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts @@ -359,6 +359,11 @@ export interface WithCommandOptions { commandOptions?: CommandOptions; } +export interface WithDataVolumeOptions { + name?: string; + isReadOnly?: boolean; +} + export interface WithDescriptionOptions { enableMarkdown?: boolean; } @@ -7490,6 +7495,25 @@ export class TestRedisResource extends ResourceBuilderBase { + const rpcArgs: Record = { builder: this._handle }; + if (name !== undefined) rpcArgs.name = name; + if (isReadOnly !== undefined) rpcArgs.isReadOnly = isReadOnly; + const result = await this._client.invokeCapability( + 'Aspire.Hosting.CodeGeneration.TypeScript.Tests/withDataVolume', + rpcArgs + ); + return new TestRedisResource(result, this._client); + } + + /** Adds a data volume with persistence */ + withDataVolume(options?: WithDataVolumeOptions): TestRedisResourcePromise { + const name = options?.name; + const isReadOnly = options?.isReadOnly; + return new TestRedisResourcePromise(this._withDataVolumeInternal(name, isReadOnly)); + } + } /** @@ -7832,6 +7856,11 @@ export class TestRedisResourcePromise implements PromiseLike return new TestRedisResourcePromise(this._promise.then(obj => obj.withMultiParamHandleCallback(callback))); } + /** Adds a data volume with persistence */ + withDataVolume(options?: WithDataVolumeOptions): TestRedisResourcePromise { + return new TestRedisResourcePromise(this._promise.then(obj => obj.withDataVolume(options))); + } + } // ============================================================================ diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TestTypes/TestExtensions.cs b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TestTypes/TestExtensions.cs index e6cf4e29d11..3917db570db 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TestTypes/TestExtensions.cs +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TestTypes/TestExtensions.cs @@ -648,6 +648,34 @@ public static IResourceBuilder WithMultiParamHandleCallback( return builder; } + // ===== Options Interface Merging Tests ===== + + /// + /// WithDataVolume on TestRedisResource — has both name and isReadOnly parameters. + /// Tests that options interfaces merge parameters across overloads targeting different types. + /// + [AspireExport("withDataVolume", Description = "Adds a data volume with persistence")] + public static IResourceBuilder WithDataVolume( + this IResourceBuilder builder, + string? name = null, + bool isReadOnly = false) + { + return builder; + } + + /// + /// WithDataVolume on TestDatabaseResource — has only name parameter. + /// When combined with the TestRedisResource overload, the generated WithDataVolumeOptions + /// interface must include both name and isReadOnly (the union of all parameters). + /// + [AspireExport("withDataVolume", Description = "Adds a data volume")] + public static IResourceBuilder WithDataVolume( + this IResourceBuilder builder, + string? name = null) + { + return builder; + } + // ===== Duplicate Class Name Tests ===== /// From cfc4f25a7ee50e435ebd9120c2425d0b3d4b5d39 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 4 Mar 2026 12:34:18 -0800 Subject: [PATCH 3/3] Add ExposeProperties to all IResourceWithConnectionString resources and include ConnectionStringExpression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add [AspireExport(ExposeProperties = true)] to 15 resource classes (Garnet, Valkey, Kafka, Milvus×2, MongoDB×2, MySql×2, Nats, Qdrant, RabbitMQ, Seq, SqlServer×2) - Remove [AspireExportIgnore] from ConnectionStringExpression on PostgreSQL and Oracle resources (4 files) to include connection strings in polyglot exports - Add [AspireExportIgnore] on Databases dictionaries (non-ATS types) for server resources - Regenerate TypeScript SDKs for all 14 affected integrations - Add connectionStringExpression.get() access in all validation apphost.ts files - All 14 apps pass TypeScript compilation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ValidationAppHost/.modules/aspire.ts | 97 +++++++++ .../ValidationAppHost/apphost.ts | 11 + .../ValidationAppHost/.modules/aspire.ts | 97 +++++++++ .../ValidationAppHost/apphost.ts | 7 + .../ValidationAppHost/.modules/aspire.ts | 164 +++++++++++++++ .../ValidationAppHost/apphost.ts | 9 + .../ValidationAppHost/.modules/aspire.ts | 175 ++++++++++++++++ .../ValidationAppHost/apphost.ts | 9 + .../ValidationAppHost/.modules/aspire.ts | 186 +++++++++++++++++ .../ValidationAppHost/apphost.ts | 9 + .../ValidationAppHost/.modules/aspire.ts | 108 ++++++++++ .../ValidationAppHost/apphost.ts | 8 + .../ValidationAppHost/.modules/aspire.ts | 22 ++ .../ValidationAppHost/apphost.ts | 2 + .../ValidationAppHost/.modules/aspire.ts | 22 ++ .../ValidationAppHost/apphost.ts | 2 + .../ValidationAppHost/.modules/aspire.ts | 152 ++++++++++++++ .../ValidationAppHost/apphost.ts | 12 ++ .../ValidationAppHost/.modules/aspire.ts | 119 +++++++++++ .../ValidationAppHost/apphost.ts | 9 + .../ValidationAppHost/.modules/aspire.ts | 97 +++++++++ .../ValidationAppHost/apphost.ts | 7 + .../ValidationAppHost/.modules/aspire.ts | 197 ++++++++++++++++++ .../ValidationAppHost/apphost.ts | 10 + .../ValidationAppHost/.modules/aspire.ts | 97 +++++++++ .../ValidationAppHost/apphost.ts | 7 + src/Aspire.Hosting.Garnet/GarnetResource.cs | 1 + .../KafkaServerResource.cs | 1 + .../MilvusDatabaseResource.cs | 1 + .../MilvusServerResource.cs | 1 + .../MongoDBDatabaseResource.cs | 1 + .../MongoDBServerResource.cs | 1 + .../MySqlDatabaseResource.cs | 1 + .../MySqlServerResource.cs | 1 + src/Aspire.Hosting.Nats/NatsServerResource.cs | 1 + .../OracleDatabaseResource.cs | 2 - .../OracleDatabaseServerResource.cs | 2 - .../PostgresDatabaseResource.cs | 2 - .../PostgresServerResource.cs | 2 - .../QdrantServerResource.cs | 1 + .../RabbitMQServerResource.cs | 1 + src/Aspire.Hosting.Seq/SeqResource.cs | 1 + .../SqlServerDatabaseResource.cs | 1 + .../SqlServerServerResource.cs | 1 + src/Aspire.Hosting.Valkey/ValkeyResource.cs | 1 + .../Snapshots/AtsGeneratedAspire.verified.go | 27 +++ ...TwoPassScanningGeneratedAspire.verified.go | 14 ++ .../AtsGeneratedAspire.verified.java | 23 ++ ...oPassScanningGeneratedAspire.verified.java | 13 ++ .../Snapshots/AtsGeneratedAspire.verified.py | 15 ++ ...TwoPassScanningGeneratedAspire.verified.py | 8 + .../Snapshots/AtsGeneratedAspire.verified.rs | 27 +++ ...TwoPassScanningGeneratedAspire.verified.rs | 15 ++ .../AtsTypeScriptCodeGeneratorTests.cs | 17 +- .../WithDataVolumeOptionsMerged.verified.ts | 4 + 55 files changed, 1802 insertions(+), 19 deletions(-) create mode 100644 tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/WithDataVolumeOptionsMerged.verified.ts diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts index 7493d322214..6bd2b573b66 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/.modules/aspire.ts @@ -2577,6 +2577,103 @@ export class GarnetResource extends ResourceBuilderBase { super(handle, client); } + /** Gets the PrimaryEndpoint property */ + primaryEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/GarnetResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/apphost.ts index f8008c25074..2f1cdf377fa 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Garnet/ValidationAppHost/apphost.ts @@ -1,4 +1,15 @@ import { createBuilder } from './.modules/aspire.js'; const builder = await createBuilder(); + +const cache = await builder.addGarnet("cache"); + +// ---- Property access on GarnetResource ---- +const garnet = await cache; +const _endpoint = await garnet.primaryEndpoint.get(); +const _host = await garnet.host.get(); +const _port = await garnet.port.get(); +const _uri = await garnet.uriExpression.get(); + +const _cstr = await garnet.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/aspire.ts index 4885338a1a6..b8e9a0bc44a 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/.modules/aspire.ts @@ -2582,6 +2582,103 @@ export class KafkaServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the InternalEndpoint property */ + internalEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.internalEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting/KafkaServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/apphost.ts index 9309fd1375e..73f03b27eb0 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Kafka/ValidationAppHost/apphost.ts @@ -24,4 +24,11 @@ await kafkaWithUi.withDataVolume(); const kafka2 = await builder.addKafka("broker2", { port: 19092 }); await kafka2.withDataBindMount("/tmp/kafka-data"); +// ---- Property access on KafkaServerResource ---- +const _endpoint = await kafka.primaryEndpoint.get(); +const _host = await kafka.host.get(); +const _port = await kafka.port.get(); +const _internal = await kafka.internalEndpoint.get(); + +const _cstr = await kafka.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/aspire.ts index 179677a0ced..39cde54694e 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/.modules/aspire.ts @@ -3490,6 +3490,48 @@ export class MilvusDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MilvusDatabaseResource.parent', + { context: this._handle } + ); + return new MilvusServerResource(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MilvusDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the DatabaseName property */ + databaseName = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MilvusDatabaseResource.databaseName', + { context: this._handle } + ); + }, + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MilvusDatabaseResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withUrlsCallbackInternal(callback: (obj: ResourceUrlsCallbackContext) => Promise): Promise { const callbackId = registerCallback(async (objData: unknown) => { @@ -3737,6 +3779,128 @@ export class MilvusServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Token property */ + token = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.token', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Databases property */ + private _databases?: AspireDict; + get databases(): AspireDict { + if (!this._databases) { + this._databases = new AspireDict( + this._handle, + this._client, + 'Aspire.Hosting.Milvus/MilvusServerResource.databases', + 'Aspire.Hosting.Milvus/MilvusServerResource.databases' + ); + } + return this._databases; + } + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.Milvus/MilvusServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/apphost.ts index 473f9fe21fb..ab4791ebda1 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Milvus/ValidationAppHost/apphost.ts @@ -66,4 +66,13 @@ await api.withReference(db); // ── 16. withReference: use Milvus server directly ────────────────────────── await api.withReference(milvus); +// ---- Property access on MilvusServerResource ---- +const _endpoint = await milvus.primaryEndpoint.get(); +const _host = await milvus.host.get(); +const _port = await milvus.port.get(); +const _token = await milvus.token.get(); +const _uri = await milvus.uriExpression.get(); + +const _cstr = await milvus.connectionStringExpression.get(); +const _databases = milvus.databases; await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/aspire.ts index 685aead6e87..951673205d5 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/.modules/aspire.ts @@ -2595,6 +2595,59 @@ export class MongoDBDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBDatabaseResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Parent property */ + parent = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBDatabaseResource.parent', + { context: this._handle } + ); + return new MongoDBServerResource(handle, this._client); + }, + }; + + /** Gets the DatabaseName property */ + databaseName = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBDatabaseResource.databaseName', + { context: this._handle } + ); + }, + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBDatabaseResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withUrlsCallbackInternal(callback: (obj: ResourceUrlsCallbackContext) => Promise): Promise { const callbackId = registerCallback(async (objData: unknown) => { @@ -2842,6 +2895,128 @@ export class MongoDBServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UserNameReference property */ + userNameReference = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.userNameReference', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Databases property */ + private _databases?: AspireDict; + get databases(): AspireDict { + if (!this._databases) { + this._databases = new AspireDict( + this._handle, + this._client, + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.databases', + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.databases' + ); + } + return this._databases; + } + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MongoDBServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/apphost.ts index 8f80a49195e..f327a7b8325 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MongoDB/ValidationAppHost/apphost.ts @@ -47,5 +47,14 @@ const mongoChained = await builder.addMongoDB("mongo-chained") await mongoChained.addDatabase("app-db"); await mongoChained.addDatabase("analytics-db", { databaseName: "analytics" }); +// ---- Property access on MongoDBServerResource ---- +const _endpoint = await mongo.primaryEndpoint.get(); +const _host = await mongo.host.get(); +const _port = await mongo.port.get(); +const _uri = await mongo.uriExpression.get(); +const _userName = await mongo.userNameReference.get(); + // Build and run the app +const _cstr = await mongo.connectionStringExpression.get(); +const _databases = mongo.databases; await builder.build().run(); \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/aspire.ts index 4a363adc7c7..4d0de54a583 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/.modules/aspire.ts @@ -2592,6 +2592,70 @@ export class MySqlDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.parent', + { context: this._handle } + ); + return new MySqlServerResource(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the JdbcConnectionString property */ + jdbcConnectionString = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.jdbcConnectionString', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the DatabaseName property */ + databaseName = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.databaseName', + { context: this._handle } + ); + }, + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlDatabaseResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withUrlsCallbackInternal(callback: (obj: ResourceUrlsCallbackContext) => Promise): Promise { const callbackId = registerCallback(async (objData: unknown) => { @@ -2859,6 +2923,128 @@ export class MySqlServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the JdbcConnectionString property */ + jdbcConnectionString = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.jdbcConnectionString', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Databases property */ + private _databases?: AspireDict; + get databases(): AspireDict { + if (!this._databases) { + this._databases = new AspireDict( + this._handle, + this._client, + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.databases', + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.databases' + ); + } + return this._databases; + } + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/MySqlServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/apphost.ts index 32af33c0ca5..11935c533a5 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.MySql/ValidationAppHost/apphost.ts @@ -20,4 +20,13 @@ await mysql.withPhpMyAdmin({ const db = await mysql.addDatabase('appdb', { databaseName: 'appdb' }); await db.withCreationScript('CREATE DATABASE IF NOT EXISTS appdb;'); +// ---- Property access on MySqlServerResource ---- +const _endpoint = await mysql.primaryEndpoint.get(); +const _host = await mysql.host.get(); +const _port = await mysql.port.get(); +const _uri = await mysql.uriExpression.get(); +const _jdbc = await mysql.jdbcConnectionString.get(); + +const _cstr = await mysql.connectionStringExpression.get(); +const _databases = mysql.databases; await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts index 755d0e19342..e2fe635069f 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/.modules/aspire.ts @@ -2576,6 +2576,114 @@ export class NatsServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UserNameReference property */ + userNameReference = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.userNameReference', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/NatsServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/apphost.ts index 236c7ccf932..fc95c441ec9 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Nats/ValidationAppHost/apphost.ts @@ -39,4 +39,12 @@ await consumer.withReference(nats); // withServiceReference — service discovery reference await consumer.withServiceReference(nats); +// ---- Property access on NatsServerResource ---- +const _endpoint = await nats.primaryEndpoint.get(); +const _host = await nats.host.get(); +const _port = await nats.port.get(); +const _uri = await nats.uriExpression.get(); +const _userName = await nats.userNameReference.get(); + +const _cstr = await nats.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/aspire.ts index 9fbd46dd96e..4d35fb39bf8 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/.modules/aspire.ts @@ -2586,6 +2586,17 @@ export class OracleDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/OracleDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + /** Gets the UriExpression property */ uriExpression = { get: async (): Promise => { @@ -2908,6 +2919,17 @@ export class OracleDatabaseServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/OracleDatabaseServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + /** Gets the UserNameReference property */ userNameReference = { get: async (): Promise => { diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/apphost.ts index cb50adcaa0d..83c0832a35a 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Oracle/ValidationAppHost/apphost.ts @@ -58,11 +58,13 @@ const _port = await oracle.port.get(); const _userNameRef = await oracle.userNameReference.get(); const _uri = await oracle.uriExpression.get(); const _jdbc = await oracle.jdbcConnectionString.get(); +const _cstr = await oracle.connectionStringExpression.get(); // ---- Property access on OracleDatabaseResource ---- const _dbName: string = await db.databaseName.get(); const _dbUri = await db.uriExpression.get(); const _dbJdbc = await db.jdbcConnectionString.get(); const _dbParent = await db.parent.get(); +const _dbCstr = await db.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts index a8868026843..90b1d002036 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/.modules/aspire.ts @@ -4739,6 +4739,17 @@ export class PostgresDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/PostgresDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + /** Gets the DatabaseName property */ databaseName = { get: async (): Promise => { @@ -6001,6 +6012,17 @@ export class PostgresServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/PostgresServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + /** Gets the Databases property */ private _databases?: AspireDict; get databases(): AspireDict { diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/apphost.ts index 9dd240c7bd1..08eebe93414 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.PostgreSQL/ValidationAppHost/apphost.ts @@ -48,10 +48,12 @@ const _endpoint = await postgres.primaryEndpoint.get(); const _nameRef = await postgres.userNameReference.get(); const _uri = await postgres.uriExpression.get(); const _jdbc = await postgres.jdbcConnectionString.get(); +const _cstr = await postgres.connectionStringExpression.get(); // ---- Property access on PostgresDatabaseResource ---- const _dbName: string = await db.databaseName.get(); const _dbUri = await db.uriExpression.get(); const _dbJdbc = await db.jdbcConnectionString.get(); +const _dbCstr = await db.connectionStringExpression.get(); await builder.build().run(); \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts index df5a794b523..13188ad52b5 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/.modules/aspire.ts @@ -3559,6 +3559,158 @@ export class QdrantServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the GrpcHost property */ + grpcHost = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.grpcHost', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the GrpcPort property */ + grpcPort = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.grpcPort', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the HttpEndpoint property */ + httpEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.httpEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the HttpHost property */ + httpHost = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.httpHost', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the HttpPort property */ + httpPort = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.httpPort', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the HttpConnectionStringExpression property */ + httpConnectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.httpConnectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the HttpUriExpression property */ + httpUriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.httpUriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/QdrantServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/apphost.ts index c64815aa41e..eb4e2f81bd8 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Qdrant/ValidationAppHost/apphost.ts @@ -3,4 +3,16 @@ import { createBuilder } from './.modules/aspire.js'; const builder = await createBuilder(); const qdrant = await builder.addQdrant('qdrant'); await qdrant.withDataVolume({ name: 'qdrant-data' }).withDataBindMount('.', { isReadOnly: true }); + +// ---- Property access on QdrantServerResource ---- +const _endpoint = await qdrant.primaryEndpoint.get(); +const _grpcHost = await qdrant.grpcHost.get(); +const _grpcPort = await qdrant.grpcPort.get(); +const _httpEndpoint = await qdrant.httpEndpoint.get(); +const _httpHost = await qdrant.httpHost.get(); +const _httpPort = await qdrant.httpPort.get(); +const _uri = await qdrant.uriExpression.get(); +const _httpUri = await qdrant.httpUriExpression.get(); + +const _cstr = await qdrant.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/aspire.ts index 5d7a8e6bf68..8591cc2aa92 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/.modules/aspire.ts @@ -3563,6 +3563,125 @@ export class RabbitMQServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the ManagementEndpoint property */ + managementEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.managementEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UserNameReference property */ + userNameReference = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.userNameReference', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/RabbitMQServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/apphost.ts index 16e66ed17d5..5bb17371064 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.RabbitMQ/ValidationAppHost/apphost.ts @@ -12,4 +12,13 @@ const rabbitmq2 = await builder .withDataVolume() .withManagementPluginWithPort({ port: 15673 }); +// ---- Property access on RabbitMQServerResource ---- +const _endpoint = await rabbitmq.primaryEndpoint.get(); +const _mgmtEndpoint = await rabbitmq.managementEndpoint.get(); +const _host = await rabbitmq.host.get(); +const _port = await rabbitmq.port.get(); +const _uri = await rabbitmq.uriExpression.get(); +const _userName = await rabbitmq.userNameReference.get(); + +const _cstr = await rabbitmq.connectionStringExpression.get(); await builder.build().run(); \ No newline at end of file diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/aspire.ts index 9b38e232a47..b42b3e1b190 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/.modules/aspire.ts @@ -3553,6 +3553,103 @@ export class SeqResource extends ResourceBuilderBase { super(handle, client); } + /** Gets the PrimaryEndpoint property */ + primaryEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SeqResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/apphost.ts index d13ded7ac2b..1ea9528ba22 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Seq/ValidationAppHost/apphost.ts @@ -9,4 +9,11 @@ await seq.withDataVolume(); await seq.withDataVolume({ name: "seq-data", isReadOnly: false }); await seq.withDataBindMount("./seq-data", { isReadOnly: true }); +// ---- Property access on SeqResource ---- +const _endpoint = await seq.primaryEndpoint.get(); +const _host = await seq.host.get(); +const _port = await seq.port.get(); +const _uri = await seq.uriExpression.get(); + +const _cstr = await seq.connectionStringExpression.get(); await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/aspire.ts index 50d06675e6f..0007e4212ad 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/.modules/aspire.ts @@ -3567,6 +3567,70 @@ export class SqlServerDatabaseResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.parent', + { context: this._handle } + ); + return new SqlServerServerResource(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the JdbcConnectionString property */ + jdbcConnectionString = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.jdbcConnectionString', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the DatabaseName property */ + databaseName = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.databaseName', + { context: this._handle } + ); + }, + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerDatabaseResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withUrlsCallbackInternal(callback: (obj: ResourceUrlsCallbackContext) => Promise): Promise { const callbackId = registerCallback(async (objData: unknown) => { @@ -3834,6 +3898,139 @@ export class SqlServerServerResource extends ResourceBuilderBase => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UserNameReference property */ + userNameReference = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.userNameReference', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the JdbcConnectionString property */ + jdbcConnectionString = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.jdbcConnectionString', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Databases property */ + private _databases?: AspireDict; + get databases(): AspireDict { + if (!this._databases) { + this._databases = new AspireDict( + this._handle, + this._client, + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.databases', + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.databases' + ); + } + return this._databases; + } + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/SqlServerServerResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/apphost.ts index 046e8daddc7..71f290927eb 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.SqlServer/ValidationAppHost/apphost.ts @@ -30,5 +30,15 @@ const sqlChained = await builder.addSqlServer("sql-chained") await sqlChained.addDatabase("db1"); await sqlChained.addDatabase("db2", { databaseName: "customdb2" }); +// ---- Property access on SqlServerServerResource ---- +const _endpoint = await sqlServer.primaryEndpoint.get(); +const _host = await sqlServer.host.get(); +const _port = await sqlServer.port.get(); +const _uri = await sqlServer.uriExpression.get(); +const _jdbc = await sqlServer.jdbcConnectionString.get(); +const _userName = await sqlServer.userNameReference.get(); + // Build and run the app +const _cstr = await sqlServer.connectionStringExpression.get(); +const _databases = sqlServer.databases; await builder.build().run(); diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/aspire.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/aspire.ts index ac6921aa5a7..90c1be77b17 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/aspire.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/.modules/aspire.ts @@ -3561,6 +3561,103 @@ export class ValkeyResource extends ResourceBuilderBase { super(handle, client); } + /** Gets the PrimaryEndpoint property */ + primaryEndpoint = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.primaryEndpoint', + { context: this._handle } + ); + return new EndpointReference(handle, this._client); + }, + }; + + /** Gets the Host property */ + host = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.host', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Port property */ + port = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.port', + { context: this._handle } + ); + return new EndpointReferenceExpression(handle, this._client); + }, + }; + + /** Gets the ConnectionStringExpression property */ + connectionStringExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.connectionStringExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the UriExpression property */ + uriExpression = { + get: async (): Promise => { + const handle = await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.uriExpression', + { context: this._handle } + ); + return new ReferenceExpression(handle, this._client); + }, + }; + + /** Gets the Entrypoint property */ + entrypoint = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.entrypoint', + { context: this._handle } + ); + }, + set: async (value: string): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.setEntrypoint', + { context: this._handle, value } + ); + } + }; + + /** Gets the ShellExecution property */ + shellExecution = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.shellExecution', + { context: this._handle } + ); + }, + set: async (value: boolean): Promise => { + await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.setShellExecution', + { context: this._handle, value } + ); + } + }; + + /** Gets the Name property */ + name = { + get: async (): Promise => { + return await this._client.invokeCapability( + 'Aspire.Hosting.ApplicationModel/ValkeyResource.name', + { context: this._handle } + ); + }, + }; + /** @internal */ private async _withBindMountInternal(source: string, target: string, isReadOnly?: boolean): Promise { const rpcArgs: Record = { builder: this._handle, source, target }; diff --git a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/apphost.ts b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/apphost.ts index 4ddc8d1bc14..cdd36196591 100644 --- a/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/apphost.ts +++ b/playground/polyglot/TypeScript/Aspire.Hosting.Valkey/ValidationAppHost/apphost.ts @@ -9,4 +9,11 @@ await valkey .withDataBindMount('.', { isReadOnly: true }) .withPersistence({ interval: 100000000, keysChangedThreshold: 1 }); +// ---- Property access on ValkeyResource ---- +const _endpoint = await valkey.primaryEndpoint.get(); +const _host = await valkey.host.get(); +const _port = await valkey.port.get(); +const _uri = await valkey.uriExpression.get(); + +const _cstr = await valkey.connectionStringExpression.get(); await builder.build().run(); diff --git a/src/Aspire.Hosting.Garnet/GarnetResource.cs b/src/Aspire.Hosting.Garnet/GarnetResource.cs index f13c2a4121f..55d95ee5794 100644 --- a/src/Aspire.Hosting.Garnet/GarnetResource.cs +++ b/src/Aspire.Hosting.Garnet/GarnetResource.cs @@ -7,6 +7,7 @@ namespace Aspire.Hosting.ApplicationModel; /// A resource that represents a Garnet resource independent of the hosting model. /// /// The name of the resource. +[AspireExport(ExposeProperties = true)] public class GarnetResource(string name) : ContainerResource(name), IResourceWithConnectionString { /// diff --git a/src/Aspire.Hosting.Kafka/KafkaServerResource.cs b/src/Aspire.Hosting.Kafka/KafkaServerResource.cs index bfd04519b4f..c9ac6d71a03 100644 --- a/src/Aspire.Hosting.Kafka/KafkaServerResource.cs +++ b/src/Aspire.Hosting.Kafka/KafkaServerResource.cs @@ -9,6 +9,7 @@ namespace Aspire.Hosting; /// A resource that represents a Kafka broker. /// /// The name of the resource. +[AspireExport(ExposeProperties = true)] public class KafkaServerResource(string name) : ContainerResource(name), IResourceWithConnectionString, IResourceWithEnvironment { // This endpoint is used for host processes Kafka broker communication. diff --git a/src/Aspire.Hosting.Milvus/MilvusDatabaseResource.cs b/src/Aspire.Hosting.Milvus/MilvusDatabaseResource.cs index ad0ac601c5f..185e7bbd390 100644 --- a/src/Aspire.Hosting.Milvus/MilvusDatabaseResource.cs +++ b/src/Aspire.Hosting.Milvus/MilvusDatabaseResource.cs @@ -15,6 +15,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The database name. /// The Milvus parent resource associated with this database. [DebuggerDisplay("Type = {GetType().Name,nq}, Name = {Name}, Database = {DatabaseName}")] +[AspireExport(ExposeProperties = true)] public class MilvusDatabaseResource(string name, string databaseName, MilvusServerResource parent) : Resource(name), IResourceWithParent, IResourceWithConnectionString { /// diff --git a/src/Aspire.Hosting.Milvus/MilvusServerResource.cs b/src/Aspire.Hosting.Milvus/MilvusServerResource.cs index a30316a141d..156b47b6914 100644 --- a/src/Aspire.Hosting.Milvus/MilvusServerResource.cs +++ b/src/Aspire.Hosting.Milvus/MilvusServerResource.cs @@ -8,6 +8,7 @@ namespace Aspire.Hosting.Milvus; /// /// A resource that represents a Milvus database. /// +[AspireExport(ExposeProperties = true)] public class MilvusServerResource : ContainerResource, IResourceWithConnectionString { internal const string PrimaryEndpointName = "grpc"; diff --git a/src/Aspire.Hosting.MongoDB/MongoDBDatabaseResource.cs b/src/Aspire.Hosting.MongoDB/MongoDBDatabaseResource.cs index ebd54c693f6..ab27ee239e5 100644 --- a/src/Aspire.Hosting.MongoDB/MongoDBDatabaseResource.cs +++ b/src/Aspire.Hosting.MongoDB/MongoDBDatabaseResource.cs @@ -14,6 +14,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The database name. /// The MongoDB server resource associated with this database. [DebuggerDisplay("Type = {GetType().Name,nq}, Name = {Name}, Database = {DatabaseName}")] +[AspireExport(ExposeProperties = true)] public class MongoDBDatabaseResource(string name, string databaseName, MongoDBServerResource parent) : Resource(name), IResourceWithParent, IResourceWithConnectionString { diff --git a/src/Aspire.Hosting.MongoDB/MongoDBServerResource.cs b/src/Aspire.Hosting.MongoDB/MongoDBServerResource.cs index de5faba3bc9..d49181b34ea 100644 --- a/src/Aspire.Hosting.MongoDB/MongoDBServerResource.cs +++ b/src/Aspire.Hosting.MongoDB/MongoDBServerResource.cs @@ -7,6 +7,7 @@ namespace Aspire.Hosting.ApplicationModel; /// A resource that represents a MongoDB container. /// /// The name of the resource. +[AspireExport(ExposeProperties = true)] public class MongoDBServerResource(string name) : ContainerResource(name), IResourceWithConnectionString { internal const string PrimaryEndpointName = "tcp"; diff --git a/src/Aspire.Hosting.MySql/MySqlDatabaseResource.cs b/src/Aspire.Hosting.MySql/MySqlDatabaseResource.cs index 28c7d46f9a9..3436c747a4a 100644 --- a/src/Aspire.Hosting.MySql/MySqlDatabaseResource.cs +++ b/src/Aspire.Hosting.MySql/MySqlDatabaseResource.cs @@ -15,6 +15,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The database name. /// The MySQL parent resource associated with this database. [DebuggerDisplay("Type = {GetType().Name,nq}, Name = {Name}, Database = {DatabaseName}")] +[AspireExport(ExposeProperties = true)] public class MySqlDatabaseResource(string name, string databaseName, MySqlServerResource parent) : Resource(name), IResourceWithParent, IResourceWithConnectionString { diff --git a/src/Aspire.Hosting.MySql/MySqlServerResource.cs b/src/Aspire.Hosting.MySql/MySqlServerResource.cs index b83275621d6..ddd77c97f03 100644 --- a/src/Aspire.Hosting.MySql/MySqlServerResource.cs +++ b/src/Aspire.Hosting.MySql/MySqlServerResource.cs @@ -6,6 +6,7 @@ namespace Aspire.Hosting.ApplicationModel; /// /// A resource that represents a MySQL container. /// +[AspireExport(ExposeProperties = true)] public class MySqlServerResource : ContainerResource, IResourceWithConnectionString { internal static string PrimaryEndpointName => "tcp"; diff --git a/src/Aspire.Hosting.Nats/NatsServerResource.cs b/src/Aspire.Hosting.Nats/NatsServerResource.cs index 79cbecbbb79..18e89a95521 100644 --- a/src/Aspire.Hosting.Nats/NatsServerResource.cs +++ b/src/Aspire.Hosting.Nats/NatsServerResource.cs @@ -7,6 +7,7 @@ namespace Aspire.Hosting.ApplicationModel; /// A resource that represents a NATS server container. /// /// The name of the resource. +[AspireExport(ExposeProperties = true)] public class NatsServerResource(string name) : ContainerResource(name), IResourceWithConnectionString { internal const string PrimaryEndpointName = "tcp"; diff --git a/src/Aspire.Hosting.Oracle/OracleDatabaseResource.cs b/src/Aspire.Hosting.Oracle/OracleDatabaseResource.cs index 1c9ae4488b5..abb2bd3ac64 100644 --- a/src/Aspire.Hosting.Oracle/OracleDatabaseResource.cs +++ b/src/Aspire.Hosting.Oracle/OracleDatabaseResource.cs @@ -26,8 +26,6 @@ public class OracleDatabaseResource(string name, string databaseName, OracleData /// /// Gets the connection string expression for the Oracle Database. /// - /// This property is not available in polyglot app hosts. - [AspireExportIgnore] public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"{Parent}/{DatabaseName}"); diff --git a/src/Aspire.Hosting.Oracle/OracleDatabaseServerResource.cs b/src/Aspire.Hosting.Oracle/OracleDatabaseServerResource.cs index 96f48442da9..50cbe935df3 100644 --- a/src/Aspire.Hosting.Oracle/OracleDatabaseServerResource.cs +++ b/src/Aspire.Hosting.Oracle/OracleDatabaseServerResource.cs @@ -48,8 +48,6 @@ public OracleDatabaseServerResource(string name, ParameterResource password) : b /// /// Gets the connection string expression for the Oracle Database server. /// - /// This property is not available in polyglot app hosts. - [AspireExportIgnore] public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create( $"user id={DefaultUserName};password={PasswordParameter};data source={PrimaryEndpoint.Property(EndpointProperty.HostAndPort)}"); diff --git a/src/Aspire.Hosting.PostgreSQL/PostgresDatabaseResource.cs b/src/Aspire.Hosting.PostgreSQL/PostgresDatabaseResource.cs index 2f480e9a628..a50619cbe5e 100644 --- a/src/Aspire.Hosting.PostgreSQL/PostgresDatabaseResource.cs +++ b/src/Aspire.Hosting.PostgreSQL/PostgresDatabaseResource.cs @@ -27,8 +27,6 @@ public class PostgresDatabaseResource(string name, string databaseName, Postgres /// /// Gets the connection string expression for the Postgres database. /// - /// This property is not available in polyglot app hosts. - [AspireExportIgnore] public ReferenceExpression ConnectionStringExpression { get diff --git a/src/Aspire.Hosting.PostgreSQL/PostgresServerResource.cs b/src/Aspire.Hosting.PostgreSQL/PostgresServerResource.cs index 7bc152d5820..e281a66a4c4 100644 --- a/src/Aspire.Hosting.PostgreSQL/PostgresServerResource.cs +++ b/src/Aspire.Hosting.PostgreSQL/PostgresServerResource.cs @@ -60,8 +60,6 @@ UserNameParameter is not null ? /// /// Gets the connection string expression for the PostgreSQL server. /// - /// This property is not available in polyglot app hosts. - [AspireExportIgnore] public ReferenceExpression ConnectionStringExpression { get diff --git a/src/Aspire.Hosting.Qdrant/QdrantServerResource.cs b/src/Aspire.Hosting.Qdrant/QdrantServerResource.cs index 39d27c97668..6d882cd6b6d 100644 --- a/src/Aspire.Hosting.Qdrant/QdrantServerResource.cs +++ b/src/Aspire.Hosting.Qdrant/QdrantServerResource.cs @@ -6,6 +6,7 @@ namespace Aspire.Hosting.ApplicationModel; /// /// A resource that represents a Qdrant database. /// +[AspireExport(ExposeProperties = true)] public class QdrantServerResource : ContainerResource, IResourceWithConnectionString { internal const string PrimaryEndpointName = "grpc"; diff --git a/src/Aspire.Hosting.RabbitMQ/RabbitMQServerResource.cs b/src/Aspire.Hosting.RabbitMQ/RabbitMQServerResource.cs index ae62db74252..e75272a3571 100644 --- a/src/Aspire.Hosting.RabbitMQ/RabbitMQServerResource.cs +++ b/src/Aspire.Hosting.RabbitMQ/RabbitMQServerResource.cs @@ -6,6 +6,7 @@ namespace Aspire.Hosting.ApplicationModel; /// /// A resource that represents a RabbitMQ resource. /// +[AspireExport(ExposeProperties = true)] public class RabbitMQServerResource : ContainerResource, IResourceWithConnectionString, IResourceWithEnvironment { internal const string PrimaryEndpointName = "tcp"; diff --git a/src/Aspire.Hosting.Seq/SeqResource.cs b/src/Aspire.Hosting.Seq/SeqResource.cs index d6b10a2329a..7df96f99554 100644 --- a/src/Aspire.Hosting.Seq/SeqResource.cs +++ b/src/Aspire.Hosting.Seq/SeqResource.cs @@ -7,6 +7,7 @@ namespace Aspire.Hosting.ApplicationModel; /// An Aspire resource that is a Seq server. /// /// The name of the Seq resource +[AspireExport(ExposeProperties = true)] public class SeqResource(string name) : ContainerResource(name), IResourceWithConnectionString { internal const string PrimaryEndpointName = "http"; diff --git a/src/Aspire.Hosting.SqlServer/SqlServerDatabaseResource.cs b/src/Aspire.Hosting.SqlServer/SqlServerDatabaseResource.cs index 969a88851a1..6257f98e0b1 100644 --- a/src/Aspire.Hosting.SqlServer/SqlServerDatabaseResource.cs +++ b/src/Aspire.Hosting.SqlServer/SqlServerDatabaseResource.cs @@ -15,6 +15,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The database name. /// The parent SQL Server server resource. [DebuggerDisplay("Type = {GetType().Name,nq}, Name = {Name}, Database = {DatabaseName}")] +[AspireExport(ExposeProperties = true)] public class SqlServerDatabaseResource(string name, string databaseName, SqlServerServerResource parent) : Resource(name), IResourceWithParent, IResourceWithConnectionString { diff --git a/src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs b/src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs index 6229310d44a..a597bcf4f6a 100644 --- a/src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs +++ b/src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs @@ -6,6 +6,7 @@ namespace Aspire.Hosting.ApplicationModel; /// /// A resource that represents a SQL Server container. /// +[AspireExport(ExposeProperties = true)] public class SqlServerServerResource : ContainerResource, IResourceWithConnectionString { internal const string PrimaryEndpointName = "tcp"; diff --git a/src/Aspire.Hosting.Valkey/ValkeyResource.cs b/src/Aspire.Hosting.Valkey/ValkeyResource.cs index b9f6e119446..21596e366c0 100644 --- a/src/Aspire.Hosting.Valkey/ValkeyResource.cs +++ b/src/Aspire.Hosting.Valkey/ValkeyResource.cs @@ -7,6 +7,7 @@ namespace Aspire.Hosting.ApplicationModel; /// A resource that represents a Valkey resource independent of the hosting model. /// /// The name of the resource. +[AspireExport(ExposeProperties = true)] public class ValkeyResource(string name) : ContainerResource(name), IResourceWithConnectionString { internal const string PrimaryEndpointName = "tcp"; diff --git a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/AtsGeneratedAspire.verified.go b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/AtsGeneratedAspire.verified.go index 51e2d384f81..76198193098 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/AtsGeneratedAspire.verified.go +++ b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/AtsGeneratedAspire.verified.go @@ -511,6 +511,19 @@ func (s *TestDatabaseResource) WithCancellableOperation(operation func(...any) a return result.(*IResource), nil } +// WithDataVolume adds a data volume +func (s *TestDatabaseResource) WithDataVolume(name string) (*TestDatabaseResource, error) { + reqArgs := map[string]any{ + "builder": SerializeValue(s.Handle()), + } + reqArgs["name"] = SerializeValue(name) + result, err := s.Client().InvokeCapability("Aspire.Hosting.CodeGeneration.Go.Tests/withDataVolume", reqArgs) + if err != nil { + return nil, err + } + return result.(*TestDatabaseResource), nil +} + // TestEnvironmentContext wraps a handle for Aspire.Hosting.CodeGeneration.Go.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestEnvironmentContext. type TestEnvironmentContext struct { HandleWrapperBase @@ -956,6 +969,20 @@ func (s *TestRedisResource) WithMultiParamHandleCallback(callback func(...any) a return result.(*TestRedisResource), nil } +// WithDataVolume adds a data volume with persistence +func (s *TestRedisResource) WithDataVolume(name string, isReadOnly bool) (*TestRedisResource, error) { + reqArgs := map[string]any{ + "builder": SerializeValue(s.Handle()), + } + reqArgs["name"] = SerializeValue(name) + reqArgs["isReadOnly"] = SerializeValue(isReadOnly) + result, err := s.Client().InvokeCapability("Aspire.Hosting.CodeGeneration.Go.Tests/withDataVolume", reqArgs) + if err != nil { + return nil, err + } + return result.(*TestRedisResource), nil +} + // TestResourceContext wraps a handle for Aspire.Hosting.CodeGeneration.Go.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext. type TestResourceContext struct { HandleWrapperBase diff --git a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go index 866433c963f..73fcc3e353e 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go +++ b/tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go @@ -5582,6 +5582,20 @@ func (s *TestRedisResource) WithMultiParamHandleCallback(callback func(...any) a return result.(*TestRedisResource), nil } +// WithDataVolume adds a data volume with persistence +func (s *TestRedisResource) WithDataVolume(name string, isReadOnly bool) (*TestRedisResource, error) { + reqArgs := map[string]any{ + "builder": SerializeValue(s.Handle()), + } + reqArgs["name"] = SerializeValue(name) + reqArgs["isReadOnly"] = SerializeValue(isReadOnly) + result, err := s.Client().InvokeCapability("Aspire.Hosting.CodeGeneration.Go.Tests/withDataVolume", reqArgs) + if err != nil { + return nil, err + } + return result.(*TestRedisResource), nil +} + // TestResourceContext wraps a handle for Aspire.Hosting.CodeGeneration.Go.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext. type TestResourceContext struct { HandleWrapperBase diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java index 4e21a9a71c7..b24fe84b254 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java @@ -412,6 +412,16 @@ public IResource withCancellableOperation(Function operation) return (IResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withCancellableOperation", reqArgs); } + /** Adds a data volume */ + public TestDatabaseResource withDataVolume(String name) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + if (name != null) { + reqArgs.put("name", AspireClient.serializeValue(name)); + } + return (TestDatabaseResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withDataVolume", reqArgs); + } + } /** Wrapper for Aspire.Hosting.CodeGeneration.Java.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestEnvironmentContext. */ @@ -707,6 +717,19 @@ public TestRedisResource withMultiParamHandleCallback(Function return (TestRedisResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withMultiParamHandleCallback", reqArgs); } + /** Adds a data volume with persistence */ + public TestRedisResource withDataVolume(String name, Boolean isReadOnly) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + if (name != null) { + reqArgs.put("name", AspireClient.serializeValue(name)); + } + if (isReadOnly != null) { + reqArgs.put("isReadOnly", AspireClient.serializeValue(isReadOnly)); + } + return (TestRedisResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withDataVolume", reqArgs); + } + } /** Wrapper for Aspire.Hosting.CodeGeneration.Java.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext. */ diff --git a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java index 94497b86860..38518a78240 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java +++ b/tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java @@ -4227,6 +4227,19 @@ public TestRedisResource withMultiParamHandleCallback(Function return (TestRedisResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withMultiParamHandleCallback", reqArgs); } + /** Adds a data volume with persistence */ + public TestRedisResource withDataVolume(String name, Boolean isReadOnly) { + Map reqArgs = new HashMap<>(); + reqArgs.put("builder", AspireClient.serializeValue(getHandle())); + if (name != null) { + reqArgs.put("name", AspireClient.serializeValue(name)); + } + if (isReadOnly != null) { + reqArgs.put("isReadOnly", AspireClient.serializeValue(isReadOnly)); + } + return (TestRedisResource) getClient().invokeCapability("Aspire.Hosting.CodeGeneration.Java.Tests/withDataVolume", reqArgs); + } + } /** Wrapper for Aspire.Hosting.CodeGeneration.Java.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext. */ diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/AtsGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/AtsGeneratedAspire.verified.py index 0cf7e213ee0..d84dd4677a6 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/AtsGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/AtsGeneratedAspire.verified.py @@ -296,6 +296,13 @@ def with_cancellable_operation(self, operation: Callable[[CancellationToken], No args["operation"] = operation_id return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withCancellableOperation", args) + def with_data_volume(self, name: str | None = None) -> TestDatabaseResource: + """Adds a data volume""" + args: Dict[str, Any] = { "builder": serialize_value(self._handle) } + if name is not None: + args["name"] = serialize_value(name) + return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withDataVolume", args) + class TestEnvironmentContext(HandleWrapperBase): def __init__(self, handle: Handle, client: AspireClient): @@ -523,6 +530,14 @@ def with_multi_param_handle_callback(self, callback: Callable[[TestCallbackConte args["callback"] = callback_id return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withMultiParamHandleCallback", args) + def with_data_volume(self, name: str | None = None, is_read_only: bool = False) -> TestRedisResource: + """Adds a data volume with persistence""" + args: Dict[str, Any] = { "builder": serialize_value(self._handle) } + if name is not None: + args["name"] = serialize_value(name) + args["isReadOnly"] = serialize_value(is_read_only) + return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withDataVolume", args) + class TestResourceContext(HandleWrapperBase): def __init__(self, handle: Handle, client: AspireClient): diff --git a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py index a783ab1a047..d80cae2cb05 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py +++ b/tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py @@ -3015,6 +3015,14 @@ def with_multi_param_handle_callback(self, callback: Callable[[TestCallbackConte args["callback"] = callback_id return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withMultiParamHandleCallback", args) + def with_data_volume(self, name: str | None = None, is_read_only: bool = False) -> TestRedisResource: + """Adds a data volume with persistence""" + args: Dict[str, Any] = { "builder": serialize_value(self._handle) } + if name is not None: + args["name"] = serialize_value(name) + args["isReadOnly"] = serialize_value(is_read_only) + return self._client.invoke_capability("Aspire.Hosting.CodeGeneration.Python.Tests/withDataVolume", args) + class TestResourceContext(HandleWrapperBase): def __init__(self, handle: Handle, client: AspireClient): diff --git a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/AtsGeneratedAspire.verified.rs b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/AtsGeneratedAspire.verified.rs index f37f76ac6a1..9fc7729a4dd 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/AtsGeneratedAspire.verified.rs +++ b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/AtsGeneratedAspire.verified.rs @@ -597,6 +597,18 @@ impl TestDatabaseResource { let handle: Handle = serde_json::from_value(result)?; Ok(IResource::new(handle, self.client.clone())) } + + /// Adds a data volume + pub fn with_data_volume(&self, name: Option<&str>) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + if let Some(ref v) = name { + args.insert("name".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting.CodeGeneration.Rust.Tests/withDataVolume", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(TestDatabaseResource::new(handle, self.client.clone())) + } } /// Wrapper for Aspire.Hosting.CodeGeneration.Rust.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestEnvironmentContext @@ -971,6 +983,21 @@ impl TestRedisResource { let handle: Handle = serde_json::from_value(result)?; Ok(TestRedisResource::new(handle, self.client.clone())) } + + /// Adds a data volume with persistence + pub fn with_data_volume(&self, name: Option<&str>, is_read_only: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + if let Some(ref v) = name { + args.insert("name".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = is_read_only { + args.insert("isReadOnly".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting.CodeGeneration.Rust.Tests/withDataVolume", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(TestRedisResource::new(handle, self.client.clone())) + } } /// Wrapper for Aspire.Hosting.CodeGeneration.Rust.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext diff --git a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs index 883d775f201..95b31be8710 100644 --- a/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs +++ b/tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs @@ -5496,6 +5496,21 @@ impl TestRedisResource { let handle: Handle = serde_json::from_value(result)?; Ok(TestRedisResource::new(handle, self.client.clone())) } + + /// Adds a data volume with persistence + pub fn with_data_volume(&self, name: Option<&str>, is_read_only: Option) -> Result> { + let mut args: HashMap = HashMap::new(); + args.insert("builder".to_string(), self.handle.to_json()); + if let Some(ref v) = name { + args.insert("name".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + if let Some(ref v) = is_read_only { + args.insert("isReadOnly".to_string(), serde_json::to_value(v).unwrap_or(Value::Null)); + } + let result = self.client.invoke_capability("Aspire.Hosting.CodeGeneration.Rust.Tests/withDataVolume", args)?; + let handle: Handle = serde_json::from_value(result)?; + Ok(TestRedisResource::new(handle, self.client.clone())) + } } /// Wrapper for Aspire.Hosting.CodeGeneration.Rust.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes.TestResourceContext diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs index ca4a8a77714..2ab0dab222d 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/AtsTypeScriptCodeGeneratorTests.cs @@ -1288,7 +1288,7 @@ public void Generate_MultiParamCallback_UsesPerPropertyTyping() // ===== Options Interface Merging Tests ===== [Fact] - public void Generate_SameMethodNameOnDifferentTypes_MergesOptionsInterface() + public async Task Generate_SameMethodNameOnDifferentTypes_MergesOptionsInterface() { // Regression test: When the same method name (e.g., withDataVolume) appears on // multiple resource types with different optional parameters, the generated options @@ -1297,20 +1297,15 @@ public void Generate_SameMethodNameOnDifferentTypes_MergesOptionsInterface() // only included parameters from whichever overload was registered first. var code = GenerateTwoPassCode(); - // There should be exactly one WithDataVolumeOptions interface - var interfaceCount = CountOccurrences(code, "export interface WithDataVolumeOptions"); - Assert.Equal(1, interfaceCount); - - // The interface must contain both 'name' (from both overloads) and 'isReadOnly' - // (only from the TestRedisResource overload) + // Extract just the WithDataVolumeOptions interface for snapshot verification. var interfaceStart = code.IndexOf("export interface WithDataVolumeOptions", StringComparison.Ordinal); - Assert.True(interfaceStart >= 0, "WithDataVolumeOptions interface not found"); + Assert.True(interfaceStart >= 0, "WithDataVolumeOptions interface not found in generated code"); var interfaceEnd = code.IndexOf("}", interfaceStart, StringComparison.Ordinal); - var interfaceBody = code[interfaceStart..interfaceEnd]; + var interfaceBody = code[interfaceStart..(interfaceEnd + 1)]; - Assert.Contains("name?:", interfaceBody); - Assert.Contains("isReadOnly?:", interfaceBody); + await Verify(interfaceBody, extension: "ts") + .UseFileName("WithDataVolumeOptionsMerged"); } private static int CountOccurrences(string text, string pattern) diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/WithDataVolumeOptionsMerged.verified.ts b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/WithDataVolumeOptionsMerged.verified.ts new file mode 100644 index 00000000000..0fab5cb4e64 --- /dev/null +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/WithDataVolumeOptionsMerged.verified.ts @@ -0,0 +1,4 @@ +export interface WithDataVolumeOptions { + name?: string; + isReadOnly?: boolean; +} \ No newline at end of file