([
"kimi",
"qiniu",
"novita",
+ "mimo",
])
/**
@@ -57,6 +77,18 @@ export function normalizeMiniMaxBaseURL(rawUrl: string): {
return { baseURL, isAnthropicCompatible }
}
+export function isAihubmixStandardBaseURL(
+ rawUrl: string | null | undefined,
+): boolean {
+ if (!rawUrl) return true
+
+ const baseURL = rawUrl.replace(/\/+$/, "")
+ return (
+ baseURL === "https://aihubmix.com" ||
+ baseURL === "https://aihubmix.com/v1"
+ )
+}
+
export interface ClientOverrides {
provider?: string | null
baseUrl?: string | null
@@ -69,6 +101,9 @@ export interface ClientOverrides {
awsSessionToken?: string | null
// Vertex AI config
vertexApiKey?: string | null // Express Mode API key
+ // baseUrl is the server's own _BASE_URL (the admin panel's Test),
+ // not one a user chose: no redirect guard
+ trustedBaseUrl?: boolean
// Custom headers (e.g., for EdgeOne cookie auth)
headers?: Record
// Custom env var name(s) for server models
@@ -77,31 +112,6 @@ export interface ClientOverrides {
baseUrlEnv?: string
}
-// Providers that can be selected from client settings
-const ALLOWED_CLIENT_PROVIDERS: ProviderName[] = [
- "openai",
- "anthropic",
- "google",
- "vertexai",
- "azure",
- "bedrock",
- "openrouter",
- "deepseek",
- "siliconflow",
- "sglang",
- "gateway",
- "edgeone",
- "ollama",
- "doubao",
- "modelscope",
- "glm",
- "qwen",
- "qiniu",
- "kimi",
- "minimax",
- "novita",
-]
-
// Bedrock provider options for Anthropic beta features
const BEDROCK_ANTHROPIC_BETA = {
bedrock: {
@@ -109,11 +119,6 @@ const BEDROCK_ANTHROPIC_BETA = {
},
}
-// Direct Anthropic API headers for beta features
-const ANTHROPIC_BETA_HEADERS = {
- "anthropic-beta": "fine-grained-tool-streaming-2025-05-14",
-}
-
/**
* Resolve baseURL based on whether user is providing their own API key.
* When user provides their own API key, we should NOT fall back to server's
@@ -218,13 +223,33 @@ function parseIntSafe(
return parsed
}
+/**
+ * GOOGLE_TOP_K and GOOGLE_TOP_P. They are call settings, so they go on the
+ * model through a middleware: as Google provider options they were dropped.
+ */
+function googleSamplingSettings(): { topK?: number; topP?: number } {
+ const settings: { topK?: number; topP?: number } = {}
+ const topK = parseIntSafe(process.env.GOOGLE_TOP_K, "GOOGLE_TOP_K", 1, 100)
+ if (topK) settings.topK = topK
+ if (process.env.GOOGLE_TOP_P) {
+ const topP = Number.parseFloat(process.env.GOOGLE_TOP_P)
+ if (Number.isNaN(topP) || topP < 0 || topP > 1) {
+ throw new Error(
+ `GOOGLE_TOP_P must be a number between 0 and 1, got: ${process.env.GOOGLE_TOP_P}`,
+ )
+ }
+ settings.topP = topP
+ }
+ return settings
+}
+
/**
* Build provider-specific options from environment variables
* Supports various AI SDK providers with their unique configuration options
*
* Environment variables:
- * - OPENAI_REASONING_EFFORT: OpenAI reasoning effort level (minimal/low/medium/high) - for o1/o3/o4/gpt-5
- * - OPENAI_REASONING_SUMMARY: OpenAI reasoning summary (auto/detailed) - auto-enabled for o1/o3/o4/gpt-5
+ * - OPENAI_REASONING_EFFORT: OpenAI reasoning effort level (minimal/low/medium/high) - for the o-series and gpt-5 or later
+ * - OPENAI_REASONING_SUMMARY: OpenAI reasoning summary (auto/detailed) - auto-enabled for the o-series and gpt-5 or later
* - ANTHROPIC_THINKING_BUDGET_TOKENS: Anthropic thinking budget in tokens (1024-64000)
* - ANTHROPIC_THINKING_TYPE: Anthropic thinking type (enabled)
* - GOOGLE_THINKING_BUDGET: Google Gemini 2.5 thinking budget in tokens (1024-100000)
@@ -248,14 +273,9 @@ function buildProviderOptions(
const reasoningEffort = process.env.OPENAI_REASONING_EFFORT
const reasoningSummary = process.env.OPENAI_REASONING_SUMMARY
- // OpenAI reasoning models (o1, o3, o4, gpt-5) need reasoningSummary to return thoughts
- if (
- modelId &&
- (modelId.includes("o1") ||
- modelId.includes("o3") ||
- modelId.includes("o4") ||
- modelId.includes("gpt-5"))
- ) {
+ // Reasoning models (the o-series, gpt-5 and later) need
+ // reasoningSummary to return thoughts
+ if (modelId && /^(o\d|gpt-([5-9]|[1-9]\d))/.test(modelId)) {
options.openai = {
// Auto-enable reasoning summary for reasoning models
// Use 'auto' as default since not all models support 'detailed'
@@ -312,7 +332,6 @@ function buildProviderOptions(
}
case "google": {
- const reasoningEffort = process.env.GOOGLE_REASONING_EFFORT
const thinkingBudgetVal = parseIntSafe(
process.env.GOOGLE_THINKING_BUDGET,
"GOOGLE_THINKING_BUDGET",
@@ -351,47 +370,6 @@ function buildProviderOptions(
}
options.google = { thinkingConfig }
- } else if (reasoningEffort) {
- options.google = {
- reasoningEffort: reasoningEffort as
- | "low"
- | "medium"
- | "high",
- }
- }
-
- // Keep existing Google options
- const options_obj: Record = {}
- const candidateCount = parseIntSafe(
- process.env.GOOGLE_CANDIDATE_COUNT,
- "GOOGLE_CANDIDATE_COUNT",
- 1,
- 8,
- )
- if (candidateCount) {
- options_obj.candidateCount = candidateCount
- }
- const topK = parseIntSafe(
- process.env.GOOGLE_TOP_K,
- "GOOGLE_TOP_K",
- 1,
- 100,
- )
- if (topK) {
- options_obj.topK = topK
- }
- if (process.env.GOOGLE_TOP_P) {
- const topP = Number.parseFloat(process.env.GOOGLE_TOP_P)
- if (Number.isNaN(topP) || topP < 0 || topP > 1) {
- throw new Error(
- `GOOGLE_TOP_P must be a number between 0 and 1, got: ${process.env.GOOGLE_TOP_P}`,
- )
- }
- options_obj.topP = topP
- }
-
- if (Object.keys(options_obj).length > 0) {
- options.google = { ...options.google, ...options_obj }
}
break
}
@@ -511,24 +489,6 @@ function buildProviderOptions(
break
}
- case "deepseek":
- case "openrouter":
- case "siliconflow":
- case "sglang":
- case "gateway":
- case "modelscope":
- case "doubao":
- case "minimax":
- case "glm":
- case "qwen":
- case "kimi":
- case "qiniu":
- case "novita": {
- // These providers don't have reasoning configs in AI SDK yet
- // Gateway passes through to underlying providers which handle their own configs
- break
- }
-
default:
break
}
@@ -537,7 +497,7 @@ function buildProviderOptions(
}
// Map of provider to required environment variable
-const PROVIDER_ENV_VARS: Record = {
+export const PROVIDER_ENV_VARS: Record = {
bedrock: null, // AWS SDK auto-uses IAM role on AWS, or env vars locally
openai: "OPENAI_API_KEY",
anthropic: "ANTHROPIC_API_KEY",
@@ -546,6 +506,7 @@ const PROVIDER_ENV_VARS: Record = {
azure: "AZURE_API_KEY",
ollama: null, // No credentials needed for local Ollama
openrouter: "OPENROUTER_API_KEY",
+ aihubmix: "AIHUBMIX_API_KEY",
deepseek: "DEEPSEEK_API_KEY",
siliconflow: "SILICONFLOW_API_KEY",
sglang: "SGLANG_API_KEY",
@@ -559,6 +520,8 @@ const PROVIDER_ENV_VARS: Record = {
kimi: "KIMI_API_KEY",
minimax: "MINIMAX_API_KEY",
novita: "NOVITA_API_KEY",
+ mimo: "MIMO_API_KEY",
+ atlascloud: "ATLASCLOUD_API_KEY",
}
/**
@@ -573,7 +536,15 @@ function detectProvider(): ProviderName | null {
// Skip ollama - it doesn't require credentials
continue
}
- if (process.env[envVar]) {
+ // Anthropic accepts ANTHROPIC_AUTH_TOKEN (Bearer auth) as alternative to ANTHROPIC_API_KEY
+ const hasCredential =
+ provider === "anthropic"
+ ? !!(
+ process.env.ANTHROPIC_API_KEY ||
+ process.env.ANTHROPIC_AUTH_TOKEN
+ )
+ : !!process.env[envVar]
+ if (hasCredential) {
// Azure requires additional config (baseURL or resourceName)
if (provider === "azure") {
const hasBaseUrl = !!process.env.AZURE_BASE_URL
@@ -602,6 +573,7 @@ function detectProvider(): ProviderName | null {
function validateProviderCredentials(
provider: ProviderName,
customApiKeyEnv?: string | string[],
+ customBaseUrlEnv?: string,
): void {
// Handle array of env var names - at least one must be set
if (Array.isArray(customApiKeyEnv)) {
@@ -615,18 +587,34 @@ function validateProviderCredentials(
return
}
- // Use custom env var name if provided, otherwise use default
- const requiredVar = customApiKeyEnv || PROVIDER_ENV_VARS[provider]
- if (requiredVar && !process.env[requiredVar]) {
- throw new Error(
- `${requiredVar} environment variable is required for ${provider} provider. ` +
- `Please set it in your .env.local file.`,
+ // Anthropic accepts ANTHROPIC_AUTH_TOKEN (Bearer auth) as alternative to ANTHROPIC_API_KEY
+ if (provider === "anthropic" && !customApiKeyEnv) {
+ const hasCredential = !!(
+ process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN
)
+ if (!hasCredential) {
+ throw new Error(
+ `Either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN environment variable is required for anthropic provider. ` +
+ `Please set one in your .env.local file.`,
+ )
+ }
+ } else {
+ // Use custom env var name if provided, otherwise use default
+ const requiredVar = customApiKeyEnv || PROVIDER_ENV_VARS[provider]
+ if (requiredVar && !process.env[requiredVar]) {
+ throw new Error(
+ `${requiredVar} environment variable is required for ${provider} provider. ` +
+ `Please set it in your .env.local file.`,
+ )
+ }
}
- // Azure requires either AZURE_BASE_URL or AZURE_RESOURCE_NAME in addition to API key
+ // Azure requires either AZURE_BASE_URL or AZURE_RESOURCE_NAME in addition
+ // to API key, or a server model's own URL variable (an admin panel entry)
if (provider === "azure") {
- const hasBaseUrl = !!process.env.AZURE_BASE_URL
+ const hasBaseUrl =
+ !!process.env.AZURE_BASE_URL ||
+ !!(customBaseUrlEnv && process.env[customBaseUrlEnv])
const hasResourceName = !!process.env.AZURE_RESOURCE_NAME
if (!hasBaseUrl && !hasResourceName) {
throw new Error(
@@ -637,40 +625,189 @@ function validateProviderCredentials(
}
}
+/** AWS's Bedrock endpoint for a region, as the Bedrock SDK builds it */
+function bedrockRuntimeUrl(region: string): string {
+ const suffix =
+ [
+ ["cn-", "amazonaws.com.cn"],
+ ["us-iso-", "c2s.ic.gov"],
+ ["us-isob-", "sc2s.sgov.gov"],
+ ["eu-isoe-", "cloud.adc-e.uk"],
+ ["us-isof-", "csp.hci.ic.gov"],
+ ["eusc-", "amazonaws.eu"],
+ ].find(([prefix]) => region.startsWith(prefix))?.[1] ?? "amazonaws.com"
+ return `https://bedrock-runtime.${region}.${suffix}`
+}
+
/**
- * Get the AI model based on environment variables
- *
- * Environment variables:
- * - AI_PROVIDER: The provider to use (bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek, siliconflow, sglang, gateway, modelscope)
- * - AI_MODEL: The model ID/name for the selected provider
- *
- * Provider-specific env vars:
- * - OPENAI_API_KEY: OpenAI API key
- * - OPENAI_BASE_URL: Custom OpenAI-compatible endpoint (optional)
- * - ANTHROPIC_API_KEY: Anthropic API key
- * - GOOGLE_GENERATIVE_AI_API_KEY: Google API key
- * - AZURE_RESOURCE_NAME, AZURE_API_KEY: Azure OpenAI credentials
- * - AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: AWS Bedrock credentials
- * - OLLAMA_BASE_URL: Ollama server URL (optional, defaults to https://ollama.com/api)
- * - OPENROUTER_API_KEY: OpenRouter API key
- * - DEEPSEEK_API_KEY: DeepSeek API key
- * - DEEPSEEK_BASE_URL: DeepSeek endpoint (optional)
- * - SILICONFLOW_API_KEY: SiliconFlow API key
- * - SILICONFLOW_BASE_URL: SiliconFlow endpoint (optional, defaults to https://api.siliconflow.cn/v1)
- * - SGLANG_API_KEY: SGLang API key
- * - SGLANG_BASE_URL: SGLang endpoint (optional)
- * - MODELSCOPE_API_KEY: ModelScope API key
- * - MODELSCOPE_BASE_URL: ModelScope endpoint (optional)
+ * Providers whose SDK has the official endpoint built in. The others are
+ * OpenAI-compatible APIs (or Anthropic) that are called at
+ * PROVIDER_INFO.defaultBaseUrl unless a base URL is configured.
*/
-export function getAIModel(overrides?: ClientOverrides): ModelConfig {
+const SDK_KNOWS_ENDPOINT = new Set([
+ "openai",
+ "google",
+ "azure",
+ "openrouter",
+ "gateway",
+ "deepseek",
+])
+
+/** Where and how to call a provider, once credentials are resolved */
+interface Endpoint {
+ apiKey?: string
+ baseURL?: string
+ headers?: Record
+ fetch?: typeof fetch
+ authToken?: string // Anthropic Bearer auth
+ resourceName?: string // Azure
+ // baseURL comes from the settings or env, not the provider's default
+ configuredBaseURL?: boolean
+}
+
+/**
+ * An OpenAI-compatible chat model. includeUsage asks for token usage in the
+ * stream, which quota tracking needs. Some of these models write their
+ * reasoning inside tags; that text becomes reasoning, not reply.
+ */
+function compatibleModel(
+ provider: ProviderName,
+ modelId: string,
+ e: Endpoint,
+): LanguageModel {
+ const model = createOpenAICompatible({
+ name: provider,
+ apiKey: e.apiKey,
+ baseURL: e.baseURL ?? "",
+ ...(e.headers && { headers: e.headers }),
+ ...(e.fetch && { fetch: e.fetch }),
+ includeUsage: true,
+ })(modelId)
+ return wrapLanguageModel({
+ model,
+ middleware: extractReasoningMiddleware({ tagName: "think" }),
+ })
+}
+
+/** Create the model for a provider. Credentials are already resolved. */
+function createModel(
+ provider: ProviderName,
+ modelId: string,
+ e: Endpoint,
+): LanguageModel {
+ const opts = {
+ apiKey: e.apiKey,
+ ...(e.baseURL && { baseURL: e.baseURL }),
+ ...(e.fetch && { fetch: e.fetch }),
+ }
+ switch (provider) {
+ case "openai": {
+ const openaiProvider = createOpenAI(opts)
+ // A configured base URL is usually a proxy that only has Chat
+ // Completions; without one the Responses API is used, which
+ // returns reasoning for the o-series and gpt-5 or later
+ return e.configuredBaseURL
+ ? openaiProvider.chat(modelId)
+ : openaiProvider(modelId)
+ }
+ case "anthropic":
+ // The provider streams tool input per tool (eager_input_streaming),
+ // which replaced the fine-grained-tool-streaming beta header
+ return createAnthropic({
+ ...(e.authToken
+ ? { authToken: e.authToken }
+ : { apiKey: e.apiKey }),
+ baseURL: e.baseURL,
+ ...(e.fetch && { fetch: e.fetch }),
+ })(modelId)
+ case "google": {
+ const model = createGoogleGenerativeAI(opts)(modelId)
+ const sampling = googleSamplingSettings()
+ return Object.keys(sampling).length > 0
+ ? wrapLanguageModel({
+ model,
+ middleware: defaultSettingsMiddleware({
+ settings: sampling,
+ }),
+ })
+ : model
+ }
+ case "azure":
+ // baseURL takes precedence over resourceName per SDK behavior
+ return createAzure({
+ ...opts,
+ ...(!e.baseURL &&
+ e.resourceName && { resourceName: e.resourceName }),
+ })(modelId)
+ case "openrouter":
+ return createOpenRouter(opts)(modelId)
+ case "gateway":
+ // Without a key or URL the SDK uses Vercel's endpoint and OIDC
+ return createGateway(opts)(modelId)
+ case "deepseek":
+ case "kimi":
+ case "mimo":
+ // Kimi and MiMo return reasoning_content like DeepSeek and need it
+ // passed back in multi-turn tool calls (MiMo answers 400 otherwise)
+ return createDeepSeek(opts)(modelId)
+ case "doubao": {
+ // DeepSeek and Kimi models on Doubao use reasoning_content too
+ const lower = modelId.toLowerCase()
+ return lower.includes("deepseek") || lower.includes("kimi")
+ ? createDeepSeek(opts)(modelId)
+ : compatibleModel(provider, modelId, e)
+ }
+ case "aihubmix":
+ return isAihubmixStandardBaseURL(e.baseURL)
+ ? createAihubmix({
+ apiKey: e.apiKey,
+ appCode: AIHUBMIX_APP_CODE,
+ })(modelId)
+ : compatibleModel(provider, modelId, e)
+ case "minimax": {
+ const { baseURL, isAnthropicCompatible } = normalizeMiniMaxBaseURL(
+ e.baseURL as string,
+ )
+ return isAnthropicCompatible
+ ? createAnthropic({
+ apiKey: e.apiKey,
+ baseURL,
+ ...(e.fetch && { fetch: e.fetch }),
+ })(modelId)
+ : compatibleModel(provider, modelId, { ...e, baseURL })
+ }
+ default:
+ // siliconflow, sglang, modelscope, glm, qwen, qiniu, novita,
+ // atlascloud, edgeone
+ return compatibleModel(provider, modelId, e)
+ }
+}
+
+/**
+ * Get the AI model for a chat request: the client's own provider and
+ * credentials, or the server's (AI_PROVIDER, AI_MODEL and each provider's
+ * _API_KEY / _BASE_URL, see env.example). The settings test
+ * button uses the same function, so a passing test means the chat works.
+ */
+export function getAIModel(clientOverrides?: ClientOverrides): ModelConfig {
+ // Drop an endpoint path pasted along with the client's base URL
+ const overrides = clientOverrides?.baseUrl
+ ? {
+ ...clientOverrides,
+ baseUrl: normalizeBaseUrl(clientOverrides.baseUrl),
+ }
+ : clientOverrides
// SECURITY: Prevent SSRF attacks (GHSA-9qf7-mprq-9qgm)
// If a custom baseUrl is provided, an API key MUST also be provided.
// This prevents attackers from redirecting server API keys to malicious endpoints.
// Exception: EdgeOne doesn't require API keys.
// Ollama is exempt only when no server OLLAMA_API_KEY is configured;
// when it IS configured, the outer guard also enforces client apiKey for custom baseUrls.
+ // A trusted URL is the server's own (the admin Test of an entry without
+ // one), not a user's
if (
overrides?.baseUrl &&
+ !overrides?.trustedBaseUrl &&
!overrides?.apiKey &&
!(overrides?.provider === "vertexai" && overrides?.vertexApiKey) &&
overrides?.provider !== "edgeone" &&
@@ -689,8 +826,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
(overrides?.provider === "vertexai" && overrides?.vertexApiKey))
)
- // Use client override if provided, otherwise fall back to env vars
- const modelId = overrides?.modelId || process.env.AI_MODEL
+ // Use client override if provided, otherwise fall back to env vars.
+ // AI_MODEL may be comma-separated (multi-model fallback); pick the first.
+ const envModel = process.env.AI_MODEL?.split(",")[0]?.trim() || undefined
+ const modelId = overrides?.modelId || envModel
if (!modelId) {
if (isClientOverride) {
@@ -707,13 +846,9 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
let provider: ProviderName
if (overrides?.provider) {
// Validate client-provided provider
- if (
- !ALLOWED_CLIENT_PROVIDERS.includes(
- overrides.provider as ProviderName,
- )
- ) {
+ if (!Object.hasOwn(PROVIDER_INFO, overrides.provider)) {
throw new Error(
- `Invalid provider: ${overrides.provider}. Allowed providers: ${ALLOWED_CLIENT_PROVIDERS.join(", ")}`,
+ `Invalid provider: ${overrides.provider}. Allowed providers: ${Object.keys(PROVIDER_INFO).join(", ")}`,
)
}
provider = overrides.provider as ProviderName
@@ -731,51 +866,75 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
.map(([p]) => p)
if (configured.length === 0) {
+ const keys = Object.entries(PROVIDER_ENV_VARS)
+ .filter(([, envVar]) => envVar)
+ .map(([p, envVar]) => `- ${envVar} for ${p}`)
throw new Error(
`No AI provider configured. Please set one of the following API keys in your .env.local file:\n` +
- `- AI_GATEWAY_API_KEY for Vercel AI Gateway\n` +
- `- DEEPSEEK_API_KEY for DeepSeek\n` +
- `- OPENAI_API_KEY for OpenAI\n` +
- `- ANTHROPIC_API_KEY for Anthropic\n` +
- `- GOOGLE_GENERATIVE_AI_API_KEY for Google\n` +
- `- AWS_ACCESS_KEY_ID for Bedrock\n` +
- `- OPENROUTER_API_KEY for OpenRouter\n` +
- `- AZURE_API_KEY for Azure\n` +
- `- SILICONFLOW_API_KEY for SiliconFlow\n` +
- `- SGLANG_API_KEY for SGLang\n` +
- `- MODELSCOPE_API_KEY for ModelScope\n` +
+ `${keys.join("\n")}\n` +
+ `- AWS_ACCESS_KEY_ID for bedrock\n` +
`Or set AI_PROVIDER=ollama for local Ollama.`,
)
- } else {
- throw new Error(
- `Multiple AI providers configured (${configured.join(", ")}). ` +
- `Please set AI_PROVIDER to specify which one to use.`,
- )
}
+ throw new Error(
+ `Multiple AI providers configured (${configured.join(", ")}). ` +
+ `Please set AI_PROVIDER to specify which one to use.`,
+ )
}
}
+ if (!Object.hasOwn(PROVIDER_INFO, provider)) {
+ throw new Error(
+ `Unknown AI provider: ${provider}. Supported providers: ${Object.keys(PROVIDER_INFO).join(", ")}`,
+ )
+ }
// Only validate server credentials if client isn't providing their own API key
if (!isClientOverride) {
- validateProviderCredentials(provider, overrides?.apiKeyEnv)
+ validateProviderCredentials(
+ provider,
+ overrides?.apiKeyEnv,
+ overrides?.baseUrlEnv,
+ )
}
console.log(`[AI Provider] Initializing ${provider} with model: ${modelId}`)
- let model: any
- let providerOptions: any
- let headers: Record | undefined
-
+ // Requests to a base URL the client chose must not follow redirects
+ const guardedFetch =
+ overrides?.baseUrl && !overrides.trustedBaseUrl
+ ? redirectGuardedFetch()
+ : undefined
// Build provider-specific options from environment variables
- const customProviderOptions = buildProviderOptions(provider, modelId)
+ let providerOptions = buildProviderOptions(provider, modelId)
+ let model: LanguageModel
switch (provider) {
case "bedrock": {
// Use client-provided credentials if available, otherwise fall back to IAM/env vars
const hasClientCredentials =
overrides?.awsAccessKeyId && overrides?.awsSecretAccessKey
+ // Keys from the admin panel. The ADMIN_ names keep them out of the
+ // default AWS credential chain, which other clients such as the
+ // DynamoDB quota manager use with their own credentials.
+ const adminAccessKeyId = process.env.ADMIN_AWS_ACCESS_KEY_ID
+ const adminSecretAccessKey = process.env.ADMIN_AWS_SECRET_ACCESS_KEY
+ // The region becomes part of the endpoint's host name, so a
+ // request's region must be a region name, or it could send the
+ // server's credentials to another host
+ if (
+ overrides?.awsRegion &&
+ !/^[a-z]{2,4}(-[a-z]+)+-\d{1,2}$/.test(overrides.awsRegion)
+ ) {
+ throw Object.assign(
+ new Error(`Invalid AWS region "${overrides.awsRegion}"`),
+ { statusCode: 400 },
+ )
+ }
const bedrockRegion =
- overrides?.awsRegion || process.env.AWS_REGION || "us-west-2"
+ overrides?.awsRegion ||
+ process.env.ADMIN_AWS_REGION ||
+ process.env.AWS_REGION ||
+ "us-west-2"
const bedrockProvider = hasClientCredentials
? createAmazonBedrock({
@@ -785,11 +944,25 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
...(overrides?.awsSessionToken && {
sessionToken: overrides.awsSessionToken,
}),
+ // Without an apiKey the SDK reads the server's
+ // AWS_BEARER_TOKEN_BEDROCK, which wins over the keys
+ apiKey: "",
+ // Without a baseURL it reads the server's
+ // AWS_ENDPOINT_URL_BEDROCK_RUNTIME / AWS_ENDPOINT_URL
+ baseURL: bedrockRuntimeUrl(bedrockRegion),
})
- : createAmazonBedrock({
- region: bedrockRegion,
- credentialProvider: fromNodeProviderChain(),
- })
+ : adminAccessKeyId && adminSecretAccessKey
+ ? createAmazonBedrock({
+ region: bedrockRegion,
+ accessKeyId: adminAccessKeyId,
+ secretAccessKey: adminSecretAccessKey,
+ // The keys the admin panel's Test button checked
+ apiKey: "",
+ })
+ : createAmazonBedrock({
+ region: bedrockRegion,
+ credentialProvider: fromNodeProviderChain(),
+ })
model = bedrockProvider(modelId)
// Add Anthropic beta options if using Claude models via Bedrock
if (modelId.includes("anthropic.claude")) {
@@ -797,94 +970,20 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
providerOptions = {
bedrock: {
...BEDROCK_ANTHROPIC_BETA.bedrock,
- ...(customProviderOptions?.bedrock || {}),
+ ...(providerOptions?.bedrock || {}),
},
}
- } else if (customProviderOptions) {
- providerOptions = customProviderOptions
}
break
}
- case "openai": {
- const apiKey = resolveApiKey(overrides, "OPENAI_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "OPENAI_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
- if (baseURL) {
- // Custom base URL = third-party proxy, use Chat Completions API
- // for compatibility (most proxies don't support /responses endpoint)
- const customOpenAI = createOpenAI({ apiKey, baseURL })
- model = customOpenAI.chat(modelId)
- } else if (overrides?.apiKey) {
- // Custom API key but official OpenAI endpoint, use Responses API
- // to support reasoning for gpt-5, o1, o3, o4 models
- const customOpenAI = createOpenAI({ apiKey })
- model = customOpenAI(modelId)
- } else {
- model = openai(modelId)
- }
- break
- }
-
- case "anthropic": {
- const apiKey = resolveApiKey(overrides, "ANTHROPIC_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "ANTHROPIC_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- "https://api.anthropic.com/v1",
- )
- const customProvider = createAnthropic({
- apiKey,
- baseURL,
- headers: ANTHROPIC_BETA_HEADERS,
- })
- model = customProvider(modelId)
- // Add beta headers for fine-grained tool streaming
- headers = ANTHROPIC_BETA_HEADERS
- break
- }
-
- case "google": {
- const apiKey = resolveApiKey(
- overrides,
- "GOOGLE_GENERATIVE_AI_API_KEY",
- )
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "GOOGLE_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
- if (baseURL || overrides?.apiKey) {
- const customGoogle = createGoogleGenerativeAI({
- apiKey,
- ...(baseURL && { baseURL }),
- })
- model = customGoogle(modelId)
- } else {
- model = google(modelId)
- }
- break
- }
case "vertexai": {
// Express Mode: Use API key for authentication
- const vertexApiKey =
- overrides?.vertexApiKey || process.env.GOOGLE_VERTEX_API_KEY
+ // SECURITY: a client base URL only ever gets the client's key, so the
+ // server's GOOGLE_VERTEX_API_KEY is never sent to a client-chosen host
+ const vertexApiKey = overrides?.baseUrl
+ ? overrides.vertexApiKey
+ : overrides?.vertexApiKey || process.env.GOOGLE_VERTEX_API_KEY
if (!vertexApiKey) {
throw new Error(
@@ -893,421 +992,241 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
)
}
- // Support custom base URL from env or client override
- const baseURL =
- overrides?.baseUrl || process.env.GOOGLE_VERTEX_BASE_URL
-
- const vertexProvider = createVertex({
+ // Support custom base URL from env or client override.
+ // A client key only goes to the client's URL or the official one.
+ const baseURL = resolveBaseURL(
+ overrides?.vertexApiKey,
+ overrides?.baseUrl,
+ process.env.GOOGLE_VERTEX_BASE_URL,
+ )
+ model = createVertex({
apiKey: vertexApiKey,
...(baseURL && { baseURL }),
- })
- model = vertexProvider(modelId)
- break
- }
-
- case "azure": {
- const apiKey = resolveApiKey(overrides, "AZURE_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(overrides, "AZURE_BASE_URL")
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
- // Only use server's resourceName if user is NOT providing their own API key
- const resourceName = overrides?.apiKey
- ? undefined
- : process.env.AZURE_RESOURCE_NAME
- // Azure requires either baseURL or resourceName to construct the endpoint
- // resourceName constructs: https://{resourceName}.openai.azure.com/openai/v1{path}
- if (baseURL || resourceName || overrides?.apiKey) {
- const customAzure = createAzure({
- apiKey,
- // baseURL takes precedence over resourceName per SDK behavior
- ...(baseURL && { baseURL }),
- ...(!baseURL && resourceName && { resourceName }),
- })
- model = customAzure(modelId)
- } else {
- model = azure(modelId)
- }
+ ...(guardedFetch && { fetch: guardedFetch }),
+ })(modelId)
break
}
case "ollama": {
- const baseURL = overrides?.baseUrl || process.env.OLLAMA_BASE_URL
// SECURITY: When client provides a custom base URL, only use
// client-provided API key. Never fall back to server OLLAMA_API_KEY
// to prevent leaking server credentials to user-controlled endpoints.
const apiKey = overrides?.baseUrl
? overrides?.apiKey || undefined
: resolveApiKey(overrides, "OLLAMA_API_KEY")
- if (baseURL || apiKey) {
- const customOllama = createOllama({
- ...(baseURL && { baseURL }),
- ...(apiKey && {
- headers: { Authorization: `Bearer ${apiKey}` },
- }),
- })
- model = customOllama(modelId)
- } else {
- model = ollama(modelId)
- }
- break
- }
-
- case "openrouter": {
- const apiKey = resolveApiKey(overrides, "OPENROUTER_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "OPENROUTER_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
- const openrouter = createOpenRouter({
- apiKey,
+ // Like other providers, a user's key never goes to the server's
+ // base URL: without a URL of their own it goes to Ollama Cloud.
+ // The server's key goes to OLLAMA_BASE_URL (or a server model's
+ // own variable), else to the SDK's local default: the desktop
+ // app's "Ollama (Local)" preset puts its key field there too.
+ const baseURL =
+ overrides?.baseUrl ||
+ (overrides?.apiKey
+ ? PROVIDER_INFO.ollama.defaultBaseUrl
+ : resolveBaseUrlEnv(overrides, "OLLAMA_BASE_URL"))
+ model = createOllama({
...(baseURL && { baseURL }),
+ ...(apiKey && {
+ headers: { Authorization: `Bearer ${apiKey}` },
+ }),
+ ...(guardedFetch && { fetch: guardedFetch }),
+ })(modelId)
+ break
+ }
+
+ case "edgeone":
+ // EdgeOne Pages Edge AI, an OpenAI-compatible API without a key.
+ // The SDK appends /chat/completions to the base URL. Cookies
+ // (eo_token, eo_time) and the access code authenticate the call.
+ model = compatibleModel(provider, modelId, {
+ apiKey: "edgeone",
+ baseURL: overrides?.baseUrl || "/api/edgeai",
+ headers: overrides?.headers,
+ fetch: guardedFetch,
})
- model = openrouter(modelId)
break
- }
- case "deepseek": {
- const apiKey = resolveApiKey(overrides, "DEEPSEEK_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
+ default: {
+ // Every other provider takes an API key and a base URL from
+ // _API_KEY / _BASE_URL (or a server model's apiKeyEnv)
+ const apiKey = resolveApiKey(
overrides,
- "DEEPSEEK_BASE_URL",
+ PROVIDER_ENV_VARS[provider] as string,
)
- const baseURL = resolveBaseURL(
+ const baseUrlEnv =
+ provider === "gateway"
+ ? "AI_GATEWAY_BASE_URL"
+ : `${provider.toUpperCase()}_BASE_URL`
+ // A local default (SGLang's 127.0.0.1) only fills the settings
+ // form; the server must not call its own machine for it. With a
+ // user's key the OpenAI SDK would read the server's
+ // OPENAI_BASE_URL, so name the official endpoint.
+ const defaultUrl = PROVIDER_INFO[provider].defaultBaseUrl
+ const publicDefault = defaultUrl?.startsWith("https://")
+ ? defaultUrl
+ : undefined
+ const configuredBaseURL = resolveBaseURL(
overrides?.apiKey,
overrides?.baseUrl,
- serverBaseUrl,
+ resolveBaseUrlEnv(overrides, baseUrlEnv),
)
- if (baseURL || overrides?.apiKey) {
- const customDeepSeek = createDeepSeek({
- apiKey,
- ...(baseURL && { baseURL }),
- })
- model = customDeepSeek(modelId)
- } else {
- model = deepseek(modelId)
- }
- break
- }
-
- case "siliconflow": {
- const apiKey = resolveApiKey(overrides, "SILICONFLOW_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "SILICONFLOW_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- "https://api.siliconflow.cn/v1",
- )
- const siliconflowProvider = createOpenAI({
- apiKey,
- baseURL,
- })
- model = siliconflowProvider.chat(modelId)
- break
- }
-
- case "sglang": {
- const apiKey = resolveApiKey(overrides, "SGLANG_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "SGLANG_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
-
- const sglangProvider = createOpenAI({
- apiKey,
- ...(baseURL && { baseURL }),
- // Add a custom fetch wrapper to intercept and fix the stream from sglang
- fetch: async (url, options) => {
- const response = await fetch(url, options)
- if (!response.body) {
- return response
- }
-
- // Create a transform stream to fix the non-compliant sglang stream
- let buffer = ""
- const decoder = new TextDecoder()
-
- const transformStream = new TransformStream({
- transform(chunk, controller) {
- buffer += decoder.decode(chunk, { stream: true })
- // Process all complete messages in the buffer
- let messageEndPos
- while (
- (messageEndPos = buffer.indexOf("\n\n")) !== -1
- ) {
- const message = buffer.substring(
- 0,
- messageEndPos,
- )
- buffer = buffer.substring(messageEndPos + 2) // Move past the '\n\n'
-
- if (message.startsWith("data: ")) {
- const jsonStr = message.substring(6).trim()
- if (jsonStr === "[DONE]") {
- controller.enqueue(
- new TextEncoder().encode(
- message + "\n\n",
- ),
- )
- continue
- }
- try {
- const data = JSON.parse(jsonStr)
- const delta = data.choices?.[0]?.delta
-
- if (delta) {
- // Fix 1: remove invalid empty role
- if (delta.role === "") {
- delete delta.role
- }
- // Fix 2: remove non-standard reasoning_content field
- if ("reasoning_content" in delta) {
- delete delta.reasoning_content
- }
- }
-
- // Re-serialize and forward the corrected data with the correct SSE format
- controller.enqueue(
- new TextEncoder().encode(
- `data: ${JSON.stringify(data)}\n\n`,
- ),
- )
- } catch (_e) {
- // If parsing fails, forward the original message to avoid breaking the stream.
- controller.enqueue(
- new TextEncoder().encode(
- message + "\n\n",
- ),
- )
- }
- } else if (message.trim() !== "") {
- // Pass through other message types (e.g., 'event: ...')
- controller.enqueue(
- new TextEncoder().encode(
- message + "\n\n",
- ),
- )
- }
- }
- },
- flush(controller) {
- // If there's anything left in the buffer, forward it.
- if (buffer.trim()) {
- controller.enqueue(
- new TextEncoder().encode(buffer),
- )
- }
- },
- })
-
- const transformedBody =
- response.body.pipeThrough(transformStream)
-
- // Return a new response with the transformed body
- return new Response(transformedBody, {
- status: response.status,
- statusText: response.statusText,
- headers: response.headers,
- })
- },
- })
- model = sglangProvider.chat(modelId)
- break
- }
-
- case "gateway": {
- // Vercel AI Gateway - unified access to multiple AI providers
- // Model format: "provider/model" e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5"
- // See: https://vercel.com/ai-gateway
- const apiKey = resolveApiKey(overrides, "AI_GATEWAY_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "AI_GATEWAY_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- )
- // Only use custom configuration if explicitly set (local dev or custom Gateway)
- // Otherwise undefined → AI SDK uses Vercel default (https://ai-gateway.vercel.sh/v1/ai) + OIDC
- if (baseURL || overrides?.apiKey) {
- const customGateway = createGateway({
- apiKey,
- ...(baseURL && { baseURL }),
- })
- model = customGateway(modelId)
- } else {
- model = gateway(modelId)
- }
- break
- }
-
- case "edgeone": {
- // EdgeOne Pages Edge AI - uses OpenAI-compatible API
- // AI SDK appends /chat/completions to baseURL
- // /api/edgeai + /chat/completions = /api/edgeai/chat/completions
- const baseURL = overrides?.baseUrl || "/api/edgeai"
- const edgeoneProvider = createOpenAI({
- apiKey: "edgeone", // Dummy key - EdgeOne doesn't require API key
- baseURL,
- // Pass cookies for EdgeOne Pages authentication (eo_token, eo_time)
- ...(overrides?.headers && { headers: overrides.headers }),
- })
- model = edgeoneProvider.chat(modelId)
- break
- }
-
- case "doubao": {
- const apiKey = resolveApiKey(overrides, "DOUBAO_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "DOUBAO_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- "https://ark.cn-beijing.volces.com/api/v3",
- )
- const lowerModelId = modelId.toLowerCase()
- // Use DeepSeek provider for DeepSeek/Kimi models, OpenAI for others (multimodal support)
+ const baseURL =
+ configuredBaseURL ||
+ (SDK_KNOWS_ENDPOINT.has(provider) &&
+ !(provider === "openai" && overrides?.apiKey)
+ ? undefined
+ : publicDefault)
+ // With a user's Azure key the SDK would read the server's
+ // AZURE_RESOURCE_NAME
if (
- lowerModelId.includes("deepseek") ||
- lowerModelId.includes("kimi")
+ !baseURL &&
+ (!SDK_KNOWS_ENDPOINT.has(provider) ||
+ (provider === "azure" && overrides?.apiKey))
) {
- const doubaoProvider = createDeepSeek({
- apiKey,
- baseURL,
- })
- model = doubaoProvider(modelId)
- } else {
- const doubaoProvider = createOpenAI({
- apiKey,
- baseURL,
- })
- model = doubaoProvider.chat(modelId)
- }
- break
- }
-
- case "modelscope": {
- const apiKey = resolveApiKey(overrides, "MODELSCOPE_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "MODELSCOPE_BASE_URL",
- )
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- "https://api-inference.modelscope.cn/v1",
- )
- const modelscopeProvider = createOpenAI({
- apiKey,
- baseURL,
- })
- model = modelscopeProvider.chat(modelId)
- break
- }
-
- case "minimax": {
- const apiKey = resolveApiKey(overrides, "MINIMAX_API_KEY")
- const serverBaseUrl = resolveBaseUrlEnv(
- overrides,
- "MINIMAX_BASE_URL",
- )
- const rawBaseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- serverBaseUrl,
- PROVIDER_INFO.minimax.defaultBaseUrl,
- )
-
- if (!rawBaseURL) {
throw new Error(
- "MiniMax base URL could not be resolved. Set MINIMAX_BASE_URL or configure a base URL in settings.",
+ `${PROVIDER_INFO[provider].label} needs a base URL. Add it in the model settings.`,
)
}
-
- const { baseURL, isAnthropicCompatible } =
- normalizeMiniMaxBaseURL(rawBaseURL)
-
- if (isAnthropicCompatible) {
- const minimax = createAnthropic({ apiKey, baseURL })
- model = minimax.chat(modelId)
- } else {
- const minimax = createOpenAI({ apiKey, baseURL })
- model = minimax.chat(modelId)
- }
- break
- }
-
- case "glm":
- case "qwen":
- case "qiniu":
- case "kimi":
- case "novita": {
- const envVar = PROVIDER_ENV_VARS[provider]
- if (!envVar) {
- throw new Error(
- `API key environment variable not defined for provider: ${provider}`,
- )
- }
- const apiKey = resolveApiKey(overrides, envVar)
- const baseURL = resolveBaseURL(
- overrides?.apiKey,
- overrides?.baseUrl,
- resolveBaseUrlEnv(
- overrides,
- `${provider.toUpperCase()}_BASE_URL`,
- ),
- PROVIDER_INFO[provider]?.defaultBaseUrl,
- )
- const customProvider = createOpenAI({
+ model = createModel(provider, modelId, {
apiKey,
baseURL,
+ configuredBaseURL: !!configuredBaseURL,
+ fetch: guardedFetch,
+ // Bearer auth for Anthropic when there is no API key
+ authToken:
+ provider === "anthropic" && !apiKey
+ ? process.env.ANTHROPIC_AUTH_TOKEN
+ : undefined,
+ // Only the server's own resource; a client key needs its URL
+ resourceName:
+ provider === "azure" && !overrides?.apiKey
+ ? process.env.AZURE_RESOURCE_NAME
+ : undefined,
})
- model = customProvider.chat(modelId)
- break
}
-
- default:
- throw new Error(
- `Unknown AI provider: ${provider}. Supported providers: bedrock, openai, anthropic, google, azure, ollama, openrouter, deepseek, siliconflow, sglang, gateway, edgeone, doubao, modelscope, glm, qwen, qiniu, kimi, minimax, novita`,
- )
}
- // Apply provider-specific options for all providers except bedrock (which has special handling)
- if (customProviderOptions && provider !== "bedrock" && !providerOptions) {
- providerOptions = customProviderOptions
- }
-
- return { model, providerOptions, headers, modelId, provider }
+ return { model, providerOptions, modelId, provider }
}
/**
- * Check if a model supports prompt caching.
- * Currently only Claude models on Bedrock support prompt caching.
+ * The deployment's EdgeOne Pages function, as an absolute URL (the SDK
+ * needs one). EdgeOne serves functions by their folder from the site root,
+ * so Next's base path does not apply.
+ */
+export function edgeOneEndpoint(req: Request): string {
+ const origin = req.headers.get("origin") || new URL(req.url).origin
+ return `${origin}/api/edgeai`
+}
+
+/**
+ * The server's _BASE_URL for a provider, which getAIModel uses for a
+ * server model without a URL variable of its own (an admin panel entry
+ * without a URL). None for Bedrock and EdgeOne. Ollama and Vertex AI share
+ * one variable with the panel, which writes an entry's URL into it: an
+ * entry without a URL gets the environment's value once saved (before a
+ * save the variable may still hold the entry's previous URL), and Ollama
+ * without one goes to the SDK's local default.
+ */
+export function globalBaseUrl(provider: ProviderName): string | undefined {
+ if (provider === "ollama") {
+ return getEnvFallback("OLLAMA_BASE_URL") || "http://127.0.0.1:11434/api"
+ }
+ if (provider === "vertexai") {
+ return getEnvFallback("GOOGLE_VERTEX_BASE_URL") || undefined
+ }
+ if (provider === "bedrock" || provider === "edgeone") return undefined
+ // Azure set up by resource name only: the URL the SDK builds from it
+ if (
+ provider === "azure" &&
+ !process.env.AZURE_BASE_URL &&
+ process.env.AZURE_RESOURCE_NAME
+ ) {
+ return `https://${process.env.AZURE_RESOURCE_NAME}.openai.azure.com/openai`
+ }
+ const name =
+ provider === "gateway"
+ ? "AI_GATEWAY_BASE_URL"
+ : `${provider.toUpperCase()}_BASE_URL`
+ return process.env[name] || undefined
+}
+
+/** The provider of the server's own config: AI_PROVIDER, or the one with a key */
+export function getServerProvider(): ProviderName | null {
+ return (process.env.AI_PROVIDER as ProviderName) || detectProvider()
+}
+
+/**
+ * Whether a call made with the caller's own settings runs on an endpoint of
+ * the deployment: its EdgeOne function, the server's keyless Ollama, or an
+ * address on the server's network (which ignores a dummy key header).
+ * Bedrock and EdgeOne never use a client base URL. Never in the desktop
+ * app, where every endpoint is the user's. clientBaseUrl: normalized.
+ */
+export async function usesServerEndpoint(
+ provider: ProviderName | null | undefined,
+ clientBaseUrl: string,
+ apiKey: string | null | undefined,
+): Promise {
+ if (process.env.NEXT_AI_DRAWIO_DESKTOP === "1") return false
+ if (provider === "edgeone") return true
+ if (provider === "ollama" && !clientBaseUrl && !apiKey) return true
+ return (
+ provider !== "bedrock" &&
+ !!clientBaseUrl &&
+ (await isPrivateUrl(clientBaseUrl))
+ )
+}
+
+/**
+ * Whether the call is paid for by the server's own credentials (env keys or
+ * IAM role) rather than credentials sent with the request. Mirrors which key
+ * each branch of getAIModel ends up using.
+ */
+export function usesServerCredentials(
+ provider: ProviderName,
+ overrides?: ClientOverrides,
+): boolean {
+ // The desktop app's local server holds the user's own preset keys
+ if (process.env.NEXT_AI_DRAWIO_DESKTOP === "1") return false
+ // Cleaned like getAIModel does: "/" means no base URL
+ const baseUrl = normalizeBaseUrl(overrides?.baseUrl ?? "")
+ switch (provider) {
+ case "bedrock":
+ return !(overrides?.awsAccessKeyId && overrides?.awsSecretAccessKey)
+ case "vertexai":
+ return !overrides?.vertexApiKey
+ case "edgeone":
+ // The platform's own endpoint, no key involved
+ return false
+ case "ollama":
+ // Only a server key costs money; a keyless local server or the
+ // client's own server does not
+ return (
+ !baseUrl &&
+ !overrides?.apiKey &&
+ !!(overrides?.apiKeyEnv || process.env.OLLAMA_API_KEY)
+ )
+ default:
+ return !overrides?.apiKey
+ }
+}
+
+/**
+ * Prompt cache breakpoint for Claude, set on a message's providerOptions.
+ * Each provider reads only its own key; OpenRouter also reads the
+ * anthropic one.
+ */
+export const CACHE_POINT = {
+ bedrock: { cachePoint: { type: "default" } },
+ anthropic: { cacheControl: { type: "ephemeral" } },
+}
+
+/**
+ * Check if a model supports prompt caching: Claude models, on Bedrock,
+ * the Anthropic API or OpenRouter (see CACHE_POINT).
*/
export function supportsPromptCaching(modelId: string): boolean {
- // Bedrock prompt caching is supported for Claude models
return (
modelId.includes("claude") ||
modelId.includes("anthropic") ||
@@ -1316,75 +1235,19 @@ export function supportsPromptCaching(modelId: string): boolean {
)
}
-/**
- * Check if a model supports image/vision input.
- * Some models silently drop image parts without error (AI SDK warning only).
- */
-export function supportsImageInput(modelId: string): boolean {
- const lowerModelId = modelId.toLowerCase()
-
- // Helper to check if model has vision capability indicator
- const hasVisionIndicator =
- lowerModelId.includes("vision") || lowerModelId.includes("vl")
-
- // Models that DON'T support image/vision input (unless vision variant)
- // Kimi K2 doesn't support images, but K2.5 does
- // Only block kimi-k2 specifically, not other Kimi models
- if (
- (lowerModelId.includes("kimi-k2") ||
- lowerModelId.includes("kimi_k2")) &&
- !hasVisionIndicator &&
- !lowerModelId.includes("2.5") &&
- !lowerModelId.includes("k2.5")
- ) {
- return false
- }
-
- // Moonshot text models (moonshot-v1 series are text-only)
- if (lowerModelId.includes("moonshot-v1") && !hasVisionIndicator) {
- return false
- }
-
- // MiniMax text models (MiniMax-M2.x series are text-only)
- if (lowerModelId.includes("minimax") && !hasVisionIndicator) {
- return false
- }
-
- // DeepSeek text models (not vision variants)
- if (lowerModelId.includes("deepseek") && !hasVisionIndicator) {
- return false
- }
-
- // Qwen text models (not vision variants like qwen-vl)
- // qwen3.5-plus is a vision model
- if (
- lowerModelId.includes("qwen") &&
- !hasVisionIndicator &&
- !lowerModelId.includes("qwen3.5-plus") &&
- !lowerModelId.includes("qwen3.5-flash")
- ) {
- return false
- }
-
- // GLM text models (not vision variants)
- // GLM vision models: glm-4v, glm-4v-9b, glm-4.1v-9b-thinking
- if (lowerModelId.includes("glm") && !hasVisionIndicator) {
- if (!/[\d.]v/.test(lowerModelId)) {
- return false
- }
- }
-
- // Default: assume model supports images
- return true
-}
-
/**
* Get the AI model for diagram validation.
* Uses VALIDATION_MODEL env var if set, otherwise falls back to AI_MODEL.
- * Throws if the model doesn't support image input.
+ *
+ * Note: we no longer guess whether the model supports image input from its
+ * name — that heuristic misfired on newer models (see issue #874). If a
+ * configured validation model can't handle images, the API call simply errors
+ * and the validate-diagram route falls back to "valid".
*/
export function getValidationModel(): ReturnType["model"] {
- const modelId = process.env.VALIDATION_MODEL || process.env.AI_MODEL
+ // AI_MODEL may be comma-separated (multi-model fallback); pick the first.
+ const envFallback = process.env.AI_MODEL?.split(",")[0]?.trim() || undefined
+ const modelId = process.env.VALIDATION_MODEL || envFallback
if (!modelId) {
throw new Error(
@@ -1392,12 +1255,17 @@ export function getValidationModel(): ReturnType["model"] {
)
}
- if (!supportsImageInput(modelId)) {
- throw new Error(
- `Validation requires a vision-capable model. Model "${modelId}" does not support image input.`,
- )
- }
+ // A default set in the admin panel becomes AI_PROVIDER/AI_MODEL, but its key
+ // lives in an ADMIN_-prefixed env var. Point at it the way the chat route
+ // does for server models, or the standard env var is required instead.
+ const panelDefault = adminProvidersToConfig(
+ loadAdminProviders(),
+ ).providers.find((p) => p.default && p.provider === process.env.AI_PROVIDER)
- const { model } = getAIModel({ modelId })
+ const { model } = getAIModel({
+ modelId,
+ apiKeyEnv: panelDefault?.apiKeyEnv,
+ baseUrlEnv: panelDefault?.baseUrlEnv,
+ })
return model
}
diff --git a/lib/cached-responses.ts b/lib/cached-responses.ts
index 8b8375f3..c2b474f7 100644
--- a/lib/cached-responses.ts
+++ b/lib/cached-responses.ts
@@ -1,6 +1,8 @@
export interface CachedResponse {
promptText: string
hasImage: boolean
+ // Name of the bundled example file the prompt is sent with
+ fileName?: string
xml: string
}
@@ -254,6 +256,7 @@ export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
{
promptText: "Replicate this in aws style",
hasImage: true,
+ fileName: "architecture.png",
xml: `
@@ -318,6 +321,7 @@ export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
{
promptText: "Replicate this flowchart.",
hasImage: true,
+ fileName: "example.png",
xml: `
@@ -379,6 +383,7 @@ export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
{
promptText: "Summarize this paper as a diagram",
hasImage: true,
+ fileName: "chain-of-thought.txt",
xml: `
@@ -879,14 +884,19 @@ export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
},
]
+// Examples that come with a file only match when that exact example file is
+// attached, so a user's own file with the same prompt still goes to the model.
+// Callers that can't tell file names (the server) only get text-only examples.
export function findCachedResponse(
promptText: string,
hasImage: boolean,
+ fileName?: string,
): CachedResponse | undefined {
return CACHED_EXAMPLE_RESPONSES.find(
(c) =>
c.promptText === promptText &&
c.hasImage === hasImage &&
+ (!c.fileName || c.fileName === fileName) &&
c.xml !== "",
)
}
diff --git a/lib/chat-helpers.ts b/lib/chat-helpers.ts
index e9c054f1..ddd8a730 100644
--- a/lib/chat-helpers.ts
+++ b/lib/chat-helpers.ts
@@ -6,25 +6,37 @@ export const MAX_FILE_SIZE = 2 * 1024 * 1024 // 2MB
export const MAX_FILES = 5
// Helper function to validate file parts in messages
+// Checks every message, since history is sent to the model too
export function validateFileParts(messages: any[]): {
valid: boolean
error?: string
} {
- const lastMessage = messages[messages.length - 1]
- const fileParts =
- lastMessage?.parts?.filter((p: any) => p.type === "file") || []
+ for (const message of messages) {
+ const fileParts =
+ message?.parts?.filter((p: any) => p.type === "file") || []
- if (fileParts.length > MAX_FILES) {
- return {
- valid: false,
- error: `Too many files. Maximum ${MAX_FILES} allowed.`,
+ if (fileParts.length > MAX_FILES) {
+ return {
+ valid: false,
+ error: `Too many files. Maximum ${MAX_FILES} allowed.`,
+ }
}
- }
- for (const filePart of fileParts) {
- // Data URLs format: data:image/png;base64,
- // Base64 increases size by ~33%, so we check the decoded size
- if (filePart.url?.startsWith("data:")) {
+ for (const filePart of fileParts) {
+ // The client sends files inline. Any other URL would be downloaded
+ // by the server (AI SDK does that for models without URL support).
+ if (
+ typeof filePart.url !== "string" ||
+ !filePart.url.startsWith("data:")
+ ) {
+ return {
+ valid: false,
+ error: "Files must be uploaded inline as data URLs.",
+ }
+ }
+
+ // Data URLs format: data:image/png;base64,
+ // Base64 increases size by ~33%, so we check the decoded size
const base64Data = filePart.url.split(",")[1]
if (base64Data) {
const sizeInBytes = Math.ceil((base64Data.length * 3) / 4)
@@ -41,49 +53,83 @@ export function validateFileParts(messages: any[]): {
return { valid: true }
}
-// Helper function to check if diagram is minimal/empty
-export function isMinimalDiagram(xml: string): boolean {
- const stripped = xml.replace(/\s/g, "")
- return !stripped.includes('id="2"')
+// A tool-call input providers accept: a non-empty JSON object
+function isValidToolInput(input: unknown): boolean {
+ return !!input && typeof input === "object" && Object.keys(input).length > 0
}
// Helper function to replace historical tool call XML with placeholders
// This reduces token usage and forces LLM to rely on the current diagram XML (source of truth)
-// Also fixes invalid/undefined inputs from interrupted streaming
+// Tool calls with invalid inputs are left for dropInvalidToolCalls to remove
export function replaceHistoricalToolInputs(messages: any[]): any[] {
return messages.map((msg) => {
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
return msg
}
- const replacedContent = msg.content
- .map((part: any) => {
- if (part.type === "tool-call") {
- const toolName = part.toolName
- // Fix invalid/undefined inputs from interrupted streaming
- if (
- !part.input ||
- typeof part.input !== "object" ||
- Object.keys(part.input).length === 0
- ) {
- // Skip tool calls with invalid inputs entirely
- return null
- }
- if (
- toolName === "display_diagram" ||
- toolName === "edit_diagram"
- ) {
- return {
- ...part,
- input: {
- placeholder:
- "[XML content replaced - see current diagram XML in system context]",
- },
- }
- }
+ const replacedContent = msg.content.map((part: any) => {
+ if (
+ part.type === "tool-call" &&
+ isValidToolInput(part.input) &&
+ (part.toolName === "display_diagram" ||
+ part.toolName === "edit_diagram")
+ ) {
+ return {
+ ...part,
+ input: {
+ placeholder:
+ "[XML content replaced - see current diagram XML in system context]",
+ },
}
- return part
- })
- .filter(Boolean) // Remove null entries (invalid tool calls)
+ }
+ return part
+ })
return { ...msg, content: replacedContent }
})
}
+
+// Remove tool-calls with invalid inputs (from failed repair or interrupted streaming),
+// together with their tool-results: providers reject a result whose call is missing.
+// Messages left empty are removed too (Bedrock rejects empty content arrays).
+export function dropInvalidToolCalls(messages: any[]): any[] {
+ const droppedIds = new Set()
+ return messages
+ .map((msg) => {
+ if (!Array.isArray(msg.content)) return msg
+ const content = msg.content.filter((part: any) => {
+ if (
+ msg.role === "assistant" &&
+ part.type === "tool-call" &&
+ !isValidToolInput(part.input)
+ ) {
+ console.warn(
+ `[chat-helpers] Dropping tool-call with invalid input:`,
+ { toolName: part.toolName, input: part.input },
+ )
+ droppedIds.add(part.toolCallId)
+ return false
+ }
+ // Results always come after their call, so the id is known by now
+ return !(
+ part.type === "tool-result" &&
+ droppedIds.has(part.toolCallId)
+ )
+ })
+ return { ...msg, content }
+ })
+ .filter((msg) => !Array.isArray(msg.content) || msg.content.length > 0)
+}
+
+// Fix common LLM JSON mistakes in tool-call input before jsonrepair runs
+export function fixToolInputJson(input: string): string {
+ return (
+ input
+ // Inconsistent quote escaping in XML attributes inside JSON strings:
+ // y="-20\" (opening quote unescaped, closing escaped) becomes y=\"-20\".
+ // Must run before the key fix below, which would rewrite the `="`.
+ .replace(/(\w+)="([^"]*?)\\"/g, '$1=\\"$2\\"')
+ // `:=` instead of `: `
+ .replace(/:=/g, ": ")
+ // `"key"= "` instead of `"key": "`, only for JSON keys
+ .replace(/"(\w+)"\s*=\s*"/g, '"$1": "')
+ )
+}
diff --git a/lib/deprecated-params.ts b/lib/deprecated-params.ts
new file mode 100644
index 00000000..2d9d2fda
--- /dev/null
+++ b/lib/deprecated-params.ts
@@ -0,0 +1,94 @@
+import { wrapLanguageModel } from "ai"
+import { rejectionText } from "@/lib/output-token-limit"
+
+type WrappedModel = ReturnType
+
+/**
+ * Claude 4.7 and later answer a non-default temperature, top_p or top_k,
+ * and the extended thinking budget (thinking type "enabled"), with a 400.
+ * TEMPERATURE and the *_THINKING_BUDGET_TOKENS settings send exactly these.
+ */
+const DEPRECATED_PARAM =
+ /`?(?:temperature|top_p|top_k)`? is deprecated for this model|"?thinking\.type\.enabled"? is not supported/i
+
+interface CallParams {
+ temperature?: number
+ topP?: number
+ topK?: number
+ providerOptions?: Record | undefined>
+}
+
+// What these models take instead of a budget. Without display "summarized"
+// they think but send no thinking text to show.
+const ADAPTIVE_THINKING = { type: "adaptive", display: "summarized" }
+
+/** Turn a thinking config of type "enabled" stored under key into adaptive */
+function adaptiveThinking(
+ options: Record | undefined,
+ key: string,
+): Record | undefined {
+ const config = options?.[key] as { type?: string } | undefined
+ if (config?.type !== "enabled") return options
+ return { ...options, [key]: ADAPTIVE_THINKING }
+}
+
+/**
+ * The params without the settings newer Claude models reject, or null when
+ * the error is about something else or there is nothing to change. The
+ * model then runs with its default sampling, and a thinking budget becomes
+ * adaptive thinking.
+ */
+export function withoutDeprecatedParams(
+ error: unknown,
+ params: T,
+): T | null {
+ const text = rejectionText(error)
+ if (!text || !DEPRECATED_PARAM.test(text)) return null
+
+ const { temperature, topP, topK, ...rest } = params
+ const options = params.providerOptions
+ const anthropic = adaptiveThinking(options?.anthropic, "thinking")
+ const bedrock = adaptiveThinking(options?.bedrock, "reasoningConfig")
+ const changed =
+ temperature !== undefined ||
+ topP !== undefined ||
+ topK !== undefined ||
+ anthropic !== options?.anthropic ||
+ bedrock !== options?.bedrock
+ if (!changed) return null
+
+ return {
+ ...rest,
+ ...(options && {
+ providerOptions: {
+ ...options,
+ ...(anthropic && { anthropic }),
+ ...(bedrock && { bedrock }),
+ },
+ }),
+ } as T
+}
+
+/** Retry the stream once without the settings newer Claude models reject. */
+export function withDeprecatedParamsFallback(
+ model: WrappedModel,
+): WrappedModel {
+ return wrapLanguageModel({
+ model,
+ middleware: {
+ specificationVersion: "v3",
+ async wrapStream({ doStream, params, model: inner }) {
+ try {
+ return await doStream()
+ } catch (error) {
+ const retry = withoutDeprecatedParams(error, params)
+ if (!retry) throw error
+ console.warn(
+ "[model params] Rejected sampling or thinking settings, retrying with default sampling and adaptive thinking",
+ )
+ return await inner.doStream(retry)
+ }
+ },
+ },
+ })
+}
diff --git a/lib/drawio-themes.ts b/lib/drawio-themes.ts
new file mode 100644
index 00000000..7eac1a65
--- /dev/null
+++ b/lib/drawio-themes.ts
@@ -0,0 +1,17 @@
+export const DRAWIO_THEMES = [
+ "kennedy",
+ "atlas",
+ "dark",
+ "min",
+ "sketch",
+ "simple",
+] as const
+
+export type DrawioTheme = (typeof DRAWIO_THEMES)[number]
+
+export function isDrawioTheme(value: unknown): value is DrawioTheme {
+ return (
+ typeof value === "string" &&
+ (DRAWIO_THEMES as readonly string[]).includes(value)
+ )
+}
diff --git a/lib/dynamo-quota-manager.ts b/lib/dynamo-quota-manager.ts
index 981868b4..f423c8ed 100644
--- a/lib/dynamo-quota-manager.ts
+++ b/lib/dynamo-quota-manager.ts
@@ -64,10 +64,13 @@ interface QuotaCheckResult {
* Check all quotas and increment request count atomically.
* Uses composite key (PK=user, SK=date) for per-day tracking.
* Each day automatically gets a new item - no explicit reset needed.
+ * A request limit of 0 means none; increment 0 checks the limits without
+ * counting a request (the screenshot check).
*/
export async function checkAndIncrementRequest(
ip: string,
limits: QuotaLimits,
+ increment = 1,
): Promise {
// Skip if quota tracking not enabled
if (!client || !TABLE) {
@@ -99,7 +102,7 @@ export async function checkAndIncrementRequest(
attribute_not_exists(tpmCount) OR tpmCount < :tpmLimit)
`,
ExpressionAttributeValues: {
- ":one": { N: "1" },
+ ":one": { N: String(increment) },
":minute": { S: currentMinute },
":reqLimit": { N: String(limits.requests || 999999) },
":tokenLimit": { N: String(limits.tokens || 999999) },
diff --git a/lib/i18n/dictionaries/en.json b/lib/i18n/dictionaries/en.json
index 0c665700..fa5ee712 100644
--- a/lib/i18n/dictionaries/en.json
+++ b/lib/i18n/dictionaries/en.json
@@ -34,7 +34,8 @@
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
- "qiniu": "Qiniu"
+ "qiniu": "Qiniu",
+ "mimo": "MiMo (Xiaomi)"
},
"chat": {
"placeholder": "Describe your diagram or upload a file...",
@@ -76,8 +77,7 @@
"creativeDescription": "Draw something fun and creative",
"cachedNote": "Examples are cached for instant response",
"mcpServer": "MCP Server",
- "mcpDescription": "Use in Claude Desktop, VS Code & Cursor",
- "preview": "PREVIEW"
+ "mcpDescription": "Use in Claude Desktop, VS Code & Cursor"
},
"settings": {
"title": "Settings",
@@ -103,10 +103,12 @@
"theme": "Theme",
"themeDescription": "Dark/Light mode for interface and DrawIO canvas.",
"drawioStyle": "DrawIO Style",
- "drawioStyleDescription": "Canvas style:",
- "switchTo": "Switch to",
- "minimal": "Minimal",
- "sketch": "Sketch",
+ "drawioStyleDescription": "Canvas style",
+ "themeDefault": "Default",
+ "themeDark": "Dark",
+ "themeMinimal": "Minimal",
+ "themeSketch": "Sketch",
+ "themeSimple": "Simple",
"diagramStyle": "Diagram Style",
"diagramStyleDescription": "Toggle between minimal and styled diagram output.",
"sendShortcut": "Send Shortcut",
@@ -130,6 +132,8 @@
"customSystemMessage": "Custom System Message",
"customSystemMessageDescription": "Add custom instructions appended to the AI's system prompt.",
"customSystemMessagePlaceholder": "e.g., Always use blue color scheme for diagrams...",
+ "maxOutputTokens": "Max Output Tokens",
+ "maxOutputTokensDescription": "Budget for one reply, shared by thinking and the diagram XML. Raise it if the AI keeps thinking and no diagram appears. Leave empty for the default.",
"panelVisibility": "Lobby Panels",
"panelVisibilityDescription": "Choose which panels to show on the chat lobby.",
"showRecentChats": "Recent Chats",
@@ -174,9 +178,6 @@
"networkError": "Network error. Please check your connection.",
"retryLimit": "Auto-retry limit reached ({max}). Please try again manually.",
"continuationRetryLimit": "Continuation retry limit reached ({max}). The diagram may be too complex.",
- "validationFailed": "Diagram validation failed. Please try regenerating.",
- "malformedXml": "AI generated invalid diagram XML. Please try regenerating.",
- "failedToProcess": "Failed to process diagram. Please try regenerating.",
"sessionCorrupted": "Session data was corrupted. Starting fresh.",
"failedToSave": "Failed to save messages to localStorage",
"failedToRestore": "Failed to restore from localStorage",
@@ -184,7 +185,25 @@
"failedToExport": "Error fetching chart data",
"failedToLoadExample": "Error loading example image",
"failedToRecordFeedback": "Failed to record your feedback. Please try again.",
- "storageUpdateFailed": "Chat cleared but browser storage could not be updated"
+ "storageUpdateFailed": "Chat cleared but browser storage could not be updated",
+ "sessionSaveFailed": "Could not save this chat. Browser storage may be full: delete old chats from history and try again.",
+ "sessionSaveFailedLeave": "Could not save this chat. Browser storage may be full. You can go on without saving it, then delete old chats from the list in the new chat.",
+ "continueWithoutSaving": "Continue without saving",
+ "llm": {
+ "invalid_api_key": "The provider rejected the API key. Check it in model settings.",
+ "forbidden": "The provider refused the request. The key may not have access to this model or region.",
+ "model_not_found": "The provider does not know this model. Check the model ID in model settings.",
+ "insufficient_quota": "The provider account has no credit or quota left.",
+ "rate_limited": "The provider is limiting requests. Wait a moment and try again.",
+ "context_too_long": "The conversation is too long for this model. Start a new chat or pick a model with a larger context.",
+ "images_unsupported": "This model doesn't support image input.",
+ "tools_unsupported": "This model doesn't support tool calls, which drawing needs. Pick another model.",
+ "output_truncated": "The output was cut off before the diagram was complete. Try a simpler request or raise the output limit in settings.",
+ "provider_unavailable": "The provider is having problems. Try again later.",
+ "cannot_connect": "Could not reach the provider. Check the base URL and your network.",
+ "timeout": "The provider did not answer in time.",
+ "openModelSettings": "Open model settings"
+ }
},
"quota": {
"dailyLimit": "Daily Quota Reached",
@@ -231,6 +250,7 @@
"reasoning": {
"thinking": "Thinking...",
"thoughtFor": "Thought for {duration} seconds",
+ "thoughtForOne": "Thought for 1 second",
"thoughtBrief": "Thought for a few seconds"
},
"dev": {
@@ -367,9 +387,15 @@
"enterSecretKey": "Enter your secret access key",
"baseUrl": "Base URL",
"optional": "(optional)",
+ "getApiKey": "Get API key",
+ "fetchModels": "Fetch models from the provider",
+ "noTools": "no tool calls",
+ "mayNotDraw": "models.dev lists no tool call support for this model, so it may not be able to draw.",
+ "requestUrl": "Requests go to {url}",
"baseUrlWithExample": "Base URL (optional, e.g. {example})",
"customEndpoint": "Custom endpoint URL",
"minimaxBaseUrlHint": "Use /anthropic for Anthropic-compatible API (recommended), or /v1 for OpenAI-compatible API",
+ "mimoBaseUrlHint": "Default works with pay-as-you-go keys (sk-...). Token Plan subscribers (tp-... keys) must set https://token-plan-cn.xiaomimimo.com/v1",
"models": "Models",
"customModelId": "Custom model ID...",
"allAdded": "All added",
@@ -401,6 +427,152 @@
"showUnvalidatedModels": "Show unvalidated models",
"allModelsShown": "All models are shown (including unvalidated)",
"unvalidatedModelWarning": "This model has not been validated",
- "serverDefaultModel": "Server default model"
+ "serverDefaultModel": "Server default model",
+ "showValue": "Show value",
+ "hideValue": "Hide value"
+ },
+ "admin": {
+ "title": "Admin Settings",
+ "loginPrompt": "Enter the admin password (the ADMIN_PASSWORD environment variable) to manage server settings.",
+ "password": "Password",
+ "signIn": "Sign In",
+ "signingIn": "Signing In…",
+ "loginFailed": "Login failed",
+ "precedence": "File overrides env · env overrides defaults",
+ "notWritable": "The settings file is not writable on this deployment (serverless platforms have no persistent disk). Settings are shown read-only — configure via environment variables instead.",
+ "settingGroups": "Setting groups",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "enableGroup": "Enable {group}",
+ "unsavedChanges": "Unsaved changes",
+ "saved": "Settings saved. Changes apply immediately.",
+ "saveFailed": "Save failed. Check your connection and try again.",
+ "invalidSettings": "Some settings are invalid.",
+ "discard": "Discard",
+ "saveChanges": "Save Changes",
+ "saving": "Saving…",
+ "sourceSaved": "Saved",
+ "sourceEnv": "Env",
+ "sourceSavedTitle": "Set in the admin settings file",
+ "sourceEnvTitle": "Set by an environment variable",
+ "restartRequired": "Restart Required",
+ "modified": "Modified",
+ "notSet": "Not set",
+ "savedReplace": "Saved ({hint}) — type to replace",
+ "showValue": "Show value",
+ "hideValue": "Hide value",
+ "removeValue": "Remove value",
+ "removeValueTitle": "Remove the stored value",
+ "resetToDefault": "Reset to default",
+ "models": "Models",
+ "modelsDescription": "Server-side providers and models available to all users — no personal API key needed. The default provider's first model is used when users don't pick one.",
+ "addProviderHint": "Add a provider to offer server-side models to all users.",
+ "selectProviderHint": "Select or add a provider to configure its credentials and models.",
+ "addProviderToOfferModels": "Add at least one model to expose this provider to users.",
+ "managedViaEnv": "(managed via env)",
+ "envReadOnly": "Defined in AI_MODELS_CONFIG / ai-models.json — read-only here. Edit the environment configuration to change it.",
+ "defaultModel": "Default Model",
+ "noModelsConfigured": "No models configured",
+ "modelCount": "{count} model",
+ "modelCountPlural": "{count} models",
+ "default": "Default",
+ "setAsDefault": "Set as default provider",
+ "defaultProvider": "Default provider",
+ "modelIdPlaceholder": "Model ID…",
+ "addModel": "Add model",
+ "suggested": "Suggested",
+ "test": "Test",
+ "testOk": "OK ({ms}ms)",
+ "testFailed": "Failed",
+ "removeModel": "Remove {model}",
+ "deleteProviderTitle": "Delete {name}?",
+ "deleteProviderDesc": "Its credentials and models will be removed from the server after you save.",
+ "cancel": "Cancel",
+ "delete": "Delete",
+ "groups": {
+ "generation": {
+ "title": "Generation",
+ "description": "Output parameters applied to all chat requests."
+ },
+ "access": {
+ "title": "Access Control",
+ "description": "Restrict who can use this deployment."
+ },
+ "features": {
+ "title": "Features",
+ "description": "Optional features and security toggles."
+ },
+ "observability": {
+ "title": "Observability",
+ "description": "Langfuse tracing for LLM calls."
+ },
+ "quota": {
+ "title": "Quota & Rate Limits",
+ "description": "Per-IP usage limits. Enforcement requires a DynamoDB table."
+ }
+ },
+ "settings": {
+ "TEMPERATURE": {
+ "label": "Temperature",
+ "description": "Leave unset for reasoning models that reject temperature."
+ },
+ "MAX_OUTPUT_TOKENS": {
+ "label": "Max Output Tokens"
+ },
+ "ACCESS_CODE_LIST": {
+ "label": "Access Codes",
+ "description": "Comma-separated list. Users must enter one to chat. Empty = open access."
+ },
+ "ENABLE_VLM_VALIDATION": {
+ "label": "VLM Diagram Validation",
+ "description": "Visually validate generated diagrams with a vision model."
+ },
+ "VALIDATION_MODEL": {
+ "label": "Validation Model",
+ "description": "Falls back to the default AI model when empty."
+ },
+ "VALIDATION_TIMEOUT": {
+ "label": "Validation Timeout (ms)"
+ },
+ "ENABLE_HISTORY_XML_REPLACE": {
+ "label": "History XML Compression",
+ "description": "Replace old diagram XML in history with placeholders."
+ },
+ "ALLOW_PRIVATE_URLS": {
+ "label": "Allow Private URLs",
+ "description": "Turn off to block requests to private IPs and internal hostnames (SSRF protection)."
+ },
+ "LANGFUSE_PUBLIC_KEY": {
+ "label": "Langfuse Public Key"
+ },
+ "LANGFUSE_SECRET_KEY": {
+ "label": "Langfuse Secret Key"
+ },
+ "LANGFUSE_BASEURL": {
+ "label": "Langfuse Base URL"
+ },
+ "DAILY_REQUEST_LIMIT": {
+ "label": "Daily Request Limit",
+ "description": "Per IP per day."
+ },
+ "DAILY_TOKEN_LIMIT": {
+ "label": "Daily Token Limit",
+ "description": "Per IP per day."
+ },
+ "TPM_LIMIT": {
+ "label": "Tokens Per Minute"
+ },
+ "DYNAMODB_QUOTA_TABLE": {
+ "label": "DynamoDB Table",
+ "description": "Quota enforcement is disabled when empty."
+ },
+ "DYNAMODB_REGION": {
+ "label": "DynamoDB Region"
+ },
+ "QUOTA_TIMEZONE": {
+ "label": "Quota Timezone",
+ "description": "Timezone for the daily reset boundary."
+ }
+ }
}
}
diff --git a/lib/i18n/dictionaries/ja.json b/lib/i18n/dictionaries/ja.json
index 925a26fa..97c5c754 100644
--- a/lib/i18n/dictionaries/ja.json
+++ b/lib/i18n/dictionaries/ja.json
@@ -34,7 +34,8 @@
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
- "qiniu": "Qiniu"
+ "qiniu": "Qiniu",
+ "mimo": "MiMo (Xiaomi)"
},
"chat": {
"placeholder": "ダイアグラムを説明するか、ファイルをアップロード...",
@@ -76,8 +77,7 @@
"creativeDescription": "楽しくてクリエイティブなものを描く",
"cachedNote": "例はキャッシュされ、即座に応答します",
"mcpServer": "MCP サーバー",
- "mcpDescription": "Claude Desktop、VS Code、Cursor で使用",
- "preview": "プレビュー"
+ "mcpDescription": "Claude Desktop、VS Code、Cursor で使用"
},
"settings": {
"title": "設定",
@@ -103,10 +103,12 @@
"theme": "テーマ",
"themeDescription": "インターフェースと DrawIO キャンバスのダーク/ライトモード。",
"drawioStyle": "DrawIO スタイル",
- "drawioStyleDescription": "キャンバススタイル:",
- "switchTo": "切り替え",
- "minimal": "ミニマル",
- "sketch": "スケッチ",
+ "drawioStyleDescription": "キャンバススタイル",
+ "themeDefault": "デフォルト",
+ "themeDark": "ダーク",
+ "themeMinimal": "ミニマル",
+ "themeSketch": "スケッチ",
+ "themeSimple": "シンプル",
"diagramStyle": "ダイアグラムスタイル",
"diagramStyleDescription": "ミニマルとスタイル付きの出力を切り替えます。",
"sendShortcut": "送信ショートカット",
@@ -130,6 +132,8 @@
"customSystemMessage": "カスタムシステムメッセージ",
"customSystemMessageDescription": "AIのシステムプロンプトに追加されるカスタム指示を入力します。",
"customSystemMessagePlaceholder": "例:ダイアグラムには常に青色のカラースキームを使用...",
+ "maxOutputTokens": "最大出力トークン数",
+ "maxOutputTokensDescription": "1回の応答の予算で、思考過程とダイアグラムの XML が共有します。AI が考え続けてダイアグラムが生成されない場合は大きくしてください。空欄ならデフォルト値を使います。",
"panelVisibility": "ロビーパネル",
"panelVisibilityDescription": "チャットロビーに表示するパネルを選択します。",
"showRecentChats": "最近のチャット",
@@ -174,9 +178,6 @@
"networkError": "ネットワークエラー。接続を確認してください。",
"retryLimit": "自動再試行制限に達しました({max})。手動で再試行してください。",
"continuationRetryLimit": "継続再試行制限に達しました({max})。ダイアグラムが複雑すぎる可能性があります。",
- "validationFailed": "ダイアグラムの検証に失敗しました。再生成してみてください。",
- "malformedXml": "AI が無効なダイアグラム XML を生成しました。再生成してみてください。",
- "failedToProcess": "ダイアグラムの処理に失敗しました。再生成してみてください。",
"sessionCorrupted": "セッションデータが破損しました。最初からやり直します。",
"failedToSave": "localStorage へのメッセージの保存に失敗しました",
"failedToRestore": "localStorage からの復元に失敗しました",
@@ -184,7 +185,25 @@
"failedToExport": "チャートデータの取得エラー",
"failedToLoadExample": "例の画像の読み込みエラー",
"failedToRecordFeedback": "フィードバックの記録に失敗しました。もう一度お試しください。",
- "storageUpdateFailed": "チャットはクリアされましたが、ブラウザストレージを更新できませんでした"
+ "storageUpdateFailed": "チャットはクリアされましたが、ブラウザストレージを更新できませんでした",
+ "sessionSaveFailed": "このチャットを保存できませんでした。ブラウザのストレージがいっぱいの可能性があります。履歴から古いチャットを削除して、もう一度お試しください。",
+ "sessionSaveFailedLeave": "このチャットを保存できませんでした。ブラウザのストレージがいっぱいの可能性があります。保存せずに続けて、新しいチャットの一覧から古いチャットを削除できます。",
+ "continueWithoutSaving": "保存せずに続ける",
+ "llm": {
+ "invalid_api_key": "プロバイダーが API キーを拒否しました。モデル設定で確認してください。",
+ "forbidden": "プロバイダーがリクエストを拒否しました。このキーにはこのモデルまたはリージョンの利用権限がない可能性があります。",
+ "model_not_found": "プロバイダーがこのモデルを認識できません。モデル設定でモデル ID を確認してください。",
+ "insufficient_quota": "プロバイダーのアカウントの残高または利用枠がなくなりました。",
+ "rate_limited": "プロバイダーがリクエスト数を制限しています。少し待ってから再試行してください。",
+ "context_too_long": "会話がこのモデルで扱える長さを超えています。新しいチャットを始めるか、より長いコンテキストに対応したモデルを選んでください。",
+ "images_unsupported": "このモデルは画像入力に対応していません。",
+ "tools_unsupported": "このモデルはツール呼び出しに対応していません。作図にはツール呼び出しが必要です。別のモデルを選んでください。",
+ "output_truncated": "ダイアグラムが完成する前に出力が途中で切れました。リクエストを簡単にするか、設定で出力上限を上げてください。",
+ "provider_unavailable": "プロバイダーで問題が発生しています。しばらくしてから再試行してください。",
+ "cannot_connect": "プロバイダーに接続できません。Base URL とネットワークを確認してください。",
+ "timeout": "プロバイダーから時間内に応答がありませんでした。",
+ "openModelSettings": "モデル設定を開く"
+ }
},
"quota": {
"dailyLimit": "1日の割当量に達しました",
@@ -231,6 +250,7 @@
"reasoning": {
"thinking": "考え中...",
"thoughtFor": "{duration} 秒考えました",
+ "thoughtForOne": "1 秒考えました",
"thoughtBrief": "数秒考えました"
},
"dev": {
@@ -321,9 +341,15 @@
"enterSecretKey": "シークレットアクセスキーを入力",
"baseUrl": "ベース URL",
"optional": "(オプション)",
+ "getApiKey": "API キーを取得",
+ "fetchModels": "プロバイダーからモデル一覧を取得",
+ "noTools": "ツール呼び出し非対応",
+ "mayNotDraw": "models.dev によると、このモデルはツール呼び出しに対応していないため、作図できない可能性があります。",
+ "requestUrl": "リクエスト先: {url}",
"baseUrlWithExample": "ベース URL(オプション、例: {example})",
"customEndpoint": "カスタムエンドポイント URL",
"minimaxBaseUrlHint": "/anthropic で Anthropic 互換 API(推奨)、または /v1 で OpenAI 互換 API を使用",
+ "mimoBaseUrlHint": "デフォルトは従量課金キー(sk-...)用です。Token Plan 加入者(tp-... キー)は https://token-plan-cn.xiaomimimo.com/v1 を設定してください",
"models": "モデル",
"customModelId": "カスタムモデル ID...",
"allAdded": "すべて追加済み",
@@ -355,7 +381,9 @@
"showUnvalidatedModels": "未検証のモデルを表示",
"allModelsShown": "すべてのモデルを表示(未検証を含む)",
"unvalidatedModelWarning": "このモデルは検証されていません",
- "serverDefaultModel": "サーバーデフォルトモデル"
+ "serverDefaultModel": "サーバーデフォルトモデル",
+ "showValue": "値を表示",
+ "hideValue": "値を非表示"
},
"templates": {
"title": "マイテンプレート",
@@ -402,5 +430,149 @@
"importNoFile": "JSON ファイルを選択してください",
"importFailed": "インポートに失敗しました:{error}",
"importSuccess": "{imported} 件インポート、{skipped} 件の重複をスキップしました"
+ },
+ "admin": {
+ "title": "管理者設定",
+ "loginPrompt": "サーバー設定を管理するには、管理者パスワード(ADMIN_PASSWORD 環境変数)を入力してください。",
+ "password": "パスワード",
+ "signIn": "ログイン",
+ "signingIn": "ログイン中…",
+ "loginFailed": "ログインに失敗しました",
+ "precedence": "ファイルが環境変数を上書き · 環境変数がデフォルトを上書き",
+ "notWritable": "このデプロイ環境では設定ファイルに書き込めません(サーバーレス環境には永続ディスクがありません)。設定は読み取り専用で表示されます——代わりに環境変数で構成してください。",
+ "settingGroups": "設定グループ",
+ "enabled": "有効",
+ "disabled": "無効",
+ "enableGroup": "{group} を有効化",
+ "unsavedChanges": "未保存の変更があります",
+ "saved": "設定を保存しました。変更は即座に反映されます。",
+ "saveFailed": "保存に失敗しました。接続を確認して再試行してください。",
+ "invalidSettings": "一部の設定が無効です。",
+ "discard": "破棄",
+ "saveChanges": "変更を保存",
+ "saving": "保存中…",
+ "sourceSaved": "保存済み",
+ "sourceEnv": "環境変数",
+ "sourceSavedTitle": "管理者設定ファイルで設定",
+ "sourceEnvTitle": "環境変数で設定",
+ "restartRequired": "再起動が必要",
+ "modified": "変更済み",
+ "notSet": "未設定",
+ "savedReplace": "保存済み({hint})——入力して置き換え",
+ "showValue": "値を表示",
+ "hideValue": "値を非表示",
+ "removeValue": "値を削除",
+ "removeValueTitle": "保存された値を削除",
+ "resetToDefault": "デフォルトに戻す",
+ "models": "モデル",
+ "modelsDescription": "全ユーザーが利用できるサーバー側のプロバイダーとモデル——個人の API キーは不要です。ユーザーがモデルを選択しない場合、デフォルトプロバイダーの最初のモデルが使用されます。",
+ "addProviderHint": "プロバイダーを追加して、全ユーザーにサーバー側モデルを提供します。",
+ "selectProviderHint": "プロバイダーを選択または追加して、その資格情報とモデルを構成します。",
+ "addProviderToOfferModels": "ユーザーにこのプロバイダーを公開するには、モデルを少なくとも 1 つ追加してください。",
+ "managedViaEnv": "(環境変数で管理)",
+ "envReadOnly": "AI_MODELS_CONFIG / ai-models.json で定義——ここでは読み取り専用です。変更するには環境構成を編集してください。",
+ "defaultModel": "デフォルトモデル",
+ "noModelsConfigured": "モデルが構成されていません",
+ "modelCount": "{count} 個のモデル",
+ "modelCountPlural": "{count} 個のモデル",
+ "default": "デフォルト",
+ "setAsDefault": "デフォルトプロバイダーに設定",
+ "defaultProvider": "デフォルトプロバイダー",
+ "modelIdPlaceholder": "モデル ID…",
+ "addModel": "モデルを追加",
+ "suggested": "おすすめ",
+ "test": "テスト",
+ "testOk": "正常({ms}ms)",
+ "testFailed": "失敗",
+ "removeModel": "{model} を削除",
+ "deleteProviderTitle": "{name} を削除しますか?",
+ "deleteProviderDesc": "保存後、その資格情報とモデルはサーバーから削除されます。",
+ "cancel": "キャンセル",
+ "delete": "削除",
+ "groups": {
+ "generation": {
+ "title": "生成",
+ "description": "すべてのチャットリクエストに適用される出力パラメーター。"
+ },
+ "access": {
+ "title": "アクセス制御",
+ "description": "このデプロイを使用できるユーザーを制限します。"
+ },
+ "features": {
+ "title": "機能",
+ "description": "オプション機能とセキュリティの切り替え。"
+ },
+ "observability": {
+ "title": "オブザーバビリティ",
+ "description": "LLM 呼び出しの Langfuse トレース。"
+ },
+ "quota": {
+ "title": "クォータとレート制限",
+ "description": "IP ごとの使用制限。強制には DynamoDB テーブルが必要です。"
+ }
+ },
+ "settings": {
+ "TEMPERATURE": {
+ "label": "温度",
+ "description": "温度を受け付けない推論モデルの場合は未設定のままにしてください。"
+ },
+ "MAX_OUTPUT_TOKENS": {
+ "label": "最大出力トークン数"
+ },
+ "ACCESS_CODE_LIST": {
+ "label": "アクセスコード",
+ "description": "カンマ区切りのリスト。チャットにはいずれかの入力が必要です。空 = オープンアクセス。"
+ },
+ "ENABLE_VLM_VALIDATION": {
+ "label": "VLM 図検証",
+ "description": "ビジョンモデルで生成された図を視覚的に検証します。"
+ },
+ "VALIDATION_MODEL": {
+ "label": "検証モデル",
+ "description": "空の場合はデフォルトの AI モデルにフォールバックします。"
+ },
+ "VALIDATION_TIMEOUT": {
+ "label": "検証タイムアウト(ms)"
+ },
+ "ENABLE_HISTORY_XML_REPLACE": {
+ "label": "履歴 XML 圧縮",
+ "description": "履歴内の古い図 XML をプレースホルダーで置き換えます。"
+ },
+ "ALLOW_PRIVATE_URLS": {
+ "label": "プライベート URL を許可",
+ "description": "オフにすると、プライベート IP や内部ホスト名へのリクエストをブロックします(SSRF 保護)。"
+ },
+ "LANGFUSE_PUBLIC_KEY": {
+ "label": "Langfuse Public Key"
+ },
+ "LANGFUSE_SECRET_KEY": {
+ "label": "Langfuse Secret Key"
+ },
+ "LANGFUSE_BASEURL": {
+ "label": "Langfuse Base URL"
+ },
+ "DAILY_REQUEST_LIMIT": {
+ "label": "1 日あたりのリクエスト上限",
+ "description": "IP ごと 1 日あたり。"
+ },
+ "DAILY_TOKEN_LIMIT": {
+ "label": "1 日あたりのトークン上限",
+ "description": "IP ごと 1 日あたり。"
+ },
+ "TPM_LIMIT": {
+ "label": "1 分あたりのトークン数"
+ },
+ "DYNAMODB_QUOTA_TABLE": {
+ "label": "DynamoDB テーブル",
+ "description": "空の場合、クォータの強制は無効になります。"
+ },
+ "DYNAMODB_REGION": {
+ "label": "DynamoDB リージョン"
+ },
+ "QUOTA_TIMEZONE": {
+ "label": "クォータタイムゾーン",
+ "description": "1 日のリセット境界に使用するタイムゾーン。"
+ }
+ }
}
}
diff --git a/lib/i18n/dictionaries/zh-Hant.json b/lib/i18n/dictionaries/zh-Hant.json
index dd8f2d85..d3521c80 100644
--- a/lib/i18n/dictionaries/zh-Hant.json
+++ b/lib/i18n/dictionaries/zh-Hant.json
@@ -34,7 +34,8 @@
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
- "qiniu": "Qiniu"
+ "qiniu": "Qiniu",
+ "mimo": "MiMo (小米)"
},
"chat": {
"placeholder": "描述您的圖表或上傳檔案...",
@@ -76,8 +77,7 @@
"creativeDescription": "繪製有趣且富有創意的內容",
"cachedNote": "範例已快取,可即時回應",
"mcpServer": "MCP 伺服器",
- "mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用",
- "preview": "預覽"
+ "mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
},
"settings": {
"title": "設定",
@@ -103,10 +103,12 @@
"theme": "主題",
"themeDescription": "介面和 DrawIO 畫布的深色/淺色模式。",
"drawioStyle": "DrawIO 樣式",
- "drawioStyleDescription": "畫布樣式:",
- "switchTo": "切換到",
- "minimal": "簡約",
- "sketch": "草圖",
+ "drawioStyleDescription": "畫布樣式",
+ "themeDefault": "預設",
+ "themeDark": "深色",
+ "themeMinimal": "簡約",
+ "themeSketch": "草圖",
+ "themeSimple": "簡單",
"diagramStyle": "圖表樣式",
"diagramStyleDescription": "切換簡約與精緻圖表輸出模式。",
"sendShortcut": "傳送快捷鍵",
@@ -130,6 +132,8 @@
"customSystemMessage": "自訂系統訊息",
"customSystemMessageDescription": "新增自訂指示,將附加到 AI 的系統提示末尾。",
"customSystemMessagePlaceholder": "例如:圖表始終使用藍色配色方案...",
+ "maxOutputTokens": "最大輸出 token 數",
+ "maxOutputTokensDescription": "單次回覆的額度,思考過程與圖表 XML 共用。若 AI 一直在思考卻沒有產生圖表,請將它調大。留空則使用預設值。",
"panelVisibility": "大廳面板",
"panelVisibilityDescription": "選擇在聊天大廳顯示哪些面板。",
"showRecentChats": "最近聊天",
@@ -174,9 +178,6 @@
"networkError": "網路錯誤。請檢查您的連線。",
"retryLimit": "已達自動重試限制({max})。請手動重試。",
"continuationRetryLimit": "已達繼續重試限制({max})。圖表可能過於複雜。",
- "validationFailed": "圖表驗證失敗。請嘗試重新產生。",
- "malformedXml": "AI 產生的圖表 XML 無效。請嘗試重新產生。",
- "failedToProcess": "無法處理圖表。請嘗試重新產生。",
"sessionCorrupted": "工作階段資料已損壞。重新開始。",
"failedToSave": "無法儲存訊息到 localStorage",
"failedToRestore": "無法從 localStorage 還原",
@@ -184,7 +185,25 @@
"failedToExport": "取得圖表資料時出錯",
"failedToLoadExample": "載入範例圖片時出錯",
"failedToRecordFeedback": "記錄您的回饋失敗。請重試。",
- "storageUpdateFailed": "聊天已清除,但無法更新瀏覽器儲存空間"
+ "storageUpdateFailed": "聊天已清除,但無法更新瀏覽器儲存空間",
+ "sessionSaveFailed": "無法儲存這個對話。瀏覽器儲存空間可能已滿,請在歷史紀錄裡刪除舊對話後重試。",
+ "sessionSaveFailedLeave": "無法儲存這個對話,瀏覽器儲存空間可能已滿。可以不儲存它、直接繼續,再在新對話的列表裡刪除舊對話。",
+ "continueWithoutSaving": "不儲存,繼續",
+ "llm": {
+ "invalid_api_key": "服務商拒絕了這個 API Key,請在模型設定中檢查。",
+ "forbidden": "服務商拒絕了這次請求。這個 Key 可能沒有使用該模型或該地區的權限。",
+ "model_not_found": "服務商找不到這個模型,請在模型設定中檢查模型 ID。",
+ "insufficient_quota": "服務商帳戶的餘額或額度已經用完。",
+ "rate_limited": "服務商正在限制請求頻率,請稍候再試。",
+ "context_too_long": "對話內容超過了這個模型能處理的長度。請開啟新的對話,或換一個上下文更長的模型。",
+ "images_unsupported": "這個模型不支援圖片輸入。",
+ "tools_unsupported": "這個模型不支援工具呼叫,而繪圖需要工具呼叫。請換一個模型。",
+ "output_truncated": "輸出在圖表完成之前就被截斷了。請簡化請求,或在設定中調高輸出上限。",
+ "provider_unavailable": "服務商發生問題,請稍後再試。",
+ "cannot_connect": "無法連線到服務商,請檢查 Base URL 和網路。",
+ "timeout": "服務商沒有及時回應。",
+ "openModelSettings": "開啟模型設定"
+ }
},
"quota": {
"dailyLimit": "已達每日配額",
@@ -231,6 +250,7 @@
"reasoning": {
"thinking": "思考中...",
"thoughtFor": "思考了 {duration} 秒",
+ "thoughtForOne": "思考了 1 秒",
"thoughtBrief": "思考了幾秒鐘"
},
"dev": {
@@ -367,9 +387,15 @@
"enterSecretKey": "輸入您的 Secret Key",
"baseUrl": "基礎 URL",
"optional": "(可選)",
+ "getApiKey": "取得 API Key",
+ "fetchModels": "從服務商取得模型清單",
+ "noTools": "不支援工具呼叫",
+ "mayNotDraw": "models.dev 顯示這個模型不支援工具呼叫,可能無法繪圖。",
+ "requestUrl": "請求將傳送至 {url}",
"baseUrlWithExample": "基礎 URL(可選,例如 {example})",
"customEndpoint": "自訂端點 URL",
"minimaxBaseUrlHint": "使用 /anthropic 端點為 Anthropic 相容 API(推薦),或使用 /v1 端點為 OpenAI 相容 API",
+ "mimoBaseUrlHint": "預設地址適用於按量付費金鑰(sk-...)。Token Plan 訂閱用戶(tp-... 金鑰)請設定為 https://token-plan-cn.xiaomimimo.com/v1",
"models": "模型",
"customModelId": "自訂模型 ID...",
"allAdded": "已全部新增",
@@ -401,6 +427,152 @@
"showUnvalidatedModels": "顯示未驗證的模型",
"allModelsShown": "顯示所有模型(包括未驗證的)",
"unvalidatedModelWarning": "此模型尚未驗證",
- "serverDefaultModel": "伺服器預設模型"
+ "serverDefaultModel": "伺服器預設模型",
+ "showValue": "顯示值",
+ "hideValue": "隱藏值"
+ },
+ "admin": {
+ "title": "管理員設定",
+ "loginPrompt": "輸入管理員密碼(即 ADMIN_PASSWORD 環境變數)以管理伺服器設定。",
+ "password": "密碼",
+ "signIn": "登入",
+ "signingIn": "正在登入…",
+ "loginFailed": "登入失敗",
+ "precedence": "檔案覆蓋環境變數 · 環境變數覆蓋預設值",
+ "notWritable": "此部署環境下設定檔不可寫入(無伺服器平台沒有持久化磁碟)。設定以唯讀方式顯示——請改用環境變數進行設定。",
+ "settingGroups": "設定分組",
+ "enabled": "已啟用",
+ "disabled": "已停用",
+ "enableGroup": "啟用 {group}",
+ "unsavedChanges": "有未儲存的變更",
+ "saved": "設定已儲存,變更立即生效。",
+ "saveFailed": "儲存失敗。請檢查網路連線後重試。",
+ "invalidSettings": "部分設定無效。",
+ "discard": "捨棄",
+ "saveChanges": "儲存變更",
+ "saving": "正在儲存…",
+ "sourceSaved": "已儲存",
+ "sourceEnv": "環境變數",
+ "sourceSavedTitle": "在管理員設定檔中設定",
+ "sourceEnvTitle": "透過環境變數設定",
+ "restartRequired": "需要重新啟動",
+ "modified": "已修改",
+ "notSet": "未設定",
+ "savedReplace": "已儲存({hint})——輸入以取代",
+ "showValue": "顯示值",
+ "hideValue": "隱藏值",
+ "removeValue": "移除值",
+ "removeValueTitle": "移除已儲存的值",
+ "resetToDefault": "重設為預設",
+ "models": "模型",
+ "modelsDescription": "面向所有使用者的伺服器端 provider 與模型——無需個人 API 金鑰。當使用者未選擇模型時,使用預設 provider 的第一個模型。",
+ "addProviderHint": "新增一個 provider,為所有使用者提供伺服器端模型。",
+ "selectProviderHint": "選擇或新增一個 provider 以設定其憑證和模型。",
+ "addProviderToOfferModels": "至少新增一個模型,才能向使用者開放此 provider。",
+ "managedViaEnv": "(透過環境變數管理)",
+ "envReadOnly": "在 AI_MODELS_CONFIG / ai-models.json 中定義——此處唯讀。請編輯環境設定以變更。",
+ "defaultModel": "預設模型",
+ "noModelsConfigured": "未設定模型",
+ "modelCount": "{count} 個模型",
+ "modelCountPlural": "{count} 個模型",
+ "default": "預設",
+ "setAsDefault": "設為預設 provider",
+ "defaultProvider": "預設 provider",
+ "modelIdPlaceholder": "模型 ID…",
+ "addModel": "新增模型",
+ "suggested": "推薦",
+ "test": "測試",
+ "testOk": "正常({ms} 毫秒)",
+ "testFailed": "失敗",
+ "removeModel": "移除 {model}",
+ "deleteProviderTitle": "刪除 {name}?",
+ "deleteProviderDesc": "儲存後,其憑證和模型將從伺服器上移除。",
+ "cancel": "取消",
+ "delete": "刪除",
+ "groups": {
+ "generation": {
+ "title": "生成",
+ "description": "套用於所有聊天請求的輸出參數。"
+ },
+ "access": {
+ "title": "存取控制",
+ "description": "限制誰可以使用此部署。"
+ },
+ "features": {
+ "title": "功能",
+ "description": "選用功能和安全開關。"
+ },
+ "observability": {
+ "title": "可觀測性",
+ "description": "對 LLM 呼叫進行 Langfuse 追蹤。"
+ },
+ "quota": {
+ "title": "配額與速率限制",
+ "description": "按 IP 的用量限制。強制執行需要 DynamoDB 表。"
+ }
+ },
+ "settings": {
+ "TEMPERATURE": {
+ "label": "溫度",
+ "description": "對於拒絕溫度參數的推理模型,請留空。"
+ },
+ "MAX_OUTPUT_TOKENS": {
+ "label": "最大輸出 token 數"
+ },
+ "ACCESS_CODE_LIST": {
+ "label": "存取碼",
+ "description": "以逗號分隔的清單。使用者需輸入其中之一才能聊天。留空 = 開放存取。"
+ },
+ "ENABLE_VLM_VALIDATION": {
+ "label": "VLM 圖表驗證",
+ "description": "使用視覺模型對產生的圖表進行視覺化驗證。"
+ },
+ "VALIDATION_MODEL": {
+ "label": "驗證模型",
+ "description": "留空時回退到預設 AI 模型。"
+ },
+ "VALIDATION_TIMEOUT": {
+ "label": "驗證逾時(毫秒)"
+ },
+ "ENABLE_HISTORY_XML_REPLACE": {
+ "label": "歷史 XML 壓縮",
+ "description": "用占位符取代歷史記錄中的舊圖表 XML。"
+ },
+ "ALLOW_PRIVATE_URLS": {
+ "label": "允許私有 URL",
+ "description": "關閉以阻擋對私有 IP 和內部主機名的請求(SSRF 防護)。"
+ },
+ "LANGFUSE_PUBLIC_KEY": {
+ "label": "Langfuse Public Key"
+ },
+ "LANGFUSE_SECRET_KEY": {
+ "label": "Langfuse Secret Key"
+ },
+ "LANGFUSE_BASEURL": {
+ "label": "Langfuse Base URL"
+ },
+ "DAILY_REQUEST_LIMIT": {
+ "label": "每日請求上限",
+ "description": "每個 IP 每天。"
+ },
+ "DAILY_TOKEN_LIMIT": {
+ "label": "每日 token 上限",
+ "description": "每個 IP 每天。"
+ },
+ "TPM_LIMIT": {
+ "label": "每分鐘 token 數"
+ },
+ "DYNAMODB_QUOTA_TABLE": {
+ "label": "DynamoDB 表",
+ "description": "留空時配額強制執行被停用。"
+ },
+ "DYNAMODB_REGION": {
+ "label": "DynamoDB 區域"
+ },
+ "QUOTA_TIMEZONE": {
+ "label": "配額時區",
+ "description": "每日重置邊界所用的時區。"
+ }
+ }
}
}
diff --git a/lib/i18n/dictionaries/zh.json b/lib/i18n/dictionaries/zh.json
index 57a03d47..1d8f9adb 100644
--- a/lib/i18n/dictionaries/zh.json
+++ b/lib/i18n/dictionaries/zh.json
@@ -34,7 +34,8 @@
"glm": "GLM",
"qwen": "Qwen",
"kimi": "Kimi",
- "qiniu": "Qiniu"
+ "qiniu": "Qiniu",
+ "mimo": "MiMo (小米)"
},
"chat": {
"placeholder": "描述您的图表或上传文件...",
@@ -76,8 +77,7 @@
"creativeDescription": "绘制有趣且富有创意的内容",
"cachedNote": "示例已缓存,可即时响应",
"mcpServer": "MCP 服务器",
- "mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用",
- "preview": "预览"
+ "mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
},
"settings": {
"title": "设置",
@@ -103,10 +103,12 @@
"theme": "主题",
"themeDescription": "界面和 DrawIO 画布的深色/浅色模式。",
"drawioStyle": "DrawIO 样式",
- "drawioStyleDescription": "画布样式:",
- "switchTo": "切换到",
- "minimal": "简约",
- "sketch": "草图",
+ "drawioStyleDescription": "画布样式",
+ "themeDefault": "默认",
+ "themeDark": "深色",
+ "themeMinimal": "简约",
+ "themeSketch": "草图",
+ "themeSimple": "简单",
"diagramStyle": "图表样式",
"diagramStyleDescription": "切换简约与精致图表输出模式。",
"sendShortcut": "发送快捷键",
@@ -130,6 +132,8 @@
"customSystemMessage": "自定义系统消息",
"customSystemMessageDescription": "添加自定义指令,将附加到 AI 的系统提示末尾。",
"customSystemMessagePlaceholder": "例如:图表始终使用蓝色配色方案...",
+ "maxOutputTokens": "最大输出 token 数",
+ "maxOutputTokensDescription": "单次回复的额度,思考过程和图表 XML 共用。如果 AI 一直在思考却没有生成图表,请把它调大。留空则使用默认值。",
"panelVisibility": "大厅面板",
"panelVisibilityDescription": "选择在聊天大厅显示哪些面板。",
"showRecentChats": "最近聊天",
@@ -174,9 +178,6 @@
"networkError": "网络错误。请检查您的连接。",
"retryLimit": "已达到自动重试限制({max})。请手动重试。",
"continuationRetryLimit": "已达到继续重试限制({max})。图表可能过于复杂。",
- "validationFailed": "图表验证失败。请尝试重新生成。",
- "malformedXml": "AI 生成的图表 XML 无效。请尝试重新生成。",
- "failedToProcess": "无法处理图表。请尝试重新生成。",
"sessionCorrupted": "会话数据已损坏。重新开始。",
"failedToSave": "无法保存消息到 localStorage",
"failedToRestore": "无法从 localStorage 恢复",
@@ -184,7 +185,25 @@
"failedToExport": "获取图表数据时出错",
"failedToLoadExample": "加载示例图片时出错",
"failedToRecordFeedback": "记录您的反馈失败。请重试。",
- "storageUpdateFailed": "聊天已清除,但无法更新浏览器存储"
+ "storageUpdateFailed": "聊天已清除,但无法更新浏览器存储",
+ "sessionSaveFailed": "无法保存这个对话。浏览器存储空间可能已满,请在历史记录里删除旧对话后重试。",
+ "sessionSaveFailedLeave": "无法保存这个对话,浏览器存储空间可能已满。可以不保存它、直接继续,再在新对话的列表里删除旧对话。",
+ "continueWithoutSaving": "不保存,继续",
+ "llm": {
+ "invalid_api_key": "服务商拒绝了这个 API Key,请在模型设置里检查。",
+ "forbidden": "服务商拒绝了这次请求。这个 Key 可能没有使用该模型或该地区的权限。",
+ "model_not_found": "服务商找不到这个模型,请在模型设置里检查模型 ID。",
+ "insufficient_quota": "服务商账户的余额或额度已经用完。",
+ "rate_limited": "服务商正在限制请求频率,请稍等片刻再试。",
+ "context_too_long": "对话内容超过了这个模型能处理的长度。请新开一个对话,或换一个上下文更长的模型。",
+ "images_unsupported": "这个模型不支持图片输入。",
+ "tools_unsupported": "这个模型不支持工具调用,而画图需要工具调用。请换一个模型。",
+ "output_truncated": "输出在图画完之前就被截断了。请简化请求,或在设置里调高输出上限。",
+ "provider_unavailable": "服务商出了问题,请稍后再试。",
+ "cannot_connect": "连接不上服务商,请检查 Base URL 和网络。",
+ "timeout": "服务商没有及时响应。",
+ "openModelSettings": "打开模型设置"
+ }
},
"quota": {
"dailyLimit": "已达每日配额",
@@ -231,6 +250,7 @@
"reasoning": {
"thinking": "思考中...",
"thoughtFor": "思考了 {duration} 秒",
+ "thoughtForOne": "思考了 1 秒",
"thoughtBrief": "思考了几秒钟"
},
"dev": {
@@ -367,9 +387,15 @@
"enterSecretKey": "输入您的 Secret Key",
"baseUrl": "基础 URL",
"optional": "(可选)",
+ "getApiKey": "获取 API Key",
+ "fetchModels": "从服务商获取模型列表",
+ "noTools": "不支持工具调用",
+ "mayNotDraw": "models.dev 显示这个模型不支持工具调用,可能无法画图。",
+ "requestUrl": "请求将发往 {url}",
"baseUrlWithExample": "基础 URL(可选,例如 {example})",
"customEndpoint": "自定义端点 URL",
"minimaxBaseUrlHint": "使用 /anthropic 端点为 Anthropic 兼容 API(推荐),或使用 /v1 端点为 OpenAI 兼容 API",
+ "mimoBaseUrlHint": "默认地址适用于按量付费密钥(sk-...)。Token Plan 订阅用户(tp-... 密钥)请设置为 https://token-plan-cn.xiaomimimo.com/v1",
"models": "模型",
"customModelId": "自定义模型 ID...",
"allAdded": "已全部添加",
@@ -401,6 +427,152 @@
"showUnvalidatedModels": "显示未验证的模型",
"allModelsShown": "显示所有模型(包括未验证的)",
"unvalidatedModelWarning": "此模型尚未验证",
- "serverDefaultModel": "服务器默认模型"
+ "serverDefaultModel": "服务器默认模型",
+ "showValue": "显示值",
+ "hideValue": "隐藏值"
+ },
+ "admin": {
+ "title": "管理员设置",
+ "loginPrompt": "输入管理员密码(即 ADMIN_PASSWORD 环境变量)以管理服务器设置。",
+ "password": "密码",
+ "signIn": "登录",
+ "signingIn": "正在登录…",
+ "loginFailed": "登录失败",
+ "precedence": "文件覆盖环境变量 · 环境变量覆盖默认值",
+ "notWritable": "此部署环境下设置文件不可写(无服务器平台没有持久化磁盘)。设置以只读方式显示——请改用环境变量进行配置。",
+ "settingGroups": "设置分组",
+ "enabled": "已启用",
+ "disabled": "已禁用",
+ "enableGroup": "启用 {group}",
+ "unsavedChanges": "有未保存的更改",
+ "saved": "设置已保存,更改立即生效。",
+ "saveFailed": "保存失败。请检查网络连接后重试。",
+ "invalidSettings": "部分设置无效。",
+ "discard": "放弃",
+ "saveChanges": "保存更改",
+ "saving": "正在保存…",
+ "sourceSaved": "已保存",
+ "sourceEnv": "环境变量",
+ "sourceSavedTitle": "在管理员设置文件中设置",
+ "sourceEnvTitle": "通过环境变量设置",
+ "restartRequired": "需要重启",
+ "modified": "已修改",
+ "notSet": "未设置",
+ "savedReplace": "已保存({hint})——输入以替换",
+ "showValue": "显示值",
+ "hideValue": "隐藏值",
+ "removeValue": "移除值",
+ "removeValueTitle": "移除已保存的值",
+ "resetToDefault": "恢复默认",
+ "models": "模型",
+ "modelsDescription": "面向所有用户的服务端 provider 和模型——无需个人 API 密钥。当用户未选择模型时,使用默认 provider 的第一个模型。",
+ "addProviderHint": "添加一个 provider,为所有用户提供服务端模型。",
+ "selectProviderHint": "选择或添加一个 provider 以配置其凭证和模型。",
+ "addProviderToOfferModels": "至少添加一个模型,才能向用户开放此 provider。",
+ "managedViaEnv": "(通过环境变量管理)",
+ "envReadOnly": "在 AI_MODELS_CONFIG / ai-models.json 中定义——此处只读。请编辑环境配置以更改。",
+ "defaultModel": "默认模型",
+ "noModelsConfigured": "未配置模型",
+ "modelCount": "{count} 个模型",
+ "modelCountPlural": "{count} 个模型",
+ "default": "默认",
+ "setAsDefault": "设为默认 provider",
+ "defaultProvider": "默认 provider",
+ "modelIdPlaceholder": "模型 ID…",
+ "addModel": "添加模型",
+ "suggested": "推荐",
+ "test": "测试",
+ "testOk": "正常({ms} 毫秒)",
+ "testFailed": "失败",
+ "removeModel": "移除 {model}",
+ "deleteProviderTitle": "删除 {name}?",
+ "deleteProviderDesc": "保存后,其凭证和模型将从服务器上移除。",
+ "cancel": "取消",
+ "delete": "删除",
+ "groups": {
+ "generation": {
+ "title": "生成",
+ "description": "应用于所有聊天请求的输出参数。"
+ },
+ "access": {
+ "title": "访问控制",
+ "description": "限制谁可以使用此部署。"
+ },
+ "features": {
+ "title": "功能",
+ "description": "可选功能和安全开关。"
+ },
+ "observability": {
+ "title": "可观测性",
+ "description": "对 LLM 调用进行 Langfuse 追踪。"
+ },
+ "quota": {
+ "title": "配额与速率限制",
+ "description": "按 IP 的用量限制。强制执行需要 DynamoDB 表。"
+ }
+ },
+ "settings": {
+ "TEMPERATURE": {
+ "label": "温度",
+ "description": "对于拒绝温度参数的推理模型,请留空。"
+ },
+ "MAX_OUTPUT_TOKENS": {
+ "label": "最大输出 token 数"
+ },
+ "ACCESS_CODE_LIST": {
+ "label": "访问码",
+ "description": "以逗号分隔的列表。用户需输入其中之一才能聊天。留空 = 开放访问。"
+ },
+ "ENABLE_VLM_VALIDATION": {
+ "label": "VLM 图表验证",
+ "description": "使用视觉模型对生成的图表进行可视化验证。"
+ },
+ "VALIDATION_MODEL": {
+ "label": "验证模型",
+ "description": "留空时回退到默认 AI 模型。"
+ },
+ "VALIDATION_TIMEOUT": {
+ "label": "验证超时(毫秒)"
+ },
+ "ENABLE_HISTORY_XML_REPLACE": {
+ "label": "历史 XML 压缩",
+ "description": "用占位符替换历史记录中的旧图表 XML。"
+ },
+ "ALLOW_PRIVATE_URLS": {
+ "label": "允许私有 URL",
+ "description": "关闭以阻止对私有 IP 和内部主机名的请求(SSRF 防护)。"
+ },
+ "LANGFUSE_PUBLIC_KEY": {
+ "label": "Langfuse Public Key"
+ },
+ "LANGFUSE_SECRET_KEY": {
+ "label": "Langfuse Secret Key"
+ },
+ "LANGFUSE_BASEURL": {
+ "label": "Langfuse Base URL"
+ },
+ "DAILY_REQUEST_LIMIT": {
+ "label": "每日请求上限",
+ "description": "每个 IP 每天。"
+ },
+ "DAILY_TOKEN_LIMIT": {
+ "label": "每日 token 上限",
+ "description": "每个 IP 每天。"
+ },
+ "TPM_LIMIT": {
+ "label": "每分钟 token 数"
+ },
+ "DYNAMODB_QUOTA_TABLE": {
+ "label": "DynamoDB 表",
+ "description": "留空时配额强制执行被禁用。"
+ },
+ "DYNAMODB_REGION": {
+ "label": "DynamoDB 区域"
+ },
+ "QUOTA_TIMEZONE": {
+ "label": "配额时区",
+ "description": "每日重置边界所用的时区。"
+ }
+ }
}
}
diff --git a/lib/langfuse.ts b/lib/langfuse.ts
index 040a296f..23398c47 100644
--- a/lib/langfuse.ts
+++ b/lib/langfuse.ts
@@ -51,8 +51,15 @@ export function setTraceOutput(output: string) {
if (!isLangfuseEnabled()) return
updateActiveTrace({ output })
+ endTrace()
+}
+
+// End the observe() wrapper span (AI SDK creates its own child spans with usage).
+// It uses endOnExit: false, so every request path has to end it, or the trace
+// is never exported: stream finish, stream error/abort, and early returns.
+export function endTrace() {
+ if (!isLangfuseEnabled()) return
- // End the observe() wrapper span (AI SDK creates its own child spans with usage)
const activeSpan = api.trace.getActiveSpan()
if (activeSpan) {
activeSpan.end()
diff --git a/lib/llm-errors.ts b/lib/llm-errors.ts
new file mode 100644
index 00000000..c587276d
--- /dev/null
+++ b/lib/llm-errors.ts
@@ -0,0 +1,187 @@
+import {
+ APICallError,
+ InvalidToolInputError,
+ LoadAPIKeyError,
+ NoSuchToolError,
+ RetryError,
+ ToolCallRepairError,
+} from "ai"
+
+/**
+ * What went wrong with a model call, for a hint the user can act on. The
+ * provider's own message always goes along, because a guess can be wrong.
+ */
+export type LLMErrorCode =
+ | "invalid_api_key"
+ | "forbidden"
+ | "model_not_found"
+ | "insufficient_quota"
+ | "rate_limited"
+ | "context_too_long"
+ | "images_unsupported"
+ | "tools_unsupported"
+ | "output_truncated"
+ | "provider_unavailable"
+ | "cannot_connect"
+ | "timeout"
+ | "unknown"
+
+export interface LLMError {
+ type: "provider"
+ code: LLMErrorCode
+ message: string
+}
+
+// Texts that name the cause more precisely than the status code: a quota
+// error can come as 403 or 429, a context or image error as a plain 400
+const SPECIFIC_TEXTS: Array<[RegExp, LLMErrorCode]> = [
+ [
+ // Not "too many tokens": that is Bedrock's throttling message
+ /context length|context window|maximum context|prompt is too long|input is too long|too many input tokens/i,
+ "context_too_long",
+ ],
+ [
+ /image content block|image_url|does not support image|image input is not supported/i,
+ "images_unsupported",
+ ],
+ [
+ /does not support tools|tool use is not supported|tools? (?:are|is) not supported|function calling is not supported/i,
+ "tools_unsupported",
+ ],
+ // Bedrock, when the output limit cut the tool call's JSON short
+ [/toolUse\.input is invalid/i, "output_truncated"],
+ // Bedrock, for a model id without the inference profile prefix
+ [/on-demand throughput isn.t supported/i, "model_not_found"],
+ [
+ /insufficient[_ ]quota|insufficient balance|exceeded your current quota|credit balance is too low|余额不足/i,
+ "insufficient_quota",
+ ],
+]
+
+const STATUS_CODES: Record = {
+ 401: "invalid_api_key",
+ 402: "insufficient_quota",
+ // Not "invalid key": a valid key can lack access to a model or region
+ 403: "forbidden",
+ 404: "model_not_found",
+ 408: "timeout",
+ // A retired model
+ 410: "model_not_found",
+ 413: "context_too_long",
+ 429: "rate_limited",
+}
+
+const GENERAL_TEXTS: Array<[RegExp, LLMErrorCode]> = [
+ [
+ /model[_ ]not[_ ]found|model .*does not exist|unknown model|no such model/i,
+ "model_not_found",
+ ],
+ [
+ /invalid[_ ]api[_ ]key|incorrect api key|unauthorized/i,
+ "invalid_api_key",
+ ],
+ // "too many tokens": Bedrock's throttling
+ [/rate limit|too many requests|too many tokens/i, "rate_limited"],
+ [
+ /Cannot connect to API|ECONNREFUSED|ENOTFOUND|ECONNRESET|ETIMEDOUT|fetch failed/i,
+ "cannot_connect",
+ ],
+]
+
+/** Secrets a provider may echo back: API keys, Bearer tokens, key=value */
+function redact(text: string): string {
+ return text
+ .replace(/\b(sk|pk|rk|ak)-[A-Za-z0-9_-]{8,}/g, "$1-[redacted]")
+ .replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [redacted]")
+ .replace(/\bAKIA[0-9A-Z]{16}\b/g, "[redacted]")
+ .replace(
+ /\b(api[_-]?key|access[_-]?key|secret|token|password|signature)(["']?\s*[:=]\s*["']?)[^\s"',&}]+/gi,
+ "$1$2[redacted]",
+ )
+}
+
+function problemDetail(body: string): string | undefined {
+ try {
+ const detail = JSON.parse(body)?.detail
+ return typeof detail === "string" ? detail : undefined
+ } catch {
+ return undefined
+ }
+}
+
+/**
+ * The error text for the chat stream: what went wrong with the provider as
+ * JSON for the hint, or the text the model must read to fix a tool call.
+ * On the server's keys the provider's own text stays in the server log:
+ * it can name the server's account, role or internal hosts.
+ */
+export function streamErrorText(error: unknown, hideDetails = false): string {
+ // The SDK passes an invalid tool call's error as a plain string. Other
+ // strings come from providers (DeepSeek's SDK sends stream errors so).
+ if (
+ typeof error === "string" &&
+ /^(Invalid input for tool|Model tried to call unavailable tool)/.test(
+ error,
+ )
+ ) {
+ return error
+ }
+ if (isToolCallError(error)) return (error as Error).message
+ const classified = classifyLLMError(error)
+ if (hideDetails) {
+ console.error("[chat] Provider error:", error)
+ classified.message = "The provider returned an error."
+ }
+ return JSON.stringify(classified)
+}
+
+/**
+ * Model and tool errors the SDK sends back to the model as the tool result,
+ * so it can fix its call. Their text has to stay as it is.
+ */
+export function isToolCallError(error: unknown): boolean {
+ return (
+ InvalidToolInputError.isInstance(error) ||
+ NoSuchToolError.isInstance(error) ||
+ ToolCallRepairError.isInstance(error)
+ )
+}
+
+export function classifyLLMError(error: unknown): LLMError {
+ // After the SDK's retries, the last attempt says what happened
+ const e = RetryError.isInstance(error) ? error.lastError : error
+ // Errors sent inside the stream can be plain objects like OpenRouter's
+ // { code: 503, message }
+ const plain = e as {
+ message?: unknown
+ code?: unknown
+ statusCode?: number
+ }
+ const raw =
+ e instanceof Error
+ ? e.message
+ : typeof plain?.message === "string"
+ ? plain.message
+ : String(e)
+ const body = APICallError.isInstance(e) ? (e.responseBody ?? "") : ""
+ // A problem+json body names the reason the SDK left out (NVIDIA: "Gone")
+ const detail = problemDetail(body)
+ const message = redact(detail ? `${raw}: ${detail}` : raw).slice(0, 500)
+ const text = `${raw} ${body}`
+ const status = APICallError.isInstance(e)
+ ? e.statusCode
+ : (plain?.statusCode ??
+ (typeof plain?.code === "number" ? plain.code : undefined))
+
+ const find = (rules: Array<[RegExp, LLMErrorCode]>) =>
+ rules.find(([pattern]) => pattern.test(text))?.[1]
+ const code =
+ (e instanceof Error && e.name === "TimeoutError" && "timeout") ||
+ (LoadAPIKeyError.isInstance(e) && "invalid_api_key") ||
+ find(SPECIFIC_TEXTS) ||
+ (status && STATUS_CODES[status]) ||
+ (status && status >= 500 && "provider_unavailable") ||
+ find(GENERAL_TEXTS) ||
+ "unknown"
+ return { type: "provider", code, message }
+}
diff --git a/lib/model-catalog.json b/lib/model-catalog.json
new file mode 100644
index 00000000..b505c31e
--- /dev/null
+++ b/lib/model-catalog.json
@@ -0,0 +1,1682 @@
+{
+ "openai": {
+ "chatgpt-image-latest": {"tools":false,"images":true,"reasoning":false},
+ "gpt-4.1": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "gpt-4.1-mini": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "gpt-4o": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "gpt-4o-2024-08-06": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "gpt-4o-2024-11-20": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "gpt-4o-mini": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "gpt-5": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":272000},
+ "gpt-5.1": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.3-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.3-codex-spark": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":32000},
+ "gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.4-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-daybreak-blue-latest": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-daybreak-red-latest": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-image-1-mini": {"tools":false,"images":true,"reasoning":false},
+ "gpt-image-1.5": {"tools":false,"images":true,"reasoning":false},
+ "gpt-realtime-2.1": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":32000},
+ "o3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "o3-pro": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "text-embedding-3-large": {"tools":false,"images":false,"reasoning":false,"context":8191,"output":3072},
+ "text-embedding-3-small": {"tools":false,"images":false,"reasoning":false,"context":8191,"output":1536},
+ "text-embedding-ada-002": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536}
+ },
+ "anthropic": {
+ "claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-haiku-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-haiku-4-5-20251001": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-5-20251101": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-4-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-4-5-20250929": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000}
+ },
+ "google": {
+ "deep-research-max-preview-04-2026": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "deep-research-preview-04-2026": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "gemini-2.5-computer-use-preview-10-2025": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":64000},
+ "gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-2.5-flash-image": {"tools":false,"images":true,"reasoning":true,"context":32768,"output":32768},
+ "gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-flash-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-pro-image": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":32768},
+ "gemini-3-pro-image-preview": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "gemini-3.1-flash-image": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "gemini-3.1-flash-image-preview": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":65536},
+ "gemini-3.1-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-flash-lite-image": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":4096},
+ "gemini-3.1-flash-live-preview": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "gemini-3.1-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-pro-preview-customtools": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-live-translate-preview": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":32768},
+ "gemini-3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-embedding-001": {"tools":false,"images":false,"reasoning":false,"context":2048,"output":1},
+ "gemini-embedding-2": {"tools":false,"images":true,"reasoning":false,"context":8192,"output":1},
+ "gemini-flash-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-flash-lite-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemma-4-26b-a4b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "gemma-4-31b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "lyria-3-clip-preview": {"tools":false,"images":true,"reasoning":false,"context":1048576,"output":65536},
+ "lyria-3-pro-preview": {"tools":false,"images":true,"reasoning":false,"context":1048576,"output":65536}
+ },
+ "vertexai": {
+ "claude-fable-5-1@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-fable-5@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-haiku-4-5@20251001": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-5@20251101": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-6@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-7@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-8@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5-5@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-4-5@20250929": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-sonnet-4-6@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-5-5@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-5@default": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-2.5-flash-image": {"tools":false,"images":true,"reasoning":false,"context":32768,"output":32768},
+ "gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65535},
+ "gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-flash-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-pro-image": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":32768},
+ "gemini-3.1-flash-image": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "gemini-3.1-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-pro-preview-customtools": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-embedding-001": {"tools":false,"images":false,"reasoning":false,"context":2048,"output":1},
+ "gemini-flash-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-flash-lite-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "meta/llama-4-maverick-17b-128e-instruct-maas": {"tools":true,"images":true,"reasoning":false,"context":524288,"output":8192},
+ "openai/gpt-oss-120b-maas": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":131072},
+ "xai/grok-4.20-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":2000000,"output":30000},
+ "xai/grok-4.20-reasoning": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":30000},
+ "xai/grok-4.3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":30000},
+ "xai/grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":524288,"output":500000},
+ "zai-org/glm-5.2-maas": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":64000}
+ },
+ "azure": {
+ "claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-haiku-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-mythos-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-1": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "claude-opus-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "codestral-2501": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":256000},
+ "cohere-command-a": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "cohere-embed-v-4-0": {"tools":false,"images":true,"reasoning":false,"context":128000,"output":1536},
+ "cohere-embed-v3-english": {"tools":false,"images":false,"reasoning":false,"context":512,"output":1024},
+ "cohere-embed-v3-multilingual": {"tools":false,"images":false,"reasoning":false,"context":512,"output":1024},
+ "deepseek-v3.2": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":128000},
+ "deepseek-v3.2-speciale": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":128000},
+ "deepseek-v4-flash": {"tools":false,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-pro": {"tools":false,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "gpt-5": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1-codex-max": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1-codex-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.3-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.4-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-chat-latest": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-image-1.5": {"tools":false,"images":true,"reasoning":false},
+ "grok-4-1-fast-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "grok-4-1-fast-reasoning": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":8192},
+ "grok-4-20-non-reasoning": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":8192},
+ "grok-4-20-reasoning": {"tools":true,"images":false,"reasoning":true,"context":262000,"output":8192},
+ "grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":128000},
+ "kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "llama-3.3-70b-instruct": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":32768},
+ "llama-4-maverick-17b-128e-instruct-fp8": {"tools":true,"images":true,"reasoning":false,"context":1000000,"output":16384},
+ "llama-4-scout-17b-16e-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "ministral-3b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "mistral-medium-2505": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":128000},
+ "mistral-small-2503": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":32768},
+ "model-router": {"tools":true,"images":true,"reasoning":false,"context":200000,"output":16384},
+ "o3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "phi-4": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "phi-4-mini": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "phi-4-mini-reasoning": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":4096},
+ "phi-4-multimodal": {"tools":false,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "phi-4-reasoning": {"tools":false,"images":false,"reasoning":true,"context":32000,"output":4096},
+ "phi-4-reasoning-plus": {"tools":false,"images":false,"reasoning":true,"context":32000,"output":4096},
+ "text-embedding-3-large": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":3072},
+ "text-embedding-3-small": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "text-embedding-ada-002": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536}
+ },
+ "bedrock": {
+ "amazon.nova-2-lite-v1:0": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "amazon.nova-lite-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "amazon.nova-micro-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":10000},
+ "amazon.nova-pro-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "anthropic.claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic.claude-opus-4-5-20251101-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic.claude-opus-4-6-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic.claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "apac.amazon.nova-lite-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "apac.amazon.nova-micro-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":10000},
+ "apac.amazon.nova-pro-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "au.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "au.anthropic.claude-opus-4-6-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "au.anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "au.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "ca.amazon.nova-lite-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "deepseek.r1-v1:0": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32768},
+ "deepseek.v3-v1:0": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":81920},
+ "deepseek.v3.2": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":81920},
+ "eu.amazon.nova-2-lite-v1:0": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "eu.amazon.nova-lite-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "eu.amazon.nova-micro-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":10000},
+ "eu.amazon.nova-pro-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "eu.anthropic.claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "eu.anthropic.claude-opus-4-5-20251101-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "eu.anthropic.claude-opus-4-6-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "eu.anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.anthropic.claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "eu.mistral.pixtral-large-2502-v1:0": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "global.amazon.nova-2-lite-v1:0": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "global.anthropic.claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "global.anthropic.claude-opus-4-5-20251101-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "global.anthropic.claude-opus-4-6-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "global.anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.anthropic.claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "global.moonshotai.kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":128000},
+ "global.openai.gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.openai.gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "global.xai.grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "global.xai.grok-4.7": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "google.gemma-3-12b-it": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "google.gemma-3-27b-it": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "google.gemma-3-4b-it": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":4096},
+ "google.gemma-4-26b-a4b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "google.gemma-4-31b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "google.gemma-4-e2b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":8192},
+ "in.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "in.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "in.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "in.openai.gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "in.openai.gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "jp.amazon.nova-2-lite-v1:0": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "jp.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "jp.anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "jp.anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "jp.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "jp.anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "jp.anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "jp.anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "jp.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "meta.llama3-1-70b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "meta.llama3-1-8b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "meta.llama3-3-70b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "meta.llama4-maverick-17b-instruct-v1:0": {"tools":true,"images":true,"reasoning":false,"context":1000000,"output":8192},
+ "meta.llama4-scout-17b-instruct-v1:0": {"tools":true,"images":true,"reasoning":false,"context":10000000,"output":8192},
+ "minimax.minimax-m2": {"tools":true,"images":false,"reasoning":true,"context":204608,"output":128000},
+ "minimax.minimax-m2.1": {"tools":true,"images":false,"reasoning":true,"context":196608,"output":131072},
+ "minimax.minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":196608,"output":98304},
+ "mistral.devstral-2-123b": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":8192},
+ "mistral.magistral-small-2509": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":40000},
+ "mistral.ministral-3-14b-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "mistral.ministral-3-3b-instruct": {"tools":true,"images":true,"reasoning":false,"context":256000,"output":8192},
+ "mistral.ministral-3-8b-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "mistral.mistral-large-3-675b-instruct": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":8192},
+ "mistral.pixtral-large-2502-v1:0": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "mistral.voxtral-mini-3b-2507": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":4096},
+ "mistral.voxtral-small-24b-2507": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":8192},
+ "moonshot.kimi-k2-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":16000},
+ "moonshotai.kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":16384},
+ "nvidia.nemotron-nano-12b-v2": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "nvidia.nemotron-nano-3-30b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":8192},
+ "nvidia.nemotron-nano-9b-v2": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "nvidia.nemotron-super-3-120b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":131072},
+ "openai.gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "openai.gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "openai.gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai.gpt-oss-120b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":131072},
+ "openai.gpt-oss-120b-1:0": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":128000},
+ "openai.gpt-oss-20b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":131072},
+ "openai.gpt-oss-20b-1:0": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":128000},
+ "openai.gpt-oss-safeguard-120b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16384},
+ "openai.gpt-oss-safeguard-20b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16384},
+ "qwen.qwen3-235b-a22b-2507-v1:0": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":131072},
+ "qwen.qwen3-32b-v1:0": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "qwen.qwen3-coder-30b-a3b-v1:0": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":131072},
+ "qwen.qwen3-coder-480b-a35b-v1:0": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":65536},
+ "qwen.qwen3-coder-next": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen.qwen3-next-80b-a3b": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":262000},
+ "qwen.qwen3-vl-235b-a22b": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":262000},
+ "us-gov.openai.gpt-oss-120b-1:0": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16384},
+ "us-gov.openai.gpt-oss-20b-1:0": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16384},
+ "us.amazon.nova-2-lite-v1:0": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "us.amazon.nova-lite-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "us.amazon.nova-micro-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":10000},
+ "us.amazon.nova-pro-v1:0": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "us.anthropic.claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-haiku-4-5-20251001-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "us.anthropic.claude-opus-4-5-20251101-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "us.anthropic.claude-opus-4-6-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "us.anthropic.claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.anthropic.claude-sonnet-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "us.deepseek.r1-v1:0": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32768},
+ "us.meta.llama3-1-70b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "us.meta.llama3-1-8b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "us.meta.llama3-3-70b-instruct-v1:0": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "us.meta.llama4-maverick-17b-instruct-v1:0": {"tools":true,"images":true,"reasoning":false,"context":1000000,"output":8192},
+ "us.meta.llama4-scout-17b-instruct-v1:0": {"tools":true,"images":true,"reasoning":false,"context":10000000,"output":8192},
+ "us.mistral.pixtral-large-2502-v1:0": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "us.moonshotai.kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":128000},
+ "us.openai.gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.openai.gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "us.writer.palmyra-x4-v1:0": {"tools":true,"images":false,"reasoning":true,"context":122880,"output":8192},
+ "us.writer.palmyra-x5-v1:0": {"tools":true,"images":false,"reasoning":true,"context":1040000,"output":8192},
+ "us.xai.grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "us.xai.grok-4.7": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "writer.palmyra-x4-v1:0": {"tools":true,"images":false,"reasoning":true,"context":122880,"output":8192},
+ "writer.palmyra-x5-v1:0": {"tools":true,"images":false,"reasoning":true,"context":1040000,"output":8192},
+ "xai.grok-4.3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "xai.grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "zai.glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":131072},
+ "zai.glm-4.7-flash": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":131072},
+ "zai.glm-5": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":131072}
+ },
+ "ollama": {
+ "deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":1048576},
+ "deepseek-v4-flash:0731": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":1048576},
+ "deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":1048576},
+ "deepseek-v4-pro:0813": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":1048576},
+ "deepseek-v4.1-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":384000},
+ "gemma4:31b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":131072},
+ "glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":976000,"output":131072},
+ "glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "glm-5.3-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "gpt-oss:120b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "gpt-oss:20b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":196608,"output":196608},
+ "minimax-m3": {"tools":true,"images":true,"reasoning":true,"context":512000,"output":131072},
+ "mistral-large-3:675b": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":262144},
+ "nemotron-3-nano:30b": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "nemotron-3-super": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "nemotron-3-ultra": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":128000},
+ "qwen3.5:397b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536}
+ },
+ "openrouter": {
+ "~anthropic/claude-fable-latest": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "~anthropic/claude-haiku-latest": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "~anthropic/claude-opus-latest": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "~anthropic/claude-sonnet-latest": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "~deepseek/deepseek-flash-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "~deepseek/deepseek-pro-latest": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":393216},
+ "~deepseek/deepseek-v4-flash-latest": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "~google/gemini-flash-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "~google/gemini-pro-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "~moonshotai/kimi-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "~openai/gpt-astra-latest": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "~openai/gpt-luna-latest": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "~openai/gpt-mini-latest": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "~openai/gpt-sol-latest": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "~openai/gpt-terra-latest": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "~x-ai/grok-latest": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":450000},
+ "~z-ai/glm-flash-latest": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "~z-ai/glm-latest": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "aion-labs/aion-2.0": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "aion-labs/aion-3.0": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "aion-labs/aion-3.0-mini": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "aion-labs/aion-3.5": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "aion-labs/aion-3.5-mini": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "aion-labs/aion-rp-llama-3.1-8b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":29491},
+ "amazon/nova-2-lite-v1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "amazon/nova-lite-v1": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":5120},
+ "amazon/nova-micro-v1": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":5120},
+ "amazon/nova-premier-v1": {"tools":true,"images":true,"reasoning":false,"context":1000000,"output":32000},
+ "amazon/nova-pro-v1": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":5120},
+ "anthracite-org/magnum-v4-72b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":4096},
+ "anthropic/claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-fable-5.1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-haiku-4.5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic/claude-opus-4.1": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "anthropic/claude-opus-4.5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic/claude-opus-4.6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-4.7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-4.8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-4": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic/claude-sonnet-4.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "anthropic/claude-sonnet-4.6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "apodex/apodex-1.1-mini:free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "arcee-ai/trinity-large-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":80000},
+ "baidu/ernie-4.5-vl-424b-a47b": {"tools":false,"images":true,"reasoning":true,"context":123000,"output":16000},
+ "bytedance-seed/seed-1.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "bytedance-seed/seed-1.6-flash": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "bytedance-seed/seed-2-1-turbo": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "bytedance-seed/seed-2.0-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "bytedance-seed/seed-2.0-lite": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "bytedance-seed/seed-2.0-mini": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "bytedance/ui-tars-1.5-7b": {"tools":false,"images":true,"reasoning":false,"context":128000,"output":2048},
+ "cognitivecomputations/dolphin-mistral-24b-venice-edition": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "cohere/command-a": {"tools":false,"images":false,"reasoning":false,"context":256000,"output":8192},
+ "cohere/command-a-plus": {"tools":true,"images":true,"reasoning":true,"context":192000,"output":64000},
+ "cohere/command-r-08-2024": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4000},
+ "cohere/command-r-plus-08-2024": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4000},
+ "cohere/command-r7b-12-2024": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":4000},
+ "cohere/north-mini-code:free": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":64000},
+ "deepseek/deepseek-chat": {"tools":true,"images":false,"reasoning":false,"context":163840,"output":16000},
+ "deepseek/deepseek-chat-v3-0324": {"tools":true,"images":false,"reasoning":false,"context":163840,"output":147456},
+ "deepseek/deepseek-chat-v3.1": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":32768},
+ "deepseek/deepseek-r1": {"tools":true,"images":false,"reasoning":true,"context":64000,"output":16000},
+ "deepseek/deepseek-r1-0528": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":32768},
+ "deepseek/deepseek-v3.1-terminus": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":147456},
+ "deepseek/deepseek-v3.2": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":65536},
+ "deepseek/deepseek-v3.2-exp": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":147456},
+ "deepseek/deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "deepseek/deepseek-v4-flash-0731": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "deepseek/deepseek-v4-flash-vision-exp": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":262144},
+ "deepseek/deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":384000},
+ "deepseek/deepseek-v4-pro-0813": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "deepseek/deepseek-v4.1-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "dots-studio/dots-3-note-preview:free": {"tools":true,"images":true,"reasoning":true,"context":512000,"output":460800},
+ "fireworks/ember-1": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "google/gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65535},
+ "google/gemini-2.5-flash-image": {"tools":false,"images":true,"reasoning":false,"context":32768,"output":8192},
+ "google/gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65535},
+ "google/gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-2.5-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3-flash-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3-pro-image": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "google/gemini-3-pro-image-preview": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":32768},
+ "google/gemini-3.1-flash-image": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "google/gemini-3.1-flash-image-preview": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":58982},
+ "google/gemini-3.1-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.1-flash-lite-image": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":58982},
+ "google/gemini-3.1-flash-lite-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.1-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.1-pro-preview-customtools": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemma-2-27b-it": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":2048},
+ "google/gemma-3-12b-it": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":16384},
+ "google/gemma-3-27b-it": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":117964},
+ "google/gemma-3-4b-it": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":16384},
+ "google/gemma-4-26b-a4b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "google/gemma-4-26b-a4b-it:free": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "google/gemma-4-31b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":16384},
+ "google/gemma-4-31b-it:free": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "google/lyria-3-clip-preview": {"tools":false,"images":true,"reasoning":false,"context":1048576,"output":65536},
+ "google/lyria-3-pro-preview": {"tools":false,"images":true,"reasoning":false,"context":1048576,"output":65536},
+ "gryphe/mythomax-l2-13b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":3686},
+ "ibm-granite/granite-4.0-h-micro": {"tools":false,"images":false,"reasoning":false,"context":131000,"output":117900},
+ "ibm-granite/granite-4.2-8b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "inception/mercury-2": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":50000},
+ "inception/mercury-2.5": {"tools":true,"images":false,"reasoning":true,"context":260000,"output":65536},
+ "inclusionai/ling-3.0-flash": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inclusionai/ling-3.0-flash-fin": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inclusionai/ling-3.0-flash-sante:free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inclusionai/ling-3.0-flash-vl": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "inclusionai/ling-3.1-flash": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inference-net/schematron-v2-small": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "inference-net/schematron-v2-turbo": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "kwaipilot/kat-coder-pro-v2.5": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":235929},
+ "liquid/lfm-2.5-2.6b:free": {"tools":true,"images":false,"reasoning":true,"context":65536,"output":8192},
+ "mancer/weaver": {"tools":false,"images":false,"reasoning":false,"context":8000,"output":6000},
+ "meituan/longcat-2.0": {"tools":true,"images":false,"reasoning":true,"context":1048756,"output":262144},
+ "meta-llama/llama-3.1-70b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "meta-llama/llama-3.1-8b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":117964},
+ "meta-llama/llama-3.2-1b-instruct": {"tools":false,"images":false,"reasoning":false,"context":60000,"output":54000},
+ "meta-llama/llama-3.2-3b-instruct": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":117964},
+ "meta-llama/llama-3.3-70b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "meta-llama/llama-4-maverick": {"tools":true,"images":true,"reasoning":false,"context":1048576,"output":16384},
+ "meta-llama/llama-4-scout": {"tools":true,"images":true,"reasoning":false,"context":1310720,"output":16384},
+ "meta-llama/llama-guard-4-12b": {"tools":false,"images":true,"reasoning":false,"context":163840,"output":16384},
+ "meta/muse-glimmer-30b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":117964},
+ "meta/muse-spark-1.1": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "meta/muse-spark-1.2": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "meta/muse-spark-1.2-contributor": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "meta/muse-spark-1.3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "meta/muse-spark-1.3-contributor": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "microsoft/phi-4": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":14745},
+ "microsoft/wizardlm-2-8x22b": {"tools":false,"images":false,"reasoning":false,"context":65535,"output":8000},
+ "minimax/minimax-01": {"tools":false,"images":true,"reasoning":false,"context":1000192,"output":40000},
+ "minimax/minimax-m1": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":40000},
+ "minimax/minimax-m2": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":176947},
+ "minimax/minimax-m2-her": {"tools":false,"images":false,"reasoning":false,"context":65536,"output":2048},
+ "minimax/minimax-m2.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "minimax/minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":176947},
+ "minimax/minimax-m3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":512000},
+ "mistralai/codestral-2508": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":204800},
+ "mistralai/devstral-2512": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":209715},
+ "mistralai/ministral-14b-2512": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":209715},
+ "mistralai/ministral-3b-2512": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":104857},
+ "mistralai/ministral-8b-2512": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":209715},
+ "mistralai/mistral-large": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":102400},
+ "mistralai/mistral-large-2407": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":104857},
+ "mistralai/mistral-large-2512": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":209715},
+ "mistralai/mistral-medium-3": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":104857},
+ "mistralai/mistral-medium-3-5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":209715},
+ "mistralai/mistral-medium-3.1": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":104857},
+ "mistralai/mistral-nemo": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "mistralai/mistral-saba": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":26214},
+ "mistralai/mistral-small-24b-instruct-2501": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":16384},
+ "mistralai/mistral-small-2603": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":209715},
+ "mistralai/mistral-small-3.1-24b-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":102400},
+ "mistralai/mistral-small-3.2-24b-instruct": {"tools":true,"images":true,"reasoning":false,"context":256000,"output":16384},
+ "mistralai/mixtral-8x22b-instruct": {"tools":true,"images":false,"reasoning":false,"context":65536,"output":52428},
+ "mistralai/voxtral-small-24b-2507": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":26214},
+ "moonshotai/kimi-k2": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":98304},
+ "moonshotai/kimi-k2-0905": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":98304},
+ "moonshotai/kimi-k2-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "moonshotai/kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "moonshotai/kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "moonshotai/kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "moonshotai/kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943718},
+ "morph/morph-v3-fast": {"tools":false,"images":false,"reasoning":false,"context":81920,"output":38000},
+ "morph/morph-v3-large": {"tools":false,"images":false,"reasoning":false,"context":262144,"output":131072},
+ "nex-agi/nex-n2.5-mini": {"tools":false,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "nex-agi/nex-n2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "nousresearch/hermes-3-llama-3.1-405b": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "nousresearch/hermes-3-llama-3.1-70b": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "nousresearch/hermes-4-405b": {"tools":false,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "nvidia/nemotron-3-nano-30b-a3b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":65536},
+ "nvidia/nemotron-3-super-120b-a12b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "nvidia/nemotron-3-super-120b-a12b:free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "nvidia/nemotron-3-ultra-550b-a55b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":16384},
+ "nvidia/nemotron-3-ultra-550b-a55b:free": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":65536},
+ "nvidia/nemotron-3.5-content-safety": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":117964},
+ "nvidia/nemotron-3.5-content-safety:free": {"tools":false,"images":true,"reasoning":true,"context":128000,"output":8192},
+ "nvidia/nemotron-3.5-lightning": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":131072},
+ "nvidia/nemotron-3.5-lightning:free": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":65536},
+ "openai/gpt-3.5-turbo": {"tools":true,"images":false,"reasoning":false,"context":16385,"output":4096},
+ "openai/gpt-3.5-turbo-0613": {"tools":true,"images":false,"reasoning":false,"context":4095,"output":3685},
+ "openai/gpt-3.5-turbo-16k": {"tools":true,"images":false,"reasoning":false,"context":16385,"output":4096},
+ "openai/gpt-3.5-turbo-instruct": {"tools":false,"images":false,"reasoning":false,"context":4095,"output":3685},
+ "openai/gpt-4": {"tools":true,"images":false,"reasoning":false,"context":8191,"output":4096},
+ "openai/gpt-4-turbo": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "openai/gpt-4.1": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-mini": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-nano": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4o": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-2024-05-13": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "openai/gpt-4o-2024-08-06": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-2024-11-20": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-mini": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-mini-2024-07-18": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-5": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-image": {"tools":false,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-image-mini": {"tools":false,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-codex-max": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-codex-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2-chat": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":32000},
+ "openai/gpt-5.2-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.3-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.4-image-2": {"tools":false,"images":true,"reasoning":true,"context":272000,"output":128000},
+ "openai/gpt-5.4-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-luna-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-sol-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-terra-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-astra-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-luna-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-sol-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6.1-sol-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-audio": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-audio-mini": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-chat-latest": {"tools":true,"images":true,"reasoning":false,"context":400000,"output":128000},
+ "openai/gpt-oss-120b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "openai/gpt-oss-20b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "openai/gpt-oss-safeguard-20b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":65536},
+ "openai/o1": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o1-pro": {"tools":false,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3-mini": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3-mini-high": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3-pro": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o4-mini": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o4-mini-high": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openrouter/auto": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "openrouter/bodybuilder": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":128000},
+ "openrouter/free": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":8000},
+ "openrouter/fusion": {"tools":false,"images":false,"reasoning":false,"context":1000000,"output":128000},
+ "openrouter/pareto-code": {"tools":false,"images":false,"reasoning":false,"context":2000000,"output":200000},
+ "perceptron/perceptron-mk1": {"tools":false,"images":true,"reasoning":true,"context":32768,"output":8192},
+ "perceptron/perceptron-mk1.5": {"tools":true,"images":true,"reasoning":true,"context":36864,"output":8192},
+ "perplexity/sonar": {"tools":false,"images":true,"reasoning":false,"context":127072,"output":114364},
+ "perplexity/sonar-deep-research": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":115200},
+ "perplexity/sonar-pro": {"tools":false,"images":true,"reasoning":false,"context":200000,"output":8000},
+ "perplexity/sonar-pro-search": {"tools":false,"images":true,"reasoning":true,"context":200000,"output":8000},
+ "perplexity/sonar-reasoning-pro": {"tools":false,"images":true,"reasoning":true,"context":128000,"output":115200},
+ "poolside/laguna-s-2.1": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "poolside/laguna-s-2.1:free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "poolside/laguna-xs-2.1": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "poolside/laguna-xs-2.1:free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "prism-ml/ternary-bonsai-2-27b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "qwen/qwen-2.5-72b-instruct": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":16384},
+ "qwen/qwen-2.5-7b-instruct": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":29491},
+ "qwen/qwen-2.5-coder-32b-instruct": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":29491},
+ "qwen/qwen-plus": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":32768},
+ "qwen/qwen-plus-2025-07-28": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":32768},
+ "qwen/qwen2.5-vl-72b-instruct": {"tools":false,"images":true,"reasoning":false,"context":128000,"output":115200},
+ "qwen/qwen3-14b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "qwen/qwen3-235b-a22b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "qwen/qwen3-235b-a22b-2507": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":235929},
+ "qwen/qwen3-235b-a22b-thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "qwen/qwen3-30b-a3b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "qwen/qwen3-30b-a3b-instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":32000},
+ "qwen/qwen3-30b-a3b-thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":81920,"output":32768},
+ "qwen/qwen3-32b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "qwen/qwen3-8b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "qwen/qwen3-coder": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen/qwen3-coder-30b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":235929},
+ "qwen/qwen3-coder-flash": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":65536},
+ "qwen/qwen3-coder-next": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":235929},
+ "qwen/qwen3-coder-plus": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":65536},
+ "qwen/qwen3-max": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen/qwen3-max-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3-next-80b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":235929},
+ "qwen/qwen3-next-80b-a3b-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "qwen/qwen3-vl-235b-a22b-instruct": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":32768},
+ "qwen/qwen3-vl-235b-a22b-thinking": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "qwen/qwen3-vl-30b-a3b-instruct": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":16384},
+ "qwen/qwen3-vl-30b-a3b-thinking": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "qwen/qwen3-vl-32b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3-vl-8b-instruct": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":32768},
+ "qwen/qwen3-vl-8b-thinking": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "qwen/qwen3.5-122b-a10b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.5-27b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.5-35b-a3b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "qwen/qwen3.5-397b-a17b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "qwen/qwen3.5-9b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "qwen/qwen3.5-flash-02-23": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.5-plus-02-15": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.5-plus-20260420": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.6-27b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":81920},
+ "qwen/qwen3.6-35b-a3b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "qwen/qwen3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.6-max-preview": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.6-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen/qwen3.7-max": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.7-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.8-2.4t-a95b": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "qwen/qwen3.8-27b": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.8-27b:free": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":235929},
+ "qwen/qwen3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.8-max-0902": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.8-max-prime": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen/qwen3.8-omni-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "rekaai/reka-edge": {"tools":true,"images":true,"reasoning":false,"context":16384,"output":14745},
+ "rekaai/reka-flash-3": {"tools":false,"images":false,"reasoning":true,"context":65536,"output":58982},
+ "relace/relace-apply-3": {"tools":false,"images":false,"reasoning":false,"context":256000,"output":128000},
+ "relace/relace-search": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":128000},
+ "sakana/fugu-max": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "sakana/fugu-ultra": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "sakana/fugu-ultra-v2": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "sakana/sakana-namazu": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "sao10k/l3-lunaris-8b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":7372},
+ "sao10k/l3.1-euryale-70b": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "sao10k/l3.3-euryale-70b": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "stealth/space-bunny-alpha": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":524288},
+ "stepfun/step-3.5-flash": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "stepfun/step-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":230400},
+ "tencent/hunyuan-a13b-instruct": {"tools":false,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "tencent/hy-mt2-1.8b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":4096},
+ "tencent/hy-mt2-30b-a3b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":4096},
+ "tencent/hy-mt2-7b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":4096},
+ "tencent/hy3": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":128000},
+ "tencent/hy3-preview": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":235929},
+ "tencent/hy4-preview": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":64000},
+ "thedrummer/cydonia-24b-v4.1": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":117964},
+ "thedrummer/skyfall-36b-v2": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":29491},
+ "thedrummer/unslopnemo-12b": {"tools":false,"images":false,"reasoning":false,"context":1024000,"output":819200},
+ "thinkingmachines/inkling": {"tools":true,"images":true,"reasoning":true,"context":524288,"output":262144},
+ "thinkingmachines/inkling-small": {"tools":true,"images":true,"reasoning":true,"context":524288,"output":262144},
+ "thinkingmachines/inkling-small:free": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":262144},
+ "thinkingmachines/inkling:free": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":262144},
+ "unbiased/pareto": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":131072},
+ "unbiased/pareto-26.10-preview": {"tools":true,"images":true,"reasoning":false,"context":1048576,"output":131072},
+ "undi95/remm-slerp-l2-13b": {"tools":false,"images":false,"reasoning":false,"context":6144,"output":5529},
+ "upstage/solar-mini4": {"tools":true,"images":false,"reasoning":true,"context":524288,"output":131072},
+ "upstage/solar-pro-3": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":117964},
+ "upstage/solar-pro4": {"tools":true,"images":false,"reasoning":true,"context":524288,"output":131072},
+ "writer/palmyra-x5": {"tools":false,"images":false,"reasoning":false,"context":1040000,"output":8192},
+ "x-ai/grok-4.20": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":1800000},
+ "x-ai/grok-4.20-multi-agent": {"tools":false,"images":true,"reasoning":true,"context":2000000,"output":1800000},
+ "x-ai/grok-4.3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":900000},
+ "x-ai/grok-4.5": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":450000},
+ "x-ai/grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":450000},
+ "x-ai/grok-4.7": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":450000},
+ "x-ai/grok-build-0.1": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":230400},
+ "xiaomi/mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":131072},
+ "xiaomi/mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1050000,"output":131072},
+ "xiaomi/mimo-v2.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":131072},
+ "xiaomi/mimo-v2.6-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":131072},
+ "xiaomi/mimo-v2.6-pro-ultraspeed": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "z-ai/glm-4.5": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "z-ai/glm-4.5-air": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "z-ai/glm-4.5v": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":16384},
+ "z-ai/glm-4.6": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":16384},
+ "z-ai/glm-4.6v": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "z-ai/glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "z-ai/glm-4.7-flash": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":117964},
+ "z-ai/glm-5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "z-ai/glm-5-turbo": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":131072},
+ "z-ai/glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "z-ai/glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":943718},
+ "z-ai/glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "z-ai/glm-5.3-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":943717},
+ "z-ai/glm-5.3-flashx": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "z-ai/glm-5.3-prime": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "z-ai/glm-5v-turbo": {"tools":true,"images":true,"reasoning":true,"context":202752,"output":131072}
+ },
+ "aihubmix": {
+ "alicloud-deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "alicloud-deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "alicloud-glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-fable-5-1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-haiku-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-opus-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-6-think": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-7-think": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-4-8": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "claude-opus-4-8-think": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-opus-5-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "claude-sonnet-4-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-4-6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-4-6-think": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "coding-glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "coding-glm-5.1-free": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "coding-minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128100},
+ "coding-minimax-m2.7-free": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128100},
+ "coding-minimax-m2.7-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128100},
+ "coding-xiaomi-mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "coding-xiaomi-mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "deep-deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deep-deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-flash-0731": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-flash-0731-fast": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-flash-vision-exp": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-pro-0813": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4.1-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":384000},
+ "doubao-seed-2-0-code-preview": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "doubao-seed-2-0-lite-260428": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "doubao-seed-2-0-mini-260428": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "doubao-seed-2-0-pro": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-2.5-flash-image": {"tools":false,"images":true,"reasoning":true,"context":32768,"output":32768},
+ "gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-flash-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3-pro-image": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":32768},
+ "gemini-3.1-flash-image": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "gemini-3.1-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-flash-lite-image": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":4096},
+ "gemini-3.1-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.1-pro-preview-customtools": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "gemini-3.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "glm-4.5v": {"tools":true,"images":true,"reasoning":true,"context":64000,"output":16384},
+ "glm-4.6": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "glm-4.6v": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":32768},
+ "glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":128000},
+ "glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":128000},
+ "glm-5.3-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "glm-5v-turbo": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":128000},
+ "gpt-5": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.1-codex-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.2-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.3-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.4-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.4-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "gpt-image-1.5": {"tools":false,"images":true,"reasoning":false},
+ "grok-4.3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "grok-4.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "grok-4.7": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "grok-build-0.1": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "hy3": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":128000},
+ "hy3-preview": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":128000},
+ "hy4-preview": {"tools":true,"images":false,"reasoning":true,"context":1024000,"output":64000},
+ "kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "kimi-k2.7-code-highspeed": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "longcat-2.0": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-pro-ultraspeed": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "minimax-m3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":512000},
+ "muse-spark-1.1": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "muse-spark-1.2": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "muse-spark-1.3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "o3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "o4-mini": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "ox-alpha": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3-max": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen3-vl-plus": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "qwen3.5-122b-a10b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.5-27b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.5-35b-a3b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.5-397b-a17b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.5-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.6-35b-a3b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":991000,"output":64000},
+ "qwen3.6-max-preview": {"tools":true,"images":false,"reasoning":true,"context":240000,"output":64000},
+ "qwen3.6-plus": {"tools":true,"images":true,"reasoning":true,"context":991000,"output":64000},
+ "qwen3.7-flash": {"tools":true,"images":false,"reasoning":true,"context":991000,"output":64000},
+ "qwen3.7-max": {"tools":true,"images":false,"reasoning":true,"context":991000,"output":64000},
+ "qwen3.7-plus": {"tools":true,"images":false,"reasoning":true,"context":991000,"output":64000},
+ "qwen3.8-2.4t-a95b": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "qwen3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3.8-max": {"tools":true,"images":false,"reasoning":true,"context":991000,"output":128000},
+ "qwen3.8-max-preview": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3.8-omni-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "step-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "step-5-preview": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "xiaomi-mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomi-mimo-v2.5-free": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomi-mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomi-mimo-v2.5-pro-free": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "zai-glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000}
+ },
+ "deepseek": {
+ "deepseek-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":393216},
+ "deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":393216}
+ },
+ "siliconflow": {
+ "ByteDance-Seed/Seed-OSS-36B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":262000},
+ "deepseek-ai/DeepSeek-OCR": {"tools":false,"images":true,"reasoning":false,"context":8192,"output":8192},
+ "deepseek-ai/DeepSeek-R1": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "deepseek-ai/DeepSeek-V3": {"tools":true,"images":false,"reasoning":false,"context":164000,"output":164000},
+ "deepseek-ai/DeepSeek-V3.1-Terminus": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "deepseek-ai/DeepSeek-V3.2": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "deepseek-ai/DeepSeek-V4-Flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-ai/DeepSeek-V4-Pro": {"tools":true,"images":false,"reasoning":true,"context":1049000,"output":393000},
+ "inclusionAI/Ling-flash-2.0": {"tools":true,"images":false,"reasoning":false,"context":131000,"output":131000},
+ "PaddlePaddle/PaddleOCR-VL-1.5": {"tools":false,"images":true,"reasoning":false,"context":16384,"output":16384},
+ "Pro/deepseek-ai/DeepSeek-R1": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "Pro/deepseek-ai/DeepSeek-V3": {"tools":true,"images":false,"reasoning":false,"context":164000,"output":164000},
+ "Pro/deepseek-ai/DeepSeek-V3.1-Terminus": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "Pro/deepseek-ai/DeepSeek-V3.2": {"tools":true,"images":false,"reasoning":true,"context":164000,"output":164000},
+ "Pro/MiniMaxAI/MiniMax-M2.5": {"tools":true,"images":false,"reasoning":false,"context":192000,"output":131000},
+ "Pro/moonshotai/Kimi-K2.5": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "Pro/moonshotai/Kimi-K2.6": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "Pro/zai-org/GLM-5": {"tools":true,"images":false,"reasoning":true,"context":205000,"output":205000},
+ "Pro/zai-org/GLM-5.1": {"tools":true,"images":false,"reasoning":true,"context":205000,"output":205000},
+ "Qwen/Qwen2.5-72B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":33000,"output":4000},
+ "Qwen/Qwen2.5-7B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":33000,"output":4000},
+ "Qwen/Qwen3-14B": {"tools":true,"images":false,"reasoning":true,"context":131000,"output":131000},
+ "Qwen/Qwen3-235B-A22B-Thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":262000,"output":262000},
+ "Qwen/Qwen3-30B-A3B-Instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3-32B": {"tools":true,"images":false,"reasoning":true,"context":131000,"output":131000},
+ "Qwen/Qwen3-8B": {"tools":true,"images":false,"reasoning":true,"context":131000,"output":131000},
+ "Qwen/Qwen3-Coder-30B-A3B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3-Coder-480B-A35B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3-VL-30B-A3B-Instruct": {"tools":true,"images":true,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3-VL-30B-A3B-Thinking": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "Qwen/Qwen3-VL-32B-Instruct": {"tools":true,"images":true,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3-VL-32B-Thinking": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "Qwen/Qwen3-VL-8B-Instruct": {"tools":true,"images":true,"reasoning":false,"context":262000,"output":262000},
+ "Qwen/Qwen3.5-122B-A10B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.5-27B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.5-35B-A3B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.5-397B-A17B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.5-4B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.5-9B": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "Qwen/Qwen3.6-35B-A3B": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":65536},
+ "stepfun-ai/Step-3.5-Flash": {"tools":true,"images":false,"reasoning":true,"context":262000,"output":262000},
+ "tencent/Hunyuan-A13B-Instruct": {"tools":true,"images":false,"reasoning":true,"context":131000,"output":131000},
+ "zai-org/GLM-4.5-Air": {"tools":true,"images":false,"reasoning":false,"context":131000,"output":131000},
+ "zai-org/GLM-5.2": {"tools":true,"images":false,"reasoning":true,"context":1049000,"output":262000}
+ },
+ "gateway": {
+ "alibaba/qwen-3-14b": {"tools":true,"images":false,"reasoning":true,"context":40960,"output":16384},
+ "alibaba/qwen-3-235b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":16384},
+ "alibaba/qwen-3-30b": {"tools":true,"images":false,"reasoning":true,"context":40960,"output":16384},
+ "alibaba/qwen-3-32b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":8192},
+ "alibaba/qwen-3.6-max-preview": {"tools":true,"images":false,"reasoning":true,"context":240000,"output":64000},
+ "alibaba/qwen3-235b-a22b-thinking": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "alibaba/qwen3-coder": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "alibaba/qwen3-coder-30b-a3b": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":8192},
+ "alibaba/qwen3-coder-next": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":256000},
+ "alibaba/qwen3-coder-plus": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":65536},
+ "alibaba/qwen3-embedding-0.6b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":32768},
+ "alibaba/qwen3-embedding-4b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":32768},
+ "alibaba/qwen3-embedding-8b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":32768},
+ "alibaba/qwen3-max": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":32768},
+ "alibaba/qwen3-max-preview": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":32768},
+ "alibaba/qwen3-max-thinking": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":65536},
+ "alibaba/qwen3-next-80b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262114,"output":262114},
+ "alibaba/qwen3-next-80b-a3b-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":262144},
+ "alibaba/qwen3-vl-235b-a22b-instruct": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":129024},
+ "alibaba/qwen3-vl-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":129024},
+ "alibaba/qwen3-vl-thinking": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "alibaba/qwen3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "alibaba/qwen3.5-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "alibaba/qwen3.6-27b": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":65536},
+ "alibaba/qwen3.6-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "alibaba/qwen3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":991000,"output":64000},
+ "alibaba/qwen3.7-max": {"tools":true,"images":false,"reasoning":true,"context":991000,"output":64000},
+ "alibaba/qwen3.7-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "alibaba/qwen3.8-2.4t-a95b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":128000},
+ "alibaba/qwen3.8-27b": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "alibaba/qwen3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":991000,"output":128000},
+ "alibaba/qwen3.8-max": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":128000},
+ "alibaba/qwen3.8-max-0902": {"tools":true,"images":true,"reasoning":true,"context":991000,"output":128000},
+ "alibaba/qwen3.8-max-prime": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "alibaba/qwen3.8-omni-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "amazon/nova-2-lite": {"tools":false,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "amazon/nova-lite": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "amazon/nova-micro": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":10000},
+ "amazon/nova-pro": {"tools":true,"images":true,"reasoning":false,"context":300000,"output":10000},
+ "amazon/titan-embed-text-v2": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "anthropic/claude-3-haiku": {"tools":true,"images":true,"reasoning":false,"context":200000,"output":4096},
+ "anthropic/claude-fable-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-fable-5.1": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-haiku-4.5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic/claude-opus-4": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "anthropic/claude-opus-4.5": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "anthropic/claude-opus-4.6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-4.7": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-4.8": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-4.8-fast": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5-fast": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-opus-5.5-fast": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-4": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "anthropic/claude-sonnet-4.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "anthropic/claude-sonnet-4.6": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "anthropic/claude-sonnet-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "arcee-ai/trinity-large-thinking": {"tools":true,"images":false,"reasoning":true,"context":262100,"output":80000},
+ "bytedance/seed-1.6": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "bytedance/seed-1.8": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32768},
+ "bytedance/seed-2.1-turbo": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "cohere/command-a": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":8000},
+ "cohere/embed-v4.0": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":1536},
+ "cohere/rerank-v3.5": {"tools":false,"images":false,"reasoning":false,"context":4096,"output":4096},
+ "cohere/rerank-v4-fast": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "cohere/rerank-v4-pro": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "deepseek/deepseek-r1": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32768},
+ "deepseek/deepseek-v3.1": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":128000},
+ "deepseek/deepseek-v3.1-terminus": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "deepseek/deepseek-v3.2": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":8000},
+ "deepseek/deepseek-v3.2-thinking": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":8000},
+ "deepseek/deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek/deepseek-v4-flash-0731": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek/deepseek-v4-flash-vision-exp": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "deepseek/deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek/deepseek-v4-pro-0813": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek/deepseek-v4.1-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":32768},
+ "deepseek/deepseek-v4.1-flash-fast": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1000000},
+ "fireworks/ember-1": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "fish-audio/transcribe-1": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-2.5-flash-image": {"tools":false,"images":true,"reasoning":false,"context":32768,"output":65535},
+ "google/gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65535},
+ "google/gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "google/gemini-3-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65000},
+ "google/gemini-3-pro-image": {"tools":false,"images":true,"reasoning":false,"context":65536,"output":32768},
+ "google/gemini-3.1-flash-image": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "google/gemini-3.1-flash-image-preview": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "google/gemini-3.1-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65000},
+ "google/gemini-3.1-flash-lite-image": {"tools":false,"images":true,"reasoning":true,"context":65536,"output":4096},
+ "google/gemini-3.1-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "google/gemini-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "google/gemini-3.5-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65000},
+ "google/gemini-3.5-transcribe": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-3.5-transcribe-live": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "google/gemini-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "google/gemini-3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65535},
+ "google/gemini-3.8-live": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-3.8-live-extended-thinking": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-embedding-001": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "google/gemini-embedding-2": {"tools":false,"images":false,"reasoning":false},
+ "google/gemini-omni-flash-preview": {"tools":false,"images":true,"reasoning":true,"context":1000000,"output":57920},
+ "google/gemma-4-26b-a4b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "google/gemma-4-31b-it": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":131072},
+ "google/text-embedding-005": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "google/text-multilingual-embedding-002": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "inception/mercury-2": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":50000},
+ "inception/mercury-2.5": {"tools":true,"images":false,"reasoning":true,"context":260000,"output":65536},
+ "inception/mercury-coder-small": {"tools":true,"images":false,"reasoning":false,"context":32000,"output":16384},
+ "inclusionai/ling-3.0-flash": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":32000},
+ "inclusionai/ling-3.0-flash-fin": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":32000},
+ "inclusionai/ling-3.0-flash-sante": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":32000},
+ "inclusionai/ling-3.0-flash-sante-free": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":32000},
+ "inclusionai/ling-3.0-flash-vl": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "inclusionai/ling-3.1-flash": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inclusionai/ling-3.1-flash-free": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "inference-net/schematron-v2-small": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "inference-net/schematron-v2-turbo": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "interfaze/interfaze-beta": {"tools":false,"images":true,"reasoning":true,"context":1000000,"output":32000},
+ "meituan/longcat-2.5-preview": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "meta/llama-3.1-70b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "meta/llama-3.1-8b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":8192},
+ "meta/llama-3.3-70b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "meta/llama-4-maverick": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "meta/llama-4-scout": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "meta/muse-glimmer-30b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":131072},
+ "meta/muse-spark-1.1": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "meta/muse-spark-1.2": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "meta/muse-spark-1.2-contributor": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "meta/muse-spark-1.3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "meta/muse-spark-1.3-contributor": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "microsoft/mai-transcribe-1.5": {"tools":false,"images":false,"reasoning":false},
+ "microsoft/mai-transcribe-2": {"tools":false,"images":false,"reasoning":false},
+ "microsoft/mai-transcribe-2-streaming": {"tools":false,"images":false,"reasoning":false},
+ "minimax/minimax-m2": {"tools":true,"images":false,"reasoning":true,"context":205000,"output":196608},
+ "minimax/minimax-m2.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.1-lightning": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131000},
+ "minimax/minimax-m2.5-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131000},
+ "minimax/minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131000},
+ "minimax/minimax-m2.7-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131100},
+ "minimax/minimax-m3": {"tools":true,"images":true,"reasoning":true,"context":512000,"output":512000},
+ "mistral/codestral": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":4096},
+ "mistral/codestral-embed": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "mistral/ministral-14b": {"tools":false,"images":true,"reasoning":false,"context":262144,"output":256000},
+ "mistral/ministral-3b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":128000},
+ "mistral/ministral-8b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":128000},
+ "mistral/mistral-embed": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "mistral/mistral-large-3": {"tools":false,"images":true,"reasoning":false,"context":262144,"output":256000},
+ "mistral/mistral-medium-3.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":256000},
+ "mistral/mistral-nemo": {"tools":true,"images":false,"reasoning":false,"context":60288,"output":16000},
+ "mistral/mistral-small": {"tools":true,"images":true,"reasoning":false,"context":262144,"output":4000},
+ "mixedbread/toast-1": {"tools":true,"images":false,"reasoning":false,"context":131000,"output":4000},
+ "moonshotai/kimi-k2": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":131072},
+ "moonshotai/kimi-k2-thinking": {"tools":true,"images":false,"reasoning":true,"context":216144,"output":216144},
+ "moonshotai/kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "moonshotai/kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262000,"output":262000},
+ "moonshotai/kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32768},
+ "moonshotai/kimi-k2.7-code-highspeed": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "moonshotai/kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "moonshotai/kimi-k3-fast": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "morph/morph-v3-fast": {"tools":false,"images":false,"reasoning":false,"context":16000,"output":16000},
+ "morph/morph-v3-large": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "nvidia/nemotron-3-nano-30b-a3b": {"tools":false,"images":false,"reasoning":true,"context":262144,"output":262144},
+ "nvidia/nemotron-3-super-120b-a12b": {"tools":false,"images":false,"reasoning":true,"context":256000,"output":32000},
+ "nvidia/nemotron-3-ultra-550b-a55b": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":65000},
+ "nvidia/nemotron-3.5-lightning": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":131072},
+ "nvidia/nemotron-nano-12b-v2-vl": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":131072},
+ "nvidia/nemotron-nano-9b-v2": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":131072},
+ "openai/gpt-3.5-turbo": {"tools":false,"images":false,"reasoning":false,"context":16385,"output":4096},
+ "openai/gpt-4.1": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-fast": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-mini": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-mini-fast": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4.1-nano-fast": {"tools":true,"images":true,"reasoning":false,"context":1047576,"output":32768},
+ "openai/gpt-4o": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-fast": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-mini": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-mini-fast": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":16384},
+ "openai/gpt-4o-mini-transcribe": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-4o-transcribe": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-5": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-mini-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":272000},
+ "openai/gpt-5.1-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-codex-max": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-codex-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-thinking": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.1-thinking-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.2-pro": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.3-codex": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.3-codex-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.4-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.4-mini": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4-mini-fast": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4-nano": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "openai/gpt-5.4-pro": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.5": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "openai/gpt-5.5-fast": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "openai/gpt-5.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":128000},
+ "openai/gpt-5.6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-luna-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-sol-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-terra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-5.6-terra-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-astra": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-astra-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-luna": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-luna-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6-sol-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6.1-sol": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-6.1-sol-fast": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":128000},
+ "openai/gpt-live-1": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-oss-120b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":131072},
+ "openai/gpt-oss-20b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "openai/gpt-oss-safeguard-120b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16000},
+ "openai/gpt-oss-safeguard-20b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16000},
+ "openai/gpt-realtime-1.5": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-realtime-2": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-realtime-2.1": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "openai/gpt-realtime-mini": {"tools":false,"images":false,"reasoning":false},
+ "openai/gpt-realtime-whisper": {"tools":false,"images":false,"reasoning":false},
+ "openai/o3": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3-fast": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o3-pro": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/o4-mini-fast": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":100000},
+ "openai/text-embedding-3-large": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "openai/text-embedding-3-small": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "openai/text-embedding-ada-002": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "openai/whisper-1": {"tools":false,"images":false,"reasoning":false},
+ "perplexity/pplx-embed-v1-0.6b": {"tools":false,"images":false,"reasoning":false,"context":32000},
+ "perplexity/pplx-embed-v1-4b": {"tools":false,"images":false,"reasoning":false,"context":32000},
+ "perplexity/sonar": {"tools":true,"images":true,"reasoning":false,"context":127000,"output":8000},
+ "poolside/laguna-s-2.1": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "poolside/laguna-s-2.1-free": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":32768},
+ "quiverai/arrow-2": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "quiverai/arrow-2-telos": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "sakana/fugu-max": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "sakana/fugu-ultra": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "sakana/fugu-ultra-v2": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "sakana/namazu": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "spacexai/grok-4.1-fast-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":1000000,"output":1000000},
+ "spacexai/grok-4.1-fast-reasoning": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "spacexai/grok-4.20-multi-agent": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "spacexai/grok-4.20-multi-agent-beta": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "spacexai/grok-4.20-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":2000000,"output":2000000},
+ "spacexai/grok-4.20-non-reasoning-beta": {"tools":true,"images":true,"reasoning":false,"context":2000000,"output":2000000},
+ "spacexai/grok-4.20-reasoning": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "spacexai/grok-4.20-reasoning-beta": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "spacexai/grok-4.3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "spacexai/grok-4.5": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "spacexai/grok-4.6": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "spacexai/grok-4.7": {"tools":true,"images":true,"reasoning":true,"context":500000,"output":500000},
+ "spacexai/grok-build-0.1": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "spacexai/grok-stt": {"tools":false,"images":false,"reasoning":false},
+ "spacexai/grok-voice-think-fast-1.0": {"tools":false,"images":false,"reasoning":false},
+ "spacexai/grok-voice-think-fast-2.0": {"tools":false,"images":false,"reasoning":false},
+ "stepfun/step-3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":262114,"output":262114},
+ "stepfun/step-3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "stepfun/step-5-preview": {"tools":false,"images":true,"reasoning":false,"context":1000000,"output":1000000},
+ "tencent/hy-mt2-lite": {"tools":false,"images":false,"reasoning":false,"context":8000,"output":4000},
+ "tencent/hy-mt2-plus": {"tools":false,"images":false,"reasoning":false,"context":8000,"output":4000},
+ "tencent/hy-mt2-pro": {"tools":false,"images":false,"reasoning":false,"context":8000,"output":4000},
+ "tencent/hy3": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":262144},
+ "tencent/hy4-preview": {"tools":true,"images":false,"reasoning":true,"context":1024000,"output":64000},
+ "thinkingmachines/inkling": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "thinkingmachines/inkling-small": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":1000000},
+ "voyage/rerank-2.5": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "voyage/rerank-2.5-lite": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "voyage/rerank-3": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "voyage/rerank-3-lite": {"tools":false,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "voyage/voyage-3-large": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-3.5": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-3.5-lite": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-4": {"tools":false,"images":false,"reasoning":false,"context":32000},
+ "voyage/voyage-4-large": {"tools":false,"images":false,"reasoning":false,"context":32000},
+ "voyage/voyage-4-lite": {"tools":false,"images":false,"reasoning":false,"context":32000},
+ "voyage/voyage-code-2": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-code-3": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-finance-2": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "voyage/voyage-law-2": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1536},
+ "xiaomi/mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1050000,"output":131100},
+ "xiaomi/mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1050000,"output":131000},
+ "xiaomi/mimo-v2.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomi/mimo-v2.6-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomi/mimo-v2.6-pro-ultraspeed": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "zai/glm-4.5": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":96000},
+ "zai/glm-4.5-air": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":96000},
+ "zai/glm-4.5v": {"tools":true,"images":true,"reasoning":true,"context":66000,"output":16000},
+ "zai/glm-4.6": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":96000},
+ "zai/glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":120000},
+ "zai/glm-4.7-flash": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":131000},
+ "zai/glm-4.7-flashx": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "zai/glm-5": {"tools":true,"images":false,"reasoning":true,"context":202800,"output":131100},
+ "zai/glm-5-turbo": {"tools":true,"images":false,"reasoning":true,"context":202800,"output":131072},
+ "zai/glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":202800,"output":64000},
+ "zai/glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":128000},
+ "zai/glm-5.2-fast": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":128000},
+ "zai/glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":1000000},
+ "zai/glm-5.3-fast": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":262144},
+ "zai/glm-5.3-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131000},
+ "zai/glm-5.3-flashx": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "zai/glm-5v-turbo": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":128000}
+ },
+ "doubao": {
+ "deepseek-v4-flash-ga-260731": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-pro-ga-260813": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "doubao-seed-1-6-251015": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":64000},
+ "doubao-seed-1-6-flash-250828": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-1-6-vision-250815": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-1-8-251228": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":64000},
+ "doubao-seed-2-0-code-preview-260215": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "doubao-seed-2-0-lite-260428": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":131072},
+ "doubao-seed-2-0-mini-260428": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":131072},
+ "doubao-seed-2-0-pro-260215": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "doubao-seed-2-1-pro-260628": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "doubao-seed-2-1-turbo-260628": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "doubao-seed-character-260628": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "doubao-seed-evolving": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":256000},
+ "glm-5-2-260617": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "glm-5-3-flash-260828": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072}
+ },
+ "modelscope": {
+ "Qwen/Qwen3-235B-A22B-Instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":131072},
+ "Qwen/Qwen3-235B-A22B-Thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":131072},
+ "Qwen/Qwen3-30B-A3B-Instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":16384},
+ "Qwen/Qwen3-30B-A3B-Thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32768},
+ "Qwen/Qwen3-Coder-30B-A3B-Instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "ZhipuAI/GLM-4.5": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "ZhipuAI/GLM-4.6": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":98304}
+ },
+ "glm": {
+ "glm-4.5": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "glm-4.5-air": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "glm-4.5-flash": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "glm-4.5v": {"tools":true,"images":true,"reasoning":true,"context":64000,"output":16384},
+ "glm-4.6": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "glm-4.6v": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":32768},
+ "glm-4.6v-flash": {"tools":true,"images":true,"reasoning":true,"context":128000,"output":32768},
+ "glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "glm-4.7-flash": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":131072},
+ "glm-4.7-flashx": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":131072},
+ "glm-5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":131072},
+ "glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "glm-5.3-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "glm-5.3-flashx": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "glm-5v-turbo": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":131072}
+ },
+ "qwen": {
+ "deepseek-r1": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "deepseek-r1-0528": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "deepseek-r1-distill-llama-70b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-r1-distill-llama-8b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-r1-distill-qwen-1-5b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-r1-distill-qwen-14b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-r1-distill-qwen-32b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-r1-distill-qwen-7b": {"tools":true,"images":false,"reasoning":true,"context":32768,"output":16384},
+ "deepseek-v3": {"tools":true,"images":false,"reasoning":false,"context":65536,"output":8192},
+ "deepseek-v3-1": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":65536},
+ "deepseek-v3-2-exp": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":65536},
+ "deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":384000},
+ "deepseek-v4.1-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":384000},
+ "glm-5": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":16384},
+ "glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":202752,"output":128000},
+ "glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":128000},
+ "glm-5.3": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":131072},
+ "kimi-k2-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":16384},
+ "kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":16384},
+ "kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "kimi/kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "MiniMax-M2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax/MiniMax-M2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "moonshot-kimi-k2-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qvq-max": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":8192},
+ "qwen-deep-research": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":32768},
+ "qwen-doc-turbo": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen-flash": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":32768},
+ "qwen-long": {"tools":true,"images":false,"reasoning":false,"context":10000000,"output":8192},
+ "qwen-math-plus": {"tools":true,"images":false,"reasoning":false,"context":4096,"output":3072},
+ "qwen-math-turbo": {"tools":true,"images":false,"reasoning":false,"context":4096,"output":3072},
+ "qwen-max": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen-mt-plus": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":8192},
+ "qwen-mt-turbo": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":8192},
+ "qwen-omni-turbo": {"tools":true,"images":true,"reasoning":false,"context":32768,"output":2048},
+ "qwen-omni-turbo-realtime": {"tools":true,"images":true,"reasoning":false,"context":32768,"output":2048},
+ "qwen-plus": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":32768},
+ "qwen-plus-character": {"tools":true,"images":false,"reasoning":false,"context":32768,"output":4096},
+ "qwen-turbo": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":16384},
+ "qwen-vl-max": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "qwen-vl-ocr": {"tools":false,"images":true,"reasoning":false,"context":34096,"output":4096},
+ "qwen-vl-plus": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-14b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-32b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-72b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-7b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-coder-32b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-coder-7b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-math-72b-instruct": {"tools":true,"images":false,"reasoning":false,"context":4096,"output":3072},
+ "qwen2-5-math-7b-instruct": {"tools":true,"images":false,"reasoning":false,"context":4096,"output":3072},
+ "qwen2-5-omni-7b": {"tools":true,"images":true,"reasoning":false,"context":32768,"output":2048},
+ "qwen2-5-vl-72b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "qwen2-5-vl-7b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "qwen3-14b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "qwen3-235b-a22b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "qwen3-32b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":16384},
+ "qwen3-8b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "qwen3-asr-flash": {"tools":false,"images":false,"reasoning":false,"context":53248,"output":4096},
+ "qwen3-coder-30b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen3-coder-480b-a35b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen3-coder-flash": {"tools":true,"images":false,"reasoning":false,"context":1000000,"output":65536},
+ "qwen3-coder-plus": {"tools":true,"images":false,"reasoning":false,"context":1048576,"output":65536},
+ "qwen3-max": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "qwen3-next-80b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":32768},
+ "qwen3-next-80b-a3b-thinking": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "qwen3-omni-flash": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":16384},
+ "qwen3-omni-flash-realtime": {"tools":true,"images":true,"reasoning":false,"context":65536,"output":16384},
+ "qwen3-vl-235b-a22b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "qwen3-vl-30b-a3b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "qwen3-vl-plus": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":32768},
+ "qwen3.5-397b-a17b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen3.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.5-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.6-max-preview": {"tools":true,"images":false,"reasoning":true,"context":245800,"output":65536},
+ "qwen3.6-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.7-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3.7-max": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":65536},
+ "qwen3.7-plus": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "qwen3.8-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3.8-max": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwen3.8-omni-flash": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":131072},
+ "qwq-32b": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "qwq-plus": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":8192},
+ "siliconflow/deepseek-r1-0528": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":32768},
+ "siliconflow/deepseek-v3-0324": {"tools":true,"images":false,"reasoning":false,"context":163840,"output":163840},
+ "siliconflow/deepseek-v3.1-terminus": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":65536},
+ "siliconflow/deepseek-v3.2": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":65536},
+ "tongyi-intent-detect-v3": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":1024}
+ },
+ "qiniu": {
+ "claude-3.5-haiku": {"tools":true,"images":true,"reasoning":false,"context":200000,"output":8192},
+ "claude-3.5-sonnet": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":8200},
+ "claude-3.7-sonnet": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":128000},
+ "claude-4.0-opus": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "claude-4.0-sonnet": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-4.1-opus": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":32000},
+ "claude-4.5-haiku": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "claude-4.5-opus": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":200000},
+ "claude-4.5-sonnet": {"tools":true,"images":true,"reasoning":true,"context":200000,"output":64000},
+ "deepseek-r1": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek-r1-0528": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek-v3": {"tools":false,"images":false,"reasoning":false,"context":128000,"output":16000},
+ "deepseek-v3-0324": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":16000},
+ "deepseek-v3.1": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek/deepseek-math-v2": {"tools":false,"images":false,"reasoning":true,"context":160000,"output":160000},
+ "deepseek/deepseek-v3.1-terminus": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":32000},
+ "deepseek/deepseek-v3.1-terminus-thinking": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek/deepseek-v3.2-251201": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek/deepseek-v3.2-exp": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":32000},
+ "deepseek/deepseek-v3.2-exp-thinking": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "doubao-1.5-pro-32k": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":12000},
+ "doubao-1.5-thinking-pro": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":16000},
+ "doubao-1.5-vision-pro": {"tools":false,"images":true,"reasoning":false,"context":128000,"output":16000},
+ "doubao-seed-1.6": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-1.6-flash": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-1.6-thinking": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-2.0-code": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "doubao-seed-2.0-lite": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-2.0-mini": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":32000},
+ "doubao-seed-2.0-pro": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":128000},
+ "gemini-2.0-flash": {"tools":true,"images":true,"reasoning":false,"context":1048576,"output":8192},
+ "gemini-2.0-flash-lite": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":8192},
+ "gemini-2.5-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":64000},
+ "gemini-2.5-flash-lite": {"tools":true,"images":true,"reasoning":false,"context":1048576,"output":64000},
+ "gemini-2.5-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":65536},
+ "gemini-3.0-flash-preview": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "gemini-3.0-pro-image-preview": {"tools":false,"images":true,"reasoning":false,"context":32768,"output":8192},
+ "gemini-3.0-pro-preview": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":64000},
+ "glm-4.5": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "glm-4.5-air": {"tools":true,"images":false,"reasoning":true,"context":131000,"output":4096},
+ "gpt-oss-120b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":4096},
+ "gpt-oss-20b": {"tools":true,"images":false,"reasoning":true,"context":128000,"output":4096},
+ "kimi-k2": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":128000},
+ "meituan/longcat-flash-chat": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":131072},
+ "meituan/longcat-flash-lite": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":320000},
+ "mimo-v2-flash": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":256000},
+ "MiniMax-M1": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":80000},
+ "minimax/minimax-m2": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "minimax/minimax-m2.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "minimax/minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "minimax/minimax-m2.5-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":128000},
+ "moonshotai/kimi-k2-0905": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":100000},
+ "moonshotai/kimi-k2-thinking": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":100000},
+ "moonshotai/kimi-k2.5": {"tools":true,"images":true,"reasoning":false,"context":256000,"output":256000},
+ "openai/gpt-5": {"tools":true,"images":false,"reasoning":false,"context":400000,"output":128000},
+ "openai/gpt-5.2": {"tools":true,"images":true,"reasoning":true,"context":400000,"output":128000},
+ "qwen-max-2025-01-25": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":4096},
+ "qwen-turbo": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":4096},
+ "qwen-vl-max-2025-01-25": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":4096},
+ "qwen2.5-vl-72b-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "qwen2.5-vl-7b-instruct": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":8192},
+ "qwen3-235b-a22b": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":32000},
+ "qwen3-235b-a22b-instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":64000},
+ "qwen3-235b-a22b-thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":4096},
+ "qwen3-30b-a3b": {"tools":true,"images":false,"reasoning":true,"context":40000,"output":4096},
+ "qwen3-30b-a3b-instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":128000,"output":32000},
+ "qwen3-30b-a3b-thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":126000,"output":32000},
+ "qwen3-32b": {"tools":true,"images":false,"reasoning":true,"context":40000,"output":4096},
+ "qwen3-coder-480b-a35b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262000,"output":4096},
+ "qwen3-max": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen3-max-preview": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":64000},
+ "qwen3-next-80b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":32768},
+ "qwen3-next-80b-a3b-thinking": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "qwen3-vl-30b-a3b-thinking": {"tools":true,"images":true,"reasoning":false,"context":128000,"output":32000},
+ "qwen3.5-397b-a17b": {"tools":true,"images":true,"reasoning":true,"context":256000,"output":64000},
+ "stepfun-ai/gelab-zero-4b-preview": {"tools":true,"images":true,"reasoning":false,"context":8192,"output":4096},
+ "stepfun/step-3.5-flash": {"tools":false,"images":true,"reasoning":false,"context":64000,"output":4096},
+ "x-ai/grok-4-fast": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "x-ai/grok-4-fast-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":2000000,"output":2000000},
+ "x-ai/grok-4-fast-reasoning": {"tools":true,"images":true,"reasoning":true,"context":2000000,"output":2000000},
+ "x-ai/grok-4.1-fast": {"tools":true,"images":false,"reasoning":true,"context":2000000,"output":2000000},
+ "x-ai/grok-4.1-fast-non-reasoning": {"tools":true,"images":true,"reasoning":false,"context":2000000,"output":2000000},
+ "x-ai/grok-4.1-fast-reasoning": {"tools":true,"images":true,"reasoning":true,"context":20000000,"output":2000000},
+ "x-ai/grok-code-fast-1": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":10000},
+ "xiaomi/mimo-v2-flash": {"tools":true,"images":false,"reasoning":true,"context":256000,"output":256000},
+ "z-ai/autoglm-phone-9b": {"tools":true,"images":true,"reasoning":false,"context":12800,"output":4096},
+ "z-ai/glm-4.6": {"tools":true,"images":false,"reasoning":false,"context":200000,"output":200000},
+ "z-ai/glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":200000},
+ "z-ai/glm-5": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000}
+ },
+ "kimi": {
+ "kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k2.7-code-highspeed": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576}
+ },
+ "minimax": {
+ "MiniMax-M2": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M2.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M2.5-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M2.7-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "MiniMax-M3": {"tools":true,"images":true,"reasoning":true,"context":1000000,"output":512000}
+ },
+ "novita": {
+ "baichuan/baichuan-m2-32b": {"tools":false,"images":false,"reasoning":false,"context":131072,"output":131072},
+ "baidu/ernie-4.5-21B-a3b": {"tools":true,"images":false,"reasoning":false,"context":120000,"output":8000},
+ "baidu/ernie-4.5-21B-a3b-thinking": {"tools":false,"images":false,"reasoning":true,"context":131072,"output":65536},
+ "baidu/ernie-4.5-300b-a47b-paddle": {"tools":false,"images":false,"reasoning":false,"context":123000,"output":12000},
+ "baidu/ernie-4.5-vl-28b-a3b": {"tools":true,"images":true,"reasoning":true,"context":30000,"output":8000},
+ "baidu/ernie-4.5-vl-28b-a3b-thinking": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":65536},
+ "baidu/ernie-4.5-vl-424b-a47b": {"tools":false,"images":true,"reasoning":true,"context":123000,"output":16000},
+ "deepseek/deepseek-ocr": {"tools":false,"images":true,"reasoning":false,"context":8192,"output":8192},
+ "deepseek/deepseek-ocr-2": {"tools":false,"images":true,"reasoning":false,"context":8192,"output":8192},
+ "deepseek/deepseek-prover-v2-671b": {"tools":false,"images":false,"reasoning":false,"context":160000,"output":160000},
+ "deepseek/deepseek-r1-0528": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":32768},
+ "deepseek/deepseek-r1-0528-qwen3-8b": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":32000},
+ "deepseek/deepseek-r1-distill-llama-70b": {"tools":false,"images":false,"reasoning":true,"context":8192,"output":8192},
+ "deepseek/deepseek-r1-distill-qwen-14b": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":16384},
+ "deepseek/deepseek-r1-distill-qwen-32b": {"tools":false,"images":false,"reasoning":false,"context":64000,"output":32000},
+ "deepseek/deepseek-r1-turbo": {"tools":true,"images":false,"reasoning":true,"context":64000,"output":16000},
+ "deepseek/deepseek-v3-0324": {"tools":true,"images":false,"reasoning":false,"context":163840,"output":163840},
+ "deepseek/deepseek-v3-turbo": {"tools":true,"images":false,"reasoning":false,"context":64000,"output":16000},
+ "deepseek/deepseek-v3.1": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "deepseek/deepseek-v3.1-terminus": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "deepseek/deepseek-v3.2": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":65536},
+ "deepseek/deepseek-v3.2-exp": {"tools":true,"images":false,"reasoning":true,"context":163840,"output":65536},
+ "deepseek/deepseek-v4-flash": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":393216},
+ "deepseek/deepseek-v4-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":393216},
+ "google/gemma-3-12b-it": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":8192},
+ "google/gemma-3-27b-it": {"tools":false,"images":true,"reasoning":false,"context":98304,"output":16384},
+ "google/gemma-4-26b-a4b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "google/gemma-4-31b-it": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":131072},
+ "gryphe/mythomax-l2-13b": {"tools":false,"images":false,"reasoning":false,"context":4096,"output":3200},
+ "inclusionai/ling-2.6-1t": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":32768},
+ "inclusionai/ling-2.6-flash": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":32768},
+ "inclusionai/ring-2.6-1t": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":65536},
+ "kwaipilot/kat-coder-pro": {"tools":true,"images":false,"reasoning":false,"context":256000,"output":128000},
+ "meta-llama/llama-3-70b-instruct": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":8000},
+ "meta-llama/llama-3-8b-instruct": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":8192},
+ "meta-llama/llama-3.1-8b-instruct": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":16384},
+ "meta-llama/llama-3.2-3b-instruct": {"tools":false,"images":false,"reasoning":false,"context":32768,"output":32000},
+ "meta-llama/llama-3.3-70b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":120000},
+ "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": {"tools":false,"images":true,"reasoning":false,"context":1048576,"output":8192},
+ "meta-llama/llama-4-scout-17b-16e-instruct": {"tools":false,"images":true,"reasoning":false,"context":131072,"output":131072},
+ "microsoft/wizardlm-2-8x22b": {"tools":false,"images":false,"reasoning":false,"context":65535,"output":8000},
+ "minimax/minimax-m2": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.1": {"tools":true,"images":false,"reasoning":false,"context":204800,"output":131072},
+ "minimax/minimax-m2.5": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.5-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimax/minimax-m2.7-highspeed": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "minimaxai/minimax-m1-80k": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":40000},
+ "mistralai/mistral-nemo": {"tools":false,"images":false,"reasoning":false,"context":60288,"output":16000},
+ "moonshotai/kimi-k2-0905": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":98304},
+ "moonshotai/kimi-k2-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":32768},
+ "moonshotai/kimi-k2-thinking": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":98304},
+ "moonshotai/kimi-k2.5": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "moonshotai/kimi-k2.6": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "moonshotai/kimi-k2.7-code": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":262144},
+ "moonshotai/kimi-k3": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":1048576},
+ "nousresearch/hermes-2-pro-llama-3-8b": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":8192},
+ "openai/gpt-oss-120b": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "openai/gpt-oss-20b": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "paddlepaddle/paddleocr-vl": {"tools":false,"images":true,"reasoning":false,"context":16384,"output":16384},
+ "qwen/qwen-2.5-72b-instruct": {"tools":true,"images":false,"reasoning":false,"context":32000,"output":8192},
+ "qwen/qwen-mt-plus": {"tools":false,"images":false,"reasoning":false,"context":16384,"output":8192},
+ "qwen/qwen2.5-7b-instruct": {"tools":true,"images":false,"reasoning":false,"context":32000,"output":32000},
+ "qwen/qwen2.5-vl-72b-instruct": {"tools":false,"images":true,"reasoning":false,"context":32768,"output":32768},
+ "qwen/qwen3-235b-a22b-fp8": {"tools":false,"images":false,"reasoning":true,"context":40960,"output":20000},
+ "qwen/qwen3-235b-a22b-instruct-2507": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":16384},
+ "qwen/qwen3-235b-a22b-thinking-2507": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "qwen/qwen3-30b-a3b-fp8": {"tools":false,"images":false,"reasoning":true,"context":40960,"output":20000},
+ "qwen/qwen3-32b-fp8": {"tools":false,"images":false,"reasoning":true,"context":40960,"output":20000},
+ "qwen/qwen3-4b-fp8": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":20000},
+ "qwen/qwen3-8b-fp8": {"tools":false,"images":false,"reasoning":true,"context":128000,"output":20000},
+ "qwen/qwen3-coder-30b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":160000,"output":32768},
+ "qwen/qwen3-coder-480b-a35b-instruct": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen/qwen3-coder-next": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen/qwen3-max": {"tools":true,"images":false,"reasoning":false,"context":262144,"output":65536},
+ "qwen/qwen3-next-80b-a3b-instruct": {"tools":true,"images":false,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3-next-80b-a3b-thinking": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":32768},
+ "qwen/qwen3-omni-30b-a3b-instruct": {"tools":true,"images":true,"reasoning":false,"context":65536,"output":16384},
+ "qwen/qwen3-omni-30b-a3b-thinking": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":16384},
+ "qwen/qwen3-vl-235b-a22b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3-vl-235b-a22b-thinking": {"tools":false,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "qwen/qwen3-vl-30b-a3b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3-vl-30b-a3b-thinking": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3-vl-8b-instruct": {"tools":true,"images":true,"reasoning":false,"context":131072,"output":32768},
+ "qwen/qwen3.5-122b-a10b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.5-27b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.5-35b-a3b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":65536},
+ "qwen/qwen3.5-397b-a17b": {"tools":true,"images":true,"reasoning":true,"context":262144,"output":64000},
+ "qwen/qwen3.7-max": {"tools":true,"images":false,"reasoning":true,"context":1000000,"output":65536},
+ "sao10K/l3-70b-euryale-v2.1": {"tools":true,"images":false,"reasoning":false,"context":8192,"output":8192},
+ "sao10K/l3-8b-lunaris": {"tools":false,"images":false,"reasoning":false,"context":8192,"output":8192},
+ "sao10K/L3-8B-stheno-v3.2": {"tools":true,"images":false,"reasoning":false,"context":8192,"output":32000},
+ "sao10K/l31-70b-euryale-v2.2": {"tools":true,"images":false,"reasoning":false,"context":8192,"output":8192},
+ "xiaomimimo/mimo-v2-flash": {"tools":true,"images":false,"reasoning":true,"context":262144,"output":32000},
+ "xiaomimimo/mimo-v2-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "xiaomimimo/mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "zai-org/autoglm-phone-9b-multilingual": {"tools":false,"images":true,"reasoning":false,"context":65536,"output":65536},
+ "zai-org/glm-4.5": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "zai-org/glm-4.5-air": {"tools":true,"images":false,"reasoning":true,"context":131072,"output":98304},
+ "zai-org/glm-4.5v": {"tools":true,"images":true,"reasoning":true,"context":65536,"output":16384},
+ "zai-org/glm-4.6": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "zai-org/glm-4.6v": {"tools":true,"images":true,"reasoning":true,"context":131072,"output":32768},
+ "zai-org/glm-4.7": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "zai-org/glm-4.7-flash": {"tools":true,"images":false,"reasoning":true,"context":200000,"output":128000},
+ "zai-org/glm-5": {"tools":true,"images":false,"reasoning":true,"context":202800,"output":131072},
+ "zai-org/glm-5.1": {"tools":true,"images":false,"reasoning":true,"context":204800,"output":131072},
+ "zai-org/glm-5.2": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072}
+ },
+ "mimo": {
+ "mimo-v2.5": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.5-pro": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.5-pro-ultraspeed": {"tools":true,"images":false,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-flash": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-pro": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072},
+ "mimo-v2.6-pro-ultraspeed": {"tools":true,"images":true,"reasoning":true,"context":1048576,"output":131072}
+ }
+}
diff --git a/lib/model-catalog.ts b/lib/model-catalog.ts
new file mode 100644
index 00000000..60f26cf5
--- /dev/null
+++ b/lib/model-catalog.ts
@@ -0,0 +1,45 @@
+import catalog from "@/lib/model-catalog.json"
+import type { ProviderName } from "@/lib/types/model-config"
+
+/**
+ * What models.dev knows about a model (scripts/update-model-catalog.mjs).
+ * Only used for hints: requests are sent the same way either way, since
+ * the data can be wrong or out of date.
+ */
+export interface ModelInfo {
+ tools: boolean
+ images: boolean
+ reasoning: boolean
+ context?: number
+ output?: number
+}
+
+const CATALOG = catalog as Record>
+
+/**
+ * The entry for a model: an exact match ignoring case, else the longest id
+ * the model id starts with, followed by "-", ":" or ".". So
+ * claude-sonnet-4-5-20250929 finds claude-sonnet-4-5, but gpt-4 does not
+ * find gpt-4o.
+ */
+export function getModelInfo(
+ provider: ProviderName,
+ modelId: string,
+): ModelInfo | undefined {
+ const models = CATALOG[provider]
+ if (!models) return undefined
+ const wanted = modelId.trim().toLowerCase()
+ let best: string | undefined
+ for (const id of Object.keys(models)) {
+ const lower = id.toLowerCase()
+ if (lower === wanted) return models[id]
+ if (
+ wanted.startsWith(lower) &&
+ "-:.".includes(wanted[lower.length]) &&
+ lower.length > (best?.length ?? 0)
+ ) {
+ best = id
+ }
+ }
+ return best ? models[best] : undefined
+}
diff --git a/lib/output-token-limit.ts b/lib/output-token-limit.ts
new file mode 100644
index 00000000..28119eea
--- /dev/null
+++ b/lib/output-token-limit.ts
@@ -0,0 +1,224 @@
+import { wrapLanguageModel } from "ai"
+
+type WrappedModel = ReturnType
+
+/**
+ * Default output budget for a chat turn.
+ *
+ * This has to cover thinking + prose + the tool call, because reasoning models
+ * spend it in that order. Measured on deepseek-v4-flash: refining an existing
+ * diagram burned 16000 tokens on thinking alone and the request ended with
+ * finishReason "length" before display_diagram was ever called (issue #924).
+ * 64000 leaves room for the plan and the XML in one turn.
+ */
+export const DEFAULT_MAX_OUTPUT_TOKENS = 64000
+
+/** Ceiling for the user-supplied override, to catch typos like an extra zero. */
+export const MAX_OUTPUT_TOKENS_LIMIT = 200000
+
+/**
+ * Below this a diagram cannot come out whole, so a retry would just produce
+ * truncated XML instead of the provider's error. Better to surface the error.
+ */
+const MIN_USABLE_OUTPUT_TOKENS = 1024
+
+/**
+ * Retry budget when a rejection names the budget parameter but no number we can
+ * read. It is the default from before 64000, which these providers ran with.
+ */
+const FALLBACK_OUTPUT_TOKENS = 16000
+
+/** Status codes that can carry a complaint about the requested budget. */
+const BUDGET_REJECTION_STATUSES = new Set([400, 422])
+
+function usableLimit(value: number): number | null {
+ return value >= MIN_USABLE_OUTPUT_TOKENS ? value : null
+}
+
+/** Message and body of an error that may be about the budget, or null. */
+export function rejectionText(error: unknown): string | null {
+ const err = error as {
+ message?: unknown
+ responseBody?: unknown
+ statusCode?: unknown
+ }
+
+ // An auth or rate-limit failure is not about the budget, so leave it alone.
+ if (
+ typeof err?.statusCode === "number" &&
+ !BUDGET_REJECTION_STATUSES.has(err.statusCode)
+ ) {
+ return null
+ }
+
+ const text = [
+ typeof err?.message === "string" ? err.message : "",
+ typeof err?.responseBody === "string" ? err.responseBody : "",
+ ].join(" ")
+
+ return text.trim() ? text : null
+}
+
+/**
+ * A budget this large exceeds what some models accept. Providers reject it with a
+ * 400 that names the real limit, so we parse the number out and retry once
+ * instead of failing the turn.
+ *
+ * Formats seen in the wild:
+ * - Bedrock: "The maximum tokens you requested exceeds the model limit of 4096."
+ * - OpenRouter: "This endpoint's maximum context length is 64000 tokens. However,
+ * you requested about 64025 tokens (25 of text input, 64000 in the output)."
+ * Note this one is an input+output ceiling, so the input has to be subtracted.
+ * vLLM and SGLang send the same kind of ceiling, with the input written as
+ * "6000 in the messages", "has 6000 input tokens" or "6000 tokens from the input".
+ * - Anthropic: "max_tokens: 200000 > 64000, which is the maximum allowed..."
+ * - OpenAI: "This model supports at most 16384 completion tokens"
+ * - Volcengine Ark: "The parameter `max_tokens` specified in the request are not
+ * valid: integer above maximum value, expected a value <= 32768, but got 64000"
+ * - DashScope: "Range of max_tokens should be [1, 8192]"
+ *
+ * Every pattern names tokens explicitly. A generic one (an earlier draft matched
+ * "lower than N") would reinterpret unrelated failures, and retrying on a bogus
+ * number turns a readable error into an empty diagram.
+ */
+function readCeiling(text: string): number | null {
+ // Combined input+output ceiling: subtract the input the provider counted,
+ // plus a small margin because its estimate is approximate.
+ const context = text.match(/maximum context length (?:is|of) (\d+)/i)
+ if (context) {
+ const input =
+ text.match(/(\d+) of text input/i) ||
+ text.match(/(\d+) in the messages/i) ||
+ text.match(/(\d+) tokens from the input/i) ||
+ text.match(/(\d+) input tokens/i)
+ return Number(context[1]) - (input ? Number(input[1]) : 0) - 1024
+ }
+
+ const output =
+ text.match(/model limit of (\d+)/i) ||
+ text.match(/> (\d+), which is the maximum/i) ||
+ text.match(/at most (\d+) completion tokens/i) ||
+ text.match(/max_\w*tokens.*?expected a value (?:<=|\\u003c=) (\d+)/i) ||
+ text.match(/Range of max_tokens should be \[1,\s*(\d+)\]/i)
+
+ return output ? Number(output[1]) : null
+}
+
+/** The usable output ceiling named in a rejection, or null. */
+export function parseOutputTokenLimit(error: unknown): number | null {
+ const text = rejectionText(error)
+ const ceiling = text ? readCeiling(text) : null
+ return ceiling === null ? null : usableLimit(ceiling)
+}
+
+/**
+ * Thinking budget the provider adds on top of maxOutputTokens. Bedrock and
+ * Anthropic send maxOutputTokens + budgetTokens as max_tokens, so a ceiling in
+ * their rejection covers both.
+ */
+function thinkingBudget(providerOptions: unknown): number {
+ const options = providerOptions as
+ | {
+ bedrock?: {
+ reasoningConfig?: { type?: string; budgetTokens?: unknown }
+ }
+ anthropic?: {
+ thinking?: { type?: string; budgetTokens?: unknown }
+ }
+ }
+ | undefined
+ const config =
+ options?.bedrock?.reasoningConfig ?? options?.anthropic?.thinking
+ return config?.type === "enabled" && typeof config.budgetTokens === "number"
+ ? config.budgetTokens
+ : 0
+}
+
+/**
+ * The budget to retry with after a rejection, or null to surface the error.
+ */
+export function retryOutputTokens(
+ error: unknown,
+ params: { maxOutputTokens?: number; providerOptions?: unknown },
+): number | null {
+ const requested = params.maxOutputTokens
+ const text = rejectionText(error)
+ if (!requested || !text) return null
+
+ const ceiling = readCeiling(text)
+ if (ceiling !== null) {
+ // The ceiling applies to what was actually sent, thinking included,
+ // so the retry has to leave room for the thinking too.
+ const thinking = thinkingBudget(params.providerOptions)
+ if (ceiling >= requested + thinking) return null
+ return usableLimit(ceiling - thinking)
+ }
+
+ // Names the budget parameter, but in a format we cannot read a number from
+ if (/max_\w*tokens/i.test(text) && requested > FALLBACK_OUTPUT_TOKENS) {
+ return FALLBACK_OUTPUT_TOKENS
+ }
+ return null
+}
+
+/**
+ * Retry the stream once with a smaller budget when the provider rejects the
+ * requested one. Without this, raising the default breaks every model whose
+ * ceiling is below it (measured: bedrock claude-3-haiku 4096, nova-lite 10000,
+ * openrouter deepseek-r1 64000 shared with the input).
+ */
+export function withOutputTokenLimitFallback(
+ model: WrappedModel,
+): WrappedModel {
+ return wrapLanguageModel({
+ model,
+ middleware: {
+ specificationVersion: "v3",
+ async wrapStream({ doStream, params, model: inner }) {
+ try {
+ return await doStream()
+ } catch (error) {
+ const retry = retryOutputTokens(error, params)
+ if (!retry) throw error
+
+ console.warn(
+ `[maxOutputTokens] ${params.maxOutputTokens} rejected, retrying with ${retry}`,
+ )
+ return await inner.doStream({
+ ...params,
+ maxOutputTokens: retry,
+ })
+ }
+ },
+ },
+ })
+}
+
+function validBudget(value: string | null | undefined): number | null {
+ const parsed = Number(value)
+ return Number.isInteger(parsed) &&
+ parsed > 0 &&
+ parsed <= MAX_OUTPUT_TOKENS_LIMIT
+ ? parsed
+ : null
+}
+
+/**
+ * Resolve the output budget: user setting (sent as a header so it works in the
+ * desktop app too), then server env, then the default. Both sources go through
+ * the same validation, so a typo in either falls back instead of reaching the
+ * provider.
+ *
+ * On the server's credentials the user setting can only lower the server value,
+ * so MAX_OUTPUT_TOKENS keeps capping what the server pays for.
+ */
+export function resolveMaxOutputTokens(
+ headerValue: string | null,
+ usesServerCredentials: boolean,
+): number {
+ const header = validBudget(headerValue)
+ const server =
+ validBudget(process.env.MAX_OUTPUT_TOKENS) ?? DEFAULT_MAX_OUTPUT_TOKENS
+ if (header === null) return server
+ return usesServerCredentials ? Math.min(header, server) : header
+}
diff --git a/lib/pdf-utils.ts b/lib/pdf-utils.ts
index 2e5c4adb..49db1041 100644
--- a/lib/pdf-utils.ts
+++ b/lib/pdf-utils.ts
@@ -1,4 +1,4 @@
-import { extractText, getDocumentProxy } from "unpdf"
+import { extractText } from "unpdf"
// Maximum characters allowed for extracted text (configurable via env)
const DEFAULT_MAX_EXTRACTED_CHARS = 150000 // 150k chars
@@ -14,6 +14,7 @@ const TEXT_EXTENSIONS = [
".json",
".csv",
".xml",
+ ".svg",
".html",
".css",
".js",
@@ -43,8 +44,10 @@ const TEXT_EXTENSIONS = [
*/
export async function extractPdfText(file: File): Promise {
const buffer = await file.arrayBuffer()
- const pdf = await getDocumentProxy(new Uint8Array(buffer))
- const { text } = await extractText(pdf, { mergePages: true })
+ // Pass raw bytes so unpdf destroys the PDF document when it is done
+ const { text } = await extractText(new Uint8Array(buffer), {
+ mergePages: true,
+ })
return text as string
}
diff --git a/lib/provider-models.ts b/lib/provider-models.ts
new file mode 100644
index 00000000..a809da75
--- /dev/null
+++ b/lib/provider-models.ts
@@ -0,0 +1,255 @@
+import { createGateway } from "ai"
+import { getModelInfo } from "@/lib/model-catalog"
+import { readLimitedBody } from "@/lib/read-limited-body"
+import {
+ normalizeBaseUrl,
+ PROVIDER_INFO,
+ type ProviderName,
+} from "@/lib/types/model-config"
+
+/** A model a provider offers. tools is false when it cannot call tools. */
+export interface ListedModel {
+ id: string
+ tools?: boolean
+}
+
+export const AIHUBMIX_MODELS_ENDPOINT = "https://aihubmix.com/api/v1/models"
+
+export function canListModels(provider: ProviderName): boolean {
+ return (
+ Object.hasOwn(PROVIDER_INFO, provider) &&
+ !!PROVIDER_INFO[provider].modelList
+ )
+}
+
+// Models in OpenAI-style lists that are not for chat
+const NON_CHAT =
+ /(?:^|[-/_])(?:embed(?:ding)?s?|whisper|tts|transcribe|dall-e|moderation|rerank|realtime|sora)(?:$|[-/_])|gpt-image/i
+
+const NON_CHAT_AIHUBMIX_TYPES = new Set([
+ "embedding",
+ "image_generation",
+ "rerank",
+ "transcription",
+ "tts",
+ "video",
+])
+
+/** Chat model ids from AIHubMix's public model list */
+export function extractAihubmixModelIds(payload: unknown): string[] {
+ const data = (payload as { data?: unknown })?.data
+ if (!Array.isArray(data)) return []
+ const ids = new Set()
+ for (const item of data) {
+ const record = item as { model_id?: unknown; types?: unknown }
+ if (typeof record?.model_id !== "string" || !record.model_id.trim()) {
+ continue
+ }
+ const types = new Set(
+ typeof record.types === "string"
+ ? record.types.split(",").map((t) => t.trim())
+ : [],
+ )
+ if (!types.has("llm")) continue
+ if ([...NON_CHAT_AIHUBMIX_TYPES].some((t) => types.has(t))) continue
+ ids.add(record.model_id.trim())
+ }
+ return [...ids]
+}
+
+/**
+ * An error this module wrote itself. Only these texts reach the caller:
+ * the base URL is the caller's and may be an internal address, so anything
+ * else (a parse error quoting the body, a network error naming a host)
+ * stays in the server log.
+ */
+export class ModelListError extends Error {
+ constructor(
+ message: string,
+ readonly statusCode?: number,
+ ) {
+ super(message)
+ this.name = "ModelListError"
+ }
+}
+
+const MAX_LIST_BYTES = 2 * 1024 * 1024
+
+/** A fetch that reads at most MAX_LIST_BYTES of each response */
+function sizeLimitedFetch(fetchFn: typeof fetch): typeof fetch {
+ return async (input, init) => {
+ // Ends a download that is too large (the Gateway SDK passes no
+ // signal of its own)
+ const download = new AbortController()
+ const signal = init?.signal
+ ? AbortSignal.any([init.signal, download.signal])
+ : download.signal
+ const response = await fetchFn(input, { ...init, signal })
+ const body = await readLimitedBody(response, MAX_LIST_BYTES)
+ if (body === null) {
+ download.abort()
+ throw new ModelListError("The model list is too large.")
+ }
+ // The body is already decoded and has its own length now
+ const headers = new Headers(response.headers)
+ headers.delete("content-encoding")
+ headers.delete("content-length")
+ // Some statuses must have no body at all
+ const noBody = [101, 204, 205, 304].includes(response.status)
+ return new Response(noBody ? null : body, {
+ status: response.status,
+ statusText: response.statusText,
+ headers,
+ })
+ }
+}
+
+/** GET a JSON list; a failed request carries its status for the error hint */
+async function getJson(
+ url: string,
+ headers: Record,
+ fetchFn: typeof fetch,
+): Promise {
+ const response = await fetchFn(url, {
+ headers,
+ signal: AbortSignal.timeout(15_000),
+ })
+ if (!response.ok) {
+ throw new ModelListError(
+ `The model list request failed (${response.status})`,
+ response.status,
+ )
+ }
+ const text = await response.text()
+ try {
+ return JSON.parse(text)
+ } catch {
+ throw new ModelListError("The model list was not valid JSON.")
+ }
+}
+
+/**
+ * Where to list from without the user's base URL: where chat goes then. For
+ * Ollama without a key that is the server's Ollama, else the SDK's local
+ * default; a local default in PROVIDER_INFO (SGLang's) only fills the
+ * settings form.
+ */
+function listFallbackUrl(provider: ProviderName, apiKey?: string): string {
+ if (provider === "ollama" && !apiKey) {
+ return process.env.OLLAMA_BASE_URL || "http://127.0.0.1:11434/api"
+ }
+ const url = PROVIDER_INFO[provider].defaultBaseUrl
+ return url?.startsWith("https://") ? url : ""
+}
+
+/**
+ * The provider's chat models, with tool support from the provider's own
+ * data or else models.dev. Only the client's key is used, so the server's
+ * keys never go to a URL the client chose.
+ */
+export async function listProviderModels(
+ provider: ProviderName,
+ { apiKey, baseUrl }: { apiKey?: string; baseUrl?: string },
+ unlimitedFetch: typeof fetch = fetch,
+): Promise {
+ const fetchFn = sizeLimitedFetch(unlimitedFetch)
+ const base = normalizeBaseUrl(baseUrl || listFallbackUrl(provider, apiKey))
+ const bearer: Record = apiKey
+ ? { Authorization: `Bearer ${apiKey}` }
+ : {}
+ let models: ListedModel[]
+
+ // AIHubMix has a public list, unless the user points to another
+ // endpoint, which is OpenAI-compatible
+ const style =
+ provider === "aihubmix" &&
+ baseUrl &&
+ !/^https:\/\/aihubmix\.com(\/v1)?$/.test(base)
+ ? "openai"
+ : PROVIDER_INFO[provider].modelList
+
+ switch (style) {
+ case "anthropic": {
+ const data = await getJson(
+ `${base}/models?limit=1000`,
+ {
+ "x-api-key": apiKey ?? "",
+ "anthropic-version": "2023-06-01",
+ },
+ fetchFn,
+ )
+ models = (data.data ?? []).map((m: { id: string }) => ({
+ id: m.id,
+ }))
+ break
+ }
+ case "google": {
+ // The key goes in a header: in the URL it would end up in logs
+ const data = await getJson(
+ `${base}/models?pageSize=1000`,
+ { "x-goog-api-key": apiKey ?? "" },
+ fetchFn,
+ )
+ models = (data.models ?? [])
+ .filter((m: { supportedGenerationMethods?: string[] }) =>
+ m.supportedGenerationMethods?.includes("generateContent"),
+ )
+ .map((m: { name: string }) => ({
+ id: m.name.replace(/^models\//, ""),
+ }))
+ break
+ }
+ case "ollama": {
+ const api = base.endsWith("/api") ? base : `${base}/api`
+ const data = await getJson(`${api}/tags`, bearer, fetchFn)
+ models = (data.models ?? []).map((m: { name: string }) => ({
+ id: m.name,
+ }))
+ break
+ }
+ case "openrouter": {
+ const data = await getJson(`${base}/models`, bearer, fetchFn)
+ models = (data.data ?? []).map(
+ (m: { id: string; supported_parameters?: string[] }) => ({
+ id: m.id,
+ ...(m.supported_parameters && {
+ tools: m.supported_parameters.includes("tools"),
+ }),
+ }),
+ )
+ break
+ }
+ case "gateway": {
+ const { models: entries } = await createGateway({
+ ...(apiKey && { apiKey }),
+ ...(baseUrl && { baseURL: base }),
+ fetch: fetchFn,
+ }).getAvailableModels()
+ models = entries
+ .filter((m) => !m.modelType || m.modelType === "language")
+ .map((m) => ({ id: m.id }))
+ break
+ }
+ case "aihubmix": {
+ const data = await getJson(AIHUBMIX_MODELS_ENDPOINT, {}, fetchFn)
+ models = extractAihubmixModelIds(data).map((id) => ({ id }))
+ break
+ }
+ default: {
+ if (!base) {
+ throw new ModelListError(
+ `${PROVIDER_INFO[provider].label} needs a base URL to list its models.`,
+ )
+ }
+ const data = await getJson(`${base}/models`, bearer, fetchFn)
+ models = (data.data ?? [])
+ .map((m: { id: string }) => ({ id: m.id }))
+ .filter((m: ListedModel) => !NON_CHAT.test(m.id))
+ }
+ }
+
+ return models.map((m) => ({
+ ...m,
+ tools: m.tools ?? getModelInfo(provider, m.id)?.tools,
+ }))
+}
diff --git a/lib/read-limited-body.ts b/lib/read-limited-body.ts
new file mode 100644
index 00000000..884462f9
--- /dev/null
+++ b/lib/read-limited-body.ts
@@ -0,0 +1,32 @@
+/**
+ * Read a response body, giving up once it passes maxBytes, so a huge
+ * download from a URL the client chose can't exhaust server memory.
+ * Returns null when it is too large; the caller then aborts the request,
+ * which ends the download.
+ */
+export async function readLimitedBody(
+ response: Response,
+ maxBytes: number,
+): Promise {
+ if (Number(response.headers.get("content-length")) > maxBytes) {
+ return null
+ }
+ if (!response.body) return new ArrayBuffer(0)
+
+ const reader = response.body.getReader()
+ const chunks: Uint8Array[] = []
+ let total = 0
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ total += value.byteLength
+ if (total > maxBytes) {
+ // Not awaited: a copy of the body that Next.js keeps (its fetch
+ // dedupe) can hold the cancel back until it is read
+ reader.cancel().catch(() => {})
+ return null
+ }
+ chunks.push(value)
+ }
+ return new Blob(chunks as BlobPart[]).arrayBuffer()
+}
diff --git a/lib/server-model-config.ts b/lib/server-model-config.ts
index a2800d48..6b5220f0 100644
--- a/lib/server-model-config.ts
+++ b/lib/server-model-config.ts
@@ -47,11 +47,14 @@ export interface FlattenedServerModel {
/**
* Convert provider name to URL-safe slug for use in model ID
- * e.g., "OpenAI Production" → "openai-production"
+ * e.g., "OpenAI Production" → "openai-production", "主力" → "4e3b-529b"
+ * Non-ASCII characters become their hex code point so CJK names stay
+ * distinct; the id is sent in HTTP headers, which must be ASCII.
*/
-function slugify(name: string): string {
+export function slugify(name: string): string {
return name
.toLowerCase()
+ .replace(/[^\p{ASCII}]/gu, (c) => `-${c.codePointAt(0)?.toString(16)}-`)
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "")
}
@@ -62,7 +65,54 @@ function getConfigPath(): string {
return path.join(process.cwd(), "ai-models.json")
}
-export async function loadRawServerModelsConfig(): Promise {
+/**
+ * Synthesize a config from a comma-separated AI_MODEL value (Priority 3 fallback).
+ * Lets users expose multiple models without authoring AI_MODELS_CONFIG / ai-models.json.
+ * Triggers only when AI_MODEL contains a comma AND AI_PROVIDER is set to a known provider.
+ */
+function configFromCommaSeparatedAiModel(): ServerModelsConfig | null {
+ const aiModel = process.env.AI_MODEL
+ if (!aiModel || !aiModel.includes(",")) return null
+
+ const aiProvider = process.env.AI_PROVIDER
+ if (!aiProvider) {
+ console.warn(
+ "[server-model-config] AI_MODEL contains commas but AI_PROVIDER is not set; " +
+ "skipping multi-model fallback. Set AI_PROVIDER, or use AI_MODELS_CONFIG / ai-models.json.",
+ )
+ return null
+ }
+ if (!(aiProvider in PROVIDER_INFO)) {
+ console.warn(
+ `[server-model-config] AI_PROVIDER="${aiProvider}" is not a known provider; skipping multi-model fallback.`,
+ )
+ return null
+ }
+
+ const models = Array.from(
+ new Set(
+ aiModel
+ .split(",")
+ .map((s) => s.trim())
+ .filter((s) => s.length > 0),
+ ),
+ )
+ if (models.length === 0) return null
+
+ const providerName = aiProvider as ProviderName
+ return {
+ providers: [
+ {
+ name: PROVIDER_INFO[providerName]?.label || providerName,
+ provider: providerName,
+ models,
+ default: true,
+ },
+ ],
+ }
+}
+
+export async function loadEnvServerModelsConfig(): Promise {
// Priority 1: AI_MODELS_CONFIG env var (JSON string) - for cloud deployments
const envConfig = process.env.AI_MODELS_CONFIG
if (envConfig && envConfig.trim().length > 0) {
@@ -85,15 +135,51 @@ export async function loadRawServerModelsConfig(): Promise {
+ const envConfig = await loadEnvServerModelsConfig()
+
+ // Merge in providers managed via the admin panel (settings.json).
+ // Dynamic import to avoid a module-init cycle with lib/admin/providers.
+ let adminConfig: ServerModelsConfig | null = null
+ try {
+ const { adminProvidersToConfig, loadAdminProviders } = await import(
+ "./admin/providers"
+ )
+ const adminProviders = loadAdminProviders()
+ if (adminProviders.length > 0) {
+ adminConfig = adminProvidersToConfig(adminProviders)
+ }
+ } catch (err) {
console.error(
- "[server-model-config] Failed to load ai-models.json:",
+ "[server-model-config] Failed to load admin providers:",
err,
)
- return null
}
+
+ if (!adminConfig || adminConfig.providers.length === 0) return envConfig
+ if (!envConfig) return adminConfig
+
+ // A panel default overrides an env default
+ const adminHasDefault = adminConfig.providers.some((p) => p.default)
+ const envProviders = adminHasDefault
+ ? envConfig.providers.map((p) =>
+ p.default ? { ...p, default: undefined } : p,
+ )
+ : envConfig.providers
+ return { providers: [...envProviders, ...adminConfig.providers] }
}
export async function loadFlattenedServerModels(): Promise<
@@ -106,6 +192,7 @@ export async function loadFlattenedServerModels(): Promise<
const defaultModelId = process.env.AI_MODEL
const flattened: FlattenedServerModel[] = []
+ const seenIds = new Set()
for (const p of cfg.providers) {
const providerLabel =
@@ -116,6 +203,16 @@ export async function loadFlattenedServerModels(): Promise<
for (const modelId of p.models) {
const id = `server:${nameSlug}:${modelId}`
+ // Names that differ only in case or punctuation share a slug.
+ // A repeated id would always resolve to the first provider's
+ // credentials, so drop it instead.
+ if (seenIds.has(id)) {
+ console.warn(
+ `[server-model-config] Skipping duplicate model id "${id}". Provider names must differ in letters or digits.`,
+ )
+ continue
+ }
+ seenIds.add(id)
// Default model priority:
// 1. From ai-models.json: first model of provider with default: true
diff --git a/lib/session-storage.ts b/lib/session-storage.ts
index 4a51d668..33098367 100644
--- a/lib/session-storage.ts
+++ b/lib/session-storage.ts
@@ -1,5 +1,6 @@
import { type DBSchema, type IDBPDatabase, openDB } from "idb"
import { nanoid } from "nanoid"
+import { toast } from "sonner"
import type { Template } from "./template-storage"
// Constants
@@ -58,36 +59,10 @@ interface ChatSessionDB extends DBSchema {
// Database singleton
let dbPromise: Promise> | null = null
-const resetDBPromise = () => {
- dbPromise = null
-}
-
-const isClosingError = (error: unknown): boolean => {
- return (
- error instanceof DOMException &&
- error.name === "InvalidStateError" &&
- /closing/i.test(error.message)
- )
-}
-
-const withDB = async (
- action: (db: IDBPDatabase) => Promise,
-): Promise => {
- try {
- const db = await getDB()
- return await action(db)
- } catch (error) {
- if (isClosingError(error)) {
- resetDBPromise()
- const db = await getDB()
- return await action(db)
- }
- throw error
- }
-}
async function getDB(): Promise> {
if (!dbPromise) {
+ // A failed or lost connection is not cached: the next call reopens it
dbPromise = openDB(DB_NAME, DB_VERSION, {
upgrade(db, oldVersion) {
if (oldVersion < 1) {
@@ -115,23 +90,29 @@ async function getDB(): Promise> {
}
}
},
- terminated() {
- resetDBPromise()
+ blocked() {
+ // An older tab keeps the DB open, so the upgrade has to wait
+ toast.warning(
+ "Please close other tabs of this app to finish updating chat storage.",
+ { id: "idb-upgrade-blocked", duration: 10000 },
+ )
},
+ blocking(_currentVersion, _blockedVersion, event) {
+ // Another tab needs to upgrade the DB: close our connection so
+ // it is not stuck, and reopen on the next call
+ const db = event.target as IDBDatabase
+ db.close()
+ dbPromise = null
+ },
+ terminated() {
+ // The browser closed the connection (e.g. Safari after a long
+ // time in the background)
+ dbPromise = null
+ },
+ }).catch((error) => {
+ dbPromise = null
+ throw error
})
- dbPromise
- .then((db) => {
- db.onversionchange = () => {
- db.close()
- resetDBPromise()
- }
- db.onclose = () => {
- resetDBPromise()
- }
- })
- .catch(() => {
- resetDBPromise()
- })
}
return dbPromise
}
@@ -146,46 +127,31 @@ export function isIndexedDBAvailable(): boolean {
}
}
-// Check if IndexedDB is actually usable (not just present).
-// Note: Do NOT close the db here - getDB() returns a shared singleton connection
-// that other code depends on.
-export async function isIndexedDBUsable(): Promise {
- if (!isIndexedDBAvailable()) return false
- try {
- await getDB()
- return true
- } catch {
- return false
- }
-}
-
// CRUD Operations
export async function getAllSessionMetadata(): Promise {
if (!isIndexedDBAvailable()) return []
try {
- return await withDB(async (db) => {
- const tx = db.transaction(STORE_NAME, "readonly")
- const index = tx.store.index("by-updated")
- const metadata: SessionMetadata[] = []
+ const db = await getDB()
+ const tx = db.transaction(STORE_NAME, "readonly")
+ const index = tx.store.index("by-updated")
+ const metadata: SessionMetadata[] = []
- // Use cursor to read only metadata fields (avoids loading full messages/XML)
- let cursor = await index.openCursor(null, "prev") // newest first
- while (cursor) {
- const s = cursor.value
- metadata.push({
- id: s.id,
- title: s.title,
- createdAt: s.createdAt,
- updatedAt: s.updatedAt,
- messageCount: s.messages.length,
- hasDiagram:
- !!s.diagramXml && s.diagramXml.trim().length > 0,
- thumbnailDataUrl: s.thumbnailDataUrl,
- })
- cursor = await cursor.continue()
- }
- return metadata
- })
+ // Use cursor to read only metadata fields (avoids loading full messages/XML)
+ let cursor = await index.openCursor(null, "prev") // newest first
+ while (cursor) {
+ const s = cursor.value
+ metadata.push({
+ id: s.id,
+ title: s.title,
+ createdAt: s.createdAt,
+ updatedAt: s.updatedAt,
+ messageCount: s.messages.length,
+ hasDiagram: !!s.diagramXml && s.diagramXml.trim().length > 0,
+ thumbnailDataUrl: s.thumbnailDataUrl,
+ })
+ cursor = await cursor.continue()
+ }
+ return metadata
} catch (error) {
console.error("Failed to get session metadata:", error)
return []
@@ -195,85 +161,70 @@ export async function getAllSessionMetadata(): Promise {
export async function getSession(id: string): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- return (await db.get(STORE_NAME, id)) || null
- })
+ const db = await getDB()
+ return (await db.get(STORE_NAME, id)) || null
} catch (error) {
console.error("Failed to get session:", error)
return null
}
}
+// Returns false on failure (e.g. storage quota exceeded). Other sessions are
+// never deleted automatically; the caller tells the user instead.
export async function saveSession(session: ChatSession): Promise {
if (!isIndexedDBAvailable()) return false
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, session)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, session)
+ // The desktop app opens this port (this origin's chats) next launch
+ window.electronAPI?.chatSaved?.().catch(() => {})
return true
} catch (error) {
- // Handle quota exceeded
- if (
- error instanceof DOMException &&
- error.name === "QuotaExceededError"
- ) {
- console.warn("Storage quota exceeded, deleting oldest session...")
- await deleteOldestSession()
- // Retry once
- try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, session)
- })
- return true
- } catch (retryError) {
- console.error(
- "Failed to save session after cleanup:",
- retryError,
- )
- return false
- }
- } else {
- console.error("Failed to save session:", error)
- return false
- }
+ console.error("Failed to save session:", error)
+ // Reopen the connection next time in case it was lost (Safari reports
+ // "Connection to Indexed Database server lost" without closing it)
+ dbPromise = null
+ return false
}
}
export async function deleteSession(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- await db.delete(STORE_NAME, id)
- })
+ const db = await getDB()
+ await db.delete(STORE_NAME, id)
} catch (error) {
console.error("Failed to delete session:", error)
}
}
export async function getSessionCount(): Promise {
- if (!isIndexedDBAvailable()) return 0
+ return (await readSessionCount()) ?? 0
+}
+
+/** The number of saved chats, or null when it could not be read */
+export async function readSessionCount(): Promise {
+ if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- return await db.count(STORE_NAME)
- })
+ const db = await getDB()
+ return await db.count(STORE_NAME)
} catch (error) {
console.error("Failed to get session count:", error)
- return 0
+ return null
}
}
export async function deleteOldestSession(): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const tx = db.transaction(STORE_NAME, "readwrite")
- const index = tx.store.index("by-updated")
- const cursor = await index.openCursor()
- if (cursor) {
- await cursor.delete()
- }
- await tx.done
- })
+ const db = await getDB()
+ const tx = db.transaction(STORE_NAME, "readwrite")
+ const index = tx.store.index("by-updated")
+ const cursor = await index.openCursor()
+ if (cursor) {
+ await cursor.delete()
+ }
+ await tx.done
} catch (error) {
console.error("Failed to delete oldest session:", error)
}
diff --git a/lib/ssrf-protection.ts b/lib/ssrf-protection.ts
index 3c599f25..20f2e191 100644
--- a/lib/ssrf-protection.ts
+++ b/lib/ssrf-protection.ts
@@ -2,62 +2,200 @@
* SSRF (Server-Side Request Forgery) protection utilities
*/
+import { lookup } from "node:dns/promises"
+
/**
- * Check if URL points to private/internal network
- * Blocks: localhost, private IPs, link-local, AWS metadata service
+ * Check if an IP address (IPv4 or IPv6) belongs to a private/internal range.
+ * Works for both user-supplied literal IPs and DNS-resolved addresses.
*/
-export function isPrivateUrl(urlString: string): boolean {
+function isPrivateIp(ip: string): boolean {
+ const addr = ip.toLowerCase().replace(/^\[|\]$/g, "")
+
+ // IPv6
+ if (addr.includes(":")) {
+ if (addr === "::1" || addr === "::") return true
+ // unique-local (fc00::/7) and IPv4-mapped (::ffff:0:0/96)
+ if (
+ addr.startsWith("fc") ||
+ addr.startsWith("fd") ||
+ addr.startsWith("::ffff:")
+ ) {
+ return true
+ }
+ // link-local (fe80::/10)
+ const linkLocal = addr.match(/^fe([0-9a-f]{2}):/)
+ if (linkLocal) {
+ const high = parseInt(linkLocal[1], 16)
+ if (high >= 0x80 && high <= 0xbf) return true
+ }
+ return false
+ }
+
+ // IPv4
+ const ipv4Match = addr.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
+ if (ipv4Match) {
+ const [, a, b] = ipv4Match.map(Number)
+ if (a === 10) return true // 10.0.0.0/8
+ if (a === 172 && b >= 16 && b <= 31) return true // 172.16.0.0/12
+ if (a === 192 && b === 168) return true // 192.168.0.0/16
+ if (a === 169 && b === 254) return true // 169.254.0.0/16 (link-local)
+ if (a === 127) return true // 127.0.0.0/8 (loopback)
+ if (a === 0) return true // 0.0.0.0/8
+ if (a === 100 && b >= 64 && b <= 127) return true // 100.64.0.0/10 (CGNAT, used by some cloud internal networks)
+ }
+
+ return false
+}
+
+/**
+ * String-only check against well-known private hostnames and literal IPs.
+ * Fast path that avoids a DNS lookup for obvious cases.
+ */
+function isPrivateHostname(hostname: string): boolean {
+ const host = hostname
+ .toLowerCase()
+ .replace(/^\[|\]$/g, "")
+ .replace(/\.$/, "")
+
+ if (
+ host === "localhost" ||
+ host === "127.0.0.1" ||
+ host === "::1" ||
+ host === "::"
+ ) {
+ return true
+ }
+
+ if (host === "169.254.169.254" || host === "metadata.google.internal") {
+ return true
+ }
+
+ if (
+ host.endsWith(".local") ||
+ host.endsWith(".internal") ||
+ host.endsWith(".localhost")
+ ) {
+ return true
+ }
+
+ // Literal IP supplied directly in the URL
+ return isPrivateIp(host)
+}
+
+/**
+ * Check if URL points to private/internal network.
+ * Blocks: localhost, private IPs, link-local, AWS metadata service.
+ *
+ * Resolves the hostname via DNS and validates every returned address, so
+ * public-looking names that map to internal IPs (e.g. "127-0-0-1.sslip.io")
+ * are caught even though they pass the string-only check.
+ */
+export async function isPrivateUrl(urlString: string): Promise {
try {
const url = new URL(urlString)
- const hostname = url.hostname.toLowerCase()
+ const hostname = url.hostname
- // Block localhost
- if (
- hostname === "localhost" ||
- hostname === "127.0.0.1" ||
- hostname === "::1"
- ) {
- return true
- }
+ // Fast path: obvious string matches and literal IPs.
+ if (isPrivateHostname(hostname)) return true
- // Block AWS/cloud metadata endpoints
- if (
- hostname === "169.254.169.254" ||
- hostname === "metadata.google.internal"
- ) {
- return true
- }
-
- // Check for private IPv4 ranges
- const ipv4Match = hostname.match(
- /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/,
- )
- if (ipv4Match) {
- const [, a, b] = ipv4Match.map(Number)
- if (a === 10) return true // 10.0.0.0/8
- if (a === 172 && b >= 16 && b <= 31) return true // 172.16.0.0/12
- if (a === 192 && b === 168) return true // 192.168.0.0/16
- if (a === 169 && b === 254) return true // 169.254.0.0/16 (link-local)
- if (a === 127) return true // 127.0.0.0/8 (loopback)
- }
-
- // Block common internal hostnames
- if (
- hostname.endsWith(".local") ||
- hostname.endsWith(".internal") ||
- hostname.endsWith(".localhost")
- ) {
- return true
- }
-
- return false
+ // Resolve DNS and reject if any address is private.
+ const stripped = hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "")
+ const addresses = await lookup(stripped, { all: true })
+ return addresses.some(({ address }) => isPrivateIp(address))
} catch {
- return true // Invalid URL - block it
+ return true // Invalid URL or DNS failure - block it
}
}
/**
* Whether private URLs are allowed (defaults to true)
* Set ALLOW_PRIVATE_URLS=false to block private URLs
+ * Read per call so admin-panel changes apply without restart
*/
-export const allowPrivateUrls = process.env.ALLOW_PRIVATE_URLS !== "false"
+export function allowPrivateUrls(): boolean {
+ return process.env.ALLOW_PRIVATE_URLS !== "false"
+}
+
+/** A redirect the guard below refused; its text is safe to show */
+export class RedirectRefusedError extends Error {
+ constructor(message = "Redirects are not allowed for custom base URLs") {
+ super(message)
+ this.name = "RedirectRefusedError"
+ }
+}
+
+const MAX_REDIRECTS = 5
+
+// Dropped when a redirect goes to another origin: those fetch drops, and
+// the key headers of providers that do not use Authorization (Anthropic,
+// Google, Azure)
+const CREDENTIAL_HEADERS = [
+ "authorization",
+ "proxy-authorization",
+ "cookie",
+ "x-api-key",
+ "x-goog-api-key",
+ "api-key",
+]
+
+/**
+ * A fetch for requests to a base URL the client chose. With private URLs
+ * blocked, a public URL could still redirect the request to an internal
+ * host, so redirects are refused. With private URLs allowed but the quota
+ * on (DYNAMODB_QUOTA_TABLE), a request to a private address counts as the
+ * server's: a public URL's redirects are followed only to public addresses,
+ * or it could reach the server's own network uncounted. Undefined otherwise.
+ */
+export function redirectGuardedFetch(): typeof fetch | undefined {
+ const blockAll = !allowPrivateUrls()
+ if (!blockAll && !process.env.DYNAMODB_QUOTA_TABLE) return undefined
+ return async (input, init) => {
+ let url = input instanceof Request ? input.url : String(input)
+ let next = init
+ // A request to a private address already counts as the server's
+ let startsPrivate: boolean | undefined
+ for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
+ const response = await fetch(url, { ...next, redirect: "manual" })
+ const location = response.headers.get("location")
+ if (response.status < 300 || response.status >= 400 || !location) {
+ return response
+ }
+ if (blockAll) throw new RedirectRefusedError()
+ startsPrivate ??= await isPrivateUrl(url)
+ const from = new URL(url)
+ url = new URL(location, url).toString()
+ if (!startsPrivate && (await isPrivateUrl(url))) {
+ throw new RedirectRefusedError(
+ "Redirects to private addresses are not allowed",
+ )
+ }
+ // The rest as fetch itself does it. Another origin gets no
+ // credentials (the user's key, EdgeOne's cookies)
+ const headers = new Headers(next?.headers)
+ if (new URL(url).origin !== from.origin) {
+ for (const name of CREDENTIAL_HEADERS) headers.delete(name)
+ }
+ next = { ...next, headers }
+ // 303, and 301 or 302 after a POST, go on as a GET without the
+ // body
+ const method = (next.method ?? "GET").toUpperCase()
+ if (
+ response.status === 303 ||
+ ((response.status === 301 || response.status === 302) &&
+ method === "POST")
+ ) {
+ for (const name of [
+ "content-type",
+ "content-length",
+ "content-encoding",
+ "content-language",
+ "content-location",
+ ]) {
+ headers.delete(name)
+ }
+ next = { ...next, method: "GET", body: undefined }
+ }
+ }
+ throw new RedirectRefusedError("Too many redirects")
+ }
+}
diff --git a/lib/storage.ts b/lib/storage.ts
index ddfc54c0..2f61181c 100644
--- a/lib/storage.ts
+++ b/lib/storage.ts
@@ -31,6 +31,9 @@ export const STORAGE_KEYS = {
// Custom system message
customSystemMessage: "next-ai-draw-io-custom-system-message",
+ // Output token budget per turn (empty = server default)
+ maxOutputTokens: "next-ai-draw-io-max-output-tokens",
+
// Panel visibility
showRecentChats: "next-ai-draw-io-show-recent-chats",
showMyTemplates: "next-ai-draw-io-show-my-templates",
diff --git a/lib/system-prompts.ts b/lib/system-prompts.ts
index aafa52b4..f033bd61 100644
--- a/lib/system-prompts.ts
+++ b/lib/system-prompts.ts
@@ -1,11 +1,14 @@
/**
* System prompts for different AI models
* Extended prompt is used for models with higher cache token minimums (Opus 4.5, Haiku 4.5)
- *
- * Token counting utilities are in a separate file (token-counter.ts) to avoid
- * WebAssembly issues with Next.js server-side rendering.
*/
+import {
+ SWIMLANE_EXAMPLE,
+ TWO_EDGES_EXAMPLE,
+ WAYPOINT_EXAMPLE,
+} from "@/packages/mcp-server/src/xml-examples.ts"
+
// Default system prompt (~1900 tokens) - works with all models
export const DEFAULT_SYSTEM_PROMPT = `
You are an expert diagram creation assistant specializing in draw.io XML generation.
@@ -41,7 +44,7 @@ parameters: {
tool name: edit_diagram
description: Edit specific parts of the EXISTING diagram. Use this when making small targeted changes like adding/removing elements, changing labels, or adjusting properties. This is more efficient than regenerating the entire diagram.
parameters: {
- edits: Array<{search: string, replace: string}>
+ operations: Array<{operation: "update" | "add" | "delete", cell_id: string, new_xml?: string}>
}
---Tool3---
tool name: append_diagram
@@ -241,21 +244,7 @@ const EXTENDED_ADDITIONS = `
**Example with swimlanes and edges** (generate ONLY this - no wrapper tags):
\`\`\`xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+${SWIMLANE_EXAMPLE}
\`\`\`
### append_diagram Details
@@ -318,12 +307,7 @@ If cell_id not found, check "Current diagram XML" for correct IDs. Use display_d
### Two edges between same nodes (CORRECT - no overlap):
\`\`\`xml
-
-
-
-
-
-
+${TWO_EDGES_EXAMPLE}
\`\`\`
### Edge with single waypoint (simple detour):
@@ -342,14 +326,7 @@ If cell_id not found, check "Current diagram XML" for correct IDs. Use display_d
**WRONG:** Direct diagonal line crosses over Develop
**CORRECT:** Route around the OUTSIDE (go right first, then up)
\`\`\`xml
-
-
-
-
-
-
-
-
+${WAYPOINT_EXAMPLE}
\`\`\`
This routes the edge to the RIGHT of all shapes (x=750), then enters Main from the right side.
diff --git a/lib/template-storage.ts b/lib/template-storage.ts
index 65c533af..8b0e4516 100644
--- a/lib/template-storage.ts
+++ b/lib/template-storage.ts
@@ -57,33 +57,6 @@ export function generateDefaultTitle(prompt: string): string {
// Database singleton
let dbPromise: Promise> | null = null
-const resetDBPromise = () => {
- dbPromise = null
-}
-
-const isClosingError = (error: unknown): boolean => {
- return (
- error instanceof DOMException &&
- error.name === "InvalidStateError" &&
- /closing/i.test(error.message)
- )
-}
-
-const withDB = async (
- action: (db: IDBPDatabase) => Promise,
-): Promise => {
- try {
- const db = await getDB()
- return await action(db)
- } catch (error) {
- if (isClosingError(error)) {
- resetDBPromise()
- const db = await getDB()
- return await action(db)
- }
- throw error
- }
-}
async function getDB(): Promise> {
if (!dbPromise) {
@@ -101,23 +74,7 @@ async function getDB(): Promise> {
}
}
},
- terminated() {
- resetDBPromise()
- },
})
- dbPromise
- .then((db) => {
- db.onversionchange = () => {
- db.close()
- resetDBPromise()
- }
- db.onclose = () => {
- resetDBPromise()
- }
- })
- .catch(() => {
- resetDBPromise()
- })
}
return dbPromise
}
@@ -137,10 +94,9 @@ export function isIndexedDBAvailable(): boolean {
export async function getAllTemplates(): Promise {
if (!isIndexedDBAvailable()) return []
try {
- return await withDB(async (db) => {
- const templates = await db.getAll(STORE_NAME)
- return sortTemplates(templates)
- })
+ const db = await getDB()
+ const templates = await db.getAll(STORE_NAME)
+ return sortTemplates(templates)
} catch (error) {
console.error("Failed to get templates:", error)
return []
@@ -150,9 +106,8 @@ export async function getAllTemplates(): Promise {
export async function getTemplate(id: string): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- return (await db.get(STORE_NAME, id)) || null
- })
+ const db = await getDB()
+ return (await db.get(STORE_NAME, id)) || null
} catch (error) {
console.error("Failed to get template:", error)
return null
@@ -182,9 +137,8 @@ export async function createTemplate(
}
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, template)
return template
} catch (error) {
console.error("Failed to create template:", error)
@@ -198,20 +152,19 @@ export async function updateTemplate(
): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- const existing = await db.get(STORE_NAME, id)
- if (!existing) return null
+ const db = await getDB()
+ const existing = await db.get(STORE_NAME, id)
+ if (!existing) return null
- const updated: Template = {
- ...existing,
- ...updates,
- id: existing.id,
- createdAt: existing.createdAt,
- updatedAt: Date.now(),
- }
- await db.put(STORE_NAME, updated)
- return updated
- })
+ const updated: Template = {
+ ...existing,
+ ...updates,
+ id: existing.id,
+ createdAt: existing.createdAt,
+ updatedAt: Date.now(),
+ }
+ await db.put(STORE_NAME, updated)
+ return updated
} catch (error) {
console.error("Failed to update template:", error)
return null
@@ -221,9 +174,8 @@ export async function updateTemplate(
export async function deleteTemplate(id: string): Promise {
if (!isIndexedDBAvailable()) return false
try {
- await withDB(async (db) => {
- await db.delete(STORE_NAME, id)
- })
+ const db = await getDB()
+ await db.delete(STORE_NAME, id)
return true
} catch (error) {
console.error("Failed to delete template:", error)
@@ -237,25 +189,24 @@ export async function duplicateTemplate(
): Promise {
if (!isIndexedDBAvailable()) return null
try {
- return await withDB(async (db) => {
- const existing = await db.get(STORE_NAME, id)
- if (!existing) return null
+ const db = await getDB()
+ const existing = await db.get(STORE_NAME, id)
+ if (!existing) return null
- const now = Date.now()
- const duplicate: Template = {
- ...existing,
- id: nanoid(),
- title: `${existing.title} ${copySuffix}`,
- createdAt: now,
- updatedAt: now,
- clickCount: 0,
- runCount: 0,
- lastUsedAt: 0,
- pinned: false,
- }
- await db.put(STORE_NAME, duplicate)
- return duplicate
- })
+ const now = Date.now()
+ const duplicate: Template = {
+ ...existing,
+ id: nanoid(),
+ title: `${existing.title} ${copySuffix}`,
+ createdAt: now,
+ updatedAt: now,
+ clickCount: 0,
+ runCount: 0,
+ lastUsedAt: 0,
+ pinned: false,
+ }
+ await db.put(STORE_NAME, duplicate)
+ return duplicate
} catch (error) {
console.error("Failed to duplicate template:", error)
return null
@@ -267,13 +218,12 @@ export async function duplicateTemplate(
export async function incrementClickCount(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const template = await db.get(STORE_NAME, id)
- if (!template) return
- template.clickCount += 1
- template.updatedAt = Date.now()
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ const template = await db.get(STORE_NAME, id)
+ if (!template) return
+ template.clickCount += 1
+ template.updatedAt = Date.now()
+ await db.put(STORE_NAME, template)
} catch (error) {
console.error("Failed to increment click count:", error)
}
@@ -282,15 +232,14 @@ export async function incrementClickCount(id: string): Promise {
export async function incrementRunCount(id: string): Promise {
if (!isIndexedDBAvailable()) return
try {
- await withDB(async (db) => {
- const template = await db.get(STORE_NAME, id)
- if (!template) return
- const now = Date.now()
- template.runCount += 1
- template.lastUsedAt = now
- template.updatedAt = now
- await db.put(STORE_NAME, template)
- })
+ const db = await getDB()
+ const template = await db.get(STORE_NAME, id)
+ if (!template) return
+ const now = Date.now()
+ template.runCount += 1
+ template.lastUsedAt = now
+ template.updatedAt = now
+ await db.put(STORE_NAME, template)
} catch (error) {
console.error("Failed to increment run count:", error)
}
@@ -423,9 +372,8 @@ export async function importTemplates(
pinned: typeof t.pinned === "boolean" ? t.pinned : false,
}
try {
- await withDB(async (db) => {
- await db.put(STORE_NAME, newTemplate)
- })
+ const db = await getDB()
+ await db.put(STORE_NAME, newTemplate)
existingKeys.add(key)
imported++
} catch (error) {
diff --git a/lib/types/model-config.ts b/lib/types/model-config.ts
index c89ca106..d8acad4f 100644
--- a/lib/types/model-config.ts
+++ b/lib/types/model-config.ts
@@ -9,6 +9,7 @@ export type ProviderName =
| "bedrock"
| "ollama"
| "openrouter"
+ | "aihubmix"
| "deepseek"
| "siliconflow"
| "sglang"
@@ -22,6 +23,8 @@ export type ProviderName =
| "kimi"
| "minimax"
| "novita"
+ | "mimo"
+ | "atlascloud"
// Individual model configuration
export interface ModelConfig {
@@ -29,6 +32,8 @@ export interface ModelConfig {
modelId: string // e.g., "gpt-4o", "claude-sonnet-4-5"
validated?: boolean // Has this model been validated
validationError?: string // Error message if validation failed
+ validationWarning?: string // Passed, but e.g. did not call a tool
+ responseTime?: number // Milliseconds the last test took
}
// Provider configuration
@@ -85,6 +90,15 @@ export interface FlattenedModel {
baseUrlEnv?: string
}
+// Providers whose server credentials live in fixed env vars
+// (AWS_ACCESS_KEY_ID, GOOGLE_VERTEX_API_KEY, OLLAMA_API_KEY) with no
+// apiKeyEnv redirection support — their credentials are global
+export const FIXED_CRED_PROVIDERS: ProviderName[] = [
+ "bedrock",
+ "vertexai",
+ "ollama",
+]
+
// Map provider names to models.dev logo names
export const PROVIDER_LOGO_MAP: Record = {
openai: "openai",
@@ -93,6 +107,7 @@ export const PROVIDER_LOGO_MAP: Record = {
azure: "azure",
bedrock: "amazon-bedrock",
openrouter: "openrouter",
+ aihubmix: "aihubmix",
deepseek: "deepseek",
siliconflow: "siliconflow",
sglang: "openai", // SGLang is OpenAI-compatible
@@ -103,24 +118,49 @@ export const PROVIDER_LOGO_MAP: Record = {
modelscope: "modelscope",
minimax: "minimax",
novita: "novita",
+ mimo: "xiaomi",
+ atlascloud: "openai",
}
-// Provider metadata
+/** How a provider lists its models (see lib/provider-models.ts) */
+export type ModelListStyle =
+ | "openai"
+ | "anthropic"
+ | "google"
+ | "ollama"
+ | "openrouter"
+ | "aihubmix"
+ | "gateway"
+
+// Provider metadata. apiKeyUrl is the page where users create a key.
+// modelList is missing where a key alone cannot list the models (Bedrock,
+// Vertex, Azure) or the list is not reliable (Doubao, MiniMax).
export const PROVIDER_INFO: Record<
ProviderName,
- { label: string; defaultBaseUrl?: string }
+ {
+ label: string
+ defaultBaseUrl?: string
+ apiKeyUrl?: string
+ modelList?: ModelListStyle
+ }
> = {
openai: {
label: "OpenAI",
defaultBaseUrl: "https://api.openai.com/v1",
+ apiKeyUrl: "https://platform.openai.com/api-keys",
+ modelList: "openai",
},
anthropic: {
label: "Anthropic",
defaultBaseUrl: "https://api.anthropic.com/v1",
+ apiKeyUrl: "https://platform.claude.com/settings/keys",
+ modelList: "anthropic",
},
google: {
label: "Google",
defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
+ apiKeyUrl: "https://aistudio.google.com/apikey",
+ modelList: "google",
},
vertexai: { label: "Google Vertex AI" },
azure: {
@@ -131,237 +171,366 @@ export const PROVIDER_INFO: Record<
ollama: {
label: "Ollama",
defaultBaseUrl: "https://ollama.com/api",
+ apiKeyUrl: "https://ollama.com/settings/keys",
+ modelList: "ollama",
},
openrouter: {
label: "OpenRouter",
defaultBaseUrl: "https://openrouter.ai/api/v1",
+ apiKeyUrl: "https://openrouter.ai/keys",
+ modelList: "openrouter",
+ },
+ aihubmix: {
+ label: "AIHubMix",
+ defaultBaseUrl: "https://aihubmix.com/v1",
+ apiKeyUrl: "https://aihubmix.com/token",
+ modelList: "aihubmix",
},
deepseek: {
label: "DeepSeek",
defaultBaseUrl: "https://api.deepseek.com/v1",
+ apiKeyUrl: "https://platform.deepseek.com/api_keys",
+ modelList: "openai",
},
siliconflow: {
label: "SiliconFlow",
defaultBaseUrl: "https://api.siliconflow.cn/v1",
+ apiKeyUrl: "https://cloud.siliconflow.cn/account/ak",
+ modelList: "openai",
},
sglang: {
label: "SGLang",
defaultBaseUrl: "http://127.0.0.1:8000/v1",
+ modelList: "openai",
},
gateway: {
label: "AI Gateway",
defaultBaseUrl: "https://ai-gateway.vercel.sh/v1/ai",
+ apiKeyUrl: "https://vercel.com/ai-gateway",
+ modelList: "gateway",
},
edgeone: { label: "EdgeOne Pages" },
doubao: {
label: "Doubao (ByteDance)",
defaultBaseUrl: "https://ark.cn-beijing.volces.com/api/v3",
+ apiKeyUrl:
+ "https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey",
},
modelscope: {
label: "ModelScope",
defaultBaseUrl: "https://api-inference.modelscope.cn/v1",
+ apiKeyUrl: "https://modelscope.cn/my/myaccesstoken",
+ modelList: "openai",
},
glm: {
label: "GLM (Zhipu)",
defaultBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
+ apiKeyUrl: "https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys",
+ modelList: "openai",
},
qwen: {
label: "Qwen (Alibaba)",
defaultBaseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ apiKeyUrl: "https://bailian.console.aliyun.com/?tab=model#/api-key",
+ modelList: "openai",
},
qiniu: {
label: "Qiniu",
defaultBaseUrl: "https://api.qnaigc.com/v1",
+ apiKeyUrl: "https://www.qiniu.com/ai/models",
+ modelList: "openai",
},
kimi: {
label: "Kimi (Moonshot)",
defaultBaseUrl: "https://api.moonshot.cn/v1",
+ apiKeyUrl: "https://platform.moonshot.cn/console/api-keys",
+ modelList: "openai",
},
minimax: {
label: "MiniMax",
defaultBaseUrl: "https://api.minimaxi.com/anthropic",
+ apiKeyUrl:
+ "https://platform.minimaxi.com/user-center/basic-information/interface-key",
},
novita: {
label: "Novita AI",
defaultBaseUrl: "https://api.novita.ai/openai",
+ apiKeyUrl: "https://novita.ai/dashboard/key",
+ modelList: "openai",
+ },
+ mimo: {
+ label: "MiMo (Xiaomi)",
+ defaultBaseUrl: "https://api.xiaomimimo.com/v1",
+ apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys",
+ modelList: "openai",
+ },
+ atlascloud: {
+ label: "Atlas Cloud",
+ defaultBaseUrl: "https://api.atlascloud.ai/v1",
+ apiKeyUrl: "https://www.atlascloud.ai/console/api-keys",
+ modelList: "openai",
},
}
// Suggested models per provider for quick add
export const SUGGESTED_MODELS: Partial> = {
openai: [
- "gpt-5.2-pro",
- "gpt-5.2-chat-latest",
- "gpt-5.2",
- "gpt-5.1-codex-mini",
- "gpt-5.1-codex",
- "gpt-5.1-chat-latest",
- "gpt-5.1",
- "gpt-5-pro",
- "gpt-5",
- "gpt-5-mini",
- "gpt-5-nano",
- "gpt-5-codex",
- "gpt-5-chat-latest",
+ "gpt-6.1-sol",
+ "gpt-6-sol",
+ "gpt-6-luna",
+ "gpt-6-astra",
+ "gpt-5.5-pro",
+ "gpt-5.5",
+ "gpt-5.4-pro",
+ "gpt-5.4",
+ "gpt-5.4-mini",
+ "gpt-5.4-nano",
+ "gpt-5-codex-mini",
"gpt-4.1",
"gpt-4.1-mini",
- "gpt-4.1-nano",
"gpt-4o",
"gpt-4o-mini",
],
anthropic: [
- // Claude 4.5 series (latest)
- "claude-opus-4-5-20250514",
- "claude-sonnet-4-5-20250514",
- // Claude 4 series
- "claude-opus-4-20250514",
- "claude-sonnet-4-20250514",
+ // Claude 5 series (latest)
+ "claude-opus-5-5",
+ "claude-sonnet-5-5",
+ "claude-fable-5-1",
+ "claude-opus-5",
+ "claude-sonnet-5",
+ // Claude 4.8 / 4.7 / 4.6 series (dateless pinned IDs)
+ "claude-opus-4-8",
+ "claude-sonnet-4-6",
+ "claude-haiku-4-5",
+ "claude-opus-4-7",
+ "claude-opus-4-6",
+ // Claude 4.5 series
+ "claude-sonnet-4-5-20250929",
+ "claude-opus-4-5-20251101",
// Claude 3.7 series
"claude-3-7-sonnet-20250219",
// Claude 3.5 series
"claude-3-5-sonnet-20241022",
"claude-3-5-haiku-20241022",
- // Claude 3 series
- "claude-3-opus-20240229",
- "claude-3-sonnet-20240229",
- "claude-3-haiku-20240307",
],
google: [
+ // Gemini 3 series
+ "gemini-3.1-pro",
+ "gemini-3.5-flash",
+ "gemini-3-flash",
+ "gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
- "gemini-2.5-flash-preview-05-20",
- // Gemini 2.0 series
- "gemini-2.0-flash",
- "gemini-2.0-flash-exp",
- "gemini-2.0-flash-lite",
- // Gemini 1.5 series
- "gemini-1.5-pro",
- "gemini-1.5-flash",
- // Legacy
- "gemini-pro",
+ "gemini-2.5-flash-lite",
],
vertexai: [
+ // Gemini 3 series
+ "gemini-3.1-pro-preview",
+ "gemini-3.5-flash",
+ "gemini-3-flash-preview",
+ "gemini-3.1-flash-lite",
// Gemini 2.5 series
"gemini-2.5-pro",
"gemini-2.5-flash",
- // Gemini 2.0 series
- "gemini-2.0-flash",
- "gemini-2.0-flash-exp",
- // Gemini 1.5 series
- "gemini-1.5-pro",
- "gemini-1.5-flash",
+ "gemini-2.5-flash-lite",
],
- azure: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-35-turbo"],
+ azure: [
+ "gpt-5.5",
+ "gpt-5.4",
+ "gpt-5.1",
+ "gpt-5",
+ "gpt-5-mini",
+ "gpt-4.1",
+ "gpt-4o",
+ "gpt-4o-mini",
+ "o3",
+ "o4-mini",
+ ],
+ // Newer models only answer through an inference profile id (the region
+ // prefix). Each id here was called once on 2026-10-04.
bedrock: [
- // Anthropic Claude
- "anthropic.claude-opus-4-5-20250514-v1:0",
- "anthropic.claude-sonnet-4-5-20250514-v1:0",
- "anthropic.claude-opus-4-20250514-v1:0",
- "anthropic.claude-sonnet-4-20250514-v1:0",
- "anthropic.claude-3-7-sonnet-20250219-v1:0",
- "anthropic.claude-3-5-sonnet-20241022-v2:0",
- "anthropic.claude-3-5-haiku-20241022-v1:0",
- "anthropic.claude-3-opus-20240229-v1:0",
- "anthropic.claude-3-sonnet-20240229-v1:0",
- "anthropic.claude-3-haiku-20240307-v1:0",
+ // Anthropic Claude ("global." works from any region)
+ "global.anthropic.claude-opus-5-5",
+ "global.anthropic.claude-sonnet-5-5",
+ "global.anthropic.claude-fable-5-1",
+ "global.anthropic.claude-opus-5",
+ "global.anthropic.claude-sonnet-5",
+ "global.anthropic.claude-opus-4-8",
+ "global.anthropic.claude-opus-4-7",
+ "global.anthropic.claude-sonnet-4-6",
+ "global.anthropic.claude-opus-4-6-v1",
+ "global.anthropic.claude-opus-4-5-20251101-v1:0",
+ "global.anthropic.claude-sonnet-4-5-20250929-v1:0",
+ "global.anthropic.claude-haiku-4-5-20251001-v1:0",
+ "global.anthropic.claude-sonnet-4-20250514-v1:0",
// Amazon Nova
+ "us.amazon.nova-2-lite-v1:0",
"amazon.nova-pro-v1:0",
"amazon.nova-lite-v1:0",
"amazon.nova-micro-v1:0",
// Meta Llama
- "meta.llama3-3-70b-instruct-v1:0",
- "meta.llama3-1-405b-instruct-v1:0",
- "meta.llama3-1-70b-instruct-v1:0",
+ "us.meta.llama4-maverick-17b-instruct-v1:0",
+ "us.meta.llama4-scout-17b-instruct-v1:0",
+ "us.meta.llama3-3-70b-instruct-v1:0",
// Mistral
- "mistral.mistral-large-2411-v1:0",
- "mistral.mistral-small-2503-v1:0",
+ "mistral.mistral-large-3-675b-instruct",
+ "us.mistral.pixtral-large-2502-v1:0",
],
openrouter: [
// Anthropic
- "anthropic/claude-sonnet-4",
- "anthropic/claude-opus-4",
- "anthropic/claude-3.5-sonnet",
- "anthropic/claude-3.5-haiku",
+ "anthropic/claude-opus-5.5",
+ "anthropic/claude-sonnet-5.5",
+ "anthropic/claude-fable-5.1",
+ "anthropic/claude-opus-4.8",
+ "anthropic/claude-sonnet-4.6",
+ "anthropic/claude-haiku-4.5",
// OpenAI
- "openai/gpt-4o",
+ "openai/gpt-6.1-sol",
+ "openai/gpt-6-luna",
+ "openai/gpt-5.5",
+ "openai/gpt-5.4",
+ "openai/gpt-5.4-mini",
"openai/gpt-4o-mini",
- "openai/o1",
- "openai/o3-mini",
// Google
- "google/gemini-2.5-pro",
- "google/gemini-2.5-flash",
- "google/gemini-2.0-flash-exp:free",
+ "google/gemini-3.1-pro-preview",
+ "google/gemini-3.5-flash",
+ "google/gemini-2.5-flash-lite",
+ // xAI
+ "x-ai/grok-4.3",
// Meta Llama
+ "meta-llama/llama-4-maverick",
+ "meta-llama/llama-4-scout",
"meta-llama/llama-3.3-70b-instruct",
- "meta-llama/llama-3.1-405b-instruct",
- "meta-llama/llama-3.1-70b-instruct",
// DeepSeek
- "deepseek/deepseek-chat",
- "deepseek/deepseek-r1",
+ "deepseek/deepseek-v4-pro",
+ "deepseek/deepseek-v3.2",
// Qwen
- "qwen/qwen-2.5-72b-instruct",
+ "qwen/qwen3.7-max",
+ "qwen/qwen3-coder",
+ // MiniMax
+ "minimax/minimax-m3",
+ ],
+ aihubmix: [
+ // Fallback list. The settings UI loads the live model list from AIHubMix when available.
+ // Anthropic Claude
+ "claude-fable-5",
+ "claude-opus-4-8",
+ "claude-sonnet-4-6",
+ // OpenAI
+ "gpt-5.5",
+ "gpt-5.5-pro",
+ "gpt-5.4",
+ // Google Gemini
+ "gemini-3.5-flash",
+ "gemini-3.1-pro-preview",
+ "gemini-3-flash-preview",
+ // DeepSeek
+ "deepseek-v4-pro",
+ "deepseek-v4-flash",
+ // Qwen
+ "qwen3.7-max",
+ "qwen3-coder-next",
+ // Z.ai
+ "glm-5.1",
+ // Moonshot AI
+ "kimi-k2.6",
+ // MiniMax
+ "minimax-m3",
+ // xAI
+ "grok-4.3",
+ // Baidu
+ "ernie-5.1",
+ // Mistral
+ "mistral-large-3",
+ // Meta
+ "llama-4-maverick",
+ ],
+ deepseek: [
+ "deepseek-v4-pro",
+ "deepseek-v4-flash",
+ "deepseek-chat",
+ "deepseek-reasoner",
],
- deepseek: ["deepseek-chat", "deepseek-reasoner", "deepseek-coder"],
siliconflow: [
// DeepSeek
- "deepseek-ai/DeepSeek-V3",
- "deepseek-ai/DeepSeek-R1",
- "deepseek-ai/DeepSeek-V2.5",
+ "deepseek-ai/DeepSeek-V4-Pro",
+ "deepseek-ai/DeepSeek-V4-Flash",
+ "deepseek-ai/DeepSeek-V3.2",
+ // MiniMax
+ "MiniMaxAI/MiniMax-M3",
+ // Moonshot
+ "moonshotai/Kimi-K2.6",
+ // Z.ai
+ "zai-org/GLM-5",
// Qwen
- "Qwen/Qwen2.5-72B-Instruct",
- "Qwen/Qwen2.5-32B-Instruct",
- "Qwen/Qwen2.5-Coder-32B-Instruct",
- "Qwen/Qwen2.5-7B-Instruct",
- "Qwen/Qwen2-VL-72B-Instruct",
- "qwen3.5-plus",
+ "Qwen/Qwen3.6-35B-A3B",
+ "Qwen/Qwen3-Coder-480B-A35B-Instruct",
+ "Qwen/Qwen3-30B-A3B-Instruct-2507",
+ "Qwen/Qwen3-VL-32B-Instruct",
+ // OpenAI open-weights
+ "openai/gpt-oss-120b",
],
sglang: [
// SGLang is OpenAI-compatible, models depend on deployment
"default",
],
gateway: [
- "openai/gpt-4o",
- "openai/gpt-4o-mini",
- "anthropic/claude-sonnet-4-5",
- "anthropic/claude-3-5-sonnet",
- "google/gemini-2.0-flash",
+ "anthropic/claude-opus-5.5",
+ "anthropic/claude-sonnet-5.5",
+ "openai/gpt-6.1-sol",
+ "openai/gpt-6-luna",
+ "openai/gpt-5.5",
+ "anthropic/claude-opus-4.7",
+ "google/gemini-3.1-pro-preview",
+ "xai/grok-4.3",
+ "anthropic/claude-sonnet-4.6",
+ "anthropic/claude-haiku-4.5",
+ "openai/gpt-5.4-mini",
],
edgeone: ["@tx/deepseek-ai/deepseek-v32"],
doubao: [
- // ByteDance Doubao models
- "doubao-1.5-thinking-pro-250415",
- "doubao-1.5-thinking-pro-m-250428",
- "doubao-1.5-pro-32k-250115",
- "doubao-1.5-pro-256k-250115",
- "doubao-pro-32k-241215",
- "doubao-pro-256k-241215",
+ // ByteDance Doubao models (Volcengine Ark IDs use dash form)
+ "doubao-seed-2-0-pro-260215",
+ "doubao-seed-2-0-lite-260428",
+ "doubao-seed-2-0-mini-260428",
+ "doubao-seed-1-8-251228",
+ "doubao-seed-1-6-251015",
+ "doubao-seed-1-6-flash-250828",
+ "doubao-seed-1-6-vision-250815",
+ "doubao-1-5-pro-32k-250115",
+ "doubao-1-5-lite-32k-250115",
],
modelscope: [
+ // DeepSeek
+ "deepseek-ai/DeepSeek-V4-Pro",
+ "deepseek-ai/DeepSeek-V3.2",
+ "deepseek-ai/DeepSeek-R1-0528",
+ "deepseek-ai/DeepSeek-R1",
// Qwen
- "Qwen/Qwen2.5-72B-Instruct",
- "Qwen/Qwen2.5-32B-Instruct",
"Qwen/Qwen3-235B-A22B-Instruct-2507",
"Qwen/Qwen3-VL-235B-A22B-Instruct",
+ "Qwen/Qwen3-Coder-30B-A3B-Instruct",
"Qwen/Qwen3-32B",
- "qwen3.5-plus",
- // DeepSeek
- "deepseek-ai/DeepSeek-R1-0528",
- "deepseek-ai/DeepSeek-V3.2",
+ "Qwen/Qwen2.5-72B-Instruct",
],
minimax: [
// MiniMax models (Anthropic-compatible API)
+ "MiniMax-M3",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
"MiniMax-M2.5",
- "MiniMax-M2.5-highspeed",
],
novita: [
// Novita AI models (OpenAI-compatible API)
- "moonshotai/kimi-k2.5",
- "zai-org/glm-5",
- "minimax/minimax-m2.5",
+ "minimax/minimax-m3",
+ "deepseek/deepseek-v4-pro",
+ "zai-org/glm-5.1",
+ "moonshotai/kimi-k2.6",
+ "deepseek/deepseek-v4-flash",
],
+ mimo: ["mimo-v2.5-pro", "mimo-v2.5"],
+ atlascloud: ["qwen/qwen3.5-flash", "deepseek-ai/deepseek-v4-pro"],
}
// Helper to generate UUID
@@ -440,3 +609,38 @@ export function findModelById(
): FlattenedModel | undefined {
return flattenModels(config).find((m) => m.id === modelId)
}
+
+/**
+ * A base URL the way the SDKs expect it: no spaces, no trailing slash, and
+ * no endpoint path users often paste along (".../v1/chat/completions"),
+ * which the SDK would append a second time.
+ */
+export function normalizeBaseUrl(url: string): string {
+ return url
+ .trim()
+ .replace(/\/+$/, "")
+ .replace(/\/(?:chat\/completions|completions|messages|responses)$/, "")
+}
+
+/** Where a chat request goes for a base URL, or null when the SDK decides */
+export function chatRequestUrl(
+ provider: ProviderName,
+ baseUrl: string,
+): string | null {
+ const url = normalizeBaseUrl(baseUrl)
+ if (!url) return null
+ if (provider === "anthropic") return `${url}/messages`
+ // These SDKs build their own paths (or, for MiniMax, pick the protocol
+ // from the URL)
+ const ownPaths: ProviderName[] = [
+ "google",
+ "vertexai",
+ "azure",
+ "bedrock",
+ "ollama",
+ "gateway",
+ "minimax",
+ "edgeone",
+ ]
+ return ownPaths.includes(provider) ? null : `${url}/chat/completions`
+}
diff --git a/lib/url-utils.ts b/lib/url-utils.ts
index e0f0010b..d0b774f4 100644
--- a/lib/url-utils.ts
+++ b/lib/url-utils.ts
@@ -1,5 +1,6 @@
import { z } from "zod"
import { getApiEndpoint } from "@/lib/base-path"
+import { STORAGE_KEYS } from "@/lib/storage"
export interface UrlData {
url: string
@@ -18,7 +19,11 @@ const UrlResponseSchema = z.object({
export async function extractUrlContent(url: string): Promise {
const response = await fetch(getApiEndpoint("/api/parse-url"), {
method: "POST",
- headers: { "Content-Type": "application/json" },
+ headers: {
+ "Content-Type": "application/json",
+ "x-access-code":
+ localStorage.getItem(STORAGE_KEYS.accessCode) || "",
+ },
body: JSON.stringify({ url }),
})
diff --git a/lib/use-file-processor.tsx b/lib/use-file-processor.tsx
index 5b2622ce..3f86d076 100644
--- a/lib/use-file-processor.tsx
+++ b/lib/use-file-processor.tsx
@@ -27,78 +27,72 @@ export function useFileProcessor() {
const handleFileChange = async (newFiles: File[]) => {
setFiles(newFiles)
- // Extract text immediately for new PDF/text files
- for (const file of newFiles) {
- const needsExtraction =
- (isPdfFile(file) || isTextFile(file)) && !pdfData.has(file)
- if (needsExtraction) {
- // Mark as extracting
- setPdfData((prev) => {
- const next = new Map(prev)
- next.set(file, {
- text: "",
- charCount: 0,
- isExtracting: true,
- })
- return next
- })
+ const pending = newFiles.filter(
+ (file) =>
+ (isPdfFile(file) || isTextFile(file)) && !pdfData.has(file),
+ )
- // Extract text asynchronously
- try {
- let text: string
- if (isPdfFile(file)) {
- text = await extractPdfText(file)
- } else {
- text = await extractTextFileContent(file)
- }
+ // Before any await: drop data for removed files and mark every new
+ // file as extracting, so queued files also block sending
+ setPdfData((prev) => {
+ const next = new Map()
+ for (const file of newFiles) {
+ const existing = prev.get(file)
+ if (existing) next.set(file, existing)
+ }
+ for (const file of pending) {
+ next.set(file, { text: "", charCount: 0, isExtracting: true })
+ }
+ return next
+ })
- // Check character limit
- if (text.length > MAX_EXTRACTED_CHARS) {
- const limitK = MAX_EXTRACTED_CHARS / 1000
- toast.error(
- `${file.name}: Content exceeds ${limitK}k character limit (${(text.length / 1000).toFixed(1)}k chars)`,
- )
- setPdfData((prev) => {
- const next = new Map(prev)
- next.delete(file)
- return next
- })
- // Remove the file from the list
- setFiles((prev) => prev.filter((f) => f !== file))
- continue
- }
+ // Extract one file at a time
+ for (const file of pending) {
+ try {
+ let text: string
+ if (isPdfFile(file)) {
+ text = await extractPdfText(file)
+ } else {
+ text = await extractTextFileContent(file)
+ }
- setPdfData((prev) => {
- const next = new Map(prev)
- next.set(file, {
- text,
- charCount: text.length,
- isExtracting: false,
- })
- return next
- })
- } catch (error) {
- console.error("Failed to extract text:", error)
- toast.error(`Failed to read file: ${file.name}`)
+ // Check character limit
+ if (text.length > MAX_EXTRACTED_CHARS) {
+ const limitK = MAX_EXTRACTED_CHARS / 1000
+ toast.error(
+ `${file.name}: Content exceeds ${limitK}k character limit (${(text.length / 1000).toFixed(1)}k chars)`,
+ )
setPdfData((prev) => {
const next = new Map(prev)
next.delete(file)
return next
})
+ // Remove the file from the list
+ setFiles((prev) => prev.filter((f) => f !== file))
+ continue
}
+
+ setPdfData((prev) => {
+ // The file was removed while extracting
+ if (!prev.has(file)) return prev
+ const next = new Map(prev)
+ next.set(file, {
+ text,
+ charCount: text.length,
+ isExtracting: false,
+ })
+ return next
+ })
+ } catch (error) {
+ console.error("Failed to extract text:", error)
+ toast.error(`Failed to read file: ${file.name}`)
+ setPdfData((prev) => {
+ const next = new Map(prev)
+ next.delete(file)
+ return next
+ })
}
}
-
- // Clean up pdfData for removed files
- setPdfData((prev) => {
- const next = new Map(prev)
- for (const key of prev.keys()) {
- if (!newFiles.includes(key)) {
- next.delete(key)
- }
- }
- return next
- })
}
return {
diff --git a/lib/utils.ts b/lib/utils.ts
index 2c1f8b43..a4749b92 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,9 +1,7 @@
import { type ClassValue, clsx } from "clsx"
import * as pako from "pako"
import { twMerge } from "tailwind-merge"
-import type { DiagramOperation } from "@/components/chat/types"
-
-export type { DiagramOperation }
+import { hasCells } from "@/packages/mcp-server/src/pages.ts"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
@@ -20,37 +18,16 @@ export function cn(...inputs: ClassValue[]) {
export const MIN_REAL_DIAGRAM_LENGTH = 300
/**
- * Check if diagram XML represents a real diagram (not just empty template).
+ * Check if diagram XML represents a real diagram (not just empty template):
+ * it has a shape (however short), or is long enough to hold pages worth
+ * keeping.
* @param xml - The diagram XML string to check
* @returns true if the XML is a real diagram with content
*/
export function isRealDiagram(xml: string | undefined | null): boolean {
- return !!xml && xml.length > MIN_REAL_DIAGRAM_LENGTH
+ return !!xml && (hasCells(xml) || xml.length > MIN_REAL_DIAGRAM_LENGTH)
}
-// ============================================================================
-// XML Validation/Fix Constants
-// ============================================================================
-
-/** Maximum XML size to process (1MB) - larger XMLs may cause performance issues */
-const MAX_XML_SIZE = 1_000_000
-
-/** Maximum iterations for aggressive cell dropping to prevent infinite loops */
-const MAX_DROP_ITERATIONS = 10
-
-/** Structural attributes that should not be duplicated in draw.io */
-const STRUCTURAL_ATTRS = [
- "edge",
- "parent",
- "source",
- "target",
- "vertex",
- "connectable",
-]
-
-/** Valid XML entity names */
-const VALID_ENTITIES = new Set(["lt", "gt", "amp", "quot", "apos"])
-
// ============================================================================
// mxCell XML Helpers
// ============================================================================
@@ -76,6 +53,20 @@ export function isMxCellXmlComplete(xml: string | undefined | null): boolean {
// No valid ending found at all
if (lastValidEnd === -1) return false
+ // If the last mxCell has no after it, it must be self-closing.
+ // Otherwise the trailing "/>" belongs to a child such as
+ // and the output was cut off before the cell was closed.
+ const lastCellStart = trimmed.lastIndexOf(" lastMxCellClose &&
+ // (quoted values may hold a raw "<", which the auto-fix escapes)
+ !/^"']|"[^"]*"|'[^']*')*\/>/.test(
+ trimmed.slice(lastCellStart),
+ )
+ ) {
+ return false
+ }
+
// Check what comes after the last valid ending
// For />: add 2 chars, for : add 9 chars
const endOffset = lastMxCellClose > lastSelfClose ? 9 : 2
@@ -95,102 +86,12 @@ export function isMxCellXmlComplete(xml: string | undefined | null): boolean {
export function extractCompleteMxCells(xml: string | undefined | null): string {
if (!xml) return ""
- const completeCells: Array<{ index: number; text: string }> = []
+ // Match self-closing or ... , in document order.
+ // The lazy [^>]*? tries "/>" first, so a self-closing cell never swallows
+ // the following cells up to the next .
+ const cellPattern = /]*?(?:\/>|>[\s\S]*?<\/mxCell>)/g
- // Match self-closing mxCell tags:
- // Also match mxCell with nested mxGeometry: ...
- const selfClosingPattern = /]*\/>/g
- const nestedPattern = /]*>[\s\S]*?<\/mxCell>/g
-
- // Find all self-closing mxCell elements
- let match: RegExpExecArray | null
- while ((match = selfClosingPattern.exec(xml)) !== null) {
- completeCells.push({ index: match.index, text: match[0] })
- }
-
- // Find all mxCell elements with nested content (like mxGeometry)
- while ((match = nestedPattern.exec(xml)) !== null) {
- completeCells.push({ index: match.index, text: match[0] })
- }
-
- // Sort by position to maintain order
- completeCells.sort((a, b) => a.index - b.index)
-
- // Remove duplicates (a self-closing match might overlap with nested match)
- const seen = new Set()
- const uniqueCells = completeCells.filter((cell) => {
- if (seen.has(cell.index)) return false
- seen.add(cell.index)
- return true
- })
-
- return uniqueCells.map((c) => c.text).join("\n")
-}
-
-// ============================================================================
-// XML Parsing Helpers
-// ============================================================================
-
-interface ParsedTag {
- tag: string
- tagName: string
- isClosing: boolean
- isSelfClosing: boolean
- startIndex: number
- endIndex: number
-}
-
-/**
- * Parse XML tags while properly handling quoted strings
- * This is a shared utility used by both validation and fixing logic
- */
-function parseXmlTags(xml: string): ParsedTag[] {
- const tags: ParsedTag[] = []
- let i = 0
-
- while (i < xml.length) {
- const tagStart = xml.indexOf("<", i)
- if (tagStart === -1) break
-
- // Find matching > by tracking quotes
- let tagEnd = tagStart + 1
- let inQuote = false
- let quoteChar = ""
-
- while (tagEnd < xml.length) {
- const c = xml[tagEnd]
- if (inQuote) {
- if (c === quoteChar) inQuote = false
- } else {
- if (c === '"' || c === "'") {
- inQuote = true
- quoteChar = c
- } else if (c === ">") {
- break
- }
- }
- tagEnd++
- }
-
- if (tagEnd >= xml.length) break
-
- const tag = xml.substring(tagStart, tagEnd + 1)
- i = tagEnd + 1
-
- const tagMatch = /^<(\/?)([a-zA-Z][a-zA-Z0-9:_-]*)/.exec(tag)
- if (!tagMatch) continue
-
- tags.push({
- tag,
- tagName: tagMatch[2],
- isClosing: tagMatch[1] === "/",
- isSelfClosing: tag.endsWith("/>"),
- startIndex: tagStart,
- endIndex: tagEnd,
- })
- }
-
- return tags
+ return (xml.match(cellPattern) || []).join("\n")
}
/**
@@ -315,61 +216,6 @@ export function convertToLegalXml(xmlString: string): string {
return result
}
-/**
- * Wrap XML content with the full mxfile structure required by draw.io.
- * Always adds root cells (id="0" and id="1") automatically.
- * If input already contains root cells, they are removed to avoid duplication.
- * LLM should only generate mxCell elements starting from id="2".
- * @param xml - The XML string (bare mxCells, , , or full )
- * @returns Full mxfile-wrapped XML string with root cells included
- */
-export function wrapWithMxFile(xml: string): string {
- const ROOT_CELLS = ' '
-
- if (!xml || !xml.trim()) {
- return `${ROOT_CELLS} `
- }
-
- // Already has full structure
- if (xml.includes("${xml} `
- }
-
- // Has wrapper - extract inner content
- let content = xml
- if (xml.includes("")) {
- content = xml.replace(/<\/?root>/g, "").trim()
- }
-
- // Strip trailing LLM wrapper tags (from any provider: Anthropic, DeepSeek, etc.)
- // Find the last valid mxCell ending and remove everything after it
- const lastSelfClose = content.lastIndexOf("/>")
- const lastMxCellClose = content.lastIndexOf(" ")
- const lastValidEnd = Math.max(lastSelfClose, lastMxCellClose)
- if (lastValidEnd !== -1) {
- const endOffset = lastMxCellClose > lastSelfClose ? 9 : 2
- const suffix = content.slice(lastValidEnd + endOffset)
- // If suffix is only closing tags (wrapper tags), strip it
- if (/^(\s*<\/[^>]+>)*\s*$/.test(suffix)) {
- content = content.slice(0, lastValidEnd + endOffset)
- }
- }
-
- // Remove any existing root cells from content (LLM shouldn't include them, but handle it gracefully)
- // Use flexible patterns that match both self-closing (/>) and non-self-closing (> ) formats
- content = content
- .replace(/]*\bid=["']0["'][^>]*(?:\/>|><\/mxCell>)/g, "")
- .replace(/]*\bid=["']1["'][^>]*(?:\/>|><\/mxCell>)/g, "")
- .trim()
-
- return `${ROOT_CELLS}${content} `
-}
-
/**
* Replace nodes in a Draw.io XML diagram
* @param currentXML - The original Draw.io XML string
@@ -472,1207 +318,11 @@ export function replaceNodes(currentXML: string, nodes: string): string {
}
}
-// ============================================================================
-// ID-based Diagram Operations
-// ============================================================================
-
-export interface OperationError {
- type: "update" | "add" | "delete"
- cellId: string
- message: string
-}
-
-export interface ApplyOperationsResult {
- result: string
- errors: OperationError[]
-}
-
/**
- * Apply diagram operations (update/add/delete) using ID-based lookup.
- * This replaces the text-matching approach with direct DOM manipulation.
- *
- * @param xmlContent - The full mxfile XML content
- * @param operations - Array of operations to apply
- * @returns Object with result XML and any errors
+ * Decode an xmlsvg export (SVG data URL) into uncompressed diagram XML.
+ * Only the first page is returned; for the full multi-page document use the
+ * autosaved chartXML instead.
*/
-export function applyDiagramOperations(
- xmlContent: string,
- operations: DiagramOperation[],
-): ApplyOperationsResult {
- const errors: OperationError[] = []
-
- // Parse the XML
- const parser = new DOMParser()
- const doc = parser.parseFromString(xmlContent, "text/xml")
-
- // Check for parse errors
- const parseError = doc.querySelector("parsererror")
- if (parseError) {
- return {
- result: xmlContent,
- errors: [
- {
- type: "update",
- cellId: "",
- message: `XML parse error: ${parseError.textContent}`,
- },
- ],
- }
- }
-
- // Find the root element (inside mxGraphModel)
- const root = doc.querySelector("root")
- if (!root) {
- return {
- result: xmlContent,
- errors: [
- {
- type: "update",
- cellId: "",
- message: "Could not find element in XML",
- },
- ],
- }
- }
-
- // Build a map of cell IDs to elements
- const cellMap = new Map()
- root.querySelectorAll("mxCell").forEach((cell) => {
- const id = cell.getAttribute("id")
- if (id) cellMap.set(id, cell)
- })
-
- // Process each operation
- for (const op of operations) {
- if (op.operation === "update") {
- const existingCell = cellMap.get(op.cell_id)
- if (!existingCell) {
- errors.push({
- type: "update",
- cellId: op.cell_id,
- message: `Cell with id="${op.cell_id}" not found`,
- })
- continue
- }
-
- if (!op.new_xml) {
- errors.push({
- type: "update",
- cellId: op.cell_id,
- message: "new_xml is required for update operation",
- })
- continue
- }
-
- // Parse the new XML
- const newDoc = parser.parseFromString(
- `${op.new_xml} `,
- "text/xml",
- )
- const newCell = newDoc.querySelector("mxCell")
- if (!newCell) {
- errors.push({
- type: "update",
- cellId: op.cell_id,
- message: "new_xml must contain an mxCell element",
- })
- continue
- }
-
- // Validate ID matches
- const newCellId = newCell.getAttribute("id")
- if (newCellId !== op.cell_id) {
- errors.push({
- type: "update",
- cellId: op.cell_id,
- message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`,
- })
- continue
- }
-
- // Import and replace the node
- const importedNode = doc.importNode(newCell, true)
- existingCell.parentNode?.replaceChild(importedNode, existingCell)
-
- // Update the map with the new element
- cellMap.set(op.cell_id, importedNode)
- } else if (op.operation === "add") {
- // Check if ID already exists
- if (cellMap.has(op.cell_id)) {
- errors.push({
- type: "add",
- cellId: op.cell_id,
- message: `Cell with id="${op.cell_id}" already exists`,
- })
- continue
- }
-
- if (!op.new_xml) {
- errors.push({
- type: "add",
- cellId: op.cell_id,
- message: "new_xml is required for add operation",
- })
- continue
- }
-
- // Parse the new XML
- const newDoc = parser.parseFromString(
- `${op.new_xml} `,
- "text/xml",
- )
- const newCell = newDoc.querySelector("mxCell")
- if (!newCell) {
- errors.push({
- type: "add",
- cellId: op.cell_id,
- message: "new_xml must contain an mxCell element",
- })
- continue
- }
-
- // Validate ID matches
- const newCellId = newCell.getAttribute("id")
- if (newCellId !== op.cell_id) {
- errors.push({
- type: "add",
- cellId: op.cell_id,
- message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`,
- })
- continue
- }
-
- // Import and append the node
- const importedNode = doc.importNode(newCell, true)
- root.appendChild(importedNode)
-
- // Add to map
- cellMap.set(op.cell_id, importedNode)
- } else if (op.operation === "delete") {
- // Protect root cells from deletion
- if (op.cell_id === "0" || op.cell_id === "1") {
- errors.push({
- type: "delete",
- cellId: op.cell_id,
- message: `Cannot delete root cell "${op.cell_id}"`,
- })
- continue
- }
-
- const existingCell = cellMap.get(op.cell_id)
- if (!existingCell) {
- // Cell not found - might have been cascade-deleted by a previous operation
- // Skip silently instead of erroring (AI may redundantly list children/edges)
- continue
- }
-
- // Cascade delete: collect all cells to delete (children + edges + self)
- const cellsToDelete = new Set()
-
- // Recursive function to find all descendants
- const collectDescendants = (cellId: string) => {
- if (cellsToDelete.has(cellId)) return
- cellsToDelete.add(cellId)
-
- // Find children (cells where parent === cellId)
- const children = root.querySelectorAll(
- `mxCell[parent="${cellId}"]`,
- )
- children.forEach((child) => {
- const childId = child.getAttribute("id")
- if (childId && childId !== "0" && childId !== "1") {
- collectDescendants(childId)
- }
- })
- }
-
- // Collect the target cell and all its descendants
- collectDescendants(op.cell_id)
-
- // Find edges referencing any of the cells to be deleted
- // Also recursively collect children of those edges (e.g., edge labels)
- for (const cellId of cellsToDelete) {
- const referencingEdges = root.querySelectorAll(
- `mxCell[source="${cellId}"], mxCell[target="${cellId}"]`,
- )
- referencingEdges.forEach((edge) => {
- const edgeId = edge.getAttribute("id")
- // Protect root cells from being added via edge references
- if (edgeId && edgeId !== "0" && edgeId !== "1") {
- // Recurse to collect edge's children (like labels)
- collectDescendants(edgeId)
- }
- })
- }
-
- // Log what will be deleted
- if (cellsToDelete.size > 1) {
- console.log(
- `[applyDiagramOperations] Cascade delete "${op.cell_id}" → deleting ${cellsToDelete.size} cells: ${Array.from(cellsToDelete).join(", ")}`,
- )
- }
-
- // Delete all collected cells
- for (const cellId of cellsToDelete) {
- const cell = cellMap.get(cellId)
- if (cell) {
- cell.parentNode?.removeChild(cell)
- cellMap.delete(cellId)
- }
- }
- }
- }
-
- // Serialize back to string
- const serializer = new XMLSerializer()
- const result = serializer.serializeToString(doc)
-
- return { result, errors }
-}
-
-// ============================================================================
-// Validation Helper Functions
-// ============================================================================
-
-/** Check for duplicate structural attributes in a tag */
-function checkDuplicateAttributes(xml: string): string | null {
- const structuralSet = new Set(STRUCTURAL_ATTRS)
- const tagPattern = /<[^>]+>/g
- let tagMatch
- while ((tagMatch = tagPattern.exec(xml)) !== null) {
- const tag = tagMatch[0]
- const attrPattern = /\s([a-zA-Z_:][a-zA-Z0-9_:.-]*)\s*=/g
- const attributes = new Map()
- let attrMatch
- while ((attrMatch = attrPattern.exec(tag)) !== null) {
- const attrName = attrMatch[1]
- attributes.set(attrName, (attributes.get(attrName) || 0) + 1)
- }
- const duplicates = Array.from(attributes.entries())
- .filter(([name, count]) => count > 1 && structuralSet.has(name))
- .map(([name]) => name)
- if (duplicates.length > 0) {
- return `Invalid XML: Duplicate structural attribute(s): ${duplicates.join(", ")}. Remove duplicate attributes.`
- }
- }
- return null
-}
-
-/** Check for duplicate IDs in XML */
-function checkDuplicateIds(xml: string): string | null {
- const idPattern = /\bid\s*=\s*["']([^"']+)["']/gi
- const ids = new Map()
- let idMatch
- while ((idMatch = idPattern.exec(xml)) !== null) {
- const id = idMatch[1]
- ids.set(id, (ids.get(id) || 0) + 1)
- }
- const duplicateIds = Array.from(ids.entries())
- .filter(([, count]) => count > 1)
- .map(([id, count]) => `'${id}' (${count}x)`)
- if (duplicateIds.length > 0) {
- return `Invalid XML: Found duplicate ID(s): ${duplicateIds.slice(0, 3).join(", ")}. All id attributes must be unique.`
- }
- return null
-}
-
-/** Check for tag mismatches using parsed tags */
-function checkTagMismatches(xml: string): string | null {
- const xmlWithoutComments = xml.replace(//g, "")
- const tags = parseXmlTags(xmlWithoutComments)
- const tagStack: string[] = []
-
- for (const { tagName, isClosing, isSelfClosing } of tags) {
- if (isClosing) {
- if (tagStack.length === 0) {
- return `Invalid XML: Closing tag ${tagName}> without matching opening tag`
- }
- const expected = tagStack.pop()
- if (expected?.toLowerCase() !== tagName.toLowerCase()) {
- return `Invalid XML: Expected closing tag ${expected}> but found ${tagName}>`
- }
- } else if (!isSelfClosing) {
- tagStack.push(tagName)
- }
- }
- if (tagStack.length > 0) {
- return `Invalid XML: Document has ${tagStack.length} unclosed tag(s): ${tagStack.join(", ")}`
- }
- return null
-}
-
-/** Check for invalid character references */
-function checkCharacterReferences(xml: string): string | null {
- const charRefPattern = /?[^;]+;?/g
- let charMatch
- while ((charMatch = charRefPattern.exec(xml)) !== null) {
- const ref = charMatch[0]
- if (ref.startsWith("")) {
- if (!ref.endsWith(";")) {
- return `Invalid XML: Missing semicolon after hex reference: ${ref}`
- }
- const hexDigits = ref.substring(3, ref.length - 1)
- if (hexDigits.length === 0 || !/^[0-9a-fA-F]+$/.test(hexDigits)) {
- return `Invalid XML: Invalid hex character reference: ${ref}`
- }
- } else if (ref.startsWith("")) {
- if (!ref.endsWith(";")) {
- return `Invalid XML: Missing semicolon after decimal reference: ${ref}`
- }
- const decDigits = ref.substring(2, ref.length - 1)
- if (decDigits.length === 0 || !/^[0-9]+$/.test(decDigits)) {
- return `Invalid XML: Invalid decimal character reference: ${ref}`
- }
- }
- }
- return null
-}
-
-/** Check for invalid entity references */
-function checkEntityReferences(xml: string): string | null {
- const xmlWithoutComments = xml.replace(//g, "")
- const bareAmpPattern = /&(?!(?:lt|gt|amp|quot|apos|#))/g
- if (bareAmpPattern.test(xmlWithoutComments)) {
- return "Invalid XML: Found unescaped & character(s). Replace & with &"
- }
- const invalidEntityPattern = /&([a-zA-Z][a-zA-Z0-9]*);/g
- let entityMatch
- while (
- (entityMatch = invalidEntityPattern.exec(xmlWithoutComments)) !== null
- ) {
- if (!VALID_ENTITIES.has(entityMatch[1])) {
- return `Invalid XML: Invalid entity reference: &${entityMatch[1]}; - use only valid XML entities (lt, gt, amp, quot, apos)`
- }
- }
- return null
-}
-
-/** Check for nested mxCell tags using regex */
-function checkNestedMxCells(xml: string): string | null {
- const cellTagPattern = /<\/?mxCell[^>]*>/g
- const cellStack: number[] = []
- let cellMatch
- while ((cellMatch = cellTagPattern.exec(xml)) !== null) {
- const tag = cellMatch[0]
- if (tag.startsWith(" ")) {
- if (cellStack.length > 0) cellStack.pop()
- } else if (!tag.endsWith("/>")) {
- const isLabelOrGeometry =
- /\sas\s*=\s*["'](valueLabel|geometry)["']/.test(tag)
- if (!isLabelOrGeometry) {
- cellStack.push(cellMatch.index)
- if (cellStack.length > 1) {
- return "Invalid XML: Found nested mxCell tags. Cells should be siblings, not nested inside other mxCell elements."
- }
- }
- }
- }
- return null
-}
-
-/**
- * Validates draw.io XML structure for common issues
- * Uses DOM parsing + additional regex checks for high accuracy
- * @param xml - The XML string to validate
- * @returns null if valid, error message string if invalid
- */
-export function validateMxCellStructure(xml: string): string | null {
- // Size check for performance
- if (xml.length > MAX_XML_SIZE) {
- console.warn(
- `[validateMxCellStructure] XML size (${xml.length}) exceeds ${MAX_XML_SIZE} bytes, may cause performance issues`,
- )
- }
-
- // 0. First use DOM parser to catch syntax errors (most accurate)
- try {
- const parser = new DOMParser()
- const doc = parser.parseFromString(xml, "text/xml")
- const parseError = doc.querySelector("parsererror")
- if (parseError) {
- return `Invalid XML: The XML contains syntax errors (likely unescaped special characters like <, >, & in attribute values). Please escape special characters: use < for <, > for >, & for &, " for ". Regenerate the diagram with properly escaped values.`
- }
-
- // DOM-based checks for nested mxCell
- const allCells = doc.querySelectorAll("mxCell")
- for (const cell of allCells) {
- if (cell.parentElement?.tagName === "mxCell") {
- const id = cell.getAttribute("id") || "unknown"
- return `Invalid XML: Found nested mxCell (id="${id}"). Cells should be siblings, not nested inside other mxCell elements.`
- }
- }
- } catch (error) {
- // Log unexpected DOMParser errors before falling back to regex checks
- console.warn(
- "[validateMxCellStructure] DOMParser threw unexpected error, falling back to regex validation:",
- error,
- )
- }
-
- // 1. Check for CDATA wrapper (invalid at document root)
- if (/^\s* from end"
- }
-
- // 2. Check for duplicate structural attributes
- const dupAttrError = checkDuplicateAttributes(xml)
- if (dupAttrError) {
- return dupAttrError
- }
-
- // 3. Check for unescaped < in attribute values
- const attrValuePattern = /=\s*"([^"]*)"/g
- let attrValMatch
- while ((attrValMatch = attrValuePattern.exec(xml)) !== null) {
- const value = attrValMatch[1]
- if (//g
- let commentMatch
- while ((commentMatch = commentPattern.exec(xml)) !== null) {
- if (/--/.test(commentMatch[1])) {
- return "Invalid XML: Comment contains -- (double hyphen) which is not allowed"
- }
- }
-
- // 8. Check for unescaped entity references and invalid entity names
- const entityError = checkEntityReferences(xml)
- if (entityError) {
- return entityError
- }
-
- // 9. Check for empty id attributes on mxCell
- if (/]*\sid\s*=\s*["']\s*["'][^>]*>/g.test(xml)) {
- return "Invalid XML: Found mxCell element(s) with empty id attribute"
- }
-
- // 10. Check for nested mxCell tags
- const nestedCellError = checkNestedMxCells(xml)
- if (nestedCellError) {
- return nestedCellError
- }
-
- return null
-}
-
-/**
- * Attempts to auto-fix common XML issues in draw.io diagrams
- * @param xml - The XML string to fix
- * @returns Object with fixed XML and list of fixes applied
- */
-export function autoFixXml(xml: string): { fixed: string; fixes: string[] } {
- let fixed = xml
- const fixes: string[] = []
-
- // 0. Fix JSON-escaped XML (common when XML is stored in JSON without unescaping)
- // Only apply when we see JSON-escaped attribute patterns like =\"value\"
- // Don't apply to legitimate \n in value attributes (draw.io uses these for line breaks)
- if (/=\\"/.test(fixed)) {
- // Replace literal \" with actual quotes
- fixed = fixed.replace(/\\"/g, '"')
- // Replace literal \n with actual newlines (only after confirming JSON-escaped)
- fixed = fixed.replace(/\\n/g, "\n")
- fixes.push("Fixed JSON-escaped XML")
- }
-
- // 1. Remove CDATA wrapper (MUST be before text-before-root check)
- if (/^\s*\s*$/, "")
- fixes.push("Removed CDATA wrapper")
- }
-
- // 1b. Strip trailing LLM wrapper tags (DeepSeek, Anthropic, etc.)
- // These are closing tags after the last valid mxCell that break XML parsing
- const lastSelfClose = fixed.lastIndexOf("/>")
- const lastMxCellClose = fixed.lastIndexOf(" ")
- const lastValidEnd = Math.max(lastSelfClose, lastMxCellClose)
- if (lastValidEnd !== -1) {
- const endOffset = lastMxCellClose > lastSelfClose ? 9 : 2
- const suffix = fixed.slice(lastValidEnd + endOffset)
- // If suffix contains only closing tags (wrapper tags) or whitespace, strip it
- if (/^(\s*<\/[^>]+>)+\s*$/.test(suffix)) {
- fixed = fixed.slice(0, lastValidEnd + endOffset)
- fixes.push("Stripped trailing LLM wrapper tags")
- }
- }
-
- // 2. Remove text before XML declaration or root element (only if it's garbage text, not valid XML)
- const xmlStart = fixed.search(/<(\?xml|mxGraphModel|mxfile)/i)
- if (xmlStart > 0 && !/^<[a-zA-Z]/.test(fixed.trim())) {
- fixed = fixed.substring(xmlStart)
- fixes.push("Removed text before XML root")
- }
-
- // 2. Fix duplicate attributes (keep first occurrence, remove duplicates)
- let dupAttrFixed = false
- fixed = fixed.replace(/<[^>]+>/g, (tag) => {
- let newTag = tag
-
- for (const attr of STRUCTURAL_ATTRS) {
- // Find all occurrences of this attribute
- const attrRegex = new RegExp(
- `\\s${attr}\\s*=\\s*["'][^"']*["']`,
- "gi",
- )
- const matches = tag.match(attrRegex)
-
- if (matches && matches.length > 1) {
- // Keep first, remove others
- let firstKept = false
- newTag = newTag.replace(attrRegex, (m) => {
- if (!firstKept) {
- firstKept = true
- return m
- }
- dupAttrFixed = true
- return ""
- })
- }
- }
- return newTag
- })
- if (dupAttrFixed) {
- fixes.push("Removed duplicate structural attributes")
- }
-
- // 3. Fix unescaped & characters (but not valid entities)
- // Match & not followed by valid entity pattern
- const ampersandPattern =
- /&(?!(?:lt|gt|amp|quot|apos|#[0-9]+|#x[0-9a-fA-F]+);)/g
- if (ampersandPattern.test(fixed)) {
- fixed = fixed.replace(
- /&(?!(?:lt|gt|amp|quot|apos|#[0-9]+|#x[0-9a-fA-F]+);)/g,
- "&",
- )
- fixes.push("Escaped unescaped & characters")
- }
-
- // 3. Fix invalid entity names like &quot; -> "
- // Common mistake: double-escaping
- const invalidEntities = [
- { pattern: /&quot;/g, replacement: """, name: "&quot;" },
- { pattern: /&lt;/g, replacement: "<", name: "&lt;" },
- { pattern: /&gt;/g, replacement: ">", name: "&gt;" },
- { pattern: /&apos;/g, replacement: "'", name: "&apos;" },
- { pattern: /&amp;/g, replacement: "&", name: "&amp;" },
- ]
- for (const { pattern, replacement, name } of invalidEntities) {
- if (pattern.test(fixed)) {
- fixed = fixed.replace(pattern, replacement)
- fixes.push(`Fixed double-escaped entity ${name}`)
- }
- }
-
- // 3b. Fix malformed attribute values where " is used as delimiter instead of actual quotes
- // Pattern: attr="value" should become attr="value" (the " was meant to be the quote delimiter)
- // This commonly happens with dashPattern="1 1;"
- const malformedQuotePattern = /(\s[a-zA-Z][a-zA-Z0-9_:-]*)="/
- if (malformedQuotePattern.test(fixed)) {
- // Replace =" with =" and trailing " before next attribute or tag end with "
- fixed = fixed.replace(
- /(\s[a-zA-Z][a-zA-Z0-9_:-]*)="([^&]*?)"/g,
- '$1="$2"',
- )
- fixes.push(
- 'Fixed malformed attribute quotes (="..." to ="...")',
- )
- }
-
- // 3c. Fix malformed closing tags like ->
- const malformedClosingTag = /<\/([a-zA-Z][a-zA-Z0-9]*)\s*\/>/g
- if (malformedClosingTag.test(fixed)) {
- fixed = fixed.replace(/<\/([a-zA-Z][a-zA-Z0-9]*)\s*\/>/g, "$1>")
- fixes.push("Fixed malformed closing tags ( to )")
- }
-
- // 3d. Fix missing space between attributes like vertex="1"parent="1"
- const missingSpacePattern = /("[^"]*")([a-zA-Z][a-zA-Z0-9_:-]*=)/g
- if (missingSpacePattern.test(fixed)) {
- fixed = fixed.replace(/("[^"]*")([a-zA-Z][a-zA-Z0-9_:-]*=)/g, "$1 $2")
- fixes.push("Added missing space between attributes")
- }
-
- // 3e. Fix unescaped quotes in style color values like fillColor="#fff2e6"
- // The " after Color= prematurely ends the style attribute. Remove it.
- // Pattern: ;fillColor="#fff → ;fillColor=#fff (remove first ", keep second as style closer)
- const quotedColorPattern = /;([a-zA-Z]*[Cc]olor)="#/
- if (quotedColorPattern.test(fixed)) {
- fixed = fixed.replace(/;([a-zA-Z]*[Cc]olor)="#/g, ";$1=#")
- fixes.push("Removed quotes around color values in style")
- }
-
- // 4. Fix unescaped < and > in attribute values
- // < is required to be escaped, > is not strictly required but we escape for consistency
- const attrPattern = /(=\s*")([^"]*?)(<)([^"]*?)(")/g
- let attrMatch
- let hasUnescapedLt = false
- while ((attrMatch = attrPattern.exec(fixed)) !== null) {
- if (!attrMatch[3].startsWith("<")) {
- hasUnescapedLt = true
- break
- }
- }
- if (hasUnescapedLt) {
- // Replace < and > with < and > inside attribute values
- fixed = fixed.replace(/=\s*"([^"]*)"/g, (_match, value) => {
- const escaped = value.replace(//g, ">")
- return `="${escaped}"`
- })
- fixes.push("Escaped <> characters in attribute values")
- }
-
- // 5. Fix invalid character references (remove malformed ones)
- // Pattern: followed by non-hex chars before ;
- const invalidHexRefs: string[] = []
- fixed = fixed.replace(/([^;]*);/g, (match, hex) => {
- if (/^[0-9a-fA-F]+$/.test(hex) && hex.length > 0) {
- return match // Valid hex ref, keep it
- }
- invalidHexRefs.push(match)
- return "" // Remove invalid ref
- })
- if (invalidHexRefs.length > 0) {
- fixes.push(
- `Removed ${invalidHexRefs.length} invalid hex character reference(s)`,
- )
- }
-
- // 6. Fix invalid decimal character references
- const invalidDecRefs: string[] = []
- fixed = fixed.replace(/([^x][^;]*);/g, (match, dec) => {
- if (/^[0-9]+$/.test(dec) && dec.length > 0) {
- return match // Valid decimal ref, keep it
- }
- invalidDecRefs.push(match)
- return "" // Remove invalid ref
- })
- if (invalidDecRefs.length > 0) {
- fixes.push(
- `Removed ${invalidDecRefs.length} invalid decimal character reference(s)`,
- )
- }
-
- // 7. Fix invalid comment syntax (replace -- with - repeatedly until none left)
- fixed = fixed.replace(//g, (match, content) => {
- if (/--/.test(content)) {
- // Keep replacing until no double hyphens remain
- let fixedContent = content
- while (/--/.test(fixedContent)) {
- fixedContent = fixedContent.replace(/--/g, "-")
- }
- fixes.push("Fixed invalid comment syntax (removed double hyphens)")
- return ``
- }
- return match
- })
-
- // 8. Fix tags that should be (common LLM mistake)
- // This handles both opening and closing tags
- const hasCellTags = /<\/?Cell[\s>]/i.test(fixed)
- if (hasCellTags) {
- console.log("[autoFixXml] Step 8: Found tags to fix")
- const beforeFix = fixed
- fixed = fixed.replace(//gi, "")
- fixed = fixed.replace(/<\/Cell>/gi, " ")
- if (beforeFix !== fixed) {
- console.log("[autoFixXml] Step 8: Fixed tags")
- }
- fixes.push("Fixed tags to ")
- }
-
- // 8b. Fix common closing tag typos (MUST run before foreign tag removal)
- const tagTypos = [
- { wrong: /<\/mxElement>/gi, right: " ", name: "" },
- { wrong: /<\/mxcell>/g, right: " | | | | ", name: " | " }, // case sensitivity
- {
- wrong: /<\/mxgeometry>/g,
- right: "",
- name: "",
- },
- { wrong: /<\/mxpoint>/g, right: "", name: "" },
- {
- wrong: /<\/mxgraphmodel>/gi,
- right: "",
- name: "",
- },
- ]
- for (const { wrong, right, name } of tagTypos) {
- const before = fixed
- fixed = fixed.replace(wrong, right)
- if (fixed !== before) {
- fixes.push(`Fixed typo ${name} to ${right}`)
- }
- }
-
- // 8c. Remove non-draw.io tags (after typo fixes so lowercase variants are fixed first)
- // IMPORTANT: Only remove tags at the element level, NOT inside quoted attribute values
- // Tags like , inside value="text " should be preserved (they're HTML content)
- const validDrawioTags = new Set([
- "mxfile",
- "diagram",
- "mxGraphModel",
- "root",
- "mxCell",
- "mxGeometry",
- "mxPoint",
- "Array",
- "Object",
- "mxRectangle",
- ])
-
- // Helper: Check if a position is inside a quoted attribute value
- // by counting unescaped quotes before that position
- const isInsideQuotes = (str: string, pos: number): boolean => {
- let inQuote = false
- let quoteChar = ""
- for (let i = 0; i < pos && i < str.length; i++) {
- const c = str[i]
- if (inQuote) {
- if (c === quoteChar) inQuote = false
- } else if (c === '"' || c === "'") {
- // Check if this quote is part of an attribute (preceded by =)
- // Look back for = sign
- let j = i - 1
- while (j >= 0 && /\s/.test(str[j])) j--
- if (j >= 0 && str[j] === "=") {
- inQuote = true
- quoteChar = c
- }
- }
- }
- return inQuote
- }
-
- const foreignTagPattern = /<\/?([a-zA-Z][a-zA-Z0-9_]*)[^>]*>/g
- let foreignMatch
- const foreignTags = new Set()
- const foreignTagPositions: Array<{
- tag: string
- start: number
- end: number
- }> = []
-
- while ((foreignMatch = foreignTagPattern.exec(fixed)) !== null) {
- const tagName = foreignMatch[1]
- // Skip if this is a valid draw.io tag
- if (validDrawioTags.has(tagName)) continue
- // Skip if this tag is inside a quoted attribute value
- if (isInsideQuotes(fixed, foreignMatch.index)) continue
-
- foreignTags.add(tagName)
- foreignTagPositions.push({
- tag: tagName,
- start: foreignMatch.index,
- end: foreignMatch.index + foreignMatch[0].length,
- })
- }
-
- if (foreignTagPositions.length > 0) {
- // Remove tags from end to start to preserve indices
- foreignTagPositions.sort((a, b) => b.start - a.start)
- for (const { start, end } of foreignTagPositions) {
- fixed = fixed.slice(0, start) + fixed.slice(end)
- }
- fixes.push(
- `Removed foreign tags: ${Array.from(foreignTags).join(", ")}`,
- )
- }
-
- // 10. Fix unclosed tags by appending missing closing tags
- // Use parseXmlTags helper to track open tags
- const tagStack: string[] = []
- const parsedTags = parseXmlTags(fixed)
-
- for (const { tagName, isClosing, isSelfClosing } of parsedTags) {
- if (isClosing) {
- // Find matching opening tag (may not be the last one if there's mismatch)
- const lastIdx = tagStack.lastIndexOf(tagName)
- if (lastIdx !== -1) {
- tagStack.splice(lastIdx, 1)
- }
- } else if (!isSelfClosing) {
- tagStack.push(tagName)
- }
- }
-
- // If there are unclosed tags, append closing tags in reverse order
- // But first verify with simple count that they're actually unclosed
- if (tagStack.length > 0) {
- const tagsToClose: string[] = []
- for (const tagName of tagStack.reverse()) {
- // Simple count check: only close if opens > closes
- const openCount = (
- fixed.match(new RegExp(`<${tagName}[\\s>]`, "gi")) || []
- ).length
- const closeCount = (
- fixed.match(new RegExp(`${tagName}>`, "gi")) || []
- ).length
- if (openCount > closeCount) {
- tagsToClose.push(tagName)
- }
- }
- if (tagsToClose.length > 0) {
- const closingTags = tagsToClose.map((t) => `${t}>`).join("\n")
- fixed = fixed.trimEnd() + "\n" + closingTags
- fixes.push(
- `Closed ${tagsToClose.length} unclosed tag(s): ${tagsToClose.join(", ")}`,
- )
- }
- }
-
- // 10b. Remove extra closing tags (more closes than opens)
- // Need to properly count self-closing tags (they don't need closing tags)
- // IMPORTANT: Only count tags at element level, NOT inside quoted attribute values
- const tagCounts = new Map<
- string,
- { opens: number; closes: number; selfClosing: number }
- >()
- // Match full tags to detect self-closing by checking if ends with />
- const fullTagPattern = /<(\/?[a-zA-Z][a-zA-Z0-9]*)[^>]*>/g
- let tagCountMatch
- while ((tagCountMatch = fullTagPattern.exec(fixed)) !== null) {
- // Skip tags inside quoted attribute values (e.g., value="Title ")
- if (isInsideQuotes(fixed, tagCountMatch.index)) continue
-
- const fullMatch = tagCountMatch[0] // e.g., " " or " "
- const tagPart = tagCountMatch[1] // e.g., "mxCell" or "/mxCell"
- const isClosing = tagPart.startsWith("/")
- const isSelfClosing = fullMatch.endsWith("/>")
- const tagName = isClosing ? tagPart.slice(1) : tagPart
-
- // Only count valid draw.io tags - skip partial/invalid tags like "mx" from streaming
- if (!validDrawioTags.has(tagName)) continue
-
- let counts = tagCounts.get(tagName)
- if (!counts) {
- counts = { opens: 0, closes: 0, selfClosing: 0 }
- tagCounts.set(tagName, counts)
- }
- if (isClosing) {
- counts.closes++
- } else if (isSelfClosing) {
- counts.selfClosing++
- } else {
- counts.opens++
- }
- }
-
- // Log tag counts for debugging
- for (const [tagName, counts] of tagCounts) {
- if (
- tagName === "mxCell" ||
- tagName === "mxGeometry" ||
- counts.opens !== counts.closes
- ) {
- console.log(
- `[autoFixXml] Step 10b: ${tagName} - opens: ${counts.opens}, closes: ${counts.closes}, selfClosing: ${counts.selfClosing}`,
- )
- }
- }
-
- // Find tags with extra closing tags (self-closing tags are balanced, don't need closing)
- for (const [tagName, counts] of tagCounts) {
- const extraCloses = counts.closes - counts.opens // Only compare opens vs closes (self-closing are balanced)
- if (extraCloses > 0) {
- console.log(
- `[autoFixXml] Step 10b: ${tagName} has ${counts.opens} opens, ${counts.closes} closes, removing ${extraCloses} extra`,
- )
- // Remove extra closing tags from the end
- let removed = 0
- const closeTagPattern = new RegExp(`${tagName}>`, "g")
- const matches = [...fixed.matchAll(closeTagPattern)]
- // Remove from the end (last occurrences are likely the extras)
- for (
- let i = matches.length - 1;
- i >= 0 && removed < extraCloses;
- i--
- ) {
- const match = matches[i]
- const idx = match.index ?? 0
- fixed = fixed.slice(0, idx) + fixed.slice(idx + match[0].length)
- removed++
- }
- if (removed > 0) {
- console.log(
- `[autoFixXml] Step 10b: Removed ${removed} extra ${tagName}>`,
- )
- fixes.push(
- `Removed ${removed} extra ${tagName}> closing tag(s)`,
- )
- }
- }
- }
-
- // 10c. Remove trailing garbage after last XML tag (e.g., stray backslashes, text)
- // Find the last valid closing tag or self-closing tag
- const closingTagPattern = /<\/[a-zA-Z][a-zA-Z0-9]*>|\/>/g
- let lastValidTagEnd = -1
- let closingMatch
- while ((closingMatch = closingTagPattern.exec(fixed)) !== null) {
- lastValidTagEnd = closingMatch.index + closingMatch[0].length
- }
- if (lastValidTagEnd > 0 && lastValidTagEnd < fixed.length) {
- const trailing = fixed.slice(lastValidTagEnd).trim()
- if (trailing) {
- fixed = fixed.slice(0, lastValidTagEnd)
- fixes.push("Removed trailing garbage after last XML tag")
- }
- }
-
- // 11. Fix nested mxCell by flattening
- // Pattern A: ...... (duplicate ID)
- // Pattern B: ...... (different ID - true nesting)
- const lines = fixed.split("\n")
- let newLines: string[] = []
- let nestedFixed = 0
- let extraClosingToRemove = 0
-
- // First pass: fix duplicate ID nesting (same as before)
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i]
- const nextLine = lines[i + 1]
-
- // Check if current line and next line are both mxCell opening tags with same ID
- if (
- nextLine &&
- / ") &&
- !nextLine.includes("/>")
- ) {
- const id1 = line.match(/\bid\s*=\s*["']([^"']+)["']/)?.[1]
- const id2 = nextLine.match(/\bid\s*=\s*["']([^"']+)["']/)?.[1]
-
- if (id1 && id1 === id2) {
- nestedFixed++
- extraClosingToRemove++ // Need to remove one later
- continue // Skip this duplicate opening line
- }
- }
-
- // Remove extra if we have pending removals
- if (extraClosingToRemove > 0 && /^\s*<\/mxCell>\s*$/.test(line)) {
- extraClosingToRemove--
- continue // Skip this closing tag
- }
-
- newLines.push(line)
- }
-
- if (nestedFixed > 0) {
- fixed = newLines.join("\n")
- fixes.push(`Flattened ${nestedFixed} duplicate-ID nested mxCell(s)`)
- }
-
- // Second pass: fix true nesting (different IDs)
- // Insert before nested child to close parent
- const lines2 = fixed.split("\n")
- newLines = []
- let trueNestedFixed = 0
- let cellDepth = 0
- let pendingCloseRemoval = 0
-
- for (let i = 0; i < lines2.length; i++) {
- const line = lines2[i]
- const trimmed = line.trim()
-
- // Track mxCell depth
- const isOpenCell = / ")
- const isCloseCell = trimmed === ""
-
- if (isOpenCell) {
- if (cellDepth > 0) {
- // Found nested cell - insert closing tag for parent before this line
- const indent = line.match(/^(\s*)/)?.[1] || ""
- newLines.push(indent + "")
- trueNestedFixed++
- pendingCloseRemoval++ // Need to remove one later
- }
- cellDepth = 1 // Reset to 1 since we just opened a new cell
- newLines.push(line)
- } else if (isCloseCell) {
- if (pendingCloseRemoval > 0) {
- pendingCloseRemoval--
- // Skip this extra closing tag
- } else {
- cellDepth = Math.max(0, cellDepth - 1)
- newLines.push(line)
- }
- } else {
- newLines.push(line)
- }
- }
-
- if (trueNestedFixed > 0) {
- fixed = newLines.join("\n")
- fixes.push(`Fixed ${trueNestedFixed} true nested mxCell(s)`)
- }
-
- // 12. Fix duplicate IDs by appending suffix
- const seenIds = new Map()
- const duplicateIds: string[] = []
-
- // First pass: find duplicates
- const idPattern = /\bid\s*=\s*["']([^"']+)["']/gi
- let idMatch
- while ((idMatch = idPattern.exec(fixed)) !== null) {
- const id = idMatch[1]
- seenIds.set(id, (seenIds.get(id) || 0) + 1)
- }
-
- // Find which IDs are duplicated
- for (const [id, count] of seenIds) {
- if (count > 1) duplicateIds.push(id)
- }
-
- // Second pass: rename duplicates (keep first occurrence, rename others)
- if (duplicateIds.length > 0) {
- const idCounters = new Map()
- fixed = fixed.replace(/\bid\s*=\s*["']([^"']+)["']/gi, (match, id) => {
- if (!duplicateIds.includes(id)) return match
-
- const count = idCounters.get(id) || 0
- idCounters.set(id, count + 1)
-
- if (count === 0) return match // Keep first occurrence
-
- // Rename subsequent occurrences
- const newId = `${id}_dup${count}`
- return match.replace(id, newId)
- })
- fixes.push(`Renamed ${duplicateIds.length} duplicate ID(s)`)
- }
-
- // 9. Fix empty id attributes by generating unique IDs
- let emptyIdCount = 0
- fixed = fixed.replace(
- /]*)\sid\s*=\s*["']\s*["']([^>]*)>/g,
- (_match, before, after) => {
- emptyIdCount++
- const newId = `cell_${Date.now()}_${emptyIdCount}`
- return ``
- },
- )
- if (emptyIdCount > 0) {
- fixes.push(`Generated ${emptyIdCount} missing ID(s)`)
- }
-
- // 13. Aggressive: drop broken mxCell elements that can't be fixed
- // Only do this if DOM parser still finds errors after all other fixes
- if (typeof DOMParser !== "undefined") {
- let droppedCells = 0
- let maxIterations = MAX_DROP_ITERATIONS
- while (maxIterations-- > 0) {
- const parser = new DOMParser()
- const doc = parser.parseFromString(fixed, "text/xml")
- const parseError = doc.querySelector("parsererror")
- if (!parseError) break // Valid now!
-
- const errText = parseError.textContent || ""
- const match = errText.match(/(\d+):\d+:/)
- if (!match) break
-
- const errLine = parseInt(match[1], 10) - 1
- const lines = fixed.split("\n")
-
- // Find the mxCell containing this error line
- let cellStart = errLine
- let cellEnd = errLine
-
- // Go back to find 0 && !lines[cellStart].includes(" or />
- while (cellEnd < lines.length - 1) {
- if (
- lines[cellEnd].includes(" ") ||
- lines[cellEnd].trim().endsWith("/>")
- ) {
- break
- }
- cellEnd++
- }
-
- // Remove these lines
- lines.splice(cellStart, cellEnd - cellStart + 1)
- fixed = lines.join("\n")
- droppedCells++
- }
- if (droppedCells > 0) {
- fixes.push(`Dropped ${droppedCells} unfixable mxCell element(s)`)
- }
- }
-
- return { fixed, fixes }
-}
-
-/**
- * Validates XML and attempts to fix if invalid
- * @param xml - The XML string to validate and potentially fix
- * @returns Object with validation result, fixed XML if applicable, and fixes applied
- */
-export function validateAndFixXml(xml: string): {
- valid: boolean
- error: string | null
- fixed: string | null
- fixes: string[]
-} {
- // First validation attempt
- let error = validateMxCellStructure(xml)
-
- if (!error) {
- return { valid: true, error: null, fixed: null, fixes: [] }
- }
-
- // Try to fix
- const { fixed, fixes } = autoFixXml(xml)
- console.log("[validateAndFixXml] Fixes applied:", fixes)
-
- // Validate the fixed version
- error = validateMxCellStructure(fixed)
- if (error) {
- console.log("[validateAndFixXml] Still invalid after fix:", error)
- }
-
- if (!error) {
- return { valid: true, error: null, fixed, fixes }
- }
-
- // Still invalid after fixes - but return the partially fixed XML
- // so we can see what was fixed and what error remains
- return {
- valid: false,
- error,
- fixed: fixes.length > 0 ? fixed : null,
- fixes,
- }
-}
-
export function extractDiagramXML(xml_svg_string: string): string {
try {
// 1. Parse the SVG string (using built-in DOMParser in a browser-like environment)
diff --git a/next.config.ts b/next.config.ts
index 466596a4..366b1351 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -10,9 +10,11 @@ const nextConfig: NextConfig = {
env: {
APP_VERSION: packageJson.version,
},
- // Include instrumentation.ts in standalone build for Langfuse telemetry
+ // Include instrumentation.ts in standalone build for Langfuse telemetry,
+ // and the shape library docs get_shape_library reads at run time
outputFileTracingIncludes: {
"*": ["./instrumentation.ts"],
+ "/api/chat": ["./docs/shape-libraries/*.md"],
},
}
diff --git a/package-lock.json b/package-lock.json
index 556ed882..4ed8ed73 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,35 +1,36 @@
{
"name": "next-ai-draw-io",
- "version": "0.4.14",
+ "version": "0.4.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "next-ai-draw-io",
- "version": "0.4.14",
+ "version": "0.4.16",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/amazon-bedrock": "^4.0.1",
- "@ai-sdk/anthropic": "^3.0.0",
- "@ai-sdk/azure": "^3.0.0",
- "@ai-sdk/deepseek": "^2.0.0",
- "@ai-sdk/gateway": "^3.0.0",
- "@ai-sdk/google": "^3.0.0",
- "@ai-sdk/google-vertex": "^4.0.16",
- "@ai-sdk/openai": "^3.0.0",
- "@ai-sdk/react": "^3.0.1",
+ "@ai-sdk/amazon-bedrock": "^4.0.191",
+ "@ai-sdk/anthropic": "^3.0.127",
+ "@ai-sdk/azure": "^3.0.133",
+ "@ai-sdk/deepseek": "^2.0.71",
+ "@ai-sdk/google": "^3.0.130",
+ "@ai-sdk/google-vertex": "^4.0.210",
+ "@ai-sdk/openai": "^3.0.124",
+ "@ai-sdk/openai-compatible": "^2.0.81",
+ "@ai-sdk/react": "^3.0.303",
+ "@aihubmix/ai-sdk-provider": "^2.2.1",
"@aws-sdk/client-dynamodb": "^3.957.0",
"@aws-sdk/credential-providers": "^3.943.0",
"@extractus/article-extractor": "^8.0.18",
- "@formatjs/intl-localematcher": "^0.8.0",
+ "@formatjs/intl-localematcher": "^0.9.0",
"@langfuse/client": "^4.4.9",
"@langfuse/otel": "^4.4.4",
"@langfuse/tracing": "^4.4.9",
"@next/third-parties": "^16.0.6",
"@opennextjs/cloudflare": "^1.17.1",
- "@openrouter/ai-sdk-provider": "^1.5.4",
+ "@openrouter/ai-sdk-provider": "^2.10.0",
"@opentelemetry/api": "^1.9.0",
- "@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
"@opentelemetry/sdk-trace-node": "^2.2.0",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-collapsible": "^1.1.12",
@@ -43,21 +44,19 @@
"@radix-ui/react-tooltip": "^1.1.8",
"@radix-ui/react-use-controllable-state": "^1.2.2",
"@xmldom/xmldom": "^0.9.8",
- "ai": "^6.0.1",
+ "ai": "^6.0.300",
"base-64": "^1.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"idb": "^8.0.3",
"jsonrepair": "^3.13.1",
- "lightningcss": "^1.32.0",
- "lightningcss-linux-x64-gnu": "^1.32.0",
"lucide-react": "^0.577.0",
"motion": "^12.23.25",
"nanoid": "^5.0.0",
"negotiator": "^1.0.0",
"next": "^16.0.7",
- "ollama-ai-provider-v2": "^2.0.0",
+ "ollama-ai-provider-v2": "^3.6.0",
"pako": "^2.1.0",
"prism-react-renderer": "^2.4.1",
"react": "^19.1.2",
@@ -67,6 +66,7 @@
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"remark-gfm": "^4.0.1",
+ "saxes": "^6.0.0",
"server-only": "^0.0.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.0.2",
@@ -77,7 +77,7 @@
},
"devDependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
- "@biomejs/biome": "2.4.10",
+ "@biomejs/biome": "2.5.7",
"@playwright/test": "^1.57.0",
"@tailwindcss/postcss": "^4",
"@tailwindcss/typography": "^0.5.19",
@@ -96,8 +96,8 @@
"cross-env": "^10.1.0",
"electron": "^39.2.7",
"electron-builder": "^26.0.12",
- "esbuild": "^0.27.2",
- "eslint": "9.39.4",
+ "esbuild": "^0.28.0",
+ "eslint": "9.39.5",
"eslint-config-next": "16.1.6",
"husky": "^9.1.7",
"jsdom": "^27.4.0",
@@ -124,14 +124,15 @@
"license": "MIT"
},
"node_modules/@ai-sdk/amazon-bedrock": {
- "version": "4.0.64",
- "resolved": "https://registry.npmjs.org/@ai-sdk/amazon-bedrock/-/amazon-bedrock-4.0.64.tgz",
- "integrity": "sha512-foZskBdIHPGPZLfEKqkOP18JgfMy5Duik1TTduvEQ97mX3yfEzr4go1V3oU7ktry0dAL5BiNY38WwRhl+xhy9g==",
+ "version": "4.0.191",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/amazon-bedrock/-/amazon-bedrock-4.0.191.tgz",
+ "integrity": "sha512-7wgtq8On8oqmofGIGtYl8EOEbawnfiPbIbWpu2ksGNF065J8mbpE6DhkHNv0dpPbvnn4txL4sflUwUPwHw7/SQ==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/anthropic": "3.0.47",
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15",
+ "@ai-sdk/anthropic": "3.0.127",
+ "@ai-sdk/openai": "3.0.124",
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57",
"@smithy/eventstream-codec": "^4.0.1",
"@smithy/util-utf8": "^4.0.0",
"aws4fetch": "^1.0.20"
@@ -144,13 +145,13 @@
}
},
"node_modules/@ai-sdk/anthropic": {
- "version": "3.0.47",
- "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.47.tgz",
- "integrity": "sha512-E6Z3i/xvxGDxRskMMbuX9+xDK4l5LesrP2O7YQ0CcbAkYP25qTo/kYGf/AsJrLkNIY23HeO/kheUWtG1XZllDA==",
+ "version": "3.0.127",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.127.tgz",
+ "integrity": "sha512-Inff1DmPRVWi6QGr9YL2wiA4maILZuve9I8id/O4keQEoq6fJv6EfvkrSFw/nNvm2jiTcIUfDrpczeHTuhl9mg==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15"
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
},
"engines": {
"node": ">=18"
@@ -160,14 +161,15 @@
}
},
"node_modules/@ai-sdk/azure": {
- "version": "3.0.34",
- "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.34.tgz",
- "integrity": "sha512-nnOFtgvZYOa6XIeAm18i56NX77Yu4Bd+Tnbt85LGUEqwJFR54kFTlR1nm3BAJCphHrmQteJd1P3QErtyoXig8A==",
+ "version": "3.0.133",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-3.0.133.tgz",
+ "integrity": "sha512-iVCT1q6bRPWjzisu7Xy1bc1yWtegd3WSZzmi4+jkiFTKTxir703F5jH8mWPG0hPnqW/OjdkOrSzcQ063j7T/ng==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/openai": "3.0.33",
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15"
+ "@ai-sdk/deepseek": "2.0.71",
+ "@ai-sdk/openai": "3.0.124",
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
},
"engines": {
"node": ">=18"
@@ -177,13 +179,13 @@
}
},
"node_modules/@ai-sdk/deepseek": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-2.0.20.tgz",
- "integrity": "sha512-MAL04sDTOWUiBjAGWaVgyeE4bYRb9QpKYRlIeCTZFga6I8yQs50XakhWEssrmvVihdpHGkqpDtCHsFqCydsWLA==",
+ "version": "2.0.71",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-2.0.71.tgz",
+ "integrity": "sha512-2uLtZBgONfzEP7tZOg23sBzi33n1/gcW1zLNNw96/PLt9vdqDYIkGQnKRd6F+/0Wwy77jVulU+gzpiudk3f0UQ==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15"
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
},
"engines": {
"node": ">=18"
@@ -193,14 +195,14 @@
}
},
"node_modules/@ai-sdk/gateway": {
- "version": "3.0.55",
- "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.55.tgz",
- "integrity": "sha512-7xMeTJnCjwRwXKVCiv4Ly4qzWvDuW3+W1WIV0X1EFu6W83d4mEhV9bFArto10MeTw40ewuDjrbrZd21mXKohkw==",
+ "version": "3.0.209",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.209.tgz",
+ "integrity": "sha512-CjCBzC35lRZ0LnUYLDfTB92p0Fr1Fu9Wrivc+LI0u3ox/DyZ0wPOHjt8DKBKHD/R4aZ86+JCFFuD9f8MFjAQFg==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15",
- "@vercel/oidc": "3.1.0"
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57",
+ "@vercel/oidc": "3.2.0"
},
"engines": {
"node": ">=18"
@@ -210,13 +212,13 @@
}
},
"node_modules/@ai-sdk/google": {
- "version": "3.0.31",
- "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.31.tgz",
- "integrity": "sha512-RVNz8WFSIRbXbYDBE6JvlE2escWPJimBCs22LzKEYH7DNfl/X7cHNa1LFho4PsY6Ib0JmbzB8s2+i0wHs/wNCg==",
+ "version": "3.0.130",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.130.tgz",
+ "integrity": "sha512-DOhGfFT667LbopxMZU7kIiT4K8F2RNN8ziwmOxAfMbp3xTvAaxQ4ynPKNWBVXcCkGYDmzPb7QiazN1IRX8y3lw==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15"
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
},
"engines": {
"node": ">=18"
@@ -226,15 +228,16 @@
}
},
"node_modules/@ai-sdk/google-vertex": {
- "version": "4.0.63",
- "resolved": "https://registry.npmjs.org/@ai-sdk/google-vertex/-/google-vertex-4.0.63.tgz",
- "integrity": "sha512-/RNi6KSB4162DDYeXHUKQc5jLPmiJMkhTswLwbfPUEPyyFjbxpBWgeAk/vS/u8jxT4IPyYQ8cD/uyvJibmtmww==",
+ "version": "4.0.210",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/google-vertex/-/google-vertex-4.0.210.tgz",
+ "integrity": "sha512-7Jj41iWsTFUcv6pzji5AlW2QaMJt/PEMU/zwqji2H7U52WvZE345kyo+nKRJMFtHgSVqFmzmMUIM9gyeaXKRtw==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/anthropic": "3.0.47",
- "@ai-sdk/google": "3.0.31",
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15",
+ "@ai-sdk/anthropic": "3.0.127",
+ "@ai-sdk/google": "3.0.130",
+ "@ai-sdk/openai-compatible": "2.0.81",
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57",
"google-auth-library": "^10.5.0"
},
"engines": {
@@ -245,13 +248,29 @@
}
},
"node_modules/@ai-sdk/openai": {
- "version": "3.0.33",
- "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.33.tgz",
- "integrity": "sha512-O/8SVKAiwFHkGAUfBnrLb7L2IjbpP9ySWbmOktOfa0KtzutZkmKNrJ5CtB5dj+lwuENbOuZeRsnsZdOjar7hig==",
+ "version": "3.0.124",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.124.tgz",
+ "integrity": "sha512-7DpRUPXzJ+S6XasEJBojzFCs04J3rNPs0FZFF3PEeix49IggZAwsMrqscUwB/+05+87r01SS/jeopQGOOL4AEQ==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15"
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/openai-compatible": {
+ "version": "2.0.81",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-2.0.81.tgz",
+ "integrity": "sha512-L14Jd0lAFKNM42l9TxlpwkNiyMlSvSvGySLc9GN4FB0qOo4/WmYOeLyn/CPhghIr0K4O6vH1y0nChxGljq2ccg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57"
},
"engines": {
"node": ">=18"
@@ -261,9 +280,9 @@
}
},
"node_modules/@ai-sdk/provider": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz",
- "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==",
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.18.tgz",
+ "integrity": "sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==",
"license": "Apache-2.0",
"dependencies": {
"json-schema": "^0.4.0"
@@ -273,30 +292,40 @@
}
},
"node_modules/@ai-sdk/provider-utils": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz",
- "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==",
+ "version": "4.0.57",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.57.tgz",
+ "integrity": "sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "3.0.8",
+ "@ai-sdk/provider": "3.0.18",
"@standard-schema/spec": "^1.1.0",
- "eventsource-parser": "^3.0.6"
+ "eventsource-parser": "^3.0.8",
+ "undici": "^6.28.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=18.17"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
+ "node_modules/@ai-sdk/provider-utils/node_modules/undici": {
+ "version": "6.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz",
+ "integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
"node_modules/@ai-sdk/react": {
- "version": "3.0.102",
- "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-3.0.102.tgz",
- "integrity": "sha512-WPSYJxk/HM3SWhhxE+WrLhIMbaRLpDHWWYzUlrptXowwEoyliYBYZAkzip/gV8hybT2NyrWnsUh5KRO5iBdsQA==",
+ "version": "3.0.303",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-3.0.303.tgz",
+ "integrity": "sha512-PeTBn25x8QxfIIpdUDOpWHfiIZxMRJCw04PhWNHI9hl+gtu+zGwAxWU+sy3PH/f/oEBBAp7eDDUDDjiuqwcw6g==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider-utils": "4.0.15",
- "ai": "6.0.100",
+ "@ai-sdk/provider-utils": "4.0.57",
+ "ai": "6.0.300",
"swr": "^2.2.5",
"throttleit": "2.1.0"
},
@@ -307,6 +336,26 @@
"react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1"
}
},
+ "node_modules/@aihubmix/ai-sdk-provider": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@aihubmix/ai-sdk-provider/-/ai-sdk-provider-2.2.1.tgz",
+ "integrity": "sha512-VUkSYFbrigs5ZF44LRSHEI/q1fZyLkExWvrgBG4w0ysGEE/XXt7fi9NgMG9UH1PQVBgnc/PP03CUhTM1nhmGxQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/anthropic": "^3.0.0",
+ "@ai-sdk/google": "^3.0.0",
+ "@ai-sdk/openai": "^3.0.0",
+ "@ai-sdk/openai-compatible": "^2.0.37",
+ "@ai-sdk/provider": "^3.0.0",
+ "@ai-sdk/provider-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
@@ -837,103 +886,21 @@
"node": ">=20.0.0"
}
},
- "node_modules/@aws-sdk/client-cognito-identity": {
- "version": "3.1021.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1021.0.tgz",
- "integrity": "sha512-J3sT35ekSK7xdm7yhmc4XrMIuSZgd+kIEzSRVAHkmeS3JgOl0jPGc+p0mjXy5V8jR7COb46uvsvKBTImk31QOA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/credential-provider-node": "^3.972.29",
- "@aws-sdk/middleware-host-header": "^3.972.8",
- "@aws-sdk/middleware-logger": "^3.972.8",
- "@aws-sdk/middleware-recursion-detection": "^3.972.9",
- "@aws-sdk/middleware-user-agent": "^3.972.28",
- "@aws-sdk/region-config-resolver": "^3.972.10",
- "@aws-sdk/types": "^3.973.6",
- "@aws-sdk/util-endpoints": "^3.996.5",
- "@aws-sdk/util-user-agent-browser": "^3.972.8",
- "@aws-sdk/util-user-agent-node": "^3.973.14",
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/core": "^3.23.13",
- "@smithy/fetch-http-handler": "^5.3.15",
- "@smithy/hash-node": "^4.2.12",
- "@smithy/invalid-dependency": "^4.2.12",
- "@smithy/middleware-content-length": "^4.2.12",
- "@smithy/middleware-endpoint": "^4.4.28",
- "@smithy/middleware-retry": "^4.4.46",
- "@smithy/middleware-serde": "^4.2.16",
- "@smithy/middleware-stack": "^4.2.12",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/node-http-handler": "^4.5.1",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-base64": "^4.3.2",
- "@smithy/util-body-length-browser": "^4.2.2",
- "@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.44",
- "@smithy/util-defaults-mode-node": "^4.2.48",
- "@smithy/util-endpoints": "^3.3.3",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-retry": "^4.2.13",
- "@smithy/util-utf8": "^4.2.2",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"node_modules/@aws-sdk/client-dynamodb": {
- "version": "3.1021.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1021.0.tgz",
- "integrity": "sha512-Yp7p5HZh4ZAOqV7kbOP8ClPGJxFUTm+FRYLQAsA3x22rB3Q+rgcr+avBNxjS2AX7NeRCI6LY4zoeVYvILWEq3Q==",
+ "version": "3.1145.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1145.0.tgz",
+ "integrity": "sha512-T5yELCRj9w2wPKPX0gGLY4tC4dhZq79LDyiqTfYUpAc9IjtR7vVTHQ6Epe8w5TnIHJVOOxUZvz77cXOWm3/IpA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/credential-provider-node": "^3.972.29",
- "@aws-sdk/dynamodb-codec": "^3.972.27",
- "@aws-sdk/middleware-endpoint-discovery": "^3.972.9",
- "@aws-sdk/middleware-host-header": "^3.972.8",
- "@aws-sdk/middleware-logger": "^3.972.8",
- "@aws-sdk/middleware-recursion-detection": "^3.972.9",
- "@aws-sdk/middleware-user-agent": "^3.972.28",
- "@aws-sdk/region-config-resolver": "^3.972.10",
- "@aws-sdk/types": "^3.973.6",
- "@aws-sdk/util-endpoints": "^3.996.5",
- "@aws-sdk/util-user-agent-browser": "^3.972.8",
- "@aws-sdk/util-user-agent-node": "^3.973.14",
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/core": "^3.23.13",
- "@smithy/fetch-http-handler": "^5.3.15",
- "@smithy/hash-node": "^4.2.12",
- "@smithy/invalid-dependency": "^4.2.12",
- "@smithy/middleware-content-length": "^4.2.12",
- "@smithy/middleware-endpoint": "^4.4.28",
- "@smithy/middleware-retry": "^4.4.46",
- "@smithy/middleware-serde": "^4.2.16",
- "@smithy/middleware-stack": "^4.2.12",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/node-http-handler": "^4.5.1",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-base64": "^4.3.2",
- "@smithy/util-body-length-browser": "^4.2.2",
- "@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.44",
- "@smithy/util-defaults-mode-node": "^4.2.48",
- "@smithy/util-endpoints": "^3.3.3",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-retry": "^4.2.13",
- "@smithy/util-utf8": "^4.2.2",
- "@smithy/util-waiter": "^4.2.14",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/credential-provider-node": "^3.972.84",
+ "@aws-sdk/dynamodb-codec": "^3.973.46",
+ "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/fetch-http-handler": "^5.8.0",
+ "@smithy/node-http-handler": "^4.12.1",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1162,29 +1129,33 @@
}
},
"node_modules/@aws-sdk/core": {
- "version": "3.973.26",
- "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.26.tgz",
- "integrity": "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==",
+ "version": "3.978.1",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.1.tgz",
+ "integrity": "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@aws-sdk/xml-builder": "^3.972.16",
- "@smithy/core": "^3.23.13",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/signature-v4": "^5.3.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/util-base64": "^4.3.2",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-utf8": "^4.2.2",
+ "@aws-sdk/types": "^3.974.6",
+ "@aws-sdk/xml-builder": "^3.972.41",
+ "@aws/lambda-invoke-store": "^0.3.0",
+ "@smithy/core": "^3.35.0",
+ "@smithy/signature-v4": "^5.7.3",
+ "@smithy/types": "^4.19.0",
+ "bowser": "^2.11.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
+ "node_modules/@aws-sdk/core/node_modules/@aws/lambda-invoke-store": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz",
+ "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@aws-sdk/crc64-nvme": {
"version": "3.972.1",
"resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.1.tgz",
@@ -1199,15 +1170,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-cognito-identity": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.21.tgz",
- "integrity": "sha512-3ooy5gLnMLgWtkxz53P9R0RiSSCCHn576kyfy/L88QXOqS/G4wYTsqoNJBGZ0Kg46FlQ9jZHuZThbyeEeXgW/g==",
+ "version": "3.972.71",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.71.tgz",
+ "integrity": "sha512-rsxsi8tF3vMGdWdaHQNXceHBdb49442yR2Pe08K+Phoc/yICbptdsHwL25Wf0z5+hjhSS5a+hDJHP7Xul4zvHQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1215,15 +1186,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-env": {
- "version": "3.972.24",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.24.tgz",
- "integrity": "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==",
+ "version": "3.972.72",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.72.tgz",
+ "integrity": "sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1231,20 +1202,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-http": {
- "version": "3.972.26",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.26.tgz",
- "integrity": "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==",
+ "version": "3.972.74",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.74.tgz",
+ "integrity": "sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/fetch-http-handler": "^5.3.15",
- "@smithy/node-http-handler": "^4.5.1",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/util-stream": "^4.5.21",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/fetch-http-handler": "^5.8.0",
+ "@smithy/node-http-handler": "^4.12.1",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1252,24 +1220,23 @@
}
},
"node_modules/@aws-sdk/credential-provider-ini": {
- "version": "3.972.28",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.28.tgz",
- "integrity": "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==",
+ "version": "3.973.17",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.17.tgz",
+ "integrity": "sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/credential-provider-env": "^3.972.24",
- "@aws-sdk/credential-provider-http": "^3.972.26",
- "@aws-sdk/credential-provider-login": "^3.972.28",
- "@aws-sdk/credential-provider-process": "^3.972.24",
- "@aws-sdk/credential-provider-sso": "^3.972.28",
- "@aws-sdk/credential-provider-web-identity": "^3.972.28",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/credential-provider-imds": "^4.2.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/credential-provider-env": "^3.972.72",
+ "@aws-sdk/credential-provider-http": "^3.972.74",
+ "@aws-sdk/credential-provider-login": "^3.972.79",
+ "@aws-sdk/credential-provider-process": "^3.972.72",
+ "@aws-sdk/credential-provider-sso": "^3.973.16",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.78",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/credential-provider-imds": "^4.5.2",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1277,18 +1244,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-login": {
- "version": "3.972.28",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.28.tgz",
- "integrity": "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==",
+ "version": "3.972.79",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.79.tgz",
+ "integrity": "sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1296,22 +1261,21 @@
}
},
"node_modules/@aws-sdk/credential-provider-node": {
- "version": "3.972.29",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.29.tgz",
- "integrity": "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==",
+ "version": "3.972.84",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.84.tgz",
+ "integrity": "sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/credential-provider-env": "^3.972.24",
- "@aws-sdk/credential-provider-http": "^3.972.26",
- "@aws-sdk/credential-provider-ini": "^3.972.28",
- "@aws-sdk/credential-provider-process": "^3.972.24",
- "@aws-sdk/credential-provider-sso": "^3.972.28",
- "@aws-sdk/credential-provider-web-identity": "^3.972.28",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/credential-provider-imds": "^4.2.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/credential-provider-env": "^3.972.72",
+ "@aws-sdk/credential-provider-http": "^3.972.74",
+ "@aws-sdk/credential-provider-ini": "^3.973.17",
+ "@aws-sdk/credential-provider-process": "^3.972.72",
+ "@aws-sdk/credential-provider-sso": "^3.973.16",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.78",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/credential-provider-imds": "^4.5.2",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1319,16 +1283,15 @@
}
},
"node_modules/@aws-sdk/credential-provider-process": {
- "version": "3.972.24",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.24.tgz",
- "integrity": "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==",
+ "version": "3.972.72",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.72.tgz",
+ "integrity": "sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1336,18 +1299,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso": {
- "version": "3.972.28",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.28.tgz",
- "integrity": "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==",
+ "version": "3.973.16",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.16.tgz",
+ "integrity": "sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/token-providers": "3.1021.0",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/token-providers": "3.1138.0",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1355,17 +1317,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-web-identity": {
- "version": "3.972.28",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.28.tgz",
- "integrity": "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==",
+ "version": "3.972.78",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.78.tgz",
+ "integrity": "sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1373,30 +1334,26 @@
}
},
"node_modules/@aws-sdk/credential-providers": {
- "version": "3.1021.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1021.0.tgz",
- "integrity": "sha512-paB93zLnBGEVgKhb3dRqfY6m5iNsTprm7fPvbTxZYGElqZTlbV3Ei3mQHuNA80mHrJ18lRtN6Yzinl++u6754w==",
+ "version": "3.1145.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1145.0.tgz",
+ "integrity": "sha512-cCChnhRZC3a6AQTw3fiqMPUREQaLKDHYKpDz0K6Gv89VsrS7SwlkMqryi6HOSCWx4bpOQsqbYkeqaxDLwUlgKQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/client-cognito-identity": "3.1021.0",
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/credential-provider-cognito-identity": "^3.972.21",
- "@aws-sdk/credential-provider-env": "^3.972.24",
- "@aws-sdk/credential-provider-http": "^3.972.26",
- "@aws-sdk/credential-provider-ini": "^3.972.28",
- "@aws-sdk/credential-provider-login": "^3.972.28",
- "@aws-sdk/credential-provider-node": "^3.972.29",
- "@aws-sdk/credential-provider-process": "^3.972.24",
- "@aws-sdk/credential-provider-sso": "^3.972.28",
- "@aws-sdk/credential-provider-web-identity": "^3.972.28",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/core": "^3.23.13",
- "@smithy/credential-provider-imds": "^4.2.12",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/credential-provider-cognito-identity": "^3.972.71",
+ "@aws-sdk/credential-provider-env": "^3.972.72",
+ "@aws-sdk/credential-provider-http": "^3.972.74",
+ "@aws-sdk/credential-provider-ini": "^3.973.17",
+ "@aws-sdk/credential-provider-login": "^3.972.79",
+ "@aws-sdk/credential-provider-node": "^3.972.84",
+ "@aws-sdk/credential-provider-process": "^3.972.72",
+ "@aws-sdk/credential-provider-sso": "^3.973.16",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.78",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/credential-provider-imds": "^4.5.2",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1404,16 +1361,14 @@
}
},
"node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.972.27",
- "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.972.27.tgz",
- "integrity": "sha512-S7IWE0K+aqbvjP8PHnOyDJK1fzrazAismH5XutJtS3YBvRvmfLb8Ac7Z1ZC4LBWvO8Gx1t/szFe46K51FqZn/A==",
+ "version": "3.973.46",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.46.tgz",
+ "integrity": "sha512-ApnSubn+5C6HQGP7025q9w/YiNzKwimIMksP76xVij779BCCqcmJ9poB6hQ4syY9NUxqmEQLBqY0PktX53L8iQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@smithy/core": "^3.23.13",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/util-base64": "^4.3.2",
+ "@aws-sdk/core": "^3.978.1",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1421,9 +1376,9 @@
}
},
"node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.5",
- "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.5.tgz",
- "integrity": "sha512-itVdge0NozgtgmtbZ25FVwWU3vGlE7x7feE/aOEJNkQfEpbkrF8Rj1QmnK+2blFfYE1xWt/iU+6/jUp/pv1+MA==",
+ "version": "3.972.11",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.11.tgz",
+ "integrity": "sha512-8q1ICxcDjHId3bBryuu/j+1L9y5/3uQnwzLDt5j2ElcjZSoWmFtymdJy7OjLrluSMe0Z4mq5bcH4fxBXvlEHfw==",
"license": "Apache-2.0",
"dependencies": {
"mnemonist": "0.38.3",
@@ -1452,16 +1407,15 @@
}
},
"node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.9.tgz",
- "integrity": "sha512-1503Y5Xk14SdXY0ucXwc08CY+aVuoY1tmQxsR/apwAVAwcLT7FFzqjYJYLq8JOkKJyzIB8M6J27e1ZcagGK+Fg==",
+ "version": "3.972.31",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.31.tgz",
+ "integrity": "sha512-JqOIXupzP6cpAmCCmuEaacbXo/bVCcg1NkglCJC6q4wDAvJO09qIs7POf50JNzmhOhWPtfXvzgWxb5nMQl8QiA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.5",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/endpoint-cache": "^3.972.11",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1509,14 +1463,14 @@
}
},
"node_modules/@aws-sdk/middleware-host-header": {
- "version": "3.972.8",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz",
- "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.10.tgz",
+ "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1538,13 +1492,13 @@
}
},
"node_modules/@aws-sdk/middleware-logger": {
- "version": "3.972.8",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz",
- "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.10.tgz",
+ "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1552,15 +1506,15 @@
}
},
"node_modules/@aws-sdk/middleware-recursion-detection": {
- "version": "3.972.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.9.tgz",
- "integrity": "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==",
+ "version": "3.972.11",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.11.tgz",
+ "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/types": "^3.973.8",
"@aws/lambda-invoke-store": "^0.2.2",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1568,24 +1522,24 @@
}
},
"node_modules/@aws-sdk/middleware-sdk-s3": {
- "version": "3.972.13",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.13.tgz",
- "integrity": "sha512-rGBz1n6PFxg1+5mnN1/IczesPwx0W39DZt2JPjqPiZAZ7LAqH8FS4AsawSNZqr+UFJfqtTXYpeLQnMfbMAgHhg==",
+ "version": "3.972.36",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.36.tgz",
+ "integrity": "sha512-YhPix+0x/MdQrb1Ug1GDKeS5fqylIy+naz800asX8II4jqfTk2KY2KhmmYCwZcky8YWtRQQwWCGdoqeAnip8Uw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.13",
- "@aws-sdk/types": "^3.973.2",
- "@aws-sdk/util-arn-parser": "^3.972.2",
- "@smithy/core": "^3.23.4",
- "@smithy/node-config-provider": "^4.3.9",
- "@smithy/protocol-http": "^5.3.9",
- "@smithy/signature-v4": "^5.3.9",
- "@smithy/smithy-client": "^4.11.7",
- "@smithy/types": "^4.12.1",
- "@smithy/util-config-provider": "^4.2.1",
- "@smithy/util-middleware": "^4.2.9",
- "@smithy/util-stream": "^4.5.14",
- "@smithy/util-utf8": "^4.2.1",
+ "@aws-sdk/core": "^3.974.7",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-arn-parser": "^3.972.3",
+ "@smithy/core": "^3.23.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/signature-v4": "^5.3.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-config-provider": "^4.2.2",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-stream": "^4.5.25",
+ "@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -1624,18 +1578,18 @@
}
},
"node_modules/@aws-sdk/middleware-user-agent": {
- "version": "3.972.28",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.28.tgz",
- "integrity": "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==",
+ "version": "3.972.37",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.37.tgz",
+ "integrity": "sha512-N1oNpdiLoVAWYD3WFBnUi3LlfoDA06ZHo4ozyjbsJNLvILzvt//0CnR8N+CZ0NWeYgVB/5V59ivixHCWCx2ALw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/types": "^3.973.6",
- "@aws-sdk/util-endpoints": "^3.996.5",
- "@smithy/core": "^3.23.13",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
- "@smithy/util-retry": "^4.2.13",
+ "@aws-sdk/core": "^3.974.7",
+ "@aws-sdk/types": "^3.973.8",
+ "@aws-sdk/util-endpoints": "^3.996.8",
+ "@smithy/core": "^3.23.17",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-retry": "^4.3.6",
"tslib": "^2.6.2"
},
"engines": {
@@ -1643,48 +1597,33 @@
}
},
"node_modules/@aws-sdk/nested-clients": {
- "version": "3.996.18",
- "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.18.tgz",
- "integrity": "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==",
+ "version": "3.997.46",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.46.tgz",
+ "integrity": "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/middleware-host-header": "^3.972.8",
- "@aws-sdk/middleware-logger": "^3.972.8",
- "@aws-sdk/middleware-recursion-detection": "^3.972.9",
- "@aws-sdk/middleware-user-agent": "^3.972.28",
- "@aws-sdk/region-config-resolver": "^3.972.10",
- "@aws-sdk/types": "^3.973.6",
- "@aws-sdk/util-endpoints": "^3.996.5",
- "@aws-sdk/util-user-agent-browser": "^3.972.8",
- "@aws-sdk/util-user-agent-node": "^3.973.14",
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/core": "^3.23.13",
- "@smithy/fetch-http-handler": "^5.3.15",
- "@smithy/hash-node": "^4.2.12",
- "@smithy/invalid-dependency": "^4.2.12",
- "@smithy/middleware-content-length": "^4.2.12",
- "@smithy/middleware-endpoint": "^4.4.28",
- "@smithy/middleware-retry": "^4.4.46",
- "@smithy/middleware-serde": "^4.2.16",
- "@smithy/middleware-stack": "^4.2.12",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/node-http-handler": "^4.5.1",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-base64": "^4.3.2",
- "@smithy/util-body-length-browser": "^4.2.2",
- "@smithy/util-body-length-node": "^4.2.3",
- "@smithy/util-defaults-mode-browser": "^4.3.44",
- "@smithy/util-defaults-mode-node": "^4.2.48",
- "@smithy/util-endpoints": "^3.3.3",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-retry": "^4.2.13",
- "@smithy/util-utf8": "^4.2.2",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/signature-v4-multi-region": "^3.996.47",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/fetch-http-handler": "^5.8.0",
+ "@smithy/node-http-handler": "^4.12.1",
+ "@smithy/types": "^4.19.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.996.47",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.47.tgz",
+ "integrity": "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/signature-v4": "^5.7.3",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1692,15 +1631,15 @@
}
},
"node_modules/@aws-sdk/region-config-resolver": {
- "version": "3.972.10",
- "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.10.tgz",
- "integrity": "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==",
+ "version": "3.972.13",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz",
+ "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1725,17 +1664,16 @@
}
},
"node_modules/@aws-sdk/token-providers": {
- "version": "3.1021.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1021.0.tgz",
- "integrity": "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==",
+ "version": "3.1138.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1138.0.tgz",
+ "integrity": "sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.26",
- "@aws-sdk/nested-clients": "^3.996.18",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/nested-clients": "^3.997.46",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1743,12 +1681,12 @@
}
},
"node_modules/@aws-sdk/types": {
- "version": "3.973.6",
- "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz",
- "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==",
+ "version": "3.974.6",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz",
+ "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -1756,9 +1694,9 @@
}
},
"node_modules/@aws-sdk/util-arn-parser": {
- "version": "3.972.2",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.2.tgz",
- "integrity": "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==",
+ "version": "3.972.3",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz",
+ "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
@@ -1768,15 +1706,15 @@
}
},
"node_modules/@aws-sdk/util-endpoints": {
- "version": "3.996.5",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz",
- "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==",
+ "version": "3.996.8",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz",
+ "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-endpoints": "^3.3.3",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
+ "@smithy/util-endpoints": "^3.4.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -1796,27 +1734,27 @@
}
},
"node_modules/@aws-sdk/util-user-agent-browser": {
- "version": "3.972.8",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz",
- "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.10.tgz",
+ "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/types": "^3.973.6",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/types": "^4.14.1",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
}
},
"node_modules/@aws-sdk/util-user-agent-node": {
- "version": "3.973.14",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.14.tgz",
- "integrity": "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==",
+ "version": "3.973.23",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.23.tgz",
+ "integrity": "sha512-gGwq8L2Euw0aNG6Ey4EktiAo3fSCVoDy1CaBIthd+oeaKHPXUrNaApMewQ6La5Hv0lcznOtECZaNvYyc5LXXfA==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/middleware-user-agent": "^3.972.28",
- "@aws-sdk/types": "^3.973.6",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/types": "^4.13.1",
+ "@aws-sdk/middleware-user-agent": "^3.972.37",
+ "@aws-sdk/types": "^3.973.8",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-config-provider": "^4.2.2",
"tslib": "^2.6.2"
},
@@ -1833,13 +1771,12 @@
}
},
"node_modules/@aws-sdk/xml-builder": {
- "version": "3.972.16",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.16.tgz",
- "integrity": "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==",
+ "version": "3.972.41",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz",
+ "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
- "fast-xml-parser": "5.5.8",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -2158,9 +2095,9 @@
}
},
"node_modules/@biomejs/biome": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.10.tgz",
- "integrity": "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.7.tgz",
+ "integrity": "sha512-zr8K/DcY5tYsQOQwqMJ0AWElo6QgmgNI7idXgXLhevVszlt8RGVpesEJPqx3ThazLaOwjJ5Y8fz3BtH5fGZNsw==",
"dev": true,
"license": "MIT OR Apache-2.0",
"bin": {
@@ -2174,20 +2111,20 @@
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
- "@biomejs/cli-darwin-arm64": "2.4.10",
- "@biomejs/cli-darwin-x64": "2.4.10",
- "@biomejs/cli-linux-arm64": "2.4.10",
- "@biomejs/cli-linux-arm64-musl": "2.4.10",
- "@biomejs/cli-linux-x64": "2.4.10",
- "@biomejs/cli-linux-x64-musl": "2.4.10",
- "@biomejs/cli-win32-arm64": "2.4.10",
- "@biomejs/cli-win32-x64": "2.4.10"
+ "@biomejs/cli-darwin-arm64": "2.5.7",
+ "@biomejs/cli-darwin-x64": "2.5.7",
+ "@biomejs/cli-linux-arm64": "2.5.7",
+ "@biomejs/cli-linux-arm64-musl": "2.5.7",
+ "@biomejs/cli-linux-x64": "2.5.7",
+ "@biomejs/cli-linux-x64-musl": "2.5.7",
+ "@biomejs/cli-win32-arm64": "2.5.7",
+ "@biomejs/cli-win32-x64": "2.5.7"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.10.tgz",
- "integrity": "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.7.tgz",
+ "integrity": "sha512-vxo/Ls3/PYdQWyLhYYcgMOCzQypAjcY+iihS8M0wW03l16TCLW4zqZzGo75gm1VdCMj38hTVZ31KBWrZ4G9dJw==",
"cpu": [
"arm64"
],
@@ -2202,9 +2139,9 @@
}
},
"node_modules/@biomejs/cli-darwin-x64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.10.tgz",
- "integrity": "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.7.tgz",
+ "integrity": "sha512-Cd3Ga61amT/Yl/0x8elP5hhGYaFy4bw6WuysTgf7oo8TA5tJ5A1k+DkVoJ2BHbTVil51gTX9VPzArnrlLJ3Kyg==",
"cpu": [
"x64"
],
@@ -2219,9 +2156,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.10.tgz",
- "integrity": "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.7.tgz",
+ "integrity": "sha512-rR2QE0yF2GYSuYuKIa7pKvODGJqnOH+2eDREAM8wV+mWKSkMQKdAp4zXEZfTaxY8PMoNONnpgSWcBCyLDPDOKg==",
"cpu": [
"arm64"
],
@@ -2239,9 +2176,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.10.tgz",
- "integrity": "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.7.tgz",
+ "integrity": "sha512-xPI5yB6XlpDbNkS+bm1t42olw5c4l3UrlOmLg7KtLJvjvkNF/1V4tnUgfkylGIeb3u/T+BzMGYqgQhzjAoJzuQ==",
"cpu": [
"arm64"
],
@@ -2259,9 +2196,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.10.tgz",
- "integrity": "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.7.tgz",
+ "integrity": "sha512-FQgqJhscrqJUFptGaRSUJWlXAExwWcDwLuK49dvKfkQ1bB5SEEyFssnsxQY83Xm6jR0EbbX3+8+D5bfvYqUG2Q==",
"cpu": [
"x64"
],
@@ -2279,9 +2216,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.10.tgz",
- "integrity": "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.7.tgz",
+ "integrity": "sha512-rE5VZi+qtmPgQH+l7jVxYoZ18b/TiHEhulhMpjmCZH1PltSbjRcxNWywC3HZ9tYottG7ORkeTtoscBilKSBm0g==",
"cpu": [
"x64"
],
@@ -2299,9 +2236,9 @@
}
},
"node_modules/@biomejs/cli-win32-arm64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.10.tgz",
- "integrity": "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.7.tgz",
+ "integrity": "sha512-Oq4x0CCwP4jirrcTywXs5kOGZ4v5vuEP+gWrbtjApOA2CL9F3F9GlIdQIci8AKSCa/zURanMRpX/4wQ7Am6hHg==",
"cpu": [
"arm64"
],
@@ -2316,9 +2253,9 @@
}
},
"node_modules/@biomejs/cli-win32-x64": {
- "version": "2.4.10",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.10.tgz",
- "integrity": "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==",
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.7.tgz",
+ "integrity": "sha512-V+0wu/nrj2S+MhP4EQ0uHNolP0IALEsz45pg0WoKkHfDeh0+ItHwP/p7bX5RPoMOl9NkpHYWdYPhIcy2mACHvQ==",
"cpu": [
"x64"
],
@@ -2333,18 +2270,33 @@
}
},
"node_modules/@cloudflare/kv-asset-handler": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
- "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz",
+ "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==",
"license": "MIT OR Apache-2.0",
"engines": {
- "node": ">=18.0.0"
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@cloudflare/unenv-preset": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.2.tgz",
+ "integrity": "sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ==",
+ "license": "MIT OR Apache-2.0",
+ "peerDependencies": {
+ "unenv": "2.0.0-rc.24",
+ "workerd": ">1.20260305.0 <2.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "workerd": {
+ "optional": true
+ }
}
},
"node_modules/@cloudflare/workerd-darwin-64": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260329.1.tgz",
- "integrity": "sha512-oyDXYlPBuGXKkZ85+M3jFz0/qYmvA4AEURN8USIGPDCR5q+HFSRwywSd9neTx3Wi7jhey2wuYaEpD3fEFWyWUA==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20261001.1.tgz",
+ "integrity": "sha512-4cgSgDf28JSw/P5Dj5GCS59hzVqS5XnmGAWNkvYHLI6ODU9idGaMMNEuhJXDkEG/lsABmlVlnCgsdh2VbKWepw==",
"cpu": [
"x64"
],
@@ -2358,9 +2310,9 @@
}
},
"node_modules/@cloudflare/workerd-darwin-arm64": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260329.1.tgz",
- "integrity": "sha512-++ZxVa3ovzYeDLEG6zMqql9gzZAG8vak6ZSBQgprGKZp7akr+GKTpw9f3RrMP552NSi3gTisroLobrrkPBtYLQ==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20261001.1.tgz",
+ "integrity": "sha512-8ulAWruEVouNmEIsQsy9WSSCS9zkLu93W2MTwp5esiFyoPp05BNSVFIFsYSPi1pkFmBBd7fsnwMpbLkaJLaVPQ==",
"cpu": [
"arm64"
],
@@ -2374,9 +2326,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-64": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260329.1.tgz",
- "integrity": "sha512-kkeywAgIHwbqHkVILqbj/YkfbrA6ARbmutjiYzZA2MwMSfNXlw6/kedAKOY8YwcymZIgepx3YTIPnBP50pOotw==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20261001.1.tgz",
+ "integrity": "sha512-kZbTZJGrhsMOdqZ2BIybjaBRLZjYsRLWj7mcNw/6Y3hodOhp5MrNzcz4iWGwNVWwyIV+7Pl+/LX5VcgnRqdHOg==",
"cpu": [
"x64"
],
@@ -2390,9 +2342,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-arm64": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260329.1.tgz",
- "integrity": "sha512-eYBN20+B7XOUSWEe0mlqkMUbfLoIKjKZnpqQiSxnLbL72JKY0D/KlfN/b7RVGLpewB7i8rTrwTNr0szCKnZzSQ==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20261001.1.tgz",
+ "integrity": "sha512-oOk3Zj6k/8oP0FJgZBWDn7+BqbsqIMEMaV95pulHPVbwVu4wYoLfQq2hp0vkbCNsCFLqZb7cqixXdrwD54ZIow==",
"cpu": [
"arm64"
],
@@ -2406,9 +2358,9 @@
}
},
"node_modules/@cloudflare/workerd-windows-64": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260329.1.tgz",
- "integrity": "sha512-5R+/oxrDhS9nL3oA3ZWtD6ndMOqm7RfKknDNxLcmYW5DkUu7UH3J/s1t/Dz66iFePzr5BJmE7/8gbmve6TjtZQ==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20261001.1.tgz",
+ "integrity": "sha512-uRxm5W4VyBkoSaoP1BfOuH0873tE+vxsknF4sab6/5YIRvIAufChq3QDp+zlAn67PuGPGcn7W8QraA0t4ucmOQ==",
"cpu": [
"x64"
],
@@ -3232,9 +3184,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
- "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
+ "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -3260,9 +3212,9 @@
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
- "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
@@ -3277,9 +3229,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
- "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
@@ -3294,9 +3246,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
- "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
@@ -3311,9 +3263,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
- "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
@@ -3328,9 +3280,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
- "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
@@ -3345,9 +3297,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
- "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
@@ -3362,9 +3314,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
- "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
@@ -3379,9 +3331,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
- "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
@@ -3396,9 +3348,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
- "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
@@ -3413,9 +3365,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
- "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
@@ -3430,9 +3382,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
- "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
@@ -3447,9 +3399,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
- "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
@@ -3464,9 +3416,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
- "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
@@ -3481,9 +3433,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
- "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
@@ -3498,9 +3450,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
- "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
@@ -3515,9 +3467,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
- "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
@@ -3532,9 +3484,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
- "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
@@ -3549,9 +3501,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
- "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
@@ -3566,9 +3518,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
- "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
@@ -3583,9 +3535,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
- "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
@@ -3600,9 +3552,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
- "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
@@ -3617,9 +3569,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
- "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
@@ -3634,9 +3586,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
- "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
@@ -3651,9 +3603,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
- "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
@@ -3668,9 +3620,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
- "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
@@ -3685,9 +3637,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
- "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
@@ -3816,9 +3768,9 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
- "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3828,7 +3780,7 @@
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
- "js-yaml": "^4.1.1",
+ "js-yaml": "^4.3.0",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
@@ -3871,9 +3823,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.39.4",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
- "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3926,16 +3878,15 @@
}
},
"node_modules/@extractus/article-extractor": {
- "version": "8.0.20",
- "resolved": "https://registry.npmjs.org/@extractus/article-extractor/-/article-extractor-8.0.20.tgz",
- "integrity": "sha512-oxHLZ3X5ctLVkQfFkOLf8afvQq6aJ2VBxwQhAaV6ZypaaMJboFz8uwpCGy7QBehmQIvzgWhCwuu8j4ayJFvPcg==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@extractus/article-extractor/-/article-extractor-8.1.0.tgz",
+ "integrity": "sha512-FLA07C3YFFyisrob3UW3kyd88M2BW3OKLQpWFMDiH0abzAR9L5CL2B+HuB+rzo3s/gu7sdRW++82qZBmdOowMg==",
"license": "MIT",
"dependencies": {
"@mozilla/readability": "^0.6.0",
- "@ndaidong/bellajs": "^12.0.1",
- "cross-fetch": "^4.1.0",
+ "@pwshub/bellajs": "^13.0.2",
"linkedom": "^0.18.12",
- "sanitize-html": "2.17.0"
+ "sanitize-html": "^2.17.3"
},
"engines": {
"node": ">= 20"
@@ -3980,18 +3931,18 @@
"license": "MIT"
},
"node_modules/@formatjs/fast-memoize": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.1.tgz",
- "integrity": "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz",
+ "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==",
"license": "MIT"
},
"node_modules/@formatjs/intl-localematcher": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.2.tgz",
- "integrity": "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.9.0.tgz",
+ "integrity": "sha512-Z7TD3ssNry1Av1dVDoq/mzpxGyxqWwU0ClWEo+BB3k0JQTeQvUGmeK9WZGYvlqStS0UqkaKXk3dfjsb6NaHAxw==",
"license": "MIT",
"dependencies": {
- "@formatjs/fast-memoize": "3.1.1"
+ "@formatjs/fast-memoize": "3.1.7"
}
},
"node_modules/@hapi/address": {
@@ -4101,9 +4052,9 @@
}
},
"node_modules/@img/colour": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
- "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -4153,6 +4104,41 @@
"@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
+ "node_modules/@img/sharp-freebsd-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
+ "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-freebsd-wasm32/node_modules/@img/sharp-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
+ "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.3"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
@@ -4508,6 +4494,41 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-webcontainers-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
+ "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-webcontainers-wasm32/node_modules/@img/sharp-wasm32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
+ "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.3"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-win32-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
@@ -4805,16 +4826,10 @@
"@tybys/wasm-util": "^0.10.0"
}
},
- "node_modules/@ndaidong/bellajs": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/@ndaidong/bellajs/-/bellajs-12.0.1.tgz",
- "integrity": "sha512-1iY42uiHz0cxNMbde7O3zVN+ZX1viOOUOBRt6ht6lkRZbSjwOnFV34Zv4URp3hGzEe6L9Byk7BOq/41H0PzAOQ==",
- "license": "MIT"
- },
"node_modules/@next/env": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
- "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz",
+ "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@@ -4828,9 +4843,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
- "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz",
+ "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==",
"cpu": [
"arm64"
],
@@ -4844,9 +4859,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
- "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz",
+ "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==",
"cpu": [
"x64"
],
@@ -4860,9 +4875,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
- "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz",
+ "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==",
"cpu": [
"arm64"
],
@@ -4876,9 +4891,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
- "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz",
+ "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==",
"cpu": [
"arm64"
],
@@ -4892,9 +4907,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
- "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz",
+ "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==",
"cpu": [
"x64"
],
@@ -4908,9 +4923,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
- "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz",
+ "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==",
"cpu": [
"x64"
],
@@ -4924,9 +4939,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
- "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz",
+ "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==",
"cpu": [
"arm64"
],
@@ -4940,9 +4955,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
- "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz",
+ "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==",
"cpu": [
"x64"
],
@@ -5798,27 +5813,15 @@
}
},
"node_modules/@openrouter/ai-sdk-provider": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-1.5.4.tgz",
- "integrity": "sha512-xrSQPUIH8n9zuyYZR0XK7Ba0h2KsjJcMkxnwaYfmv13pKs3sDkjPzVPPhlhzqBGddHb5cFEwJ9VFuFeDcxCDSw==",
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-2.10.0.tgz",
+ "integrity": "sha512-FMsAEjLUt5pWuRE2LDC/LCvVrFjLlrEzUITH5+5SZtfq7KZ2wrOHjQVxzz92sju8S9ltpzW87CLW8/b0oBXVCw==",
"license": "Apache-2.0",
- "dependencies": {
- "@openrouter/sdk": "^0.1.27"
- },
"engines": {
"node": ">=18"
},
"peerDependencies": {
- "ai": "^5.0.0",
- "zod": "^3.24.1 || ^v4"
- }
- },
- "node_modules/@openrouter/sdk": {
- "version": "0.1.27",
- "resolved": "https://registry.npmjs.org/@openrouter/sdk/-/sdk-0.1.27.tgz",
- "integrity": "sha512-RH//L10bSmc81q25zAZudiI4kNkLgxF2E+WU42vghp3N6TEvZ6F0jK7uT3tOxkEn91gzmMw9YVmDENy7SJsajQ==",
- "license": "Apache-2.0",
- "dependencies": {
+ "ai": "^6.0.0",
"zod": "^3.25.0 || ^4.0.0"
}
},
@@ -5832,9 +5835,9 @@
}
},
"node_modules/@opentelemetry/api-logs": {
- "version": "0.214.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz",
- "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==",
+ "version": "0.222.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.222.0.tgz",
+ "integrity": "sha512-9mb1If+IF6u0ZVXkHQ6ogEae5HwA6ajIVUgpSDQyRASxft6BSXHvBvPooRle3yFN/fKnCdSOnuu0OC3PLcF6+g==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api": "^1.3.0"
@@ -5844,9 +5847,9 @@
}
},
"node_modules/@opentelemetry/context-async-hooks": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.1.tgz",
- "integrity": "sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.11.0.tgz",
+ "integrity": "sha512-Tr79DyWI8itsBdg+jH+opjfrwLzX+erk1/ExkIwhWoAVjVrJIn2y5+cGjTC0Vy8fyNIA/y8wuJPZwr1T3xCZeQ==",
"license": "Apache-2.0",
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -5856,9 +5859,9 @@
}
},
"node_modules/@opentelemetry/core": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz",
- "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.11.0.tgz",
+ "integrity": "sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==",
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
@@ -5871,16 +5874,14 @@
}
},
"node_modules/@opentelemetry/exporter-trace-otlp-http": {
- "version": "0.214.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.214.0.tgz",
- "integrity": "sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw==",
+ "version": "0.222.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.222.0.tgz",
+ "integrity": "sha512-RCnPWcHppwiquQ+cV3nWvNwdf0MG1w26e5jewW2T83nTZOlXgcg88sY9ulCVgagGHcq1mj0L0GP6YHgzk2v8oA==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/otlp-exporter-base": "0.214.0",
- "@opentelemetry/otlp-transformer": "0.214.0",
- "@opentelemetry/resources": "2.6.1",
- "@opentelemetry/sdk-trace-base": "2.6.1"
+ "@opentelemetry/otlp-exporter-base": "0.222.0",
+ "@opentelemetry/otlp-transformer": "0.222.0",
+ "@opentelemetry/sdk-trace": "2.11.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -5890,13 +5891,13 @@
}
},
"node_modules/@opentelemetry/otlp-exporter-base": {
- "version": "0.214.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.214.0.tgz",
- "integrity": "sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==",
+ "version": "0.222.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.222.0.tgz",
+ "integrity": "sha512-YbywG3veEm2Fb6TbdxRkuquWob6eVWXuA8/Ba1tXz9jHfUqpdE3keilOHEtPboC4CvS1bjeeVfNkWGOOrLj+lw==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/otlp-transformer": "0.214.0"
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/otlp-transformer": "0.222.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -5906,18 +5907,17 @@
}
},
"node_modules/@opentelemetry/otlp-transformer": {
- "version": "0.214.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.214.0.tgz",
- "integrity": "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==",
+ "version": "0.222.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.222.0.tgz",
+ "integrity": "sha512-/F3BZ89+CJQnZkMh2tCrtcdB+XT2Dxhj4FFE+WPQ//413hmFL0/RfEX6vgOIWGhiSzrkHWTK3+6SiT7K5/g/jQ==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/api-logs": "0.214.0",
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/resources": "2.6.1",
- "@opentelemetry/sdk-logs": "0.214.0",
- "@opentelemetry/sdk-metrics": "2.6.1",
- "@opentelemetry/sdk-trace-base": "2.6.1",
- "protobufjs": "^7.0.0"
+ "@opentelemetry/api-logs": "0.222.0",
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/resources": "2.11.0",
+ "@opentelemetry/sdk-logs": "0.222.0",
+ "@opentelemetry/sdk-metrics": "2.11.0",
+ "@opentelemetry/sdk-trace": "2.11.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -5927,12 +5927,12 @@
}
},
"node_modules/@opentelemetry/resources": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz",
- "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.11.0.tgz",
+ "integrity": "sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.1",
+ "@opentelemetry/core": "2.11.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
@@ -5943,14 +5943,14 @@
}
},
"node_modules/@opentelemetry/sdk-logs": {
- "version": "0.214.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.214.0.tgz",
- "integrity": "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==",
+ "version": "0.222.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.222.0.tgz",
+ "integrity": "sha512-+19YHODIjaUCArxleaJtuufFZVpz/xvvK+VllQqE+W8hHolxdoRwHfK/s667zezwh1hkx6FFF+oYzetYgqK+Bg==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/api-logs": "0.214.0",
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/resources": "2.6.1",
+ "@opentelemetry/api-logs": "0.222.0",
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/resources": "2.11.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
@@ -5961,13 +5961,13 @@
}
},
"node_modules/@opentelemetry/sdk-metrics": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.6.1.tgz",
- "integrity": "sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.11.0.tgz",
+ "integrity": "sha512-7GXXcObyHyDUUSG+L+kJoquty01bzm7ivE7+SSgXXJcHuPzGviptxwARmI2c+bnnxjexGQbJnyNlN8HxBP/Y7A==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/resources": "2.6.1"
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/resources": "2.11.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -5976,14 +5976,32 @@
"@opentelemetry/api": ">=1.9.0 <1.10.0"
}
},
- "node_modules/@opentelemetry/sdk-trace-base": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz",
- "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==",
+ "node_modules/@opentelemetry/sdk-trace": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.11.0.tgz",
+ "integrity": "sha512-fFnTqGm8/G73GQVnxYi7LXa1ZVYEUvgL6XI1LpvV0bPC7WQ/ZGgKxCSl8FnlZBKto9JHHEFTO6s6CUpvvtwFrA==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/resources": "2.6.1",
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/resources": "2.11.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-base": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.11.0.tgz",
+ "integrity": "sha512-H19x/TX/LZdqiYOjM7fqtSxwlplC5pgelavqbQdHbhdq0q/AI/TGkM2dfGuuynTXmJPeF2HoZVoPDu+TGoW78A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/resources": "2.11.0",
+ "@opentelemetry/sdk-trace": "2.11.0",
"@opentelemetry/semantic-conventions": "^1.29.0"
},
"engines": {
@@ -5994,14 +6012,14 @@
}
},
"node_modules/@opentelemetry/sdk-trace-node": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.6.1.tgz",
- "integrity": "sha512-Hh2i4FwHWRFhnO2Q/p6svMxy8MPsNCG0uuzUY3glqm0rwM0nQvbTO1dXSp9OqQoTKXcQzaz9q1f65fsurmOhNw==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.11.0.tgz",
+ "integrity": "sha512-CuvCMJmZxswhNLlM2LfuLOW3h3fZujA4hsG4B+Sz4dX2zvaXO8Ng74cnDHWD64gLszTlhiG3c0iNUjj4g+0/sA==",
"license": "Apache-2.0",
"dependencies": {
- "@opentelemetry/context-async-hooks": "2.6.1",
- "@opentelemetry/core": "2.6.1",
- "@opentelemetry/sdk-trace-base": "2.6.1"
+ "@opentelemetry/context-async-hooks": "2.11.0",
+ "@opentelemetry/core": "2.11.0",
+ "@opentelemetry/sdk-trace-base": "2.11.0"
},
"engines": {
"node": "^18.19.0 || >=20.6.0"
@@ -6030,19 +6048,19 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.59.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0.tgz",
- "integrity": "sha512-TOA5sTLd49rTDaZpYpvCQ9hGefHQq/OYOyCVnGqS2mjMfX+lGZv2iddIJd0I48cfxqSPttS9S3OuLKyylHcO1w==",
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz",
+ "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.59.0"
+ "playwright": "1.63.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/@poppinss/colors": {
@@ -6095,94 +6113,35 @@
"integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
"license": "MIT"
},
- "node_modules/@protobufjs/aspromise": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
- "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/codegen": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
- "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/eventemitter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
- "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/fetch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
- "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.1",
- "@protobufjs/inquire": "^1.1.0"
- }
- },
- "node_modules/@protobufjs/float": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
- "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/inquire": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
- "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/path": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
- "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/pool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
- "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/utf8": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
- "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
- "license": "BSD-3-Clause"
+ "node_modules/@pwshub/bellajs": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/@pwshub/bellajs/-/bellajs-13.1.0.tgz",
+ "integrity": "sha512-TAmuiXHyh59lWY8JiDWARObLKeteC5ClXufnC3x7oqdlidlDGK3qSTCKDnCRtygiY+C28ZLllAnagmmAPaoo7w==",
+ "license": "MIT"
},
"node_modules/@radix-ui/number": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
- "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz",
+ "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
- "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz",
+ "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==",
"license": "MIT"
},
"node_modules/@radix-ui/react-alert-dialog": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz",
- "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==",
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz",
+ "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-dialog": "1.1.15",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dialog": "1.1.23",
+ "@radix-ui/react-primitive": "2.1.10"
},
"peerDependencies": {
"@types/react": "*",
@@ -6199,31 +6158,13 @@
}
}
},
- "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-arrow": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
- "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz",
+ "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-primitive": "2.1.3"
+ "@radix-ui/react-primitive": "2.1.10"
},
"peerDependencies": {
"@types/react": "*",
@@ -6241,19 +6182,19 @@
}
},
"node_modules/@radix-ui/react-collapsible": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz",
- "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==",
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz",
+ "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-id": "1.1.1",
- "@radix-ui/react-presence": "1.1.5",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6271,15 +6212,15 @@
}
},
"node_modules/@radix-ui/react-collection": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
- "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz",
+ "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3"
},
"peerDependencies": {
"@types/react": "*",
@@ -6296,28 +6237,10 @@
}
}
},
- "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-compose-refs": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
- "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
+ "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6330,9 +6253,9 @@
}
},
"node_modules/@radix-ui/react-context": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
- "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz",
+ "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6345,25 +6268,26 @@
}
},
"node_modules/@radix-ui/react-dialog": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
- "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz",
+ "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-dismissable-layer": "1.1.11",
- "@radix-ui/react-focus-guards": "1.1.3",
- "@radix-ui/react-focus-scope": "1.1.7",
- "@radix-ui/react-id": "1.1.1",
- "@radix-ui/react-portal": "1.1.9",
- "@radix-ui/react-presence": "1.1.5",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
"aria-hidden": "^1.2.4",
- "react-remove-scroll": "^2.6.3"
+ "react-remove-scroll": "^2.7.2"
},
"peerDependencies": {
"@types/react": "*",
@@ -6380,28 +6304,10 @@
}
}
},
- "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-direction": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
- "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz",
+ "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6414,16 +6320,16 @@
}
},
"node_modules/@radix-ui/react-dismissable-layer": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
- "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz",
+ "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-callback-ref": "1.1.1",
- "@radix-ui/react-use-escape-keydown": "1.1.1"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-effect-event": "0.0.5"
},
"peerDependencies": {
"@types/react": "*",
@@ -6441,9 +6347,9 @@
}
},
"node_modules/@radix-ui/react-focus-guards": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
- "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz",
+ "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6456,14 +6362,14 @@
}
},
"node_modules/@radix-ui/react-focus-scope": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
- "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz",
+ "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-callback-ref": "1.1.1"
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6481,12 +6387,12 @@
}
},
"node_modules/@radix-ui/react-id": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
- "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz",
+ "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6499,35 +6405,12 @@
}
},
"node_modules/@radix-ui/react-label": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz",
- "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==",
+ "version": "2.1.15",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz",
+ "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-primitive": "2.1.4"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
- "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-slot": "1.2.4"
+ "@radix-ui/react-primitive": "2.1.10"
},
"peerDependencies": {
"@types/react": "*",
@@ -6545,26 +6428,26 @@
}
},
"node_modules/@radix-ui/react-popover": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
- "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
+ "version": "1.1.23",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz",
+ "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-dismissable-layer": "1.1.11",
- "@radix-ui/react-focus-guards": "1.1.3",
- "@radix-ui/react-focus-scope": "1.1.7",
- "@radix-ui/react-id": "1.1.1",
- "@radix-ui/react-popper": "1.2.8",
- "@radix-ui/react-portal": "1.1.9",
- "@radix-ui/react-presence": "1.1.5",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
"aria-hidden": "^1.2.4",
- "react-remove-scroll": "^2.6.3"
+ "react-remove-scroll": "^2.7.2"
},
"peerDependencies": {
"@types/react": "*",
@@ -6581,40 +6464,22 @@
}
}
},
- "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-popper": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
- "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz",
+ "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
- "@radix-ui/react-arrow": "1.1.7",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-callback-ref": "1.1.1",
- "@radix-ui/react-use-layout-effect": "1.1.1",
- "@radix-ui/react-use-rect": "1.1.1",
- "@radix-ui/react-use-size": "1.1.1",
- "@radix-ui/rect": "1.1.1"
+ "@radix-ui/react-arrow": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-rect": "1.1.4",
+ "@radix-ui/react-use-size": "1.1.4",
+ "@radix-ui/rect": "1.1.3"
},
"peerDependencies": {
"@types/react": "*",
@@ -6632,13 +6497,13 @@
}
},
"node_modules/@radix-ui/react-portal": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
- "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
+ "version": "1.1.17",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz",
+ "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6656,13 +6521,12 @@
}
},
"node_modules/@radix-ui/react-presence": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
- "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz",
+ "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6680,12 +6544,12 @@
}
},
"node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "version": "2.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz",
+ "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/react-slot": "1.3.3"
},
"peerDependencies": {
"@types/react": "*",
@@ -6702,39 +6566,21 @@
}
}
},
- "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-scroll-area": {
- "version": "1.2.10",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz",
- "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==",
+ "version": "1.2.18",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz",
+ "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/number": "1.1.1",
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-direction": "1.1.1",
- "@radix-ui/react-presence": "1.1.5",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-callback-ref": "1.1.1",
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6752,32 +6598,33 @@
}
},
"node_modules/@radix-ui/react-select": {
- "version": "2.2.6",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
- "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz",
+ "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/number": "1.1.1",
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-collection": "1.1.7",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-direction": "1.1.1",
- "@radix-ui/react-dismissable-layer": "1.1.11",
- "@radix-ui/react-focus-guards": "1.1.3",
- "@radix-ui/react-focus-scope": "1.1.7",
- "@radix-ui/react-id": "1.1.1",
- "@radix-ui/react-popper": "1.2.8",
- "@radix-ui/react-portal": "1.1.9",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3",
- "@radix-ui/react-use-callback-ref": "1.1.1",
- "@radix-ui/react-use-controllable-state": "1.2.2",
- "@radix-ui/react-use-layout-effect": "1.1.1",
- "@radix-ui/react-use-previous": "1.1.1",
- "@radix-ui/react-visually-hidden": "1.2.3",
+ "@radix-ui/number": "1.1.3",
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-collection": "1.1.15",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-direction": "1.1.4",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-focus-guards": "1.1.6",
+ "@radix-ui/react-focus-scope": "1.1.16",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-callback-ref": "1.1.4",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-use-previous": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11",
"aria-hidden": "^1.2.4",
- "react-remove-scroll": "^2.6.3"
+ "react-remove-scroll": "^2.7.2"
},
"peerDependencies": {
"@types/react": "*",
@@ -6794,31 +6641,13 @@
}
}
},
- "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-slot": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
- "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
+ "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
+ "@radix-ui/react-compose-refs": "1.1.5"
},
"peerDependencies": {
"@types/react": "*",
@@ -6831,18 +6660,17 @@
}
},
"node_modules/@radix-ui/react-switch": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
- "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz",
+ "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
- "@radix-ui/react-use-previous": "1.1.1",
- "@radix-ui/react-use-size": "1.1.1"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-size": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6860,23 +6688,24 @@
}
},
"node_modules/@radix-ui/react-tooltip": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
- "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
+ "version": "1.2.16",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz",
+ "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.3",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-dismissable-layer": "1.1.11",
- "@radix-ui/react-id": "1.1.1",
- "@radix-ui/react-popper": "1.2.8",
- "@radix-ui/react-portal": "1.1.9",
- "@radix-ui/react-presence": "1.1.5",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
- "@radix-ui/react-visually-hidden": "1.2.3"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.5",
+ "@radix-ui/react-context": "1.2.2",
+ "@radix-ui/react-dismissable-layer": "1.1.19",
+ "@radix-ui/react-id": "1.1.4",
+ "@radix-ui/react-popper": "1.3.7",
+ "@radix-ui/react-portal": "1.1.17",
+ "@radix-ui/react-presence": "1.1.10",
+ "@radix-ui/react-primitive": "2.1.10",
+ "@radix-ui/react-slot": "1.3.3",
+ "@radix-ui/react-use-controllable-state": "1.2.6",
+ "@radix-ui/react-use-layout-effect": "1.1.4",
+ "@radix-ui/react-visually-hidden": "1.2.11"
},
"peerDependencies": {
"@types/react": "*",
@@ -6893,28 +6722,10 @@
}
}
},
- "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-use-callback-ref": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
- "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz",
+ "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6927,13 +6738,14 @@
}
},
"node_modules/@radix-ui/react-use-controllable-state": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
- "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz",
+ "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-use-effect-event": "0.0.2",
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/primitive": "1.1.7",
+ "@radix-ui/react-use-effect-event": "0.0.5",
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6946,30 +6758,12 @@
}
},
"node_modules/@radix-ui/react-use-effect-event": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
- "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz",
+ "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-use-layout-effect": "1.1.1"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-use-escape-keydown": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
- "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-use-callback-ref": "1.1.1"
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -6982,9 +6776,9 @@
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
- "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz",
+ "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -6997,9 +6791,9 @@
}
},
"node_modules/@radix-ui/react-use-previous": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
- "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz",
+ "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -7012,12 +6806,12 @@
}
},
"node_modules/@radix-ui/react-use-rect": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
- "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz",
+ "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/rect": "1.1.1"
+ "@radix-ui/rect": "1.1.3"
},
"peerDependencies": {
"@types/react": "*",
@@ -7030,12 +6824,12 @@
}
},
"node_modules/@radix-ui/react-use-size": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
- "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz",
+ "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-use-layout-effect": "1.1.1"
+ "@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -7048,12 +6842,12 @@
}
},
"node_modules/@radix-ui/react-visually-hidden": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
- "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz",
+ "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-primitive": "2.1.3"
+ "@radix-ui/react-primitive": "2.1.10"
},
"peerDependencies": {
"@types/react": "*",
@@ -7071,9 +6865,9 @@
}
},
"node_modules/@radix-ui/rect": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
- "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz",
+ "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==",
"license": "MIT"
},
"node_modules/@rolldown/pluginutils": {
@@ -7479,16 +7273,16 @@
}
},
"node_modules/@smithy/config-resolver": {
- "version": "4.4.13",
- "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz",
- "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==",
+ "version": "4.4.17",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz",
+ "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"@smithy/util-config-provider": "^4.2.2",
- "@smithy/util-endpoints": "^3.3.3",
- "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-endpoints": "^3.4.2",
+ "@smithy/util-middleware": "^4.2.14",
"tslib": "^2.6.2"
},
"engines": {
@@ -7496,20 +7290,12 @@
}
},
"node_modules/@smithy/core": {
- "version": "3.23.13",
- "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.13.tgz",
- "integrity": "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==",
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.1.tgz",
+ "integrity": "sha512-i4YPS4B6ts7bjn7UwLnGjiZdprOvHvgGobFZsYK3GIY3E5hIqtj0rReU69BcTpGp+fvtraSNXeG1l+jtJvF55w==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-base64": "^4.3.2",
- "@smithy/util-body-length-browser": "^4.2.2",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-stream": "^4.5.21",
- "@smithy/util-utf8": "^4.2.2",
- "@smithy/uuid": "^1.1.2",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -7517,15 +7303,13 @@
}
},
"node_modules/@smithy/credential-provider-imds": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz",
- "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==",
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz",
+ "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
+ "@smithy/core": "^3.33.2",
+ "@smithy/types": "^4.17.2",
"tslib": "^2.6.2"
},
"engines": {
@@ -7603,15 +7387,13 @@
}
},
"node_modules/@smithy/fetch-http-handler": {
- "version": "5.3.15",
- "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz",
- "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==",
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz",
+ "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/querystring-builder": "^4.2.12",
- "@smithy/types": "^4.13.1",
- "@smithy/util-base64": "^4.3.2",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.18.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -7634,12 +7416,12 @@
}
},
"node_modules/@smithy/hash-node": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz",
- "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.14.tgz",
+ "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"@smithy/util-buffer-from": "^4.2.2",
"@smithy/util-utf8": "^4.2.2",
"tslib": "^2.6.2"
@@ -7663,12 +7445,12 @@
}
},
"node_modules/@smithy/invalid-dependency": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz",
- "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.14.tgz",
+ "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7702,13 +7484,13 @@
}
},
"node_modules/@smithy/middleware-content-length": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz",
- "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.14.tgz",
+ "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7716,18 +7498,18 @@
}
},
"node_modules/@smithy/middleware-endpoint": {
- "version": "4.4.28",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.28.tgz",
- "integrity": "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==",
+ "version": "4.4.32",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.32.tgz",
+ "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.13",
- "@smithy/middleware-serde": "^4.2.16",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
- "@smithy/url-parser": "^4.2.12",
- "@smithy/util-middleware": "^4.2.12",
+ "@smithy/core": "^3.23.17",
+ "@smithy/middleware-serde": "^4.2.20",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
+ "@smithy/url-parser": "^4.2.14",
+ "@smithy/util-middleware": "^4.2.14",
"tslib": "^2.6.2"
},
"engines": {
@@ -7735,18 +7517,19 @@
}
},
"node_modules/@smithy/middleware-retry": {
- "version": "4.4.46",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.46.tgz",
- "integrity": "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==",
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.7.tgz",
+ "integrity": "sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/service-error-classification": "^4.2.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-retry": "^4.2.13",
+ "@smithy/core": "^3.23.17",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/service-error-classification": "^4.3.1",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-middleware": "^4.2.14",
+ "@smithy/util-retry": "^4.3.6",
"@smithy/uuid": "^1.1.2",
"tslib": "^2.6.2"
},
@@ -7755,14 +7538,14 @@
}
},
"node_modules/@smithy/middleware-serde": {
- "version": "4.2.16",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.16.tgz",
- "integrity": "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==",
+ "version": "4.2.20",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.20.tgz",
+ "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.13",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/core": "^3.23.17",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7770,12 +7553,12 @@
}
},
"node_modules/@smithy/middleware-stack": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz",
- "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.14.tgz",
+ "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7783,14 +7566,14 @@
}
},
"node_modules/@smithy/node-config-provider": {
- "version": "4.3.12",
- "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz",
- "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==",
+ "version": "4.3.14",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.14.tgz",
+ "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^4.2.12",
- "@smithy/shared-ini-file-loader": "^4.4.7",
- "@smithy/types": "^4.13.1",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/shared-ini-file-loader": "^4.4.9",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7798,14 +7581,13 @@
}
},
"node_modules/@smithy/node-http-handler": {
- "version": "4.5.1",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.1.tgz",
- "integrity": "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz",
+ "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/querystring-builder": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/core": "^3.33.3",
+ "@smithy/types": "^4.18.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -7813,12 +7595,12 @@
}
},
"node_modules/@smithy/property-provider": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz",
- "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.14.tgz",
+ "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7826,26 +7608,12 @@
}
},
"node_modules/@smithy/protocol-http": {
- "version": "5.3.12",
- "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz",
- "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==",
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.14.tgz",
+ "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/querystring-builder": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz",
- "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.13.1",
- "@smithy/util-uri-escape": "^4.2.2",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7853,12 +7621,12 @@
}
},
"node_modules/@smithy/querystring-parser": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz",
- "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.14.tgz",
+ "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7866,24 +7634,24 @@
}
},
"node_modules/@smithy/service-error-classification": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz",
- "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.1.tgz",
+ "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1"
+ "@smithy/types": "^4.14.1"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@smithy/shared-ini-file-loader": {
- "version": "4.4.7",
- "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz",
- "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==",
+ "version": "4.4.9",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.9.tgz",
+ "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -7891,18 +7659,13 @@
}
},
"node_modules/@smithy/signature-v4": {
- "version": "5.3.12",
- "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz",
- "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==",
+ "version": "5.7.4",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz",
+ "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/is-array-buffer": "^4.2.2",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
- "@smithy/util-hex-encoding": "^4.2.2",
- "@smithy/util-middleware": "^4.2.12",
- "@smithy/util-uri-escape": "^4.2.2",
- "@smithy/util-utf8": "^4.2.2",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -7910,17 +7673,17 @@
}
},
"node_modules/@smithy/smithy-client": {
- "version": "4.12.8",
- "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.8.tgz",
- "integrity": "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==",
+ "version": "4.12.13",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.13.tgz",
+ "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/core": "^3.23.13",
- "@smithy/middleware-endpoint": "^4.4.28",
- "@smithy/middleware-stack": "^4.2.12",
- "@smithy/protocol-http": "^5.3.12",
- "@smithy/types": "^4.13.1",
- "@smithy/util-stream": "^4.5.21",
+ "@smithy/core": "^3.23.17",
+ "@smithy/middleware-endpoint": "^4.4.32",
+ "@smithy/middleware-stack": "^4.2.14",
+ "@smithy/protocol-http": "^5.3.14",
+ "@smithy/types": "^4.14.1",
+ "@smithy/util-stream": "^4.5.25",
"tslib": "^2.6.2"
},
"engines": {
@@ -7928,9 +7691,9 @@
}
},
"node_modules/@smithy/types": {
- "version": "4.13.1",
- "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz",
- "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==",
+ "version": "4.19.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz",
+ "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
@@ -7940,13 +7703,13 @@
}
},
"node_modules/@smithy/url-parser": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz",
- "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.14.tgz",
+ "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/querystring-parser": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/querystring-parser": "^4.2.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8017,14 +7780,14 @@
}
},
"node_modules/@smithy/util-defaults-mode-browser": {
- "version": "4.3.44",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.44.tgz",
- "integrity": "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==",
+ "version": "4.3.49",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.49.tgz",
+ "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/property-provider": "^4.2.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8032,17 +7795,17 @@
}
},
"node_modules/@smithy/util-defaults-mode-node": {
- "version": "4.2.48",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.48.tgz",
- "integrity": "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==",
+ "version": "4.2.54",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.54.tgz",
+ "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/config-resolver": "^4.4.13",
- "@smithy/credential-provider-imds": "^4.2.12",
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/property-provider": "^4.2.12",
- "@smithy/smithy-client": "^4.12.8",
- "@smithy/types": "^4.13.1",
+ "@smithy/config-resolver": "^4.4.17",
+ "@smithy/credential-provider-imds": "^4.2.14",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/property-provider": "^4.2.14",
+ "@smithy/smithy-client": "^4.12.13",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8050,13 +7813,13 @@
}
},
"node_modules/@smithy/util-endpoints": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz",
- "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz",
+ "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/node-config-provider": "^4.3.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/node-config-provider": "^4.3.14",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8076,12 +7839,12 @@
}
},
"node_modules/@smithy/util-middleware": {
- "version": "4.2.12",
- "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz",
- "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==",
+ "version": "4.2.14",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.14.tgz",
+ "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8089,13 +7852,13 @@
}
},
"node_modules/@smithy/util-retry": {
- "version": "4.2.13",
- "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.13.tgz",
- "integrity": "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==",
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.6.tgz",
+ "integrity": "sha512-p6/FO1n2KxMeQyna067i0uJ6TSbb165ZhnRtCpWh4Foxqbfc6oW+XITaL8QkFJj3KFnDe2URt4gOhgU06EP9ew==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/service-error-classification": "^4.2.12",
- "@smithy/types": "^4.13.1",
+ "@smithy/service-error-classification": "^4.3.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8103,14 +7866,14 @@
}
},
"node_modules/@smithy/util-stream": {
- "version": "4.5.21",
- "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.21.tgz",
- "integrity": "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==",
+ "version": "4.5.25",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.25.tgz",
+ "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/fetch-http-handler": "^5.3.15",
- "@smithy/node-http-handler": "^4.5.1",
- "@smithy/types": "^4.13.1",
+ "@smithy/fetch-http-handler": "^5.3.17",
+ "@smithy/node-http-handler": "^4.6.1",
+ "@smithy/types": "^4.14.1",
"@smithy/util-base64": "^4.3.2",
"@smithy/util-buffer-from": "^4.2.2",
"@smithy/util-hex-encoding": "^4.2.2",
@@ -8121,18 +7884,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@smithy/util-uri-escape": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz",
- "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
"node_modules/@smithy/util-utf8": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz",
@@ -8147,12 +7898,12 @@
}
},
"node_modules/@smithy/util-waiter": {
- "version": "4.2.14",
- "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.14.tgz",
- "integrity": "sha512-2zqq5o/oizvMaFUlNiTyZ7dbgYv1a893aGut2uaxtbzTx/VYYnRxWzDHuD/ftgcw94ffenua+ZNLrbqwUYE+Bg==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.3.0.tgz",
+ "integrity": "sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.13.1",
+ "@smithy/types": "^4.14.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -8259,9 +8010,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8607,9 +8355,9 @@
}
},
"node_modules/@testing-library/dom": {
- "version": "10.4.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
- "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
+ "version": "10.4.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz",
+ "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8627,9 +8375,9 @@
}
},
"node_modules/@testing-library/react": {
- "version": "16.3.2",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz",
- "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==",
+ "version": "16.3.3",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz",
+ "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8655,9 +8403,9 @@
}
},
"node_modules/@testing-library/user-event": {
- "version": "14.6.1",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
- "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
+ "version": "14.6.7",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.7.tgz",
+ "integrity": "sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8858,19 +8606,19 @@
"license": "MIT"
},
"node_modules/@types/negotiator": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/@types/negotiator/-/negotiator-0.6.4.tgz",
- "integrity": "sha512-elf6BsTq+AkyNsb2h5cGNst2Mc7dPliVoAPm1fXglC/BM3f2pFA40BaSSv3E5lyHteEawVKLP+8TwiY1DMNb3A==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/@types/negotiator/-/negotiator-0.6.5.tgz",
+ "integrity": "sha512-MPOlB48mfWhoUlynY0ga7CFsXIPcH6vGPkjzXMn2p+4PH1QUyn2KPtw0hrLLmO6SaX4zse3X6h2x/083vveAlA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.12.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
- "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
+ "version": "24.19.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz",
+ "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==",
"license": "MIT",
"dependencies": {
- "undici-types": "~7.16.0"
+ "undici-types": ">=7.24.0 <7.24.7"
}
},
"node_modules/@types/node-fetch": {
@@ -8909,22 +8657,22 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.2.14",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
- "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "version": "19.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz",
+ "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==",
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.2.3",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
- "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "version": "19.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz",
+ "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
- "@types/react": "^19.2.0"
+ "@types/react": "^19.3.0"
}
},
"node_modules/@types/responselike": {
@@ -9501,9 +9249,9 @@
]
},
"node_modules/@vercel/oidc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.1.0.tgz",
- "integrity": "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz",
+ "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==",
"license": "Apache-2.0",
"engines": {
"node": ">= 20"
@@ -9531,14 +9279,14 @@
}
},
"node_modules/@vitest/coverage-v8": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz",
- "integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz",
+ "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
- "@vitest/utils": "4.1.2",
+ "@vitest/utils": "4.1.11",
"ast-v8-to-istanbul": "^1.0.0",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
@@ -9552,8 +9300,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/browser": "4.1.2",
- "vitest": "4.1.2"
+ "@vitest/browser": "4.1.11",
+ "vitest": "4.1.11"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -9562,16 +9310,16 @@
}
},
"node_modules/@vitest/expect": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz",
- "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz",
+ "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
- "@vitest/spy": "4.1.2",
- "@vitest/utils": "4.1.2",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
@@ -9580,13 +9328,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz",
- "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz",
+ "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "4.1.2",
+ "@vitest/spy": "4.1.11",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -9607,9 +9355,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz",
- "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz",
+ "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9620,13 +9368,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz",
- "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz",
+ "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "4.1.2",
+ "@vitest/utils": "4.1.11",
"pathe": "^2.0.3"
},
"funding": {
@@ -9634,14 +9382,14 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz",
- "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz",
+ "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.1.2",
- "@vitest/utils": "4.1.2",
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/utils": "4.1.11",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -9650,9 +9398,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz",
- "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz",
+ "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -9660,13 +9408,13 @@
}
},
"node_modules/@vitest/utils": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz",
- "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz",
+ "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.1.2",
+ "@vitest/pretty-format": "4.1.11",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
@@ -9675,9 +9423,9 @@
}
},
"node_modules/@xmldom/xmldom": {
- "version": "0.9.9",
- "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.9.tgz",
- "integrity": "sha512-qycIHAucxy/LXAYIjmLmtQ8q9GPnMbnjG1KXhWm9o5sCr6pOYDATkMPiTNa6/v8eELyqOQ2FsEqeoFYmgv/gJg==",
+ "version": "0.9.12",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.12.tgz",
+ "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==",
"license": "MIT",
"engines": {
"node": ">=14.6"
@@ -9769,15 +9517,15 @@
}
},
"node_modules/ai": {
- "version": "6.0.100",
- "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.100.tgz",
- "integrity": "sha512-BIxhG7M7wvcWCF+IEnZi7WpkRLOM3jR2vJ0mMuohl2UB2i1R/ZUa1cHFel1xI8nWvyUpOoQXKqsM0BAH50EYSQ==",
+ "version": "6.0.300",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.300.tgz",
+ "integrity": "sha512-ZBT30eQTy6exP+EWJJxm3DMb34m9td8STy+hfnvKdjhyEQoM8vQN5YfFLTRVVTCONHoM+y5K+Tudb6PTFrpikA==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/gateway": "3.0.55",
- "@ai-sdk/provider": "3.0.8",
- "@ai-sdk/provider-utils": "4.0.15",
- "@opentelemetry/api": "1.9.0"
+ "@ai-sdk/gateway": "3.0.209",
+ "@ai-sdk/provider": "3.0.18",
+ "@ai-sdk/provider-utils": "4.0.57",
+ "@opentelemetry/api": "^1.9.0"
},
"engines": {
"node": ">=18"
@@ -9786,15 +9534,6 @@
"zod": "^3.25.76 || ^4.1.8"
}
},
- "node_modules/ai/node_modules/@opentelemetry/api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
- "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8.0.0"
- }
- },
"node_modules/ajv": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
@@ -11522,15 +11261,15 @@
"license": "MIT"
},
"node_modules/concurrently": {
- "version": "9.2.1",
- "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
- "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "version": "9.2.4",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz",
+ "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "4.1.2",
"rxjs": "7.8.2",
- "shell-quote": "1.8.3",
+ "shell-quote": "1.9.0",
"supports-color": "8.1.1",
"tree-kill": "1.2.2",
"yargs": "17.7.2"
@@ -11749,15 +11488,6 @@
"node": ">=20"
}
},
- "node_modules/cross-fetch": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
- "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==",
- "license": "MIT",
- "dependencies": {
- "node-fetch": "^2.7.0"
- }
- },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -11980,6 +11710,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -12486,9 +12222,9 @@
}
},
"node_modules/electron": {
- "version": "39.6.1",
- "resolved": "https://registry.npmjs.org/electron/-/electron-39.6.1.tgz",
- "integrity": "sha512-pgmTbWnT3rP+eo3EolO5EdNw5f7/x/0S7vP+eXC8Zyp2sWGjP4+kmo1RyeAYCChwIRWJFKQ2rQVl/ZkqwK6O2Q==",
+ "version": "39.8.5",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-39.8.5.tgz",
+ "integrity": "sha512-q6+LiQIcTadSyvtPgLDQkCtVA9jQJXQVMrQcctfOJILh6OFMN+UJJLRkuUTy8CZDYeCIBn1ZycqsL1dAXugxZA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -12836,9 +12572,9 @@
}
},
"node_modules/electron/node_modules/@types/node": {
- "version": "22.19.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
- "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
+ "version": "22.19.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
+ "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13154,9 +12890,9 @@
"optional": true
},
"node_modules/esbuild": {
- "version": "0.27.4",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
- "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -13167,32 +12903,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.4",
- "@esbuild/android-arm": "0.27.4",
- "@esbuild/android-arm64": "0.27.4",
- "@esbuild/android-x64": "0.27.4",
- "@esbuild/darwin-arm64": "0.27.4",
- "@esbuild/darwin-x64": "0.27.4",
- "@esbuild/freebsd-arm64": "0.27.4",
- "@esbuild/freebsd-x64": "0.27.4",
- "@esbuild/linux-arm": "0.27.4",
- "@esbuild/linux-arm64": "0.27.4",
- "@esbuild/linux-ia32": "0.27.4",
- "@esbuild/linux-loong64": "0.27.4",
- "@esbuild/linux-mips64el": "0.27.4",
- "@esbuild/linux-ppc64": "0.27.4",
- "@esbuild/linux-riscv64": "0.27.4",
- "@esbuild/linux-s390x": "0.27.4",
- "@esbuild/linux-x64": "0.27.4",
- "@esbuild/netbsd-arm64": "0.27.4",
- "@esbuild/netbsd-x64": "0.27.4",
- "@esbuild/openbsd-arm64": "0.27.4",
- "@esbuild/openbsd-x64": "0.27.4",
- "@esbuild/openharmony-arm64": "0.27.4",
- "@esbuild/sunos-x64": "0.27.4",
- "@esbuild/win32-arm64": "0.27.4",
- "@esbuild/win32-ia32": "0.27.4",
- "@esbuild/win32-x64": "0.27.4"
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
}
},
"node_modules/escalade": {
@@ -13223,9 +12959,9 @@
}
},
"node_modules/eslint": {
- "version": "9.39.4",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
- "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz",
+ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13234,8 +12970,8 @@
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
- "@eslint/eslintrc": "^3.3.5",
- "@eslint/js": "9.39.4",
+ "@eslint/eslintrc": "^3.3.6",
+ "@eslint/js": "9.39.5",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@@ -13715,9 +13451,9 @@
"license": "MIT"
},
"node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13891,9 +13627,9 @@
"license": "MIT"
},
"node_modules/eventsource-parser": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
- "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz",
+ "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
@@ -14096,41 +13832,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-xml-builder": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
- "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "path-expression-matcher": "^1.1.3"
- }
- },
- "node_modules/fast-xml-parser": {
- "version": "5.5.8",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz",
- "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "fast-xml-builder": "^1.1.4",
- "path-expression-matcher": "^1.2.0",
- "strnum": "^2.2.0"
- },
- "bin": {
- "fxparser": "src/cli/cli.js"
- }
- },
"node_modules/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -14441,13 +14142,13 @@
}
},
"node_modules/framer-motion": {
- "version": "12.38.0",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
- "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
+ "version": "12.43.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz",
+ "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.38.0",
- "motion-utils": "^12.36.0",
+ "motion-dom": "^12.43.0",
+ "motion-utils": "^12.39.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -14511,9 +14212,9 @@
"license": "ISC"
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -16145,10 +15846,20 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
+ "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -16362,6 +16073,15 @@
"node": ">=0.10"
}
},
+ "node_modules/launder": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz",
+ "integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==",
+ "license": "MIT",
+ "dependencies": {
+ "dayjs": "^1.11.7"
+ }
+ },
"node_modules/lazy-val": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
@@ -16384,9 +16104,9 @@
}
},
"node_modules/lightningcss": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
- "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
"license": "MPL-2.0",
"optional": true,
"dependencies": {
@@ -16400,17 +16120,17 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "lightningcss-android-arm64": "1.32.0",
- "lightningcss-darwin-arm64": "1.32.0",
- "lightningcss-darwin-x64": "1.32.0",
- "lightningcss-freebsd-x64": "1.32.0",
- "lightningcss-linux-arm-gnueabihf": "1.32.0",
- "lightningcss-linux-arm64-gnu": "1.32.0",
- "lightningcss-linux-arm64-musl": "1.32.0",
- "lightningcss-linux-x64-gnu": "1.32.0",
- "lightningcss-linux-x64-musl": "1.32.0",
- "lightningcss-win32-arm64-msvc": "1.32.0",
- "lightningcss-win32-x64-msvc": "1.32.0"
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
}
},
"node_modules/lightningcss-android-arm64": {
@@ -16561,9 +16281,9 @@
}
},
"node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
- "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
"cpu": [
"x64"
],
@@ -16647,9 +16367,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-android-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
- "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
"cpu": [
"arm64"
],
@@ -16667,9 +16387,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-darwin-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
- "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
"cpu": [
"arm64"
],
@@ -16687,9 +16407,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-darwin-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
- "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
"cpu": [
"x64"
],
@@ -16707,9 +16427,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-freebsd-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
- "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
"cpu": [
"x64"
],
@@ -16727,9 +16447,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
- "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
"cpu": [
"arm"
],
@@ -16747,9 +16467,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
- "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
"cpu": [
"arm64"
],
@@ -16770,9 +16490,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
- "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
"cpu": [
"arm64"
],
@@ -16793,9 +16513,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-linux-x64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
- "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
"cpu": [
"x64"
],
@@ -16816,9 +16536,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
- "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
"cpu": [
"arm64"
],
@@ -16836,9 +16556,9 @@
}
},
"node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
- "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
"cpu": [
"x64"
],
@@ -17234,12 +16954,6 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "node_modules/long": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
- "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
- "license": "Apache-2.0"
- },
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -18396,42 +18110,564 @@
}
},
"node_modules/miniflare": {
- "version": "4.20260329.0",
- "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260329.0.tgz",
- "integrity": "sha512-+G+1YFVeuEpw/gZZmUHQR7IfzJV+DDGvnSl0yXzhgvHh8Nbr8Go5uiWIwl17EyZ1Uors3FKUMDUyU6+ejeKZOw==",
+ "version": "5.20261001.0-alpha",
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20261001.0-alpha.tgz",
+ "integrity": "sha512-GaimS5mSIOMyvd16ga+e1/QkI8cmp3z35QPHFex0DktqNQf2RVZQwMPQXdyoUreUiFP/0Gpe2MoQInTyMAD5xA==",
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "0.8.1",
- "sharp": "^0.34.5",
- "undici": "7.24.4",
- "workerd": "1.20260329.1",
- "ws": "8.18.0",
+ "sharp": "0.35.4",
+ "undici": "7.29.1",
+ "workerd": "1.20261001.1",
+ "ws": "8.21.0",
"youch": "4.1.0-beta.10"
},
- "bin": {
- "miniflare": "bootstrap.js"
- },
"engines": {
- "node": ">=18.0.0"
+ "node": ">=22.0.0"
}
},
- "node_modules/miniflare/node_modules/ws": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
- "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
- "license": "MIT",
+ "node_modules/miniflare/node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz",
+ "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=10.0.0"
+ "node": ">=20.9.0"
},
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-darwin-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
+ "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz",
+ "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
+ "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
+ "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
+ "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
+ "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
+ "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
+ "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
+ "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
+ "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
+ "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-arm": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
+ "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
+ "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
+ "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
+ "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-s390x": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
+ "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linux-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
+ "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
+ "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
+ "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-win32-arm64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
+ "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-win32-ia32": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
+ "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/@img/sharp-win32-x64": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
+ "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/miniflare/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/miniflare/node_modules/sharp": {
+ "version": "0.35.4",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
+ "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@img/colour": "^1.1.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.8.5"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.35.4",
+ "@img/sharp-darwin-x64": "0.35.4",
+ "@img/sharp-freebsd-wasm32": "0.35.4",
+ "@img/sharp-libvips-darwin-arm64": "1.3.3",
+ "@img/sharp-libvips-darwin-x64": "1.3.3",
+ "@img/sharp-libvips-linux-arm": "1.3.3",
+ "@img/sharp-libvips-linux-arm64": "1.3.3",
+ "@img/sharp-libvips-linux-ppc64": "1.3.3",
+ "@img/sharp-libvips-linux-riscv64": "1.3.3",
+ "@img/sharp-libvips-linux-s390x": "1.3.3",
+ "@img/sharp-libvips-linux-x64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.3",
+ "@img/sharp-linux-arm": "0.35.4",
+ "@img/sharp-linux-arm64": "0.35.4",
+ "@img/sharp-linux-ppc64": "0.35.4",
+ "@img/sharp-linux-riscv64": "0.35.4",
+ "@img/sharp-linux-s390x": "0.35.4",
+ "@img/sharp-linux-x64": "0.35.4",
+ "@img/sharp-linuxmusl-arm64": "0.35.4",
+ "@img/sharp-linuxmusl-x64": "0.35.4",
+ "@img/sharp-webcontainers-wasm32": "0.35.4",
+ "@img/sharp-win32-arm64": "0.35.4",
+ "@img/sharp-win32-ia32": "0.35.4",
+ "@img/sharp-win32-x64": "0.35.4"
},
"peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
+ "@types/node": {
"optional": true
}
}
@@ -18635,12 +18871,12 @@
}
},
"node_modules/motion": {
- "version": "12.38.0",
- "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
- "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
+ "version": "12.43.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.43.0.tgz",
+ "integrity": "sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==",
"license": "MIT",
"dependencies": {
- "framer-motion": "^12.38.0",
+ "framer-motion": "^12.43.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -18661,18 +18897,18 @@
}
},
"node_modules/motion-dom": {
- "version": "12.38.0",
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
- "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
+ "version": "12.43.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz",
+ "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==",
"license": "MIT",
"dependencies": {
- "motion-utils": "^12.36.0"
+ "motion-utils": "^12.39.0"
}
},
"node_modules/motion-utils": {
- "version": "12.36.0",
- "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
- "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
+ "version": "12.39.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
"license": "MIT"
},
"node_modules/ms": {
@@ -18691,9 +18927,9 @@
}
},
"node_modules/nanoid": {
- "version": "5.1.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz",
- "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==",
+ "version": "5.1.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz",
+ "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==",
"funding": [
{
"type": "github",
@@ -18732,23 +18968,43 @@
"license": "MIT"
},
"node_modules/negotiator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
- "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz",
+ "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/negotiator/node_modules/content-type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
+ "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
"license": "MIT",
"engines": {
- "node": ">= 0.6"
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/next": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
- "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
+ "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==",
"license": "MIT",
"dependencies": {
- "@next/env": "16.1.6",
+ "@next/env": "16.2.6",
"@swc/helpers": "0.5.15",
- "baseline-browser-mapping": "^2.8.3",
+ "baseline-browser-mapping": "^2.9.19",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
"styled-jsx": "5.1.6"
@@ -18760,15 +19016,15 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "16.1.6",
- "@next/swc-darwin-x64": "16.1.6",
- "@next/swc-linux-arm64-gnu": "16.1.6",
- "@next/swc-linux-arm64-musl": "16.1.6",
- "@next/swc-linux-x64-gnu": "16.1.6",
- "@next/swc-linux-x64-musl": "16.1.6",
- "@next/swc-win32-arm64-msvc": "16.1.6",
- "@next/swc-win32-x64-msvc": "16.1.6",
- "sharp": "^0.34.4"
+ "@next/swc-darwin-arm64": "16.2.6",
+ "@next/swc-darwin-x64": "16.2.6",
+ "@next/swc-linux-arm64-gnu": "16.2.6",
+ "@next/swc-linux-arm64-musl": "16.2.6",
+ "@next/swc-linux-x64-gnu": "16.2.6",
+ "@next/swc-linux-x64-musl": "16.2.6",
+ "@next/swc-win32-arm64-msvc": "16.2.6",
+ "@next/swc-win32-x64-msvc": "16.2.6",
+ "sharp": "^0.34.5"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -18794,9 +19050,9 @@
}
},
"node_modules/next/node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"funding": [
{
"type": "github",
@@ -19247,16 +19503,20 @@
"license": "MIT"
},
"node_modules/ollama-ai-provider-v2": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ollama-ai-provider-v2/-/ollama-ai-provider-v2-2.0.0.tgz",
- "integrity": "sha512-vbLG/xsW0kgGlInrTIa3W8niy294dw7n8nkLFVwjPHlX9EjtBH4bNPgHQEbmQUy60LjV3NF3Y1HtWPxORSb3bQ==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/ollama-ai-provider-v2/-/ollama-ai-provider-v2-3.6.0.tgz",
+ "integrity": "sha512-1Om3FVJYhBwkAr5kQ+BX1s/tdVdtVdoFQWrX4PBQHDHPISyGt24CjhtggEjUYpy5ait0YeVfZwEpIYjgD8Ih7Q==",
"license": "Apache-2.0",
"dependencies": {
- "@ai-sdk/provider": "^3.0.0",
- "@ai-sdk/provider-utils": "^4.0.1"
+ "@ai-sdk/provider": "^3.0.10",
+ "@ai-sdk/provider-utils": "^4.0.27"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "Buy Me a Coffee",
+ "url": "https://buymeacoffee.com/nordwestt"
},
"peerDependencies": {
"ai": "^5.0.0 || ^6.0.0",
@@ -19504,9 +19764,19 @@
"license": "BlueOak-1.0.0"
},
"node_modules/pako": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
- "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz",
+ "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
"license": "(MIT AND Zlib)"
},
"node_modules/parent-module": {
@@ -19598,21 +19868,6 @@
"node": ">=8"
}
},
- "node_modules/path-expression-matcher": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz",
- "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -19717,35 +19972,32 @@
}
},
"node_modules/playwright": {
- "version": "1.59.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0.tgz",
- "integrity": "sha512-wihGScriusvATUxmhfENxg0tj1vHEFeIwxlnPFKQTOQVd7aG08mUfvvniRP/PtQOC+2Bs52kBOC/Up1jTXeIbw==",
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
+ "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.59.0"
+ "playwright-core": "1.63.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "fsevents": "2.3.2"
+ "node": ">=20"
}
},
"node_modules/playwright-core": {
- "version": "1.59.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0.tgz",
- "integrity": "sha512-PW/X/IoZ6BMUUy8rpwHEZ8Kc0IiLIkgKYGNFaMs5KmQhcfLILNx9yCQD0rnWeWfz1PNeqcFP1BsihQhDOBCwZw==",
+ "version": "1.63.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
+ "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/plist": {
@@ -19986,30 +20238,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/protobufjs": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
- "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
- "hasInstallScript": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.2",
- "@protobufjs/base64": "^1.1.2",
- "@protobufjs/codegen": "^2.0.4",
- "@protobufjs/eventemitter": "^1.1.0",
- "@protobufjs/fetch": "^1.1.0",
- "@protobufjs/float": "^1.0.2",
- "@protobufjs/inquire": "^1.1.0",
- "@protobufjs/path": "^1.1.2",
- "@protobufjs/pool": "^1.1.0",
- "@protobufjs/utf8": "^1.1.0",
- "@types/node": ">=13.7.0",
- "long": "^5.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -20141,24 +20369,24 @@
}
},
"node_modules/react": {
- "version": "19.2.4",
- "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
- "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "version": "19.3.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
+ "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.2.4",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
- "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+ "version": "19.3.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
+ "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==",
"license": "MIT",
"dependencies": {
- "scheduler": "^0.27.0"
+ "scheduler": "^0.28.0"
},
"peerDependencies": {
- "react": "^19.2.4"
+ "react": "^19.3.0"
}
},
"node_modules/react-drawio": {
@@ -20171,9 +20399,9 @@
}
},
"node_modules/react-icons": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
- "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz",
+ "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==",
"license": "MIT",
"peerDependencies": {
"react": "*"
@@ -21019,23 +21247,106 @@
}
},
"node_modules/sanitize-html": {
- "version": "2.17.0",
- "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz",
- "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==",
+ "version": "2.17.6",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.6.tgz",
+ "integrity": "sha512-M4bo9tfv1yfhQZZKkc6dL07ALrGJtfvNOuhX3hU9AVPR/uPQ+nKOJBqTYc7LfMQblTW04mtSWDJWEyLvygJsLA==",
"license": "MIT",
"dependencies": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
- "htmlparser2": "^8.0.0",
+ "htmlparser2": "^12.0.0",
"is-plain-object": "^5.0.0",
+ "launder": "^1.7.1",
"parse-srcset": "^1.0.2",
"postcss": "^8.3.11"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/dom-serializer": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
+ "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0",
+ "entities": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/domelementtype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
+ "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/domhandler": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
+ "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/domutils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
+ "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^3.0.0",
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/entities": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+ "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/sanitize-html/node_modules/htmlparser2": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
- "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz",
+ "integrity": "sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -21045,10 +21356,13 @@
],
"license": "MIT",
"dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.0.1",
- "entities": "^4.4.0"
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0",
+ "domutils": "^4.0.2",
+ "entities": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
}
},
"node_modules/sax": {
@@ -21065,7 +21379,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
- "dev": true,
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
@@ -21075,9 +21388,9 @@
}
},
"node_modules/scheduler": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
- "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
+ "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
"license": "MIT"
},
"node_modules/semver": {
@@ -21227,6 +21540,7 @@
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"hasInstallScript": true,
"license": "Apache-2.0",
+ "optional": true,
"dependencies": {
"@img/colour": "^1.0.0",
"detect-libc": "^2.1.2",
@@ -21270,6 +21584,7 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
+ "optional": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -21299,9 +21614,9 @@
}
},
"node_modules/shell-quote": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
- "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz",
+ "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -21698,13 +22013,19 @@
}
},
"node_modules/sonner": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
- "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==",
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.8.tgz",
+ "integrity": "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==",
"license": "MIT",
"peerDependencies": {
+ "@types/react": "^18.0.0 || ^19.0.0",
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
}
},
"node_modules/source-map": {
@@ -22075,18 +22396,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/strnum": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz",
- "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT"
- },
"node_modules/style-to-js": {
"version": "1.1.21",
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
@@ -22720,12 +23029,16 @@
"license": "0BSD"
},
"node_modules/turndown": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.2.tgz",
- "integrity": "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==",
+ "version": "7.2.4",
+ "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz",
+ "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==",
"license": "MIT",
"dependencies": {
"@mixmark-io/domino": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=18",
+ "npm": ">=9"
}
},
"node_modules/type-check": {
@@ -22911,18 +23224,18 @@
}
},
"node_modules/undici": {
- "version": "7.24.4",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
- "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz",
+ "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"license": "MIT"
},
"node_modules/unenv": {
@@ -23058,12 +23371,15 @@
}
},
"node_modules/unpdf": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/unpdf/-/unpdf-1.4.0.tgz",
- "integrity": "sha512-TahIk0xdH/4jh/MxfclzU79g40OyxtP00VnEUZdEkJoYtXAHWLiir6t3FC6z3vDqQTzc2ZHcla6uEiVTNjejuA==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/unpdf/-/unpdf-1.8.1.tgz",
+ "integrity": "sha512-xkURhy2SoGpOIH0a1gLHNkASPIQYonadDJs2AQwPEfUakafeD9EA1WTWWsaR++gfTCXJpV27W7tU1nXuk82UKQ==",
"license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
"peerDependencies": {
- "@napi-rs/canvas": "^0.1.69"
+ "@napi-rs/canvas": "^0.1.69 || ^1.0.0"
},
"peerDependenciesMeta": {
"@napi-rs/canvas": {
@@ -23379,35 +23695,504 @@
"vite": "*"
}
},
- "node_modules/vite/node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
}
},
"node_modules/vitest": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz",
- "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==",
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz",
+ "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "4.1.2",
- "@vitest/mocker": "4.1.2",
- "@vitest/pretty-format": "4.1.2",
- "@vitest/runner": "4.1.2",
- "@vitest/snapshot": "4.1.2",
- "@vitest/spy": "4.1.2",
- "@vitest/utils": "4.1.2",
+ "@vitest/expect": "4.1.11",
+ "@vitest/mocker": "4.1.11",
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/runner": "4.1.11",
+ "@vitest/snapshot": "4.1.11",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
@@ -23435,10 +24220,12 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
- "@vitest/browser-playwright": "4.1.2",
- "@vitest/browser-preview": "4.1.2",
- "@vitest/browser-webdriverio": "4.1.2",
- "@vitest/ui": "4.1.2",
+ "@vitest/browser-playwright": "4.1.11",
+ "@vitest/browser-preview": "4.1.11",
+ "@vitest/browser-webdriverio": "4.1.11",
+ "@vitest/coverage-istanbul": "4.1.11",
+ "@vitest/coverage-v8": "4.1.11",
+ "@vitest/ui": "4.1.11",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -23462,6 +24249,12 @@
"@vitest/browser-webdriverio": {
"optional": true
},
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
"@vitest/ui": {
"optional": true
},
@@ -23694,9 +24487,9 @@
}
},
"node_modules/workerd": {
- "version": "1.20260329.1",
- "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260329.1.tgz",
- "integrity": "sha512-+ifMv3uBuD33ee7pan5n8+sgVxm2u5HnbgfXzHKwMNTKw86znqBJSnJoBqtP88+2T5U2Lu11xXUt+khPYioXwQ==",
+ "version": "1.20261001.1",
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20261001.1.tgz",
+ "integrity": "sha512-d/SIYHFO0PT/wiFZg8in4NpRIxYuFwslX1HdylOtWkBIIUmSpkGFhK820cV84XACFylwJ48xuRoWW/8DWDPsPQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"bin": {
@@ -23706,40 +24499,41 @@
"node": ">=16"
},
"optionalDependencies": {
- "@cloudflare/workerd-darwin-64": "1.20260329.1",
- "@cloudflare/workerd-darwin-arm64": "1.20260329.1",
- "@cloudflare/workerd-linux-64": "1.20260329.1",
- "@cloudflare/workerd-linux-arm64": "1.20260329.1",
- "@cloudflare/workerd-windows-64": "1.20260329.1"
+ "@cloudflare/workerd-darwin-64": "1.20261001.1",
+ "@cloudflare/workerd-darwin-arm64": "1.20261001.1",
+ "@cloudflare/workerd-linux-64": "1.20261001.1",
+ "@cloudflare/workerd-linux-arm64": "1.20261001.1",
+ "@cloudflare/workerd-windows-64": "1.20261001.1"
}
},
"node_modules/wrangler": {
- "version": "4.79.0",
- "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.79.0.tgz",
- "integrity": "sha512-NMinIdB1pXIqdk+NLw4+RjzB7K5z4+lWMxhTxFTfZomwJu3Pm6N+kZ+a66D3nI7w0oCjsdv/umrZVmSHCBp2cg==",
+ "version": "4.146.0",
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.146.0.tgz",
+ "integrity": "sha512-c27eHUH0Isr8HTmgZ6cLtLr/0cxtiBoLren6ywBeTW8ZIzj/dINVNBcqsbZc+iETnI5jrzO7E7Z8mLgLZ/l9iw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
- "@cloudflare/kv-asset-handler": "0.4.2",
- "@cloudflare/unenv-preset": "2.16.0",
+ "@cloudflare/kv-asset-handler": "0.5.0",
+ "@cloudflare/unenv-preset": "2.16.2",
"blake3-wasm": "2.1.5",
- "esbuild": "0.27.3",
- "miniflare": "4.20260329.0",
+ "esbuild": "0.28.1",
+ "miniflare": "5.20261001.0-alpha",
"path-to-regexp": "6.3.0",
"unenv": "2.0.0-rc.24",
- "workerd": "1.20260329.1"
+ "workerd": "1.20261001.1"
},
"bin": {
+ "cf-wrangler": "bin/cf-wrangler.js",
"wrangler": "bin/wrangler.js",
"wrangler2": "bin/wrangler.js"
},
"engines": {
- "node": ">=20.3.0"
+ "node": ">=22.0.0"
},
"optionalDependencies": {
- "fsevents": "~2.3.2"
+ "fsevents": "2.3.3"
},
"peerDependencies": {
- "@cloudflare/workers-types": "^4.20260329.1"
+ "@cloudflare/workers-types": "^5.20261001.1"
},
"peerDependenciesMeta": {
"@cloudflare/workers-types": {
@@ -23747,25 +24541,10 @@
}
}
},
- "node_modules/wrangler/node_modules/@cloudflare/unenv-preset": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz",
- "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==",
- "license": "MIT OR Apache-2.0",
- "peerDependencies": {
- "unenv": "2.0.0-rc.24",
- "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0"
- },
- "peerDependenciesMeta": {
- "workerd": {
- "optional": true
- }
- }
- },
"node_modules/wrangler/node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
- "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [
"ppc64"
],
@@ -23779,9 +24558,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/android-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
- "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [
"arm"
],
@@ -23795,9 +24574,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/android-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
- "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [
"arm64"
],
@@ -23811,9 +24590,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/android-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
- "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [
"x64"
],
@@ -23827,9 +24606,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
- "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [
"arm64"
],
@@ -23843,9 +24622,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/darwin-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
- "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [
"x64"
],
@@ -23859,9 +24638,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
- "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [
"arm64"
],
@@ -23875,9 +24654,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
- "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [
"x64"
],
@@ -23891,9 +24670,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
- "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [
"arm"
],
@@ -23907,9 +24686,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
- "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [
"arm64"
],
@@ -23923,9 +24702,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
- "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [
"ia32"
],
@@ -23939,9 +24718,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-loong64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
- "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [
"loong64"
],
@@ -23955,9 +24734,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
- "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [
"mips64el"
],
@@ -23971,9 +24750,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
- "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [
"ppc64"
],
@@ -23987,9 +24766,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
- "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [
"riscv64"
],
@@ -24003,9 +24782,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-s390x": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
- "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [
"s390x"
],
@@ -24019,9 +24798,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/linux-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
- "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [
"x64"
],
@@ -24035,9 +24814,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
@@ -24051,9 +24830,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
- "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [
"x64"
],
@@ -24067,9 +24846,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [
"arm64"
],
@@ -24083,9 +24862,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
- "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [
"x64"
],
@@ -24099,9 +24878,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
- "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [
"arm64"
],
@@ -24115,9 +24894,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/sunos-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
- "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [
"x64"
],
@@ -24131,9 +24910,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/win32-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
- "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [
"arm64"
],
@@ -24147,9 +24926,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/win32-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
- "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [
"ia32"
],
@@ -24163,9 +24942,9 @@
}
},
"node_modules/wrangler/node_modules/@esbuild/win32-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
- "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [
"x64"
],
@@ -24179,9 +24958,9 @@
}
},
"node_modules/wrangler/node_modules/esbuild": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
- "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -24191,32 +24970,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.3",
- "@esbuild/android-arm": "0.27.3",
- "@esbuild/android-arm64": "0.27.3",
- "@esbuild/android-x64": "0.27.3",
- "@esbuild/darwin-arm64": "0.27.3",
- "@esbuild/darwin-x64": "0.27.3",
- "@esbuild/freebsd-arm64": "0.27.3",
- "@esbuild/freebsd-x64": "0.27.3",
- "@esbuild/linux-arm": "0.27.3",
- "@esbuild/linux-arm64": "0.27.3",
- "@esbuild/linux-ia32": "0.27.3",
- "@esbuild/linux-loong64": "0.27.3",
- "@esbuild/linux-mips64el": "0.27.3",
- "@esbuild/linux-ppc64": "0.27.3",
- "@esbuild/linux-riscv64": "0.27.3",
- "@esbuild/linux-s390x": "0.27.3",
- "@esbuild/linux-x64": "0.27.3",
- "@esbuild/netbsd-arm64": "0.27.3",
- "@esbuild/netbsd-x64": "0.27.3",
- "@esbuild/openbsd-arm64": "0.27.3",
- "@esbuild/openbsd-x64": "0.27.3",
- "@esbuild/openharmony-arm64": "0.27.3",
- "@esbuild/sunos-x64": "0.27.3",
- "@esbuild/win32-arm64": "0.27.3",
- "@esbuild/win32-ia32": "0.27.3",
- "@esbuild/win32-x64": "0.27.3"
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/wrap-ansi": {
@@ -24338,10 +25117,9 @@
"license": "ISC"
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
- "dev": true,
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -24383,7 +25161,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true,
"license": "MIT"
},
"node_modules/y18n": {
@@ -24541,9 +25318,9 @@
}
},
"node_modules/zod": {
- "version": "4.3.6",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
- "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "version": "4.6.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
+ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
diff --git a/package.json b/package.json
index 965e11fb..3db426aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "next-ai-draw-io",
- "version": "0.4.14",
+ "version": "0.4.16",
"license": "Apache-2.0",
"private": true,
"main": "dist-electron/main/index.js",
@@ -31,27 +31,28 @@
"test:e2e": "playwright test"
},
"dependencies": {
- "@ai-sdk/amazon-bedrock": "^4.0.1",
- "@ai-sdk/anthropic": "^3.0.0",
- "@ai-sdk/azure": "^3.0.0",
- "@ai-sdk/deepseek": "^2.0.0",
- "@ai-sdk/gateway": "^3.0.0",
- "@ai-sdk/google": "^3.0.0",
- "@ai-sdk/google-vertex": "^4.0.16",
- "@ai-sdk/openai": "^3.0.0",
- "@ai-sdk/react": "^3.0.1",
+ "@ai-sdk/amazon-bedrock": "^4.0.191",
+ "@ai-sdk/anthropic": "^3.0.127",
+ "@ai-sdk/azure": "^3.0.133",
+ "@ai-sdk/deepseek": "^2.0.71",
+ "@ai-sdk/google": "^3.0.130",
+ "@ai-sdk/google-vertex": "^4.0.210",
+ "@ai-sdk/openai": "^3.0.124",
+ "@ai-sdk/openai-compatible": "^2.0.81",
+ "@ai-sdk/react": "^3.0.303",
+ "@aihubmix/ai-sdk-provider": "^2.2.1",
"@aws-sdk/client-dynamodb": "^3.957.0",
"@aws-sdk/credential-providers": "^3.943.0",
"@extractus/article-extractor": "^8.0.18",
- "@formatjs/intl-localematcher": "^0.8.0",
+ "@formatjs/intl-localematcher": "^0.9.0",
"@langfuse/client": "^4.4.9",
"@langfuse/otel": "^4.4.4",
"@langfuse/tracing": "^4.4.9",
"@next/third-parties": "^16.0.6",
"@opennextjs/cloudflare": "^1.17.1",
- "@openrouter/ai-sdk-provider": "^1.5.4",
+ "@openrouter/ai-sdk-provider": "^2.10.0",
"@opentelemetry/api": "^1.9.0",
- "@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
"@opentelemetry/sdk-trace-node": "^2.2.0",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-collapsible": "^1.1.12",
@@ -65,7 +66,7 @@
"@radix-ui/react-tooltip": "^1.1.8",
"@radix-ui/react-use-controllable-state": "^1.2.2",
"@xmldom/xmldom": "^0.9.8",
- "ai": "^6.0.1",
+ "ai": "^6.0.300",
"base-64": "^1.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -77,7 +78,7 @@
"nanoid": "^5.0.0",
"negotiator": "^1.0.0",
"next": "^16.0.7",
- "ollama-ai-provider-v2": "^2.0.0",
+ "ollama-ai-provider-v2": "^3.6.0",
"pako": "^2.1.0",
"prism-react-renderer": "^2.4.1",
"react": "^19.1.2",
@@ -87,6 +88,7 @@
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"remark-gfm": "^4.0.1",
+ "saxes": "^6.0.0",
"server-only": "^0.0.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.0.2",
@@ -108,7 +110,7 @@
},
"devDependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
- "@biomejs/biome": "2.4.10",
+ "@biomejs/biome": "2.5.7",
"@playwright/test": "^1.57.0",
"@tailwindcss/postcss": "^4",
"@tailwindcss/typography": "^0.5.19",
@@ -127,8 +129,8 @@
"cross-env": "^10.1.0",
"electron": "^39.2.7",
"electron-builder": "^26.0.12",
- "esbuild": "^0.27.2",
- "eslint": "9.39.4",
+ "esbuild": "^0.28.0",
+ "eslint": "9.39.5",
"eslint-config-next": "16.1.6",
"husky": "^9.1.7",
"jsdom": "^27.4.0",
diff --git a/packages/claude-plugin/README.md b/packages/claude-plugin/README.md
index db4de05b..752fabbc 100644
--- a/packages/claude-plugin/README.md
+++ b/packages/claude-plugin/README.md
@@ -25,10 +25,13 @@ claude mcp add drawio -- npx @next-ai-drawio/mcp-server@latest
## Features
- **Real-time Preview**: Diagrams appear and update in your browser as Claude creates them
-- **Version History**: Restore previous diagram versions with visual thumbnails
+- **Drawing Rules and Shape Libraries**: Claude gets the web app's layout and style rules and the icon docs for AWS, Azure, GCP, Kubernetes and more
+- **Self-check**: Claude can take a screenshot of the rendered diagram and fix what looks wrong
+- **Version History**: Restore one of the last 20 versions from the **History** button, shown as thumbnails
- **Natural Language**: Describe diagrams in plain text - flowcharts, architecture diagrams, etc.
-- **Edit Support**: Modify existing diagrams with natural language instructions
-- **Export**: Save diagrams as `.drawio` files
+- **Edit Support**: Modify existing diagrams with natural language instructions, including your own edits in the browser
+- **Export**: Save diagrams as `.drawio`, `.png`, `.svg`, or `.drawio.svg` files
+- **Auto-save**: Each diagram is saved to `~/.next-ai-drawio/`, so `claude --resume` can pick it up again
- **Self-contained**: Embedded server, no external dependencies required
## Use Case Examples
@@ -72,11 +75,16 @@ between user, client app, auth server, and resource server
| Tool | Description |
|------|-------------|
-| `start_session` | Opens browser with real-time diagram preview |
+| `start_session` | Opens browser with real-time diagram preview; the result includes the drawing rules |
+| `get_drawing_guide` | Return the drawing rules again |
+| `get_shape_library` | Return the shapes and icon styles of a library such as `aws4` |
| `create_new_diagram` | Create a new diagram from XML |
-| `edit_diagram` | Edit diagram by ID-based operations |
+| `load_diagram` | Load a `.drawio` file from disk |
+| `edit_diagram` | Edit diagram by ID-based operations; all or nothing |
| `get_diagram` | Get the current diagram XML |
-| `export_diagram` | Save diagram to a `.drawio` file |
+| `screenshot_diagram` | Return a PNG of a page so Claude can check the result |
+| `export_diagram` | Save diagram to a `.drawio`, `.png`, `.svg`, or `.drawio.svg` file |
+| `list_pages`, `add_page`, `rename_page`, `delete_page` | Work with multi-page diagrams |
## How It Works
@@ -95,6 +103,8 @@ Claude Code <--stdio--> MCP Server <--http--> Browser (draw.io)
|----------|---------|-------------|
| `PORT` | `6002` | Port for the embedded HTTP server |
| `DRAWIO_BASE_URL` | `https://embed.diagrams.net` | Base URL for draw.io (for self-hosted deployments) |
+| `DRAWIO_DATA_DIR` | `~/.next-ai-drawio` | Folder for auto-saved diagrams; `off` turns auto-save off |
+| `DEBUG` | unset | Set to `true` to log debug messages |
## Links
diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md
index 4298689d..968688a2 100644
--- a/packages/mcp-server/README.md
+++ b/packages/mcp-server/README.md
@@ -104,21 +104,40 @@ Use the standard MCP configuration with:
## Features
- **Real-time Preview**: Diagrams appear and update in your browser as the AI creates them
-- **Version History**: Restore previous diagram versions with visual thumbnails - click the clock button (bottom-right) to browse and restore earlier states
-- **Natural Language**: Describe diagrams in plain text - flowcharts, architecture diagrams, etc.
-- **Edit Support**: Modify existing diagrams with natural language instructions
-- **Export**: Save diagrams as `.drawio` files
+- **Drawing Rules**: The AI gets the same layout, edge and style rules as the web app, plus the shape library docs (AWS, Azure, GCP, Kubernetes, Cisco and more), so it uses real icon names instead of guessing
+- **Self-check**: The AI can take a screenshot of the rendered diagram and fix overlapping shapes or edges that cross shapes
+- **Edit Support**: Modify existing diagrams with natural language instructions. If any change in an edit fails, nothing is written and the AI gets the reason and the current page XML
+- **Your Edits Are Kept**: Changes you make in the browser are read before the AI edits again. If the AI overwrites a change you were still making, your version is saved in History
+- **Version History**: Click **History** at the top right of the preview page to restore one of the last 20 versions, shown as thumbnails
+- **Download and Export**: Save as `.drawio`, `.png`, `.svg`, or `.drawio.svg` (an SVG with the diagram embedded, which draw.io can open and edit again), from the **Download** button or through `export_diagram`
+- **Multi-page**: List, add, rename, and delete pages, and edit any page
+- **Auto-save**: Each session's diagram is saved to `~/.next-ai-drawio/.drawio`, so it survives a restart of the MCP client
+- **Themes and Dark Mode**: Pick a draw.io theme under **Extras > Theme**; the page follows the system dark mode
- **Self-contained**: Embedded server, works offline (except draw.io UI which loads from `embed.diagrams.net` by default, configurable via `DRAWIO_BASE_URL`)
## Available Tools
| Tool | Description |
|------|-------------|
-| `start_session` | Opens browser with real-time diagram preview |
-| `create_new_diagram` | Create a new diagram from XML (requires `xml` argument) |
-| `edit_diagram` | Edit diagram by ID-based operations (update/add/delete cells) |
-| `get_diagram` | Get the current diagram XML |
-| `export_diagram` | Save diagram to a `.drawio` file |
+| `start_session` | Opens browser with real-time diagram preview; the result includes the drawing rules |
+| `get_drawing_guide` | Return the drawing rules again, for example after a long conversation was compacted |
+| `get_shape_library` | Return the shapes and icon styles of a library such as `aws4`, `azure2`, or `kubernetes` |
+| `create_new_diagram` | Create a new diagram from XML; a plain list of `mxCell` elements is enough |
+| `load_diagram` | Load a `.drawio` file from disk into the session (handles compressed files) |
+| `edit_diagram` | Edit diagram by ID-based operations (update/add/delete cells); all or nothing |
+| `get_diagram` | Get the current diagram XML, including your edits in the browser |
+| `screenshot_diagram` | Return a PNG of a page so the AI can check the rendered diagram |
+| `export_diagram` | Save diagram to a `.drawio`, `.png`, `.svg`, or `.drawio.svg` file |
+| `list_pages` | List every page (tab) with id, name, index, and cell count |
+| `add_page` | Append a new page without touching existing ones |
+| `rename_page` | Rename a page |
+| `delete_page` | Delete a page (refuses to delete the last one) |
+
+## Continue a Diagram Later
+
+After every change, the diagram is saved as a normal `.drawio` file in `~/.next-ai-drawio/`, and `start_session` tells the AI the file path. When you resume a conversation after restarting your MCP client (for example `claude --resume`), the AI calls `start_session` and then `load_diagram` with that path. You can also open the file in draw.io yourself.
+
+The newest 50 files are kept. Set `DRAWIO_DATA_DIR` to use another folder, or to `off` to turn auto-save off.
## How It Works
@@ -149,6 +168,8 @@ Use the standard MCP configuration with:
|----------|---------|-------------|
| `PORT` | `6002` | Port for the embedded HTTP server |
| `DRAWIO_BASE_URL` | `https://embed.diagrams.net` | Base URL for the draw.io embed. Set this to use a self-hosted draw.io instance for private deployments. |
+| `DRAWIO_DATA_DIR` | `~/.next-ai-drawio` | Folder for the auto-saved `.drawio` files. Set to `off` to turn auto-save off. |
+| `DEBUG` | unset | Set to `true` to log debug messages to stderr. |
### Private Deployment (Self-hosted draw.io)
@@ -174,7 +195,7 @@ You can deploy your own draw.io instance using the official Docker image:
docker run -d -p 8080:8080 jgraph/drawio
```
-Then set `DRAWIO_BASE_URL=http://localhost:8080` (or your server's URL).
+Then set `DRAWIO_BASE_URL=http://localhost:8080` (or your server's URL). The preview page loads nothing else from the internet, so with a local draw.io it works offline. One exception: shapes from the Material Design library show icons from `fonts.gstatic.com`.
## Troubleshooting
@@ -203,6 +224,10 @@ Call `start_session` first to open the browser window.
Check that the browser URL has the `?mcp=` query parameter. The MCP session ID connects the browser to the server.
+### Screenshot or PNG/SVG export times out
+
+PNG and SVG files are rendered by draw.io in the preview tab. Browsers slow down tabs that stay in the background, so the tab may not answer in time. Bring the preview tab to the front and try again.
+
## License
Apache-2.0
diff --git a/packages/mcp-server/package-lock.json b/packages/mcp-server/package-lock.json
index e6afbe5e..fb2ded77 100644
--- a/packages/mcp-server/package-lock.json
+++ b/packages/mcp-server/package-lock.json
@@ -1,17 +1,19 @@
{
"name": "@next-ai-drawio/mcp-server",
- "version": "0.1.17",
+ "version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@next-ai-drawio/mcp-server",
- "version": "0.1.17",
+ "version": "0.3.0",
"license": "Apache-2.0",
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.0.4",
+ "@modelcontextprotocol/sdk": "^1.31.0",
"linkedom": "^0.18.0",
"open": "^11.0.0",
+ "pako": "^2.2.0",
+ "saxes": "^6.0.0",
"zod": "^4.0.0"
},
"bin": {
@@ -19,17 +21,53 @@
},
"devDependencies": {
"@types/node": "^24.0.0",
+ "@types/pako": "^2.0.4",
"tsx": "^4.19.0",
- "typescript": "^5"
+ "typescript": "^5",
+ "vitest": "^4.1.8"
},
"engines": {
"node": ">=18"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
- "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [
"ppc64"
],
@@ -44,9 +82,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
- "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [
"arm"
],
@@ -61,9 +99,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
- "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [
"arm64"
],
@@ -78,9 +116,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
- "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [
"x64"
],
@@ -95,9 +133,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
- "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [
"arm64"
],
@@ -112,9 +150,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
- "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [
"x64"
],
@@ -129,9 +167,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
- "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [
"arm64"
],
@@ -146,9 +184,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
- "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [
"x64"
],
@@ -163,9 +201,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
- "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [
"arm"
],
@@ -180,9 +218,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
- "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [
"arm64"
],
@@ -197,9 +235,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
- "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [
"ia32"
],
@@ -214,9 +252,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
- "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [
"loong64"
],
@@ -231,9 +269,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
- "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [
"mips64el"
],
@@ -248,9 +286,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
- "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [
"ppc64"
],
@@ -265,9 +303,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
- "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [
"riscv64"
],
@@ -282,9 +320,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
- "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [
"s390x"
],
@@ -299,9 +337,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
- "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [
"x64"
],
@@ -316,9 +354,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
- "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
@@ -333,9 +371,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
- "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [
"x64"
],
@@ -350,9 +388,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
- "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [
"arm64"
],
@@ -367,9 +405,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
- "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [
"x64"
],
@@ -384,9 +422,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
- "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [
"arm64"
],
@@ -401,9 +439,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
- "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [
"x64"
],
@@ -418,9 +456,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
- "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [
"arm64"
],
@@ -435,9 +473,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
- "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [
"ia32"
],
@@ -452,9 +490,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
- "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [
"x64"
],
@@ -480,13 +518,20 @@
"hono": "^4"
}
},
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@modelcontextprotocol/sdk": {
- "version": "1.29.0",
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
- "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==",
+ "version": "1.31.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.31.0.tgz",
+ "integrity": "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw==",
"license": "MIT",
"dependencies": {
- "@hono/node-server": "^1.19.9",
+ "@hono/node-server": "^1.19.9 || ^2.0.5",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"content-type": "^1.0.5",
@@ -520,14 +565,470 @@
}
}
},
- "node_modules/@types/node": {
- "version": "24.12.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
- "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.133.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
+ "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
+ "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
+ "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
+ "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
+ "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
+ "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
+ "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
+ "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
+ "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
+ "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
+ "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
+ "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
+ "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
+ "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
+ "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
+ "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~7.16.0"
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.19.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz",
+ "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": ">=7.24.0 <7.24.7"
+ }
+ },
+ "node_modules/@types/pako": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
+ "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz",
+ "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz",
+ "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.11",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz",
+ "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz",
+ "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.11",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz",
+ "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz",
+ "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz",
+ "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.11",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
}
},
"node_modules/accepts": {
@@ -576,6 +1077,16 @@
}
}
},
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/body-parser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
@@ -601,10 +1112,17 @@
}
},
"node_modules/boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "license": "ISC"
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-2.0.0.tgz",
+ "integrity": "sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
},
"node_modules/bundle-name": {
"version": "4.1.0",
@@ -659,6 +1177,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/content-disposition": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
@@ -681,6 +1209,13 @@
"node": ">= 0.6"
}
},
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@@ -727,30 +1262,114 @@
}
},
"node_modules/css-select": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
- "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-7.0.0.tgz",
+ "integrity": "sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==",
"license": "BSD-2-Clause",
"dependencies": {
- "boolbase": "^1.0.0",
- "css-what": "^6.1.0",
- "domhandler": "^5.0.2",
- "domutils": "^3.0.1",
- "nth-check": "^2.0.1"
+ "boolbase": "^2.0.0",
+ "css-what": "^8.0.0",
+ "domhandler": "^6.0.1",
+ "domutils": "^4.0.2",
+ "nth-check": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=20.19.0"
},
"funding": {
+ "type": "github",
"url": "https://github.com/sponsors/fb55"
}
},
- "node_modules/css-what": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
- "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
- "license": "BSD-2-Clause",
+ "node_modules/css-select/node_modules/dom-serializer": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
+ "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0",
+ "entities": "^8.0.0"
+ },
"engines": {
- "node": ">= 6"
+ "node": ">=20.19.0"
},
"funding": {
+ "type": "github",
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/css-select/node_modules/domelementtype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
+ "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/css-select/node_modules/domhandler": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
+ "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/css-select/node_modules/domutils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
+ "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^3.0.0",
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/css-select/node_modules/entities": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+ "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-8.0.0.tgz",
+ "integrity": "sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
"url": "https://github.com/sponsors/fb55"
}
},
@@ -778,9 +1397,9 @@
}
},
"node_modules/default-browser": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz",
- "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==",
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
+ "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
"license": "MIT",
"dependencies": {
"bundle-name": "^4.1.0",
@@ -826,6 +1445,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -940,6 +1569,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-module-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+ "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -953,9 +1589,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.27.1",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
- "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -966,32 +1602,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.1",
- "@esbuild/android-arm": "0.27.1",
- "@esbuild/android-arm64": "0.27.1",
- "@esbuild/android-x64": "0.27.1",
- "@esbuild/darwin-arm64": "0.27.1",
- "@esbuild/darwin-x64": "0.27.1",
- "@esbuild/freebsd-arm64": "0.27.1",
- "@esbuild/freebsd-x64": "0.27.1",
- "@esbuild/linux-arm": "0.27.1",
- "@esbuild/linux-arm64": "0.27.1",
- "@esbuild/linux-ia32": "0.27.1",
- "@esbuild/linux-loong64": "0.27.1",
- "@esbuild/linux-mips64el": "0.27.1",
- "@esbuild/linux-ppc64": "0.27.1",
- "@esbuild/linux-riscv64": "0.27.1",
- "@esbuild/linux-s390x": "0.27.1",
- "@esbuild/linux-x64": "0.27.1",
- "@esbuild/netbsd-arm64": "0.27.1",
- "@esbuild/netbsd-x64": "0.27.1",
- "@esbuild/openbsd-arm64": "0.27.1",
- "@esbuild/openbsd-x64": "0.27.1",
- "@esbuild/openharmony-arm64": "0.27.1",
- "@esbuild/sunos-x64": "0.27.1",
- "@esbuild/win32-arm64": "0.27.1",
- "@esbuild/win32-ia32": "0.27.1",
- "@esbuild/win32-x64": "0.27.1"
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/escape-html": {
@@ -1000,6 +1636,16 @@
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -1030,6 +1676,16 @@
"node": ">=18.0.0"
}
},
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/express": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
@@ -1113,6 +1769,24 @@
],
"license": "BSD-3-Clause"
},
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/finalhandler": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -1213,19 +1887,6 @@
"node": ">= 0.4"
}
},
- "node_modules/get-tsconfig": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
- "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1278,9 +1939,9 @@
"license": "MIT"
},
"node_modules/htmlparser2": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
- "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
+ "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -1292,14 +1953,14 @@
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
- "domutils": "^3.2.1",
- "entities": "^6.0.0"
+ "domutils": "^3.2.2",
+ "entities": "^7.0.1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -1461,16 +2122,277 @@
"integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
"license": "BSD-2-Clause"
},
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
"node_modules/linkedom": {
- "version": "0.18.12",
- "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz",
- "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==",
+ "version": "0.18.13",
+ "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.13.tgz",
+ "integrity": "sha512-ES/o9qotMpzpN2MHs+Iq/JcVoOj8Fa5wiQYrTdFpvAnwXL0g66XHHUc9WUMk6nAlBtGsFQ24ne+SYnvnaQ2FSw==",
"license": "ISC",
"dependencies": {
- "css-select": "^5.1.0",
+ "css-select": "^7.0.0",
"cssom": "^0.5.0",
"html-escaper": "^3.0.3",
- "htmlparser2": "^10.0.0",
+ "htmlparser2": "^10.1.0",
"uhyphen": "^0.2.0"
},
"engines": {
@@ -1485,6 +2407,16 @@
}
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -1546,6 +2478,25 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
@@ -1556,14 +2507,18 @@
}
},
"node_modules/nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-3.0.1.tgz",
+ "integrity": "sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==",
"license": "BSD-2-Clause",
"dependencies": {
- "boolbase": "^1.0.0"
+ "boolbase": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
},
"funding": {
+ "type": "github",
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
@@ -1588,6 +2543,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/obug": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz",
+ "integrity": "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -1610,17 +2579,17 @@
}
},
"node_modules/open": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz",
- "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==",
+ "version": "11.0.4",
+ "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz",
+ "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==",
"license": "MIT",
"dependencies": {
- "default-browser": "^5.4.0",
+ "default-browser": "^5.5.1",
"define-lazy-prop": "^3.0.0",
"is-in-ssh": "^1.0.0",
"is-inside-container": "^1.0.0",
- "powershell-utils": "^0.1.0",
- "wsl-utils": "^0.3.0"
+ "powershell-utils": "^0.2.1",
+ "wsl-utils": "^1.0.0"
},
"engines": {
"node": ">=20"
@@ -1629,6 +2598,34 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/open/node_modules/powershell-utils": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz",
+ "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pako": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz",
+ "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1657,6 +2654,33 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/pkce-challenge": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
@@ -1666,6 +2690,35 @@
"node": ">=16.20.0"
}
},
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
"node_modules/powershell-utils": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz",
@@ -1739,14 +2792,38 @@
"node": ">=0.10.0"
}
},
- "node_modules/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "node_modules/rolldown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
+ "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
"dev": true,
"license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ "dependencies": {
+ "@oxc-project/types": "=0.133.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.3",
+ "@rolldown/binding-darwin-arm64": "1.0.3",
+ "@rolldown/binding-darwin-x64": "1.0.3",
+ "@rolldown/binding-freebsd-x64": "1.0.3",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.3",
+ "@rolldown/binding-linux-arm64-musl": "1.0.3",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.3",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-gnu": "1.0.3",
+ "@rolldown/binding-linux-x64-musl": "1.0.3",
+ "@rolldown/binding-openharmony-arm64": "1.0.3",
+ "@rolldown/binding-wasm32-wasi": "1.0.3",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.3",
+ "@rolldown/binding-win32-x64-msvc": "1.0.3"
}
},
"node_modules/router": {
@@ -1783,6 +2860,18 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
"node_modules/send": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
@@ -1927,6 +3016,30 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
@@ -1936,6 +3049,57 @@
"node": ">= 0.8"
}
},
+ "node_modules/std-env": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -1945,15 +3109,22 @@
"node": ">=0.6"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/tsx": {
- "version": "4.21.0",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
- "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "version": "4.23.15",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz",
+ "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "~0.27.0",
- "get-tsconfig": "^4.7.5"
+ "esbuild": "~0.28.0"
},
"bin": {
"tsx": "dist/cli.mjs"
@@ -2000,9 +3171,9 @@
"license": "ISC"
},
"node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"dev": true,
"license": "MIT"
},
@@ -2024,6 +3195,174 @@
"node": ">= 0.8"
}
},
+ "node_modules/vite": {
+ "version": "8.0.16",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
+ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.15",
+ "rolldown": "1.0.3",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.18",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz",
+ "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.11",
+ "@vitest/mocker": "4.1.11",
+ "@vitest/pretty-format": "4.1.11",
+ "@vitest/runner": "4.1.11",
+ "@vitest/snapshot": "4.1.11",
+ "@vitest/spy": "4.1.11",
+ "@vitest/utils": "4.1.11",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.11",
+ "@vitest/browser-preview": "4.1.11",
+ "@vitest/browser-webdriverio": "4.1.11",
+ "@vitest/coverage-istanbul": "4.1.11",
+ "@vitest/coverage-v8": "4.1.11",
+ "@vitest/ui": "4.1.11",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -2039,6 +3378,23 @@
"node": ">= 8"
}
},
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -2046,9 +3402,9 @@
"license": "ISC"
},
"node_modules/wsl-utils": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.0.tgz",
- "integrity": "sha512-3sFIGLiaDP7rTO4xh3g+b3AzhYDIUGGywE/WsmqzJWDxus5aJXVnPTNC/6L+r2WzrwXqVOdD262OaO+cEyPMSQ==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz",
+ "integrity": "sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==",
"license": "MIT",
"dependencies": {
"is-wsl": "^3.1.0",
@@ -2061,10 +3417,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "license": "MIT"
+ },
"node_modules/zod": {
- "version": "4.3.6",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
- "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "version": "4.6.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
+ "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index 2690aaf5..9349eb6f 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@next-ai-drawio/mcp-server",
- "version": "0.1.17",
+ "version": "0.3.0",
"description": "MCP server for Next AI Draw.io - AI-powered diagram generation with real-time browser preview",
"type": "module",
"main": "dist/index.js",
@@ -8,9 +8,12 @@
"next-ai-drawio-mcp": "./dist/index.js"
},
"scripts": {
- "build": "tsc",
+ "build": "tsc && node scripts/copy-assets.mjs",
+ "check-package": "node scripts/check-package.mjs",
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
+ "test": "vitest run",
+ "test:watch": "vitest",
"prepublishOnly": "npm run build"
},
"keywords": [
@@ -36,15 +39,19 @@
"access": "public"
},
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.0.4",
+ "@modelcontextprotocol/sdk": "^1.31.0",
"linkedom": "^0.18.0",
"open": "^11.0.0",
+ "pako": "^2.2.0",
+ "saxes": "^6.0.0",
"zod": "^4.0.0"
},
"devDependencies": {
"@types/node": "^24.0.0",
+ "@types/pako": "^2.0.4",
"tsx": "^4.19.0",
- "typescript": "^5"
+ "typescript": "^5",
+ "vitest": "^4.1.8"
},
"engines": {
"node": ">=18"
diff --git a/packages/mcp-server/scripts/check-package.mjs b/packages/mcp-server/scripts/check-package.mjs
new file mode 100644
index 00000000..a739ea32
--- /dev/null
+++ b/packages/mcp-server/scripts/check-package.mjs
@@ -0,0 +1,25 @@
+// Fail if the npm package would miss files the server reads at runtime.
+// Tests run from src/ (tsx) and cannot notice a broken dist/ copy step.
+// Run after `npm run build`.
+import { execSync } from "node:child_process"
+
+const REQUIRED = [
+ "dist/index.js",
+ "dist/shape-libraries/aws4.md",
+ "dist/preview/index.html",
+ "dist/preview/preview.css",
+ "dist/preview/preview.js",
+]
+
+const output = JSON.parse(
+ execSync("npm pack --dry-run --json", { encoding: "utf8" }),
+)
+// npm 11 prints a list, npm 12 an object keyed by package name
+const pack = Array.isArray(output) ? output[0] : Object.values(output)[0]
+const files = new Set(pack.files.map((f) => f.path))
+const missing = REQUIRED.filter((f) => !files.has(f))
+if (missing.length > 0) {
+ console.error(`npm package is missing: ${missing.join(", ")}`)
+ process.exit(1)
+}
+console.log(`npm package OK (${files.size} files)`)
diff --git a/packages/mcp-server/scripts/copy-assets.mjs b/packages/mcp-server/scripts/copy-assets.mjs
new file mode 100644
index 00000000..67f8e0e8
--- /dev/null
+++ b/packages/mcp-server/scripts/copy-assets.mjs
@@ -0,0 +1,22 @@
+// Copy non-TypeScript assets into dist/ after tsc, so they ship in the npm
+// package ("files": ["dist"]).
+import { cpSync, mkdirSync, readdirSync } from "node:fs"
+import { dirname, join } from "node:path"
+import { fileURLToPath } from "node:url"
+
+const pkg = join(dirname(fileURLToPath(import.meta.url)), "..")
+
+// Shape library docs live at the repository root, shared with the web app
+const libSrc = join(pkg, "../../docs/shape-libraries")
+const libDest = join(pkg, "dist/shape-libraries")
+mkdirSync(libDest, { recursive: true })
+for (const file of readdirSync(libSrc)) {
+ if (file.endsWith(".md") && file !== "README.md") {
+ cpSync(join(libSrc, file), join(libDest, file))
+ }
+}
+
+// Browser preview page (HTML, CSS and script)
+cpSync(join(pkg, "src/preview"), join(pkg, "dist/preview"), {
+ recursive: true,
+})
diff --git a/packages/mcp-server/src/diagram-operations.ts b/packages/mcp-server/src/diagram-operations.ts
index 93225fc1..5315f191 100644
--- a/packages/mcp-server/src/diagram-operations.ts
+++ b/packages/mcp-server/src/diagram-operations.ts
@@ -1,8 +1,17 @@
/**
* ID-based diagram operations
- * Copied from lib/utils.ts to avoid cross-package imports
+ *
+ * The xmlContent argument may be either a bare (legacy) or a
+ * full with one or more pages. For mxfile inputs, an
+ * optional pageSelector identifies which page to edit; when omitted, the
+ * first page is targeted (the "active page by convention" — see pages.ts).
*/
+import { decompressPageContent } from "./load-diagram.ts"
+import { log } from "./logger.ts"
+import { findPageElement, hasPageSelector, type PageSelector } from "./pages.ts"
+import { getXmlSyntaxError } from "./xml-syntax.ts"
+
export interface DiagramOperation {
operation: "update" | "add" | "delete"
cell_id: string
@@ -20,26 +29,75 @@ export interface ApplyOperationsResult {
errors: OperationError[]
}
+// Cells with links, tooltips or custom data are stored as
+// (or ): the id sits
+// on the wrapper, so the wrapper is treated as the cell.
+const CELL_SELECTOR = "mxCell, UserObject, object"
+
+/**
+ * Return the of a page, creating it when missing. An empty
+ * page gets a blank model with the "0" and "1" root cells; a page whose text
+ * is draw.io's compressed format is decompressed in place. Returns null if
+ * the text is neither empty nor decompressible.
+ */
+function ensurePageRoot(doc: Document, page: Element): Element | null {
+ const existing = page.querySelector("root")
+ if (existing) return existing
+
+ let model = page.querySelector("mxGraphModel")
+ if (!model) {
+ const text = page.textContent?.trim() ?? ""
+ if (text) {
+ const xml = decompressPageContent(text)
+ if (!xml || getXmlSyntaxError(xml)) return null
+ const parsed = new DOMParser().parseFromString(xml, "text/xml")
+ if (parsed.documentElement?.tagName !== "mxGraphModel") return null
+ page.textContent = ""
+ model = page.appendChild(
+ doc.importNode(parsed.documentElement, true),
+ ) as Element
+ const decompressedRoot = model.querySelector("root")
+ if (decompressedRoot) return decompressedRoot
+ } else {
+ model = page.appendChild(doc.createElement("mxGraphModel"))
+ }
+ }
+
+ const blank = new DOMParser().parseFromString(
+ ` `,
+ "text/xml",
+ )
+ return model.appendChild(
+ doc.importNode(blank.documentElement, true),
+ ) as Element
+}
+
+/** Read parent/source/target, which a wrapped cell keeps on its inner mxCell. */
+function cellAttr(cell: Element, name: string): string | null {
+ const inner =
+ cell.tagName === "mxCell" ? cell : cell.querySelector("mxCell")
+ return inner?.getAttribute(name) ?? null
+}
+
/**
* Apply diagram operations (update/add/delete) using ID-based lookup.
- * This replaces the text-matching approach with direct DOM manipulation.
*
- * @param xmlContent - The full mxfile XML content
- * @param operations - Array of operations to apply
- * @returns Object with result XML and any errors
+ * @param xmlContent - The diagram XML. May be either a bare or
+ * a full with one or more children.
+ * @param operations - Array of operations to apply.
+ * @param pageSelector - Optional page selector for multi-page docs. Defaults
+ * to the first page.
+ * @returns Object with result XML (same shape as input) and any per-op errors.
*/
export function applyDiagramOperations(
xmlContent: string,
operations: DiagramOperation[],
+ pageSelector?: PageSelector,
): ApplyOperationsResult {
const errors: OperationError[] = []
- // Parse the XML
- const parser = new DOMParser()
- const doc = parser.parseFromString(xmlContent, "text/xml")
-
- // Check for parse errors
- const parseError = doc.querySelector("parsererror")
+ // Check for syntax errors, then parse the XML
+ const parseError = getXmlSyntaxError(xmlContent)
if (parseError) {
return {
result: xmlContent,
@@ -47,33 +105,94 @@ export function applyDiagramOperations(
{
type: "update",
cellId: "",
- message: `XML parse error: ${parseError.textContent}`,
+ message: `XML parse error: ${parseError}`,
},
],
}
}
+ const parser = new DOMParser()
+ const doc = parser.parseFromString(xmlContent, "text/xml")
- // Find the root element (inside mxGraphModel)
- const root = doc.querySelector("root")
- if (!root) {
- return {
- result: xmlContent,
- errors: [
- {
- type: "update",
- cellId: "",
- message: "Could not find element in XML",
- },
- ],
+ // Locate the element to operate on.
+ //
+ // - For input: resolve the page via pageSelector, then dive into
+ // its . This scopes querySelectorAll calls below to one page so
+ // cells on other pages aren't accidentally matched.
+ // - For bare input: use the document's only .
+ let root: Element | null
+ if (doc.documentElement?.tagName === "mxfile") {
+ const found = findPageElement(doc as unknown as Document, pageSelector)
+ if (!found) {
+ const selDesc = hasPageSelector(pageSelector)
+ ? ` matching selector ${JSON.stringify(pageSelector)}`
+ : ""
+ return {
+ result: xmlContent,
+ errors: [
+ {
+ type: "update",
+ cellId: "",
+ message: `Page${selDesc} not found in `,
+ },
+ ],
+ }
+ }
+ root = ensurePageRoot(doc as unknown as Document, found.element)
+ if (!root) {
+ const pageId =
+ found.element.getAttribute("id") || `(index ${found.index})`
+ return {
+ result: xmlContent,
+ errors: [
+ {
+ type: "update",
+ cellId: "",
+ message: `Page "${pageId}" has no element and its content could not be decompressed`,
+ },
+ ],
+ }
+ }
+ } else {
+ if (hasPageSelector(pageSelector)) {
+ return {
+ result: xmlContent,
+ errors: [
+ {
+ type: "update",
+ cellId: "",
+ message:
+ "Page selector provided but document is not multi-page (no wrapper). Use create_new_diagram with a full first, or omit the page selector.",
+ },
+ ],
+ }
+ }
+ root = doc.querySelector("root")
+ if (!root) {
+ return {
+ result: xmlContent,
+ errors: [
+ {
+ type: "update",
+ cellId: "",
+ message: "Could not find element in XML",
+ },
+ ],
+ }
}
}
- // Build a map of cell IDs to elements
+ // Build a map of cell IDs to elements (scoped to the resolved page).
const cellMap = new Map()
- root.querySelectorAll("mxCell").forEach((cell) => {
+ root.querySelectorAll(CELL_SELECTOR).forEach((cell) => {
const id = cell.getAttribute("id")
- if (id) cellMap.set(id, cell)
+ // A wrapped mxCell may repeat its wrapper's id; the wrapper is the cell
+ const wrapped =
+ cell.tagName === "mxCell" &&
+ /^(UserObject|object)$/.test(cell.parentElement?.tagName ?? "")
+ if (id && !wrapped) cellMap.set(id, cell)
})
+ // Ids deleted so far in this batch; deleting one again is a no-op
+ const deletedIds = new Set()
// Process each operation
for (const op of operations) {
@@ -102,7 +221,7 @@ export function applyDiagramOperations(
`${op.new_xml} `,
"text/xml",
)
- const newCell = newDoc.querySelector("mxCell")
+ const newCell = newDoc.querySelector(CELL_SELECTOR)
if (!newCell) {
errors.push({
type: "update",
@@ -154,7 +273,7 @@ export function applyDiagramOperations(
`${op.new_xml} `,
"text/xml",
)
- const newCell = newDoc.querySelector("mxCell")
+ const newCell = newDoc.querySelector(CELL_SELECTOR)
if (!newCell) {
errors.push({
type: "add",
@@ -194,8 +313,15 @@ export function applyDiagramOperations(
const existingCell = cellMap.get(op.cell_id)
if (!existingCell) {
- // Cell not found - might have been cascade-deleted by a previous operation
- // Skip silently instead of erroring (AI may redundantly list children/edges)
+ // Skip cells already cascade-deleted by a previous operation
+ // (AI may redundantly list children/edges); warn otherwise
+ if (!deletedIds.has(op.cell_id)) {
+ errors.push({
+ type: "delete",
+ cellId: op.cell_id,
+ message: `Cell with id="${op.cell_id}" not found`,
+ })
+ }
continue
}
@@ -208,15 +334,17 @@ export function applyDiagramOperations(
cellsToDelete.add(cellId)
// Find children (cells where parent === cellId)
- const children = root.querySelectorAll(
- `mxCell[parent="${cellId}"]`,
- )
- children.forEach((child) => {
- const childId = child.getAttribute("id")
- if (childId && childId !== "0" && childId !== "1") {
+ // cellMap only holds this page's cells, so other pages' cells
+ // with the same parent id (notably "1") are never touched.
+ for (const [childId, child] of cellMap) {
+ if (
+ childId !== "0" &&
+ childId !== "1" &&
+ cellAttr(child, "parent") === cellId
+ ) {
collectDescendants(childId)
}
- })
+ }
}
// Collect the target cell and all its descendants
@@ -225,23 +353,23 @@ export function applyDiagramOperations(
// Find edges referencing any of the cells to be deleted
// Also recursively collect children of those edges (e.g., edge labels)
for (const cellId of cellsToDelete) {
- const referencingEdges = root.querySelectorAll(
- `mxCell[source="${cellId}"], mxCell[target="${cellId}"]`,
- )
- referencingEdges.forEach((edge) => {
- const edgeId = edge.getAttribute("id")
+ for (const [edgeId, edge] of cellMap) {
// Protect root cells from being added via edge references
- if (edgeId && edgeId !== "0" && edgeId !== "1") {
+ if (edgeId === "0" || edgeId === "1") continue
+ if (
+ cellAttr(edge, "source") === cellId ||
+ cellAttr(edge, "target") === cellId
+ ) {
// Recurse to collect edge's children (like labels)
collectDescendants(edgeId)
}
- })
+ }
}
- // Log what will be deleted
+ // Log what will be deleted (stderr: stdout carries JSON-RPC)
if (cellsToDelete.size > 1) {
- console.log(
- `[applyDiagramOperations] Cascade delete "${op.cell_id}" → deleting ${cellsToDelete.size} cells: ${Array.from(cellsToDelete).join(", ")}`,
+ log.debug(
+ `Cascade delete "${op.cell_id}" → deleting ${cellsToDelete.size} cells: ${Array.from(cellsToDelete).join(", ")}`,
)
}
@@ -252,6 +380,7 @@ export function applyDiagramOperations(
cell.parentNode?.removeChild(cell)
cellMap.delete(cellId)
}
+ deletedIds.add(cellId)
}
}
}
diff --git a/packages/mcp-server/src/dom.ts b/packages/mcp-server/src/dom.ts
new file mode 100644
index 00000000..13972b3a
--- /dev/null
+++ b/packages/mcp-server/src/dom.ts
@@ -0,0 +1,98 @@
+/**
+ * DOM setup for Node. The XML helpers use the global DOMParser and
+ * XMLSerializer, which the browser has and Node gets from here.
+ *
+ * linkedom gives us a DOM with querySelector, but it is lenient: it never
+ * reports syntax errors (no ; xml-syntax.ts checks them), and
+ * its serializer writes raw newlines inside attribute values, which the
+ * browser reads back as spaces. serializeXml writes attribute values safely.
+ */
+import { DOMParser } from "linkedom"
+
+const ESCAPES: Record = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "\t": " ",
+ "\n": "
",
+ "\r": "
",
+}
+const escapeChars = (text: string, chars: RegExp) =>
+ text.replace(chars, (c) => ESCAPES[c])
+
+/**
+ * Serialize a linkedom node as XML. Attribute values escape tabs and line
+ * breaks too, so multi-line labels (value="a
b") survive a round trip.
+ */
+export function serializeXml(node: Node): string {
+ switch (node.nodeType) {
+ case 9: {
+ // Document
+ const root = (node as Document).documentElement
+ return root ? serializeXml(root) : ""
+ }
+ case 1: {
+ // Element
+ const el = node as Element
+ let out = `<${el.tagName}`
+ for (const attr of Array.from(el.attributes)) {
+ out += ` ${attr.name}="${escapeChars(attr.value, /[&<>"\t\n\r]/g)}"`
+ }
+ if (el.childNodes.length === 0) return `${out}/>`
+ out += ">"
+ for (const child of Array.from(el.childNodes)) {
+ out += serializeXml(child)
+ }
+ return `${out}${el.tagName}>`
+ }
+ case 3:
+ // Text
+ return escapeChars(node.textContent ?? "", /[&<>]/g)
+ case 4:
+ // CDATA
+ return ``
+ case 8:
+ // Comment
+ return ``
+ default:
+ return ""
+ }
+}
+
+/**
+ * XML parsers read a literal tab or line break inside an attribute value as
+ * a space (a line break written as
stays one). linkedom keeps it, and
+ * serializeXml would then write it as a real line break, so an edit would
+ * change labels it never touched. Applied to the text before linkedom.
+ */
+function normalizeAttributeWhitespace(xml: string): string {
+ return xml.replace(
+ /<[A-Za-z][^"'<>]*(?:(?:"[^"]*"|'[^']*')[^"'<>]*)*>/g,
+ (tag) =>
+ tag.replace(/"[^"]*"|'[^']*'/g, (value) =>
+ value.replace(/\r\n|[\t\n\r]/g, " "),
+ ),
+ )
+}
+
+class XmlDomParser extends DOMParser {
+ parseFromString(text: string, type: string) {
+ return super.parseFromString(
+ type.includes("xml") ? normalizeAttributeWhitespace(text) : text,
+ type as any,
+ )
+ }
+}
+
+class XMLSerializerPolyfill {
+ serializeToString(node: Node): string {
+ return serializeXml(node)
+ }
+}
+
+/** Install the DOMParser and XMLSerializer globals the XML helpers use. */
+export function installDomPolyfill(): void {
+ ;(globalThis as any).DOMParser = XmlDomParser
+ ;(globalThis as any).XMLSerializer = XMLSerializerPolyfill
+}
diff --git a/packages/mcp-server/src/drawing-guide.ts b/packages/mcp-server/src/drawing-guide.ts
new file mode 100644
index 00000000..4265a64e
--- /dev/null
+++ b/packages/mcp-server/src/drawing-guide.ts
@@ -0,0 +1,107 @@
+/**
+ * Drawing guide for the model, returned by start_session, get_drawing_guide
+ * and the diagram-workflow prompt.
+ *
+ * Adapted from the web app's system prompt (lib/system-prompts.ts) and its
+ * tool descriptions (app/api/chat/route.ts). When drawing rules change there,
+ * update this file too.
+ */
+
+import {
+ indent,
+ SWIMLANE_EXAMPLE,
+ TWO_EDGES_EXAMPLE,
+ WAYPOINT_EXAMPLE,
+} from "./xml-examples.ts"
+
+export const DRAWING_GUIDE = `# Draw.io drawing guide
+
+## Workflow
+- create_new_diagram draws a new diagram and REPLACES the whole document. add_page adds another tab. edit_diagram changes cells of an existing page. load_diagram opens a .drawio file (the server reads the file itself). get_diagram returns the current XML, including the user's manual edits. export_diagram saves to a file.
+- Before drawing, describe your layout plan in 2-3 sentences, so shapes do not overlap and edges do not cross shapes.
+- Send XML only through tool calls, never in chat text. Never draw a box just to send the user a message.
+- Before using any icon library (AWS, Azure, GCP, Kubernetes, Cisco, BPMN, Material Design, web icons...), call get_shape_library and use the exact style names it returns. NEVER guess icon style names. For AWS, use the AWS 2025 icons (library aws4).
+- After drawing or heavily editing a complex diagram, call screenshot_diagram once to see the result, and fix overlapping shapes and edges that cross shapes.
+- When replicating a diagram from an image, match its style and layout closely: straight or curved lines, rounded or square shapes.
+- The preview page has History (it saves a snapshot before every AI change and can restore any of the last 20 versions) and Download. You can make changes freely; nothing is lost.
+
+## The XML you send
+Single page (create_new_diagram, add_page): send ONLY the mxCell elements. The server adds , , and the root cells id="0" and id="1".
+
+
+
+
+
+
+
+
+Several pages at once (create_new_diagram only): send a full with one per page. Every page's must start with .
+
+Rules (XML that breaks them is rejected):
+1. All mxCell elements are siblings. NEVER nest an mxCell inside another mxCell.
+2. Ids are unique within a page and start from "2" ("0" and "1" are the root cells).
+3. parent="1" for top-level shapes, parent="" for shapes inside a container.
+4. Edge source and target must reference existing cell ids.
+5. Escape special characters in attribute values: < for <, > for >, & for &, " for ".
+6. NEVER include XML comments (). draw.io strips them.
+7. In tool arguments (JSON), every " inside the XML must be escaped as \\".
+
+Containers and swimlanes: children use the container id as parent and coordinates relative to the container.
+
+${indent(SWIMLANE_EXAMPLE)}
+
+## Layout
+- Keep every element of a page within x 0 to 800 and y 0 to 600, so the whole diagram fits one view without a page break.
+- Containers (for example AWS cloud boxes) are at most 700 pixels wide and 550 pixels tall.
+- Start near x=40, y=40 and keep elements grouped closely.
+- For large diagrams, stack vertically or use a grid instead of spreading wide.
+
+## Edge routing rules
+Rule 1: Never let two edges share a path. Two edges between the same nodes exit and enter at different points (exitY=0.3 for the first, exitY=0.7 for the second, not both 0.5).
+Rule 2: For bidirectional connections (A to B and B to A), use opposite sides: A exits right (exitX=1) into the left of B (entryX=0); B exits left (exitX=0) into the right of A (entryX=1).
+Rule 3: Always set exitX, exitY, entryX and entryY in the edge style, e.g. style="edgeStyle=orthogonalEdgeStyle;exitX=1;exitY=0.3;entryX=0;entryY=0.3;endArrow=classic;".
+Rule 4: Route edges AROUND shapes in the way. Before drawing an edge, find every shape between source and target; if one is in the path, add waypoints. Route diagonal connections along the outside of the diagram, not through the middle. Keep 20-30px clearance from shapes. An edge must never cross another shape's box.
+Rule 5: Plan the layout first. Organize shapes into rows or columns following the flow, space them 150-200px apart so edges have room, and prefer one flow direction (left to right or top to bottom).
+Rule 6: Use 2-3 waypoints for L-shaped or U-shaped paths. Each change of direction needs a waypoint, and segments should be horizontal or vertical.
+Rule 7: Use natural connection points. Never connect at corners (both X and Y 0 or 1). Top-to-bottom flow: exitY=1 into entryY=0. Left-to-right flow: exitX=1 into entryX=0. Diagonal: the side closest to the target.
+
+Before sending XML, check:
+1. Does any edge cross a shape that is not its source or target? Add waypoints.
+2. Do two edges share a path? Change their exit or entry points.
+3. Is any connection point at a corner? Use the middle of a side.
+4. Could moving shapes remove edge crossings? Revise the layout.
+
+Two edges between the same nodes:
+
+${indent(TWO_EDGES_EXAMPLE)}
+
+Waypoints go inside in the edge geometry. Example: Hotfix (right, bottom) connects to Main (center, top) while Develop (center, middle) is in between, so the edge goes right to x=750 first, then up, and enters Main from the right:
+
+${indent(WAYPOINT_EXAMPLE)}
+
+## Styles
+- Shapes: rounded=1, fillColor=#hex, strokeColor=#hex, whiteSpace=wrap;html=1;
+- Edges: endArrow=classic, block, open or none; startArrow=none or classic; curved=1; edgeStyle=orthogonalEdgeStyle
+- Text: fontSize=14, fontStyle=1 (bold), align=center, left or right
+- Animated connectors: add flowAnimation=1 to the edge style.
+
+## Minimal style
+When the user asks for a minimal, plain, black-and-white or unstyled diagram, use these rules instead of the styles above:
+- No fillColor, strokeColor, rounded, fontSize, fontStyle or hex colors.
+- Shapes use style "whiteSpace=wrap;html=1;", edges use "html=1;endArrow=classic;".
+- Containers that hold other shapes use "whiteSpace=wrap;html=1;fillColor=none;" so they do not cover their children.
+- Keep at least 50px between elements, and follow all edge routing rules strictly.
+
+## Editing with edit_diagram
+- update replaces a cell: send the complete mxCell including mxGeometry, with the same id as cell_id.
+- add inserts a new cell with a new id. One cell per operation.
+- delete removes a cell. Its children and every edge connected to it are deleted too, so give only the container's id.
+- All-or-nothing: if any operation fails, nothing is applied. A rejected call includes the current XML of the page; rebuild your operations on it and retry.
+- If the diagram is large, change it with edit_diagram instead of redrawing it.
+
+ {"operations": [{"operation": "update", "cell_id": "3", "new_xml": " "}]}
+ {"page_name": "CNN", "operations": [{"operation": "add", "cell_id": "conv-1", "new_xml": " "}]}
+ {"page_index": 1, "operations": [{"operation": "delete", "cell_id": "5"}]}
+
+Pages: list_pages shows every page's id, name and index. edit_diagram, get_diagram and export_diagram take an optional page_id, page_name or page_index; without one they use the first page.
+`
diff --git a/packages/mcp-server/src/edit-diagram.ts b/packages/mcp-server/src/edit-diagram.ts
new file mode 100644
index 00000000..a77e8e92
--- /dev/null
+++ b/packages/mcp-server/src/edit-diagram.ts
@@ -0,0 +1,109 @@
+/**
+ * Core of the edit_diagram tool, kept free of session state so it can be
+ * unit tested.
+ *
+ * All-or-nothing, like the web app (hooks/use-diagram-tool-handlers.ts):
+ * if any operation fails, nothing is written and every failure is reported,
+ * so the model never builds on a half-applied edit.
+ */
+
+import {
+ applyDiagramOperations,
+ type DiagramOperation,
+} from "./diagram-operations.ts"
+import { type PageSelector, projectPage } from "./pages.ts"
+import { validateAndFixXml, validateMxCellStructure } from "./xml-validation.ts"
+
+export type EditOutcome =
+ | { ok: true; xml: string; applied: number; fixes: string[] }
+ | { ok: false; errors: string[]; pageError: boolean }
+
+/** Number of top-level elements in an XML fragment. */
+function countTopLevelElements(fragment: string): number {
+ const doc = new DOMParser().parseFromString(
+ `${fragment} `,
+ "text/xml",
+ )
+ // On a syntax error the browser adds a element (Chrome
+ // next to the cells, Firefox as the root); the syntax is checked later
+ const root = doc.documentElement
+ if (!root || root.tagName === "parsererror") return 1
+ return Array.from(root.children).filter(
+ (el) => el.tagName !== "parsererror",
+ ).length
+}
+
+/** The target page as a one-page , or the whole document. */
+export function targetPageXml(xml: string, selector: PageSelector): string {
+ const projection = projectPage(xml, selector)
+ return projection.ok ? projection.xml : xml
+}
+
+export function editDiagram(
+ xml: string,
+ operations: DiagramOperation[],
+ selector: PageSelector,
+): EditOutcome {
+ const errors: string[] = []
+ const fixes: string[] = []
+ const prepared: DiagramOperation[] = []
+
+ for (const op of operations) {
+ if (op.operation === "delete" || !op.new_xml) {
+ prepared.push(op)
+ continue
+ }
+ // Checked before validation: several cells fail the strict parser
+ // with a misleading "only one root" syntax error.
+ if (countTopLevelElements(op.new_xml) > 1) {
+ errors.push(
+ `${op.operation} ${op.cell_id}: new_xml must contain exactly one cell; use one add operation per cell`,
+ )
+ continue
+ }
+ const check = validateAndFixXml(op.new_xml)
+ if (!check.valid) {
+ errors.push(
+ `${op.operation} ${op.cell_id}: invalid new_xml: ${check.error}`,
+ )
+ continue
+ }
+ if (check.fixed) {
+ fixes.push(`${op.cell_id}: ${check.fixes.join(", ")}`)
+ }
+ prepared.push({ ...op, new_xml: check.fixed ?? op.new_xml })
+ }
+ if (errors.length > 0) return { ok: false, errors, pageError: false }
+
+ const { result, errors: opErrors } = applyDiagramOperations(
+ xml,
+ prepared,
+ selector,
+ )
+ // An empty cellId means the page itself could not be edited
+ const pageLevel = opErrors.find((e) => e.cellId === "")
+ if (pageLevel) {
+ return { ok: false, errors: [pageLevel.message], pageError: true }
+ }
+ if (opErrors.length > 0) {
+ return {
+ ok: false,
+ errors: opErrors.map((e) => `${e.type} ${e.cellId}: ${e.message}`),
+ pageError: false,
+ }
+ }
+
+ // Validate only the target page, and reject only errors this edit
+ // introduced: problems already in other pages or in a loaded file must
+ // not block every edit.
+ const after = validateMxCellStructure(targetPageXml(result, selector))
+ if (after && !validateMxCellStructure(targetPageXml(xml, selector))) {
+ return {
+ ok: false,
+ errors: [`the edit would make the page invalid: ${after}`],
+ pageError: false,
+ }
+ }
+
+ return { ok: true, xml: result, applied: operations.length, fixes }
+}
diff --git a/packages/mcp-server/src/edit-gate.ts b/packages/mcp-server/src/edit-gate.ts
new file mode 100644
index 00000000..f80240b2
--- /dev/null
+++ b/packages/mcp-server/src/edit-gate.ts
@@ -0,0 +1,144 @@
+/**
+ * Workflow gate for edit_diagram.
+ *
+ * Instead of a wall-clock timeout (the old 30s rule rejected slow-but-correct
+ * clients, see #885), we compare content: `lastSeenXml` is the state-store
+ * XML the model last saw (get_diagram) or wrote itself (create_new_diagram /
+ * edit_diagram / page CRUD). The store only changes on server writes or
+ * browser pushes (user autosave, sync exports), so if the live store still
+ * matches `lastSeenXml`, nothing happened that the model hasn't seen — the
+ * edit is safe no matter how much time passed.
+ *
+ * "Matches" is structural, not byte-for-byte: draw.io re-serialises the
+ * document when it pushes state back (different attribute order, pretty-
+ * printed whitespace, regenerated diagram ids, viewport attributes like
+ * dx/dy/pageWidth on , a different mxfile host). None of that
+ * is a user edit, so the fingerprint keeps only what a user can actually
+ * change: the set of pages, each page's name, and each page's cell tree
+ * (tags + sorted attributes + text). Byte equality is kept as a fast path.
+ */
+import {
+ findPageElement,
+ isMxGraphModel,
+ normalizeToMxfile,
+ type PageSelector,
+ parseMxfile,
+ serializeMxfile,
+} from "./pages.ts"
+
+export type EditGateResult =
+ | { ok: true }
+ | { ok: false; reason: "no-context" | "stale" }
+
+/**
+ * Canonical serialisation of an element subtree: tag + attributes sorted by
+ * name + child elements in order + non-whitespace text. Whitespace-only text
+ * nodes (pretty-printing) are dropped.
+ */
+function canonicalizeElement(el: Element): string {
+ const attrs = Array.from(el.attributes)
+ .map((a) => `${a.name}=${JSON.stringify(a.value)}`)
+ .sort()
+ .join(" ")
+ let children = ""
+ for (const child of Array.from(el.childNodes)) {
+ if (child.nodeType === 1) {
+ children += canonicalizeElement(child as Element)
+ } else if (child.nodeType === 3 || child.nodeType === 4) {
+ const text = (child.textContent ?? "").trim()
+ if (text) children += JSON.stringify(text)
+ }
+ }
+ return `<${el.tagName} ${attrs}>${children}${el.tagName}>`
+}
+
+/**
+ * Structural fingerprint of a diagram document: page names + each page's
+ * subtree, ignoring everything draw.io rewrites on re-serialisation
+ * (mxfile/mxGraphModel attributes, diagram ids, formatting). A bare
+ * fingerprints identically to its single-page mxfile wrapping.
+ * Unparseable input falls back to the trimmed raw string, degrading to the
+ * plain string comparison.
+ *
+ * `includeNames=false` drops page names from the fingerprint — used when the
+ * other side of a comparison is a bare , which carries no page
+ * name at all (normalizeToMxfile would invent "Page-1", falsely mismatching
+ * any real page name).
+ */
+export function contentFingerprint(xml: string, includeNames = true): string {
+ const normalized = normalizeToMxfile(xml)
+ const doc = normalized ? parseMxfile(normalized) : null
+ if (!doc) return xml.trim()
+ const pages: string[] = []
+ doc.querySelectorAll("diagram").forEach((d) => {
+ const name = includeNames ? d.getAttribute("name") || "" : ""
+ const root = d.querySelector("root")
+ // No means the page content is not plain XML (e.g. draw.io's
+ // compressed format) — fingerprint the raw text instead.
+ const body = root
+ ? canonicalizeElement(root)
+ : (d.textContent || "").trim()
+ pages.push(`${name}=${body}`)
+ })
+ return pages.join("\n")
+}
+
+export function checkEditGate(
+ lastSeenXml: string,
+ liveXml: string,
+): EditGateResult {
+ // Model never fetched or produced any diagram state in this session.
+ if (!lastSeenXml) return { ok: false, reason: "no-context" }
+ // Browser state moved since the model last looked (e.g. manual user
+ // edits): force a re-fetch so update/delete operations don't build on
+ // stale cell contents. An empty liveXml means the store has no entry to
+ // compare against, so there is nothing newer to have missed.
+ if (liveXml && liveXml !== lastSeenXml) {
+ // A bare on either side carries no page name, so
+ // comparing names would mismatch against anything not called
+ // "Page-1". Compare cell trees only in that case.
+ const includeNames =
+ !isMxGraphModel(liveXml) && !isMxGraphModel(lastSeenXml)
+ if (
+ contentFingerprint(liveXml, includeNames) !==
+ contentFingerprint(lastSeenXml, includeNames)
+ )
+ return { ok: false, reason: "stale" }
+ }
+ return { ok: true }
+}
+
+/**
+ * The model was shown only the selected page of liveXml (get_diagram with a
+ * page selector, or a rejected edit's error). It has seen the whole document
+ * if the other pages are as it last saw them, or if it saw nothing before
+ * (it then has no old copy of them to edit from). Returns the new
+ * lastSeenXml: liveXml, or lastSeenXml unchanged.
+ */
+export function markPageSeen(
+ lastSeenXml: string,
+ liveXml: string,
+ selector: PageSelector,
+): string {
+ const parse = (xml: string) => {
+ const normalized = normalizeToMxfile(xml)
+ return normalized ? parseMxfile(normalized) : null
+ }
+ // An empty record also follows load_diagram and page tools that wrote
+ // over unseen changes, when the model may remember older pages: only
+ // a one-page document is then fully seen
+ if (!lastSeenXml) {
+ const pages = parse(liveXml)?.querySelectorAll("diagram").length ?? 1
+ return pages <= 1 ? liveXml : lastSeenXml
+ }
+ const otherPages = (xml: string) => {
+ const doc = parse(xml)
+ if (!doc) return null
+ findPageElement(doc, selector)?.element.remove()
+ return contentFingerprint(serializeMxfile(doc))
+ }
+ const before = otherPages(lastSeenXml)
+ return before !== null && before === otherPages(liveXml)
+ ? liveXml
+ : lastSeenXml
+}
diff --git a/packages/mcp-server/src/exclusive.ts b/packages/mcp-server/src/exclusive.ts
new file mode 100644
index 00000000..364f1c15
--- /dev/null
+++ b/packages/mcp-server/src/exclusive.ts
@@ -0,0 +1,25 @@
+/**
+ * A queue for tool handlers: each call waits until the previous one ended.
+ * A tool call the client cancelled while it waited (the MCP SDK aborts its
+ * extra.signal, the handler's last argument) is skipped.
+ */
+export function createExclusive() {
+ let tail: Promise = Promise.resolve()
+ return function exclusive Promise>(
+ handler: T,
+ ): T {
+ return ((...args: unknown[]) => {
+ const extra = args.at(-1) as { signal?: AbortSignal } | undefined
+ const run = tail.then(() =>
+ extra?.signal?.aborted
+ ? {
+ content: [{ type: "text", text: "Cancelled." }],
+ isError: true,
+ }
+ : handler(...args),
+ )
+ tail = run.catch(() => {})
+ return run
+ }) as T
+ }
+}
diff --git a/packages/mcp-server/src/history.ts b/packages/mcp-server/src/history.ts
index 492eda1b..7fff09a8 100644
--- a/packages/mcp-server/src/history.ts
+++ b/packages/mcp-server/src/history.ts
@@ -3,10 +3,18 @@
* Stores {xml, svg} entries in a circular buffer
*/
-import { log } from "./logger.js"
+import { log } from "./logger.ts"
const MAX_HISTORY = 20
-const historyStore = new Map>()
+
+interface HistoryEntry {
+ id: number // Stable across shifts of the circular buffer
+ xml: string
+ svg: string
+}
+
+let nextEntryId = 0
+const historyStore = new Map()
export function addHistory(sessionId: string, xml: string, svg = ""): number {
let history = historyStore.get(sessionId)
@@ -15,13 +23,16 @@ export function addHistory(sessionId: string, xml: string, svg = ""): number {
historyStore.set(sessionId, history)
}
- // Dedupe: skip if same as last entry
+ // Dedupe: skip if same as last entry (any other change, also of page
+ // settings only, is a new version; keepInHistory leaves out draw.io's
+ // copy of a server write)
const last = history[history.length - 1]
- if (last?.xml === xml) {
+ if (last && last.xml === xml) {
+ if (svg && !last.svg) last.svg = svg
return history.length - 1
}
- history.push({ xml, svg })
+ history.push({ id: nextEntryId++, xml, svg })
// Circular buffer
if (history.length > MAX_HISTORY) {
@@ -32,29 +43,35 @@ export function addHistory(sessionId: string, xml: string, svg = ""): number {
return history.length - 1
}
-export function getHistory(
- sessionId: string,
-): Array<{ xml: string; svg: string }> {
+export function getHistory(sessionId: string): HistoryEntry[] {
return historyStore.get(sessionId) || []
}
+/** Look up an entry by its id; the array index shifts as old entries drop. */
export function getHistoryEntry(
sessionId: string,
- index: number,
-): { xml: string; svg: string } | undefined {
- const history = historyStore.get(sessionId)
- return history?.[index]
+ id: number,
+): HistoryEntry | undefined {
+ return historyStore.get(sessionId)?.find((entry) => entry.id === id)
}
export function clearHistory(sessionId: string): void {
historyStore.delete(sessionId)
}
-export function updateLastHistorySvg(sessionId: string, svg: string): boolean {
+/**
+ * Give the last entry the image the browser took of shownXml, the diagram
+ * it just loaded, when that entry is this diagram
+ */
+export function updateLastHistorySvg(
+ sessionId: string,
+ svg: string,
+ shownXml: string,
+): boolean {
const history = historyStore.get(sessionId)
if (!history || history.length === 0) return false
const last = history[history.length - 1]
- if (!last.svg) {
+ if (!last.svg && last.xml === shownXml) {
last.svg = svg
return true
}
diff --git a/packages/mcp-server/src/http-server.ts b/packages/mcp-server/src/http-server.ts
index eca6a58c..26170052 100644
--- a/packages/mcp-server/src/http-server.ts
+++ b/packages/mcp-server/src/http-server.ts
@@ -3,15 +3,57 @@
* Serves draw.io embed with state sync and history UI
*/
+import { randomUUID } from "node:crypto"
+import { readFileSync } from "node:fs"
import http from "node:http"
+import { dirname, join } from "node:path"
+import { fileURLToPath } from "node:url"
+
+const MAX_BODY_BYTES = 10 * 1024 * 1024 // 10 MiB
+
+function readBody(
+ req: http.IncomingMessage,
+ res: http.ServerResponse,
+ cb: (body: string) => void,
+): void {
+ // Decode once at the end: a multi-byte UTF-8 character can be split
+ // across two chunks.
+ const chunks: Buffer[] = []
+ let size = 0
+ let tooLarge = false
+ req.on("data", (chunk: Buffer) => {
+ if (tooLarge) return
+ size += chunk.length
+ if (size > MAX_BODY_BYTES) {
+ // Read the rest without keeping it and answer at the end: a
+ // connection closed mid-upload reaches the browser as a network
+ // error, without this answer
+ tooLarge = true
+ chunks.length = 0
+ return
+ }
+ chunks.push(chunk)
+ })
+ req.on("end", () => {
+ if (tooLarge) {
+ res.writeHead(413, { "Content-Type": "application/json" })
+ res.end(JSON.stringify({ error: "Payload too large" }))
+ return
+ }
+ cb(Buffer.concat(chunks).toString("utf8"))
+ })
+}
+
+import { contentFingerprint } from "./edit-gate.ts"
import {
addHistory,
clearHistory,
getHistory,
getHistoryEntry,
updateLastHistorySvg,
-} from "./history.js"
-import { log } from "./logger.js"
+} from "./history.ts"
+import { log } from "./logger.ts"
+import { BLANK_MXFILE } from "./pages.ts"
// Configurable draw.io embed URL for private deployments
const DRAWIO_BASE_URL =
@@ -29,19 +71,17 @@ function getOrigin(url: string): string {
const DRAWIO_ORIGIN = getOrigin(DRAWIO_BASE_URL)
-// Minimal blank diagram used to bootstrap new sessions.
-// This avoids the draw.io embed spinner (spin=1) getting stuck when no `load(xml)` is ever sent.
-const DEFAULT_DIAGRAM_XML = ` `
-
// Normalize URL for iframe src - ensure no double slashes
function normalizeUrl(url: string): string {
// Remove trailing slash to avoid double slashes
return url.replace(/\/$/, "")
}
-function isLikelyMcpSessionId(sessionId: string): boolean {
- // Keep this cheap and conservative to avoid creating state for arbitrary IDs.
- return sessionId.startsWith("mcp-") && sessionId.length <= 128
+// Session ids look like "mcp--" (start_session).
+// Only this charset is accepted, because ids are written into the page's
+// HTML and script and into the redirect Location header.
+function isValidSessionId(sessionId: string): boolean {
+ return /^mcp-[a-z0-9-]{1,64}$/.test(sessionId)
}
// Find the most recent active session (for auto-redirect when no sessionId provided)
@@ -55,24 +95,75 @@ function getMostRecentSessionId(): string | null {
return mostRecent?.id || null
}
+/**
+ * Give a session whose state is gone (it expired, or the MCP process
+ * restarted) its auto-saved diagram back. The MCP tools call this before
+ * they read the state, so they never build on an older copy and then
+ * overwrite the file. Not a change worth saving again.
+ */
+export function restoreSavedSession(sessionId: string): void {
+ if (stateStore.has(sessionId) || !isValidSessionId(sessionId)) return
+ const saved = savedStateLoader?.(sessionId)
+ if (saved) setState(sessionId, saved, undefined, false, false)
+}
+
function ensureSessionStateInitialized(sessionId: string): void {
if (!sessionId) return
- if (!isLikelyMcpSessionId(sessionId)) return
+ if (!isValidSessionId(sessionId)) return
+ restoreSavedSession(sessionId)
if (stateStore.has(sessionId)) return
- setState(sessionId, DEFAULT_DIAGRAM_XML)
+ // Not a change worth saving: the browser fills it on its next push
+ // A blank diagram keeps the draw.io spinner (spin=1) from waiting
+ // forever when no load(xml) is ever sent
+ setState(sessionId, BLANK_MXFILE, undefined, false, false)
+ // Nothing is known about this session: a tab that still shows it keeps
+ // its diagram
+ const state = stateStore.get(sessionId)
+ if (state) state.blank = true
}
interface SessionState {
xml: string
version: number
+ // Made when the state is created (first use, or again after it expired
+ // or the MCP process restarted) and kept by every write. A tab tells by
+ // it that the server lost what it knew, and every push names the state
+ // it was based on, so one based on a lost state is refused.
+ stateId: string
+ // Created blank because nothing was saved; cleared by the first write
+ blank?: boolean
+ // Version of the last write the browser did not make itself (AI edit,
+ // restore). A browser push based on an older version is rejected.
+ serverVersion?: number
+ // The XML of that write: what a thumbnail taken after loading it shows
+ serverXml?: string
+ // The browser saved a change of the user's since that write (a sync
+ // reply is no change)
+ userEdited?: boolean
lastUpdated: Date
+ lastPolled?: number // Last browser poll; an open tab keeps the session alive
svg?: string // Cached SVG from last browser save
syncRequested?: number // Timestamp when sync requested, cleared when browser responds
- exportFormat?: "png" | "svg" // Set by MCP tool to request browser export
+ exportFormat?: ExportFormat // Set by MCP tool to request browser export
+ exportXml?: string // Single-page projection to load before a page-targeted export
+ exportOptions?: ExportOptions // Extra draw.io export parameters (PNG only)
+ exportId?: number // Number of the pending export, echoed with its result
exportData?: string // Base64/SVG data returned by browser after export
}
+/** draw.io export formats; xmlsvg is an SVG with the diagram embedded */
+export type ExportFormat = "png" | "svg" | "xmlsvg"
+
+/**
+ * draw.io's PNG export takes these directly: width caps the image size
+ * (never upscales), pageId renders a page other than the one on screen.
+ */
+export interface ExportOptions {
+ width?: number
+ pageId?: string
+}
+
export const stateStore = new Map()
let server: http.Server | null = null
@@ -84,22 +175,107 @@ export function getState(sessionId: string): SessionState | undefined {
return stateStore.get(sessionId)
}
-export function setState(sessionId: string, xml: string, svg?: string): number {
+// Called after every state change (AI write, browser push, restore)
+let stateListener: ((sessionId: string, xml: string) => void) | null = null
+
+export function onStateChange(
+ listener: (sessionId: string, xml: string) => void,
+): void {
+ stateListener = listener
+}
+
+// Reads a session's saved diagram when its state is created again (it
+// expired, or the MCP process restarted)
+let savedStateLoader: ((sessionId: string) => string | null) | null = null
+
+export function onSessionRecreate(
+ loader: (sessionId: string) => string | null,
+): void {
+ savedStateLoader = loader
+}
+
+export function setState(
+ sessionId: string,
+ xml: string,
+ svg?: string,
+ fromBrowser = false,
+ notify = true,
+): number {
const existing = stateStore.get(sessionId)
const newVersion = (existing?.version || 0) + 1
stateStore.set(sessionId, {
xml,
version: newVersion,
+ stateId: existing?.stateId ?? randomUUID(),
+ serverVersion: fromBrowser ? existing?.serverVersion : newVersion,
+ serverXml: fromBrowser ? existing?.serverXml : xml,
+ userEdited: fromBrowser ? existing?.userEdited : false,
lastUpdated: new Date(),
- svg: svg || existing?.svg, // Preserve cached SVG if not provided
+ lastPolled: existing?.lastPolled,
+ // The image of this XML, never an older one's: a write without an
+ // image (AI write, sync reply) leaves none until the browser sends
+ // it, unless it is the same XML
+ svg:
+ svg ||
+ (existing && existing.xml === xml ? existing.svg : undefined),
syncRequested: undefined, // Clear sync request when browser pushes state
exportFormat: existing?.exportFormat, // Preserve pending export request
+ exportXml: existing?.exportXml, // Preserve pending projection
+ exportOptions: existing?.exportOptions,
+ exportId: existing?.exportId,
exportData: existing?.exportData, // Preserve export result
})
log.debug(`State updated: session=${sessionId}, version=${newVersion}`)
+ if (notify) stateListener?.(sessionId, xml)
return newVersion
}
+/**
+ * Keep the session's diagram in History before a write replaces it.
+ * Nothing to keep when the browser saved no change of the user's since the
+ * last server write and History ends with that write: the state is that
+ * write, or draw.io's own copy of it from a sync (other text, same diagram).
+ */
+export function keepInHistory(sessionId: string, xml: string, svg = ""): void {
+ const state = stateStore.get(sessionId)
+ const last = getHistory(sessionId).at(-1)
+ if (state && !state.userEdited && last && last.xml === state.serverXml) {
+ return
+ }
+ addHistory(sessionId, xml, svg)
+}
+
+/**
+ * Ask the browser bridge to export the current diagram as png/svg.
+ *
+ * When `projectionXml` is given (a single-page ), the bridge loads it
+ * first, waits for draw.io's own load event, exports, then reloads the
+ * session's real document — so a page-targeted export never mutates the
+ * canonical session state and needs no fixed-delay guessing on the server.
+ *
+ * Returns false when the session is unknown. Callers should then poll
+ * `getState(sessionId)?.exportData` for the result.
+ */
+export function requestExport(
+ sessionId: string,
+ format: ExportFormat,
+ projectionXml?: string,
+ options?: ExportOptions,
+): boolean {
+ const state = stateStore.get(sessionId)
+ if (!state) return false
+ state.exportData = undefined
+ state.exportXml = projectionXml
+ state.exportOptions = options
+ state.exportFormat = format
+ // The browser sends this back with the result, so a late result of an
+ // export that timed out is not taken for this one
+ state.exportId = ++lastExportId
+ return true
+}
+
+let lastExportId = 0
+
export function requestSync(sessionId: string): boolean {
const state = stateStore.get(sessionId)
if (state) {
@@ -155,7 +331,7 @@ export function startHttpServer(port = 6002): Promise {
}
})
- server.listen(port, () => {
+ server.listen(port, "127.0.0.1", () => {
serverPort = port
log.info(`HTTP server running on http://localhost:${port}`)
resolve(port)
@@ -173,7 +349,11 @@ export function stopHttpServer(): void {
function cleanupExpiredSessions(): void {
const now = Date.now()
for (const [sessionId, state] of stateStore) {
- if (now - state.lastUpdated.getTime() > SESSION_TTL) {
+ const lastActive = Math.max(
+ state.lastUpdated.getTime(),
+ state.lastPolled ?? 0,
+ )
+ if (now - lastActive > SESSION_TTL) {
stateStore.delete(sessionId)
clearHistory(sessionId)
log.info(`Cleaned up expired session: ${sessionId}`)
@@ -196,7 +376,49 @@ function handleRequest(
req: http.IncomingMessage,
res: http.ServerResponse,
): void {
- const url = new URL(req.url || "/", `http://localhost:${serverPort}`)
+ // A bad request must never take down the MCP process
+ try {
+ routeRequest(req, res)
+ } catch (err) {
+ log.error("HTTP request failed:", err)
+ if (!res.headersSent) res.writeHead(500)
+ res.end()
+ }
+}
+
+// Serve only requests addressed to localhost, sent by the preview page
+// itself (Origin is the address it was opened at, the Host) or by a
+// non-browser client (no Origin header). This blocks DNS rebinding, other
+// websites, and pages on other localhost ports, whose plain text POSTs need
+// no CORS preflight.
+function isLocalRequest(req: http.IncomingMessage): boolean {
+ const host = req.headers.host ?? ""
+ const origin = req.headers.origin
+ return (
+ /^(localhost|127\.0\.0\.1)(:\d+)?$/.test(host) &&
+ (origin === undefined || origin === `http://${host}`)
+ )
+}
+
+function routeRequest(
+ req: http.IncomingMessage,
+ res: http.ServerResponse,
+): void {
+ let url: URL
+ try {
+ url = new URL(req.url || "/", `http://localhost:${serverPort}`)
+ } catch {
+ // e.g. "//" is not a valid URL path
+ res.writeHead(400)
+ res.end("Bad Request")
+ return
+ }
+
+ if (!isLocalRequest(req)) {
+ res.writeHead(403)
+ res.end("Forbidden")
+ return
+ }
const requestOrigin = req.headers.origin
if (requestOrigin === `http://localhost:${serverPort}`) {
@@ -213,12 +435,19 @@ function handleRequest(
if (url.pathname === "/" || url.pathname === "/index.html") {
const sessionId = url.searchParams.get("mcp") || ""
+ if (sessionId && !isValidSessionId(sessionId)) {
+ res.writeHead(400)
+ res.end("Invalid session id")
+ return
+ }
// Auto-redirect to most recent session if no sessionId provided
if (!sessionId) {
const recentSessionId = getMostRecentSessionId()
if (recentSessionId) {
- res.writeHead(302, { Location: `/?mcp=${recentSessionId}` })
+ res.writeHead(302, {
+ Location: `/?mcp=${encodeURIComponent(recentSessionId)}`,
+ })
res.end()
return
}
@@ -256,46 +485,146 @@ function handleStateApi(
}
ensureSessionStateInitialized(sessionId)
const state = stateStore.get(sessionId)
+ // Polling counts as activity, so a session stays alive while its
+ // tab is open
+ if (state) state.lastPolled = Date.now()
res.writeHead(200, { "Content-Type": "application/json" })
res.end(
JSON.stringify({
xml: state?.xml || null,
version: state?.version || 0,
+ stateId: state?.stateId ?? null,
+ blank: !!state?.blank,
syncRequested: !!state?.syncRequested,
exportFormat: state?.exportFormat || null,
+ exportXml: state?.exportXml || null,
+ exportOptions: state?.exportOptions || null,
+ exportId: state?.exportId ?? null,
}),
)
} else if (req.method === "POST") {
- let body = ""
- req.on("data", (chunk) => {
- body += chunk
- })
- req.on("end", () => {
+ readBody(req, res, (body) => {
try {
const data = JSON.parse(body)
const { sessionId } = data
- if (!sessionId) {
+ if (!sessionId || !isValidSessionId(sessionId)) {
res.writeHead(400, { "Content-Type": "application/json" })
- res.end(JSON.stringify({ error: "sessionId required" }))
+ res.end(
+ JSON.stringify({ error: "valid sessionId required" }),
+ )
return
}
// Browser is returning export data (png/svg)
if (data.exportData !== undefined) {
const state = stateStore.get(sessionId)
- if (state) {
+ if (state && data.exportId === state.exportId) {
state.exportData = data.exportData
state.exportFormat = undefined
+ state.exportXml = undefined
+ state.exportOptions = undefined
+ state.exportId = undefined
log.debug(
`Export data received for session=${sessionId}`,
)
+ } else if (state) {
+ log.debug(
+ `Ignored a late export result for session=${sessionId}`,
+ )
}
res.writeHead(200, { "Content-Type": "application/json" })
res.end(JSON.stringify({ success: true }))
return
}
- const version = setState(sessionId, data.xml, data.svg)
+ // A push can come before the tab's first poll after a
+ // restart: recover the saved file first, so it is compared
+ // with that and never overwrites it unseen
+ ensureSessionStateInitialized(sessionId)
+ const current = stateStore.get(sessionId)
+
+ // A tab of this version names the state its push is based
+ // on. Another state (the server lost the one it knew, or
+ // the tab has not polled yet): refused, and the tab's next
+ // poll decides whose diagram wins.
+ if (current && "stateId" in data) {
+ if (data.stateId !== current.stateId) {
+ res.writeHead(409, {
+ "Content-Type": "application/json",
+ })
+ res.end(
+ JSON.stringify({
+ error: "Session was recreated",
+ stateChanged: true,
+ version: current.version,
+ }),
+ )
+ return
+ }
+ // What a recovering tab showed: kept in history only
+ if (data.source === "recover") {
+ const saved =
+ typeof data.xml === "string" &&
+ !!data.xml &&
+ data.xml !== current.xml
+ if (saved) {
+ addHistory(sessionId, data.xml, data.svg || "")
+ }
+ res.writeHead(409, {
+ "Content-Type": "application/json",
+ })
+ res.end(
+ JSON.stringify({
+ error: "Diagram changed on the server",
+ version: current.version,
+ savedToHistory: saved,
+ }),
+ )
+ return
+ }
+ }
+
+ // The browser edited a version older than the latest AI write
+ // (it has not loaded that write yet). Keep the AI write; the
+ // browser loads it on its next poll. A sync reply is also
+ // stale after a newer write of the browser's own (a user
+ // edit saved while the export ran).
+ if (
+ typeof data.baseVersion === "number" &&
+ (data.baseVersion < (current?.serverVersion ?? 0) ||
+ (data.source === "sync" &&
+ data.baseVersion < (current?.version ?? 0)))
+ ) {
+ let savedToHistory = false
+ if (data.source === "sync") {
+ // A stale sync reply: the store already holds the
+ // newer AI write, so the sync is done.
+ if (current) current.syncRequested = undefined
+ } else if (typeof data.xml === "string" && data.xml) {
+ // A user edit lost the race with an AI write. Keep
+ // it in history so the user can restore it.
+ addHistory(sessionId, data.xml, data.svg || "")
+ savedToHistory = true
+ }
+ res.writeHead(409, { "Content-Type": "application/json" })
+ res.end(
+ JSON.stringify({
+ error: "Diagram changed on the server",
+ version: current?.version,
+ savedToHistory,
+ }),
+ )
+ return
+ }
+
+ if (typeof data.xml !== "string") {
+ res.writeHead(400, { "Content-Type": "application/json" })
+ res.end(JSON.stringify({ error: "xml must be a string" }))
+ return
+ }
+ const version = setState(sessionId, data.xml, data.svg, true)
+ const saved = stateStore.get(sessionId)
+ if (saved && data.source !== "sync") saved.userEdited = true
res.writeHead(200, { "Content-Type": "application/json" })
res.end(JSON.stringify({ success: true, version }))
} catch {
@@ -331,7 +660,11 @@ function handleHistoryApi(
res.writeHead(200, { "Content-Type": "application/json" })
res.end(
JSON.stringify({
- entries: history.map((entry, i) => ({ index: i, svg: entry.svg })),
+ entries: history.map((entry, i) => ({
+ index: i,
+ id: entry.id,
+ svg: entry.svg,
+ })),
count: history.length,
}),
)
@@ -347,32 +680,38 @@ function handleRestoreApi(
return
}
- let body = ""
- req.on("data", (chunk) => {
- body += chunk
- })
- req.on("end", () => {
+ readBody(req, res, (body) => {
try {
- const { sessionId, index } = JSON.parse(body)
- if (!sessionId || index === undefined) {
+ const { sessionId, id } = JSON.parse(body)
+ if (!sessionId || typeof id !== "number") {
res.writeHead(400, { "Content-Type": "application/json" })
- res.end(
- JSON.stringify({ error: "sessionId and index required" }),
- )
+ res.end(JSON.stringify({ error: "sessionId and id required" }))
return
}
- const entry = getHistoryEntry(sessionId, index)
+ const entry = getHistoryEntry(sessionId, id)
if (!entry) {
res.writeHead(404, { "Content-Type": "application/json" })
res.end(JSON.stringify({ error: "Entry not found" }))
return
}
+ // Edits in the browser since the last entry are not in history
+ // yet: keep them, so the restore can be undone
+ // (any state besides a blank page; a cleared document with its
+ // own pages counts)
+ const current = stateStore.get(sessionId)
+ if (
+ current &&
+ contentFingerprint(current.xml) !==
+ contentFingerprint(BLANK_MXFILE)
+ ) {
+ keepInHistory(sessionId, current.xml, current.svg)
+ }
const newVersion = setState(sessionId, entry.xml)
addHistory(sessionId, entry.xml, entry.svg)
- log.info(`Restored session ${sessionId} to index ${index}`)
+ log.info(`Restored session ${sessionId} to history entry ${id}`)
res.writeHead(200, { "Content-Type": "application/json" })
res.end(JSON.stringify({ success: true, newVersion }))
@@ -393,20 +732,29 @@ function handleHistorySvgApi(
return
}
- let body = ""
- req.on("data", (chunk) => {
- body += chunk
- })
- req.on("end", () => {
+ readBody(req, res, (body) => {
try {
- const { sessionId, svg } = JSON.parse(body)
+ const { sessionId, svg, stateId, version } = JSON.parse(body)
if (!sessionId || !svg) {
res.writeHead(400, { "Content-Type": "application/json" })
res.end(JSON.stringify({ error: "sessionId and svg required" }))
return
}
- updateLastHistorySvg(sessionId, svg)
+ // The browser took it of the server write it loaded, named by
+ // the state and version. One that arrives after the next server
+ // write, or for a state since lost, is dropped; a browser write
+ // since (a sync reply) leaves that write's image valid.
+ const state = stateStore.get(sessionId)
+ if (
+ state &&
+ state.stateId === stateId &&
+ state.serverVersion === version &&
+ state.serverXml !== undefined
+ ) {
+ updateLastHistorySvg(sessionId, svg, state.serverXml)
+ if (state.xml === state.serverXml) state.svg = svg
+ }
res.writeHead(200, { "Content-Type": "application/json" })
res.end(JSON.stringify({ success: true }))
} catch {
@@ -416,508 +764,34 @@ function handleHistorySvgApi(
})
}
-function getHtmlPage(sessionId: string): string {
- return `
-
-
-
-
- Next AI Draw.io
-
-
-
-
-
-
-
-
-
-
-
-
-
- Format
-
- Draw.io (.drawio)
- PNG Image (.png)
- SVG Vector (.svg)
-
-
-
-
-
-
-
-
-
-`
+function loadPreviewTemplate(): string {
+ if (!previewTemplate) {
+ const read = (file: string) =>
+ readFileSync(join(PREVIEW_DIR, file), "utf-8")
+ previewTemplate = read("index.html")
+ .replace("{{CSS}}", () => read("preview.css"))
+ .replace("{{SCRIPT}}", () => read("preview.js"))
+ }
+ return previewTemplate
+}
+
+/** A JSON string literal that is safe inside a
+
+