ai-gateway-proxy 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3CallOptions, LanguageModelV3Content, LanguageModelV3FinishReason, LanguageModelV3Usage, SharedV3Warning, SharedV3ProviderMetadata, LanguageModelV3StreamPart } from '@ai-sdk/provider';
1
+ import { LanguageModelV4CallOptions, LanguageModelV4Content, LanguageModelV4FinishReason, LanguageModelV4Usage, SharedV4Warning, SharedV4ProviderMetadata, LanguageModelV4StreamPart } from '@ai-sdk/provider';
2
2
 
3
3
  type GatewayAuthToken = {
4
4
  token: string;
@@ -7,6 +7,7 @@ type GatewayAuthToken = {
7
7
  /**
8
8
  * Get authentication token for the AI Gateway.
9
9
  * Tries API key first (AI_GATEWAY_API_KEY env var), then falls back to Vercel OIDC.
10
+ * Returns `null` when neither is available, after logging why OIDC failed, and the proxy then answers 500.
10
11
  */
11
12
  declare function getGatewayAuthToken(): Promise<GatewayAuthToken | null>;
12
13
 
@@ -15,11 +16,37 @@ declare function getGatewayAuthToken(): Promise<GatewayAuthToken | null>;
15
16
  * For streaming requests, this is aggregated from the stream events.
16
17
  */
17
18
  type GatewayResponse = {
18
- content: LanguageModelV3Content[];
19
- finishReason: LanguageModelV3FinishReason;
20
- usage: LanguageModelV3Usage;
21
- warnings: SharedV3Warning[];
22
- providerMetadata?: SharedV3ProviderMetadata;
19
+ content: LanguageModelV4Content[];
20
+ finishReason: LanguageModelV4FinishReason;
21
+ /** `undefined` when a stream ended without a `finish` event, so the usage is unknown. */
22
+ usage: LanguageModelV4Usage | undefined;
23
+ warnings: SharedV4Warning[];
24
+ providerMetadata?: SharedV4ProviderMetadata;
25
+ /**
26
+ * The provider's response id, model and start time, from a stream's `response-metadata` parts.
27
+ * `timestamp` is the string sent over the wire, not a `Date`. Neither the proxy nor the client reads it from a result.
28
+ */
29
+ response?: GatewayResponseMetadata;
30
+ };
31
+ /**
32
+ * What a stream's `response-metadata` parts say about the response, the latest value of each field winning.
33
+ */
34
+ type GatewayResponseMetadata = {
35
+ id?: string;
36
+ modelId?: string;
37
+ timestamp?: string;
38
+ };
39
+ /**
40
+ * Why a stream had no `finish` event; the cancel reason is in `cancelReason`.
41
+ */
42
+ type IncompleteStream = {
43
+ reason: "ended";
44
+ } | {
45
+ reason: "error";
46
+ error: unknown;
47
+ } | {
48
+ reason: "cancelled";
49
+ cancelReason?: unknown;
23
50
  };
24
51
  /**
25
52
  * Error response from the AI Gateway.
@@ -29,52 +56,204 @@ type GatewayError = {
29
56
  message: string;
30
57
  type: string;
31
58
  param?: unknown;
59
+ code?: string | number | null;
32
60
  };
61
+ /** The gateway's id for the failed generation, which `@ai-sdk/gateway` appends to the error message. */
62
+ generationId?: string | null;
63
+ };
64
+ /**
65
+ * The kind of a call that is not a language model call, named after the `@ai-sdk/gateway` model it comes from.
66
+ */
67
+ type ModelCallKind = "embedding" | "image" | "video" | "speech" | "transcription" | "reranking" | "decision" | "batch";
68
+ /**
69
+ * What `beforeRawRequest` receives.
70
+ */
71
+ type RawRequestContext = {
72
+ kind: ModelCallKind;
73
+ /** The upstream path relative to `baseUrl`, such as `embedding-model` or `batch/start`. */
74
+ path: string;
75
+ /** The `ai-model-id` header the client sent, which names the model; a batch names it per request too. */
76
+ modelId: string | undefined;
77
+ /** The parsed JSON body, unchecked. */
78
+ request: unknown;
79
+ };
80
+ /**
81
+ * What `afterRawResponse` receives.
82
+ */
83
+ type RawResponseContext = RawRequestContext & {
84
+ /**
85
+ * The response the proxy would send: the upstream status, `content-type` and allowlisted
86
+ * headers, with the upstream body on success and a `GatewayError` JSON body on failure.
87
+ * Its body can be read once, so read `response.clone()` to leave it for the client.
88
+ */
89
+ response: Response;
33
90
  };
34
91
  /**
35
92
  * Options for creating a gateway proxy.
36
93
  */
37
94
  type CreateGatewayProxyOptions = {
38
- /** Base URL for the AI Gateway. Defaults to Vercel's gateway. */
95
+ /**
96
+ * Base URL for the AI Gateway. A trailing slash is accepted.
97
+ * Request paths are resolved under it and cannot leave it; one that would returns 400 Invalid path.
98
+ * @default "https://ai-gateway.vercel.sh/v4/ai"
99
+ */
39
100
  baseUrl?: string;
40
- /** Additional headers to send with requests. */
101
+ /**
102
+ * Additional headers to send with requests.
103
+ * The proxy sets `authorization`, `ai-gateway-auth-method` and `ai-gateway-protocol-version` after these, so they win.
104
+ * On requests with a body it also sets `content-type`, drops entries naming the model call
105
+ * (`ai-language-model-*`, `ai-model-id`, `ai-*-model-specification-version`) in favour of the
106
+ * incoming request's, and lets the incoming `ai-o11y-*`, `idempotency-key` and `accept` override these.
107
+ */
41
108
  headers?: Record<string, string>;
109
+ /**
110
+ * The name of the catch-all parameter in the route.
111
+ * @default "segments"
112
+ * @example
113
+ * // For Next.js route `app/api/ai/[...path]/route.ts`
114
+ * createGatewayProxy({ segmentsParam: "path" })
115
+ */
116
+ segmentsParam?: string;
117
+ /**
118
+ * Custom function to extract the path from the request.
119
+ * Use this for frameworks without catch-all route support (e.g., Hono, Express).
120
+ *
121
+ * When provided, `segmentsParam` is ignored.
122
+ *
123
+ * Return a path relative to `baseUrl`, without a leading `/`.
124
+ * A path that starts with `/`, contains `://`, or has a segment that fails to decode or decodes
125
+ * to `.` or `..` or contains `/`, `\` or a NUL byte returns 400 Invalid path.
126
+ *
127
+ * @example
128
+ * // Hono with a base path
129
+ * createGatewayProxy({
130
+ * extractPath: (request) => {
131
+ * const url = new URL(request.url);
132
+ * return url.pathname.replace(/^\/api\/ai\//, "");
133
+ * }
134
+ * })
135
+ */
136
+ extractPath?: (request: Request) => string | Promise<string>;
42
137
  /**
43
138
  * Called before the request is sent to the gateway.
44
- * Allows modification of the request body.
139
+ * Allows modification of the request body. Returning `undefined` or `null` sends the
140
+ * parsed body unchanged and logs an error. Any other result that is not an object, such as
141
+ * a string or an array, is not sent: the client gets a 500 and the gateway is not called.
142
+ *
143
+ * Not called for GET, HEAD or other requests without a body, which are forwarded as is.
144
+ * A body that is JSON but not an object gets a 400 before this or any other language model
145
+ * hook runs.
146
+ *
147
+ * Not called when the path relative to `baseUrl` is exactly one of the paths `@ai-sdk/gateway`
148
+ * uses for other calls: `embedding-model`, `image-model`, `video-model`, `video-model/start`,
149
+ * `video-model/status`, `speech-model`, `transcription-model`, `reranking-model`,
150
+ * `decision-model`, `batch/start`, `batch/status`, `batch/results` and `batch/cancel`.
151
+ * Every other path runs it, whatever headers the request sends.
45
152
  */
46
153
  beforeRequest?: (context: {
47
- request: LanguageModelV3CallOptions;
48
- }) => LanguageModelV3CallOptions | Promise<LanguageModelV3CallOptions>;
154
+ request: LanguageModelV4CallOptions;
155
+ /** The `ai-language-model-id` header the client sent, which names the model; the body does not. */
156
+ modelId?: string;
157
+ }) => LanguageModelV4CallOptions | Promise<LanguageModelV4CallOptions>;
49
158
  /**
50
159
  * Called after a successful response is received from the gateway.
51
160
  * Allows modification of the response body.
52
161
  *
162
+ * For non-streaming responses, the returned object replaces the JSON body sent to the client.
163
+ * A result with a missing or wrong-typed field is not sent: the client gets a 500 naming the
164
+ * field, and on a stream the original finish event is forwarded. `null` stands for an absent
165
+ * field, and `warnings` may be left out. An absent `usage` keeps the gateway's, on both paths.
166
+ *
53
167
  * For streaming responses, this is called when the stream completes with
54
168
  * the aggregated response. The content has already been streamed, but you
55
169
  * can modify the finish event metadata (usage, finishReason, providerMetadata).
170
+ * If the hook throws while handling a stream, the error is logged and the
171
+ * original finish event is forwarded unchanged. It runs once per stream: a
172
+ * later finish event gets the same rewrite, or none if the hook failed.
173
+ *
174
+ * A stream that ends, fails or is cancelled before its finish event calls the hook once
175
+ * with the content received so far, `incomplete` set, `response.usage` undefined and
176
+ * `response.finishReason` `error` when the read failed or the stream sent an `error` part,
177
+ * `other` otherwise. Nothing can be rewritten then, so the
178
+ * returned value is ignored and a throw is logged.
179
+ *
180
+ * Not called for an event stream or JSON Lines body answering a request not marked
181
+ * `ai-language-model-streaming: true`, such as a video model call or batch results, which is
182
+ * forwarded as is.
183
+ *
184
+ * Not called for a 204 or 205 response, which is forwarded without a body.
185
+ *
186
+ * Not called for GET, HEAD or other requests without a body, which are forwarded as is.
187
+ *
188
+ * Not called when the path relative to `baseUrl` is exactly one of the paths `@ai-sdk/gateway`
189
+ * uses for other calls: `embedding-model`, `image-model`, `video-model`, `video-model/start`,
190
+ * `video-model/status`, `speech-model`, `transcription-model`, `reranking-model`,
191
+ * `decision-model`, `batch/start`, `batch/status`, `batch/results` and `batch/cancel`.
192
+ * Every other path runs it, whatever headers the request sends.
56
193
  */
