@ai-sdk/gateway 0.0.0-64aae7dd-20260114144918 → 0.0.0-98261322-20260122142521

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +49 -4
  2. package/dist/index.d.mts +20 -10
  3. package/dist/index.d.ts +20 -10
  4. package/dist/index.js +62 -25
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +62 -25
  7. package/dist/index.mjs.map +1 -1
  8. package/docs/00-ai-gateway.mdx +625 -0
  9. package/package.json +12 -5
  10. package/src/errors/as-gateway-error.ts +33 -0
  11. package/src/errors/create-gateway-error.test.ts +590 -0
  12. package/src/errors/create-gateway-error.ts +132 -0
  13. package/src/errors/extract-api-call-response.test.ts +270 -0
  14. package/src/errors/extract-api-call-response.ts +15 -0
  15. package/src/errors/gateway-authentication-error.ts +84 -0
  16. package/src/errors/gateway-error-types.test.ts +278 -0
  17. package/src/errors/gateway-error.ts +47 -0
  18. package/src/errors/gateway-internal-server-error.ts +33 -0
  19. package/src/errors/gateway-invalid-request-error.ts +33 -0
  20. package/src/errors/gateway-model-not-found-error.ts +47 -0
  21. package/src/errors/gateway-rate-limit-error.ts +33 -0
  22. package/src/errors/gateway-response-error.ts +42 -0
  23. package/src/errors/index.ts +16 -0
  24. package/src/errors/parse-auth-method.test.ts +136 -0
  25. package/src/errors/parse-auth-method.ts +23 -0
  26. package/src/gateway-config.ts +7 -0
  27. package/src/gateway-embedding-model-settings.ts +22 -0
  28. package/src/gateway-embedding-model.test.ts +213 -0
  29. package/src/gateway-embedding-model.ts +109 -0
  30. package/src/gateway-fetch-metadata.test.ts +774 -0
  31. package/src/gateway-fetch-metadata.ts +127 -0
  32. package/src/gateway-image-model-settings.ts +12 -0
  33. package/src/gateway-image-model.test.ts +823 -0
  34. package/src/gateway-image-model.ts +145 -0
  35. package/src/gateway-language-model-settings.ts +159 -0
  36. package/src/gateway-language-model.test.ts +1485 -0
  37. package/src/gateway-language-model.ts +212 -0
  38. package/src/gateway-model-entry.ts +58 -0
  39. package/src/gateway-provider-options.ts +66 -0
  40. package/src/gateway-provider.test.ts +1210 -0
  41. package/src/gateway-provider.ts +284 -0
  42. package/src/gateway-tools.ts +15 -0
  43. package/src/index.ts +27 -0
  44. package/src/tool/perplexity-search.ts +294 -0
  45. package/src/vercel-environment.test.ts +65 -0
  46. package/src/vercel-environment.ts +6 -0
  47. package/src/version.ts +6 -0
