Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions eng/scripts/doc-updater/knowledge/tcgc.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,26 @@ namespace (@clientNamespace), naming (@clientName), overload, structure (@client
## @clientLocation + scoped @client validation (July 2026)

- Bug fix in `src/validations/types.ts`: `@clientLocation` name-collision validation now skips operations that belong to an explicit `@client` scoped to a _different_ language than the scope being validated (`isClientForOtherScopeOnly`). Prevents false-positive collisions for `is`-derived operations inside a `@client(..., "java")` interface. Internal validation only — no user-facing doc change.

## SdkClientType.versionsEnum (July 2026)

- `SdkClientType` gained `versionsEnum?: SdkEnumType` (`src/interfaces.ts`, populated by `getVersionsEnum` in `src/clients.ts`). It is the API-versions enum for the client's service, `usage` includes `UsageFlags.ApiVersionEnum` (8), and it is the SAME object instance that appears in `SdkPackage.enums`.
Comment thread
tadelesh marked this conversation as resolved.
- `undefined` for unversioned services and for multi-service root clients (spanning >1 service). Sub-clients that map to a single service still get their own service's enum. Verified by `test/package/versioning.test.ts` ("client has versionsEnum reference", "multi-service client has no versionsEnum").
- Context caches these per-service via `__serviceToVersionsSdkEnum` and exposes `getPackageVersionSdkEnum(): Map<Namespace, SdkEnumType>` (emitter helper on `TCGCContext`/`SdkContext`).
- Documented in guideline.md "Client" section. Emitter-consumed type-graph metadata — no Spector spec needed.

## SSE Metadata (July 2026)

- `SdkBodyParameter` and `SdkMethodResponse` gained `sseMetadata?: SdkSseMetadata`, set ALONGSIDE `streamMetadata` when the body/response is a server-sent event stream (`text/event-stream`, `SSEStream`). `undefined` for non-event streams like JSONL. Built by `buildSdkSseMetadata` in `src/http.ts`.
- `SdkSseMetadata.events` is `SdkSseEventMetadata[]`, one entry per variant of the streamed `@events` union. Derived from `@typespec/events` event definitions plus the `@typespec/sse` `@terminalEvent` marker. Fields: `eventType?` (SSE `event:` name from named variant; undefined→`message` event), `isTerminalEvent`, `isEventEnvelope`, `type`/`contentType`, `payloadType`/`payloadContentType`. When `isEventEnvelope` is false, `type`==`payloadType` and content types match.
- Kept separate from `SdkStreamMetadata` because SSE, streaming, and events are modeled by three distinct TypeSpec libraries (`@typespec/sse`, `@typespec/http`, `@typespec/events`).
- Documented in guideline.md "Operation" section under a new "Streaming and Server-Sent Events" subsection (also introduced `streamMetadata` documentation there, which was previously undocumented). Tests: `test/methods/sse.test.ts`, `test/methods/streams.test.ts`. Emitter type-graph metadata — no Spector spec needed.

## no-unnamed-types Linter Rule REMOVED (July 2026)

- The `no-unnamed-types` rule was removed from `src/linter.ts` (both the rules array and `no-unnamed-types.rule.ts` / `.md` deleted). `reference/linter.md` no longer lists it — already consistent. Do NOT re-add it.
Comment thread
tadelesh marked this conversation as resolved.
- Rule source files were renamed to the `<name>.rule.ts` convention (e.g. `property-name-conflict.ts` → `property-name-conflict.rule.ts`); `csharp-no-url-suffix` still uses `.ts`.

## Diagnostic Messages Externalized (July 2026)

- Diagnostic message definitions were moved out of `src/lib.ts` into individual `src/diagnostics/<name>.md` files (loaded at build). Purely an authoring refactor; reference docs regenerate the same content. No user-facing doc action.
4 changes: 2 additions & 2 deletions eng/scripts/doc-updater/knowledge/tcgc.meta.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"lastCommit": "5036a37233b303fd4c59afc2c6c8ffc6d6194e3a",
"lastUpdated": "2026-07-20T10:09:56.076Z",
"lastCommit": "51207aabe222d09901be75e5c7cd3bcb10464f51",
"lastUpdated": "2026-07-27T10:27:10.237Z",
"analyzedPaths": [
"packages/typespec-client-generator-core/src",
"packages/typespec-client-generator-core/lib",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ llmstxt: true
import { ClientTabs, ClientTabItem } from "@components/client-tabs";

This page documents the default generation logic for client's basic method as well as how to customize it.
For advanced paging, long-running or multipart operations, see the [Paging Operations](../05pagingoperations/), [Long-Running Operations](../06longrunningoperations/) and [Multipart Operations](../07multipart/) pages.
For advanced paging, long-running, multipart or streaming operations, see the [Paging Operations](../05pagingoperations/), [Long-Running Operations](../06longrunningoperations/), [Multipart Operations](../07multipart/) and [Streaming Operations](../13streaming/) pages.

## Convenience and protocol methods

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
---
title: Streaming Operations
llmstxt: true
---

import { ClientTabs, ClientTabItem } from "@components/client-tabs";
Comment thread
lirenhe marked this conversation as resolved.

This doc explains how to author TypeSpec streaming operations and what client emitters will generate for them.

TypeSpec supports three kinds of streaming bodies via the `@typespec/streams`, `@typespec/sse`, and `@typespec/events` packages:

- **`JsonlStream<T>`** — a newline-delimited JSON stream where every line is a JSON-encoded `T`.
- **`SSEStream<TEvents>`** — a server-sent event stream (`text/event-stream`) carrying heterogeneous events described by an `@events` union.
- **`HttpStream<T, ContentType>`** — a generic streaming body; using `ContentType = "text/event-stream"` with an `@events` union activates the same SSE metadata as `SSEStream`.

:::note
Import the required packages in your TypeSpec spec:

```tsp
import "@typespec/streams";
import "@typespec/sse";
import "@typespec/events";
```
:::

## JSONL streaming response

Use `JsonlStream<T>` when your service responds with newline-delimited JSON lines, each encoding a value of type `T`.

<ClientTabs>

```typespec
import "@typespec/streams";

using TypeSpec.Streams;

model Chunk {
text: string;
}

@post
op generate(@body request: { prompt: string }): JsonlStream<Chunk>;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

## SSE streaming response

Use `SSEStream<TEvents>` to define a server-sent event stream. Events are described by a union decorated with `@Events.events`. Each named variant becomes an SSE event type; unnamed variants produce `message` events.

<ClientTabs>

```typespec
import "@typespec/sse";
import "@typespec/events";

using TypeSpec.SSE;
using TypeSpec.Events;

model ResponseCreated {
id: string;
}

model ResponseDelta {
delta: string;
}

@events
union ResponseEvents {
@contentType("application/json")
responseCreated: ResponseCreated,

@contentType("application/json")
responseDelta: ResponseDelta,

@contentType("text/plain")
@terminalEvent
"[DONE]",
}

@post
op stream(@body request: { prompt: string }): SSEStream<ResponseEvents>;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

### Terminal events

Mark a variant with `@terminalEvent` to indicate that receiving this event signals the end of the stream. Client emitters use this to know when to stop reading and close the connection.

<ClientTabs>

```typespec
import "@typespec/sse";
import "@typespec/events";

using TypeSpec.SSE;
using TypeSpec.Events;

model DeltaEvent {
delta: string;
}

@events
union ChatEvents {
@contentType("application/json")
delta: DeltaEvent,

@contentType("text/plain")
@terminalEvent
"[DONE]",
}

@post
op chat(@body request: { message: string }): SSEStream<ChatEvents>;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

### Event envelopes

When your SSE event wraps a separate payload, use `@Events.data` on the payload property to distinguish the envelope from its content. Emitters expose both the envelope type and the unwrapped payload type through `sseMetadata`.

<ClientTabs>

```typespec
import "@typespec/sse";
import "@typespec/events";

using TypeSpec.SSE;
using TypeSpec.Events;

model ChatMessage {
role: string;
content: string;
}

@events
union ChatEvents {
@contentType("application/json")
message: { done: false, @data message: ChatMessage },

@contentType("text/plain")
@terminalEvent
done: { done: true },
}

@post
op chat(@body request: { message: string }): SSEStream<ChatEvents>;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

### Unnamed (message) events

A variant without a name in the union produces an SSE `message` event — the `event:` field is omitted on the wire. Use this when your service emits a single homogeneous event type.

<ClientTabs>

```typespec
import "@typespec/sse";
import "@typespec/events";

using TypeSpec.SSE;
using TypeSpec.Events;

model Info {
desc: string;
}

@events
union BasicEvents {
@contentType("application/json")
Info,
}

op receive(): SSEStream<BasicEvents>;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

## SSE streaming request

`SSEStream` can also be used as a request body when the client sends an event stream to the service.

<ClientTabs>

```typespec
import "@typespec/sse";
import "@typespec/events";

using TypeSpec.SSE;
using TypeSpec.Events;

model InputEvent {
data: string;
}

@events
union InputEvents {
@contentType("application/json")
inputEvent: InputEvent,
}

op send(stream: SSEStream<InputEvents>): void;
```

```python
# unsupported
```

```csharp
// unsupported
```

```typescript
// unsupported
```

```java
// unsupported
```

```go
// unsupported
```

</ClientTabs>

## Custom streaming body

For content types other than JSONL or `text/event-stream`, define a model with `@streamOf` and a `@body bytes` property.

```typespec
import "@typespec/streams";

using TypeSpec.Streams;

model AudioChunk { id: string; }

@streamOf(AudioChunk)
model AudioStream {
@header contentType: "audio/mpeg";
@body body: bytes;
}

op synthesize(@body request: { text: string }): AudioStream;
```
Loading
Loading