57
194
  afterResponse?: (context: {
58
- request: LanguageModelV3CallOptions;
195
+ request: LanguageModelV4CallOptions;
196
+ /** The `ai-language-model-id` header the client sent, which names the model; the body does not. */
197
+ modelId?: string;
59
198
  response: GatewayResponse;
199
+ /** Set only for a stream that did not deliver its `finish` event. */
200
+ incomplete?: IncompleteStream;
60
201
  }) => GatewayResponse | Promise<GatewayResponse>;
61
202
  /**
62
203
  * Called when the gateway returns an error response.
63
204
  * Allows custom error handling or modification.
205
+ *
206
+ * A returned error body is sent as JSON with the upstream status.
207
+ * A returned `Response` is sent as is.
208
+ * Returning `undefined`, `null` or a body without a string `error.message` (or with a
209
+ * non-string `error.type`, an `error.code` that is neither a string nor a number, or a
210
+ * non-string `generationId`) logs an error and sends the upstream error body instead, and so
211
+ * does a hook that throws.
212
+ *
213
+ * Not called for GET, HEAD or other requests without a body, which are forwarded as is.
214
+ *
215
+ * Not called when the path relative to `baseUrl` is exactly one of the paths `@ai-sdk/gateway`
216
+ * uses for other calls: `embedding-model`, `image-model`, `video-model`, `video-model/start`,
217
+ * `video-model/status`, `speech-model`, `transcription-model`, `reranking-model`,
218
+ * `decision-model`, `batch/start`, `batch/status`, `batch/results` and `batch/cancel`.
219
+ * Every other path runs it, whatever headers the request sends.
64
220
  */