@@ -0,0 +1,278 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ GatewayError,
4
+ GatewayAuthenticationError,
5
+ GatewayInvalidRequestError,
6
+ GatewayRateLimitError,
7
+ GatewayModelNotFoundError,
8
+ GatewayInternalServerError,
9
+ GatewayResponseError,
10
+ } from './index';
11
+
12
+ describe('GatewayAuthenticationError', () => {
13
+ it('should create error with default values', () => {
14
+ const error = new GatewayAuthenticationError();
15
+
16
+ expect(error).toBeInstanceOf(Error);
17
+ expect(error).toBeInstanceOf(GatewayError);
18
+ expect(error.name).toBe('GatewayAuthenticationError');
19
+ expect(error.type).toBe('authentication_error');
20
+ expect(error.message).toBe('Authentication failed');
21
+ expect(error.statusCode).toBe(401);
22
+ expect(error.cause).toBeUndefined();
23
+ });
24
+
25
+ it('should create error with custom values', () => {
26
+ const cause = new Error('Original error');
27
+ const error = new GatewayAuthenticationError({
28
+ message: 'Custom auth failed',
29
+ statusCode: 403,
30
+ cause,
31
+ });
32
+
33
+ expect(error.message).toBe('Custom auth failed');
34
+ expect(error.statusCode).toBe(403);
35
+ expect(error.cause).toBe(cause);
36
+ });
37
+
38
+ it('should be identifiable via instance check', () => {
39
+ const error = new GatewayAuthenticationError();
40
+ const otherError = new Error('Not a gateway error');
41
+
42
+ expect(GatewayAuthenticationError.isInstance(error)).toBe(true);
43
+ expect(GatewayAuthenticationError.isInstance(otherError)).toBe(false);
44
+ expect(GatewayError.isInstance(error)).toBe(true);
45
+ });
46
+
47
+ describe('createContextualError', () => {
48
+ it('should create error for invalid API key only', () => {
49
+ const error = GatewayAuthenticationError.createContextualError({
50
+ apiKeyProvided: true,
51
+ oidcTokenProvided: false,
52
+ });
53
+
54
+ expect(error.message).toContain('Invalid API key');
55
+ expect(error.message).toContain('vercel.com/d?to=');
56
+ expect(error.statusCode).toBe(401);
57
+ });
58
+
59
+ it('should create error for invalid OIDC token only', () => {
60
+ const error = GatewayAuthenticationError.createContextualError({
61
+ apiKeyProvided: false,
62
+ oidcTokenProvided: true,
63
+ });
64
+
65
+ expect(error.message).toContain('Invalid OIDC token');
66
+ expect(error.message).toContain('npx vercel link');
67
+ expect(error.statusCode).toBe(401);
68
+ });
69
+
70
+ it('should create error for no authentication provided', () => {
71
+ const error = GatewayAuthenticationError.createContextualError({
72
+ apiKeyProvided: false,
73
+ oidcTokenProvided: false,
74
+ });
75
+
76
+ expect(error.message).toContain('No authentication provided');
77
+ expect(error.message).toContain('Option 1');
78
+ expect(error.message).toContain('Option 2');
79
+ expect(error.statusCode).toBe(401);
80
+ });
81
+
82
+ it('should prioritize API key error when both were provided', () => {
83
+ const error = GatewayAuthenticationError.createContextualError({
84
+ apiKeyProvided: true,
85
+ oidcTokenProvided: true,
86
+ });
87
+
88
+ expect(error.message).toContain('Invalid API key');
89
+ expect(error.message).toContain('vercel.com/d?to=');
90
+ expect(error.statusCode).toBe(401);
91
+ });
92
+
93
+ it('should create error for neither provided (legacy test)', () => {
94
+ const error = GatewayAuthenticationError.createContextualError({
95
+ apiKeyProvided: false,
96
+ oidcTokenProvided: false,
97
+ });
98
+
99
+ expect(error.message).toContain('No authentication provided');
100
+ expect(error.message).toContain('Option 1');
101
+ expect(error.message).toContain('Option 2');
102
+ expect(error.statusCode).toBe(401);
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('GatewayInvalidRequestError', () => {
108
+ it('should create error with default values', () => {
109
+ const error = new GatewayInvalidRequestError();
110
+
111
+ expect(error.name).toBe('GatewayInvalidRequestError');
112
+ expect(error.type).toBe('invalid_request_error');
113
+ expect(error.message).toBe('Invalid request');
114
+ expect(error.statusCode).toBe(400);
115
+ });
116
+
117
+ it('should create error with custom values', () => {
118
+ const error = new GatewayInvalidRequestError({
119
+ message: 'Missing required field',
120
+ statusCode: 422,
121
+ });
122
+
123
+ expect(error.message).toBe('Missing required field');
124
+ expect(error.statusCode).toBe(422);
125
+ });
126
+
127
+ it('should be identifiable via instance check', () => {
128
+ const error = new GatewayInvalidRequestError();
129
+
130
+ expect(GatewayInvalidRequestError.isInstance(error)).toBe(true);
131
+ expect(GatewayAuthenticationError.isInstance(error)).toBe(false);
132
+ expect(GatewayError.isInstance(error)).toBe(true);
133
+ });
134
+ });
135
+
136
+ describe('GatewayRateLimitError', () => {
137
+ it('should create error with default values', () => {
138
+ const error = new GatewayRateLimitError();
139
+
140
+ expect(error.name).toBe('GatewayRateLimitError');
141
+ expect(error.type).toBe('rate_limit_exceeded');
142
+ expect(error.message).toBe('Rate limit exceeded');
143
+ expect(error.statusCode).toBe(429);
144
+ });
145
+
146
+ it('should be identifiable via instance check', () => {
147
+ const error = new GatewayRateLimitError();
148
+
149
+ expect(GatewayRateLimitError.isInstance(error)).toBe(true);
150
+ expect(GatewayError.isInstance(error)).toBe(true);
151
+ });
152
+ });
153
+
154
+ describe('GatewayModelNotFoundError', () => {
155
+ it('should create error with default values', () => {
156
+ const error = new GatewayModelNotFoundError();
157
+
158
+ expect(error.name).toBe('GatewayModelNotFoundError');
159
+ expect(error.type).toBe('model_not_found');
160
+ expect(error.message).toBe('Model not found');
161
+ expect(error.statusCode).toBe(404);
162
+ expect(error.modelId).toBeUndefined();
163
+ });
164
+
165
+ it('should create error with model ID', () => {
166
+ const error = new GatewayModelNotFoundError({
167
+ message: 'Model gpt-4 not found',
168
+ modelId: 'gpt-4',
169
+ });
170
+
171
+ expect(error.message).toBe('Model gpt-4 not found');
172
+ expect(error.modelId).toBe('gpt-4');
173
+ });
174
+
175
+ it('should be identifiable via instance check', () => {
176
+ const error = new GatewayModelNotFoundError();
177
+
178
+ expect(GatewayModelNotFoundError.isInstance(error)).toBe(true);
179
+ expect(GatewayError.isInstance(error)).toBe(true);
180
+ });
181
+ });
182
+
183
+ describe('GatewayInternalServerError', () => {
184
+ it('should create error with default values', () => {
185
+ const error = new GatewayInternalServerError();
186
+
187
+ expect(error.name).toBe('GatewayInternalServerError');
188
+ expect(error.type).toBe('internal_server_error');
189
+ expect(error.message).toBe('Internal server error');
190
+ expect(error.statusCode).toBe(500);
191
+ });
192
+
193
+ it('should be identifiable via instance check', () => {
194
+ const error = new GatewayInternalServerError();
195
+
196
+ expect(GatewayInternalServerError.isInstance(error)).toBe(true);
197
+ expect(GatewayError.isInstance(error)).toBe(true);
198
+ });
199
+ });
200
+
201
+ describe('GatewayResponseError', () => {
202
+ it('should create error with default values', () => {
203
+ const error = new GatewayResponseError();
204
+
205
+ expect(error.name).toBe('GatewayResponseError');
206
+ expect(error.type).toBe('response_error');
207
+ expect(error.message).toBe('Invalid response from Gateway');
208
+ expect(error.statusCode).toBe(502);
209
+ expect(error.response).toBeUndefined();
210
+ expect(error.validationError).toBeUndefined();
211
+ });
212
+
213
+ it('should create error with response and validation error details', () => {
214
+ const response = { invalidField: 'value' };
215
+ const validationError = {
216
+ issues: [{ path: ['error'], message: 'Required' }],
217
+ } as any; // Mock ZodError structure
218
+
219
+ const error = new GatewayResponseError({
220
+ message: 'Custom parsing error',
221
+ statusCode: 422,
222
+ response,
223
+ validationError,
224
+ });
225
+
226
+ expect(error.message).toBe('Custom parsing error');
227
+ expect(error.statusCode).toBe(422);
228
+ expect(error.response).toBe(response);
229
+ expect(error.validationError).toBe(validationError);
230
+ });
231
+
232
+ it('should be identifiable via instance check', () => {
233
+ const error = new GatewayResponseError();
234
+
235
+ expect(GatewayResponseError.isInstance(error)).toBe(true);
236
+ expect(GatewayError.isInstance(error)).toBe(true);
237
+ });
238
+ });
239
+
240
+ describe('Cross-realm instance checking', () => {
241
+ it('should work with symbol-based type checking', () => {
242
+ const error = new GatewayAuthenticationError();
243
+
244
+ // Simulate different realm by creating a new instance in different context
245
+ const gatewayErrorMarker = Symbol.for('vercel.ai.gateway.error');
246
+ const authErrorMarker = Symbol.for(
247
+ 'vercel.ai.gateway.error.GatewayAuthenticationError',
248
+ );
249
+
250
+ // Verify the symbols are present
251
+ expect((error as any)[gatewayErrorMarker]).toBe(true);
252
+ expect((error as any)[authErrorMarker]).toBe(true);
253
+
254
+ // Test cross-realm safety
255
+ expect(GatewayError.hasMarker(error)).toBe(true);
256
+ expect(GatewayAuthenticationError.isInstance(error)).toBe(true);
257
+ });
258
+ });
259
+
260
+ describe('Error inheritance chain', () => {
261
+ it('should maintain proper inheritance', () => {
262
+ const error = new GatewayAuthenticationError();
263
+
264
+ expect(error instanceof Error).toBe(true);
265
+ expect(error instanceof GatewayError).toBe(true);
266
+ expect(error instanceof GatewayAuthenticationError).toBe(true);
267
+ });
268
+
269
+ it('should have proper stack traces', () => {
270
+ const error = new GatewayAuthenticationError({
271
+ message: 'Test error',
272
+ });
273
+
274
+ expect(error.stack).toBeDefined();
275
+ expect(error.stack).toContain('GatewayAuthenticationError');
276
+ expect(error.stack).toContain('Test error');
277
+ });
278
+ });
@@ -0,0 +1,47 @@
1
+ const marker = 'vercel.ai.gateway.error';
2
+ const symbol = Symbol.for(marker);
3
+
4
+ export abstract class GatewayError extends Error {
5
+ private readonly [symbol] = true; // used in isInstance
6
+
7
+ abstract readonly name: string;
8
+ abstract readonly type: string;
9
+ readonly statusCode: number;
10
+ readonly cause?: unknown;
11
+ readonly generationId?: string;
12
+
13
+ constructor({
14
+ message,
15
+ statusCode = 500,
16
+ cause,
17
+ generationId,
18
+ }: {
19
+ message: string;
20
+ statusCode?: number;
21
+ cause?: unknown;
22
+ generationId?: string;
23
+ }) {
24
+ super(generationId ? `${message} [${generationId}]` : message);
25
+ this.statusCode = statusCode;
26
+ this.cause = cause;
27
+ this.generationId = generationId;
28
+ }
29
+
30
+ /**
31
+ * Checks if the given error is a Gateway Error.
32
+ * @param {unknown} error - The error to check.
33
+ * @returns {boolean} True if the error is a Gateway Error, false otherwise.
34
+ */
35
+ static isInstance(error: unknown): error is GatewayError {
36
+ return GatewayError.hasMarker(error);
37
+ }
38
+
39
+ static hasMarker(error: unknown): error is GatewayError {
40
+ return (
41
+ typeof error === 'object' &&
42
+ error !== null &&
43
+ symbol in error &&
44
+ (error as any)[symbol] === true
45
+ );
46
+ }
47
+ }
@@ -0,0 +1,33 @@
1
+ import { GatewayError } from './gateway-error';
2
+
3
+ const name = 'GatewayInternalServerError';
4
+ const marker = `vercel.ai.gateway.error.${name}`;
5
+ const symbol = Symbol.for(marker);
6
+
7
+ /**
8
+ * Internal server error from the Gateway
9
+ */
10
+ export class GatewayInternalServerError extends GatewayError {
11
+ private readonly [symbol] = true; // used in isInstance
12
+
13
+ readonly name = name;
14
+ readonly type = 'internal_server_error';
15
+
16
+ constructor({
17
+ message = 'Internal server error',
18
+ statusCode = 500,
19
+ cause,
20
+ generationId,
21
+ }: {
22
+ message?: string;
23
+ statusCode?: number;
24
+ cause?: unknown;
25
+ generationId?: string;
26
+ } = {}) {
27
+ super({ message, statusCode, cause, generationId });
28
+ }
29
+
30
+ static isInstance(error: unknown): error is GatewayInternalServerError {
31
+ return GatewayError.hasMarker(error) && symbol in error;
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ import { GatewayError } from './gateway-error';
2
+
3
+ const name = 'GatewayInvalidRequestError';
4
+ const marker = `vercel.ai.gateway.error.${name}`;
5
+ const symbol = Symbol.for(marker);
6
+
7
+ /**
8
+ * Invalid request - missing headers, malformed data, etc.
9
+ */
10
+ export class GatewayInvalidRequestError extends GatewayError {
11
+ private readonly [symbol] = true; // used in isInstance
12
+
13
+ readonly name = name;
14
+ readonly type = 'invalid_request_error';
15
+
16
+ constructor({
17
+ message = 'Invalid request',
18
+ statusCode = 400,
19
+ cause,
20
+ generationId,
21
+ }: {
22
+ message?: string;
23
+ statusCode?: number;
24
+ cause?: unknown;
25
+ generationId?: string;
26
+ } = {}) {
27
+ super({ message, statusCode, cause, generationId });
28
+ }
29
+
30
+ static isInstance(error: unknown): error is GatewayInvalidRequestError {
31
+ return GatewayError.hasMarker(error) && symbol in error;
32
+ }
33
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod/v4';
2
+ import { GatewayError } from './gateway-error';
3
+ import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
4
+
5
+ const name = 'GatewayModelNotFoundError';
6
+ const marker = `vercel.ai.gateway.error.${name}`;
7
+ const symbol = Symbol.for(marker);
8
+
9
+ export const modelNotFoundParamSchema = lazySchema(() =>
10
+ zodSchema(
11
+ z.object({
12
+ modelId: z.string(),
13
+ }),
14
+ ),
15
+ );
16
+
17
+ /**
18
+ * Model not found or not available
19
+ */
20
+ export class GatewayModelNotFoundError extends GatewayError {
21
+ private readonly [symbol] = true; // used in isInstance
22
+
23
+ readonly name = name;
24
+ readonly type = 'model_not_found';
25
+ readonly modelId?: string;
26
+
27
+ constructor({
28
+ message = 'Model not found',
29
+ statusCode = 404,
30
+ modelId,
31
+ cause,
32
+ generationId,
33
+ }: {
34
+ message?: string;
35
+ statusCode?: number;
36
+ modelId?: string;
37
+ cause?: unknown;
38
+ generationId?: string;
39
+ } = {}) {
40
+ super({ message, statusCode, cause, generationId });
41
+ this.modelId = modelId;
42
+ }
43
+
44
+ static isInstance(error: unknown): error is GatewayModelNotFoundError {
45
+ return GatewayError.hasMarker(error) && symbol in error;
46
+ }
47
+ }
@@ -0,0 +1,33 @@
1
+ import { GatewayError } from './gateway-error';
2
+
3
+ const name = 'GatewayRateLimitError';
4
+ const marker = `vercel.ai.gateway.error.${name}`;
5
+ const symbol = Symbol.for(marker);
6
+
7
+ /**
8
+ * Rate limit exceeded.
9
+ */
10
+ export class GatewayRateLimitError extends GatewayError {
11
+ private readonly [symbol] = true; // used in isInstance
12
+
13
+ readonly name = name;
14
+ readonly type = 'rate_limit_exceeded';
15
+
16
+ constructor({
17
+ message = 'Rate limit exceeded',
18
+ statusCode = 429,
19
+ cause,
20
+ generationId,
21
+ }: {
22
+ message?: string;
23
+ statusCode?: number;
24
+ cause?: unknown;
25
+ generationId?: string;
26
+ } = {}) {
27
+ super({ message, statusCode, cause, generationId });
28
+ }
29
+
30
+ static isInstance(error: unknown): error is GatewayRateLimitError {
31
+ return GatewayError.hasMarker(error) && symbol in error;
32
+ }
33
+ }
@@ -0,0 +1,42 @@
1
+ import { TypeValidationError } from '@ai-sdk/provider';
2
+ import { GatewayError } from './gateway-error';
3
+
4
+ const name = 'GatewayResponseError';
5
+ const marker = `vercel.ai.gateway.error.${name}`;
6
+ const symbol = Symbol.for(marker);
7
+
8
+ /**
9
+ * Gateway response parsing error
10
+ */
11
+ export class GatewayResponseError extends GatewayError {
12
+ private readonly [symbol] = true; // used in isInstance
13
+
14
+ readonly name = name;
15
+ readonly type = 'response_error';
16
+ readonly response?: unknown;
17
+ readonly validationError?: TypeValidationError;
18
+
19
+ constructor({
20
+ message = 'Invalid response from Gateway',
21
+ statusCode = 502,
22
+ response,
23
+ validationError,
24
+ cause,
25
+ generationId,
26
+ }: {
27
+ message?: string;
28
+ statusCode?: number;
29
+ response?: unknown;
30
+ validationError?: TypeValidationError;
31
+ cause?: unknown;
32
+ generationId?: string;
33
+ } = {}) {
34
+ super({ message, statusCode, cause, generationId });
35
+ this.response = response;
36
+ this.validationError = validationError;
37
+ }
38
+
39
+ static isInstance(error: unknown): error is GatewayResponseError {
40
+ return GatewayError.hasMarker(error) && symbol in error;
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ export { asGatewayError } from './as-gateway-error';
2
+ export {
3
+ createGatewayErrorFromResponse,
4
+ type GatewayErrorResponse,
5
+ } from './create-gateway-error';
6
+ export { extractApiCallResponse } from './extract-api-call-response';
7
+ export { GatewayError } from './gateway-error';
8
+ export { GatewayAuthenticationError } from './gateway-authentication-error';
9
+ export { GatewayInternalServerError } from './gateway-internal-server-error';
10
+ export { GatewayInvalidRequestError } from './gateway-invalid-request-error';
11
+ export {
12
+ GatewayModelNotFoundError,
13
+ modelNotFoundParamSchema,
14
+ } from './gateway-model-not-found-error';
15
+ export { GatewayRateLimitError } from './gateway-rate-limit-error';
16
+ export { GatewayResponseError } from './gateway-response-error';
@@ -0,0 +1,136 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ GATEWAY_AUTH_METHOD_HEADER,
4
+ parseAuthMethod,
5
+ } from './parse-auth-method';
6
+
7
+ describe('GATEWAY_AUTH_METHOD_HEADER', () => {
8
+ it('should export the correct header name', () => {
9
+ expect(GATEWAY_AUTH_METHOD_HEADER).toBe('ai-gateway-auth-method');
10
+ });
11
+ });
12
+
13
+ describe('parseAuthMethod', () => {
14
+ describe('valid authentication methods', () => {
15
+ it('should parse "api-key" auth method', async () => {
16
+ const headers = {
17
+ [GATEWAY_AUTH_METHOD_HEADER]: 'api-key',
18
+ };
19
+
20
+ const result = await parseAuthMethod(headers);
21
+
22
+ expect(result).toBe('api-key');
23
+ });
24
+
25
+ it('should parse "oidc" auth method', async () => {
26
+ const headers = {
27
+ [GATEWAY_AUTH_METHOD_HEADER]: 'oidc',
28
+ };
29
+
30
+ const result = await parseAuthMethod(headers);
31
+
32
+ expect(result).toBe('oidc');
33
+ });
34
+
35
+ it('should handle headers with other fields present', async () => {
36
+ const headers = {
37
+ authorization: 'Bearer token',
38
+ 'content-type': 'application/json',
39
+ [GATEWAY_AUTH_METHOD_HEADER]: 'api-key',
40
+ 'user-agent': 'test-agent',
41
+ };
42
+
43
+ const result = await parseAuthMethod(headers);
44
+
45
+ expect(result).toBe('api-key');
46
+ });
47
+ });
48
+
49
+ describe('invalid authentication methods', () => {
50
+ it('should return undefined for invalid auth method string', async () => {
51
+ const headers = {
52
+ [GATEWAY_AUTH_METHOD_HEADER]: 'invalid-method',
53
+ };
54
+ expect(await parseAuthMethod(headers)).toBeUndefined();
55
+ });
56
+
57
+ it('should return undefined for empty string', async () => {
58
+ const headers = {
59
+ [GATEWAY_AUTH_METHOD_HEADER]: '',
60
+ };
61
+ expect(await parseAuthMethod(headers)).toBeUndefined();
62
+ });
63
+
64
+ it('should return undefined for numeric value', async () => {
65
+ const headers = {
66
+ [GATEWAY_AUTH_METHOD_HEADER]: '123',
67
+ };
68
+ expect(await parseAuthMethod(headers)).toBeUndefined();
69
+ });
70
+
71
+ it('should return undefined for boolean-like strings', async () => {
72
+ const headers = {
73
+ [GATEWAY_AUTH_METHOD_HEADER]: 'true',
74
+ };
75
+ expect(await parseAuthMethod(headers)).toBeUndefined();
76
+ });
77
+
78
+ it('should return undefined for case-sensitive variations', async () => {
79
+ const headers = {
80
+ [GATEWAY_AUTH_METHOD_HEADER]: 'API-KEY',
81
+ };
82
+ expect(await parseAuthMethod(headers)).toBeUndefined();
83
+ });
84
+
85
+ it('should return undefined for OIDC case variations', async () => {
86
+ const headers = {
87
+ [GATEWAY_AUTH_METHOD_HEADER]: 'OIDC',
88
+ };
89
+ expect(await parseAuthMethod(headers)).toBeUndefined();
90
+ });
91
+ });
92
+
93
+ describe('missing or undefined headers', () => {
94
+ it('should return undefined when header is missing', async () => {
95
+ const headers = {
96
+ authorization: 'Bearer token',
97
+ };
98
+ expect(await parseAuthMethod(headers)).toBeUndefined();
99
+ });
100
+
101
+ it('should return undefined when header is undefined', async () => {
102
+ const headers = {
103
+ [GATEWAY_AUTH_METHOD_HEADER]: undefined,
104
+ };
105
+ expect(await parseAuthMethod(headers)).toBeUndefined();
106
+ });
107
+
108
+ it('should return undefined when headers object is empty', async () => {
109
+ const headers = {};
110
+ expect(await parseAuthMethod(headers)).toBeUndefined();
111
+ });
112
+ });
113
+
114
+ describe('edge cases', () => {
115
+ it('should return undefined for whitespace-only strings', async () => {
116
+ const headers = {
117
+ [GATEWAY_AUTH_METHOD_HEADER]: ' ',
118
+ };
119
+ expect(await parseAuthMethod(headers)).toBeUndefined();
120
+ });
121
+
122
+ it('should return undefined for auth methods with extra whitespace', async () => {
123
+ const headers = {
124
+ [GATEWAY_AUTH_METHOD_HEADER]: ' api-key ',
125
+ };
126
+ expect(await parseAuthMethod(headers)).toBeUndefined();
127
+ });
128
+
129
+ it('should handle null values', async () => {
130
+ const headers = {
131
+ [GATEWAY_AUTH_METHOD_HEADER]: null as any,
132
+ };
133
+ expect(await parseAuthMethod(headers)).toBeUndefined();
134
+ });
135
+ });
136
+ });
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod/v4';
2
+ import {
3
+ lazySchema,
4
+ safeValidateTypes,
5
+ zodSchema,
6
+ } from '@ai-sdk/provider-utils';
7
+
8
+ export const GATEWAY_AUTH_METHOD_HEADER = 'ai-gateway-auth-method' as const;
9
+
10
+ export async function parseAuthMethod(
11
+ headers: Record<string, string | undefined>,
12
+ ) {
13
+ const result = await safeValidateTypes({
14
+ value: headers[GATEWAY_AUTH_METHOD_HEADER],
15
+ schema: gatewayAuthMethodSchema,
16
+ });
17
+
18
+ return result.success ? result.value : undefined;
19
+ }
20
+
21
+ const gatewayAuthMethodSchema = lazySchema(() =>
22
+ zodSchema(z.union([z.literal('api-key'), z.literal('oidc')])),
23
+ );