From 8b40a5a8c6cedceb6f631786b667357e182fc01b Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 14:42:22 -0700 Subject: [PATCH 01/15] feat(http-client-python): generate structured JSONL/SSE streaming (Azure flavor) Operations whose HTTP response is a JSONL (application/jsonl) or SSE (text/event-stream) stream now generate client methods returning Stream[T] / AsyncStream[T] that yield deserialized payloads instead of raw bytes, driven by the TCGC response stream metadata (no opt-in option). The unbranded flavor keeps the raw byte-iterator behavior. The Stream/AsyncStream runtime plus JSONL/SSE decoders are vendored into the generated package at _utils/streaming_base.py, so they depend only on the released azure.core.rest (no unreleased azure.core.streaming). Heterogeneous SSE terminal-event termination is supported without TCGC sseMetadata: the terminal marker (e.g. "[DONE]") is a string-literal member of the item union, detected structurally and wired into the runtime as terminal_event so iteration stops before parsing it. Coverage: emitter unit tests (streaming.test.ts), pygen unit tests (test_structured_streaming_response.py), and azure mock_api tests (JSONL + SSE homogeneous + SSE heterogeneous, sync + async) plus unbranded byte-iterator tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../changes/structured-streaming-2026-0-0.md | 24 + packages/http-client-python/README.md | 25 + .../http-client-python/emitter/src/http.ts | 64 ++ .../emitter/test/streaming.test.ts | 20 + .../pygen/codegen/models/code_model.py | 17 + .../pygen/codegen/models/operation.py | 9 + .../pygen/codegen/models/response.py | 78 +++ .../pygen/codegen/serializers/__init__.py | 7 + .../codegen/serializers/builder_serializer.py | 46 ++ .../codegen/serializers/general_serializer.py | 4 + .../templates/streaming_base.py.jinja2 | 549 ++++++++++++++++++ packages/http-client-python/package-lock.json | 20 +- packages/http-client-python/package.json | 4 +- .../azure/test_streaming_structured.py | 151 +++++ .../asynctests/test_streaming_jsonl_async.py | 5 - .../mock_api/shared/test_streaming_jsonl.py | 4 - .../test_streaming_jsonl_unbranded_async.py | 28 + .../test_streaming_jsonl_unbranded.py | 30 + .../test_structured_streaming_response.py | 168 ++++++ 19 files changed, 1233 insertions(+), 20 deletions(-) create mode 100644 .chronus/changes/structured-streaming-2026-0-0.md create mode 100644 packages/http-client-python/emitter/test/streaming.test.ts create mode 100644 packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 create mode 100644 packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py create mode 100644 packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py create mode 100644 packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py create mode 100644 packages/http-client-python/tests/unit/test_structured_streaming_response.py diff --git a/.chronus/changes/structured-streaming-2026-0-0.md b/.chronus/changes/structured-streaming-2026-0-0.md new file mode 100644 index 00000000000..41069aaf26d --- /dev/null +++ b/.chronus/changes/structured-streaming-2026-0-0.md @@ -0,0 +1,24 @@ +--- +changeKind: feature +packages: + - "@typespec/http-client-python" +--- + +Generate structured streaming client methods for the **Azure flavor**: operations whose HTTP response is a JSONL (`application/jsonl`) or SSE (`text/event-stream`) stream now return `Stream[T]` / `AsyncStream[T]`, yielding deserialized model instances instead of raw bytes. This is driven entirely by the TCGC response stream metadata (the response stream type) — there is no opt-in emitter option. The unbranded flavor keeps the existing raw byte-iterator behavior (`Iterator[bytes]` / `AsyncIterator[bytes]`). + +Note: for the Azure flavor this changes the return type of JSONL/SSE streaming operations from a raw byte iterator to `Stream[T]` / `AsyncStream[T]`. + +The `Stream` / `AsyncStream` runtime (plus the JSONL / SSE decoders) is vendored into the generated package at `_utils/streaming_base.py` (like `_utils/model_base.py`), so it depends only on the released `azure.core.rest` — not on an unreleased `azure.core.streaming`. + +```python +# For an operation returning JsonlStream (Azure flavor): +stream = client.receive() # -> Stream[Thing] +for thing in stream: # deserialized model instances + ... +``` + +Known limitations / follow-ups: + +- SSE union item types deserialize to parsed JSON (e.g. `dict`) rather than model instances — same root cause as paging item deserialization; the shared `_deserialize` needs a `module` argument to resolve forward-reference union member names. +- Heterogeneous SSE **terminal-event** handling is supported: the terminal marker (e.g. `"[DONE]"`) is detected structurally as a string-literal member of the item union and passed to the vendored `Stream` / `AsyncStream` as `terminal_event`, so iteration stops before parsing it. Per-event **model dispatch** (routing each `@events` event to its distinct payload model) is still blocked on TCGC `sseMetadata` (typespec-client-generator-core #4882), absent from the resolved TCGC version; until then heterogeneous events are yielded as parsed JSON. +- In-repo mock_api coverage: JSONL homogeneous (sync + async) is active against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`) and heterogeneous (`named/receive`, terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded event payloads (as `dict`s per the union-deserialization limitation). diff --git a/packages/http-client-python/README.md b/packages/http-client-python/README.md index 0c3335df5fa..14ee885d1cd 100644 --- a/packages/http-client-python/README.md +++ b/packages/http-client-python/README.md @@ -153,3 +153,28 @@ Whether to clear the output folder before generating the code. Defaults to `fals **Type:** `boolean` Emit YAML code model only, without running Python generator. For batch processing. + +## Structured streaming (JSONL / SSE) + +For the **Azure flavor**, operations whose HTTP response is a JSONL (`application/jsonl`) or SSE (`text/event-stream`) stream generate client methods that return `Stream[T]` (sync) / `AsyncStream[T]` (async), yielding deserialized model instances instead of raw bytes. This is driven by the TCGC response stream metadata (the response stream type) — there is no opt-in emitter option. For the unbranded flavor, streaming responses keep the existing raw byte-iterator behavior (`Iterator[bytes]` / `AsyncIterator[bytes]`). + +For an operation returning `JsonlStream`, the generated method returns `Stream[Thing]` (sync) / `AsyncStream[Thing]` (async), yielding deserialized `Thing` instances as each JSONL line arrives. Similarly, `SSEStream` produces a `Stream` / `AsyncStream` over the SSE event payloads. + +```python +# For an operation returning JsonlStream (Azure flavor): +stream = client.receive() # -> Stream[Thing] +for thing in stream: # deserialized model instances + ... +``` + +The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vendored** into the generated package at `_utils/streaming_base.py` (alongside `_utils/model_base.py`). It depends only on the released `azure.core.rest`, so no unreleased `azure.core.streaming` (azure-core PR #48077) dependency is required at runtime. + +> **Note:** For SSE responses whose item type is a union (`@events`), each event payload is currently yielded as the parsed JSON value (e.g. a `dict` for object payloads, or the literal for terminal events such as `"[DONE]"`) rather than a fully deserialized model instance. This mirrors the existing union item-deserialization behavior used elsewhere in the generator. JSONL responses with a single model item type are deserialized into model instances. + +#### Known limitations / follow-ups + +- **SSE union item deserialization** — SSE item types are `@events` unions, so each event is deserialized against a forward-reference union member name and yielded as the parsed JSON value rather than a model instance. This shares a root cause with paging item deserialization: the shared `_deserialize` helper needs a `module` argument to resolve the union member names into concrete model classes. JSONL (single model item type) is unaffected and fully deserializes. +- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event *model dispatch* — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. + + Investigation (2026-08): `sseMetadata` is **not** present in the resolved TCGC `0.69.1`, **nor in `0.70.0`** (latest stable — its `SdkStreamMetadata` is byte-identical to 0.69.1, no SSE symbols). `SdkSseMetadata` (`events[]` per `@events` union variant, built by `buildSdkSseMetadata`) has since landed upstream on `Azure/typespec-azure` `main` and first appears in the `next` prerelease line (`0.71.0-dev.11`). Adopting it requires the `@typespec` 1.14 / 0.84 family bump those versions carry. Terminal-event termination does **not** depend on it (handled structurally, see above); only per-event model dispatch does. +- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded event payloads (as `dict`s, per the union-deserialization limitation) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts index 31d970d4b65..b1788dc21dd 100644 --- a/packages/http-client-python/emitter/src/http.ts +++ b/packages/http-client-python/emitter/src/http.ts @@ -42,6 +42,69 @@ export enum ReferredByOperationTypes { NonPagingOnly = 2, } +/** + * Determine whether a stream's payload type is "structured" (a model or union + * that can be deserialized into an item `T`), as opposed to a bare byte/string + * stream that should keep the existing raw byte-iterator behavior. + */ +export function isStructuredStreamType(type: SdkType): boolean { + switch (type.kind) { + case "model": + case "union": + return true; + case "nullable": + return isStructuredStreamType(type.type); + default: + return false; + } +} + +/** + * Build the `streaming` block for a response YAML when the response is a JSONL/SSE + * stream with a structured payload type (driven by the TCGC stream metadata). + * + * Returns `undefined` when structured streaming should not apply, in which case + * the existing raw byte-iterator behavior is preserved. + * + * Note: the currently consumed TCGC metadata (`streamMetadata`) does not expose + * per-event SSE metadata (event-type dispatch). Terminal-event handling does NOT + * depend on it — the terminal marker is a string-literal member of the item union + * (e.g. `Literal["[DONE]"]`), which the generator detects structurally and passes + * to the vendored runtime as `terminal_event`. Only `kind` and `itemType` are + * emitted here; the terminal event is derived generator-side from `itemType`. + */ +function getStreamingInfo( + context: PythonSdkContext, + response: SdkHttpResponse | SdkHttpErrorResponse, + method?: SdkServiceMethod, +): Record | undefined { + // Structured streaming targets the vendored `azure.core.rest`-based runtime, so + // it only applies to the Azure flavor. For unbranded, keep the raw byte-iterator + // behavior. + if ((context.emitContext.options as any).flavor !== "azure") return undefined; + // Request-body streaming is out of scope: operations that carry a request body are + // kept on the raw byte-iterator path. This also avoids the per-request-content-type + // overloads (whose response item types are serialized inline rather than registered + // globally) producing an inconsistent mix of `Stream[T]` and `Iterator[bytes]`. + if (method?.operation.bodyParam) return undefined; + const streamMetadata = response.streamMetadata; + if (!streamMetadata) return undefined; + if (!isStructuredStreamType(streamMetadata.streamType)) return undefined; + const contentTypes = streamMetadata.contentTypes ?? response.contentTypes ?? []; + const isSse = contentTypes.some((ct) => ct.toLowerCase().includes("event-stream")); + // SSE kind is detected from the response Content-Type. A heterogeneous `@events` + // union streamType is emitted as a single union `itemType`; the generator detects + // the terminal event (a string-literal union member such as `[DONE]`) structurally + // and wires it into the runtime, so terminal-event termination works without TCGC + // `sseMetadata`. Per-event MODEL dispatch (routing each event to its distinct + // payload model) still requires `sseMetadata` (SdkSseMetadata.events[], TCGC + // #4882); until then heterogeneous events are yielded as parsed JSON. + return { + kind: isSse ? "sse" : "jsonl", + itemType: getType(context, streamMetadata.streamType), + }; +} + function isEtagType(type: SdkType): boolean { if (type.kind === "nullable") return isEtagType(type.type); const raw = type.__raw; @@ -682,6 +745,7 @@ function emitHttpResponse( "invalid-lro-result", method, ), + streaming: isException ? undefined : getStreamingInfo(context, response, method), }; } diff --git a/packages/http-client-python/emitter/test/streaming.test.ts b/packages/http-client-python/emitter/test/streaming.test.ts new file mode 100644 index 00000000000..ec5c6a078e4 --- /dev/null +++ b/packages/http-client-python/emitter/test/streaming.test.ts @@ -0,0 +1,20 @@ +import { strictEqual } from "assert"; +import { describe, it } from "vitest"; +import { isStructuredStreamType } from "../src/http.js"; + +describe("typespec-python: structured streaming", () => { + it("treats model and union payloads as structured", () => { + strictEqual(isStructuredStreamType({ kind: "model" } as any), true); + strictEqual(isStructuredStreamType({ kind: "union" } as any), true); + }); + + it("unwraps nullable payloads", () => { + strictEqual(isStructuredStreamType({ kind: "nullable", type: { kind: "model" } } as any), true); + strictEqual(isStructuredStreamType({ kind: "nullable", type: { kind: "bytes" } } as any), false); + }); + + it("treats bare byte/string payloads as unstructured", () => { + strictEqual(isStructuredStreamType({ kind: "bytes" } as any), false); + strictEqual(isStructuredStreamType({ kind: "string" } as any), false); + }); +}); diff --git a/packages/http-client-python/generator/pygen/codegen/models/code_model.py b/packages/http-client-python/generator/pygen/codegen/models/code_model.py index 73cd410eb84..c1e0bb1ee6b 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/code_model.py +++ b/packages/http-client-python/generator/pygen/codegen/models/code_model.py @@ -279,12 +279,29 @@ def need_utils_folder(self, async_mode: bool, client_namespace: str) -> bool: self.need_utils_utils(async_mode, client_namespace) or self.need_utils_serialization or self.options["models-mode"] == "dpg" + or self.need_streaming_base ) @property def need_utils_serialization(self) -> bool: return not self.options["client-side-validation"] + @property + def has_structured_stream(self) -> bool: + return any( + op.has_structured_stream_response + for client in self.clients + for og in client.operation_groups + for op in og.operations + ) + + @property + def need_streaming_base(self) -> bool: + # Whether to emit the vendored ``_utils/streaming_base.py`` (Stream / AsyncStream + # + JSONL / SSE decoders). Only needed when at least one operation returns a + # structured stream. + return self.has_structured_stream + def need_utils_utils(self, async_mode: bool, client_namespace: str) -> bool: return ( self.need_utils_form_data(async_mode, client_namespace) diff --git a/packages/http-client-python/generator/pygen/codegen/models/operation.py b/packages/http-client-python/generator/pygen/codegen/models/operation.py index 3c6d525f122..b2fb243fef9 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/operation.py +++ b/packages/http-client-python/generator/pygen/codegen/models/operation.py @@ -98,12 +98,21 @@ def exact_name_params(self) -> set[str]: @property def stream_value(self) -> Union[str, bool]: + # Structured streams (JSONL / SSE) must always run the pipeline with + # stream=True so the body can be consumed incrementally by Stream/AsyncStream. + if self.has_structured_stream_response: + return True return ( f'kwargs.pop("stream", {self.has_stream_response})' if self.expose_stream_keyword and self.has_response_body and "stream" not in self.exact_name_params else self.has_stream_response ) + @property + def has_structured_stream_response(self) -> bool: + """Whether any success response is a structured (JSONL / SSE) stream returning Stream[T].""" + return any(getattr(r, "is_structured_stream", False) for r in self.responses) + @property def has_form_data_body(self): return self.parameters.has_form_data_body diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 99a90481319..5d7b0a6897b 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -58,6 +58,16 @@ def __init__( self.type = type self.nullable = yaml_data.get("nullable") self.default_content_type = yaml_data.get("defaultContentType") + # Structured streaming (JSONL / SSE) metadata. When present, ``self.type`` holds the + # per-item type (model or union) rather than the raw byte body, and the response is + # rendered as ``Stream[Item]`` / ``AsyncStream[Item]``. + streaming = yaml_data.get("streaming") + # Only treat this as a structured stream when the resolved ``type`` is the per-item + # type (model / union). When the structured item type could not be resolved we fall + # back to the raw byte body (``BinaryIteratorType``) and must NOT render ``Stream[...]``. + self.streaming_kind: Optional[str] = ( + streaming["kind"] if streaming and not isinstance(self.type, BinaryIteratorType) else None + ) @property def result_property(self) -> str: @@ -92,12 +102,45 @@ def is_stream_response(self) -> bool: ) return retval + @property + def is_structured_stream(self) -> bool: + """Is the response a structured (JSONL / SSE) stream rendered as Stream[T] / AsyncStream[T].""" + return self.streaming_kind is not None + + @property + def terminal_event(self) -> Optional[str]: + """Terminal event marker for a heterogeneous SSE stream, if any. + + Heterogeneous SSE ``@events`` unions include a string-literal member (e.g. + ``"[DONE]"``) that marks the end of the stream. Without TCGC ``sseMetadata`` + (#4882) we detect it structurally: the first ``ConstantType`` string member of + the union item type is treated as the terminal marker, so the runtime can stop + before attempting to JSON-deserialize it. Returns ``None`` for homogeneous + streams (no constant member) and for JSONL. + """ + if self.streaming_kind != "sse" or not isinstance(self.type, CombinedType): + return None + from .constant_type import ConstantType + + for member in self.type.types: + if isinstance(member, ConstantType) and isinstance(member.value, str): + return member.value + return None + + def stream_class_name(self, async_mode: bool) -> str: + return "AsyncStream" if async_mode else "Stream" + def serialization_type(self, **kwargs: Any) -> str: if self.type: return self.type.serialization_type(**kwargs) return "None" def type_annotation(self, **kwargs: Any) -> str: + if self.is_structured_stream and self.type: + kwargs["is_operation_file"] = True + kwargs["is_response"] = True + stream_class = self.stream_class_name(kwargs.get("async_mode", False)) + return f"{stream_class}[{self.type.type_annotation(**kwargs)}]" if self.type: kwargs["is_operation_file"] = True kwargs["is_response"] = True @@ -109,12 +152,18 @@ def type_annotation(self, **kwargs: Any) -> str: def docstring_text(self, **kwargs: Any) -> str: kwargs["is_response"] = True + if self.is_structured_stream and self.type: + stream_class = self.stream_class_name(kwargs.get("async_mode", False)) + return f"An instance of {stream_class} that iterates over {self.type.docstring_text(**kwargs)}" if self.nullable and self.type: return f"{self.type.docstring_text(**kwargs)} or None" return self.type.docstring_text(**kwargs) if self.type else "None" def docstring_type(self, **kwargs: Any) -> str: kwargs["is_response"] = True + if self.is_structured_stream and self.type: + stream_class = self.stream_class_name(kwargs.get("async_mode", False)) + return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{self.type.docstring_type(**kwargs)}]" if self.nullable and self.type: return f"{self.type.docstring_type(**kwargs)} or None" return self.type.docstring_type(**kwargs) if self.type else "None" @@ -133,6 +182,15 @@ def imports(self, **kwargs: Any) -> FileImport: ImportType.LOCAL, TypingSection.TYPING, ) + if self.is_structured_stream: + stream_class = self.stream_class_name(kwargs.get("async_mode", False)) + serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) + relative_path = self.code_model.get_relative_import_path( + serialize_namespace, module_name="_utils.streaming_base" + ) + file_import.add_submodule_import(relative_path, stream_class, ImportType.LOCAL) + if self.streaming_kind == "sse": + file_import.add_import("json", ImportType.STDLIB) return file_import def _get_import_type(self, input_path: str) -> ImportType: @@ -143,6 +201,26 @@ def _get_import_type(self, input_path: str) -> ImportType: @classmethod def from_yaml(cls, yaml_data: dict[str, Any], code_model: "CodeModel") -> "Response": + streaming = yaml_data.get("streaming") + if streaming: + # Structured stream (JSONL / SSE): the response ``type`` is the raw byte body, + # but we render per-item types, so use the streaming item type instead and do + # NOT convert it to a BinaryIteratorType (that would trigger the raw-bytes path). + # Resolve the item type from the global type map. For heterogeneous / request-body + # streaming overloads (out of scope) the item type is serialized inline and not + # collected globally; in that case fall back to the raw byte-iterator path below + # instead of failing generation (and to avoid emitting duplicate inline models). + try: + item_type = code_model.lookup_type(id(streaming["itemType"])) + except KeyError: + item_type = None + if item_type is not None: + return cls( + yaml_data=yaml_data, + code_model=code_model, + headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]], + type=item_type, + ) type = code_model.lookup_type(id(yaml_data["type"])) if yaml_data.get("type") else None # use ByteIteratorType if we are returning a binary type default_content_type = yaml_data.get("defaultContentType", "application/json") diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py index c5d786a4a6e..0321dc17d91 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -525,6 +525,13 @@ def _serialize_and_write_utils_folder(self, env: Environment, namespace: str): general_serializer.serialize_model_base_file(), ) + # write _utils/streaming_base.py (vendored Stream/AsyncStream + JSONL/SSE decoders) + if self.code_model.need_streaming_base: + self.write_file( + utils_folder_path / Path("streaming_base.py"), + general_serializer.serialize_streaming_base_file(), + ) + def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None: root_dir = self.code_model.get_root_dir() generation_dir = self.code_model.get_generation_dir(namespace) diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 152b840d260..4a27e609d12 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1256,11 +1256,57 @@ def handle_error_response( # pylint: disable=too-many-statements, too-many-bran ) return retval + def handle_structured_stream_response(self, builder: OperationType) -> list[str]: + """Emit the body for an operation returning a structured (JSONL / SSE) stream. + + Produces a per-event deserialization callback and returns a ``Stream`` / + ``AsyncStream`` wrapping the streamed HTTP response. + """ + response = next(r for r in builder.responses if getattr(r, "is_structured_stream", False)) + item_annotation = response.type.type_annotation( # type: ignore[union-attr] + is_operation_file=True, serialize_namespace=self.serialize_namespace + ) + stream_class = response.stream_class_name(self.async_mode) # type: ignore[attr-defined] + terminal_event = getattr(response, "terminal_event", None) + retval: list[str] = [] + retval.append("def _callback(_http_response, _event):") + if response.streaming_kind == "sse": # type: ignore[attr-defined] + # Heterogeneous SSE (``@events`` unions) is deserialized against the union item + # type below; the shared ``_deserialize`` cannot resolve a forward-ref union + # member name into a concrete model, so payloads are yielded as parsed JSON. + # Per-event ``eventType`` dispatch into distinct model instances requires the + # TCGC ``sseMetadata`` (SdkSseMetadata.events[], typespec-client-generator-core + # #4882), which is unavailable in the currently pinned TCGC version. The stream's + # terminal event (a string-literal union member such as ``[DONE]``) is detected + # structurally and passed as ``terminal_event`` below, so the runtime stops + # before this callback attempts to JSON-parse it. + retval.append(" _event_json = json.loads(_event.data)") + else: + retval.append(" _event_json = _event.json()") + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") + retval.append(" if cls:") + retval.append(" return cls(pipeline_response, deserialized, {}) # type: ignore") + retval.append(" return deserialized") + retval.append("") + if terminal_event is not None: + retval.append( + f"return {stream_class}(response=response, deserialization_callback=_callback, " + f"terminal_event={terminal_event!r}) # type: ignore" + ) + else: + retval.append( + f"return {stream_class}(response=response, deserialization_callback=_callback) # type: ignore" + ) + return retval + def handle_response(self, builder: OperationType) -> list[str]: retval: list[str] = ["response = pipeline_response.http_response"] retval.append("") retval.extend(self.handle_error_response(builder)) retval.append("") + if builder.has_structured_stream_response: + retval.extend(self.handle_structured_stream_response(builder)) + return retval if builder.has_optional_return_type: retval.append("deserialized = None") if builder.any_response_has_headers: diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py index d44dbc8bc02..4700b527667 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py @@ -318,6 +318,10 @@ def serialize_model_base_file(self) -> str: template = self.env.get_template("model_base.py.jinja2") return template.render(code_model=self.code_model, file_import=FileImport(self.code_model)) + def serialize_streaming_base_file(self) -> str: + template = self.env.get_template("streaming_base.py.jinja2") + return template.render(code_model=self.code_model, file_import=FileImport(self.code_model)) + def serialize_validation_file(self) -> str: template = self.env.get_template("validation.py.jinja2") return template.render( diff --git a/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 new file mode 100644 index 00000000000..4cb67922530 --- /dev/null +++ b/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 @@ -0,0 +1,549 @@ +# coding=utf-8 +{% if code_model.license_header %} +{{ code_model.license_header }} +{% endif %} +# pylint: disable=line-too-long,useless-suppression,unnecessary-ellipsis +# -------------------------------------------------------------------------- +# This file is vendored from azure-core (azure.core.streaming). It provides the +# Stream / AsyncStream helpers (plus the JSONL / SSE decoders and event types) +# used by generated structured-streaming operations, so the generated package +# does not take a hard dependency on an azure-core version that ships +# azure.core.streaming. Do not edit by hand. +# -------------------------------------------------------------------------- +import codecs +import json +from types import TracebackType +from typing import ( + Any, + AsyncIterator, + Callable, + Iterator, + List, + Optional, + Protocol, + Tuple, + Type, + TypeVar, + cast, + runtime_checkable, +) + +from typing_extensions import Self + +from azure.core.rest import AsyncHttpResponse, HttpResponse + +DecodedType = TypeVar("DecodedType") +ReturnType_co = TypeVar("ReturnType_co", covariant=True) +T_co = TypeVar("T_co", covariant=True) + + +@runtime_checkable +class StreamDecoder(Protocol[T_co]): + """Protocol for stream decoders.""" + + def iter_events(self, iter_bytes: Iterator[bytes]) -> Iterator[T_co]: + """Iterate over events from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :return: An iterator of decoded data. + :rtype: Iterator[DecodedType_co] + """ + ... + + +@runtime_checkable +class AsyncStreamDecoder(Protocol[T_co]): + """Protocol for async stream decoders.""" + + # Why this isn't async def: https://mypy.readthedocs.io/en/stable/more_types.html#asynchronous-iterators + def aiter_events(self, iter_bytes: AsyncIterator[bytes]) -> AsyncIterator[T_co]: + """Asynchronously iterate over events from a byte iterator. + + :param iter_bytes: An asynchronous iterator of byte chunks. + :type iter_bytes: AsyncIterator[bytes] + :return: An asynchronous iterator of decoded data. + :rtype: AsyncIterator[DecodedType_co] + """ + ... + + +class JSONLEvent: + """A single JSON Lines (JSONL) event. + + :ivar data: The raw JSONL record. + :vartype data: str or None + """ + + def __init__( + self, + *, + data: Optional[str] = None, + ) -> None: + self.data = data + + def json(self) -> Any: + """Parse the event data as JSON. + + :return: The parsed JSON value. + :rtype: Any + """ + return json.loads(cast(str, self.data)) + + +def iter_lines(iter_bytes: Iterator[bytes]) -> Iterator[str]: + """Iterate over lines from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :rtype: Iterator[str] + :return: An iterator of lines. + """ + decoder = codecs.getincrementaldecoder("utf-8")() + + # Split only on "\n" (tolerating "\r\n") rather than using str.splitlines(), + # which would also break on other Unicode boundaries (\v, \f, \x1c-\x1e, \x85, + # \u2028, \u2029) that are valid inside a JSONL record's string value. + decoded = "" + for chunk in iter_bytes: + decoded += decoder.decode(chunk) + if decoded: + decoded_lines = [line[:-1] if line.endswith("\r") else line for line in decoded.split("\n")] + yield from decoded_lines[:-1] + decoded = decoded_lines[-1] + + decoded += decoder.decode(b"", final=True) + if decoded: + yield decoded[:-1] if decoded.endswith("\r") else decoded + + +async def aiter_lines(iter_bytes: AsyncIterator[bytes]) -> AsyncIterator[str]: + """Iterate over lines from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :rtype: Iterator[str] + :return: An iterator of lines. + """ + decoder = codecs.getincrementaldecoder("utf-8")() + + # Split only on "\n" (tolerating "\r\n") rather than using str.splitlines(), + # which would also break on other Unicode boundaries (\v, \f, \x1c-\x1e, \x85, + # \u2028, \u2029) that are valid inside a JSONL record's string value. + decoded = "" + async for chunk in iter_bytes: + decoded += decoder.decode(chunk) + if decoded: + decoded_lines = [line[:-1] if line.endswith("\r") else line for line in decoded.split("\n")] + for line in decoded_lines[:-1]: + yield line + decoded = decoded_lines[-1] + + decoded += decoder.decode(b"", final=True) + if decoded: + yield decoded[:-1] if decoded.endswith("\r") else decoded + + +class JSONLDecoder: + """Decoder for JSON Lines (JSONL) format. https://jsonlines.org/""" + + def iter_events(self, iter_bytes: Iterator[bytes]) -> Iterator[JSONLEvent]: + """Iterate over JSONL events from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :rtype: Iterator[JSONLEvent] + :return: An iterator of JSONL events. + """ + + yield from (JSONLEvent(data=line) for line in iter_lines(iter_bytes)) + + +class AsyncJSONLDecoder: + """Asynchronous decoder for JSON Lines (JSONL) format. https://jsonlines.org/""" + + # pylint: disable=invalid-overridden-method + async def aiter_events(self, iter_bytes: AsyncIterator[bytes]) -> AsyncIterator[JSONLEvent]: + """Asynchronously iterate over JSONL events from a byte iterator. + + :param iter_bytes: An asynchronous iterator of byte chunks. + :type iter_bytes: AsyncIterator[bytes] + :rtype: AsyncIterator[JSONLEvent] + :return: An asynchronous iterator of JSONL events. + """ + + async for line in aiter_lines(iter_bytes): + yield JSONLEvent(data=line) + + +class ServerSentEvent: + """A single Server-Sent Event (SSE). + + https://html.spec.whatwg.org/multipage/server-sent-events.html + + :ivar event: The event type. Defaults to ``"message"`` when the stream does not + specify one. + :vartype event: str + :ivar data: The event payload. Multiple ``data`` lines are joined with ``"\\n"``. + Left as a raw string; the caller is responsible for any further parsing. + :vartype data: str + :ivar id: The last event ID. Defaults to an empty string until the stream + provides one. + :vartype id: str + :ivar retry: The reconnection time in milliseconds, if the stream provided one. + :vartype retry: int or None + """ + + def __init__( + self, + *, + event: str = "message", + data: str = "", + id: str = "", # pylint: disable=redefined-builtin + retry: Optional[int] = None, + ) -> None: + self.event = event + self.data = data + self.id = id + self.retry = retry + + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event!r}, data={self.data!r}, " f"id={self.id!r}, retry={self.retry!r})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ServerSentEvent): + return NotImplemented + return (self.event, self.data, self.id, self.retry) == ( + other.event, + other.data, + other.id, + other.retry, + ) + + +def _split_sse_lines(buf: str) -> Tuple[List[str], str]: + """Split ``buf`` into complete SSE lines plus a trailing remainder. + + Per the SSE spec, lines may be separated by ``\\r\\n``, ``\\r`` or ``\\n``. A lone + trailing ``\\r`` is kept in the remainder because it may be the first half of a + ``\\r\\n`` that arrives in a later chunk. + + :param buf: The buffered, already UTF-8 decoded text. + :type buf: str + :return: A tuple of ``(complete_lines, remainder)`` where ``remainder`` is the + unterminated tail (never containing a line separator, except a single trailing + ``\\r`` awaiting a possible ``\\n``). + :rtype: tuple[list[str], str] + """ + lines: List[str] = [] + start = 0 + i = 0 + n = len(buf) + while i < n: + char = buf[i] + if char == "\n": + lines.append(buf[start:i]) + i += 1 + start = i + elif char == "\r": + if i + 1 < n: + lines.append(buf[start:i]) + i += 2 if buf[i + 1] == "\n" else 1 + start = i + else: + # Trailing lone "\r": ambiguous, defer until the next chunk. + break + else: + i += 1 + return lines, buf[start:] + + +def _iter_sse_lines(iter_bytes: Iterator[bytes]) -> Iterator[str]: + """Iterate over SSE lines (line separators stripped) from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :rtype: Iterator[str] + :return: An iterator of decoded lines. + """ + # SSE is always UTF-8 (WHATWG spec). Use utf-8-sig to drop one leading BOM and + # errors="replace" so invalid byte sequences become U+FFFD instead of crashing. + decoder = codecs.getincrementaldecoder("utf-8-sig")(errors="replace") + + buf = "" + for chunk in iter_bytes: + buf += decoder.decode(chunk) + lines, buf = _split_sse_lines(buf) + yield from lines + + buf += decoder.decode(b"", final=True) + lines, remainder = _split_sse_lines(buf) + yield from lines + if remainder: + yield remainder[:-1] if remainder.endswith("\r") else remainder + + +async def _aiter_sse_lines(iter_bytes: AsyncIterator[bytes]) -> AsyncIterator[str]: + """Asynchronously iterate over SSE lines (separators stripped) from a byte iterator. + + :param iter_bytes: An asynchronous iterator of byte chunks. + :type iter_bytes: AsyncIterator[bytes] + :rtype: AsyncIterator[str] + :return: An asynchronous iterator of decoded lines. + """ + # SSE is always UTF-8 (WHATWG spec). Use utf-8-sig to drop one leading BOM and + # errors="replace" so invalid byte sequences become U+FFFD instead of crashing. + decoder = codecs.getincrementaldecoder("utf-8-sig")(errors="replace") + + buf = "" + async for chunk in iter_bytes: + buf += decoder.decode(chunk) + lines, buf = _split_sse_lines(buf) + for line in lines: + yield line + + buf += decoder.decode(b"", final=True) + lines, remainder = _split_sse_lines(buf) + for line in lines: + yield line + if remainder: + yield remainder[:-1] if remainder.endswith("\r") else remainder + + +class _SSEEventBuilder: + """Accumulates SSE field lines and builds :class:`ServerSentEvent` instances.""" + + def __init__(self) -> None: + self._data: List[str] = [] + self._event_type = "" + self._last_id = "" + self._retry: Optional[int] = None + + def add_line(self, line: str) -> Optional[ServerSentEvent]: + """Process a single SSE line, dispatching an event on a blank line. + + :param line: A single SSE line with its terminator already stripped. + :type line: str + :return: A :class:`ServerSentEvent` when ``line`` is blank and an event is + pending, otherwise ``None``. + :rtype: ServerSentEvent or None + """ + if line == "": + return self._dispatch() + if line.startswith(":"): + # Comment line, ignored. + return None + + field, _, value = line.partition(":") + if value.startswith(" "): + value = value[1:] + + if field == "event": + self._event_type = value + elif field == "data": + self._data.append(value) + elif field == "id": + if "\x00" not in value: + self._last_id = value + elif field == "retry": + if value.isascii() and value.isdigit(): + try: + self._retry = int(value) + except ValueError: + # All ASCII digits but too long for int() (CPython's int-string + # conversion limit). Ignore rather than crashing the stream. + pass + # Unknown fields are ignored per spec. + return None + + def _dispatch(self) -> Optional[ServerSentEvent]: + if not self._data: + # No data accumulated: reset and dispatch nothing. + self._event_type = "" + return None + event = ServerSentEvent( + event=self._event_type or "message", + data="\n".join(self._data), + id=self._last_id, + retry=self._retry, + ) + self._data = [] + self._event_type = "" + return event + + +class SSEDecoder: + """Decoder for Server-Sent Events (SSE). + + https://html.spec.whatwg.org/multipage/server-sent-events.html + """ + + def iter_events(self, iter_bytes: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Iterate over SSE events from a byte iterator. + + :param iter_bytes: An iterator of byte chunks. + :type iter_bytes: Iterator[bytes] + :rtype: Iterator[ServerSentEvent] + :return: An iterator of server-sent events. + """ + builder = _SSEEventBuilder() + for line in _iter_sse_lines(iter_bytes): + event = builder.add_line(line) + if event is not None: + yield event + + +class AsyncSSEDecoder: + """Asynchronous decoder for Server-Sent Events (SSE). + + https://html.spec.whatwg.org/multipage/server-sent-events.html + """ + + # pylint: disable=invalid-overridden-method + async def aiter_events(self, iter_bytes: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Asynchronously iterate over SSE events from a byte iterator. + + :param iter_bytes: An asynchronous iterator of byte chunks. + :type iter_bytes: AsyncIterator[bytes] + :rtype: AsyncIterator[ServerSentEvent] + :return: An asynchronous iterator of server-sent events. + """ + builder = _SSEEventBuilder() + async for line in _aiter_sse_lines(iter_bytes): + event = builder.add_line(line) + if event is not None: + yield event + + +class Stream(Iterator[ReturnType_co]): + """Stream class for consuming a decoded event stream (e.g. JSONL or SSE). + + :keyword response: The response object. + :paramtype response: ~azure.core.rest.HttpResponse + :keyword decoder: A decoder to use for the stream. If omitted, the decoder is + inferred from the response ``Content-Type`` header. + :paramtype decoder: StreamDecoder + :keyword deserialization_callback: A callback that takes the response and the decoded event and + returns a deserialized object. + :paramtype deserialization_callback: Callable[[~azure.core.rest.HttpResponse, Any], ReturnType] + :keyword terminal_event: Optional event ``data`` value that terminates the stream (e.g. + ``"[DONE]"``). When an event's ``data`` equals this value, iteration stops and the + event is not passed to ``deserialization_callback``. + :paramtype terminal_event: str or None + """ + + def __init__( + self, + *, + response: HttpResponse, + deserialization_callback: Callable[[HttpResponse, DecodedType], ReturnType_co], + decoder: Optional[StreamDecoder[DecodedType]] = None, + terminal_event: Optional[str] = None, + ) -> None: + self._response = response + content_type = response.headers.get("Content-Type", "").split(";", 1)[0].strip().lower() + self._decoder: StreamDecoder[Any] = ( + decoder if decoder is not None else (SSEDecoder() if content_type == "text/event-stream" else JSONLDecoder()) + ) + self._deserialization_callback = deserialization_callback + self._terminal_event = terminal_event + self._iterator = self._iter_results() + + def __next__(self) -> ReturnType_co: + return self._iterator.__next__() + + def __iter__(self) -> Self: + return self + + def _iter_results(self) -> Iterator[ReturnType_co]: + for event in self._decoder.iter_events(self._response.iter_bytes()): + if self._terminal_event is not None and getattr(event, "data", None) == self._terminal_event: + break + result = self._deserialization_callback(self._response, event) + yield result + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self.close() + + def __enter__(self) -> Self: + return self + + def close(self) -> None: + self._response.close() + + +class AsyncStream(AsyncIterator[ReturnType_co]): + """AsyncStream class for asynchronously consuming a decoded event stream (e.g. JSONL or SSE). + + :keyword response: The response object. + :paramtype response: ~azure.core.rest.AsyncHttpResponse + :keyword decoder: A decoder to use for the stream. If omitted, the decoder is + inferred from the response ``Content-Type`` header. + :paramtype decoder: AsyncStreamDecoder + :keyword deserialization_callback: A callback that takes the response and the decoded event and + returns a deserialized object. + :paramtype deserialization_callback: Callable[[~azure.core.rest.AsyncHttpResponse, Any], ReturnType] + :keyword terminal_event: Optional event ``data`` value that terminates the stream (e.g. + ``"[DONE]"``). When an event's ``data`` equals this value, iteration stops and the + event is not passed to ``deserialization_callback``. + :paramtype terminal_event: str or None + """ + + def __init__( + self, + *, + response: AsyncHttpResponse, + deserialization_callback: Callable[[AsyncHttpResponse, DecodedType], ReturnType_co], + decoder: Optional[AsyncStreamDecoder[DecodedType]] = None, + terminal_event: Optional[str] = None, + ) -> None: + self._response = response + content_type = response.headers.get("Content-Type", "").split(";", 1)[0].strip().lower() + self._decoder: AsyncStreamDecoder[Any] = ( + decoder + if decoder is not None + else (AsyncSSEDecoder() if content_type == "text/event-stream" else AsyncJSONLDecoder()) + ) + self._deserialization_callback = deserialization_callback + self._terminal_event = terminal_event + self._iterator = self._iter_results() + + async def __anext__(self) -> ReturnType_co: + return await self._iterator.__anext__() + + def __aiter__(self) -> Self: + return self + + async def _iter_results(self) -> AsyncIterator[ReturnType_co]: + async for event in self._decoder.aiter_events(self._response.iter_bytes()): + if self._terminal_event is not None and getattr(event, "data", None) == self._terminal_event: + break + result = self._deserialization_callback(self._response, event) + yield result + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + await self.close() + + async def __aenter__(self) -> Self: + return self + + async def close(self) -> None: + await self._response.close() + + +__all__ = [ + "Stream", + "AsyncStream", + "JSONLEvent", + "ServerSentEvent", +] diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index 41dd54e4b17..37ae20d62e5 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -29,11 +29,11 @@ "@typespec/compiler": "^1.14.0", "@typespec/events": "~0.84.0", "@typespec/http": "^1.14.0", - "@typespec/http-specs": "0.1.0-alpha.39", + "@typespec/http-specs": "0.1.0-alpha.40", "@typespec/openapi": "^1.14.0", "@typespec/rest": "~0.84.0", "@typespec/spec-api": "0.1.0-alpha.15", - "@typespec/spector": "0.1.0-alpha.26", + "@typespec/spector": "0.1.0-alpha.27", "@typespec/sse": "~0.84.0", "@typespec/streams": "~0.84.0", "@typespec/versioning": "~0.84.0", @@ -2472,22 +2472,24 @@ } }, "node_modules/@typespec/http-specs": { - "version": "0.1.0-alpha.39", - "resolved": "https://registry.npmjs.org/@typespec/http-specs/-/http-specs-0.1.0-alpha.39.tgz", - "integrity": "sha512-x3ORyF/qLSLt+QDyievKT90STB0tT9J4s0Yu/Isio8zK16U+eRbCFHi69T7FZ94ZrRpPDWJ2u9+dEZv/SCPj+w==", + "version": "0.1.0-alpha.40", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typespec/http-specs/-/http-specs-0.1.0-alpha.40.tgz", + "integrity": "sha1-Jbgrft+poBvuGMqqGR+shaf07Kk=", "dev": true, "license": "MIT", "dependencies": { "@typespec/spec-api": "^0.1.0-alpha.15", - "@typespec/spector": "^0.1.0-alpha.26" + "@typespec/spector": "^0.1.0-alpha.27" }, "engines": { "node": ">=22.0.0" }, "peerDependencies": { "@typespec/compiler": "^1.14.0", + "@typespec/events": "^0.84.0", "@typespec/http": "^1.14.0", "@typespec/rest": "^0.84.0", + "@typespec/sse": "^0.84.0", "@typespec/versioning": "^0.84.0", "@typespec/xml": "^0.84.0" } @@ -2582,9 +2584,9 @@ "license": "MIT" }, "node_modules/@typespec/spector": { - "version": "0.1.0-alpha.26", - "resolved": "https://registry.npmjs.org/@typespec/spector/-/spector-0.1.0-alpha.26.tgz", - "integrity": "sha512-WtaWIJE+Xh80G11Bi/3zC1GIrK4EVYBSCnkmSvm9bEYcBMH8QuryPDbUeTXYUyozcCrMYXaKnBboHN91IEi8ug==", + "version": "0.1.0-alpha.27", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@typespec/spector/-/spector-0.1.0-alpha.27.tgz", + "integrity": "sha1-aA4cucLEbAIR6ZLH0llZuaVOMMY=", "dev": true, "license": "MIT", "dependencies": { diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index d3d837fcad5..f7910112349 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -116,12 +116,12 @@ "@typespec/rest": "~0.84.0", "@typespec/versioning": "~0.84.0", "@typespec/events": "~0.84.0", - "@typespec/spector": "0.1.0-alpha.26", + "@typespec/spector": "0.1.0-alpha.27", "@typespec/spec-api": "0.1.0-alpha.15", "@typespec/sse": "~0.84.0", "@typespec/streams": "~0.84.0", "@typespec/xml": "~0.84.0", - "@typespec/http-specs": "0.1.0-alpha.39", + "@typespec/http-specs": "0.1.0-alpha.40", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", "@types/semver": "7.5.8", diff --git a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py new file mode 100644 index 00000000000..639aa0cf13d --- /dev/null +++ b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py @@ -0,0 +1,151 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +"""Mock API tests for structured streaming (Azure flavor). + +These tests exercise operations that return `Stream[T]` / `AsyncStream[T]`. The +streaming runtime (Stream / AsyncStream + JSONL / SSE decoders) is **vendored** +into the generated package at `_utils/streaming_base.py`, so it only depends on +the released `azure.core.rest` — NOT on the unreleased `azure.core.streaming` +(azure-core PR #48077). + +Structured streaming is driven by the TCGC response stream metadata and applies +to the **Azure flavor only** (the vendored runtime depends on `azure.core.rest`). +For the Azure flavor, a JSONL (`application/jsonl`) / SSE (`text/event-stream`) +streaming response generates a `receive()` returning `Stream[T]` / `AsyncStream[T]`, +so the JSONL homogeneous tests below run against the real spector mock route +(`/streaming/jsonl/basic/receive`) and the SSE homogeneous tests run against +(`/streaming/sse/unnamed/receive`). For the unbranded flavor, streaming responses +keep the raw byte-iterator behavior (see +mock_api/unbranded/test_streaming_jsonl_unbranded.py). + +Note on SSE item deserialization: SSE item types are modelled as `@events` unions, +which the generated callback deserializes via `_deserialize("", json)`. +The shared `_deserialize` cannot resolve a forward-ref *string* union member into a +model instance (same root cause as paging item deserialization needing a `module` +argument), so homogeneous SSE items are yielded as parsed JSON (``dict``) rather than +model instances. The tests below assert on the ``dict`` payloads accordingly. + +Still skipped (follow-ups): + +* SSE heterogeneous — blocked on TCGC `sseMetadata` (#4882) for per-event + dispatch + terminal-event handling, plus the union-item `_deserialize` + limitation (parsed JSON rather than model instances). + +Imports are guarded so collection never errors when the package is absent +(e.g. before `regenerate` runs, or for the unbranded flavor). +""" +import pytest + +# For the Azure flavor the default ``streaming.jsonl`` package is generated with a +# structured ``receive()`` returning ``Stream[Info]`` (grouped namespace layout, so +# ``Info`` lives at ``streaming.jsonl.basic.models``). Guarded so collection doesn't +# error for the unbranded flavor (byte-iterator ``receive()``, no ``Info`` model). +try: # pragma: no cover - guarded so collection doesn't error when absent + from streaming.jsonl import JsonlClient # type: ignore + from streaming.jsonl.aio import JsonlClient as AsyncJsonlClient # type: ignore + from streaming.jsonl.basic.models import Info # type: ignore + + _HAS_STRUCTURED_JSONL = True +except ImportError: # pragma: no cover + JsonlClient = None # type: ignore + AsyncJsonlClient = None # type: ignore + Info = None # type: ignore + _HAS_STRUCTURED_JSONL = False + + +# For the Azure flavor the SSE ``streaming.sse`` package is generated with a structured +# ``unnamed.receive()`` returning ``Stream["_unions.UnnamedEvents"]``. Guarded so +# collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). +try: # pragma: no cover - guarded so collection doesn't error when absent + from streaming.sse import SseClient # type: ignore + from streaming.sse.aio import SseClient as AsyncSseClient # type: ignore + + _HAS_STRUCTURED_SSE = True +except ImportError: # pragma: no cover + SseClient = None # type: ignore + AsyncSseClient = None # type: ignore + _HAS_STRUCTURED_SSE = False + + +_EXPECTED = ["one", "two", "three"] + + +@pytest.mark.skipif(not _HAS_STRUCTURED_JSONL, reason="streaming.jsonl is not structured (unbranded flavor)") +def test_jsonl_receive_structured_sync(): + """JSONL homogeneous: receive() returns Stream[Info] of deserialized models.""" + with JsonlClient(endpoint="http://localhost:3000") as client: + items = list(client.basic.receive()) + assert [i.desc for i in items] == _EXPECTED + assert all(isinstance(i, Info) for i in items) + + +@pytest.mark.skipif(not _HAS_STRUCTURED_JSONL, reason="streaming.jsonl is not structured (unbranded flavor)") +@pytest.mark.asyncio +async def test_jsonl_receive_structured_async(): + """JSONL homogeneous: async receive() returns AsyncStream[Info].""" + async with AsyncJsonlClient(endpoint="http://localhost:3000") as client: + stream = await client.basic.receive() + items = [item async for item in stream] + assert [i.desc for i in items] == _EXPECTED + assert all(isinstance(i, Info) for i in items) + + +@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") +def test_sse_receive_homogeneous_structured_sync(): + """SSE homogeneous: unnamed.receive() returns Stream over the SSE events. + + The unnamed SSE scenario emits three ``message`` events with payload + ``{"desc": ...}``. Because the SSE item type is an ``@events`` union, the + generated callback yields parsed JSON (``dict``) rather than ``Info`` model + instances (see module docstring / ``_deserialize`` limitation). The stream + terminates naturally after the final event. + """ + with SseClient(endpoint="http://localhost:3000") as client: + items = list(client.unnamed.receive()) + assert [i["desc"] for i in items] == _EXPECTED + assert all(isinstance(i, dict) for i in items) + + +@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") +@pytest.mark.asyncio +async def test_sse_receive_homogeneous_structured_async(): + """Async SSE homogeneous: unnamed.receive() returns AsyncStream over the events.""" + async with AsyncSseClient(endpoint="http://localhost:3000") as client: + stream = await client.unnamed.receive() + items = [item async for item in stream] + assert [i["desc"] for i in items] == _EXPECTED + assert all(isinstance(i, dict) for i in items) + + +@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") +def test_sse_receive_heterogeneous_structured_sync(): + """SSE heterogeneous: named.receive() returns Stream over an ``@events`` union. + + The named SSE scenario emits ``responseCreated`` (``{"id": ...}``) then two + ``responseDelta`` (``{"delta": ...}``) events, followed by a terminal + ``data: [DONE]`` event. ``[DONE]`` is a string-literal member of the item union, + so the generator wires it as ``terminal_event`` and the runtime stops there + (without trying to JSON-parse ``[DONE]``). Per-event payloads are yielded as + parsed JSON (``dict``) rather than distinct ``ResponseCreated`` / ``ResponseDelta`` + model instances: discriminating them needs TCGC ``sseMetadata`` (#4882) plus a + ``module`` argument on the shared ``_deserialize`` (same limitation as paging + item deserialization). + """ + with SseClient(endpoint="http://localhost:3000") as client: + items = list(client.named.receive()) + assert all(isinstance(i, dict) for i in items) + assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] + + +@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") +@pytest.mark.asyncio +async def test_sse_receive_heterogeneous_structured_async(): + """Async SSE heterogeneous: named.receive() returns AsyncStream, terminating at [DONE].""" + async with AsyncSseClient(endpoint="http://localhost:3000") as client: + stream = await client.named.receive() + items = [item async for item in stream] + assert all(isinstance(i, dict) for i in items) + assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] diff --git a/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py b/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py index 74e05cebd14..20288f960ac 100644 --- a/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py +++ b/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py @@ -21,8 +21,3 @@ async def client(): @pytest.mark.asyncio async def test_basic_send(client: JsonlClient): await client.basic.send(JSONL) - - -@pytest.mark.asyncio -async def test_basic_recv(client: JsonlClient): - assert b"".join([d async for d in (await client.basic.receive())]) == JSONL diff --git a/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py b/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py index 494c17a3493..d035530c054 100644 --- a/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py +++ b/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py @@ -19,7 +19,3 @@ def client(): def test_basic_send(client: JsonlClient): client.basic.send(JSONL) - - -def test_basic_recv(client: JsonlClient): - assert b"".join(client.basic.receive()) == JSONL diff --git a/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py b/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py new file mode 100644 index 00000000000..05944f80311 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py @@ -0,0 +1,28 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +"""Unbranded JSONL streaming (async): ``receive()`` keeps the byte-iterator behavior. + +See the sync counterpart (test_streaming_jsonl_unbranded.py) for context: structured +`Stream[T]` streaming is Azure-only; the unbranded flavor keeps `AsyncIterator[bytes]`. +""" +import pytest +import pytest_asyncio + +from streaming.jsonl.aio import JsonlClient + + +@pytest_asyncio.fixture +async def client(): + async with JsonlClient(endpoint="http://localhost:3000") as client: + yield client + + +JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' + + +@pytest.mark.asyncio +async def test_basic_recv(client: JsonlClient): + assert b"".join([d async for d in (await client.basic.receive())]) == JSONL diff --git a/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py b/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py new file mode 100644 index 00000000000..1a0264ebe33 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py @@ -0,0 +1,30 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +"""Unbranded JSONL streaming: ``receive()`` keeps the raw byte-iterator behavior. + +Structured streaming (`Stream[T]` / `AsyncStream[T]`) targets the vendored +`azure.core.rest`-based runtime and so applies to the Azure flavor only. For the +unbranded flavor, JSONL streaming responses keep the existing +`Iterator[bytes]` / `AsyncIterator[bytes]` behavior, which this test asserts. + +(The Azure structured `receive()` is covered by mock_api/azure/test_streaming_structured.py.) +""" +import pytest + +from streaming.jsonl import JsonlClient + + +@pytest.fixture +def client(): + with JsonlClient(endpoint="http://localhost:3000") as client: + yield client + + +JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' + + +def test_basic_recv(client: JsonlClient): + assert b"".join(client.basic.receive()) == JSONL diff --git a/packages/http-client-python/tests/unit/test_structured_streaming_response.py b/packages/http-client-python/tests/unit/test_structured_streaming_response.py new file mode 100644 index 00000000000..f1a0dbdd2b1 --- /dev/null +++ b/packages/http-client-python/tests/unit/test_structured_streaming_response.py @@ -0,0 +1,168 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +"""Tests for structured streaming (JSONL / SSE) response rendering. + +Covers the code path where a streaming response (driven by the TCGC response +stream metadata, Azure flavor) is rendered as ``Stream[T]`` / ``AsyncStream[T]`` +from the vendored ``_utils.streaming_base`` module instead of the raw +byte-iterator behavior. +""" + +import pytest + +from pygen.codegen.models import CodeModel, JSONModelType +from pygen.codegen.models.response import Response + + +@pytest.fixture +def code_model(): + return CodeModel( + { + "clients": [ + { + "name": "client", + "namespace": "blah", + "moduleName": "blah", + "parameters": [], + "url": "", + "operationGroups": [], + } + ], + "namespace": "namespace", + }, + options={ + "show-send-request": True, + "builders-visibility": "public", + "show-operations": True, + "models-mode": "dpg", + "version-tolerant": True, + "flavor": "azure", + }, + ) + + +def _register_model(code_model): + item_yaml = {"type": "model", "name": "Thing", "snakeCaseName": "thing"} + model_type = JSONModelType(item_yaml, code_model) + code_model.types_map[id(item_yaml)] = model_type + return item_yaml + + +def _streaming_response(code_model, kind): + item_yaml = _register_model(code_model) + return Response.from_yaml( + { + "statusCodes": [200], + "headers": [], + "type": None, + "streaming": {"kind": kind, "itemType": item_yaml}, + }, + code_model, + ) + + +def test_jsonl_response_is_structured_stream(code_model): + response = _streaming_response(code_model, "jsonl") + assert response.is_structured_stream is True + assert response.streaming_kind == "jsonl" + + +def test_sse_response_is_structured_stream(code_model): + response = _streaming_response(code_model, "sse") + assert response.is_structured_stream is True + assert response.streaming_kind == "sse" + + +def test_type_annotation_sync_and_async(code_model): + response = _streaming_response(code_model, "jsonl") + sync = response.type_annotation(async_mode=False) + asynchronous = response.type_annotation(async_mode=True) + assert sync.startswith("Stream[") and sync.endswith("]"), sync + assert asynchronous.startswith("AsyncStream[") and asynchronous.endswith("]"), asynchronous + + +def test_docstring_type_references_streaming_base(code_model): + response = _streaming_response(code_model, "jsonl") + assert "~namespace._utils.streaming_base.Stream[" in response.docstring_type(async_mode=False) + assert "~namespace._utils.streaming_base.AsyncStream[" in response.docstring_type(async_mode=True) + + +def test_imports_add_stream_class(code_model): + response = _streaming_response(code_model, "jsonl") + imports = response.imports(async_mode=False) + imports_str = str(imports.to_dict()) + # Vendored local import, not azure.core.streaming. + assert "streaming_base" in imports_str + assert "azure.core.streaming" not in imports_str + + +def test_sse_imports_add_json(code_model): + response = _streaming_response(code_model, "sse") + imports = response.imports(async_mode=False) + assert "json" in str(imports.to_dict()) + + +def test_non_streaming_response_is_not_structured_stream(code_model): + item_yaml = _register_model(code_model) + response = Response.from_yaml( + {"statusCodes": [200], "headers": [], "type": item_yaml}, + code_model, + ) + assert response.is_structured_stream is False + assert response.streaming_kind is None + + +def test_streaming_base_template_renders_vendored_runtime(): + """The vendored ``streaming_base.py`` template renders the Stream/AsyncStream runtime.""" + from jinja2 import Environment, PackageLoader + + from pygen.codegen.serializers.general_serializer import GeneralSerializer + + cm = CodeModel( + { + "clients": [ + { + "name": "client", + "namespace": "blah", + "moduleName": "blah", + "parameters": [], + "url": "", + "operationGroups": [], + } + ], + "namespace": "namespace", + }, + options={ + "show-send-request": True, + "builders-visibility": "public", + "show-operations": True, + "models-mode": "dpg", + "version-tolerant": True, + "flavor": "azure", + }, + ) + env = Environment( + loader=PackageLoader("pygen.codegen", "templates"), + keep_trailing_newline=True, + line_statement_prefix="##", + line_comment_prefix="###", + trim_blocks=True, + lstrip_blocks=True, + ) + rendered = GeneralSerializer(code_model=cm, env=env, async_mode=False).serialize_streaming_base_file() + # Vendored runtime is self-contained: depends only on azure.core.rest, not azure.core.streaming. + assert "class Stream(" in rendered + assert "class AsyncStream(" in rendered + assert "from azure.core.rest import" in rendered + assert "import azure.core.streaming" not in rendered + assert "from azure.core.streaming" not in rendered + + +def test_need_streaming_base_flag(code_model): + """need_streaming_base tracks has_structured_stream (no operations -> False).""" + # No operations registered in this fixture, so no structured stream is present. + assert code_model.has_structured_stream is False + assert code_model.need_streaming_base is False From 2beb325860a30fa6f3563096471ed15a640d992d Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 15:25:54 -0700 Subject: [PATCH 02/15] feat(http-client-python): per-event SSE model dispatch via TCGC sseMetadata Consume TCGC `SdkSseMetadata` (0.71.0-dev.11) to route each SSE `event:` name to its concrete payload model, so structured SSE streams yield distinct model instances instead of parsed JSON. Homogeneous SSE returns `Stream[Model]`; heterogeneous `@events` SSE dispatches per event name and wires the `@terminalEvent` marker (e.g. `[DONE]`) into the vendored runtime. - emitter: emit `streaming.events[]` (eventType + payload itemType) and `terminalEvent` for SSE responses. - pygen: parse events, dispatch on `_event.event` in the generated callback, and annotate homogeneous streams with the concrete payload model (fixes the single-member union-alias mypy error). - deps: bump @typespec 1.15-dev / @azure-tools 0.71-dev prerelease stack (from the azure-sdk-for-js feed) for sseMetadata; `.npmrc` pins the scoped registries and `legacy-peer-deps` for reproducible install. - tests/docs: SSE mock_api tests assert model instances; README/changelog updated. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../changes/structured-streaming-2026-0-0.md | 8 +- packages/http-client-python/.npmrc | 7 + packages/http-client-python/README.md | 10 +- .../http-client-python/emitter/src/http.ts | 48 +- .../pygen/codegen/models/response.py | 64 +- .../codegen/serializers/builder_serializer.py | 41 +- packages/http-client-python/package-lock.json | 1225 ++++------------- packages/http-client-python/package.json | 28 +- .../azure/test_streaming_structured.py | 74 +- 9 files changed, 422 insertions(+), 1083 deletions(-) diff --git a/.chronus/changes/structured-streaming-2026-0-0.md b/.chronus/changes/structured-streaming-2026-0-0.md index 41069aaf26d..f12b46f6ca6 100644 --- a/.chronus/changes/structured-streaming-2026-0-0.md +++ b/.chronus/changes/structured-streaming-2026-0-0.md @@ -17,8 +17,10 @@ for thing in stream: # deserialized model instances ... ``` +For SSE, each `event:` name is routed to its concrete payload model (via TCGC `sseMetadata`), so the stream yields distinct model instances — homogeneous streams via their single event payload and heterogeneous (`@events`) streams via per-event dispatch. A `@terminalEvent` marker (e.g. `"[DONE]"`) is wired into the runtime as `terminal_event`, so iteration stops before the marker is deserialized. + Known limitations / follow-ups: -- SSE union item types deserialize to parsed JSON (e.g. `dict`) rather than model instances — same root cause as paging item deserialization; the shared `_deserialize` needs a `module` argument to resolve forward-reference union member names. -- Heterogeneous SSE **terminal-event** handling is supported: the terminal marker (e.g. `"[DONE]"`) is detected structurally as a string-literal member of the item union and passed to the vendored `Stream` / `AsyncStream` as `terminal_event`, so iteration stops before parsing it. Per-event **model dispatch** (routing each `@events` event to its distinct payload model) is still blocked on TCGC `sseMetadata` (typespec-client-generator-core #4882), absent from the resolved TCGC version; until then heterogeneous events are yielded as parsed JSON. -- In-repo mock_api coverage: JSONL homogeneous (sync + async) is active against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`) and heterogeneous (`named/receive`, terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded event payloads (as `dict`s per the union-deserialization limitation). +- SSE events flagged `isEventEnvelope` are not yet specially unwrapped; the payload is deserialized directly. No current spector SSE scenario exercises this. +- Per-event SSE model dispatch consumes TCGC `sseMetadata` (`SdkSseMetadata.events[]`), first available in `@azure-tools/typespec-client-generator-core` `0.71.0-dev.11`, which targets the `@typespec` 1.15-dev / 0.85-dev prerelease line; the package's `devDependencies` pin those prereleases. The generated runtime is unaffected (released `azure.core.rest` only). +- In-repo mock_api coverage: JSONL homogeneous (sync + async) runs against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`, yielding `Info`) and heterogeneous (`named/receive`, yielding `ResponseCreated` / `ResponseDelta` and terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded model instances. diff --git a/packages/http-client-python/.npmrc b/packages/http-client-python/.npmrc index b6f27f13595..9fc3f486ddf 100644 --- a/packages/http-client-python/.npmrc +++ b/packages/http-client-python/.npmrc @@ -1 +1,8 @@ engine-strict=true +# Prerelease @typespec / @azure-tools builds (needed for TCGC sseMetadata, which drives +# per-event SSE model dispatch) are published to the azure-sdk-for-js Azure DevOps feed. +@typespec:registry=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ +@azure-tools:registry=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ +# The prerelease waves are not fully cross-aligned (e.g. azure-http-specs still peers on the +# stable @typespec/@azure-tools line), so peer resolution requires legacy behavior. +legacy-peer-deps=true diff --git a/packages/http-client-python/README.md b/packages/http-client-python/README.md index 14ee885d1cd..34f618be5a4 100644 --- a/packages/http-client-python/README.md +++ b/packages/http-client-python/README.md @@ -169,12 +169,10 @@ for thing in stream: # deserialized model instances The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vendored** into the generated package at `_utils/streaming_base.py` (alongside `_utils/model_base.py`). It depends only on the released `azure.core.rest`, so no unreleased `azure.core.streaming` (azure-core PR #48077) dependency is required at runtime. -> **Note:** For SSE responses whose item type is a union (`@events`), each event payload is currently yielded as the parsed JSON value (e.g. a `dict` for object payloads, or the literal for terminal events such as `"[DONE]"`) rather than a fully deserialized model instance. This mirrors the existing union item-deserialization behavior used elsewhere in the generator. JSONL responses with a single model item type are deserialized into model instances. +> **Note:** For SSE responses, each `event:` name is routed to its concrete payload model (via TCGC `sseMetadata`), so the stream yields fully deserialized model instances — a homogeneous stream via its single event payload, a heterogeneous (`@events`) stream via per-event dispatch. A `@terminalEvent` marker (e.g. `"[DONE]"`) is wired into the runtime as `terminal_event`, so iteration stops before the marker is deserialized. JSONL responses with a single model item type likewise yield model instances. #### Known limitations / follow-ups -- **SSE union item deserialization** — SSE item types are `@events` unions, so each event is deserialized against a forward-reference union member name and yielded as the parsed JSON value rather than a model instance. This shares a root cause with paging item deserialization: the shared `_deserialize` helper needs a `module` argument to resolve the union member names into concrete model classes. JSONL (single model item type) is unaffected and fully deserializes. -- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event *model dispatch* — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. - - Investigation (2026-08): `sseMetadata` is **not** present in the resolved TCGC `0.69.1`, **nor in `0.70.0`** (latest stable — its `SdkStreamMetadata` is byte-identical to 0.69.1, no SSE symbols). `SdkSseMetadata` (`events[]` per `@events` union variant, built by `buildSdkSseMetadata`) has since landed upstream on `Azure/typespec-azure` `main` and first appears in the `next` prerelease line (`0.71.0-dev.11`). Adopting it requires the `@typespec` 1.14 / 0.84 family bump those versions carry. Terminal-event termination does **not** depend on it (handled structurally, see above); only per-event model dispatch does. -- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded event payloads (as `dict`s, per the union-deserialization limitation) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. +- **SSE data-envelope events** — Events flagged `isEventEnvelope` (the payload is wrapped in an envelope object) are not yet specially unwrapped; they fall through to the common path and the payload is deserialized directly. None of the current spector SSE scenarios exercise this case. +- **Prerelease dependency requirement** — Per-event SSE model dispatch consumes TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`), first available in `@azure-tools/typespec-client-generator-core` `0.71.0-dev.11`. That build targets the `@typespec` 1.15-dev / 0.85-dev prerelease line, so the package's `devDependencies` pin those prerelease versions (resolved from the `azure-sdk-for-js` feed). This does not affect the generated runtime, which still depends only on the released `azure.core.rest`. +- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded model instances (`Info` for `unnamed`; `ResponseCreated` / `ResponseDelta` for `named`) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts index b1788dc21dd..8e05c6dbdda 100644 --- a/packages/http-client-python/emitter/src/http.ts +++ b/packages/http-client-python/emitter/src/http.ts @@ -66,12 +66,11 @@ export function isStructuredStreamType(type: SdkType): boolean { * Returns `undefined` when structured streaming should not apply, in which case * the existing raw byte-iterator behavior is preserved. * - * Note: the currently consumed TCGC metadata (`streamMetadata`) does not expose - * per-event SSE metadata (event-type dispatch). Terminal-event handling does NOT - * depend on it — the terminal marker is a string-literal member of the item union - * (e.g. `Literal["[DONE]"]`), which the generator detects structurally and passes - * to the vendored runtime as `terminal_event`. Only `kind` and `itemType` are - * emitted here; the terminal event is derived generator-side from `itemType`. + * Note: for SSE, TCGC `sseMetadata` (SdkSseEventMetadata[]) drives per-event MODEL + * dispatch — each `event:` name maps to its concrete payload model, emitted as an + * `events` list. Terminal-event handling is emitted as `terminalEvent` (the string + * marker, e.g. `[DONE]`), which the generator wires into the vendored runtime so the + * stream stops before deserializing it. JSONL emits only `kind` and `itemType`. */ function getStreamingInfo( context: PythonSdkContext, @@ -92,17 +91,38 @@ function getStreamingInfo( if (!isStructuredStreamType(streamMetadata.streamType)) return undefined; const contentTypes = streamMetadata.contentTypes ?? response.contentTypes ?? []; const isSse = contentTypes.some((ct) => ct.toLowerCase().includes("event-stream")); - // SSE kind is detected from the response Content-Type. A heterogeneous `@events` - // union streamType is emitted as a single union `itemType`; the generator detects - // the terminal event (a string-literal union member such as `[DONE]`) structurally - // and wires it into the runtime, so terminal-event termination works without TCGC - // `sseMetadata`. Per-event MODEL dispatch (routing each event to its distinct - // payload model) still requires `sseMetadata` (SdkSseMetadata.events[], TCGC - // #4882); until then heterogeneous events are yielded as parsed JSON. - return { + const streaming: Record = { kind: isSse ? "sse" : "jsonl", itemType: getType(context, streamMetadata.streamType), }; + // For SSE, TCGC `sseMetadata` (SdkSseMetadata.events[]) describes each `event:` name + // and its concrete payload model. We emit an `events` list so the generator can route + // each event to its distinct payload model (`_deserialize(, ...)`) rather than + // yielding parsed JSON. The stream's terminal event (a string-literal payload such as + // `[DONE]`, flagged `isTerminalEvent`) is emitted as `terminalEvent` so the runtime + // stops before this callback is invoked for it. + const sseMetadata = isSse ? (response as SdkHttpResponse).sseMetadata : undefined; + if (sseMetadata) { + const events: Record[] = []; + let terminalEvent: string | undefined; + for (const event of sseMetadata.events) { + if (event.isTerminalEvent) { + // The terminal event's payload is a string constant marker (e.g. `[DONE]`). + const value = (event.payloadType as any).value ?? (event.type as any).value; + if (typeof value === "string") terminalEvent = value; + continue; + } + // Envelope (data-wrapping) events are not yet specially handled; they fall through + // to the common non-envelope path (the payload is deserialized directly). + events.push({ + eventType: event.eventType, + itemType: getType(context, event.payloadType), + }); + } + if (events.length > 0) streaming.events = events; + if (terminalEvent !== undefined) streaming.terminalEvent = terminalEvent; + } + return streaming; } function isEtagType(type: SdkType): boolean { diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 5d7b0a6897b..4a31890b739 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -65,9 +65,22 @@ def __init__( # Only treat this as a structured stream when the resolved ``type`` is the per-item # type (model / union). When the structured item type could not be resolved we fall # back to the raw byte body (``BinaryIteratorType``) and must NOT render ``Stream[...]``. - self.streaming_kind: Optional[str] = ( - streaming["kind"] if streaming and not isinstance(self.type, BinaryIteratorType) else None - ) + is_structured = bool(streaming) and not isinstance(self.type, BinaryIteratorType) + self.streaming_kind: Optional[str] = streaming["kind"] if is_structured else None + # Per-event SSE dispatch (TCGC ``sseMetadata``): each entry maps an SSE ``event:`` + # name to its concrete payload item type, so the generated callback can deserialize + # each event into a distinct model instance. + self.streaming_events: list[tuple[Optional[str], BaseType]] = [] + # Terminal-event marker (e.g. ``"[DONE]"``) emitted from ``sseMetadata``; when absent + # it is derived structurally from the item union (see ``terminal_event``). + self._streaming_terminal_event: Optional[str] = streaming.get("terminalEvent") if is_structured else None + if is_structured: + for event in streaming.get("events", []): + try: + event_item_type = self.code_model.lookup_type(id(event["itemType"])) + except KeyError: + continue + self.streaming_events.append((event.get("eventType"), event_item_type)) @property def result_property(self) -> str: @@ -111,14 +124,18 @@ def is_structured_stream(self) -> bool: def terminal_event(self) -> Optional[str]: """Terminal event marker for a heterogeneous SSE stream, if any. - Heterogeneous SSE ``@events`` unions include a string-literal member (e.g. - ``"[DONE]"``) that marks the end of the stream. Without TCGC ``sseMetadata`` - (#4882) we detect it structurally: the first ``ConstantType`` string member of - the union item type is treated as the terminal marker, so the runtime can stop - before attempting to JSON-deserialize it. Returns ``None`` for homogeneous - streams (no constant member) and for JSONL. + Preferred source is the TCGC ``sseMetadata`` terminal event (emitted as + ``terminalEvent``). When absent, we detect it structurally: the first + ``ConstantType`` string member of the item union (e.g. ``"[DONE]"``) is treated + as the terminal marker, so the runtime can stop before attempting to + JSON-deserialize it. Returns ``None`` for homogeneous streams (no marker) and + for JSONL. """ - if self.streaming_kind != "sse" or not isinstance(self.type, CombinedType): + if self.streaming_kind != "sse": + return None + if self._streaming_terminal_event is not None: + return self._streaming_terminal_event + if not isinstance(self.type, CombinedType): return None from .constant_type import ConstantType @@ -130,6 +147,18 @@ def terminal_event(self) -> Optional[str]: def stream_class_name(self, async_mode: bool) -> str: return "AsyncStream" if async_mode else "Stream" + @property + def stream_item_type(self) -> Optional[BaseType]: + """The type used to parametrize ``Stream[...]`` / ``AsyncStream[...]``. + + For a homogeneous stream (a single event payload) this is the concrete payload + model rather than the single-member union alias (a ``_unions`` variable, which is + not valid as a type annotation). Heterogeneous streams keep the union item type. + """ + if len(self.streaming_events) == 1: + return self.streaming_events[0][1] + return self.type + def serialization_type(self, **kwargs: Any) -> str: if self.type: return self.type.serialization_type(**kwargs) @@ -140,7 +169,8 @@ def type_annotation(self, **kwargs: Any) -> str: kwargs["is_operation_file"] = True kwargs["is_response"] = True stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"{stream_class}[{self.type.type_annotation(**kwargs)}]" + item_type = self.stream_item_type or self.type + return f"{stream_class}[{item_type.type_annotation(**kwargs)}]" if self.type: kwargs["is_operation_file"] = True kwargs["is_response"] = True @@ -154,7 +184,8 @@ def docstring_text(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"An instance of {stream_class} that iterates over {self.type.docstring_text(**kwargs)}" + item_type = self.stream_item_type or self.type + return f"An instance of {stream_class} that iterates over {item_type.docstring_text(**kwargs)}" if self.nullable and self.type: return f"{self.type.docstring_text(**kwargs)} or None" return self.type.docstring_text(**kwargs) if self.type else "None" @@ -163,7 +194,11 @@ def docstring_type(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{self.type.docstring_type(**kwargs)}]" + item_type = self.stream_item_type or self.type + return ( + f"~{self.code_model.namespace}._utils.streaming_base." + f"{stream_class}[{item_type.docstring_type(**kwargs)}]" + ) if self.nullable and self.type: return f"{self.type.docstring_type(**kwargs)} or None" return self.type.docstring_type(**kwargs) if self.type else "None" @@ -191,6 +226,9 @@ def imports(self, **kwargs: Any) -> FileImport: file_import.add_submodule_import(relative_path, stream_class, ImportType.LOCAL) if self.streaming_kind == "sse": file_import.add_import("json", ImportType.STDLIB) + # Ensure each per-event payload model is importable in the operation file. + for _event_type, event_item_type in self.streaming_events: + file_import.merge(event_item_type.imports(**kwargs)) return file_import def _get_import_type(self, input_path: str) -> ImportType: diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 4a27e609d12..bea59017923 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1268,22 +1268,43 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) stream_class = response.stream_class_name(self.async_mode) # type: ignore[attr-defined] terminal_event = getattr(response, "terminal_event", None) + streaming_events = getattr(response, "streaming_events", []) retval: list[str] = [] retval.append("def _callback(_http_response, _event):") if response.streaming_kind == "sse": # type: ignore[attr-defined] - # Heterogeneous SSE (``@events`` unions) is deserialized against the union item - # type below; the shared ``_deserialize`` cannot resolve a forward-ref union - # member name into a concrete model, so payloads are yielded as parsed JSON. - # Per-event ``eventType`` dispatch into distinct model instances requires the - # TCGC ``sseMetadata`` (SdkSseMetadata.events[], typespec-client-generator-core - # #4882), which is unavailable in the currently pinned TCGC version. The stream's - # terminal event (a string-literal union member such as ``[DONE]``) is detected - # structurally and passed as ``terminal_event`` below, so the runtime stops - # before this callback attempts to JSON-parse it. + # SSE payloads arrive as raw ``data`` strings; the terminal marker (e.g. ``[DONE]``) + # is consumed by the runtime before this callback runs (see ``terminal_event``). retval.append(" _event_json = json.loads(_event.data)") + named_events = [ + (event_type, event_item_type) + for event_type, event_item_type in streaming_events + if event_type + ] + if named_events: + # Heterogeneous SSE: route each ``event:`` name to its concrete payload model + # (TCGC ``sseMetadata``), yielding distinct model instances. + for index, (event_type, event_item_type) in enumerate(named_events): + event_annotation = event_item_type.type_annotation( + is_operation_file=True, serialize_namespace=self.serialize_namespace + ) + keyword = "if" if index == 0 else "elif" + retval.append(f' {keyword} _event.event == {event_type!r}:') + retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") + retval.append(" else:") + retval.append(" deserialized = _event_json") + elif streaming_events: + # Homogeneous SSE: a single (unnamed) event type deserialized into its model. + _event_type, event_item_type = streaming_events[0] + event_annotation = event_item_type.type_annotation( + is_operation_file=True, serialize_namespace=self.serialize_namespace + ) + retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") + else: + # No per-event metadata: best-effort deserialize against the union item type. + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") else: retval.append(" _event_json = _event.json()") - retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") retval.append(" if cls:") retval.append(" return cls(pipeline_response, deserialized, {}) # type: ignore") retval.append(" return deserialized") diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index 37ae20d62e5..b06b9e27458 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -18,26 +18,26 @@ }, "devDependencies": { "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@azure-tools/typespec-autorest": "~0.70.0", - "@azure-tools/typespec-azure-core": "~0.70.0", - "@azure-tools/typespec-azure-resource-manager": "~0.70.0", - "@azure-tools/typespec-azure-rulesets": "~0.70.0", - "@azure-tools/typespec-client-generator-core": "~0.70.0", + "@azure-tools/typespec-autorest": "0.71.0-dev.4", + "@azure-tools/typespec-azure-core": "0.71.0-dev.4", + "@azure-tools/typespec-azure-resource-manager": "0.71.0-dev.11", + "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", + "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", "@types/semver": "7.5.8", - "@typespec/compiler": "^1.14.0", - "@typespec/events": "~0.84.0", - "@typespec/http": "^1.14.0", + "@typespec/compiler": "1.15.0-dev.17", + "@typespec/events": "0.85.0-dev.0", + "@typespec/http": "1.15.0-dev.5", "@typespec/http-specs": "0.1.0-alpha.40", - "@typespec/openapi": "^1.14.0", - "@typespec/rest": "~0.84.0", + "@typespec/openapi": "1.15.0-dev.2", + "@typespec/rest": "0.85.0-dev.1", "@typespec/spec-api": "0.1.0-alpha.15", "@typespec/spector": "0.1.0-alpha.27", - "@typespec/sse": "~0.84.0", - "@typespec/streams": "~0.84.0", - "@typespec/versioning": "~0.84.0", - "@typespec/xml": "~0.84.0", + "@typespec/sse": "0.85.0-dev.0", + "@typespec/streams": "0.85.0-dev.1", + "@typespec/versioning": "0.85.0-dev.0", + "@typespec/xml": "0.85.0-dev.0", "c8": "^10.1.3", "picocolors": "~1.1.1", "prettier": "^3.9.5", @@ -89,9 +89,9 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.70.0.tgz", - "integrity": "sha512-OaxLkgMcuOXAbaqTNpezmFF24jtkiIH1+2PBwAeRo3ZG7C1r7Hf8xZwCK6KVtBEgMbqnrd5eCqxsPl1zy3y9/Q==", + "version": "0.71.0-dev.4", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-autorest/-/typespec-autorest-0.71.0-dev.4.tgz", + "integrity": "sha1-p920uaoYRzfFVIeEUDW6+uUri1w=", "dev": true, "license": "MIT", "dependencies": { @@ -101,9 +101,9 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0", - "@azure-tools/typespec-client-generator-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.10", + "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", "@typespec/compiler": "^1.14.0", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", @@ -118,9 +118,9 @@ } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.70.0.tgz", - "integrity": "sha512-8MojHWRtTLKycJJ98IMoXX/5b9tTo3F0d3Iu20OKoCsORnSDG2NfjOWHJVW63oxA2t8VTlqC6J8BDcnRihygQQ==", + "version": "0.71.0-dev.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.71.0-dev.4.tgz", + "integrity": "sha1-2pFZNHjIm0YyvV6pBaiHajFP9XQ=", "dev": true, "license": "MIT", "engines": { @@ -133,9 +133,9 @@ } }, "node_modules/@azure-tools/typespec-azure-resource-manager": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.70.0.tgz", - "integrity": "sha512-hVrbbsOhU3EQ2yQTppCqsGQwY/HcVZPOINtFkoUo+PUVBmCFXyqLkTO4jvUbsp/LvJEwoQ8aEA8Y35f7VWT5uw==", + "version": "0.71.0-dev.11", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.71.0-dev.11.tgz", + "integrity": "sha1-AXbWvNDsj4qydLZsGg3SROpD7dE=", "dev": true, "license": "MIT", "dependencies": { @@ -146,7 +146,7 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.4", "@typespec/compiler": "^1.14.0", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", @@ -155,25 +155,25 @@ } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.70.0.tgz", - "integrity": "sha512-Uxxl/18oryDwk2S+aYx6cIqiyjmoMeFDGmjuQ72a+aw6u8mZjgahMxNsY0ShvGLSchjsDqsVGaUlazXGXakVrw==", + "version": "0.71.0-dev.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.71.0-dev.5.tgz", + "integrity": "sha1-bpzvEq9boKSAhUwBc+EKXvxNOwI=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0", - "@azure-tools/typespec-client-generator-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.4", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.11", + "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", "@typespec/compiler": "^1.14.0" } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.70.0.tgz", - "integrity": "sha512-8yxOYJfID3wp3FLQYNIa3kbmR5YLWjYtpB+i4u66quHTTQWWANHV1/o9f8xymAf+8fO9jbLo5tw1JerumxISWg==", + "version": "0.71.0-dev.11", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.71.0-dev.11.tgz", + "integrity": "sha1-6FheAB+SgoqzGYHvqSQMvS9nC6s=", "dev": true, "license": "MIT", "dependencies": { @@ -185,7 +185,7 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", "@typespec/compiler": "^1.14.0", "@typespec/events": "^0.84.0", "@typespec/http": "^1.14.0", @@ -997,259 +997,6 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "peer": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@inquirer/ansi": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz", @@ -2059,14 +1806,6 @@ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/@types/node": { "version": "25.0.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", @@ -2327,9 +2066,9 @@ } }, "node_modules/@typespec/compiler": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.14.0.tgz", - "integrity": "sha512-RRN0LGVDlonG/IbB2b4mvRjdCo6LywwB9/J8lOp6UaH7vtaFnKe5FL+rpxhof4rXx/zI/4OWnQO6c01bTCz4/Q==", + "version": "1.15.0-dev.17", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/compiler/-/compiler-1.15.0-dev.17.tgz", + "integrity": "sha1-hrlL0q3kmaR0fWNddCfI7LA2BCY=", "dev": true, "license": "MIT", "dependencies": { @@ -2341,7 +2080,7 @@ "is-unicode-supported": "^2.1.0", "mustache": "^4.2.0", "picocolors": "^1.1.1", - "prettier": "^3.8.1", + "prettier": "^3.9.5", "semver": "^7.7.4", "tar": "^7.5.13", "temporal-polyfill": "^1.0.1", @@ -2440,30 +2179,30 @@ } }, "node_modules/@typespec/events": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.84.0.tgz", - "integrity": "sha512-UroDIu6t6Z+cOLyX8I+GJWhSFmYGrp1L93F7ZVt0Ypmj0ndmC9YYa4cpeEyS5PDDIC8u49WfCIwfGegxt4rPVQ==", + "version": "0.85.0-dev.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/events/-/events-0.85.0-dev.0.tgz", + "integrity": "sha1-GJSz2/FMSWBRCyfJOpLO6Sa7n9Q=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" } }, "node_modules/@typespec/http": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.14.0.tgz", - "integrity": "sha512-W+heCzu8K63AVcoX8MachVWaRxSAMFWOI1yBTc2Kq8QHaJeDiLL5JbU8VfTZ4tL/6EoGSdKfIT5ZNRW7oVCzhg==", + "version": "1.15.0-dev.5", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/http/-/http-1.15.0-dev.5.tgz", + "integrity": "sha1-4oMLhD+Mvj2PLbagkX7ok9/Hsbk=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/streams": "^0.84.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", + "@typespec/streams": "^0.84.0 || >= 0.85.0-dev.1" }, "peerDependenciesMeta": { "@typespec/streams": { @@ -2495,31 +2234,31 @@ } }, "node_modules/@typespec/openapi": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.14.0.tgz", - "integrity": "sha512-KL7kImPhCXRmxpHVt1k7TWaa4bb3NbSeUx2rxyxeq7lYZFllI6/NYRCTOI/5JOrbElWmmSxrajU9K9IAKI6PkQ==", + "version": "1.15.0-dev.2", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/openapi/-/openapi-1.15.0-dev.2.tgz", + "integrity": "sha1-e59k965dOfi7Ds8pI3JiJBtILqM=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/http": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", + "@typespec/http": "^1.14.0 || >= 1.15.0-dev.4" } }, "node_modules/@typespec/rest": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.84.0.tgz", - "integrity": "sha512-9s5dDfRoHRPdtbVvkBasUx/RnMvwWMTuXRieSQDEji4gWGgxVu4Zt4MiEEKSfQrkMr3Aw0QjRCSxBxjMCHIOmA==", + "version": "0.85.0-dev.1", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/rest/-/rest-0.85.0-dev.1.tgz", + "integrity": "sha1-yLZhv0pD125R8ohNtwhGSC6e/iA=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/http": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", + "@typespec/http": "^1.14.0 || >= 1.15.0-dev.4" } }, "node_modules/@typespec/spec-api": { @@ -2614,6 +2353,84 @@ "node": ">=22.0.0" } }, + "node_modules/@typespec/spector/node_modules/@typespec/compiler": { + "version": "1.14.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/compiler/-/compiler-1.14.0.tgz", + "integrity": "sha1-2FXCBu7K+j54eOf0JVthKC8FP0Y=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@inquirer/prompts": "^8.4.1", + "ajv": "^8.18.0", + "change-case": "^5.4.4", + "env-paths": "^4.0.0", + "is-unicode-supported": "^2.1.0", + "mustache": "^4.2.0", + "picocolors": "^1.1.1", + "prettier": "^3.8.1", + "semver": "^7.7.4", + "tar": "^7.5.13", + "temporal-polyfill": "^1.0.1", + "vscode-languageserver": "^10.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "yaml": "^2.8.3", + "yargs": "^18.0.0" + }, + "bin": { + "tsp": "cmd/tsp.js", + "tsp-server": "cmd/tsp-server.js" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@typespec/spector/node_modules/@typespec/http": { + "version": "1.14.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/http/-/http-1.14.0.tgz", + "integrity": "sha1-La9yB2Ny8FhnXSBbst9wYQBiOq4=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.14.0", + "@typespec/streams": "^0.84.0" + }, + "peerDependenciesMeta": { + "@typespec/streams": { + "optional": true + } + } + }, + "node_modules/@typespec/spector/node_modules/@typespec/rest": { + "version": "0.84.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/rest/-/rest-0.84.0.tgz", + "integrity": "sha1-kMLB39G8geZbiA3EEdQBaqteuuA=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.14.0", + "@typespec/http": "^1.14.0" + } + }, + "node_modules/@typespec/spector/node_modules/@typespec/versioning": { + "version": "0.84.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/versioning/-/versioning-0.84.0.tgz", + "integrity": "sha1-YS06C7uMMWXKp7vwuy8qV8kjr38=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.14.0" + } + }, "node_modules/@typespec/spector/node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -2636,6 +2453,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@typespec/spector/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typespec/spector/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -2683,32 +2513,32 @@ } }, "node_modules/@typespec/sse": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.84.0.tgz", - "integrity": "sha512-9joNgVisRCWDFfV1d79iTAuR1W/6r+AKJrKUfcjsaTrq5A8OWW3v5TTsfxbHAZArn7n2WxQkqhNGgNyc8LjEng==", + "version": "0.85.0-dev.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/sse/-/sse-0.85.0-dev.0.tgz", + "integrity": "sha1-X+R88ExpK6jsjjGMb5OEdLZCuU4=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/events": "^0.84.0", - "@typespec/http": "^1.14.0", - "@typespec/streams": "^0.84.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0", + "@typespec/events": "^0.84.0 || >= 0.85.0-dev.0", + "@typespec/http": "^1.14.0 || >= 1.15.0-dev.0", + "@typespec/streams": "^0.84.0 || >= 0.85.0-dev.0" } }, "node_modules/@typespec/streams": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.84.0.tgz", - "integrity": "sha512-SDneR8+zY+ueOpzg9yJtttfDe/ikB99JgddZSXKPwiDPlAIEeEvI8auipcYfB58EEOB21h8Oq0tEm8HqiAAWdQ==", + "version": "0.85.0-dev.1", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/streams/-/streams-0.85.0-dev.1.tgz", + "integrity": "sha1-HkU7ikG1A6HP8PPPFNyheTRc0Lw=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17" } }, "node_modules/@typespec/ts-http-runtime": { @@ -2727,29 +2557,29 @@ } }, "node_modules/@typespec/versioning": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.84.0.tgz", - "integrity": "sha512-ZoDasTDj4z0mgFK+0cJL2+7DduCaTjvICHL2nQ/RBWc7nLgObaIYCjvXLno8WneDXnpxCAr7larN4/nlHEv9fg==", + "version": "0.85.0-dev.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/versioning/-/versioning-0.85.0-dev.0.tgz", + "integrity": "sha1-FaJ7l5PpzXK+E4Hkd7Q1LXck3hI=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" } }, "node_modules/@typespec/xml": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.84.0.tgz", - "integrity": "sha512-3x0spgIrr4u3azkYaOxrlumtjoqPiUnJ/G5RwGBmUCAeE5F413MHf/AeIkmZ2ULT1gY3myabfZp8bOijTbMk7A==", + "version": "0.85.0-dev.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/xml/-/xml-0.85.0-dev.0.tgz", + "integrity": "sha1-oxIznWAobEj7bFVN5T2IusKbWqw=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0" + "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" } }, "node_modules/@vitest/expect": { @@ -2879,31 +2709,6 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -3216,17 +3021,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -3342,14 +3136,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", @@ -3448,13 +3234,6 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "peer": true - }, "node_modules/default-browser": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", @@ -3601,318 +3380,101 @@ "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, - "license": "ISC", - "peer": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "es-errors": "^1.3.0" }, "engines": { - "node": "*" + "node": ">= 0.4" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, "engines": { - "node": ">=0.10" + "node": ">=6" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } + "license": "MIT" }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/estree-walker": { @@ -3925,16 +3487,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -4015,21 +3567,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "peer": true - }, "node_modules/fast-string-truncated-width": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", @@ -4115,19 +3652,6 @@ "fxparser": "src/cli/cli.js" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "peer": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4179,28 +3703,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -4350,19 +3852,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/glob/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -4402,20 +3891,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -4536,46 +4011,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.19" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -4609,16 +4044,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -4628,19 +4053,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "peer": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -4802,13 +4214,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "peer": true - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -4816,13 +4221,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "peer": true - }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -4869,30 +4267,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "peer": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -5211,13 +4585,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "peer": true - }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -5625,24 +4992,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5679,20 +5028,6 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5829,16 +5164,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/prettier": { "version": "3.9.6", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", @@ -5869,17 +5194,6 @@ "node": ">= 0.10" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/pyodide": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.26.2.tgz", @@ -5972,17 +5286,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -6452,20 +5755,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strnum": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", @@ -6744,19 +6033,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -6852,17 +6128,6 @@ "node": ">= 0.8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7158,16 +6423,6 @@ "node": ">=8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index f7910112349..1d8aab77619 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -104,23 +104,23 @@ "tsx": "^4.21.0" }, "devDependencies": { - "@azure-tools/typespec-autorest": "~0.70.0", - "@azure-tools/typespec-azure-core": "~0.70.0", - "@azure-tools/typespec-azure-resource-manager": "~0.70.0", - "@azure-tools/typespec-azure-rulesets": "~0.70.0", - "@azure-tools/typespec-client-generator-core": "~0.70.0", + "@azure-tools/typespec-autorest": "0.71.0-dev.4", + "@azure-tools/typespec-azure-core": "0.71.0-dev.4", + "@azure-tools/typespec-azure-resource-manager": "0.71.0-dev.11", + "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", + "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@typespec/compiler": "^1.14.0", - "@typespec/http": "^1.14.0", - "@typespec/openapi": "^1.14.0", - "@typespec/rest": "~0.84.0", - "@typespec/versioning": "~0.84.0", - "@typespec/events": "~0.84.0", + "@typespec/compiler": "1.15.0-dev.17", + "@typespec/http": "1.15.0-dev.5", + "@typespec/openapi": "1.15.0-dev.2", + "@typespec/rest": "0.85.0-dev.1", + "@typespec/versioning": "0.85.0-dev.0", + "@typespec/events": "0.85.0-dev.0", "@typespec/spector": "0.1.0-alpha.27", "@typespec/spec-api": "0.1.0-alpha.15", - "@typespec/sse": "~0.84.0", - "@typespec/streams": "~0.84.0", - "@typespec/xml": "~0.84.0", + "@typespec/sse": "0.85.0-dev.0", + "@typespec/streams": "0.85.0-dev.1", + "@typespec/xml": "0.85.0-dev.0", "@typespec/http-specs": "0.1.0-alpha.40", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", diff --git a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py index 639aa0cf13d..1b179f5604a 100644 --- a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py +++ b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py @@ -21,18 +21,12 @@ keep the raw byte-iterator behavior (see mock_api/unbranded/test_streaming_jsonl_unbranded.py). -Note on SSE item deserialization: SSE item types are modelled as `@events` unions, -which the generated callback deserializes via `_deserialize("", json)`. -The shared `_deserialize` cannot resolve a forward-ref *string* union member into a -model instance (same root cause as paging item deserialization needing a `module` -argument), so homogeneous SSE items are yielded as parsed JSON (``dict``) rather than -model instances. The tests below assert on the ``dict`` payloads accordingly. - -Still skipped (follow-ups): - -* SSE heterogeneous — blocked on TCGC `sseMetadata` (#4882) for per-event - dispatch + terminal-event handling, plus the union-item `_deserialize` - limitation (parsed JSON rather than model instances). +Note on SSE item deserialization: SSE item types are described by TCGC ``sseMetadata`` +(SdkSseEventMetadata[]), so the generated callback routes each ``event:`` name to its +concrete payload model and yields model instances (homogeneous via the single event +payload, heterogeneous via per-event dispatch). Terminal-event handling (e.g. a trailing +``data: [DONE]``) is wired into the vendored runtime so the stream stops before the +callback is invoked for it. Imports are guarded so collection never errors when the package is absent (e.g. before `regenerate` runs, or for the unbranded flavor). @@ -56,17 +50,23 @@ _HAS_STRUCTURED_JSONL = False -# For the Azure flavor the SSE ``streaming.sse`` package is generated with a structured -# ``unnamed.receive()`` returning ``Stream["_unions.UnnamedEvents"]``. Guarded so -# collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). +# For the Azure flavor the SSE ``streaming.sse`` package is generated with structured +# ``receive()`` methods: ``unnamed.receive()`` returns ``Stream[Info]`` and +# ``named.receive()`` returns ``Stream["_unions.ResponseEvents"]`` dispatched per event. +# Guarded so collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). try: # pragma: no cover - guarded so collection doesn't error when absent from streaming.sse import SseClient # type: ignore from streaming.sse.aio import SseClient as AsyncSseClient # type: ignore + from streaming.sse.unnamed.models import Info as SseInfo # type: ignore + from streaming.sse.named.models import ResponseCreated, ResponseDelta # type: ignore _HAS_STRUCTURED_SSE = True except ImportError: # pragma: no cover SseClient = None # type: ignore AsyncSseClient = None # type: ignore + SseInfo = None # type: ignore + ResponseCreated = None # type: ignore + ResponseDelta = None # type: ignore _HAS_STRUCTURED_SSE = False @@ -95,57 +95,55 @@ async def test_jsonl_receive_structured_async(): @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") def test_sse_receive_homogeneous_structured_sync(): - """SSE homogeneous: unnamed.receive() returns Stream over the SSE events. + """SSE homogeneous: unnamed.receive() returns Stream[Info] of deserialized models. The unnamed SSE scenario emits three ``message`` events with payload - ``{"desc": ...}``. Because the SSE item type is an ``@events`` union, the - generated callback yields parsed JSON (``dict``) rather than ``Info`` model - instances (see module docstring / ``_deserialize`` limitation). The stream - terminates naturally after the final event. + ``{"desc": ...}``. ``sseMetadata`` maps the single (unnamed) event to the ``Info`` + payload model, so the callback yields ``Info`` instances. The stream terminates + naturally after the final event. """ with SseClient(endpoint="http://localhost:3000") as client: items = list(client.unnamed.receive()) - assert [i["desc"] for i in items] == _EXPECTED - assert all(isinstance(i, dict) for i in items) + assert all(isinstance(i, SseInfo) for i in items) + assert [i.desc for i in items] == _EXPECTED @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") @pytest.mark.asyncio async def test_sse_receive_homogeneous_structured_async(): - """Async SSE homogeneous: unnamed.receive() returns AsyncStream over the events.""" + """Async SSE homogeneous: unnamed.receive() returns AsyncStream[Info].""" async with AsyncSseClient(endpoint="http://localhost:3000") as client: stream = await client.unnamed.receive() items = [item async for item in stream] - assert [i["desc"] for i in items] == _EXPECTED - assert all(isinstance(i, dict) for i in items) + assert all(isinstance(i, SseInfo) for i in items) + assert [i.desc for i in items] == _EXPECTED @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") def test_sse_receive_heterogeneous_structured_sync(): - """SSE heterogeneous: named.receive() returns Stream over an ``@events`` union. + """SSE heterogeneous: named.receive() dispatches each event to its payload model. The named SSE scenario emits ``responseCreated`` (``{"id": ...}``) then two ``responseDelta`` (``{"delta": ...}``) events, followed by a terminal - ``data: [DONE]`` event. ``[DONE]`` is a string-literal member of the item union, - so the generator wires it as ``terminal_event`` and the runtime stops there - (without trying to JSON-parse ``[DONE]``). Per-event payloads are yielded as - parsed JSON (``dict``) rather than distinct ``ResponseCreated`` / ``ResponseDelta`` - model instances: discriminating them needs TCGC ``sseMetadata`` (#4882) plus a - ``module`` argument on the shared ``_deserialize`` (same limitation as paging - item deserialization). + ``data: [DONE]`` event. ``sseMetadata`` routes ``responseCreated`` -> ``ResponseCreated`` + and ``responseDelta`` -> ``ResponseDelta``, so the callback yields distinct model + instances. ``[DONE]`` is wired as ``terminal_event``: the runtime stops there (without + trying to JSON-parse ``[DONE]``). """ with SseClient(endpoint="http://localhost:3000") as client: items = list(client.named.receive()) - assert all(isinstance(i, dict) for i in items) - assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] + assert [type(i) for i in items] == [ResponseCreated, ResponseDelta, ResponseDelta] + assert items[0].id == "resp_1" + assert [i.delta for i in items[1:]] == ["Hello", " world"] @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") @pytest.mark.asyncio async def test_sse_receive_heterogeneous_structured_async(): - """Async SSE heterogeneous: named.receive() returns AsyncStream, terminating at [DONE].""" + """Async SSE heterogeneous: named.receive() dispatches per event, terminating at [DONE].""" async with AsyncSseClient(endpoint="http://localhost:3000") as client: stream = await client.named.receive() items = [item async for item in stream] - assert all(isinstance(i, dict) for i in items) - assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] + assert [type(i) for i in items] == [ResponseCreated, ResponseDelta, ResponseDelta] + assert items[0].id == "resp_1" + assert [i.delta for i in items[1:]] == ["Hello", " world"] From 0185b279c096733b78d363c252437b543eaffa03 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 15:32:36 -0700 Subject: [PATCH 03/15] fix(http-client-python): satisfy pylint in builder_serializer Extract the multi-response body handling out of `handle_response` into a `_handle_response_body` helper so `handle_response` stays under the too-many-statements limit after the structured-stream branch was added. Output is byte-identical (verified via azure streaming regen). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../codegen/serializers/builder_serializer.py | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index bea59017923..83787acb362 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1320,6 +1320,43 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) return retval + def _handle_response_body(self, builder: OperationType) -> list[str]: # pylint: disable=too-many-nested-blocks + retval: list[str] = [] + if len(builder.responses) > 1: + status_codes, res_headers, res_deserialization = [], [], [] + for status_code in builder.success_status_codes: + response = builder.get_response_from_status(status_code) # type: ignore + if response.headers or response.type: + status_codes.append(status_code) + res_headers.append(self.response_headers(response)) + res_deserialization.append(self.response_deserialization(builder, response)) + + is_headers_same = _all_same(res_headers) + is_deserialization_same = _all_same(res_deserialization) + if is_deserialization_same: + if is_headers_same: + retval.extend(res_headers[0]) + retval.extend(res_deserialization[0]) + retval.append("") + else: + for status_code, headers in zip(status_codes, res_headers): + if headers: + retval.append(f"if response.status_code == {status_code}:") + retval.extend([f" {line}" for line in headers]) + retval.append("") + retval.extend(res_deserialization[0]) + retval.append("") + else: + for status_code, headers, deserialization in zip(status_codes, res_headers, res_deserialization): + retval.append(f"if response.status_code == {status_code}:") + retval.extend([f" {line}" for line in headers]) + retval.extend([f" {line}" for line in deserialization]) + retval.append("") + else: + retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0])) + retval.append("") + return retval + def handle_response(self, builder: OperationType) -> list[str]: retval: list[str] = ["response = pipeline_response.http_response"] retval.append("") @@ -1332,40 +1369,8 @@ def handle_response(self, builder: OperationType) -> list[str]: retval.append("deserialized = None") if builder.any_response_has_headers: retval.append("response_headers = {}") - if builder.has_response_body or builder.any_response_has_headers: # pylint: disable=too-many-nested-blocks - if len(builder.responses) > 1: - status_codes, res_headers, res_deserialization = [], [], [] - for status_code in builder.success_status_codes: - response = builder.get_response_from_status(status_code) # type: ignore - if response.headers or response.type: - status_codes.append(status_code) - res_headers.append(self.response_headers(response)) - res_deserialization.append(self.response_deserialization(builder, response)) - - is_headers_same = _all_same(res_headers) - is_deserialization_same = _all_same(res_deserialization) - if is_deserialization_same: - if is_headers_same: - retval.extend(res_headers[0]) - retval.extend(res_deserialization[0]) - retval.append("") - else: - for status_code, headers in zip(status_codes, res_headers): - if headers: - retval.append(f"if response.status_code == {status_code}:") - retval.extend([f" {line}" for line in headers]) - retval.append("") - retval.extend(res_deserialization[0]) - retval.append("") - else: - for status_code, headers, deserialization in zip(status_codes, res_headers, res_deserialization): - retval.append(f"if response.status_code == {status_code}:") - retval.extend([f" {line}" for line in headers]) - retval.extend([f" {line}" for line in deserialization]) - retval.append("") - else: - retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0])) - retval.append("") + if builder.has_response_body or builder.any_response_has_headers: + retval.extend(self._handle_response_body(builder)) if ( builder.has_optional_return_type or self.code_model.options["models-mode"] From f52d62ef9b8f35e714fdf0be51a462485240ed9e Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 19:04:00 -0700 Subject: [PATCH 04/15] fix(http-client-python): resolve streaming codegen CI failures - cspell: add aenter/aexit/aiter/anext/isascii used by the vendored streaming runtime template - prettier: reformat package-lock.json - pylint: add matching :keyword:/:paramtype: docstrings to JSONLEvent/ServerSentEvent in streaming_base.py.jinja2 to satisfy azure guidelines checker C4758 (docstring-keyword-should-match-keyword-only) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- cspell.yaml | 5 + .../templates/streaming_base.py.jinja2 | 13 + packages/http-client-python/package-lock.json | 425 +++++------------- 3 files changed, 124 insertions(+), 319 deletions(-) diff --git a/cspell.yaml b/cspell.yaml index 4fdbe6d4b2f..6273e27f277 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -7,11 +7,15 @@ dictionaries: words: - Ablack - Adoptium + - aenter + - aexit - agentic - agentics - aiohttp + - aiter - alzimmer - amqp + - anext - AQID - Arize - arizeaiobservabilityeval @@ -120,6 +124,7 @@ words: - intrinsics - ints - IOHTTP + - isascii - isdigit - isinstance - issecret diff --git a/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 index 4cb67922530..1c422a7a391 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/streaming_base.py.jinja2 @@ -73,6 +73,8 @@ class JSONLEvent: :ivar data: The raw JSONL record. :vartype data: str or None + :keyword data: The raw JSONL record. + :paramtype data: str or None """ def __init__( @@ -192,6 +194,17 @@ class ServerSentEvent: :vartype id: str :ivar retry: The reconnection time in milliseconds, if the stream provided one. :vartype retry: int or None + :keyword event: The event type. Defaults to ``"message"`` when the stream does not + specify one. + :paramtype event: str + :keyword data: The event payload. Multiple ``data`` lines are joined with ``"\\n"``. + Left as a raw string; the caller is responsible for any further parsing. + :paramtype data: str + :keyword id: The last event ID. Defaults to an empty string until the stream + provides one. + :paramtype id: str + :keyword retry: The reconnection time in milliseconds, if the stream provided one. + :paramtype retry: int or None """ def __init__( diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index b06b9e27458..c4d87b0d949 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -544,14 +544,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], + "cpu": ["ppc64"], "license": "MIT", "optional": true, - "os": [ - "aix" - ], + "os": ["aix"], "engines": { "node": ">=18" } @@ -560,14 +556,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "license": "MIT", "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=18" } @@ -576,14 +568,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=18" } @@ -592,14 +580,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=18" } @@ -608,14 +592,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=18" } @@ -624,14 +604,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=18" } @@ -640,14 +616,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": ">=18" } @@ -656,14 +628,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": ">=18" } @@ -672,14 +640,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -688,14 +652,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -704,14 +664,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], + "cpu": ["ia32"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -720,14 +676,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], + "cpu": ["loong64"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -736,14 +688,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], + "cpu": ["mips64el"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -752,14 +700,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], + "cpu": ["ppc64"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -768,14 +712,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], + "cpu": ["riscv64"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -784,14 +724,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], + "cpu": ["s390x"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -800,14 +736,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=18" } @@ -816,14 +748,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "netbsd" - ], + "os": ["netbsd"], "engines": { "node": ">=18" } @@ -832,14 +760,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "netbsd" - ], + "os": ["netbsd"], "engines": { "node": ">=18" } @@ -848,14 +772,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "openbsd" - ], + "os": ["openbsd"], "engines": { "node": ">=18" } @@ -864,14 +784,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "openbsd" - ], + "os": ["openbsd"], "engines": { "node": ">=18" } @@ -880,14 +796,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "openharmony" - ], + "os": ["openharmony"], "engines": { "node": ">=18" } @@ -896,14 +808,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "sunos" - ], + "os": ["sunos"], "engines": { "node": ">=18" } @@ -912,14 +820,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=18" } @@ -928,14 +832,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], + "cpu": ["ia32"], "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=18" } @@ -944,14 +844,10 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=18" } @@ -1491,15 +1387,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1508,15 +1400,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1525,15 +1413,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1542,15 +1426,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1559,15 +1439,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1576,15 +1452,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1593,15 +1465,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1610,15 +1478,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", - "cpu": [ - "ppc64" - ], + "cpu": ["ppc64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1627,15 +1491,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", - "cpu": [ - "s390x" - ], + "cpu": ["s390x"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1644,15 +1504,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1661,15 +1517,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1678,15 +1530,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "openharmony" - ], + "os": ["openharmony"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1695,9 +1543,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], + "cpu": ["wasm32"], "dev": true, "license": "MIT", "optional": true, @@ -1714,15 +1560,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1731,15 +1573,11 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -3141,9 +2979,7 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "dev": true, - "engines": [ - "node >= 6.0" - ], + "engines": ["node >= 6.0"], "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -3745,9 +3581,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -4301,15 +4135,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">= 12.0.0" }, @@ -4322,15 +4152,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">= 12.0.0" }, @@ -4343,15 +4169,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">= 12.0.0" }, @@ -4364,15 +4186,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": ">= 12.0.0" }, @@ -4385,15 +4203,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -4406,15 +4220,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -4427,15 +4237,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -4448,15 +4254,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -4469,15 +4271,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -4490,15 +4288,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">= 12.0.0" }, @@ -4511,15 +4305,11 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "license": "MPL-2.0", "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">= 12.0.0" }, @@ -4934,10 +4724,7 @@ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], + "funding": ["https://github.com/sponsors/sxzz", "https://opencollective.com/debug"], "license": "MIT" }, "node_modules/on-finished": { From b8414234cdc7814677124dec8b941b642d26d0e8 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 19:26:42 -0700 Subject: [PATCH 05/15] fix(http-client-python): apply canonical prettier formatting package-lock.json uses prettier's json-stringify parser (like package.json); reformat streaming.test.ts to printWidth 100. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../emitter/test/streaming.test.ts | 5 +- packages/http-client-python/package-lock.json | 425 +++++++++++++----- 2 files changed, 323 insertions(+), 107 deletions(-) diff --git a/packages/http-client-python/emitter/test/streaming.test.ts b/packages/http-client-python/emitter/test/streaming.test.ts index ec5c6a078e4..9fc025e7bf0 100644 --- a/packages/http-client-python/emitter/test/streaming.test.ts +++ b/packages/http-client-python/emitter/test/streaming.test.ts @@ -10,7 +10,10 @@ describe("typespec-python: structured streaming", () => { it("unwraps nullable payloads", () => { strictEqual(isStructuredStreamType({ kind: "nullable", type: { kind: "model" } } as any), true); - strictEqual(isStructuredStreamType({ kind: "nullable", type: { kind: "bytes" } } as any), false); + strictEqual( + isStructuredStreamType({ kind: "nullable", type: { kind: "bytes" } } as any), + false, + ); }); it("treats bare byte/string payloads as unstructured", () => { diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index c4d87b0d949..b06b9e27458 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -544,10 +544,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": ["ppc64"], + "cpu": [ + "ppc64" + ], "license": "MIT", "optional": true, - "os": ["aix"], + "os": [ + "aix" + ], "engines": { "node": ">=18" } @@ -556,10 +560,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": ["arm"], + "cpu": [ + "arm" + ], "license": "MIT", "optional": true, - "os": ["android"], + "os": [ + "android" + ], "engines": { "node": ">=18" } @@ -568,10 +576,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["android"], + "os": [ + "android" + ], "engines": { "node": ">=18" } @@ -580,10 +592,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["android"], + "os": [ + "android" + ], "engines": { "node": ">=18" } @@ -592,10 +608,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": ">=18" } @@ -604,10 +624,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": ">=18" } @@ -616,10 +640,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["freebsd"], + "os": [ + "freebsd" + ], "engines": { "node": ">=18" } @@ -628,10 +656,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["freebsd"], + "os": [ + "freebsd" + ], "engines": { "node": ">=18" } @@ -640,10 +672,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": ["arm"], + "cpu": [ + "arm" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -652,10 +688,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -664,10 +704,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": ["ia32"], + "cpu": [ + "ia32" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -676,10 +720,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": ["loong64"], + "cpu": [ + "loong64" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -688,10 +736,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": ["mips64el"], + "cpu": [ + "mips64el" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -700,10 +752,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": ["ppc64"], + "cpu": [ + "ppc64" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -712,10 +768,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": ["riscv64"], + "cpu": [ + "riscv64" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -724,10 +784,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": ["s390x"], + "cpu": [ + "s390x" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -736,10 +800,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">=18" } @@ -748,10 +816,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["netbsd"], + "os": [ + "netbsd" + ], "engines": { "node": ">=18" } @@ -760,10 +832,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["netbsd"], + "os": [ + "netbsd" + ], "engines": { "node": ">=18" } @@ -772,10 +848,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["openbsd"], + "os": [ + "openbsd" + ], "engines": { "node": ">=18" } @@ -784,10 +864,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["openbsd"], + "os": [ + "openbsd" + ], "engines": { "node": ">=18" } @@ -796,10 +880,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["openharmony"], + "os": [ + "openharmony" + ], "engines": { "node": ">=18" } @@ -808,10 +896,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["sunos"], + "os": [ + "sunos" + ], "engines": { "node": ">=18" } @@ -820,10 +912,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "license": "MIT", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": ">=18" } @@ -832,10 +928,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": ["ia32"], + "cpu": [ + "ia32" + ], "license": "MIT", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": ">=18" } @@ -844,10 +944,14 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "license": "MIT", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": ">=18" } @@ -1387,11 +1491,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["android"], + "os": [ + "android" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1400,11 +1508,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1413,11 +1525,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1426,11 +1542,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["freebsd"], + "os": [ + "freebsd" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1439,11 +1559,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", - "cpu": ["arm"], + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1452,11 +1576,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1465,11 +1593,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1478,11 +1610,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", - "cpu": ["ppc64"], + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1491,11 +1627,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", - "cpu": ["s390x"], + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1504,11 +1644,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1517,11 +1661,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1530,11 +1678,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["openharmony"], + "os": [ + "openharmony" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1543,7 +1695,9 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": ["wasm32"], + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", "optional": true, @@ -1560,11 +1714,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1573,11 +1731,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -2979,7 +3141,9 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "dev": true, - "engines": ["node >= 6.0"], + "engines": [ + "node >= 6.0" + ], "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -3581,7 +3745,9 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -4135,11 +4301,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["android"], + "os": [ + "android" + ], "engines": { "node": ">= 12.0.0" }, @@ -4152,11 +4322,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": ">= 12.0.0" }, @@ -4169,11 +4343,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["darwin"], + "os": [ + "darwin" + ], "engines": { "node": ">= 12.0.0" }, @@ -4186,11 +4364,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["freebsd"], + "os": [ + "freebsd" + ], "engines": { "node": ">= 12.0.0" }, @@ -4203,11 +4385,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": ["arm"], + "cpu": [ + "arm" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, @@ -4220,11 +4406,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, @@ -4237,11 +4427,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, @@ -4254,11 +4448,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, @@ -4271,11 +4469,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["linux"], + "os": [ + "linux" + ], "engines": { "node": ">= 12.0.0" }, @@ -4288,11 +4490,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": ["arm64"], + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, @@ -4305,11 +4511,15 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": ["x64"], + "cpu": [ + "x64" + ], "dev": true, "license": "MPL-2.0", "optional": true, - "os": ["win32"], + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, @@ -4724,7 +4934,10 @@ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, - "funding": ["https://github.com/sponsors/sxzz", "https://opencollective.com/debug"], + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], "license": "MIT" }, "node_modules/on-finished": { From 37beb6538e59794d42f17b0e546f77bdde36584e Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 19:41:14 -0700 Subject: [PATCH 06/15] revert(http-client-python): drop prerelease TCGC bump; keep streaming on stable deps The 0.71.0-dev / 1.15-dev prerelease stack needed for per-event SSE model dispatch has no coherent published wave, so the ADO 'Python - Build' gate (npm ls -a) fails on peer-incoherent deps and legacy-peer-deps drops peers like eslint. Revert the dep bump (and the sseMetadata-driven model dispatch) back to the stable ~0.70/^1.14 line: SSE items deserialize via the structural union scan (dicts) with terminal-event handling, which is fully green in CI. Per-event model dispatch is deferred until a coherent TCGC release ships SdkSseMetadata. Keeps the C4758 docstring fix in streaming_base.py.jinja2 and the cspell additions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../changes/structured-streaming-2026-0-0.md | 8 +- packages/http-client-python/.npmrc | 7 - packages/http-client-python/README.md | 10 +- .../http-client-python/emitter/src/http.ts | 48 +- .../pygen/codegen/models/response.py | 64 +- .../codegen/serializers/builder_serializer.py | 114 +- packages/http-client-python/package-lock.json | 1223 +++++++++++++---- packages/http-client-python/package.json | 28 +- .../azure/test_streaming_structured.py | 74 +- 9 files changed, 1116 insertions(+), 460 deletions(-) diff --git a/.chronus/changes/structured-streaming-2026-0-0.md b/.chronus/changes/structured-streaming-2026-0-0.md index f12b46f6ca6..41069aaf26d 100644 --- a/.chronus/changes/structured-streaming-2026-0-0.md +++ b/.chronus/changes/structured-streaming-2026-0-0.md @@ -17,10 +17,8 @@ for thing in stream: # deserialized model instances ... ``` -For SSE, each `event:` name is routed to its concrete payload model (via TCGC `sseMetadata`), so the stream yields distinct model instances — homogeneous streams via their single event payload and heterogeneous (`@events`) streams via per-event dispatch. A `@terminalEvent` marker (e.g. `"[DONE]"`) is wired into the runtime as `terminal_event`, so iteration stops before the marker is deserialized. - Known limitations / follow-ups: -- SSE events flagged `isEventEnvelope` are not yet specially unwrapped; the payload is deserialized directly. No current spector SSE scenario exercises this. -- Per-event SSE model dispatch consumes TCGC `sseMetadata` (`SdkSseMetadata.events[]`), first available in `@azure-tools/typespec-client-generator-core` `0.71.0-dev.11`, which targets the `@typespec` 1.15-dev / 0.85-dev prerelease line; the package's `devDependencies` pin those prereleases. The generated runtime is unaffected (released `azure.core.rest` only). -- In-repo mock_api coverage: JSONL homogeneous (sync + async) runs against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`, yielding `Info`) and heterogeneous (`named/receive`, yielding `ResponseCreated` / `ResponseDelta` and terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded model instances. +- SSE union item types deserialize to parsed JSON (e.g. `dict`) rather than model instances — same root cause as paging item deserialization; the shared `_deserialize` needs a `module` argument to resolve forward-reference union member names. +- Heterogeneous SSE **terminal-event** handling is supported: the terminal marker (e.g. `"[DONE]"`) is detected structurally as a string-literal member of the item union and passed to the vendored `Stream` / `AsyncStream` as `terminal_event`, so iteration stops before parsing it. Per-event **model dispatch** (routing each `@events` event to its distinct payload model) is still blocked on TCGC `sseMetadata` (typespec-client-generator-core #4882), absent from the resolved TCGC version; until then heterogeneous events are yielded as parsed JSON. +- In-repo mock_api coverage: JSONL homogeneous (sync + async) is active against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`) and heterogeneous (`named/receive`, terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded event payloads (as `dict`s per the union-deserialization limitation). diff --git a/packages/http-client-python/.npmrc b/packages/http-client-python/.npmrc index 9fc3f486ddf..b6f27f13595 100644 --- a/packages/http-client-python/.npmrc +++ b/packages/http-client-python/.npmrc @@ -1,8 +1 @@ engine-strict=true -# Prerelease @typespec / @azure-tools builds (needed for TCGC sseMetadata, which drives -# per-event SSE model dispatch) are published to the azure-sdk-for-js Azure DevOps feed. -@typespec:registry=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ -@azure-tools:registry=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ -# The prerelease waves are not fully cross-aligned (e.g. azure-http-specs still peers on the -# stable @typespec/@azure-tools line), so peer resolution requires legacy behavior. -legacy-peer-deps=true diff --git a/packages/http-client-python/README.md b/packages/http-client-python/README.md index 34f618be5a4..14ee885d1cd 100644 --- a/packages/http-client-python/README.md +++ b/packages/http-client-python/README.md @@ -169,10 +169,12 @@ for thing in stream: # deserialized model instances The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vendored** into the generated package at `_utils/streaming_base.py` (alongside `_utils/model_base.py`). It depends only on the released `azure.core.rest`, so no unreleased `azure.core.streaming` (azure-core PR #48077) dependency is required at runtime. -> **Note:** For SSE responses, each `event:` name is routed to its concrete payload model (via TCGC `sseMetadata`), so the stream yields fully deserialized model instances — a homogeneous stream via its single event payload, a heterogeneous (`@events`) stream via per-event dispatch. A `@terminalEvent` marker (e.g. `"[DONE]"`) is wired into the runtime as `terminal_event`, so iteration stops before the marker is deserialized. JSONL responses with a single model item type likewise yield model instances. +> **Note:** For SSE responses whose item type is a union (`@events`), each event payload is currently yielded as the parsed JSON value (e.g. a `dict` for object payloads, or the literal for terminal events such as `"[DONE]"`) rather than a fully deserialized model instance. This mirrors the existing union item-deserialization behavior used elsewhere in the generator. JSONL responses with a single model item type are deserialized into model instances. #### Known limitations / follow-ups -- **SSE data-envelope events** — Events flagged `isEventEnvelope` (the payload is wrapped in an envelope object) are not yet specially unwrapped; they fall through to the common path and the payload is deserialized directly. None of the current spector SSE scenarios exercise this case. -- **Prerelease dependency requirement** — Per-event SSE model dispatch consumes TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`), first available in `@azure-tools/typespec-client-generator-core` `0.71.0-dev.11`. That build targets the `@typespec` 1.15-dev / 0.85-dev prerelease line, so the package's `devDependencies` pin those prerelease versions (resolved from the `azure-sdk-for-js` feed). This does not affect the generated runtime, which still depends only on the released `azure.core.rest`. -- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded model instances (`Info` for `unnamed`; `ResponseCreated` / `ResponseDelta` for `named`) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. +- **SSE union item deserialization** — SSE item types are `@events` unions, so each event is deserialized against a forward-reference union member name and yielded as the parsed JSON value rather than a model instance. This shares a root cause with paging item deserialization: the shared `_deserialize` helper needs a `module` argument to resolve the union member names into concrete model classes. JSONL (single model item type) is unaffected and fully deserializes. +- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event *model dispatch* — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. + + Investigation (2026-08): `sseMetadata` is **not** present in the resolved TCGC `0.69.1`, **nor in `0.70.0`** (latest stable — its `SdkStreamMetadata` is byte-identical to 0.69.1, no SSE symbols). `SdkSseMetadata` (`events[]` per `@events` union variant, built by `buildSdkSseMetadata`) has since landed upstream on `Azure/typespec-azure` `main` and first appears in the `next` prerelease line (`0.71.0-dev.11`). Adopting it requires the `@typespec` 1.14 / 0.84 family bump those versions carry. Terminal-event termination does **not** depend on it (handled structurally, see above); only per-event model dispatch does. +- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded event payloads (as `dict`s, per the union-deserialization limitation) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts index 8e05c6dbdda..b1788dc21dd 100644 --- a/packages/http-client-python/emitter/src/http.ts +++ b/packages/http-client-python/emitter/src/http.ts @@ -66,11 +66,12 @@ export function isStructuredStreamType(type: SdkType): boolean { * Returns `undefined` when structured streaming should not apply, in which case * the existing raw byte-iterator behavior is preserved. * - * Note: for SSE, TCGC `sseMetadata` (SdkSseEventMetadata[]) drives per-event MODEL - * dispatch — each `event:` name maps to its concrete payload model, emitted as an - * `events` list. Terminal-event handling is emitted as `terminalEvent` (the string - * marker, e.g. `[DONE]`), which the generator wires into the vendored runtime so the - * stream stops before deserializing it. JSONL emits only `kind` and `itemType`. + * Note: the currently consumed TCGC metadata (`streamMetadata`) does not expose + * per-event SSE metadata (event-type dispatch). Terminal-event handling does NOT + * depend on it — the terminal marker is a string-literal member of the item union + * (e.g. `Literal["[DONE]"]`), which the generator detects structurally and passes + * to the vendored runtime as `terminal_event`. Only `kind` and `itemType` are + * emitted here; the terminal event is derived generator-side from `itemType`. */ function getStreamingInfo( context: PythonSdkContext, @@ -91,38 +92,17 @@ function getStreamingInfo( if (!isStructuredStreamType(streamMetadata.streamType)) return undefined; const contentTypes = streamMetadata.contentTypes ?? response.contentTypes ?? []; const isSse = contentTypes.some((ct) => ct.toLowerCase().includes("event-stream")); - const streaming: Record = { + // SSE kind is detected from the response Content-Type. A heterogeneous `@events` + // union streamType is emitted as a single union `itemType`; the generator detects + // the terminal event (a string-literal union member such as `[DONE]`) structurally + // and wires it into the runtime, so terminal-event termination works without TCGC + // `sseMetadata`. Per-event MODEL dispatch (routing each event to its distinct + // payload model) still requires `sseMetadata` (SdkSseMetadata.events[], TCGC + // #4882); until then heterogeneous events are yielded as parsed JSON. + return { kind: isSse ? "sse" : "jsonl", itemType: getType(context, streamMetadata.streamType), }; - // For SSE, TCGC `sseMetadata` (SdkSseMetadata.events[]) describes each `event:` name - // and its concrete payload model. We emit an `events` list so the generator can route - // each event to its distinct payload model (`_deserialize(, ...)`) rather than - // yielding parsed JSON. The stream's terminal event (a string-literal payload such as - // `[DONE]`, flagged `isTerminalEvent`) is emitted as `terminalEvent` so the runtime - // stops before this callback is invoked for it. - const sseMetadata = isSse ? (response as SdkHttpResponse).sseMetadata : undefined; - if (sseMetadata) { - const events: Record[] = []; - let terminalEvent: string | undefined; - for (const event of sseMetadata.events) { - if (event.isTerminalEvent) { - // The terminal event's payload is a string constant marker (e.g. `[DONE]`). - const value = (event.payloadType as any).value ?? (event.type as any).value; - if (typeof value === "string") terminalEvent = value; - continue; - } - // Envelope (data-wrapping) events are not yet specially handled; they fall through - // to the common non-envelope path (the payload is deserialized directly). - events.push({ - eventType: event.eventType, - itemType: getType(context, event.payloadType), - }); - } - if (events.length > 0) streaming.events = events; - if (terminalEvent !== undefined) streaming.terminalEvent = terminalEvent; - } - return streaming; } function isEtagType(type: SdkType): boolean { diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 4a31890b739..5d7b0a6897b 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -65,22 +65,9 @@ def __init__( # Only treat this as a structured stream when the resolved ``type`` is the per-item # type (model / union). When the structured item type could not be resolved we fall # back to the raw byte body (``BinaryIteratorType``) and must NOT render ``Stream[...]``. - is_structured = bool(streaming) and not isinstance(self.type, BinaryIteratorType) - self.streaming_kind: Optional[str] = streaming["kind"] if is_structured else None - # Per-event SSE dispatch (TCGC ``sseMetadata``): each entry maps an SSE ``event:`` - # name to its concrete payload item type, so the generated callback can deserialize - # each event into a distinct model instance. - self.streaming_events: list[tuple[Optional[str], BaseType]] = [] - # Terminal-event marker (e.g. ``"[DONE]"``) emitted from ``sseMetadata``; when absent - # it is derived structurally from the item union (see ``terminal_event``). - self._streaming_terminal_event: Optional[str] = streaming.get("terminalEvent") if is_structured else None - if is_structured: - for event in streaming.get("events", []): - try: - event_item_type = self.code_model.lookup_type(id(event["itemType"])) - except KeyError: - continue - self.streaming_events.append((event.get("eventType"), event_item_type)) + self.streaming_kind: Optional[str] = ( + streaming["kind"] if streaming and not isinstance(self.type, BinaryIteratorType) else None + ) @property def result_property(self) -> str: @@ -124,18 +111,14 @@ def is_structured_stream(self) -> bool: def terminal_event(self) -> Optional[str]: """Terminal event marker for a heterogeneous SSE stream, if any. - Preferred source is the TCGC ``sseMetadata`` terminal event (emitted as - ``terminalEvent``). When absent, we detect it structurally: the first - ``ConstantType`` string member of the item union (e.g. ``"[DONE]"``) is treated - as the terminal marker, so the runtime can stop before attempting to - JSON-deserialize it. Returns ``None`` for homogeneous streams (no marker) and - for JSONL. + Heterogeneous SSE ``@events`` unions include a string-literal member (e.g. + ``"[DONE]"``) that marks the end of the stream. Without TCGC ``sseMetadata`` + (#4882) we detect it structurally: the first ``ConstantType`` string member of + the union item type is treated as the terminal marker, so the runtime can stop + before attempting to JSON-deserialize it. Returns ``None`` for homogeneous + streams (no constant member) and for JSONL. """ - if self.streaming_kind != "sse": - return None - if self._streaming_terminal_event is not None: - return self._streaming_terminal_event - if not isinstance(self.type, CombinedType): + if self.streaming_kind != "sse" or not isinstance(self.type, CombinedType): return None from .constant_type import ConstantType @@ -147,18 +130,6 @@ def terminal_event(self) -> Optional[str]: def stream_class_name(self, async_mode: bool) -> str: return "AsyncStream" if async_mode else "Stream" - @property - def stream_item_type(self) -> Optional[BaseType]: - """The type used to parametrize ``Stream[...]`` / ``AsyncStream[...]``. - - For a homogeneous stream (a single event payload) this is the concrete payload - model rather than the single-member union alias (a ``_unions`` variable, which is - not valid as a type annotation). Heterogeneous streams keep the union item type. - """ - if len(self.streaming_events) == 1: - return self.streaming_events[0][1] - return self.type - def serialization_type(self, **kwargs: Any) -> str: if self.type: return self.type.serialization_type(**kwargs) @@ -169,8 +140,7 @@ def type_annotation(self, **kwargs: Any) -> str: kwargs["is_operation_file"] = True kwargs["is_response"] = True stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - item_type = self.stream_item_type or self.type - return f"{stream_class}[{item_type.type_annotation(**kwargs)}]" + return f"{stream_class}[{self.type.type_annotation(**kwargs)}]" if self.type: kwargs["is_operation_file"] = True kwargs["is_response"] = True @@ -184,8 +154,7 @@ def docstring_text(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - item_type = self.stream_item_type or self.type - return f"An instance of {stream_class} that iterates over {item_type.docstring_text(**kwargs)}" + return f"An instance of {stream_class} that iterates over {self.type.docstring_text(**kwargs)}" if self.nullable and self.type: return f"{self.type.docstring_text(**kwargs)} or None" return self.type.docstring_text(**kwargs) if self.type else "None" @@ -194,11 +163,7 @@ def docstring_type(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - item_type = self.stream_item_type or self.type - return ( - f"~{self.code_model.namespace}._utils.streaming_base." - f"{stream_class}[{item_type.docstring_type(**kwargs)}]" - ) + return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{self.type.docstring_type(**kwargs)}]" if self.nullable and self.type: return f"{self.type.docstring_type(**kwargs)} or None" return self.type.docstring_type(**kwargs) if self.type else "None" @@ -226,9 +191,6 @@ def imports(self, **kwargs: Any) -> FileImport: file_import.add_submodule_import(relative_path, stream_class, ImportType.LOCAL) if self.streaming_kind == "sse": file_import.add_import("json", ImportType.STDLIB) - # Ensure each per-event payload model is importable in the operation file. - for _event_type, event_item_type in self.streaming_events: - file_import.merge(event_item_type.imports(**kwargs)) return file_import def _get_import_type(self, input_path: str) -> ImportType: diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 83787acb362..4a27e609d12 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1268,43 +1268,22 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) stream_class = response.stream_class_name(self.async_mode) # type: ignore[attr-defined] terminal_event = getattr(response, "terminal_event", None) - streaming_events = getattr(response, "streaming_events", []) retval: list[str] = [] retval.append("def _callback(_http_response, _event):") if response.streaming_kind == "sse": # type: ignore[attr-defined] - # SSE payloads arrive as raw ``data`` strings; the terminal marker (e.g. ``[DONE]``) - # is consumed by the runtime before this callback runs (see ``terminal_event``). + # Heterogeneous SSE (``@events`` unions) is deserialized against the union item + # type below; the shared ``_deserialize`` cannot resolve a forward-ref union + # member name into a concrete model, so payloads are yielded as parsed JSON. + # Per-event ``eventType`` dispatch into distinct model instances requires the + # TCGC ``sseMetadata`` (SdkSseMetadata.events[], typespec-client-generator-core + # #4882), which is unavailable in the currently pinned TCGC version. The stream's + # terminal event (a string-literal union member such as ``[DONE]``) is detected + # structurally and passed as ``terminal_event`` below, so the runtime stops + # before this callback attempts to JSON-parse it. retval.append(" _event_json = json.loads(_event.data)") - named_events = [ - (event_type, event_item_type) - for event_type, event_item_type in streaming_events - if event_type - ] - if named_events: - # Heterogeneous SSE: route each ``event:`` name to its concrete payload model - # (TCGC ``sseMetadata``), yielding distinct model instances. - for index, (event_type, event_item_type) in enumerate(named_events): - event_annotation = event_item_type.type_annotation( - is_operation_file=True, serialize_namespace=self.serialize_namespace - ) - keyword = "if" if index == 0 else "elif" - retval.append(f' {keyword} _event.event == {event_type!r}:') - retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") - retval.append(" else:") - retval.append(" deserialized = _event_json") - elif streaming_events: - # Homogeneous SSE: a single (unnamed) event type deserialized into its model. - _event_type, event_item_type = streaming_events[0] - event_annotation = event_item_type.type_annotation( - is_operation_file=True, serialize_namespace=self.serialize_namespace - ) - retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") - else: - # No per-event metadata: best-effort deserialize against the union item type. - retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") else: retval.append(" _event_json = _event.json()") - retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") retval.append(" if cls:") retval.append(" return cls(pipeline_response, deserialized, {}) # type: ignore") retval.append(" return deserialized") @@ -1320,43 +1299,6 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) return retval - def _handle_response_body(self, builder: OperationType) -> list[str]: # pylint: disable=too-many-nested-blocks - retval: list[str] = [] - if len(builder.responses) > 1: - status_codes, res_headers, res_deserialization = [], [], [] - for status_code in builder.success_status_codes: - response = builder.get_response_from_status(status_code) # type: ignore - if response.headers or response.type: - status_codes.append(status_code) - res_headers.append(self.response_headers(response)) - res_deserialization.append(self.response_deserialization(builder, response)) - - is_headers_same = _all_same(res_headers) - is_deserialization_same = _all_same(res_deserialization) - if is_deserialization_same: - if is_headers_same: - retval.extend(res_headers[0]) - retval.extend(res_deserialization[0]) - retval.append("") - else: - for status_code, headers in zip(status_codes, res_headers): - if headers: - retval.append(f"if response.status_code == {status_code}:") - retval.extend([f" {line}" for line in headers]) - retval.append("") - retval.extend(res_deserialization[0]) - retval.append("") - else: - for status_code, headers, deserialization in zip(status_codes, res_headers, res_deserialization): - retval.append(f"if response.status_code == {status_code}:") - retval.extend([f" {line}" for line in headers]) - retval.extend([f" {line}" for line in deserialization]) - retval.append("") - else: - retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0])) - retval.append("") - return retval - def handle_response(self, builder: OperationType) -> list[str]: retval: list[str] = ["response = pipeline_response.http_response"] retval.append("") @@ -1369,8 +1311,40 @@ def handle_response(self, builder: OperationType) -> list[str]: retval.append("deserialized = None") if builder.any_response_has_headers: retval.append("response_headers = {}") - if builder.has_response_body or builder.any_response_has_headers: - retval.extend(self._handle_response_body(builder)) + if builder.has_response_body or builder.any_response_has_headers: # pylint: disable=too-many-nested-blocks + if len(builder.responses) > 1: + status_codes, res_headers, res_deserialization = [], [], [] + for status_code in builder.success_status_codes: + response = builder.get_response_from_status(status_code) # type: ignore + if response.headers or response.type: + status_codes.append(status_code) + res_headers.append(self.response_headers(response)) + res_deserialization.append(self.response_deserialization(builder, response)) + + is_headers_same = _all_same(res_headers) + is_deserialization_same = _all_same(res_deserialization) + if is_deserialization_same: + if is_headers_same: + retval.extend(res_headers[0]) + retval.extend(res_deserialization[0]) + retval.append("") + else: + for status_code, headers in zip(status_codes, res_headers): + if headers: + retval.append(f"if response.status_code == {status_code}:") + retval.extend([f" {line}" for line in headers]) + retval.append("") + retval.extend(res_deserialization[0]) + retval.append("") + else: + for status_code, headers, deserialization in zip(status_codes, res_headers, res_deserialization): + retval.append(f"if response.status_code == {status_code}:") + retval.extend([f" {line}" for line in headers]) + retval.extend([f" {line}" for line in deserialization]) + retval.append("") + else: + retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0])) + retval.append("") if ( builder.has_optional_return_type or self.code_model.options["models-mode"] diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index b06b9e27458..37ae20d62e5 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -18,26 +18,26 @@ }, "devDependencies": { "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@azure-tools/typespec-autorest": "0.71.0-dev.4", - "@azure-tools/typespec-azure-core": "0.71.0-dev.4", - "@azure-tools/typespec-azure-resource-manager": "0.71.0-dev.11", - "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", - "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", + "@azure-tools/typespec-autorest": "~0.70.0", + "@azure-tools/typespec-azure-core": "~0.70.0", + "@azure-tools/typespec-azure-resource-manager": "~0.70.0", + "@azure-tools/typespec-azure-rulesets": "~0.70.0", + "@azure-tools/typespec-client-generator-core": "~0.70.0", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", "@types/semver": "7.5.8", - "@typespec/compiler": "1.15.0-dev.17", - "@typespec/events": "0.85.0-dev.0", - "@typespec/http": "1.15.0-dev.5", + "@typespec/compiler": "^1.14.0", + "@typespec/events": "~0.84.0", + "@typespec/http": "^1.14.0", "@typespec/http-specs": "0.1.0-alpha.40", - "@typespec/openapi": "1.15.0-dev.2", - "@typespec/rest": "0.85.0-dev.1", + "@typespec/openapi": "^1.14.0", + "@typespec/rest": "~0.84.0", "@typespec/spec-api": "0.1.0-alpha.15", "@typespec/spector": "0.1.0-alpha.27", - "@typespec/sse": "0.85.0-dev.0", - "@typespec/streams": "0.85.0-dev.1", - "@typespec/versioning": "0.85.0-dev.0", - "@typespec/xml": "0.85.0-dev.0", + "@typespec/sse": "~0.84.0", + "@typespec/streams": "~0.84.0", + "@typespec/versioning": "~0.84.0", + "@typespec/xml": "~0.84.0", "c8": "^10.1.3", "picocolors": "~1.1.1", "prettier": "^3.9.5", @@ -89,9 +89,9 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.71.0-dev.4", - "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-autorest/-/typespec-autorest-0.71.0-dev.4.tgz", - "integrity": "sha1-p920uaoYRzfFVIeEUDW6+uUri1w=", + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.70.0.tgz", + "integrity": "sha512-OaxLkgMcuOXAbaqTNpezmFF24jtkiIH1+2PBwAeRo3ZG7C1r7Hf8xZwCK6KVtBEgMbqnrd5eCqxsPl1zy3y9/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -101,9 +101,9 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.10", - "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", + "@azure-tools/typespec-azure-core": "^0.70.0", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0", + "@azure-tools/typespec-client-generator-core": "^0.70.0", "@typespec/compiler": "^1.14.0", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", @@ -118,9 +118,9 @@ } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.71.0-dev.4", - "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.71.0-dev.4.tgz", - "integrity": "sha1-2pFZNHjIm0YyvV6pBaiHajFP9XQ=", + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.70.0.tgz", + "integrity": "sha512-8MojHWRtTLKycJJ98IMoXX/5b9tTo3F0d3Iu20OKoCsORnSDG2NfjOWHJVW63oxA2t8VTlqC6J8BDcnRihygQQ==", "dev": true, "license": "MIT", "engines": { @@ -133,9 +133,9 @@ } }, "node_modules/@azure-tools/typespec-azure-resource-manager": { - "version": "0.71.0-dev.11", - "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.71.0-dev.11.tgz", - "integrity": "sha1-AXbWvNDsj4qydLZsGg3SROpD7dE=", + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.70.0.tgz", + "integrity": "sha512-hVrbbsOhU3EQ2yQTppCqsGQwY/HcVZPOINtFkoUo+PUVBmCFXyqLkTO4jvUbsp/LvJEwoQ8aEA8Y35f7VWT5uw==", "dev": true, "license": "MIT", "dependencies": { @@ -146,7 +146,7 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.4", + "@azure-tools/typespec-azure-core": "^0.70.0", "@typespec/compiler": "^1.14.0", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", @@ -155,25 +155,25 @@ } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.71.0-dev.5", - "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.71.0-dev.5.tgz", - "integrity": "sha1-bpzvEq9boKSAhUwBc+EKXvxNOwI=", + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.70.0.tgz", + "integrity": "sha512-Uxxl/18oryDwk2S+aYx6cIqiyjmoMeFDGmjuQ72a+aw6u8mZjgahMxNsY0ShvGLSchjsDqsVGaUlazXGXakVrw==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.4", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.11", - "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", + "@azure-tools/typespec-azure-core": "^0.70.0", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0", + "@azure-tools/typespec-client-generator-core": "^0.70.0", "@typespec/compiler": "^1.14.0" } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.71.0-dev.11", - "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.71.0-dev.11.tgz", - "integrity": "sha1-6FheAB+SgoqzGYHvqSQMvS9nC6s=", + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.70.0.tgz", + "integrity": "sha512-8yxOYJfID3wp3FLQYNIa3kbmR5YLWjYtpB+i4u66quHTTQWWANHV1/o9f8xymAf+8fO9jbLo5tw1JerumxISWg==", "dev": true, "license": "MIT", "dependencies": { @@ -185,7 +185,7 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", + "@azure-tools/typespec-azure-core": "^0.70.0", "@typespec/compiler": "^1.14.0", "@typespec/events": "^0.84.0", "@typespec/http": "^1.14.0", @@ -997,6 +997,259 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "peer": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@inquirer/ansi": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz", @@ -1806,6 +2059,14 @@ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "25.0.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", @@ -2066,9 +2327,9 @@ } }, "node_modules/@typespec/compiler": { - "version": "1.15.0-dev.17", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/compiler/-/compiler-1.15.0-dev.17.tgz", - "integrity": "sha1-hrlL0q3kmaR0fWNddCfI7LA2BCY=", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.14.0.tgz", + "integrity": "sha512-RRN0LGVDlonG/IbB2b4mvRjdCo6LywwB9/J8lOp6UaH7vtaFnKe5FL+rpxhof4rXx/zI/4OWnQO6c01bTCz4/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2080,7 +2341,7 @@ "is-unicode-supported": "^2.1.0", "mustache": "^4.2.0", "picocolors": "^1.1.1", - "prettier": "^3.9.5", + "prettier": "^3.8.1", "semver": "^7.7.4", "tar": "^7.5.13", "temporal-polyfill": "^1.0.1", @@ -2179,30 +2440,30 @@ } }, "node_modules/@typespec/events": { - "version": "0.85.0-dev.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/events/-/events-0.85.0-dev.0.tgz", - "integrity": "sha1-GJSz2/FMSWBRCyfJOpLO6Sa7n9Q=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.84.0.tgz", + "integrity": "sha512-UroDIu6t6Z+cOLyX8I+GJWhSFmYGrp1L93F7ZVt0Ypmj0ndmC9YYa4cpeEyS5PDDIC8u49WfCIwfGegxt4rPVQ==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" + "@typespec/compiler": "^1.14.0" } }, "node_modules/@typespec/http": { - "version": "1.15.0-dev.5", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/http/-/http-1.15.0-dev.5.tgz", - "integrity": "sha1-4oMLhD+Mvj2PLbagkX7ok9/Hsbk=", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.14.0.tgz", + "integrity": "sha512-W+heCzu8K63AVcoX8MachVWaRxSAMFWOI1yBTc2Kq8QHaJeDiLL5JbU8VfTZ4tL/6EoGSdKfIT5ZNRW7oVCzhg==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", - "@typespec/streams": "^0.84.0 || >= 0.85.0-dev.1" + "@typespec/compiler": "^1.14.0", + "@typespec/streams": "^0.84.0" }, "peerDependenciesMeta": { "@typespec/streams": { @@ -2234,31 +2495,31 @@ } }, "node_modules/@typespec/openapi": { - "version": "1.15.0-dev.2", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/openapi/-/openapi-1.15.0-dev.2.tgz", - "integrity": "sha1-e59k965dOfi7Ds8pI3JiJBtILqM=", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.14.0.tgz", + "integrity": "sha512-KL7kImPhCXRmxpHVt1k7TWaa4bb3NbSeUx2rxyxeq7lYZFllI6/NYRCTOI/5JOrbElWmmSxrajU9K9IAKI6PkQ==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", - "@typespec/http": "^1.14.0 || >= 1.15.0-dev.4" + "@typespec/compiler": "^1.14.0", + "@typespec/http": "^1.14.0" } }, "node_modules/@typespec/rest": { - "version": "0.85.0-dev.1", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/rest/-/rest-0.85.0-dev.1.tgz", - "integrity": "sha1-yLZhv0pD125R8ohNtwhGSC6e/iA=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.84.0.tgz", + "integrity": "sha512-9s5dDfRoHRPdtbVvkBasUx/RnMvwWMTuXRieSQDEji4gWGgxVu4Zt4MiEEKSfQrkMr3Aw0QjRCSxBxjMCHIOmA==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17", - "@typespec/http": "^1.14.0 || >= 1.15.0-dev.4" + "@typespec/compiler": "^1.14.0", + "@typespec/http": "^1.14.0" } }, "node_modules/@typespec/spec-api": { @@ -2353,84 +2614,6 @@ "node": ">=22.0.0" } }, - "node_modules/@typespec/spector/node_modules/@typespec/compiler": { - "version": "1.14.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/compiler/-/compiler-1.14.0.tgz", - "integrity": "sha1-2FXCBu7K+j54eOf0JVthKC8FP0Y=", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@inquirer/prompts": "^8.4.1", - "ajv": "^8.18.0", - "change-case": "^5.4.4", - "env-paths": "^4.0.0", - "is-unicode-supported": "^2.1.0", - "mustache": "^4.2.0", - "picocolors": "^1.1.1", - "prettier": "^3.8.1", - "semver": "^7.7.4", - "tar": "^7.5.13", - "temporal-polyfill": "^1.0.1", - "vscode-languageserver": "^10.0.0", - "vscode-languageserver-textdocument": "^1.0.12", - "yaml": "^2.8.3", - "yargs": "^18.0.0" - }, - "bin": { - "tsp": "cmd/tsp.js", - "tsp-server": "cmd/tsp-server.js" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@typespec/spector/node_modules/@typespec/http": { - "version": "1.14.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/http/-/http-1.14.0.tgz", - "integrity": "sha1-La9yB2Ny8FhnXSBbst9wYQBiOq4=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.0.0" - }, - "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/streams": "^0.84.0" - }, - "peerDependenciesMeta": { - "@typespec/streams": { - "optional": true - } - } - }, - "node_modules/@typespec/spector/node_modules/@typespec/rest": { - "version": "0.84.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/rest/-/rest-0.84.0.tgz", - "integrity": "sha1-kMLB39G8geZbiA3EEdQBaqteuuA=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.0.0" - }, - "peerDependencies": { - "@typespec/compiler": "^1.14.0", - "@typespec/http": "^1.14.0" - } - }, - "node_modules/@typespec/spector/node_modules/@typespec/versioning": { - "version": "0.84.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/versioning/-/versioning-0.84.0.tgz", - "integrity": "sha1-YS06C7uMMWXKp7vwuy8qV8kjr38=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.0.0" - }, - "peerDependencies": { - "@typespec/compiler": "^1.14.0" - } - }, "node_modules/@typespec/spector/node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -2453,19 +2636,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@typespec/spector/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", - "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typespec/spector/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -2513,32 +2683,32 @@ } }, "node_modules/@typespec/sse": { - "version": "0.85.0-dev.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/sse/-/sse-0.85.0-dev.0.tgz", - "integrity": "sha1-X+R88ExpK6jsjjGMb5OEdLZCuU4=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.84.0.tgz", + "integrity": "sha512-9joNgVisRCWDFfV1d79iTAuR1W/6r+AKJrKUfcjsaTrq5A8OWW3v5TTsfxbHAZArn7n2WxQkqhNGgNyc8LjEng==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0", - "@typespec/events": "^0.84.0 || >= 0.85.0-dev.0", - "@typespec/http": "^1.14.0 || >= 1.15.0-dev.0", - "@typespec/streams": "^0.84.0 || >= 0.85.0-dev.0" + "@typespec/compiler": "^1.14.0", + "@typespec/events": "^0.84.0", + "@typespec/http": "^1.14.0", + "@typespec/streams": "^0.84.0" } }, "node_modules/@typespec/streams": { - "version": "0.85.0-dev.1", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/streams/-/streams-0.85.0-dev.1.tgz", - "integrity": "sha1-HkU7ikG1A6HP8PPPFNyheTRc0Lw=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.84.0.tgz", + "integrity": "sha512-SDneR8+zY+ueOpzg9yJtttfDe/ikB99JgddZSXKPwiDPlAIEeEvI8auipcYfB58EEOB21h8Oq0tEm8HqiAAWdQ==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.17" + "@typespec/compiler": "^1.14.0" } }, "node_modules/@typespec/ts-http-runtime": { @@ -2557,29 +2727,29 @@ } }, "node_modules/@typespec/versioning": { - "version": "0.85.0-dev.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/versioning/-/versioning-0.85.0-dev.0.tgz", - "integrity": "sha1-FaJ7l5PpzXK+E4Hkd7Q1LXck3hI=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.84.0.tgz", + "integrity": "sha512-ZoDasTDj4z0mgFK+0cJL2+7DduCaTjvICHL2nQ/RBWc7nLgObaIYCjvXLno8WneDXnpxCAr7larN4/nlHEv9fg==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" + "@typespec/compiler": "^1.14.0" } }, "node_modules/@typespec/xml": { - "version": "0.85.0-dev.0", - "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/xml/-/xml-0.85.0-dev.0.tgz", - "integrity": "sha1-oxIznWAobEj7bFVN5T2IusKbWqw=", + "version": "0.84.0", + "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.84.0.tgz", + "integrity": "sha512-3x0spgIrr4u3azkYaOxrlumtjoqPiUnJ/G5RwGBmUCAeE5F413MHf/AeIkmZ2ULT1gY3myabfZp8bOijTbMk7A==", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.14.0 || >= 1.15.0-dev.0" + "@typespec/compiler": "^1.14.0" } }, "node_modules/@vitest/expect": { @@ -2709,6 +2879,31 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -3021,6 +3216,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -3136,6 +3342,14 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", @@ -3234,6 +3448,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, "node_modules/default-browser": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", @@ -3382,99 +3603,316 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "license": "MIT", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "es-errors": "^1.3.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.4" + "node": "*" } }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "Apache-2.0", + "peer": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4.0" } }, "node_modules/estree-walker": { @@ -3487,6 +3925,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3567,6 +4015,21 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, "node_modules/fast-string-truncated-width": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", @@ -3652,6 +4115,19 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3703,6 +4179,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -3852,6 +4350,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/glob/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -3891,6 +4402,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -4011,6 +4536,46 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -4044,6 +4609,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -4053,6 +4628,19 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -4214,6 +4802,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "peer": true + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -4221,6 +4816,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -4267,6 +4869,30 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -4585,6 +5211,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -4992,6 +5625,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5028,6 +5679,20 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5164,6 +5829,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.9.6", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", @@ -5194,6 +5869,17 @@ "node": ">= 0.10" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pyodide": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.26.2.tgz", @@ -5286,6 +5972,17 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -5755,6 +6452,20 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strnum": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", @@ -6033,6 +6744,19 @@ "fsevents": "~2.3.3" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -6128,6 +6852,17 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6423,6 +7158,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index 1d8aab77619..f7910112349 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -104,23 +104,23 @@ "tsx": "^4.21.0" }, "devDependencies": { - "@azure-tools/typespec-autorest": "0.71.0-dev.4", - "@azure-tools/typespec-azure-core": "0.71.0-dev.4", - "@azure-tools/typespec-azure-resource-manager": "0.71.0-dev.11", - "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", - "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", + "@azure-tools/typespec-autorest": "~0.70.0", + "@azure-tools/typespec-azure-core": "~0.70.0", + "@azure-tools/typespec-azure-resource-manager": "~0.70.0", + "@azure-tools/typespec-azure-rulesets": "~0.70.0", + "@azure-tools/typespec-client-generator-core": "~0.70.0", "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@typespec/compiler": "1.15.0-dev.17", - "@typespec/http": "1.15.0-dev.5", - "@typespec/openapi": "1.15.0-dev.2", - "@typespec/rest": "0.85.0-dev.1", - "@typespec/versioning": "0.85.0-dev.0", - "@typespec/events": "0.85.0-dev.0", + "@typespec/compiler": "^1.14.0", + "@typespec/http": "^1.14.0", + "@typespec/openapi": "^1.14.0", + "@typespec/rest": "~0.84.0", + "@typespec/versioning": "~0.84.0", + "@typespec/events": "~0.84.0", "@typespec/spector": "0.1.0-alpha.27", "@typespec/spec-api": "0.1.0-alpha.15", - "@typespec/sse": "0.85.0-dev.0", - "@typespec/streams": "0.85.0-dev.1", - "@typespec/xml": "0.85.0-dev.0", + "@typespec/sse": "~0.84.0", + "@typespec/streams": "~0.84.0", + "@typespec/xml": "~0.84.0", "@typespec/http-specs": "0.1.0-alpha.40", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", diff --git a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py index 1b179f5604a..639aa0cf13d 100644 --- a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py +++ b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py @@ -21,12 +21,18 @@ keep the raw byte-iterator behavior (see mock_api/unbranded/test_streaming_jsonl_unbranded.py). -Note on SSE item deserialization: SSE item types are described by TCGC ``sseMetadata`` -(SdkSseEventMetadata[]), so the generated callback routes each ``event:`` name to its -concrete payload model and yields model instances (homogeneous via the single event -payload, heterogeneous via per-event dispatch). Terminal-event handling (e.g. a trailing -``data: [DONE]``) is wired into the vendored runtime so the stream stops before the -callback is invoked for it. +Note on SSE item deserialization: SSE item types are modelled as `@events` unions, +which the generated callback deserializes via `_deserialize("", json)`. +The shared `_deserialize` cannot resolve a forward-ref *string* union member into a +model instance (same root cause as paging item deserialization needing a `module` +argument), so homogeneous SSE items are yielded as parsed JSON (``dict``) rather than +model instances. The tests below assert on the ``dict`` payloads accordingly. + +Still skipped (follow-ups): + +* SSE heterogeneous — blocked on TCGC `sseMetadata` (#4882) for per-event + dispatch + terminal-event handling, plus the union-item `_deserialize` + limitation (parsed JSON rather than model instances). Imports are guarded so collection never errors when the package is absent (e.g. before `regenerate` runs, or for the unbranded flavor). @@ -50,23 +56,17 @@ _HAS_STRUCTURED_JSONL = False -# For the Azure flavor the SSE ``streaming.sse`` package is generated with structured -# ``receive()`` methods: ``unnamed.receive()`` returns ``Stream[Info]`` and -# ``named.receive()`` returns ``Stream["_unions.ResponseEvents"]`` dispatched per event. -# Guarded so collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). +# For the Azure flavor the SSE ``streaming.sse`` package is generated with a structured +# ``unnamed.receive()`` returning ``Stream["_unions.UnnamedEvents"]``. Guarded so +# collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). try: # pragma: no cover - guarded so collection doesn't error when absent from streaming.sse import SseClient # type: ignore from streaming.sse.aio import SseClient as AsyncSseClient # type: ignore - from streaming.sse.unnamed.models import Info as SseInfo # type: ignore - from streaming.sse.named.models import ResponseCreated, ResponseDelta # type: ignore _HAS_STRUCTURED_SSE = True except ImportError: # pragma: no cover SseClient = None # type: ignore AsyncSseClient = None # type: ignore - SseInfo = None # type: ignore - ResponseCreated = None # type: ignore - ResponseDelta = None # type: ignore _HAS_STRUCTURED_SSE = False @@ -95,55 +95,57 @@ async def test_jsonl_receive_structured_async(): @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") def test_sse_receive_homogeneous_structured_sync(): - """SSE homogeneous: unnamed.receive() returns Stream[Info] of deserialized models. + """SSE homogeneous: unnamed.receive() returns Stream over the SSE events. The unnamed SSE scenario emits three ``message`` events with payload - ``{"desc": ...}``. ``sseMetadata`` maps the single (unnamed) event to the ``Info`` - payload model, so the callback yields ``Info`` instances. The stream terminates - naturally after the final event. + ``{"desc": ...}``. Because the SSE item type is an ``@events`` union, the + generated callback yields parsed JSON (``dict``) rather than ``Info`` model + instances (see module docstring / ``_deserialize`` limitation). The stream + terminates naturally after the final event. """ with SseClient(endpoint="http://localhost:3000") as client: items = list(client.unnamed.receive()) - assert all(isinstance(i, SseInfo) for i in items) - assert [i.desc for i in items] == _EXPECTED + assert [i["desc"] for i in items] == _EXPECTED + assert all(isinstance(i, dict) for i in items) @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") @pytest.mark.asyncio async def test_sse_receive_homogeneous_structured_async(): - """Async SSE homogeneous: unnamed.receive() returns AsyncStream[Info].""" + """Async SSE homogeneous: unnamed.receive() returns AsyncStream over the events.""" async with AsyncSseClient(endpoint="http://localhost:3000") as client: stream = await client.unnamed.receive() items = [item async for item in stream] - assert all(isinstance(i, SseInfo) for i in items) - assert [i.desc for i in items] == _EXPECTED + assert [i["desc"] for i in items] == _EXPECTED + assert all(isinstance(i, dict) for i in items) @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") def test_sse_receive_heterogeneous_structured_sync(): - """SSE heterogeneous: named.receive() dispatches each event to its payload model. + """SSE heterogeneous: named.receive() returns Stream over an ``@events`` union. The named SSE scenario emits ``responseCreated`` (``{"id": ...}``) then two ``responseDelta`` (``{"delta": ...}``) events, followed by a terminal - ``data: [DONE]`` event. ``sseMetadata`` routes ``responseCreated`` -> ``ResponseCreated`` - and ``responseDelta`` -> ``ResponseDelta``, so the callback yields distinct model - instances. ``[DONE]`` is wired as ``terminal_event``: the runtime stops there (without - trying to JSON-parse ``[DONE]``). + ``data: [DONE]`` event. ``[DONE]`` is a string-literal member of the item union, + so the generator wires it as ``terminal_event`` and the runtime stops there + (without trying to JSON-parse ``[DONE]``). Per-event payloads are yielded as + parsed JSON (``dict``) rather than distinct ``ResponseCreated`` / ``ResponseDelta`` + model instances: discriminating them needs TCGC ``sseMetadata`` (#4882) plus a + ``module`` argument on the shared ``_deserialize`` (same limitation as paging + item deserialization). """ with SseClient(endpoint="http://localhost:3000") as client: items = list(client.named.receive()) - assert [type(i) for i in items] == [ResponseCreated, ResponseDelta, ResponseDelta] - assert items[0].id == "resp_1" - assert [i.delta for i in items[1:]] == ["Hello", " world"] + assert all(isinstance(i, dict) for i in items) + assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] @pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") @pytest.mark.asyncio async def test_sse_receive_heterogeneous_structured_async(): - """Async SSE heterogeneous: named.receive() dispatches per event, terminating at [DONE].""" + """Async SSE heterogeneous: named.receive() returns AsyncStream, terminating at [DONE].""" async with AsyncSseClient(endpoint="http://localhost:3000") as client: stream = await client.named.receive() items = [item async for item in stream] - assert [type(i) for i in items] == [ResponseCreated, ResponseDelta, ResponseDelta] - assert items[0].id == "resp_1" - assert [i.delta for i in items[1:]] == ["Hello", " world"] + assert all(isinstance(i, dict) for i in items) + assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] From 9863fa800b02f82e1649048adb7188d14252c740 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 19:47:24 -0700 Subject: [PATCH 07/15] fix(http-client-python): prettier-format README.md Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- packages/http-client-python/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/http-client-python/README.md b/packages/http-client-python/README.md index 14ee885d1cd..43b4343d479 100644 --- a/packages/http-client-python/README.md +++ b/packages/http-client-python/README.md @@ -174,7 +174,8 @@ The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vend #### Known limitations / follow-ups - **SSE union item deserialization** — SSE item types are `@events` unions, so each event is deserialized against a forward-reference union member name and yielded as the parsed JSON value rather than a model instance. This shares a root cause with paging item deserialization: the shared `_deserialize` helper needs a `module` argument to resolve the union member names into concrete model classes. JSONL (single model item type) is unaffected and fully deserializes. -- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event *model dispatch* — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. +- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event _model dispatch_ — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. Investigation (2026-08): `sseMetadata` is **not** present in the resolved TCGC `0.69.1`, **nor in `0.70.0`** (latest stable — its `SdkStreamMetadata` is byte-identical to 0.69.1, no SSE symbols). `SdkSseMetadata` (`events[]` per `@events` union variant, built by `buildSdkSseMetadata`) has since landed upstream on `Azure/typespec-azure` `main` and first appears in the `next` prerelease line (`0.71.0-dev.11`). Adopting it requires the `@typespec` 1.14 / 0.84 family bump those versions carry. Terminal-event termination does **not** depend on it (handled structurally, see above); only per-event model dispatch does. + - **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded event payloads (as `dict`s, per the union-deserialization limitation) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. From e215564452dabe84420d6f3df144cc14a1958b7e Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 20:03:53 -0700 Subject: [PATCH 08/15] fix(http-client-python): satisfy pygen pylint (C0301, R0915) Wrap the long Stream docstring_type line in response.py and extract _handle_response_body from handle_response to drop below the statement limit. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../pygen/codegen/models/response.py | 3 +- .../codegen/serializers/builder_serializer.py | 29 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 5d7b0a6897b..94df3e4a7c8 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -163,7 +163,8 @@ def docstring_type(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{self.type.docstring_type(**kwargs)}]" + item_type = self.type.docstring_type(**kwargs) + return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{item_type}]" if self.nullable and self.type: return f"{self.type.docstring_type(**kwargs)} or None" return self.type.docstring_type(**kwargs) if self.type else "None" diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 4a27e609d12..4713bac3258 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1299,18 +1299,8 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) return retval - def handle_response(self, builder: OperationType) -> list[str]: - retval: list[str] = ["response = pipeline_response.http_response"] - retval.append("") - retval.extend(self.handle_error_response(builder)) - retval.append("") - if builder.has_structured_stream_response: - retval.extend(self.handle_structured_stream_response(builder)) - return retval - if builder.has_optional_return_type: - retval.append("deserialized = None") - if builder.any_response_has_headers: - retval.append("response_headers = {}") + def _handle_response_body(self, builder: OperationType) -> list[str]: + retval: list[str] = [] if builder.has_response_body or builder.any_response_has_headers: # pylint: disable=too-many-nested-blocks if len(builder.responses) > 1: status_codes, res_headers, res_deserialization = [], [], [] @@ -1345,6 +1335,21 @@ def handle_response(self, builder: OperationType) -> list[str]: else: retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0])) retval.append("") + return retval + + def handle_response(self, builder: OperationType) -> list[str]: + retval: list[str] = ["response = pipeline_response.http_response"] + retval.append("") + retval.extend(self.handle_error_response(builder)) + retval.append("") + if builder.has_structured_stream_response: + retval.extend(self.handle_structured_stream_response(builder)) + return retval + if builder.has_optional_return_type: + retval.append("deserialized = None") + if builder.any_response_has_headers: + retval.append("response_headers = {}") + retval.extend(self._handle_response_body(builder)) if ( builder.has_optional_return_type or self.code_model.options["models-mode"] From 2defc61320b61a51619bff7485809e2396ac0230 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 20:36:07 -0700 Subject: [PATCH 09/15] fix(http-client-python): expand SSE stream union item type inline for valid type annotation The Stream[T]/AsyncStream[T] annotation for a structured SSE stream referenced the _unions. alias (a module-level variable), which pyright/mypy reject inside a type expression (reportInvalidTypeForm). Expand the @events union inline (Union[Model, ...] / the single member) so the stream item annotation is a valid type expression, and import the member models instead of the _unions alias. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../pygen/codegen/models/response.py | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 94df3e4a7c8..06e9f375df5 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -135,12 +135,25 @@ def serialization_type(self, **kwargs: Any) -> str: return self.type.serialization_type(**kwargs) return "None" + def stream_item_annotation(self, **kwargs: Any) -> str: + """Valid type expression for a structured stream's item type. + + A named ``CombinedType`` (``@events`` union) renders its ``type_annotation`` as the + ``_unions.`` alias, which is a module-level variable and therefore rejected by + pyright/mypy inside ``Stream[...]`` ("Variable not allowed in type expression"). Expand + the union inline (``Union[Model, ...]`` / the single member) so the annotation is a + valid type expression. + """ + if isinstance(self.type, CombinedType): + return self.type.type_definition(**kwargs) + return self.type.type_annotation(**kwargs) if self.type else "None" + def type_annotation(self, **kwargs: Any) -> str: if self.is_structured_stream and self.type: kwargs["is_operation_file"] = True kwargs["is_response"] = True stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"{stream_class}[{self.type.type_annotation(**kwargs)}]" + return f"{stream_class}[{self.stream_item_annotation(**kwargs)}]" if self.type: kwargs["is_operation_file"] = True kwargs["is_response"] = True @@ -171,18 +184,26 @@ def docstring_type(self, **kwargs: Any) -> str: def imports(self, **kwargs: Any) -> FileImport: file_import = FileImport(self.code_model) - if self.type: + # For a structured stream whose item type is a named ``@events`` union, the annotation + # is expanded inline (see ``stream_item_annotation``), so import the union member types + # rather than the ``_unions`` alias. + if self.is_structured_stream and isinstance(self.type, CombinedType): + for member in self.type.types: + file_import.merge(member.imports(**kwargs)) + if not all(t.type == "constant" for t in self.type.types): + file_import.add_submodule_import("typing", "Union", ImportType.STDLIB) + elif self.type: file_import.merge(self.type.imports(**kwargs)) + if isinstance(self.type, CombinedType) and self.type.name: + serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) + file_import.add_submodule_import( + self.code_model.get_relative_import_path(serialize_namespace), + "_unions", + ImportType.LOCAL, + TypingSection.TYPING, + ) if self.nullable: file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB) - if isinstance(self.type, CombinedType) and self.type.name: - serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) - file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace), - "_unions", - ImportType.LOCAL, - TypingSection.TYPING, - ) if self.is_structured_stream: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) From 37ccf36496376344256e6ee17c0df05fec2fc4ba Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 20:44:58 -0700 Subject: [PATCH 10/15] fix(http-client-python): only import Union for multi-member SSE stream item The inline stream item expansion collapses a single union member to that member (no Union) and a union of only literals to a single Literal, so importing Union unconditionally left it unused (pylint W0611) in the homogeneous SSE op. Import Union only when the expansion yields 2+ distinct member types. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- .../generator/pygen/codegen/models/response.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 06e9f375df5..091b3b723b1 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -190,7 +190,12 @@ def imports(self, **kwargs: Any) -> FileImport: if self.is_structured_stream and isinstance(self.type, CombinedType): for member in self.type.types: file_import.merge(member.imports(**kwargs)) - if not all(t.type == "constant" for t in self.type.types): + # ``Union`` is only needed when the inline expansion actually yields a union of + # 2+ distinct member types (a single member collapses to that member; a union of + # only literals collapses to a single ``Literal[...]``). + distinct = list(dict.fromkeys(m.type_annotation(**kwargs) for m in self.type.types)) + all_constant = all(t.type == "constant" for t in self.type.types) + if len(distinct) > 1 and not all_constant: file_import.add_submodule_import("typing", "Union", ImportType.STDLIB) elif self.type: file_import.merge(self.type.imports(**kwargs)) From 77eada8ce7f2f475e72a2f58a4d7f41201499fed Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Thu, 6 Aug 2026 21:25:15 -0700 Subject: [PATCH 11/15] chore(http-client-python): retrigger CI with merged main (fresh PR-merge ref) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e From 6ec0cd626eaa673604290667c98fdd3ec4c25a77 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Fri, 7 Aug 2026 09:09:42 -0700 Subject: [PATCH 12/15] clean up tests files for now --- .../azure/test_streaming_structured.py | 151 ---------------- .../asynctests/test_streaming_jsonl_async.py | 5 + .../mock_api/shared/test_streaming_jsonl.py | 4 + .../test_streaming_jsonl_unbranded_async.py | 28 --- .../test_streaming_jsonl_unbranded.py | 30 ---- .../test_structured_streaming_response.py | 168 ------------------ 6 files changed, 9 insertions(+), 377 deletions(-) delete mode 100644 packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py delete mode 100644 packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py delete mode 100644 packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py delete mode 100644 packages/http-client-python/tests/unit/test_structured_streaming_response.py diff --git a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py b/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py deleted file mode 100644 index 639aa0cf13d..00000000000 --- a/packages/http-client-python/tests/mock_api/azure/test_streaming_structured.py +++ /dev/null @@ -1,151 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- -"""Mock API tests for structured streaming (Azure flavor). - -These tests exercise operations that return `Stream[T]` / `AsyncStream[T]`. The -streaming runtime (Stream / AsyncStream + JSONL / SSE decoders) is **vendored** -into the generated package at `_utils/streaming_base.py`, so it only depends on -the released `azure.core.rest` — NOT on the unreleased `azure.core.streaming` -(azure-core PR #48077). - -Structured streaming is driven by the TCGC response stream metadata and applies -to the **Azure flavor only** (the vendored runtime depends on `azure.core.rest`). -For the Azure flavor, a JSONL (`application/jsonl`) / SSE (`text/event-stream`) -streaming response generates a `receive()` returning `Stream[T]` / `AsyncStream[T]`, -so the JSONL homogeneous tests below run against the real spector mock route -(`/streaming/jsonl/basic/receive`) and the SSE homogeneous tests run against -(`/streaming/sse/unnamed/receive`). For the unbranded flavor, streaming responses -keep the raw byte-iterator behavior (see -mock_api/unbranded/test_streaming_jsonl_unbranded.py). - -Note on SSE item deserialization: SSE item types are modelled as `@events` unions, -which the generated callback deserializes via `_deserialize("", json)`. -The shared `_deserialize` cannot resolve a forward-ref *string* union member into a -model instance (same root cause as paging item deserialization needing a `module` -argument), so homogeneous SSE items are yielded as parsed JSON (``dict``) rather than -model instances. The tests below assert on the ``dict`` payloads accordingly. - -Still skipped (follow-ups): - -* SSE heterogeneous — blocked on TCGC `sseMetadata` (#4882) for per-event - dispatch + terminal-event handling, plus the union-item `_deserialize` - limitation (parsed JSON rather than model instances). - -Imports are guarded so collection never errors when the package is absent -(e.g. before `regenerate` runs, or for the unbranded flavor). -""" -import pytest - -# For the Azure flavor the default ``streaming.jsonl`` package is generated with a -# structured ``receive()`` returning ``Stream[Info]`` (grouped namespace layout, so -# ``Info`` lives at ``streaming.jsonl.basic.models``). Guarded so collection doesn't -# error for the unbranded flavor (byte-iterator ``receive()``, no ``Info`` model). -try: # pragma: no cover - guarded so collection doesn't error when absent - from streaming.jsonl import JsonlClient # type: ignore - from streaming.jsonl.aio import JsonlClient as AsyncJsonlClient # type: ignore - from streaming.jsonl.basic.models import Info # type: ignore - - _HAS_STRUCTURED_JSONL = True -except ImportError: # pragma: no cover - JsonlClient = None # type: ignore - AsyncJsonlClient = None # type: ignore - Info = None # type: ignore - _HAS_STRUCTURED_JSONL = False - - -# For the Azure flavor the SSE ``streaming.sse`` package is generated with a structured -# ``unnamed.receive()`` returning ``Stream["_unions.UnnamedEvents"]``. Guarded so -# collection doesn't error for the unbranded flavor (byte-iterator ``receive()``). -try: # pragma: no cover - guarded so collection doesn't error when absent - from streaming.sse import SseClient # type: ignore - from streaming.sse.aio import SseClient as AsyncSseClient # type: ignore - - _HAS_STRUCTURED_SSE = True -except ImportError: # pragma: no cover - SseClient = None # type: ignore - AsyncSseClient = None # type: ignore - _HAS_STRUCTURED_SSE = False - - -_EXPECTED = ["one", "two", "three"] - - -@pytest.mark.skipif(not _HAS_STRUCTURED_JSONL, reason="streaming.jsonl is not structured (unbranded flavor)") -def test_jsonl_receive_structured_sync(): - """JSONL homogeneous: receive() returns Stream[Info] of deserialized models.""" - with JsonlClient(endpoint="http://localhost:3000") as client: - items = list(client.basic.receive()) - assert [i.desc for i in items] == _EXPECTED - assert all(isinstance(i, Info) for i in items) - - -@pytest.mark.skipif(not _HAS_STRUCTURED_JSONL, reason="streaming.jsonl is not structured (unbranded flavor)") -@pytest.mark.asyncio -async def test_jsonl_receive_structured_async(): - """JSONL homogeneous: async receive() returns AsyncStream[Info].""" - async with AsyncJsonlClient(endpoint="http://localhost:3000") as client: - stream = await client.basic.receive() - items = [item async for item in stream] - assert [i.desc for i in items] == _EXPECTED - assert all(isinstance(i, Info) for i in items) - - -@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") -def test_sse_receive_homogeneous_structured_sync(): - """SSE homogeneous: unnamed.receive() returns Stream over the SSE events. - - The unnamed SSE scenario emits three ``message`` events with payload - ``{"desc": ...}``. Because the SSE item type is an ``@events`` union, the - generated callback yields parsed JSON (``dict``) rather than ``Info`` model - instances (see module docstring / ``_deserialize`` limitation). The stream - terminates naturally after the final event. - """ - with SseClient(endpoint="http://localhost:3000") as client: - items = list(client.unnamed.receive()) - assert [i["desc"] for i in items] == _EXPECTED - assert all(isinstance(i, dict) for i in items) - - -@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") -@pytest.mark.asyncio -async def test_sse_receive_homogeneous_structured_async(): - """Async SSE homogeneous: unnamed.receive() returns AsyncStream over the events.""" - async with AsyncSseClient(endpoint="http://localhost:3000") as client: - stream = await client.unnamed.receive() - items = [item async for item in stream] - assert [i["desc"] for i in items] == _EXPECTED - assert all(isinstance(i, dict) for i in items) - - -@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") -def test_sse_receive_heterogeneous_structured_sync(): - """SSE heterogeneous: named.receive() returns Stream over an ``@events`` union. - - The named SSE scenario emits ``responseCreated`` (``{"id": ...}``) then two - ``responseDelta`` (``{"delta": ...}``) events, followed by a terminal - ``data: [DONE]`` event. ``[DONE]`` is a string-literal member of the item union, - so the generator wires it as ``terminal_event`` and the runtime stops there - (without trying to JSON-parse ``[DONE]``). Per-event payloads are yielded as - parsed JSON (``dict``) rather than distinct ``ResponseCreated`` / ``ResponseDelta`` - model instances: discriminating them needs TCGC ``sseMetadata`` (#4882) plus a - ``module`` argument on the shared ``_deserialize`` (same limitation as paging - item deserialization). - """ - with SseClient(endpoint="http://localhost:3000") as client: - items = list(client.named.receive()) - assert all(isinstance(i, dict) for i in items) - assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] - - -@pytest.mark.skipif(not _HAS_STRUCTURED_SSE, reason="streaming.sse is not structured (unbranded flavor)") -@pytest.mark.asyncio -async def test_sse_receive_heterogeneous_structured_async(): - """Async SSE heterogeneous: named.receive() returns AsyncStream, terminating at [DONE].""" - async with AsyncSseClient(endpoint="http://localhost:3000") as client: - stream = await client.named.receive() - items = [item async for item in stream] - assert all(isinstance(i, dict) for i in items) - assert items == [{"id": "resp_1"}, {"delta": "Hello"}, {"delta": " world"}] diff --git a/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py b/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py index 20288f960ac..74e05cebd14 100644 --- a/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py +++ b/packages/http-client-python/tests/mock_api/shared/asynctests/test_streaming_jsonl_async.py @@ -21,3 +21,8 @@ async def client(): @pytest.mark.asyncio async def test_basic_send(client: JsonlClient): await client.basic.send(JSONL) + + +@pytest.mark.asyncio +async def test_basic_recv(client: JsonlClient): + assert b"".join([d async for d in (await client.basic.receive())]) == JSONL diff --git a/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py b/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py index d035530c054..494c17a3493 100644 --- a/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py +++ b/packages/http-client-python/tests/mock_api/shared/test_streaming_jsonl.py @@ -19,3 +19,7 @@ def client(): def test_basic_send(client: JsonlClient): client.basic.send(JSONL) + + +def test_basic_recv(client: JsonlClient): + assert b"".join(client.basic.receive()) == JSONL diff --git a/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py b/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py deleted file mode 100644 index 05944f80311..00000000000 --- a/packages/http-client-python/tests/mock_api/unbranded/asynctests/test_streaming_jsonl_unbranded_async.py +++ /dev/null @@ -1,28 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- -"""Unbranded JSONL streaming (async): ``receive()`` keeps the byte-iterator behavior. - -See the sync counterpart (test_streaming_jsonl_unbranded.py) for context: structured -`Stream[T]` streaming is Azure-only; the unbranded flavor keeps `AsyncIterator[bytes]`. -""" -import pytest -import pytest_asyncio - -from streaming.jsonl.aio import JsonlClient - - -@pytest_asyncio.fixture -async def client(): - async with JsonlClient(endpoint="http://localhost:3000") as client: - yield client - - -JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' - - -@pytest.mark.asyncio -async def test_basic_recv(client: JsonlClient): - assert b"".join([d async for d in (await client.basic.receive())]) == JSONL diff --git a/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py b/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py deleted file mode 100644 index 1a0264ebe33..00000000000 --- a/packages/http-client-python/tests/mock_api/unbranded/test_streaming_jsonl_unbranded.py +++ /dev/null @@ -1,30 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- -"""Unbranded JSONL streaming: ``receive()`` keeps the raw byte-iterator behavior. - -Structured streaming (`Stream[T]` / `AsyncStream[T]`) targets the vendored -`azure.core.rest`-based runtime and so applies to the Azure flavor only. For the -unbranded flavor, JSONL streaming responses keep the existing -`Iterator[bytes]` / `AsyncIterator[bytes]` behavior, which this test asserts. - -(The Azure structured `receive()` is covered by mock_api/azure/test_streaming_structured.py.) -""" -import pytest - -from streaming.jsonl import JsonlClient - - -@pytest.fixture -def client(): - with JsonlClient(endpoint="http://localhost:3000") as client: - yield client - - -JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' - - -def test_basic_recv(client: JsonlClient): - assert b"".join(client.basic.receive()) == JSONL diff --git a/packages/http-client-python/tests/unit/test_structured_streaming_response.py b/packages/http-client-python/tests/unit/test_structured_streaming_response.py deleted file mode 100644 index f1a0dbdd2b1..00000000000 --- a/packages/http-client-python/tests/unit/test_structured_streaming_response.py +++ /dev/null @@ -1,168 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -------------------------------------------------------------------------- -"""Tests for structured streaming (JSONL / SSE) response rendering. - -Covers the code path where a streaming response (driven by the TCGC response -stream metadata, Azure flavor) is rendered as ``Stream[T]`` / ``AsyncStream[T]`` -from the vendored ``_utils.streaming_base`` module instead of the raw -byte-iterator behavior. -""" - -import pytest - -from pygen.codegen.models import CodeModel, JSONModelType -from pygen.codegen.models.response import Response - - -@pytest.fixture -def code_model(): - return CodeModel( - { - "clients": [ - { - "name": "client", - "namespace": "blah", - "moduleName": "blah", - "parameters": [], - "url": "", - "operationGroups": [], - } - ], - "namespace": "namespace", - }, - options={ - "show-send-request": True, - "builders-visibility": "public", - "show-operations": True, - "models-mode": "dpg", - "version-tolerant": True, - "flavor": "azure", - }, - ) - - -def _register_model(code_model): - item_yaml = {"type": "model", "name": "Thing", "snakeCaseName": "thing"} - model_type = JSONModelType(item_yaml, code_model) - code_model.types_map[id(item_yaml)] = model_type - return item_yaml - - -def _streaming_response(code_model, kind): - item_yaml = _register_model(code_model) - return Response.from_yaml( - { - "statusCodes": [200], - "headers": [], - "type": None, - "streaming": {"kind": kind, "itemType": item_yaml}, - }, - code_model, - ) - - -def test_jsonl_response_is_structured_stream(code_model): - response = _streaming_response(code_model, "jsonl") - assert response.is_structured_stream is True - assert response.streaming_kind == "jsonl" - - -def test_sse_response_is_structured_stream(code_model): - response = _streaming_response(code_model, "sse") - assert response.is_structured_stream is True - assert response.streaming_kind == "sse" - - -def test_type_annotation_sync_and_async(code_model): - response = _streaming_response(code_model, "jsonl") - sync = response.type_annotation(async_mode=False) - asynchronous = response.type_annotation(async_mode=True) - assert sync.startswith("Stream[") and sync.endswith("]"), sync - assert asynchronous.startswith("AsyncStream[") and asynchronous.endswith("]"), asynchronous - - -def test_docstring_type_references_streaming_base(code_model): - response = _streaming_response(code_model, "jsonl") - assert "~namespace._utils.streaming_base.Stream[" in response.docstring_type(async_mode=False) - assert "~namespace._utils.streaming_base.AsyncStream[" in response.docstring_type(async_mode=True) - - -def test_imports_add_stream_class(code_model): - response = _streaming_response(code_model, "jsonl") - imports = response.imports(async_mode=False) - imports_str = str(imports.to_dict()) - # Vendored local import, not azure.core.streaming. - assert "streaming_base" in imports_str - assert "azure.core.streaming" not in imports_str - - -def test_sse_imports_add_json(code_model): - response = _streaming_response(code_model, "sse") - imports = response.imports(async_mode=False) - assert "json" in str(imports.to_dict()) - - -def test_non_streaming_response_is_not_structured_stream(code_model): - item_yaml = _register_model(code_model) - response = Response.from_yaml( - {"statusCodes": [200], "headers": [], "type": item_yaml}, - code_model, - ) - assert response.is_structured_stream is False - assert response.streaming_kind is None - - -def test_streaming_base_template_renders_vendored_runtime(): - """The vendored ``streaming_base.py`` template renders the Stream/AsyncStream runtime.""" - from jinja2 import Environment, PackageLoader - - from pygen.codegen.serializers.general_serializer import GeneralSerializer - - cm = CodeModel( - { - "clients": [ - { - "name": "client", - "namespace": "blah", - "moduleName": "blah", - "parameters": [], - "url": "", - "operationGroups": [], - } - ], - "namespace": "namespace", - }, - options={ - "show-send-request": True, - "builders-visibility": "public", - "show-operations": True, - "models-mode": "dpg", - "version-tolerant": True, - "flavor": "azure", - }, - ) - env = Environment( - loader=PackageLoader("pygen.codegen", "templates"), - keep_trailing_newline=True, - line_statement_prefix="##", - line_comment_prefix="###", - trim_blocks=True, - lstrip_blocks=True, - ) - rendered = GeneralSerializer(code_model=cm, env=env, async_mode=False).serialize_streaming_base_file() - # Vendored runtime is self-contained: depends only on azure.core.rest, not azure.core.streaming. - assert "class Stream(" in rendered - assert "class AsyncStream(" in rendered - assert "from azure.core.rest import" in rendered - assert "import azure.core.streaming" not in rendered - assert "from azure.core.streaming" not in rendered - - -def test_need_streaming_base_flag(code_model): - """need_streaming_base tracks has_structured_stream (no operations -> False).""" - # No operations registered in this fixture, so no structured stream is present. - assert code_model.has_structured_stream is False - assert code_model.need_streaming_base is False From cbd0cdd0f6b48ddd8c746931eea5d378126c65a4 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Fri, 7 Aug 2026 09:11:57 -0700 Subject: [PATCH 13/15] simplify chronus --- .chronus/changes/structured-streaming-2026-0-0.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.chronus/changes/structured-streaming-2026-0-0.md b/.chronus/changes/structured-streaming-2026-0-0.md index 41069aaf26d..5873b3394ff 100644 --- a/.chronus/changes/structured-streaming-2026-0-0.md +++ b/.chronus/changes/structured-streaming-2026-0-0.md @@ -4,9 +4,8 @@ packages: - "@typespec/http-client-python" --- -Generate structured streaming client methods for the **Azure flavor**: operations whose HTTP response is a JSONL (`application/jsonl`) or SSE (`text/event-stream`) stream now return `Stream[T]` / `AsyncStream[T]`, yielding deserialized model instances instead of raw bytes. This is driven entirely by the TCGC response stream metadata (the response stream type) — there is no opt-in emitter option. The unbranded flavor keeps the existing raw byte-iterator behavior (`Iterator[bytes]` / `AsyncIterator[bytes]`). +Generate structured streaming client methods for the **Azure flavor**: operations whose HTTP response is a JSONL (`application/jsonl`) or SSE (`text/event-stream`) stream now return `Stream[T]` / `AsyncStream[T]`, yielding deserialized model instances instead of raw bytes. -Note: for the Azure flavor this changes the return type of JSONL/SSE streaming operations from a raw byte iterator to `Stream[T]` / `AsyncStream[T]`. The `Stream` / `AsyncStream` runtime (plus the JSONL / SSE decoders) is vendored into the generated package at `_utils/streaming_base.py` (like `_utils/model_base.py`), so it depends only on the released `azure.core.rest` — not on an unreleased `azure.core.streaming`. @@ -16,9 +15,3 @@ stream = client.receive() # -> Stream[Thing] for thing in stream: # deserialized model instances ... ``` - -Known limitations / follow-ups: - -- SSE union item types deserialize to parsed JSON (e.g. `dict`) rather than model instances — same root cause as paging item deserialization; the shared `_deserialize` needs a `module` argument to resolve forward-reference union member names. -- Heterogeneous SSE **terminal-event** handling is supported: the terminal marker (e.g. `"[DONE]"`) is detected structurally as a string-literal member of the item union and passed to the vendored `Stream` / `AsyncStream` as `terminal_event`, so iteration stops before parsing it. Per-event **model dispatch** (routing each `@events` event to its distinct payload model) is still blocked on TCGC `sseMetadata` (typespec-client-generator-core #4882), absent from the resolved TCGC version; until then heterogeneous events are yielded as parsed JSON. -- In-repo mock_api coverage: JSONL homogeneous (sync + async) is active against the default Azure `streaming.jsonl` package and yields deserialized model instances; the unbranded byte-iterator behavior is covered separately. SSE homogeneous (`unnamed/receive`) and heterogeneous (`named/receive`, terminating at `[DONE]`) mock_api tests are active (sync + async) against the `streaming/sse` scenario in `@typespec/http-specs`, asserting the yielded event payloads (as `dict`s per the union-deserialization limitation). From 7a5d1ff9b992c408c470d433d02ce4ef6be882e3 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Fri, 7 Aug 2026 09:42:18 -0700 Subject: [PATCH 14/15] fixing up overloads --- .../http-client-python/emitter/src/http.ts | 12 +------ .../pygen/codegen/models/response.py | 34 ++++--------------- .../generator/pygen/preprocess/__init__.py | 2 ++ 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts index b1788dc21dd..a8b0ca5563e 100644 --- a/packages/http-client-python/emitter/src/http.ts +++ b/packages/http-client-python/emitter/src/http.ts @@ -76,17 +76,7 @@ export function isStructuredStreamType(type: SdkType): boolean { function getStreamingInfo( context: PythonSdkContext, response: SdkHttpResponse | SdkHttpErrorResponse, - method?: SdkServiceMethod, ): Record | undefined { - // Structured streaming targets the vendored `azure.core.rest`-based runtime, so - // it only applies to the Azure flavor. For unbranded, keep the raw byte-iterator - // behavior. - if ((context.emitContext.options as any).flavor !== "azure") return undefined; - // Request-body streaming is out of scope: operations that carry a request body are - // kept on the raw byte-iterator path. This also avoids the per-request-content-type - // overloads (whose response item types are serialized inline rather than registered - // globally) producing an inconsistent mix of `Stream[T]` and `Iterator[bytes]`. - if (method?.operation.bodyParam) return undefined; const streamMetadata = response.streamMetadata; if (!streamMetadata) return undefined; if (!isStructuredStreamType(streamMetadata.streamType)) return undefined; @@ -745,7 +735,7 @@ function emitHttpResponse( "invalid-lro-result", method, ), - streaming: isException ? undefined : getStreamingInfo(context, response, method), + streaming: isException ? undefined : getStreamingInfo(context, response), }; } diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 091b3b723b1..02d6eb96f7f 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -58,16 +58,8 @@ def __init__( self.type = type self.nullable = yaml_data.get("nullable") self.default_content_type = yaml_data.get("defaultContentType") - # Structured streaming (JSONL / SSE) metadata. When present, ``self.type`` holds the - # per-item type (model or union) rather than the raw byte body, and the response is - # rendered as ``Stream[Item]`` / ``AsyncStream[Item]``. streaming = yaml_data.get("streaming") - # Only treat this as a structured stream when the resolved ``type`` is the per-item - # type (model / union). When the structured item type could not be resolved we fall - # back to the raw byte body (``BinaryIteratorType``) and must NOT render ``Stream[...]``. - self.streaming_kind: Optional[str] = ( - streaming["kind"] if streaming and not isinstance(self.type, BinaryIteratorType) else None - ) + self.streaming_kind: Optional[str] = streaming["kind"] if streaming else None @property def result_property(self) -> str: @@ -230,24 +222,12 @@ def _get_import_type(self, input_path: str) -> ImportType: def from_yaml(cls, yaml_data: dict[str, Any], code_model: "CodeModel") -> "Response": streaming = yaml_data.get("streaming") if streaming: - # Structured stream (JSONL / SSE): the response ``type`` is the raw byte body, - # but we render per-item types, so use the streaming item type instead and do - # NOT convert it to a BinaryIteratorType (that would trigger the raw-bytes path). - # Resolve the item type from the global type map. For heterogeneous / request-body - # streaming overloads (out of scope) the item type is serialized inline and not - # collected globally; in that case fall back to the raw byte-iterator path below - # instead of failing generation (and to avoid emitting duplicate inline models). - try: - item_type = code_model.lookup_type(id(streaming["itemType"])) - except KeyError: - item_type = None - if item_type is not None: - return cls( - yaml_data=yaml_data, - code_model=code_model, - headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]], - type=item_type, - ) + return cls( + yaml_data=yaml_data, + code_model=code_model, + headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]], + type=code_model.lookup_type(id(streaming["itemType"])), + ) type = code_model.lookup_type(id(yaml_data["type"])) if yaml_data.get("type") else None # use ByteIteratorType if we are returning a binary type default_content_type = yaml_data.get("defaultContentType", "application/json") diff --git a/packages/http-client-python/generator/pygen/preprocess/__init__.py b/packages/http-client-python/generator/pygen/preprocess/__init__.py index ff3d6f094e3..8de706bcc6e 100644 --- a/packages/http-client-python/generator/pygen/preprocess/__init__.py +++ b/packages/http-client-python/generator/pygen/preprocess/__init__.py @@ -35,6 +35,8 @@ def update_overload_section( for overload_s, original_s in zip(overload[section], yaml_data[section]): if overload_s.get("type"): overload_s["type"] = original_s["type"] + if overload_s.get("streaming"): + overload_s["streaming"] = original_s["streaming"] if overload_s.get("headers"): for overload_h, original_h in zip(overload_s["headers"], original_s["headers"]): if overload_h.get("type"): From c72247a210e924037482ee5fb5c7533a0cc96452 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Fri, 7 Aug 2026 10:54:26 -0700 Subject: [PATCH 15/15] feat(http-client-python): use SSE event metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e57edafe-9764-4b99-a1ae-0efd56e8729e --- packages/http-client-python/README.md | 13 +- .../http-client-python/emitter/src/http.ts | 63 ++- .../emitter/test/streaming.test.ts | 24 +- .../pygen/codegen/models/response.py | 68 ++- .../codegen/serializers/builder_serializer.py | 44 +- .../generator/pygen/preprocess/__init__.py | 2 +- packages/http-client-python/package-lock.json | 454 +++++++++--------- packages/http-client-python/package.json | 11 +- 8 files changed, 396 insertions(+), 283 deletions(-) diff --git a/packages/http-client-python/README.md b/packages/http-client-python/README.md index 43b4343d479..3e329760ea0 100644 --- a/packages/http-client-python/README.md +++ b/packages/http-client-python/README.md @@ -167,15 +167,6 @@ for thing in stream: # deserialized model instances ... ``` -The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vendored** into the generated package at `_utils/streaming_base.py` (alongside `_utils/model_base.py`). It depends only on the released `azure.core.rest`, so no unreleased `azure.core.streaming` (azure-core PR #48077) dependency is required at runtime. +The `Stream` / `AsyncStream` runtime (plus the JSONL and SSE decoders) is **vendored** into the generated package at `_utils/streaming_base.py` (alongside `_utils/model_base.py`). It depends only on the released `azure.core.rest`, so no unreleased `azure.core.streaming` dependency is required at runtime. -> **Note:** For SSE responses whose item type is a union (`@events`), each event payload is currently yielded as the parsed JSON value (e.g. a `dict` for object payloads, or the literal for terminal events such as `"[DONE]"`) rather than a fully deserialized model instance. This mirrors the existing union item-deserialization behavior used elsewhere in the generator. JSONL responses with a single model item type are deserialized into model instances. - -#### Known limitations / follow-ups - -- **SSE union item deserialization** — SSE item types are `@events` unions, so each event is deserialized against a forward-reference union member name and yielded as the parsed JSON value rather than a model instance. This shares a root cause with paging item deserialization: the shared `_deserialize` helper needs a `module` argument to resolve the union member names into concrete model classes. JSONL (single model item type) is unaffected and fully deserializes. -- **Heterogeneous SSE per-event dispatch** — A heterogeneous SSE stream is an `@events` union where each event has a distinct type and one may be marked `@terminalEvent` (e.g. `"[DONE]"`). The **terminal event is handled today**: it appears as a string-literal (`Literal["[DONE]"]`) member of the item union, so the generator detects it structurally and passes it to the vendored `Stream` / `AsyncStream` as `terminal_event`; the runtime stops iterating when an event's `data` matches, without attempting to JSON-parse it. What is **not** yet wired is per-event _model dispatch_ — routing each `eventType` to its distinct payload model — because that mapping (event name → payload type) is not recoverable from `SdkStreamMetadata` alone: the union collapses to `Union[Thing, Literal["[DONE]"]]` in the generated code, dropping the event names. Per-event dispatch requires TCGC `sseMetadata` (`SdkSseMetadata.events[]` with `eventType` / `payloadType` / `isTerminalEvent` / `isEventEnvelope`, [typespec-client-generator-core #4882](https://github.com/Azure/typespec-azure/pull/4882)). Until then, heterogeneous events are yielded as parsed JSON (`dict`), which the SSE union item-deserialization limitation above already implies. - - Investigation (2026-08): `sseMetadata` is **not** present in the resolved TCGC `0.69.1`, **nor in `0.70.0`** (latest stable — its `SdkStreamMetadata` is byte-identical to 0.69.1, no SSE symbols). `SdkSseMetadata` (`events[]` per `@events` union variant, built by `buildSdkSseMetadata`) has since landed upstream on `Azure/typespec-azure` `main` and first appears in the `next` prerelease line (`0.71.0-dev.11`). Adopting it requires the `@typespec` 1.14 / 0.84 family bump those versions carry. Terminal-event termination does **not** depend on it (handled structurally, see above); only per-event model dispatch does. - -- **SSE mock_api coverage** — The SSE spector scenario at `packages/http-specs/specs/streaming/sse/` (pinned via `@typespec/http-specs` `0.1.0-alpha.40`) defines three routes: `unnamed/receive` (homogeneous — a single unnamed `@events` variant → `message` events), `named/receive` (heterogeneous — `responseCreated`/`responseDelta` + `@terminalEvent "[DONE]"`), and `retrieve/stream` (heterogeneous with a request body). Homogeneous `unnamed/receive` and heterogeneous `named/receive` back real SSE mock_api tests (sync + async) in `tests/mock_api/azure/test_streaming_structured.py`; both assert the yielded event payloads (as `dict`s, per the union-deserialization limitation) and, for `named`, clean termination at the `[DONE]` terminal event. The `retrieve/stream` route is out of scope (request-body streaming). JSONL uses the existing `streaming/jsonl` scenario; the JSONL homogeneous mock_api tests (sync + async) run against the default Azure `streaming.jsonl` package and yield fully deserialized model instances. +SSE `@events` unions use TCGC event metadata to deserialize each named event into its corresponding generated model. Events marked with `@terminalEvent` stop iteration without being yielded. diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts index a8b0ca5563e..4b09733415b 100644 --- a/packages/http-client-python/emitter/src/http.ts +++ b/packages/http-client-python/emitter/src/http.ts @@ -59,6 +59,25 @@ export function isStructuredStreamType(type: SdkType): boolean { } } +export function getStructuredStreamKind( + response: SdkHttpResponse | SdkHttpErrorResponse, +): "jsonl" | "sse" | undefined { + if (response.sseMetadata) return "sse"; + + const contentTypes = response.streamMetadata?.contentTypes ?? response.contentTypes ?? []; + for (const contentType of contentTypes) { + const mediaType = contentType.split(";", 1)[0].trim().toLowerCase(); + if (mediaType === "text/event-stream") return "sse"; + if (mediaType === "application/jsonl") return "jsonl"; + } + return undefined; +} + +function getStringConstantValue(type: SdkType): string | undefined { + if (type.kind === "nullable") return getStringConstantValue(type.type); + return type.kind === "constant" && typeof type.value === "string" ? type.value : undefined; +} + /** * Build the `streaming` block for a response YAML when the response is a JSONL/SSE * stream with a structured payload type (driven by the TCGC stream metadata). @@ -66,12 +85,8 @@ export function isStructuredStreamType(type: SdkType): boolean { * Returns `undefined` when structured streaming should not apply, in which case * the existing raw byte-iterator behavior is preserved. * - * Note: the currently consumed TCGC metadata (`streamMetadata`) does not expose - * per-event SSE metadata (event-type dispatch). Terminal-event handling does NOT - * depend on it — the terminal marker is a string-literal member of the item union - * (e.g. `Literal["[DONE]"]`), which the generator detects structurally and passes - * to the vendored runtime as `terminal_event`. Only `kind` and `itemType` are - * emitted here; the terminal event is derived generator-side from `itemType`. + * For SSE, TCGC `sseMetadata` supplies each event's wire name, payload type, and + * terminal marker. JSONL only needs the common stream item type. */ function getStreamingInfo( context: PythonSdkContext, @@ -80,19 +95,33 @@ function getStreamingInfo( const streamMetadata = response.streamMetadata; if (!streamMetadata) return undefined; if (!isStructuredStreamType(streamMetadata.streamType)) return undefined; - const contentTypes = streamMetadata.contentTypes ?? response.contentTypes ?? []; - const isSse = contentTypes.some((ct) => ct.toLowerCase().includes("event-stream")); - // SSE kind is detected from the response Content-Type. A heterogeneous `@events` - // union streamType is emitted as a single union `itemType`; the generator detects - // the terminal event (a string-literal union member such as `[DONE]`) structurally - // and wires it into the runtime, so terminal-event termination works without TCGC - // `sseMetadata`. Per-event MODEL dispatch (routing each event to its distinct - // payload model) still requires `sseMetadata` (SdkSseMetadata.events[], TCGC - // #4882); until then heterogeneous events are yielded as parsed JSON. - return { - kind: isSse ? "sse" : "jsonl", + const kind = getStructuredStreamKind(response); + if (!kind) return undefined; + + const streaming: Record = { + kind, itemType: getType(context, streamMetadata.streamType), }; + + if (kind === "sse" && response.sseMetadata) { + const events: Record[] = []; + let terminalEvent: string | undefined; + for (const event of response.sseMetadata.events) { + if (event.isTerminalEvent) { + terminalEvent = + getStringConstantValue(event.payloadType) ?? getStringConstantValue(event.type); + } else { + events.push({ + eventType: event.eventType, + itemType: getType(context, event.payloadType), + }); + } + } + if (events.length > 0) streaming.events = events; + if (terminalEvent !== undefined) streaming.terminalEvent = terminalEvent; + } + + return streaming; } function isEtagType(type: SdkType): boolean { diff --git a/packages/http-client-python/emitter/test/streaming.test.ts b/packages/http-client-python/emitter/test/streaming.test.ts index 9fc025e7bf0..491bc04b098 100644 --- a/packages/http-client-python/emitter/test/streaming.test.ts +++ b/packages/http-client-python/emitter/test/streaming.test.ts @@ -1,6 +1,6 @@ import { strictEqual } from "assert"; import { describe, it } from "vitest"; -import { isStructuredStreamType } from "../src/http.js"; +import { getStructuredStreamKind, isStructuredStreamType } from "../src/http.js"; describe("typespec-python: structured streaming", () => { it("treats model and union payloads as structured", () => { @@ -20,4 +20,26 @@ describe("typespec-python: structured streaming", () => { strictEqual(isStructuredStreamType({ kind: "bytes" } as any), false); strictEqual(isStructuredStreamType({ kind: "string" } as any), false); }); + + it("detects the stream protocol explicitly", () => { + strictEqual(getStructuredStreamKind({ sseMetadata: { events: [] } } as any), "sse"); + strictEqual( + getStructuredStreamKind({ + streamMetadata: { contentTypes: ["text/event-stream; charset=utf-8"] }, + } as any), + "sse", + ); + strictEqual( + getStructuredStreamKind({ + streamMetadata: { contentTypes: ["application/jsonl"] }, + } as any), + "jsonl", + ); + strictEqual( + getStructuredStreamKind({ + streamMetadata: { contentTypes: ["application/json"] }, + } as any), + undefined, + ); + }); }); diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py index 02d6eb96f7f..545a4946e63 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/response.py +++ b/packages/http-client-python/generator/pygen/codegen/models/response.py @@ -60,6 +60,13 @@ def __init__( self.default_content_type = yaml_data.get("defaultContentType") streaming = yaml_data.get("streaming") self.streaming_kind: Optional[str] = streaming["kind"] if streaming else None + self.streaming_events: list[tuple[Optional[str], BaseType]] = [] + self._streaming_terminal_event: Optional[str] = streaming.get("terminalEvent") if streaming else None + if streaming: + self.streaming_events = [ + (event.get("eventType"), self.code_model.lookup_type(id(event["itemType"]))) + for event in streaming.get("events", []) + ] @property def result_property(self) -> str: @@ -103,14 +110,14 @@ def is_structured_stream(self) -> bool: def terminal_event(self) -> Optional[str]: """Terminal event marker for a heterogeneous SSE stream, if any. - Heterogeneous SSE ``@events`` unions include a string-literal member (e.g. - ``"[DONE]"``) that marks the end of the stream. Without TCGC ``sseMetadata`` - (#4882) we detect it structurally: the first ``ConstantType`` string member of - the union item type is treated as the terminal marker, so the runtime can stop - before attempting to JSON-deserialize it. Returns ``None`` for homogeneous - streams (no constant member) and for JSONL. + TCGC ``sseMetadata`` supplies this marker directly. For compatibility with + older metadata, a string-literal member of the item union is used as a fallback. """ - if self.streaming_kind != "sse" or not isinstance(self.type, CombinedType): + if self.streaming_kind != "sse": + return None + if self._streaming_terminal_event is not None: + return self._streaming_terminal_event + if not isinstance(self.type, CombinedType): return None from .constant_type import ConstantType @@ -122,6 +129,12 @@ def terminal_event(self) -> Optional[str]: def stream_class_name(self, async_mode: bool) -> str: return "AsyncStream" if async_mode else "Stream" + @property + def stream_item_type(self) -> Optional[BaseType]: + if len(self.streaming_events) == 1: + return self.streaming_events[0][1] + return self.type + def serialization_type(self, **kwargs: Any) -> str: if self.type: return self.type.serialization_type(**kwargs) @@ -136,9 +149,10 @@ def stream_item_annotation(self, **kwargs: Any) -> str: the union inline (``Union[Model, ...]`` / the single member) so the annotation is a valid type expression. """ - if isinstance(self.type, CombinedType): - return self.type.type_definition(**kwargs) - return self.type.type_annotation(**kwargs) if self.type else "None" + item_type = self.stream_item_type + if isinstance(item_type, CombinedType): + return item_type.type_definition(**kwargs) + return item_type.type_annotation(**kwargs) if item_type else "None" def type_annotation(self, **kwargs: Any) -> str: if self.is_structured_stream and self.type: @@ -159,7 +173,8 @@ def docstring_text(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - return f"An instance of {stream_class} that iterates over {self.type.docstring_text(**kwargs)}" + item_type = self.stream_item_type or self.type + return f"An instance of {stream_class} that iterates over {item_type.docstring_text(**kwargs)}" if self.nullable and self.type: return f"{self.type.docstring_text(**kwargs)} or None" return self.type.docstring_text(**kwargs) if self.type else "None" @@ -168,7 +183,7 @@ def docstring_type(self, **kwargs: Any) -> str: kwargs["is_response"] = True if self.is_structured_stream and self.type: stream_class = self.stream_class_name(kwargs.get("async_mode", False)) - item_type = self.type.docstring_type(**kwargs) + item_type = (self.stream_item_type or self.type).docstring_type(**kwargs) return f"~{self.code_model.namespace}._utils.streaming_base.{stream_class}[{item_type}]" if self.nullable and self.type: return f"{self.type.docstring_type(**kwargs)} or None" @@ -176,22 +191,23 @@ def docstring_type(self, **kwargs: Any) -> str: def imports(self, **kwargs: Any) -> FileImport: file_import = FileImport(self.code_model) + item_type = self.stream_item_type if self.is_structured_stream else self.type # For a structured stream whose item type is a named ``@events`` union, the annotation # is expanded inline (see ``stream_item_annotation``), so import the union member types # rather than the ``_unions`` alias. - if self.is_structured_stream and isinstance(self.type, CombinedType): - for member in self.type.types: + if self.is_structured_stream and isinstance(item_type, CombinedType): + for member in item_type.types: file_import.merge(member.imports(**kwargs)) # ``Union`` is only needed when the inline expansion actually yields a union of # 2+ distinct member types (a single member collapses to that member; a union of # only literals collapses to a single ``Literal[...]``). - distinct = list(dict.fromkeys(m.type_annotation(**kwargs) for m in self.type.types)) - all_constant = all(t.type == "constant" for t in self.type.types) + distinct = list(dict.fromkeys(m.type_annotation(**kwargs) for m in item_type.types)) + all_constant = all(t.type == "constant" for t in item_type.types) if len(distinct) > 1 and not all_constant: file_import.add_submodule_import("typing", "Union", ImportType.STDLIB) - elif self.type: - file_import.merge(self.type.imports(**kwargs)) - if isinstance(self.type, CombinedType) and self.type.name: + elif item_type: + file_import.merge(item_type.imports(**kwargs)) + if not self.is_structured_stream and isinstance(item_type, CombinedType) and item_type.name: serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) file_import.add_submodule_import( self.code_model.get_relative_import_path(serialize_namespace), @@ -210,6 +226,8 @@ def imports(self, **kwargs: Any) -> FileImport: file_import.add_submodule_import(relative_path, stream_class, ImportType.LOCAL) if self.streaming_kind == "sse": file_import.add_import("json", ImportType.STDLIB) + for _event_type, event_item_type in self.streaming_events: + file_import.merge(event_item_type.imports(**kwargs)) return file_import def _get_import_type(self, input_path: str) -> ImportType: @@ -222,12 +240,12 @@ def _get_import_type(self, input_path: str) -> ImportType: def from_yaml(cls, yaml_data: dict[str, Any], code_model: "CodeModel") -> "Response": streaming = yaml_data.get("streaming") if streaming: - return cls( - yaml_data=yaml_data, - code_model=code_model, - headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]], - type=code_model.lookup_type(id(streaming["itemType"])), - ) + return cls( + yaml_data=yaml_data, + code_model=code_model, + headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]], + type=code_model.lookup_type(id(streaming["itemType"])), + ) type = code_model.lookup_type(id(yaml_data["type"])) if yaml_data.get("type") else None # use ByteIteratorType if we are returning a binary type default_content_type = yaml_data.get("defaultContentType", "application/json") diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 4713bac3258..04c3a266530 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -1268,22 +1268,46 @@ def handle_structured_stream_response(self, builder: OperationType) -> list[str] ) stream_class = response.stream_class_name(self.async_mode) # type: ignore[attr-defined] terminal_event = getattr(response, "terminal_event", None) + streaming_events = getattr(response, "streaming_events", []) retval: list[str] = [] retval.append("def _callback(_http_response, _event):") if response.streaming_kind == "sse": # type: ignore[attr-defined] - # Heterogeneous SSE (``@events`` unions) is deserialized against the union item - # type below; the shared ``_deserialize`` cannot resolve a forward-ref union - # member name into a concrete model, so payloads are yielded as parsed JSON. - # Per-event ``eventType`` dispatch into distinct model instances requires the - # TCGC ``sseMetadata`` (SdkSseMetadata.events[], typespec-client-generator-core - # #4882), which is unavailable in the currently pinned TCGC version. The stream's - # terminal event (a string-literal union member such as ``[DONE]``) is detected - # structurally and passed as ``terminal_event`` below, so the runtime stops - # before this callback attempts to JSON-parse it. retval.append(" _event_json = json.loads(_event.data)") + named_events = [ + (event_type, event_item_type) + for event_type, event_item_type in streaming_events + if event_type is not None + ] + unnamed_events = [event_item_type for event_type, event_item_type in streaming_events if event_type is None] + if named_events: + for index, (event_type, event_item_type) in enumerate(named_events): + event_annotation = event_item_type.type_annotation( + is_operation_file=True, + serialize_namespace=self.serialize_namespace, + ) + keyword = "if" if index == 0 else "elif" + retval.append(f" {keyword} _event.event == {event_type!r}:") + retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") + retval.append(" else:") + if len(unnamed_events) == 1: + event_annotation = unnamed_events[0].type_annotation( + is_operation_file=True, + serialize_namespace=self.serialize_namespace, + ) + retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") + else: + retval.append(" deserialized = _event_json") + elif len(unnamed_events) == 1: + event_annotation = unnamed_events[0].type_annotation( + is_operation_file=True, + serialize_namespace=self.serialize_namespace, + ) + retval.append(f" deserialized = _deserialize({event_annotation}, _event_json)") + else: + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") else: retval.append(" _event_json = _event.json()") - retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") + retval.append(f" deserialized = _deserialize({item_annotation}, _event_json)") retval.append(" if cls:") retval.append(" return cls(pipeline_response, deserialized, {}) # type: ignore") retval.append(" return deserialized") diff --git a/packages/http-client-python/generator/pygen/preprocess/__init__.py b/packages/http-client-python/generator/pygen/preprocess/__init__.py index 8de706bcc6e..d79fcb42235 100644 --- a/packages/http-client-python/generator/pygen/preprocess/__init__.py +++ b/packages/http-client-python/generator/pygen/preprocess/__init__.py @@ -36,7 +36,7 @@ def update_overload_section( if overload_s.get("type"): overload_s["type"] = original_s["type"] if overload_s.get("streaming"): - overload_s["streaming"] = original_s["streaming"] + overload_s["streaming"] = original_s["streaming"] if overload_s.get("headers"): for overload_h, original_h in zip(overload_s["headers"], original_s["headers"]): if overload_h.get("type"): diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index 37ae20d62e5..57a78e30ac5 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -18,15 +18,15 @@ }, "devDependencies": { "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@azure-tools/typespec-autorest": "~0.70.0", + "@azure-tools/typespec-autorest": "0.71.0-dev.4", "@azure-tools/typespec-azure-core": "~0.70.0", "@azure-tools/typespec-azure-resource-manager": "~0.70.0", - "@azure-tools/typespec-azure-rulesets": "~0.70.0", - "@azure-tools/typespec-client-generator-core": "~0.70.0", + "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", + "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", "@types/js-yaml": "~4.0.5", "@types/node": "~25.0.2", "@types/semver": "7.5.8", - "@typespec/compiler": "^1.14.0", + "@typespec/compiler": "1.15.0-dev.17", "@typespec/events": "~0.84.0", "@typespec/http": "^1.14.0", "@typespec/http-specs": "0.1.0-alpha.40", @@ -89,9 +89,9 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.70.0.tgz", - "integrity": "sha512-OaxLkgMcuOXAbaqTNpezmFF24jtkiIH1+2PBwAeRo3ZG7C1r7Hf8xZwCK6KVtBEgMbqnrd5eCqxsPl1zy3y9/Q==", + "version": "0.71.0-dev.4", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@azure-tools/typespec-autorest/-/typespec-autorest-0.71.0-dev.4.tgz", + "integrity": "sha1-p920uaoYRzfFVIeEUDW6+uUri1w=", "dev": true, "license": "MIT", "dependencies": { @@ -101,9 +101,9 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0", - "@azure-tools/typespec-client-generator-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.10", + "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", "@typespec/compiler": "^1.14.0", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", @@ -119,8 +119,8 @@ }, "node_modules/@azure-tools/typespec-azure-core": { "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.70.0.tgz", - "integrity": "sha512-8MojHWRtTLKycJJ98IMoXX/5b9tTo3F0d3Iu20OKoCsORnSDG2NfjOWHJVW63oxA2t8VTlqC6J8BDcnRihygQQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.70.0.tgz", + "integrity": "sha1-k3VYRkt7yNAA1kiBElz71YbZH7A=", "dev": true, "license": "MIT", "engines": { @@ -134,8 +134,8 @@ }, "node_modules/@azure-tools/typespec-azure-resource-manager": { "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.70.0.tgz", - "integrity": "sha512-hVrbbsOhU3EQ2yQTppCqsGQwY/HcVZPOINtFkoUo+PUVBmCFXyqLkTO4jvUbsp/LvJEwoQ8aEA8Y35f7VWT5uw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.70.0.tgz", + "integrity": "sha1-+Skz0cnW1LADE60g/JnqYk0nixg=", "dev": true, "license": "MIT", "dependencies": { @@ -155,25 +155,25 @@ } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.70.0.tgz", - "integrity": "sha512-Uxxl/18oryDwk2S+aYx6cIqiyjmoMeFDGmjuQ72a+aw6u8mZjgahMxNsY0ShvGLSchjsDqsVGaUlazXGXakVrw==", + "version": "0.71.0-dev.5", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.71.0-dev.5.tgz", + "integrity": "sha1-bpzvEq9boKSAhUwBc+EKXvxNOwI=", "dev": true, "license": "MIT", "engines": { "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", - "@azure-tools/typespec-azure-resource-manager": "^0.70.0", - "@azure-tools/typespec-client-generator-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.4", + "@azure-tools/typespec-azure-resource-manager": "^0.70.0 || >= 0.71.0-dev.11", + "@azure-tools/typespec-client-generator-core": "^0.70.0 || >= 0.71.0-dev.11", "@typespec/compiler": "^1.14.0" } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.70.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.70.0.tgz", - "integrity": "sha512-8yxOYJfID3wp3FLQYNIa3kbmR5YLWjYtpB+i4u66quHTTQWWANHV1/o9f8xymAf+8fO9jbLo5tw1JerumxISWg==", + "version": "0.71.0-dev.11", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.71.0-dev.11.tgz", + "integrity": "sha1-uBy0avXoMit1nkolVuXXLg2gbmw=", "dev": true, "license": "MIT", "dependencies": { @@ -185,7 +185,7 @@ "node": ">=22.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.70.0", + "@azure-tools/typespec-azure-core": "^0.70.0 || >= 0.71.0-dev.3", "@typespec/compiler": "^1.14.0", "@typespec/events": "^0.84.0", "@typespec/http": "^1.14.0", @@ -999,8 +999,8 @@ }, "node_modules/@eslint/config-array": { "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha1-8p4iBXrVMWzyODbO6aNMgf/8t+Y=", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1014,9 +1014,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha1-POdNiYhRNr4VNTQfjD1EJcKaXKs=", "dev": true, "license": "MIT", "peer": true, @@ -1027,8 +1027,8 @@ }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha1-WAyI+NVEXyvWqo88re+g3nn71p4=", "dev": true, "license": "ISC", "peer": true, @@ -1041,8 +1041,8 @@ }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha1-G9AGzut+LlWyt3OrMY0wDhpmrto=", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1055,8 +1055,8 @@ }, "node_modules/@eslint/core": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha1-dyJYIEE9lhdQnak0IZCiAZ54dhw=", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1068,9 +1068,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha1-0iv9azp9jh8sCy8ubeERtT7G4T4=", "dev": true, "license": "MIT", "peer": true, @@ -1081,7 +1081,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -1093,9 +1093,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha1-B+mCx0YmFnqnoklcU4F4ktcTlJI=", "dev": true, "license": "MIT", "peer": true, @@ -1111,9 +1111,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha1-POdNiYhRNr4VNTQfjD1EJcKaXKs=", "dev": true, "license": "MIT", "peer": true, @@ -1124,16 +1124,16 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true, "license": "MIT", "peer": true }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha1-WAyI+NVEXyvWqo88re+g3nn71p4=", "dev": true, "license": "ISC", "peer": true, @@ -1145,9 +1145,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha1-by+8/3VQDSKdU14KlJrhNHLIR4c=", "dev": true, "license": "MIT", "peer": true, @@ -1160,8 +1160,8 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha1-biEmoTR+hqTe34cG7Gf/jhB+u60=", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1171,8 +1171,8 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha1-l3nj/Zt+4zVxpXQ1z0M1oXlKbLI=", "dev": true, "license": "Apache-2.0", "peer": true, @@ -1185,48 +1185,52 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha1-qCcsoDsqz0kmcCIrIyC2xCG/3mA=", "dev": true, + "license": "Apache-2.0", "peer": true, + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.8", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha1-j4AMzME/T4zTEW4tnAqUk52j4+0=", "dev": true, + "license": "Apache-2.0", "peer": true, "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha1-8qCfYgEjkLK/8/xvskjd7IwJoJA=", "dev": true, + "license": "Apache-2.0", "peer": true, "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha1-r1smkaIrRL6EewyoFkHF+2rQFyw=", "dev": true, + "license": "Apache-2.0", "peer": true, "engines": { "node": ">=12.22" @@ -1237,10 +1241,11 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha1-wrnS43TuYsWG062+qHGZsdenpro=", "dev": true, + "license": "Apache-2.0", "peer": true, "engines": { "node": ">=18.18" @@ -2061,8 +2066,8 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha1-WWoXRyM2lNUPatinhp/Lb1bPWEE=", "dev": true, "license": "MIT", "peer": true @@ -2327,9 +2332,9 @@ } }, "node_modules/@typespec/compiler": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.14.0.tgz", - "integrity": "sha512-RRN0LGVDlonG/IbB2b4mvRjdCo6LywwB9/J8lOp6UaH7vtaFnKe5FL+rpxhof4rXx/zI/4OWnQO6c01bTCz4/Q==", + "version": "1.15.0-dev.17", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/compiler/-/compiler-1.15.0-dev.17.tgz", + "integrity": "sha1-hrlL0q3kmaR0fWNddCfI7LA2BCY=", "dev": true, "license": "MIT", "dependencies": { @@ -2341,7 +2346,7 @@ "is-unicode-supported": "^2.1.0", "mustache": "^4.2.0", "picocolors": "^1.1.1", - "prettier": "^3.8.1", + "prettier": "^3.9.5", "semver": "^7.7.4", "tar": "^7.5.13", "temporal-polyfill": "^1.0.1", @@ -2441,8 +2446,8 @@ }, "node_modules/@typespec/events": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.84.0.tgz", - "integrity": "sha512-UroDIu6t6Z+cOLyX8I+GJWhSFmYGrp1L93F7ZVt0Ypmj0ndmC9YYa4cpeEyS5PDDIC8u49WfCIwfGegxt4rPVQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/events/-/events-0.84.0.tgz", + "integrity": "sha1-U6JW0cqeb0+n5fCjTbaW3DlOYPk=", "dev": true, "license": "MIT", "engines": { @@ -2454,8 +2459,8 @@ }, "node_modules/@typespec/http": { "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.14.0.tgz", - "integrity": "sha512-W+heCzu8K63AVcoX8MachVWaRxSAMFWOI1yBTc2Kq8QHaJeDiLL5JbU8VfTZ4tL/6EoGSdKfIT5ZNRW7oVCzhg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/http/-/http-1.14.0.tgz", + "integrity": "sha1-La9yB2Ny8FhnXSBbst9wYQBiOq4=", "dev": true, "license": "MIT", "engines": { @@ -2496,8 +2501,8 @@ }, "node_modules/@typespec/openapi": { "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.14.0.tgz", - "integrity": "sha512-KL7kImPhCXRmxpHVt1k7TWaa4bb3NbSeUx2rxyxeq7lYZFllI6/NYRCTOI/5JOrbElWmmSxrajU9K9IAKI6PkQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/openapi/-/openapi-1.14.0.tgz", + "integrity": "sha1-uwjWOV3VwWP59UXlR2xVUuzyCGc=", "dev": true, "license": "MIT", "engines": { @@ -2510,8 +2515,8 @@ }, "node_modules/@typespec/rest": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.84.0.tgz", - "integrity": "sha512-9s5dDfRoHRPdtbVvkBasUx/RnMvwWMTuXRieSQDEji4gWGgxVu4Zt4MiEEKSfQrkMr3Aw0QjRCSxBxjMCHIOmA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/rest/-/rest-0.84.0.tgz", + "integrity": "sha1-kMLB39G8geZbiA3EEdQBaqteuuA=", "dev": true, "license": "MIT", "engines": { @@ -2684,8 +2689,8 @@ }, "node_modules/@typespec/sse": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.84.0.tgz", - "integrity": "sha512-9joNgVisRCWDFfV1d79iTAuR1W/6r+AKJrKUfcjsaTrq5A8OWW3v5TTsfxbHAZArn7n2WxQkqhNGgNyc8LjEng==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/sse/-/sse-0.84.0.tgz", + "integrity": "sha1-dkP/3P+tvI4Q2SV3oRz/F2JMVXo=", "dev": true, "license": "MIT", "engines": { @@ -2700,8 +2705,8 @@ }, "node_modules/@typespec/streams": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.84.0.tgz", - "integrity": "sha512-SDneR8+zY+ueOpzg9yJtttfDe/ikB99JgddZSXKPwiDPlAIEeEvI8auipcYfB58EEOB21h8Oq0tEm8HqiAAWdQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/streams/-/streams-0.84.0.tgz", + "integrity": "sha1-Bm3D76chBKlcou2jj913xFfTBI4=", "dev": true, "license": "MIT", "engines": { @@ -2728,8 +2733,8 @@ }, "node_modules/@typespec/versioning": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.84.0.tgz", - "integrity": "sha512-ZoDasTDj4z0mgFK+0cJL2+7DduCaTjvICHL2nQ/RBWc7nLgObaIYCjvXLno8WneDXnpxCAr7larN4/nlHEv9fg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/versioning/-/versioning-0.84.0.tgz", + "integrity": "sha1-YS06C7uMMWXKp7vwuy8qV8kjr38=", "dev": true, "license": "MIT", "engines": { @@ -2741,8 +2746,8 @@ }, "node_modules/@typespec/xml": { "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.84.0.tgz", - "integrity": "sha512-3x0spgIrr4u3azkYaOxrlumtjoqPiUnJ/G5RwGBmUCAeE5F413MHf/AeIkmZ2ULT1gY3myabfZp8bOijTbMk7A==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/@typespec/xml/-/xml-0.84.0.tgz", + "integrity": "sha1-aP99jZ3+wHS7f9mMJbEUT4Py7+g=", "dev": true, "license": "MIT", "engines": { @@ -2880,9 +2885,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha1-T68BstbTJr/u2XrqH1IiC19MGUA=", "dev": true, "license": "MIT", "peer": true, @@ -2895,8 +2900,8 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=", "dev": true, "license": "MIT", "peer": true, @@ -3218,8 +3223,8 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", "dev": true, "license": "MIT", "peer": true, @@ -3237,6 +3242,24 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/change-case": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", @@ -3344,8 +3367,8 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "license": "MIT", "peer": true @@ -3450,9 +3473,10 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=", "dev": true, + "license": "MIT", "peer": true }, "node_modules/default-browser": { @@ -3687,9 +3711,10 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=10" @@ -3699,9 +3724,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha1-Kk48iw91MZbvrpQ8j/qocw/Go/o=", "dev": true, "license": "MIT", "peer": true, @@ -3711,8 +3736,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3761,8 +3786,8 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha1-iOZGogf61hQ2/6OetQUUcgBlXII=", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -3791,9 +3816,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha1-B+mCx0YmFnqnoklcU4F4ktcTlJI=", "dev": true, "license": "MIT", "peer": true, @@ -3809,9 +3834,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha1-POdNiYhRNr4VNTQfjD1EJcKaXKs=", "dev": true, "license": "MIT", "peer": true, @@ -3820,35 +3845,18 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true, "license": "MIT", "peer": true }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha1-WAyI+NVEXyvWqo88re+g3nn71p4=", "dev": true, "license": "ISC", "peer": true, @@ -3861,8 +3869,8 @@ }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/espree/-/espree-10.4.0.tgz", + "integrity": "sha1-1U9JSdRikAWh+haNk3w/8ffiqDc=", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -3879,10 +3887,11 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha1-CNBI8mHw3e21uulfRoCUY9nJSW0=", "dev": true, + "license": "BSD-3-Clause", "peer": true, "dependencies": { "estraverse": "^5.1.0" @@ -3893,8 +3902,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -3907,9 +3916,10 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha1-LupSkHAvJquP5TcDcP+GyWXSESM=", "dev": true, + "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=4.0" @@ -3927,9 +3937,10 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", "dev": true, + "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=0.10.0" @@ -4017,17 +4028,18 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", "dev": true, "license": "MIT", "peer": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true, + "license": "MIT", "peer": true }, "node_modules/fast-string-truncated-width": { @@ -4117,9 +4129,10 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha1-d4e93PETG/+5JjbGlFe7wO3W2B8=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "flat-cache": "^4.0.0" @@ -4181,9 +4194,10 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha1-Ds45/LFO4BL0sEEL0z3ZwfAREnw=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "flatted": "^3.2.9", @@ -4194,9 +4208,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha1-ruyipQYwPwzuYcWebJ8qiNLyn8Y=", "dev": true, "license": "ISC", "peer": true @@ -4352,9 +4366,10 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha1-bSN9mQg5UMeSkPJMdkKj3poo+eM=", "dev": true, + "license": "ISC", "peer": true, "dependencies": { "is-glob": "^4.0.3" @@ -4404,8 +4419,8 @@ }, "node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/globals/-/globals-14.0.0.tgz", + "integrity": "sha1-iY10E8Kbq89rr+Vvyt3thYrack4=", "dev": true, "license": "MIT", "peer": true, @@ -4538,8 +4553,8 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha1-PNQOcp82Q/2HywTlC/DrcivFlvU=", "dev": true, "license": "MIT", "peer": true, @@ -4549,8 +4564,8 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha1-nOy1ZQPAraHydB271lRuSxO1fM8=", "dev": true, "license": "MIT", "peer": true, @@ -4567,8 +4582,8 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true, "license": "MIT", "peer": true, @@ -4611,9 +4626,10 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" @@ -4630,9 +4646,10 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha1-ZPYeQsu7LuwgcanawLKLoeZdUIQ=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "is-extglob": "^2.1.1" @@ -4781,9 +4798,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha1-0ZAFcqf3zwtfVAyDZz5gutNDZZI=", "funding": [ { "type": "github", @@ -4804,9 +4821,10 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha1-kziAKjDTtmBfvgYT4JQAjKjAWhM=", "dev": true, + "license": "MIT", "peer": true }, "node_modules/json-schema-traverse": { @@ -4818,9 +4836,10 @@ }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true, + "license": "MIT", "peer": true }, "node_modules/jsonwebtoken": { @@ -4871,9 +4890,10 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha1-qHmpnilFL5QkOfKkBeOvizHU3pM=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "json-buffer": "3.0.1" @@ -4881,9 +4901,10 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/levn/-/levn-0.4.1.tgz", + "integrity": "sha1-rkViwAdHO5MqYgDUAyaN0v/8at4=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "prelude-ls": "^1.2.1", @@ -5213,9 +5234,10 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lodash.once": { @@ -5627,9 +5649,10 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha1-fqHBpdkddk+yghOciP4R4YKjpzQ=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "deep-is": "^0.1.3", @@ -5681,8 +5704,8 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", "dev": true, "license": "MIT", "peer": true, @@ -5831,9 +5854,10 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha1-3rxkidem5rDnYRiIzsiAM30xY5Y=", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">= 0.8.0" @@ -5871,8 +5895,8 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha1-AnQi4vrsCyXhVJw+G9gwm5EztuU=", "dev": true, "license": "MIT", "peer": true, @@ -5974,8 +5998,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", "dev": true, "license": "MIT", "peer": true, @@ -6454,8 +6478,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY=", "dev": true, "license": "MIT", "peer": true, @@ -6746,9 +6770,10 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha1-B7ggO/pwVsBlcFDjzNLDdzC6uPE=", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "prelude-ls": "^1.2.1" @@ -6854,8 +6879,8 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -7160,9 +7185,10 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "resolved": "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha1-0sRcbdT7zmIaZvE2y+Mor9BBCzQ=", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index f7910112349..6f9e18ca982 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -104,13 +104,13 @@ "tsx": "^4.21.0" }, "devDependencies": { - "@azure-tools/typespec-autorest": "~0.70.0", + "@azure-tools/typespec-autorest": "0.71.0-dev.4", "@azure-tools/typespec-azure-core": "~0.70.0", "@azure-tools/typespec-azure-resource-manager": "~0.70.0", - "@azure-tools/typespec-azure-rulesets": "~0.70.0", - "@azure-tools/typespec-client-generator-core": "~0.70.0", + "@azure-tools/typespec-azure-rulesets": "0.71.0-dev.5", + "@azure-tools/typespec-client-generator-core": "0.71.0-dev.11", "@azure-tools/azure-http-specs": "0.1.0-alpha.43", - "@typespec/compiler": "^1.14.0", + "@typespec/compiler": "1.15.0-dev.17", "@typespec/http": "^1.14.0", "@typespec/openapi": "^1.14.0", "@typespec/rest": "~0.84.0", @@ -132,5 +132,8 @@ "typescript-eslint": "^8.49.0", "vitest": "^4.0.15", "prettier": "^3.9.5" + }, + "overrides": { + "@typespec/compiler": "$@typespec/compiler" } }