65
221
  onError?: (context: {
66
- request: LanguageModelV3CallOptions;
222
+ request: LanguageModelV4CallOptions;
223
+ /** The `ai-language-model-id` header the client sent, which names the model; the body does not. */
224
+ modelId?: string;
67
225
  error: GatewayError;
68
226
  status: number;
69
227
  }) => GatewayError | Response | Promise<GatewayError | Response>;
228
+ /**
229
+ * Called before a call that is not a language model call is sent to the gateway: a request
230
+ * with a body whose path relative to `baseUrl` is exactly `embedding-model`, `image-model`,
231
+ * `video-model`, `video-model/start`, `speech-model`, `transcription-model`,
232
+ * `reranking-model`, `decision-model` or `batch/start`. Job reads (`video-model/status` and
233
+ * `batch/status`, `batch/results`, `batch/cancel`) run no hook.
234
+ *
235
+ * A returned value replaces the body sent upstream; `undefined` or `null` sends the body as is.
236
+ */
237
+ beforeRawRequest?: (context: RawRequestContext) => unknown;
238
+ /**
239
+ * Called with the response to a call `beforeRawRequest` would run for, on success and on an
240
+ * upstream error status, but not on a redirect, which is answered with a 502, nor when the
241
+ * upstream request fails.
242
+ *
243
+ * A returned `Response` is sent instead. Returning nothing sends `response`, which must then
244
+ * be unread; a hook that read it without returning a `Response`, or returned anything else,
245
+ * is a 500. When the hook returns another `Response`, throws or returns anything else, an
246
+ * unread `response.body` is cancelled unless the returned `Response` is built on it.
247
+ */
248
+ afterRawResponse?: (context: RawResponseContext) => Response | undefined | Promise<Response | undefined>;
70
249
  };
71
250
  /**
72
- * Next.js route handler function signature for the gateway proxy.
251
+ * Route handler function signature for the gateway proxy.
252
+ * Compatible with Next.js App Router and other frameworks.
253
+ * Takes a Fetch API `Request`, not a Node or Express request object.
73
254
  */
