UNPKG

@openrouter/ai-sdk-provider

Version:

The [OpenRouter](https://openrouter.ai/) provider for the [Vercel AI SDK](https://sdk.vercel.ai/docs) gives access to over 300 large language models on the OpenRouter chat and completion APIs.

1 lines 431 kB
{"version":3,"sources":["../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/ai-sdk-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/api-call-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/empty-response-body-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/get-error-message.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/invalid-argument-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/invalid-prompt-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/invalid-response-data-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/json-parse-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/load-api-key-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/load-setting-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/no-content-generated-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/no-such-model-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/too-many-embedding-values-for-call-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/type-validation-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/errors/unsupported-functionality-error.ts","../../node_modules/.pnpm/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/src/json-value/is-json.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/combine-headers.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/convert-async-iterator-to-readable-stream.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/create-tool-name-mapping.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/delay.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/delayed-promise.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/extract-response-headers.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/uint8-utils.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/convert-image-model-file-to-data-uri.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/convert-to-form-data.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/download-error.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/read-response-with-size-limit.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/validate-download-url.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/download-blob.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/generate-id.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/get-error-message.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/get-from-api.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/handle-fetch-error.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/is-abort-error.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/get-runtime-environment-user-agent.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/normalize-headers.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/with-user-agent-suffix.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/version.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/inject-json-instruction.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/is-non-nullable.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/is-url-supported.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/load-api-key.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/load-optional-setting.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/load-setting.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/media-type-to-extension.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/parse-json.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/secure-json-parse.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/validate-types.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/schema.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/add-additional-properties-to-json-schema.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/options.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/select-parser.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/any.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/array.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/boolean.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/date.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/default.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/record.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/string.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/map.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/never.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/null.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/union.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/number.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/object.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/set.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/get-relative-path.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/parse-def.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/refs.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/parse-json-event-stream.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/parse-provider-options.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/post-to-api.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/types/tool.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/provider-tool-factory.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/remove-undefined-entries.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/resolve.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/response-handler.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/strip-file-extension.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/without-trailing-slash.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/is-async-iterable.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/types/execute-tool.ts","../../node_modules/.pnpm/@ai-sdk+provider-utils@4.0.23_zod@4.3.5/node_modules/@ai-sdk/provider-utils/src/index.ts","../../node_modules/.pnpm/eventsource-parser@3.0.6/node_modules/eventsource-parser/src/errors.ts","../../node_modules/.pnpm/eventsource-parser@3.0.6/node_modules/eventsource-parser/src/parse.ts","../../node_modules/.pnpm/eventsource-parser@3.0.6/node_modules/eventsource-parser/src/stream.ts","../../src/schemas/reasoning-details.ts","../../src/utils/type-guards.ts","../../src/schemas/format.ts","../../src/schemas/error-response.ts","../../src/schemas/provider-metadata.ts","../../src/utils/compute-token-usage.ts","../../src/utils/map-finish-reason.ts","../../src/utils/with-stream-error-handling.ts","../../src/utils/deterministic-stringify.ts","../../src/utils/reasoning-details-duplicate-tracker.ts","../../src/types/openrouter-chat-completions-input.ts","../../src/chat/is-url.ts","../../src/chat/file-url-utils.ts","../../src/chat/convert-to-openrouter-chat-messages.ts","../../src/chat/get-tool-choice.ts","../../src/chat/schemas.ts","../../src/schemas/image.ts","../../src/chat/index.ts","../../src/completion/convert-to-openrouter-completion-prompt.ts","../../src/completion/schemas.ts","../../src/completion/index.ts","../../src/embedding/schemas.ts","../../src/embedding/index.ts","../../src/image/schemas.ts","../../src/image/index.ts","../../src/video/schemas.ts","../../src/video/index.ts"],"sourcesContent":["/**\n * Symbol used for identifying AI SDK Error instances.\n * Enables checking if an error is an instance of AISDKError across package versions.\n */\nconst marker = 'vercel.ai.error';\nconst symbol = Symbol.for(marker);\n\n/**\n * Custom error class for AI SDK related errors.\n * @extends Error\n */\nexport class AISDKError extends Error {\n private readonly [symbol] = true; // used in isInstance\n\n /**\n * The underlying cause of the error, if any.\n */\n readonly cause?: unknown;\n\n /**\n * Creates an AI SDK Error.\n *\n * @param {Object} params - The parameters for creating the error.\n * @param {string} params.name - The name of the error.\n * @param {string} params.message - The error message.\n * @param {unknown} [params.cause] - The underlying cause of the error.\n */\n constructor({\n name,\n message,\n cause,\n }: {\n name: string;\n message: string;\n cause?: unknown;\n }) {\n super(message);\n\n this.name = name;\n this.cause = cause;\n }\n\n /**\n * Checks if the given error is an AI SDK Error.\n * @param {unknown} error - The error to check.\n * @returns {boolean} True if the error is an AI SDK Error, false otherwise.\n */\n static isInstance(error: unknown): error is AISDKError {\n return AISDKError.hasMarker(error, marker);\n }\n\n protected static hasMarker(error: unknown, marker: string): boolean {\n const markerSymbol = Symbol.for(marker);\n return (\n error != null &&\n typeof error === 'object' &&\n markerSymbol in error &&\n typeof error[markerSymbol] === 'boolean' &&\n error[markerSymbol] === true\n );\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_APICallError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class APICallError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly url: string;\n readonly requestBodyValues: unknown;\n readonly statusCode?: number;\n\n readonly responseHeaders?: Record<string, string>;\n readonly responseBody?: string;\n\n readonly isRetryable: boolean;\n readonly data?: unknown;\n\n constructor({\n message,\n url,\n requestBodyValues,\n statusCode,\n responseHeaders,\n responseBody,\n cause,\n isRetryable = statusCode != null &&\n (statusCode === 408 || // request timeout\n statusCode === 409 || // conflict\n statusCode === 429 || // too many requests\n statusCode >= 500), // server error\n data,\n }: {\n message: string;\n url: string;\n requestBodyValues: unknown;\n statusCode?: number;\n responseHeaders?: Record<string, string>;\n responseBody?: string;\n cause?: unknown;\n isRetryable?: boolean;\n data?: unknown;\n }) {\n super({ name, message, cause });\n\n this.url = url;\n this.requestBodyValues = requestBodyValues;\n this.statusCode = statusCode;\n this.responseHeaders = responseHeaders;\n this.responseBody = responseBody;\n this.isRetryable = isRetryable;\n this.data = data;\n }\n\n static isInstance(error: unknown): error is APICallError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_EmptyResponseBodyError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class EmptyResponseBodyError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n constructor({ message = 'Empty response body' }: { message?: string } = {}) {\n super({ name, message });\n }\n\n static isInstance(error: unknown): error is EmptyResponseBodyError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","export function getErrorMessage(error: unknown | undefined) {\n if (error == null) {\n return 'unknown error';\n }\n\n if (typeof error === 'string') {\n return error;\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return JSON.stringify(error);\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_InvalidArgumentError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\n/**\n * A function argument is invalid.\n */\nexport class InvalidArgumentError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly argument: string;\n\n constructor({\n message,\n cause,\n argument,\n }: {\n argument: string;\n message: string;\n cause?: unknown;\n }) {\n super({ name, message, cause });\n\n this.argument = argument;\n }\n\n static isInstance(error: unknown): error is InvalidArgumentError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_InvalidPromptError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\n/**\n * A prompt is invalid. This error should be thrown by providers when they cannot\n * process a prompt.\n */\nexport class InvalidPromptError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly prompt: unknown;\n\n constructor({\n prompt,\n message,\n cause,\n }: {\n prompt: unknown;\n message: string;\n cause?: unknown;\n }) {\n super({ name, message: `Invalid prompt: ${message}`, cause });\n\n this.prompt = prompt;\n }\n\n static isInstance(error: unknown): error is InvalidPromptError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_InvalidResponseDataError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\n/**\n * Server returned a response with invalid data content.\n * This should be thrown by providers when they cannot parse the response from the API.\n */\nexport class InvalidResponseDataError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly data: unknown;\n\n constructor({\n data,\n message = `Invalid response data: ${JSON.stringify(data)}.`,\n }: {\n data: unknown;\n message?: string;\n }) {\n super({ name, message });\n\n this.data = data;\n }\n\n static isInstance(error: unknown): error is InvalidResponseDataError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\nimport { getErrorMessage } from './get-error-message';\n\nconst name = 'AI_JSONParseError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class JSONParseError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly text: string;\n\n constructor({ text, cause }: { text: string; cause: unknown }) {\n super({\n name,\n message:\n `JSON parsing failed: ` +\n `Text: ${text}.\\n` +\n `Error message: ${getErrorMessage(cause)}`,\n cause,\n });\n\n this.text = text;\n }\n\n static isInstance(error: unknown): error is JSONParseError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_LoadAPIKeyError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class LoadAPIKeyError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n constructor({ message }: { message: string }) {\n super({ name, message });\n }\n\n static isInstance(error: unknown): error is LoadAPIKeyError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_LoadSettingError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class LoadSettingError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n constructor({ message }: { message: string }) {\n super({ name, message });\n }\n\n static isInstance(error: unknown): error is LoadSettingError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_NoContentGeneratedError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\n/**\n * Thrown when the AI provider fails to generate any content.\n */\nexport class NoContentGeneratedError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n constructor({\n message = 'No content generated.',\n }: { message?: string } = {}) {\n super({ name, message });\n }\n\n static isInstance(error: unknown): error is NoContentGeneratedError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_NoSuchModelError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class NoSuchModelError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly modelId: string;\n readonly modelType:\n | 'languageModel'\n | 'embeddingModel'\n | 'imageModel'\n | 'transcriptionModel'\n | 'speechModel'\n | 'rerankingModel'\n | 'videoModel';\n\n constructor({\n errorName = name,\n modelId,\n modelType,\n message = `No such ${modelType}: ${modelId}`,\n }: {\n errorName?: string;\n modelId: string;\n modelType:\n | 'languageModel'\n | 'embeddingModel'\n | 'imageModel'\n | 'transcriptionModel'\n | 'speechModel'\n | 'rerankingModel'\n | 'videoModel';\n message?: string;\n }) {\n super({ name: errorName, message });\n\n this.modelId = modelId;\n this.modelType = modelType;\n }\n\n static isInstance(error: unknown): error is NoSuchModelError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_TooManyEmbeddingValuesForCallError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class TooManyEmbeddingValuesForCallError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly provider: string;\n readonly modelId: string;\n readonly maxEmbeddingsPerCall: number;\n readonly values: Array<unknown>;\n\n constructor(options: {\n provider: string;\n modelId: string;\n maxEmbeddingsPerCall: number;\n values: Array<unknown>;\n }) {\n super({\n name,\n message:\n `Too many values for a single embedding call. ` +\n `The ${options.provider} model \"${options.modelId}\" can only embed up to ` +\n `${options.maxEmbeddingsPerCall} values per call, but ${options.values.length} values were provided.`,\n });\n\n this.provider = options.provider;\n this.modelId = options.modelId;\n this.maxEmbeddingsPerCall = options.maxEmbeddingsPerCall;\n this.values = options.values;\n }\n\n static isInstance(\n error: unknown,\n ): error is TooManyEmbeddingValuesForCallError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { AISDKError } from './ai-sdk-error';\nimport { getErrorMessage } from './get-error-message';\n\nconst name = 'AI_TypeValidationError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport interface TypeValidationContext {\n /**\n * Field path in dot notation (e.g., \"message.metadata\", \"message.parts[3].data\")\n */\n field?: string;\n\n /**\n * Entity name (e.g., tool name, data type name)\n */\n entityName?: string;\n\n /**\n * Entity identifier (e.g., message ID, tool call ID)\n */\n entityId?: string;\n}\n\nexport class TypeValidationError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly value: unknown;\n readonly context?: TypeValidationContext;\n\n constructor({\n value,\n cause,\n context,\n }: {\n value: unknown;\n cause: unknown;\n context?: TypeValidationContext;\n }) {\n let contextPrefix = 'Type validation failed';\n\n if (context?.field) {\n contextPrefix += ` for ${context.field}`;\n }\n\n if (context?.entityName || context?.entityId) {\n contextPrefix += ' (';\n const parts: string[] = [];\n if (context.entityName) {\n parts.push(context.entityName);\n }\n if (context.entityId) {\n parts.push(`id: \"${context.entityId}\"`);\n }\n contextPrefix += parts.join(', ');\n contextPrefix += ')';\n }\n\n super({\n name,\n message:\n `${contextPrefix}: ` +\n `Value: ${JSON.stringify(value)}.\\n` +\n `Error message: ${getErrorMessage(cause)}`,\n cause,\n });\n\n this.value = value;\n this.context = context;\n }\n\n static isInstance(error: unknown): error is TypeValidationError {\n return AISDKError.hasMarker(error, marker);\n }\n\n /**\n * Wraps an error into a TypeValidationError.\n * If the cause is already a TypeValidationError with the same value and context, it returns the cause.\n * Otherwise, it creates a new TypeValidationError.\n *\n * @param {Object} params - The parameters for wrapping the error.\n * @param {unknown} params.value - The value that failed validation.\n * @param {unknown} params.cause - The original error or cause of the validation failure.\n * @param {TypeValidationContext} params.context - Optional context about what is being validated.\n * @returns {TypeValidationError} A TypeValidationError instance.\n */\n static wrap({\n value,\n cause,\n context,\n }: {\n value: unknown;\n cause: unknown;\n context?: TypeValidationContext;\n }): TypeValidationError {\n if (\n TypeValidationError.isInstance(cause) &&\n cause.value === value &&\n cause.context?.field === context?.field &&\n cause.context?.entityName === context?.entityName &&\n cause.context?.entityId === context?.entityId\n ) {\n return cause;\n }\n\n return new TypeValidationError({ value, cause, context });\n }\n}\n","import { AISDKError } from './ai-sdk-error';\n\nconst name = 'AI_UnsupportedFunctionalityError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class UnsupportedFunctionalityError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly functionality: string;\n\n constructor({\n functionality,\n message = `'${functionality}' functionality not supported.`,\n }: {\n functionality: string;\n message?: string;\n }) {\n super({ name, message });\n this.functionality = functionality;\n }\n\n static isInstance(error: unknown): error is UnsupportedFunctionalityError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { JSONArray, JSONObject, JSONValue } from './json-value';\n\nexport function isJSONValue(value: unknown): value is JSONValue {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return true;\n }\n\n if (Array.isArray(value)) {\n return value.every(isJSONValue);\n }\n\n if (typeof value === 'object') {\n return Object.entries(value).every(\n ([key, val]) =>\n typeof key === 'string' && (val === undefined || isJSONValue(val)),\n );\n }\n\n return false;\n}\n\nexport function isJSONArray(value: unknown): value is JSONArray {\n return Array.isArray(value) && value.every(isJSONValue);\n}\n\nexport function isJSONObject(value: unknown): value is JSONObject {\n return (\n value != null &&\n typeof value === 'object' &&\n Object.entries(value).every(\n ([key, val]) =>\n typeof key === 'string' && (val === undefined || isJSONValue(val)),\n )\n );\n}\n","export function combineHeaders(\n ...headers: Array<Record<string, string | undefined> | undefined>\n): Record<string, string | undefined> {\n return headers.reduce(\n (combinedHeaders, currentHeaders) => ({\n ...combinedHeaders,\n ...(currentHeaders ?? {}),\n }),\n {},\n ) as Record<string, string | undefined>;\n}\n","/**\n * Converts an AsyncIterator to a ReadableStream.\n *\n * @template T - The type of elements produced by the AsyncIterator.\n * @param { <T>} iterator - The AsyncIterator to convert.\n * @returns {ReadableStream<T>} - A ReadableStream that provides the same data as the AsyncIterator.\n */\nexport function convertAsyncIteratorToReadableStream<T>(\n iterator: AsyncIterator<T>,\n): ReadableStream<T> {\n let cancelled = false;\n\n return new ReadableStream<T>({\n /**\n * Called when the consumer wants to pull more data from the stream.\n *\n * @param {ReadableStreamDefaultController<T>} controller - The controller to enqueue data into the stream.\n * @returns {Promise<void>}\n */\n async pull(controller) {\n if (cancelled) return;\n try {\n const { value, done } = await iterator.next();\n if (done) {\n controller.close();\n } else {\n controller.enqueue(value);\n }\n } catch (error) {\n controller.error(error);\n }\n },\n /**\n * Called when the consumer cancels the stream.\n */\n async cancel(reason?: unknown) {\n cancelled = true;\n if (iterator.return) {\n try {\n await iterator.return(reason);\n } catch {\n // intentionally ignore errors during cancellation\n }\n }\n },\n });\n}\n","import {\n LanguageModelV3FunctionTool,\n LanguageModelV3ProviderTool,\n} from '@ai-sdk/provider';\n\n/**\n * Interface for mapping between custom tool names and provider tool names.\n */\nexport interface ToolNameMapping {\n /**\n * Maps a custom tool name (used by the client) to the provider's tool name.\n * If the custom tool name does not have a mapping, returns the input name.\n *\n * @param customToolName - The custom name of the tool defined by the client.\n * @returns The corresponding provider tool name, or the input name if not mapped.\n */\n toProviderToolName: (customToolName: string) => string;\n\n /**\n * Maps a provider tool name to the custom tool name used by the client.\n * If the provider tool name does not have a mapping, returns the input name.\n *\n * @param providerToolName - The name of the tool as understood by the provider.\n * @returns The corresponding custom tool name, or the input name if not mapped.\n */\n toCustomToolName: (providerToolName: string) => string;\n}\n\n/**\n * @param tools - Tools that were passed to the language model.\n * @param providerToolNames - Maps the provider tool ids to the provider tool names.\n */\nexport function createToolNameMapping({\n tools = [],\n providerToolNames,\n resolveProviderToolName,\n}: {\n /**\n * Tools that were passed to the language model.\n */\n tools:\n | Array<LanguageModelV3FunctionTool | LanguageModelV3ProviderTool>\n | undefined;\n\n /**\n * Maps the provider tool ids to the provider tool names.\n */\n providerToolNames: Record<`${string}.${string}`, string>;\n\n /**\n * Optional resolver for provider tool names that cannot be represented as\n * static id -> name mappings (e.g. dynamic provider names).\n */\n resolveProviderToolName?: (\n tool: LanguageModelV3ProviderTool,\n ) => string | undefined;\n}): ToolNameMapping {\n const customToolNameToProviderToolName: Record<string, string> = {};\n const providerToolNameToCustomToolName: Record<string, string> = {};\n\n for (const tool of tools) {\n if (tool.type === 'provider') {\n const providerToolName =\n resolveProviderToolName?.(tool) ??\n (tool.id in providerToolNames ? providerToolNames[tool.id] : undefined);\n\n if (providerToolName == null) {\n continue;\n }\n\n customToolNameToProviderToolName[tool.name] = providerToolName;\n providerToolNameToCustomToolName[providerToolName] = tool.name;\n }\n }\n\n return {\n toProviderToolName: (customToolName: string) =>\n customToolNameToProviderToolName[customToolName] ?? customToolName,\n toCustomToolName: (providerToolName: string) =>\n providerToolNameToCustomToolName[providerToolName] ?? providerToolName,\n };\n}\n","/**\n * Creates a Promise that resolves after a specified delay\n * @param delayInMs - The delay duration in milliseconds. If null or undefined, resolves immediately.\n * @param signal - Optional AbortSignal to cancel the delay\n * @returns A Promise that resolves after the specified delay\n * @throws {DOMException} When the signal is aborted\n */\nexport async function delay(\n delayInMs?: number | null,\n options?: {\n abortSignal?: AbortSignal;\n },\n): Promise<void> {\n if (delayInMs == null) {\n return Promise.resolve();\n }\n\n const signal = options?.abortSignal;\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(createAbortError());\n return;\n }\n\n const timeoutId = setTimeout(() => {\n cleanup();\n resolve();\n }, delayInMs);\n\n const cleanup = () => {\n clearTimeout(timeoutId);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const onAbort = () => {\n cleanup();\n reject(createAbortError());\n };\n\n signal?.addEventListener('abort', onAbort);\n });\n}\n\nfunction createAbortError(): DOMException {\n return new DOMException('Delay was aborted', 'AbortError');\n}\n","/**\n * Delayed promise. It is only constructed once the value is accessed.\n * This is useful to avoid unhandled promise rejections when the promise is created\n * but not accessed.\n */\nexport class DelayedPromise<T> {\n private status:\n | { type: 'pending' }\n | { type: 'resolved'; value: T }\n | { type: 'rejected'; error: unknown } = { type: 'pending' };\n private _promise: Promise<T> | undefined;\n private _resolve: undefined | ((value: T) => void) = undefined;\n private _reject: undefined | ((error: unknown) => void) = undefined;\n\n get promise(): Promise<T> {\n if (this._promise) {\n return this._promise;\n }\n\n this._promise = new Promise<T>((resolve, reject) => {\n if (this.status.type === 'resolved') {\n resolve(this.status.value);\n } else if (this.status.type === 'rejected') {\n reject(this.status.error);\n }\n\n this._resolve = resolve;\n this._reject = reject;\n });\n\n return this._promise;\n }\n\n resolve(value: T): void {\n this.status = { type: 'resolved', value };\n\n if (this._promise) {\n this._resolve?.(value);\n }\n }\n\n reject(error: unknown): void {\n this.status = { type: 'rejected', error };\n\n if (this._promise) {\n this._reject?.(error);\n }\n }\n\n isResolved(): boolean {\n return this.status.type === 'resolved';\n }\n\n isRejected(): boolean {\n return this.status.type === 'rejected';\n }\n\n isPending(): boolean {\n return this.status.type === 'pending';\n }\n}\n","/**\n * Extracts the headers from a response object and returns them as a key-value object.\n *\n * @param response - The response object to extract headers from.\n * @returns The headers as a key-value object.\n */\nexport function extractResponseHeaders(response: Response) {\n return Object.fromEntries<string>([...response.headers]);\n}\n","// btoa and atob need to be invoked as a function call, not as a method call.\n// Otherwise CloudFlare will throw a\n// \"TypeError: Illegal invocation: function called with incorrect this reference\"\nconst { btoa, atob } = globalThis;\n\nexport function convertBase64ToUint8Array(base64String: string) {\n const base64Url = base64String.replace(/-/g, '+').replace(/_/g, '/');\n const latin1string = atob(base64Url);\n return Uint8Array.from(latin1string, byte => byte.codePointAt(0)!);\n}\n\nexport function convertUint8ArrayToBase64(array: Uint8Array): string {\n let latin1string = '';\n\n // Note: regular for loop to support older JavaScript versions that\n // do not support for..of on Uint8Array\n for (let i = 0; i < array.length; i++) {\n latin1string += String.fromCodePoint(array[i]);\n }\n\n return btoa(latin1string);\n}\n\nexport function convertToBase64(value: string | Uint8Array): string {\n return value instanceof Uint8Array ? convertUint8ArrayToBase64(value) : value;\n}\n","import { ImageModelV3File } from '@ai-sdk/provider';\nimport { convertUint8ArrayToBase64 } from './uint8-utils';\n\n/**\n * Convert an ImageModelV3File to a URL or data URI string.\n *\n * If the file is a URL, it returns the URL as-is.\n * If the file is base64 data, it returns a data URI with the base64 data.\n * If the file is a Uint8Array, it converts it to base64 and returns a data URI.\n */\nexport function convertImageModelFileToDataUri(file: ImageModelV3File): string {\n if (file.type === 'url') return file.url;\n\n return `data:${file.mediaType};base64,${\n typeof file.data === 'string'\n ? file.data\n : convertUint8ArrayToBase64(file.data)\n }`;\n}\n","/**\n * Converts an input object to FormData for multipart/form-data requests.\n *\n * Handles the following cases:\n * - `null` or `undefined` values are skipped\n * - Arrays with a single element are appended as a single value\n * - Arrays with multiple elements are appended with `[]` suffix (e.g., `image[]`)\n * unless `useArrayBrackets` is set to `false`\n * - All other values are appended directly\n *\n * @param input - The input object to convert. Use a generic type for type validation.\n * @param options - Optional configuration object.\n * @param options.useArrayBrackets - Whether to add `[]` suffix for multi-element arrays.\n * Defaults to `true`. Set to `false` for APIs that expect repeated keys without brackets.\n * @returns A FormData object containing the input values.\n *\n * @example\n * ```ts\n * type MyInput = {\n * model: string;\n * prompt: string;\n * images: Blob[];\n * };\n *\n * const formData = convertToFormData<MyInput>({\n * model: 'gpt-image-1',\n * prompt: 'A cat',\n * images: [blob1, blob2],\n * });\n * ```\n */\nexport function convertToFormData<T extends Record<string, unknown>>(\n input: T,\n options: { useArrayBrackets?: boolean } = {},\n): FormData {\n const { useArrayBrackets = true } = options;\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(input)) {\n if (value == null) {\n continue;\n }\n\n if (Array.isArray(value)) {\n if (value.length === 1) {\n formData.append(key, value[0] as string | Blob);\n continue;\n }\n\n const arrayKey = useArrayBrackets ? `${key}[]` : key;\n for (const item of value) {\n formData.append(arrayKey, item as string | Blob);\n }\n continue;\n }\n\n formData.append(key, value as string | Blob);\n }\n\n return formData;\n}\n","import { AISDKError } from '@ai-sdk/provider';\n\nconst name = 'AI_DownloadError';\nconst marker = `vercel.ai.error.${name}`;\nconst symbol = Symbol.for(marker);\n\nexport class DownloadError extends AISDKError {\n private readonly [symbol] = true; // used in isInstance\n\n readonly url: string;\n readonly statusCode?: number;\n readonly statusText?: string;\n\n constructor({\n url,\n statusCode,\n statusText,\n cause,\n message = cause == null\n ? `Failed to download ${url}: ${statusCode} ${statusText}`\n : `Failed to download ${url}: ${cause}`,\n }: {\n url: string;\n statusCode?: number;\n statusText?: string;\n message?: string;\n cause?: unknown;\n }) {\n super({ name, message, cause });\n\n this.url = url;\n this.statusCode = statusCode;\n this.statusText = statusText;\n }\n\n static isInstance(error: unknown): error is DownloadError {\n return AISDKError.hasMarker(error, marker);\n }\n}\n","import { DownloadError } from './download-error';\n\n/**\n * Default maximum download size: 2 GiB.\n *\n * `fetch().arrayBuffer()` has ~2x peak memory overhead (undici buffers the\n * body internally, then creates the JS ArrayBuffer), so very large downloads\n * risk exceeding the default V8 heap limit on 64-bit systems and terminating\n * the process with an out-of-memory error.\n *\n * Setting this limit converts an unrecoverable OOM crash into a catchable\n * `DownloadError`.\n */\nexport const DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;\n\n/**\n * Reads a fetch Response body with a size limit to prevent memory exhaustion.\n *\n * Checks the Content-Length header for early rejection, then reads the body\n * incrementally via ReadableStream and aborts with a DownloadError when the\n * limit is exceeded.\n *\n * @param response - The fetch Response to read.\n * @param url - The URL being downloaded (used in error messages).\n * @param maxBytes - Maximum allowed bytes. Defaults to DEFAULT_MAX_DOWNLOAD_SIZE.\n * @returns A Uint8Array containing the response body.\n * @throws DownloadError if the response exceeds maxBytes.\n */\nexport async function readResponseWithSizeLimit({\n response,\n url,\n maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE,\n}: {\n response: Response;\n url: string;\n maxBytes?: number;\n}): Promise<Uint8Array> {\n // Early rejection based on Content-Length header\n const contentLength = response.headers.get('content-length');\n if (contentLength != null) {\n const length = parseInt(contentLength, 10);\n if (!isNaN(length) && length > maxBytes) {\n throw new DownloadError({\n url,\n message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`,\n });\n }\n }\n\n const body = response.body;\n\n // Handle missing body (empty responses)\n if (body == null) {\n return new Uint8Array(0);\n }\n\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n totalBytes += value.length;\n\n if (totalBytes > maxBytes) {\n throw new DownloadError({\n url,\n message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`,\n });\n }\n\n chunks.push(value);\n }\n } finally {\n try {\n await reader.cancel();\n } finally {\n reader.releaseLock();\n }\n }\n\n // Concatenate chunks into a single Uint8Array\n const result = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n}\n","import { DownloadError } from './download-error';\n\n/**\n * Validates that a URL is safe to download from, blocking private/internal addresses\n * to prevent SSRF attacks.\n *\n * @param url - The URL string to validate.\n * @throws DownloadError if the URL is unsafe.\n */\nexport function validateDownloadUrl(url: string): void {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new DownloadError({\n url,\n message: `Invalid URL: ${url}`,\n });\n }\n\n // data: URLs are inline content, so they do not trigger a network fetch or SSRF risk.\n if (parsed.protocol === 'data:') {\n return;\n }\n\n // Only allow http and https network protocols\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new DownloadError({\n url,\n message: `URL scheme must be http, https, or data, got ${parsed.protocol}`,\n });\n }\n\n const hostname = parsed.hostname;\n\n // Block empty hostname\n if (!hostname) {\n throw new DownloadError({\n url,\n message: `URL must have a hostname`,\n });\n }\n\n // Block localhost and .local domains\n if (\n hostname === 'localhost' ||\n hostname.endsWith('.local') ||\n hostname.endsWith('.localhost')\n ) {\n throw new DownloadError({\n url,\n message: `URL with hostname ${hostname} is not allowed`,\n });\n }\n\n // Check for IPv6 addresses (enclosed in brackets in URLs)\n if (hostname.startsWith('[') && hostname.endsWith(']')) {\n const ipv6 = hostname.slice(1, -1);\n if (isPrivateIPv6(ipv6)) {\n throw new DownloadError({\n url,\n message: `URL with IPv6 address ${hostname} is not allowed`,\n });\n }\n return;\n }\n\n // Check for IPv4 addresses\n if (isIPv4(hostname)) {\n if (isPrivateIPv4(hostname)) {\n throw new DownloadError({\n url,\n message: `URL with IP address ${hostname} is not allowed`,\n });\n }\n return;\n }\n}\n\nfunction isIPv4(hostname: string): boolean {\n const parts = hostname.split('.');\n if (parts.length !== 4) return false;\n return parts.every(part => {\n const num = Number(part);\n return (\n Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part\n );\n });\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const parts = ip.split('.').map(Number);\n const [a, b] = parts;\n\n // 0.0.0.0/8\n if (a === 0) return true;\n // 10.0.0.0/8\n if (a === 10) return true;\n // 127.0.0.0/8\n if (a === 127) return true;\n // 169.254.0.0/16\n if (a === 169 && b === 254) return true;\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n\n return false;\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n\n // ::1 (loopback)\n if (normalized === '::1') return true;\n // :: (unspecified)\n if (normalized === '::') return true;\n\n // Check for IPv4-mapped addresses (::ffff:x.x.x.x or ::ffff:HHHH:HHHH)\n if (normalized.startsWith('::ffff:')) {\n const mappedPart = normalized.slice(7);\n // Dotted-decimal form: ::ffff:127.0.0.1\n if (isIPv4(mappedPart)) {\n return isPrivateIPv4(mappedPart);\n }\n // Hex form: ::ffff:7f00:1 (URL parser normalizes to this)\n const hexParts = mappedPart.split(':');\n if (hexParts.length === 2) {\n const high = parseInt(hexParts[0], 16);\n const low = parseInt(hexParts[1], 16);\n if (!isNaN(high) && !isNaN(low)) {\n const a = (high >> 8) & 0xff;\n const b = high & 0xff;\n const c = (low >> 8) & 0xff;\n const d = low & 0xff;\n return isPrivateIPv4(`${a}.${b}.${c}.${d}`);\n }\n }\n }\n\n // fc00::/7 (unique local addresses - fc00:: and fd00::)\n if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true;\n\n // fe80::/10 (link-local)\n if (normalized.startsWith('fe80')) return true;\n\n return false;\n}\n","import { DownloadError } from './download-error';\nimport {\n readResponseWithSizeLimit,\n DEFAULT_MAX_DOWNLOAD_S