74
- type CreateGatewayProxyFn = (request: Request, context: {
75
- params: Promise<{
76
- segments?: string[];
77
- }>;
255
+ type CreateGatewayProxyFn = (request: Request, context?: {
256
+ params: Promise<Record<string, string | string[] | undefined>>;
78
257
  }) => Promise<Response>;
79
258
  /**
80
259
  * Result of createGatewayProxy - a handler function with HTTP method exports.
@@ -90,17 +269,17 @@ type CreateGatewayProxyResult = CreateGatewayProxyFn & {
90
269
  /**
91
270
  * Creates a proxy handler for the Vercel AI Gateway.
92
271
  *
93
- * Uses AI SDK V3 Language Model.
272
+ * Uses AI SDK V4 Language Model.
94
273
  * @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts
95
274
  */
96
- declare const createGatewayProxy: ({ baseUrl, headers, beforeRequest, afterResponse, onError, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
275
+ declare const createGatewayProxy: ({ baseUrl, headers, segmentsParam, extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
97
276
 
98
277
  /**
99
278
  * Aggregator for collecting stream content.
100
279
  * Tracks text deltas, reasoning, tool calls, and other content to build the final GatewayResponse.
101
280
  *
102
- * Uses AI SDK V3 protocol types.
103
- * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v3/language-model-v3-stream-part.ts
281
+ * Uses AI SDK V4 protocol types.
282
+ * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v4/language-model-v4-stream-part.ts
104
283
  */
105
284
  declare class StreamContentAggregator {
106
285
  private content;
@@ -110,15 +289,20 @@ declare class StreamContentAggregator {
110
289
  private finishReason;
111
290
  private usage;
112
291
  private providerMetadata;
292
+ private response;
113
293
  /**
114
294
  * Process a stream part and update aggregated state.
115
- * Uses LanguageModelV3StreamPart type from @ai-sdk/provider.
295
+ * Uses LanguageModelV4StreamPart type from @ai-sdk/provider.
116
296
  *
117
- * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v3/language-model-v3-stream-part.ts
297
+ * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v4/language-model-v4-stream-part.ts
118
298
  */
119
- process(part: LanguageModelV3StreamPart): void;
299
+ process(part: LanguageModelV4StreamPart): void;
300
+ private recordResponseMetadata;
301
+ private openText;
302
+ private openReasoning;
120
303
  /**
121
- * Get the aggregated response.
304
+ * Get the aggregated response. Before a `finish` part, `usage` is undefined and
305
+ * `finishReason` is `error` after an `error` part and `other` otherwise.
122
306
  */
123
307
  getResponse(): GatewayResponse;
124
308
  }
@@ -126,16 +310,20 @@ declare class StreamContentAggregator {
126
310
  * Creates a transform stream that:
127
311
  * 1. Passes through all SSE events immediately
128
312
  * 2. Aggregates content using StreamContentAggregator
129
- * 3. Calls afterResponse when finish event is received
130
- * 4. Modifies the finish event based on afterResponse result
313
+ * 3. Calls afterResponse once: on the first finish event, or with partial content if the
314
+ * stream ends, fails or is cancelled first
315
+ * 4. Modifies every finish event based on that afterResponse result
316
+ *
317
+ * Pass the client request's `signal` when it is also the upstream fetch's signal: a read that
318
+ * fails because it aborted is then reported as a cancel rather than an upstream failure.
131
319
  *
132
320
  * Uses EventSourceParserStream from @ai-sdk/provider-utils for SSE parsing.
133
321
  * EventSourceParserStream is re-exported from the eventsource-parser package.
134
322
  * @see https://github.com/vercel/ai/blob/main/packages/provider-utils/src/parse-json-event-stream.ts
135
323
  * @see https://github.com/rexxars/eventsource-parser
136
324
  */
137
- declare function createStreamTransformer(body: ReadableStream<Uint8Array>, requestBody: LanguageModelV3CallOptions, afterResponse: NonNullable<CreateGatewayProxyOptions["afterResponse"]>): ReadableStream<Uint8Array>;
325
+ declare function createStreamTransformer(body: ReadableStream<Uint8Array>, requestBody: LanguageModelV4CallOptions, afterResponse: NonNullable<CreateGatewayProxyOptions["afterResponse"]>, signal?: AbortSignal): ReadableStream<Uint8Array>;
138
326
 
139
327
  export { StreamContentAggregator, createGatewayProxy, createStreamTransformer, getGatewayAuthToken };
140
- export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayAuthToken, GatewayError, GatewayResponse };
328
+ export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayAuthToken, GatewayError, GatewayResponse, GatewayResponseMetadata, IncompleteStream, ModelCallKind, RawRequestContext, RawResponseContext };
141
329
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"sourcesContent":["import { getVercelOidcToken } from \"@vercel/oidc\";\n\nexport type GatewayAuthToken = {\n\ttoken: string;\n\tauthMethod: \"api-key\" | \"oidc\";\n};\n\n/**\n * Get authentication token for the AI Gateway.\n * Tries API key first (AI_GATEWAY_API_KEY env var), then falls back to Vercel OIDC.\n */\nexport async function getGatewayAuthToken(): Promise<GatewayAuthToken | null> {\n\tconst apiKey = process.env.AI_GATEWAY_API_KEY;\n\tif (apiKey) {\n\t\treturn { token: apiKey, authMethod: \"api-key\" };\n\t}\n\n\ttry {\n\t\tconst oidcToken = await getVercelOidcToken();\n\t\treturn { token: oidcToken, authMethod: \"oidc\" };\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import type {\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3Usage,\n\tSharedV3ProviderMetadata,\n\tSharedV3Warning,\n} from \"@ai-sdk/provider\";\n\n/**\n * The response body from the AI Gateway for non-streaming requests.\n * For streaming requests, this is aggregated from the stream events.\n */\nexport type GatewayResponse = {\n\tcontent: LanguageModelV3Content[];\n\tfinishReason: LanguageModelV3FinishReason;\n\tusage: LanguageModelV3Usage;\n\twarnings: SharedV3Warning[];\n\tproviderMetadata?: SharedV3ProviderMetadata;\n};\n\n/**\n * Error response from the AI Gateway.\n */\nexport type GatewayError = {\n\terror: {\n\t\tmessage: string;\n\t\ttype: string;\n\t\tparam?: unknown;\n\t};\n};\n\n/**\n * Options for creating a gateway proxy.\n */\nexport type CreateGatewayProxyOptions = {\n\t/** Base URL for the AI Gateway. Defaults to Vercel's gateway. */\n\tbaseUrl?: string;\n\t/** Additional headers to send with requests. */\n\theaders?: Record<string, string>;\n\t/**\n\t * Called before the request is sent to the gateway.\n\t * Allows modification of the request body.\n\t */\n\tbeforeRequest?: (context: {\n\t\trequest: LanguageModelV3CallOptions;\n\t}) => LanguageModelV3CallOptions | Promise<LanguageModelV3CallOptions>;\n\t/**\n\t * Called after a successful response is received from the gateway.\n\t * Allows modification of the response body.\n\t *\n\t * For streaming responses, this is called when the stream completes with\n\t * the aggregated response. The content has already been streamed, but you\n\t * can modify the finish event metadata (usage, finishReason, providerMetadata).\n\t */\n\tafterResponse?: (context: {\n\t\trequest: LanguageModelV3CallOptions;\n\t\tresponse: GatewayResponse;\n\t}) => GatewayResponse | Promise<GatewayResponse>;\n\t/**\n\t * Called when the gateway returns an error response.\n\t * Allows custom error handling or modification.\n\t */\n\tonError?: (context: {\n\t\trequest: LanguageModelV3CallOptions;\n\t\terror: GatewayError;\n\t\tstatus: number;\n\t}) => GatewayError | Response | Promise<GatewayError | Response>;\n};\n\n/**\n * Next.js route handler function signature for the gateway proxy.\n */\nexport type CreateGatewayProxyFn = (\n\trequest: Request,\n\tcontext: { params: Promise<{ segments?: string[] }> },\n) => Promise<Response>;\n\n/**\n * Result of createGatewayProxy - a handler function with HTTP method exports.\n */\nexport type CreateGatewayProxyResult = CreateGatewayProxyFn & {\n\tGET: CreateGatewayProxyFn;\n\tPOST: CreateGatewayProxyFn;\n\tPUT: CreateGatewayProxyFn;\n\tDELETE: CreateGatewayProxyFn;\n\tPATCH: CreateGatewayProxyFn;\n};\n","import type { LanguageModelV3CallOptions } from \"@ai-sdk/provider\";\nimport { getGatewayAuthToken } from \"./auth\";\nimport { createStreamTransformer } from \"./stream\";\nimport type {\n\tCreateGatewayProxyOptions,\n\tCreateGatewayProxyResult,\n\tGatewayError,\n\tGatewayResponse,\n} from \"./types\";\n\n/**\n * Creates a proxy handler for the Vercel AI Gateway.\n *\n * Uses AI SDK V3 Language Model.\n * @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts\n */\nexport const createGatewayProxy = ({\n\tbaseUrl = \"https://ai-gateway.vercel.sh/v1/ai\",\n\theaders,\n\tbeforeRequest,\n\tafterResponse,\n\tonError,\n}: CreateGatewayProxyOptions = {}): CreateGatewayProxyResult => {\n\tconst handler = async (\n\t\trequest: Request,\n\t\t{ params }: { params: Promise<{ segments?: string[] }> },\n\t) => {\n\t\tconst auth = await getGatewayAuthToken();\n\t\tif (!auth) {\n\t\t\treturn new Response(\"AI Gateway authentication not configured\", {\n\t\t\t\tstatus: 500,\n\t\t\t});\n\t\t}\n\n\t\tconst { segments } = await params;\n\t\tconst path = segments?.join(\"/\") ?? \"\";\n\t\tconst searchParams = new URL(request.url).searchParams;\n\t\tconst upstream = `${baseUrl}/${path}${searchParams.toString() ? `?${searchParams.toString()}` : \"\"}`;\n\n\t\tlet requestBody: LanguageModelV3CallOptions;\n\t\ttry {\n\t\t\trequestBody = await request.clone().json();\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid JSON body\", { status: 400 });\n\t\t}\n\n\t\t// Apply beforeRequest hook\n\t\tif (beforeRequest) {\n\t\t\trequestBody = await beforeRequest({ request: requestBody });\n\t\t}\n\n\t\tconst isStreaming = request.headers.get(\"ai-language-model-streaming\") === \"true\";\n\n\t\ttry {\n\t\t\tconst res = await fetch(upstream, {\n\t\t\t\tmethod: request.method,\n\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\tduplex: \"half\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t...headers,\n\t\t\t\t\tauthorization: `Bearer ${auth.token}`,\n\t\t\t\t\t\"ai-gateway-auth-method\": auth.authMethod,\n\t\t\t\t\t\"ai-gateway-protocol-version\": \"0.0.1\",\n\t\t\t\t\t\"ai-language-model-id\": request.headers.get(\"ai-language-model-id\") ?? \"\",\n\t\t\t\t\t\"ai-language-model-streaming\": isStreaming ? \"true\" : \"false\",\n\t\t\t\t},\n\t\t\t} as RequestInit);\n\n\t\t\t// Handle error responses\n\t\t\tif (!res.ok) {\n\t\t\t\tlet errorBody: GatewayError;\n\t\t\t\ttry {\n\t\t\t\t\terrorBody = await res.json();\n\t\t\t\t} catch {\n\t\t\t\t\terrorBody = {\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tmessage: res.statusText || \"Unknown error\",\n\t\t\t\t\t\t\ttype: \"gateway_error\",\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (onError) {\n\t\t\t\t\tconst result = await onError({\n\t\t\t\t\t\trequest: requestBody,\n\t\t\t\t\t\terror: errorBody,\n\t\t\t\t\t\tstatus: res.status,\n\t\t\t\t\t});\n\n\t\t\t\t\t// If onError returns a Response, use it directly\n\t\t\t\t\tif (result instanceof Response) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Otherwise, return the modified error\n\t\t\t\t\treturn new Response(JSON.stringify(result), {\n\t\t\t\t\t\tstatus: res.status,\n\t\t\t\t\t\tstatusText: res.statusText,\n\t\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn new Response(JSON.stringify(errorBody), {\n\t\t\t\t\tstatus: res.status,\n\t\t\t\t\tstatusText: res.statusText,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For streaming responses\n\t\t\tif (isStreaming) {\n\t\t\t\tif (!res.body) {\n\t\t\t\t\treturn new Response(\"No response body\", { status: 500 });\n\t\t\t\t}\n\n\t\t\t\t// If no afterResponse hook, pass through directly\n\t\t\t\tif (!afterResponse) {\n\t\t\t\t\treturn new Response(res.body, {\n\t\t\t\t\t\tstatus: res.status,\n\t\t\t\t\t\tstatusText: res.statusText,\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"content-type\": res.headers.get(\"content-type\") || \"text/event-stream\",\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Transform the stream to call afterResponse on finish\n\t\t\t\tconst transformedStream = createStreamTransformer(res.body, requestBody, afterResponse);\n\n\t\t\t\treturn new Response(transformedStream, {\n\t\t\t\t\tstatus: res.status,\n\t\t\t\t\tstatusText: res.statusText,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": res.headers.get(\"content-type\") || \"text/event-stream\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For non-streaming responses, parse and optionally transform\n\t\t\tlet responseBody: GatewayResponse = await res.json();\n\n\t\t\tif (afterResponse) {\n\t\t\t\tresponseBody = await afterResponse({\n\t\t\t\t\trequest: requestBody,\n\t\t\t\t\tresponse: responseBody,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn new Response(JSON.stringify(responseBody), {\n\t\t\t\tstatus: res.status,\n\t\t\t\tstatusText: res.statusText,\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t});\n\t\t} catch {\n\t\t\treturn new Response(\"Error proxying request to AI Gateway\", {\n\t\t\t\tstatus: 500,\n\t\t\t});\n\t\t}\n\t};\n\n\thandler.GET = handler;\n\thandler.POST = handler;\n\thandler.PUT = handler;\n\thandler.DELETE = handler;\n\thandler.PATCH = handler;\n\n\treturn handler;\n};\n","import type {\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3StreamPart,\n\tLanguageModelV3Usage,\n\tSharedV3ProviderMetadata,\n\tSharedV3Warning,\n} from \"@ai-sdk/provider\";\nimport { EventSourceParserStream } from \"@ai-sdk/provider-utils\";\nimport type { CreateGatewayProxyOptions, GatewayResponse } from \"./types\";\n\n/**\n * Aggregator for collecting stream content.\n * Tracks text deltas, reasoning, tool calls, and other content to build the final GatewayResponse.\n *\n * Uses AI SDK V3 protocol types.\n * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v3/language-model-v3-stream-part.ts\n */\nexport class StreamContentAggregator {\n\tprivate content: LanguageModelV3Content[] = [];\n\tprivate warnings: SharedV3Warning[] = [];\n\n\t// Text content being built (keyed by id)\n\tprivate textContent = new Map<string, { type: \"text\"; text: string }>();\n\n\t// Reasoning content being built (keyed by id)\n\tprivate reasoningContent = new Map<string, { type: \"reasoning\"; text: string }>();\n\n\tprivate finishReason: LanguageModelV3FinishReason = {\n\t\tunified: \"stop\",\n\t\traw: undefined,\n\t};\n\n\tprivate usage: LanguageModelV3Usage = {\n\t\tinputTokens: {\n\t\t\ttotal: undefined,\n\t\t\tnoCache: undefined,\n\t\t\tcacheRead: undefined,\n\t\t\tcacheWrite: undefined,\n\t\t},\n\t\toutputTokens: {\n\t\t\ttotal: undefined,\n\t\t\ttext: undefined,\n\t\t\treasoning: undefined,\n\t\t},\n\t};\n\n\tprivate providerMetadata: SharedV3ProviderMetadata | undefined;\n\n\t/**\n\t * Process a stream part and update aggregated state.\n\t * Uses LanguageModelV3StreamPart type from @ai-sdk/provider.\n\t *\n\t * @see https://github.com/vercel/ai/blob/main/packages/provider/src/language-model/v3/language-model-v3-stream-part.ts\n\t */\n\tprocess(part: LanguageModelV3StreamPart): void {\n\t\tswitch (part.type) {\n\t\t\tcase \"stream-start\":\n\t\t\t\tthis.warnings.push(...part.warnings);\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-start\":\n\t\t\t\tthis.textContent.set(part.id, { type: \"text\", text: \"\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-delta\":\n\t\t\t\t{\n\t\t\t\t\tconst text = this.textContent.get(part.id);\n\t\t\t\t\tif (text) {\n\t\t\t\t\t\ttext.text += part.delta;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-end\":\n\t\t\t\t{\n\t\t\t\t\tconst text = this.textContent.get(part.id);\n\t\t\t\t\tif (text) {\n\t\t\t\t\t\tthis.content.push(text);\n\t\t\t\t\t\tthis.textContent.delete(part.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"reasoning-start\":\n\t\t\t\tthis.reasoningContent.set(part.id, { type: \"reasoning\", text: \"\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"reasoning-delta\":\n\t\t\t\t{\n\t\t\t\t\tconst reasoning = this.reasoningContent.get(part.id);\n\t\t\t\t\tif (reasoning) {\n\t\t\t\t\t\treasoning.text += part.delta;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"reasoning-end\":\n\t\t\t\t{\n\t\t\t\t\tconst reasoning = this.reasoningContent.get(part.id);\n\t\t\t\t\tif (reasoning) {\n\t\t\t\t\t\tthis.content.push(reasoning);\n\t\t\t\t\t\tthis.reasoningContent.delete(part.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-input-start\":\n\t\t\tcase \"tool-input-delta\":\n\t\t\tcase \"tool-input-end\":\n\t\t\t\t// Tool input is streamed incrementally but we wait for the complete tool-call event\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-call\":\n\t\t\t\tthis.content.push(part);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-result\":\n\t\t\t\tthis.content.push(part);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-approval-request\":\n\t\t\t\tthis.content.push(part);\n\t\t\t\tbreak;\n\n\t\t\tcase \"file\":\n\t\t\t\tthis.content.push(part);\n\t\t\t\tbreak;\n\n\t\t\tcase \"source\":\n\t\t\t\tthis.content.push(part);\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish\":\n\t\t\t\t// Finalize any remaining content\n\t\t\t\tfor (const [, text] of this.textContent) {\n\t\t\t\t\tthis.content.push(text);\n\t\t\t\t}\n\t\t\t\tthis.textContent.clear();\n\n\t\t\t\tfor (const [, reasoning] of this.reasoningContent) {\n\t\t\t\t\tthis.content.push(reasoning);\n\t\t\t\t}\n\t\t\t\tthis.reasoningContent.clear();\n\n\t\t\t\tthis.finishReason = part.finishReason;\n\t\t\t\tthis.usage = part.usage;\n\t\t\t\tthis.providerMetadata = part.providerMetadata;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Get the aggregated response.\n\t */\n\tgetResponse(): GatewayResponse {\n\t\treturn {\n\t\t\tcontent: this.content,\n\t\t\tfinishReason: this.finishReason,\n\t\t\tusage: this.usage,\n\t\t\twarnings: this.warnings,\n\t\t\tproviderMetadata: this.providerMetadata,\n\t\t};\n\t}\n}\n\n/**\n * Creates a transform stream that:\n * 1. Passes through all SSE events immediately\n * 2. Aggregates content using StreamContentAggregator\n * 3. Calls afterResponse when finish event is received\n * 4. Modifies the finish event based on afterResponse result\n *\n * Uses EventSourceParserStream from @ai-sdk/provider-utils for SSE parsing.\n * EventSourceParserStream is re-exported from the eventsource-parser package.\n * @see https://github.com/vercel/ai/blob/main/packages/provider-utils/src/parse-json-event-stream.ts\n * @see https://github.com/rexxars/eventsource-parser\n */\nexport function createStreamTransformer(\n\tbody: ReadableStream<Uint8Array>,\n\trequestBody: LanguageModelV3CallOptions,\n\tafterResponse: NonNullable<CreateGatewayProxyOptions[\"afterResponse\"]>,\n): ReadableStream<Uint8Array> {\n\tconst encoder = new TextEncoder();\n\tconst aggregator = new StreamContentAggregator();\n\n\t// Use EventSourceParserStream for SSE parsing (re-exported from eventsource-parser via @ai-sdk/provider-utils)\n\tconst eventStream = body\n\t\t.pipeThrough(new TextDecoderStream() as TransformStream<Uint8Array, string>)\n\t\t.pipeThrough(new EventSourceParserStream());\n\n\treturn new ReadableStream({\n\t\tasync start(controller) {\n\t\t\tconst reader = eventStream.getReader();\n\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) break;\n\n\t\t\t\t\tconst { data } = value;\n\n\t\t\t\t\t// Handle DONE marker (end of stream)\n\t\t\t\t\tif (data === \"DONE\" || data === \"[DONE]\") {\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${data}\\n\\n`));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Parse the JSON event data\n\t\t\t\t\tlet parsed: LanguageModelV3StreamPart;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed = JSON.parse(data);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not valid JSON, pass through as-is\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${data}\\n\\n`));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Track content for aggregation\n\t\t\t\t\taggregator.process(parsed);\n\n\t\t\t\t\t// Handle finish event specially - call afterResponse hook\n\t\t\t\t\tif (parsed.type === \"finish\") {\n\t\t\t\t\t\tconst modifiedResponse = await afterResponse({\n\t\t\t\t\t\t\trequest: requestBody,\n\t\t\t\t\t\t\tresponse: aggregator.getResponse(),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Emit modified finish event\n\t\t\t\t\t\tconst modifiedFinish: LanguageModelV3StreamPart = {\n\t\t\t\t\t\t\ttype: \"finish\",\n\t\t\t\t\t\t\tfinishReason: modifiedResponse.finishReason,\n\t\t\t\t\t\t\tusage: modifiedResponse.usage,\n\t\t\t\t\t\t\tproviderMetadata: modifiedResponse.providerMetadata,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${JSON.stringify(modifiedFinish)}\\n\\n`));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Pass through event unchanged\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${data}\\n\\n`));\n\t\t\t\t}\n\n\t\t\t\tcontroller.close();\n\t\t\t} catch (error) {\n\t\t\t\tcontroller.error(error);\n\t\t\t} finally {\n\t\t\t\treader.releaseLock();\n\t\t\t}\n\t\t},\n\t});\n}\n"],"names":[],"mappings":";;AAAO,KAAA,gBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO,iBAAA,mBAAA,IAAA,OAAA,CAAA,gBAAA;;ACPP;AACA;AACA;AACA;AACO,KAAA,eAAA;AACP,aAAA,sBAAA;AACA,kBAAA,2BAAA;AACA,WAAA,oBAAA;AACA,cAAA,eAAA;AACA,uBAAA,wBAAA;AACA;AACA;AACA;AACA;AACO,KAAA,YAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,KAAA,yBAAA;AACP;AACA;AACA;AACA,cAAA,MAAA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAA,0BAAA;AACA,UAAA,0BAAA,GAAA,OAAA,CAAA,0BAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAA,0BAAA;AACA,kBAAA,eAAA;AACA,UAAA,eAAA,GAAA,OAAA,CAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAA,0BAAA;AACA,eAAA,YAAA;AACA;AACA,UAAA,YAAA,GAAA,QAAA,GAAA,OAAA,CAAA,YAAA,GAAA,QAAA;AACA;AACA;AACA;AACA;AACO,KAAA,oBAAA,aAAA,OAAA;AACP,YAAA,OAAA;AACA;AACA;AACA,MAAA,OAAA,CAAA,QAAA;AACA;AACA;AACA;AACO,KAAA,wBAAA,GAAA,oBAAA;AACP,SAAA,oBAAA;AACA,UAAA,oBAAA;AACA,SAAA,oBAAA;AACA,YAAA,oBAAA;AACA,WAAA,oBAAA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,kBAAA,kEAAA,yBAAA,KAAA,wBAAA;;ACLP;AACA;AACA;AACA;AACA;AACA;AACA;AACO,cAAA,uBAAA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAA,yBAAA;AACA;AACA;AACA;AACA,mBAAA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,iBAAA,uBAAA,OAAA,cAAA,CAAA,UAAA,gBAAA,0BAAA,iBAAA,WAAA,CAAA,yBAAA,qBAAA,cAAA,CAAA,UAAA;;;;"}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"mappings":";;AAAA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB;;ACRhE;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,aAAa,sBAAsB;AACnC,kBAAkB,2BAA2B;AAC7C;AACA,WAAW,oBAAoB;AAC/B,cAAc,eAAe;AAC7B,uBAAuB,wBAAwB;AAC/C;AACA;AACA;AACA;AACA,eAAe,uBAAuB;AACtC;AACA;AACA;AACA;AACA,KAAK,uBAAuB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,iBAAiB;AACtB,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,kBAAkB,GAAG,iBAAiB;AAC3C;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,KAAK,yBAAyB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,MAAM;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,OAAO,cAAc,OAAO;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,UAAU,0BAA0B,GAAG,OAAO,CAAC,0BAA0B;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,kBAAkB,eAAe;AACjC;AACA,qBAAqB,gBAAgB;AACrC,UAAU,eAAe,GAAG,OAAO,CAAC,eAAe;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,eAAe,YAAY;AAC3B;AACA,UAAU,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iBAAiB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,kBAAkB,KAAK,QAAQ,eAAe,OAAO,CAAC,QAAQ;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,oBAAoB,aAAa,OAAO;AAC7C,YAAY,OAAO,CAAC,MAAM;AAC1B,MAAM,OAAO,CAAC,QAAQ;AACtB;AACA;AACA;AACA,KAAK,wBAAwB,GAAG,oBAAoB;AACpD,SAAS,oBAAoB;AAC7B,UAAU,oBAAoB;AAC9B,SAAS,oBAAoB;AAC7B,YAAY,oBAAoB;AAChC,WAAW,oBAAoB;AAC/B;;AC7PA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,kIAAkI,yBAAyB,KAAK,wBAAwB;;ACLxN;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,uBAAuB;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,yBAAyB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,eAAe;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,cAAc,CAAC,UAAU,gBAAgB,0BAA0B,iBAAiB,WAAW,CAAC,yBAAyB,6BAA6B,WAAW,GAAG,cAAc,CAAC,UAAU;;","names":[]}