diff --git a/.github/workflows/e2e-fallback-tests.yaml b/.github/workflows/e2e-fallback-tests.yaml new file mode 100644 index 0000000000..d5b0a5fdca --- /dev/null +++ b/.github/workflows/e2e-fallback-tests.yaml @@ -0,0 +1,164 @@ +# TODO: keep in sync with e2e-tests.yaml — this is a near-copy with the backend +# started on the fallback port (8110) only, so the SDK exercises fallback logic. +name: Runs E2E Fallback Tests + +on: + push: + branches: + - main + - dev + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }} + +jobs: + build: + name: E2E Fallback Tests (Node ${{ matrix.node-version }}) + runs-on: ubicloud-standard-8 + env: + NODE_ENV: test + STACK_ENABLE_HARDCODED_PASSKEY_CHALLENGE_FOR_TESTING: yes + STACK_DATABASE_CONNECTION_STRING: "postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:8128/stackframe" + STACK_EXTERNAL_DB_SYNC_MAX_DURATION_MS: "20000" + STACK_EXTERNAL_DB_SYNC_DIRECT: "false" + # SDK reads this as the primary URL, discovers hardcoded fallback to port 8110 + NEXT_PUBLIC_STACK_API_URL: "http://localhost:8102" + # Tells js-helpers to omit explicit baseUrl so the SDK exercises fallback logic + STACK_TEST_SDK_FALLBACK: "true" + + strategy: + matrix: + node-version: [22.x] + + steps: + - uses: actions/checkout@v6 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node-version }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Start Docker Compose in background + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: docker compose -f docker/dependencies/docker.compose.yaml up --pull always -d & + wait-on: /dev/null + tail: true + wait-for: 3s + log-output-if: true + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Create .env.test.local files + run: | + cp apps/backend/.env.development apps/backend/.env.test.local + cp apps/dashboard/.env.development apps/dashboard/.env.test.local + cp apps/e2e/.env.development apps/e2e/.env.test.local + cp docs/.env.development docs/.env.test.local + cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local + cp examples/demo/.env.development examples/demo/.env.test.local + cp examples/docs-examples/.env.development examples/docs-examples/.env.test.local + cp examples/e-commerce/.env.development examples/e-commerce/.env.test.local + cp examples/middleware/.env.development examples/middleware/.env.test.local + cp examples/supabase/.env.development examples/supabase/.env.test.local + cp examples/convex/.env.development examples/convex/.env.test.local + + - name: Build + run: pnpm build + + - name: Wait on Postgres + run: pnpm run wait-until-postgres-is-ready:pg_isready + + - name: Wait on Inbucket + run: pnpx wait-on tcp:localhost:8129 + + - name: Wait on Svix + run: pnpx wait-on tcp:localhost:8113 + + - name: Wait on QStash + run: pnpx wait-on tcp:localhost:8125 + + - name: Wait on ClickHouse + run: pnpx wait-on http://localhost:8136/ping + + - name: Initialize database + run: pnpm run db:init + + # Start backend ONLY on fallback port 8110 — primary port 8102 is intentionally left down + # so the SDK exercises its fallback logic for every request. + - name: Start stack-backend on fallback port (8110) + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: pnpm -C apps/backend run with-env:test next start --port 8110 & + wait-on: | + http://localhost:8110 + tail: true + wait-for: 30s + log-output-if: true + + - name: Start stack-dashboard in background + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: pnpm run start:dashboard --log-order=stream & + wait-on: | + http://localhost:8101 + tail: true + wait-for: 30s + log-output-if: true + + - name: Start mock-oauth-server in background + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: pnpm run start:mock-oauth-server --log-order=stream & + wait-on: | + http://localhost:8110 + tail: true + wait-for: 30s + log-output-if: true + + - name: Start run-email-queue in background + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: pnpm -C apps/backend run run-email-queue --log-order=stream & + wait-on: | + http://localhost:8110 + tail: true + wait-for: 30s + log-output-if: true + + - name: Start run-cron-jobs in background + uses: JarvusInnovations/background-action@v1.0.7 + with: + run: pnpm -C apps/backend run run-cron-jobs:test --log-order=stream & + wait-on: | + http://localhost:8110 + tail: true + wait-for: 30s + log-output-if: true + + - name: Wait 10 seconds + run: sleep 10 + + - name: Verify primary port 8102 is NOT running + run: | + if curl -s -o /dev/null -w "%{http_code}" http://localhost:8102/health 2>/dev/null | grep -q "200"; then + echo "ERROR: Primary backend on port 8102 should NOT be running for fallback tests" + exit 1 + fi + echo "Confirmed: primary port 8102 is down, fallback tests will exercise SDK fallback logic" + + # Only run JS SDK tests — these exercise the SDK's fallback logic. + # Backend API tests use direct HTTP calls that don't go through fallback. + # Exclude cross-domain-auth which hardcodes the primary URL. + - name: Run SDK fallback tests + run: pnpm -w run pre && cd apps/e2e && npx vitest run tests/js/ --exclude '**/{cross-domain-auth,oauth,email-template-existing-project}*' + + - name: Print Docker Compose logs + if: always() + run: docker compose -f docker/dependencies/docker.compose.yaml logs diff --git a/apps/backend/package.json b/apps/backend/package.json index f387744017..0a3d174a38 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -11,7 +11,7 @@ "with-env:dev": "dotenv -c development --", "with-env:prod": "dotenv -c production --", "with-env:test": "dotenv -c test --", - "dev": "concurrently -n \"dev,codegen,prisma-studio,email-queue,cron-jobs\" -k \"next dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}02 ${STACK_BACKEND_DEV_EXTRA_ARGS:-}\" \"pnpm run codegen:watch\" \"pnpm run prisma-studio\" \"pnpm run run-email-queue\" \"pnpm run run-cron-jobs\"", + "dev": "BACKEND_PORT=${STACK_DEV_FALLBACK_BACKEND:+${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}10} && BACKEND_PORT=${BACKEND_PORT:-${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}02} && concurrently -n \"dev,codegen,prisma-studio,email-queue,cron-jobs\" -k \"next dev --port $BACKEND_PORT ${STACK_BACKEND_DEV_EXTRA_ARGS:-}\" \"pnpm run codegen:watch\" \"pnpm run prisma-studio\" \"pnpm run run-email-queue\" \"pnpm run run-cron-jobs\"", "dev:inspect": "STACK_BACKEND_DEV_EXTRA_ARGS=\"--inspect\" pnpm run dev", "dev:profile": "STACK_BACKEND_DEV_EXTRA_ARGS=\"--experimental-cpu-prof\" pnpm run dev", "build": "pnpm run codegen && next build", diff --git a/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx b/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx index 0b6f25053d..d633d04da3 100644 --- a/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx +++ b/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx @@ -4,7 +4,7 @@ import { createAuthTokens } from "@/lib/tokens"; import { getRequestContextAndBotChallengeAssessment, botChallengeFlowRequestSchemaFields } from "@/lib/turnstile"; import { createOrUpgradeAnonymousUserWithRules } from "@/lib/users"; import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { KnownErrors } from "@stackframe/stack-shared"; import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password"; import { adaptSchema, clientOrHigherAuthTypeSchema, emailVerificationCallbackUrlSchema, passwordSchema, signInEmailSchema, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; diff --git a/apps/backend/src/app/api/latest/internal/backend-urls/route.test.tsx b/apps/backend/src/app/api/latest/internal/backend-urls/route.test.tsx new file mode 100644 index 0000000000..523b60b9ca --- /dev/null +++ b/apps/backend/src/app/api/latest/internal/backend-urls/route.test.tsx @@ -0,0 +1,68 @@ +import { describe, expect, it } from 'vitest'; +import { parseAndValidateConfig } from './route'; + +describe('parseAndValidateConfig', () => { + it('should parse a single entry with probability 1', () => { + const result = parseAndValidateConfig({ + "1": ["https://api.stack-auth.com"], + }); + expect(result).toEqual([ + { probability: 1, urls: ["https://api.stack-auth.com"] }, + ]); + }); + + it('should parse multiple entries', () => { + const result = parseAndValidateConfig({ + "0.7": ["https://api.stack-auth.com", "https://api2.stack-auth.com"], + "0.3": ["https://api2.stack-auth.com", "https://api.stack-auth.com"], + }); + expect(result).toEqual([ + { probability: 0.7, urls: ["https://api.stack-auth.com", "https://api2.stack-auth.com"] }, + { probability: 0.3, urls: ["https://api2.stack-auth.com", "https://api.stack-auth.com"] }, + ]); + }); + + it('should allow probabilities summing to less than 1', () => { + const result = parseAndValidateConfig({ + "0.5": ["https://api.stack-auth.com"], + "0.3": ["https://api2.stack-auth.com"], + }); + expect(result).toHaveLength(2); + }); + + it('should reject non-object input', () => { + expect(() => parseAndValidateConfig("string")).toThrow("must be a JSON object"); + expect(() => parseAndValidateConfig(null)).toThrow("must be a JSON object"); + expect(() => parseAndValidateConfig([])).toThrow("must be a JSON object"); + expect(() => parseAndValidateConfig(42)).toThrow("must be a JSON object"); + }); + + it('should reject empty object', () => { + expect(() => parseAndValidateConfig({})).toThrow("at least one entry"); + }); + + it('should reject invalid probability keys', () => { + expect(() => parseAndValidateConfig({ "abc": ["https://a.com"] })).toThrow("must be a number between 0 and 1"); + expect(() => parseAndValidateConfig({ "-0.1": ["https://a.com"] })).toThrow("must be a number between 0 and 1"); + expect(() => parseAndValidateConfig({ "1.5": ["https://a.com"] })).toThrow("must be a number between 0 and 1"); + }); + + it('should reject probabilities summing to more than 1', () => { + expect(() => parseAndValidateConfig({ + "0.6": ["https://api.stack-auth.com"], + "0.5": ["https://api2.stack-auth.com"], + })).toThrow("exceeds 1"); + }); + + it('should reject invalid URL values', () => { + expect(() => parseAndValidateConfig({ "1": ["not-a-url"] })).toThrow(); + }); + + it('should reject empty URL arrays', () => { + expect(() => parseAndValidateConfig({ "1": [] })).toThrow(); + }); + + it('should reject non-array values', () => { + expect(() => parseAndValidateConfig({ "1": "https://api.stack-auth.com" })).toThrow(); + }); +}); diff --git a/apps/backend/src/app/api/latest/internal/backend-urls/route.tsx b/apps/backend/src/app/api/latest/internal/backend-urls/route.tsx new file mode 100644 index 0000000000..8f50911cc7 --- /dev/null +++ b/apps/backend/src/app/api/latest/internal/backend-urls/route.tsx @@ -0,0 +1,104 @@ +import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler"; +import { urlSchema, yupArray, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; +import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; +import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env"; +import { getDefaultApiUrls } from "@stackframe/stack-shared/dist/utils/urls"; + +/** + * Env var format: JSON object mapping probability (as string number) to URL arrays. + * Probabilities must sum to <= 1. Remaining probability uses the last entry as fallback. + * + * Example: + * { + * "0.7": ["https://api.stack-auth.com", "https://api2.stack-auth.com"], + * "0.3": ["https://api2.stack-auth.com", "https://api.stack-auth.com"] + * } + */ + +const urlsArraySchema = yupArray(urlSchema.defined()).min(1).defined(); + +export function parseAndValidateConfig(raw: unknown): Array<{ probability: number, urls: string[] }> { + if (typeof raw !== "object" || raw === null || Array.isArray(raw)) { + throw new StackAssertionError("STACK_BACKEND_URLS_CONFIG must be a JSON object mapping probability strings to URL arrays"); + } + + const entries = Object.entries(raw as Record).map(([key, value]) => { + const probability = Number(key); + if (isNaN(probability) || probability < 0 || probability > 1) { + throw new StackAssertionError(`Invalid probability key "${key}": must be a number between 0 and 1`); + } + const urls = urlsArraySchema.validateSync(value); + return { probability, urls }; + }); + + if (entries.length === 0) { + throw new StackAssertionError("STACK_BACKEND_URLS_CONFIG must have at least one entry"); + } + + const sum = entries.reduce((acc, e) => acc + e.probability, 0); + if (sum > 1 + 1e-9) { + throw new StackAssertionError(`Probabilities sum to ${sum}, which exceeds 1`); + } + + return entries; +} + +let cachedEntries: ReturnType | undefined; +function getCachedConfig() { + if (!cachedEntries) { + const rawEnv = getEnvVariable("STACK_BACKEND_URLS_CONFIG", ""); + if (rawEnv) { + let parsed; + try { + parsed = JSON.parse(rawEnv); + } catch (e) { + throw new StackAssertionError(`STACK_BACKEND_URLS_CONFIG is not valid JSON: ${e}`); + } + cachedEntries = parseAndValidateConfig(parsed); + } else { + cachedEntries = [{ probability: 1, urls: getDefaultApiUrls(getEnvVariable("NEXT_PUBLIC_STACK_API_URL")) }]; + } + } + return cachedEntries; +} + +export const GET = createSmartRouteHandler({ + metadata: { + hidden: true, + summary: "Get backend URLs", + description: "Returns a prioritized list of backend API URLs for client-side failover", + tags: ["Internal"], + }, + request: yupObject({ + method: yupString().oneOf(["GET"]).defined(), + }), + response: yupObject({ + statusCode: yupNumber().oneOf([200]).defined(), + bodyType: yupString().oneOf(["json"]).defined(), + body: yupObject({ + urls: yupArray(yupString().defined()).defined(), + }).defined(), + }), + handler: async () => { + const entries = getCachedConfig(); + + const roll = Math.random(); + let cumulative = 0; + for (const entry of entries) { + cumulative += entry.probability; + if (roll < cumulative) { + return { + statusCode: 200, + bodyType: "json", + body: { urls: entry.urls }, + } as const; + } + } + + return { + statusCode: 200, + bodyType: "json", + body: { urls: entries[entries.length - 1].urls }, + } as const; + }, +}); diff --git a/apps/backend/src/app/api/latest/project-permissions/crud.tsx b/apps/backend/src/app/api/latest/project-permissions/crud.tsx index fe9aff673d..89285b2c02 100644 --- a/apps/backend/src/app/api/latest/project-permissions/crud.tsx +++ b/apps/backend/src/app/api/latest/project-permissions/crud.tsx @@ -3,7 +3,7 @@ import { ensureProjectPermissionExists, ensureUserExists } from "@/lib/request-c import { sendProjectPermissionCreatedWebhook, sendProjectPermissionDeletedWebhook } from "@/lib/webhooks"; import { getPrismaClientForTenancy, retryTransaction } from "@/prisma-client"; import { createCrudHandlers } from "@/route-handlers/crud-handler"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { KnownErrors } from "@stackframe/stack-shared"; import { projectPermissionsCrud } from '@stackframe/stack-shared/dist/interface/crud/project-permissions'; import { permissionDefinitionIdSchema, userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; diff --git a/apps/backend/src/app/api/latest/team-memberships/crud.tsx b/apps/backend/src/app/api/latest/team-memberships/crud.tsx index ae3e6f117b..b0479b0764 100644 --- a/apps/backend/src/app/api/latest/team-memberships/crud.tsx +++ b/apps/backend/src/app/api/latest/team-memberships/crud.tsx @@ -6,7 +6,7 @@ import { PrismaTransaction } from "@/lib/types"; import { sendTeamMembershipCreatedWebhook, sendTeamMembershipDeletedWebhook, sendTeamPermissionCreatedWebhook } from "@/lib/webhooks"; import { getPrismaClientForTenancy, retryTransaction } from "@/prisma-client"; import { createCrudHandlers } from "@/route-handlers/crud-handler"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { KnownErrors } from "@stackframe/stack-shared"; import { teamMembershipsCrud } from "@stackframe/stack-shared/dist/interface/crud/team-memberships"; import { userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; diff --git a/apps/backend/src/app/api/latest/team-permissions/crud.tsx b/apps/backend/src/app/api/latest/team-permissions/crud.tsx index bb82f04635..bc4a27c6de 100644 --- a/apps/backend/src/app/api/latest/team-permissions/crud.tsx +++ b/apps/backend/src/app/api/latest/team-permissions/crud.tsx @@ -3,7 +3,7 @@ import { ensureTeamMembershipExists, ensureUserTeamPermissionExists } from "@/li import { sendTeamPermissionCreatedWebhook, sendTeamPermissionDeletedWebhook } from "@/lib/webhooks"; import { getPrismaClientForTenancy, retryTransaction } from "@/prisma-client"; import { createCrudHandlers } from "@/route-handlers/crud-handler"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { KnownErrors } from "@stackframe/stack-shared"; import { teamPermissionsCrud } from '@stackframe/stack-shared/dist/interface/crud/team-permissions'; import { permissionDefinitionIdSchema, userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; diff --git a/apps/backend/src/app/api/latest/teams/crud.tsx b/apps/backend/src/app/api/latest/teams/crud.tsx index 6ae06487fd..b2751c1ab1 100644 --- a/apps/backend/src/app/api/latest/teams/crud.tsx +++ b/apps/backend/src/app/api/latest/teams/crud.tsx @@ -4,7 +4,7 @@ import { sendTeamCreatedWebhook, sendTeamDeletedWebhook, sendTeamUpdatedWebhook import { getPrismaClientForTenancy, retryTransaction } from "@/prisma-client"; import { createCrudHandlers } from "@/route-handlers/crud-handler"; import { uploadAndGetUrl } from "@/s3"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { Prisma } from "@/generated/prisma/client"; import { KnownErrors } from "@stackframe/stack-shared"; import { teamsCrud } from "@stackframe/stack-shared/dist/interface/crud/teams"; diff --git a/apps/backend/src/app/api/latest/users/crud.tsx b/apps/backend/src/app/api/latest/users/crud.tsx index 23cf68199f..6b28e88840 100644 --- a/apps/backend/src/app/api/latest/users/crud.tsx +++ b/apps/backend/src/app/api/latest/users/crud.tsx @@ -12,7 +12,7 @@ import { PrismaClientTransaction, RawQuery, getPrismaClientForSourceOfTruth, get import { createCrudHandlers } from "@/route-handlers/crud-handler"; import { uploadAndGetUrl } from "@/s3"; import { log } from "@/utils/telemetry"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { KnownErrors } from "@stackframe/stack-shared"; import { currentUserCrud } from "@stackframe/stack-shared/dist/interface/crud/current-user"; import { UsersCrud, usersCrud } from "@stackframe/stack-shared/dist/interface/crud/users"; diff --git a/apps/backend/src/lib/email-queue-step.tsx b/apps/backend/src/lib/email-queue-step.tsx index 453c1f0bd9..01f297e0e2 100644 --- a/apps/backend/src/lib/email-queue-step.tsx +++ b/apps/backend/src/lib/email-queue-step.tsx @@ -6,7 +6,7 @@ import { generateUnsubscribeLink, getNotificationCategoryById, hasNotificationEn import { getTenancy, Tenancy } from "@/lib/tenancies"; import { getPrismaClientForTenancy, globalPrismaClient, PrismaClientTransaction } from "@/prisma-client"; import { withTraceSpan } from "@/utils/telemetry"; -import { allPromisesAndWaitUntilEach } from "@/utils/vercel"; +import { allPromisesAndWaitUntilEach } from "@/utils/background-tasks"; import { groupBy } from "@stackframe/stack-shared/dist/utils/arrays"; import { getEnvBoolean, getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env"; import { captureError, errorToNiceString, StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors"; diff --git a/apps/backend/src/lib/emails.tsx b/apps/backend/src/lib/emails.tsx index bf54982aa0..5358b9b00a 100644 --- a/apps/backend/src/lib/emails.tsx +++ b/apps/backend/src/lib/emails.tsx @@ -1,5 +1,5 @@ import { globalPrismaClient } from '@/prisma-client'; -import { runAsynchronouslyAndWaitUntil } from '@/utils/vercel'; +import { runAsynchronouslyAndWaitUntil } from '@/utils/background-tasks'; import { EmailOutboxCreatedWith } from '@/generated/prisma/client'; import { DEFAULT_TEMPLATE_IDS } from '@stackframe/stack-shared/dist/helpers/emails'; import { UsersCrud } from '@stackframe/stack-shared/dist/interface/crud/users'; diff --git a/apps/backend/src/lib/end-users.tsx b/apps/backend/src/lib/end-users.tsx index 9b707ad988..f482699deb 100644 --- a/apps/backend/src/lib/end-users.tsx +++ b/apps/backend/src/lib/end-users.tsx @@ -43,7 +43,7 @@ type EndUserLocation = { tzIdentifier?: string, }; -type TrustedProxy = "" | "vercel" | "cloudflare"; +type TrustedProxy = "" | "vercel" | "cloudflare" | "cloudrun"; export async function getSpoofableEndUserLocation(): Promise { const endUserInfo = await getEndUserInfo(); @@ -98,15 +98,21 @@ function getBrowserEndUserInfo(allHeaders: Headers, trustedProxy: TrustedProxy): | null { const isVercelTrusted = trustedProxy === "vercel"; const isCloudflareTrusted = trustedProxy === "cloudflare"; + const isCloudRunTrusted = trustedProxy === "cloudrun"; - // Only read proxy headers as trusted when the corresponding proxy is configured + // Only read proxy headers as trusted when the corresponding proxy is configured. + // Google Cloud's HTTP(S) LB appends two entries to X-Forwarded-For: + // , , + // So the real client IP is the second-to-last entry (.at(-2)). + // See: https://cloud.google.com/load-balancing/docs/https#x-forwarded-for_header const trustedIp = (isVercelTrusted ? allHeaders.get("x-vercel-forwarded-for") : undefined) ?? (isCloudflareTrusted ? allHeaders.get("cf-connecting-ip") : undefined) + ?? (isCloudRunTrusted ? allHeaders.get("x-forwarded-for")?.split(",").at(-2)?.trim() : undefined) ?? undefined; // All other IP headers are always spoofable — including proxy headers when the proxy is not configured as trusted const spoofableIp = allHeaders.get("x-real-ip") - ?? allHeaders.get("x-forwarded-for")?.split(",").at(0) + ?? (!isCloudRunTrusted ? allHeaders.get("x-forwarded-for")?.split(",").at(0) : undefined) ?? (!isVercelTrusted ? allHeaders.get("x-vercel-forwarded-for") : undefined) ?? (!isCloudflareTrusted ? allHeaders.get("cf-connecting-ip") : undefined) ?? undefined; @@ -170,10 +176,10 @@ export async function getEndUserInfo(): Promise< if (isClaimingToBeBrowser) { // Determine which proxy we trust based on deployment configuration. // These headers can only be trusted when the origin is exclusively reachable through the proxy; - // STACK_TRUSTED_PROXY should be set to "vercel", "cloudflare", or left empty/unset for no proxy trust. + // STACK_TRUSTED_PROXY should be set to "vercel", "cloudflare", "cloudrun", or left empty/unset for no proxy trust. const trustedProxy = getEnvVariable("STACK_TRUSTED_PROXY", "").toLowerCase().trim(); - if (trustedProxy !== "" && trustedProxy !== "vercel" && trustedProxy !== "cloudflare") { - throw new StackAssertionError(`STACK_TRUSTED_PROXY must be "vercel", "cloudflare", or empty/unset, but got: "${trustedProxy}"`); + if (trustedProxy !== "" && trustedProxy !== "vercel" && trustedProxy !== "cloudflare" && trustedProxy !== "cloudrun") { + throw new StackAssertionError(`STACK_TRUSTED_PROXY must be "vercel", "cloudflare", "cloudrun", or empty/unset, but got: "${trustedProxy}"`); } return getBrowserEndUserInfo(allHeaders, trustedProxy); } @@ -223,6 +229,67 @@ import.meta.vitest?.describe("getBrowserEndUserInfo(...)", () => { }); }); + test("trusts second-to-last x-forwarded-for entry when Cloud Run proxy is configured", () => { + // Google Cloud LB appends: , , + const result = getBrowserEndUserInfo(new Headers({ + "user-agent": "Mozilla/5.0", + "x-forwarded-for": "198.51.100.42, 10.0.0.1", + }), "cloudrun"); + + expect(result).toEqual({ + maybeSpoofed: false, + exactInfo: { + ip: "198.51.100.42", + }, + }); + }); + + test("ignores client-spoofed x-forwarded-for entries for Cloud Run proxy", () => { + // Client sends "1.1.1.1", LB appends real client IP and its own IP + const result = getBrowserEndUserInfo(new Headers({ + "user-agent": "Mozilla/5.0", + "x-forwarded-for": "1.1.1.1, 198.51.100.42, 10.0.0.1", + }), "cloudrun"); + + expect(result).toEqual({ + maybeSpoofed: false, + exactInfo: { + ip: "198.51.100.42", + }, + }); + }); + + test("does not expose x-forwarded-for as spoofable when Cloud Run proxy is configured", () => { + const result = getBrowserEndUserInfo(new Headers({ + "user-agent": "Mozilla/5.0", + "x-forwarded-for": "198.51.100.42, 10.0.0.1", + "x-real-ip": "10.0.0.1", + }), "cloudrun"); + + expect(result).toEqual({ + maybeSpoofed: false, + exactInfo: { + ip: "198.51.100.42", + }, + }); + }); + + test("does not trust geo headers for Cloud Run proxy", () => { + const result = getBrowserEndUserInfo(new Headers({ + "user-agent": "Mozilla/5.0", + "x-forwarded-for": "198.51.100.42, 10.0.0.1", + "x-vercel-ip-country": "US", + "cf-ipcountry": "DE", + }), "cloudrun"); + + expect(result).toEqual({ + maybeSpoofed: false, + exactInfo: { + ip: "198.51.100.42", + }, + }); + }); + test("keeps trusted proxy geo headers when the trusted IP header is present", () => { const result = getBrowserEndUserInfo(new Headers({ "user-agent": "Mozilla/5.0", diff --git a/apps/backend/src/lib/events.tsx b/apps/backend/src/lib/events.tsx index 7fe9a1ae4f..77fb19606a 100644 --- a/apps/backend/src/lib/events.tsx +++ b/apps/backend/src/lib/events.tsx @@ -1,6 +1,6 @@ import withPostHog from "@/analytics"; import { globalPrismaClient } from "@/prisma-client"; -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import { urlSchema, yupBoolean, yupMixed, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; import { getEnvVariable, getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env"; import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors"; diff --git a/apps/backend/src/lib/js-execution.tsx b/apps/backend/src/lib/js-execution.tsx index d98d7b2d20..0a56cda581 100644 --- a/apps/backend/src/lib/js-execution.tsx +++ b/apps/backend/src/lib/js-execution.tsx @@ -1,5 +1,5 @@ import { traceSpan } from '@/utils/telemetry'; -import { runAsynchronouslyAndWaitUntil } from '@/utils/vercel'; +import { runAsynchronouslyAndWaitUntil } from '@/utils/background-tasks'; import { getEnvVariable, getNodeEnvironment } from '@stackframe/stack-shared/dist/utils/env'; import { StackAssertionError, captureError } from '@stackframe/stack-shared/dist/utils/errors'; import { Result } from '@stackframe/stack-shared/dist/utils/results'; diff --git a/apps/backend/src/lib/sign-up-rules.ts b/apps/backend/src/lib/sign-up-rules.ts index 4a9b7ecde7..16da6acb77 100644 --- a/apps/backend/src/lib/sign-up-rules.ts +++ b/apps/backend/src/lib/sign-up-rules.ts @@ -1,4 +1,4 @@ -import { runAsynchronouslyAndWaitUntil } from "@/utils/vercel"; +import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks"; import type { SignUpRule, SignUpRuleAction } from "@stackframe/stack-shared/dist/interface/crud/sign-up-rules"; import { captureError, StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; import { typedEntries } from "@stackframe/stack-shared/dist/utils/objects"; diff --git a/apps/backend/src/prisma-client.tsx b/apps/backend/src/prisma-client.tsx index c06fe3a21a..c5a1b1cfff 100644 --- a/apps/backend/src/prisma-client.tsx +++ b/apps/backend/src/prisma-client.tsx @@ -9,7 +9,7 @@ import { getEnvVariable, getNodeEnvironment } from '@stackframe/stack-shared/dis import { captureError, StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; import { globalVar } from "@stackframe/stack-shared/dist/utils/globals"; import { deepPlainEquals, filterUndefined, typedFromEntries, typedKeys } from "@stackframe/stack-shared/dist/utils/objects"; -import { concatStacktracesIfRejected, ignoreUnhandledRejection, wait } from "@stackframe/stack-shared/dist/utils/promises"; +import { concatStacktracesIfRejected, ignoreUnhandledRejection, runAsynchronously, wait } from "@stackframe/stack-shared/dist/utils/promises"; import { throwingProxy } from "@stackframe/stack-shared/dist/utils/proxies"; import { Result } from "@stackframe/stack-shared/dist/utils/results"; import { traceSpan } from "@stackframe/stack-shared/dist/utils/telemetry"; @@ -20,6 +20,7 @@ import { isPromise } from "util/types"; import { runMigrationNeeded } from "./auto-migrations"; import { registerPgPool } from "./lib/dev-perf-stats"; import { Tenancy } from "./lib/tenancies"; +import { drainInFlightPromises } from "./utils/background-tasks"; import { ensurePolyfilled } from "./polyfills"; // just ensure we're polyfilled because this file relies on envvars being expanded @@ -85,6 +86,8 @@ function getPostgresPrismaClient(connectionString: string, poolLabel?: string) { if (!postgresPrismaClient) { const schema = getSchemaFromConnectionString(connectionString); const pool = new Pool({ connectionString, max: 25 }); + // pg Pool emits 'error' on idle clients (e.g. TCP reset); unhandled = process crash + pool.on('error', (err) => captureError("pg-pool-error", err)); registerPgPool(pool, poolLabel ?? connectionString); // Register pool for dev performance stats const adapter = new PrismaPg(pool, schema ? { schema } : undefined); postgresPrismaClient = { @@ -96,6 +99,27 @@ function getPostgresPrismaClient(connectionString: string, poolLabel?: string) { return postgresPrismaClient; } +// Cloud Run sends SIGTERM before shutdown; drain background tasks and close DB connections. +if (!getEnvVariable("VERCEL", "") && !globalVar.__stack_prisma_sigterm_registered) { + globalVar.__stack_prisma_sigterm_registered = true; + process.on("SIGTERM", () => { + const keepAlive = setTimeout(() => {}, 10_000); + runAsynchronously(async () => { + try { + await drainInFlightPromises(8000); + for (const [, entry] of postgresPrismaClientsStore) { + await entry.client.$disconnect(); + } + for (const [, client] of prismaClientsStore.neon) { + await client.$disconnect(); + } + } finally { + clearTimeout(keepAlive); + } + }); + }); +} + async function tcpPing(host: string, port: number, timeout = 2000) { return await new Promise((resolve) => { const s = net.connect({ host, port }).setTimeout(timeout); diff --git a/apps/backend/src/utils/background-tasks.tsx b/apps/backend/src/utils/background-tasks.tsx new file mode 100644 index 0000000000..e23f55c3dd --- /dev/null +++ b/apps/backend/src/utils/background-tasks.tsx @@ -0,0 +1,45 @@ +import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env"; +import { ignoreUnhandledRejection, runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; + +// Tracked for SIGTERM drain on non-Vercel runtimes (e.g. Cloud Run). +const inFlightPromises = new Set>(); + +const isVercel = !!getEnvVariable("VERCEL", ""); + +function waitUntilImpl(promise: Promise) { + if (isVercel) { + // On Vercel, use the native waitUntil to keep the function alive + const { waitUntil } = require("@vercel/functions") as typeof import("@vercel/functions"); + waitUntil(promise); + } else { + // On Cloud Run / self-hosted: track the promise for SIGTERM drain + inFlightPromises.add(promise); + const cleanup = promise.finally(() => inFlightPromises.delete(promise)); + ignoreUnhandledRejection(cleanup); + } +} + +export function runAsynchronouslyAndWaitUntil(promiseOrFunction: Promise | (() => Promise)) { + const promise = typeof promiseOrFunction === "function" ? promiseOrFunction() : promiseOrFunction; + runAsynchronously(promise); + waitUntilImpl(promise); +} + +export async function allPromisesAndWaitUntilEach(promises: Promise[]): Promise { + for (const promise of promises) { + waitUntilImpl(promise); + } + return await Promise.all(promises); +} + +/** + * Drains all in-flight background promises (non-Vercel only). + * Called from the SIGTERM handler to allow background work to finish before exit. + */ +export async function drainInFlightPromises(timeoutMs = 8000): Promise { + if (inFlightPromises.size === 0) return; + await Promise.race([ + Promise.allSettled([...inFlightPromises]), + new Promise(resolve => setTimeout(resolve, timeoutMs)), + ]); +} diff --git a/apps/backend/src/utils/vercel.tsx b/apps/backend/src/utils/vercel.tsx deleted file mode 100644 index 1ca40dfa9d..0000000000 --- a/apps/backend/src/utils/vercel.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; -// eslint-disable-next-line no-restricted-imports -import { waitUntil as waitUntilVercel } from "@vercel/functions"; - -export function runAsynchronouslyAndWaitUntil(promiseOrFunction: Promise | (() => Promise)) { - const promise = typeof promiseOrFunction === "function" ? promiseOrFunction() : promiseOrFunction; - runAsynchronously(promise); - waitUntilVercel(promise); -} - -export async function allPromisesAndWaitUntilEach(promises: Promise[]): Promise { - for (const promise of promises) { - waitUntilVercel(promise); - } - return await Promise.all(promises); -} diff --git a/apps/e2e/tests/js/inheritance.test.ts b/apps/e2e/tests/js/inheritance.test.ts index bea061574e..6efa7e96f3 100644 --- a/apps/e2e/tests/js/inheritance.test.ts +++ b/apps/e2e/tests/js/inheritance.test.ts @@ -4,11 +4,15 @@ import { isUuid } from "@stackframe/stack-shared/dist/utils/uuids"; import { STACK_BACKEND_BASE_URL, it } from "../helpers"; import { scaffoldProject } from "./js-helpers"; +// When STACK_TEST_SDK_FALLBACK is set, omit explicit baseUrl so the SDK resolves +// from NEXT_PUBLIC_STACK_API_URL and exercises its fallback logic +const sdkBaseUrl = process.env.STACK_TEST_SDK_FALLBACK ? undefined : STACK_BACKEND_BASE_URL; + it("StackServerApp can inherit configuration from StackClientApp", async ({ expect }) => { const { project, adminUser } = await scaffoldProject(); const adminApp = new StackAdminApp({ projectId: project.id, - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectOwnerSession: adminUser._internalSession, tokenStore: "memory", redirectMethod: "none", @@ -23,7 +27,7 @@ it("StackServerApp can inherit configuration from StackClientApp", async ({ expe }); const clientApp = new StackClientApp({ - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectId: project.id, publishableClientKey: key.publishableClientKey, tokenStore: "memory", @@ -51,7 +55,7 @@ it("StackAdminApp can inherit configuration from StackServerApp", async ({ expec const { project, adminUser } = await scaffoldProject(); const adminApp = new StackAdminApp({ projectId: project.id, - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectOwnerSession: adminUser._internalSession, tokenStore: "memory", redirectMethod: "none", @@ -66,7 +70,7 @@ it("StackAdminApp can inherit configuration from StackServerApp", async ({ expec }); const clientApp = new StackClientApp({ - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectId: project.id, publishableClientKey: key.publishableClientKey, tokenStore: "memory", diff --git a/apps/e2e/tests/js/js-helpers.ts b/apps/e2e/tests/js/js-helpers.ts index 64ee24d04b..22bfc3f2cc 100644 --- a/apps/e2e/tests/js/js-helpers.ts +++ b/apps/e2e/tests/js/js-helpers.ts @@ -8,10 +8,14 @@ const testExtraRequestHeaders = { "x-stack-disable-artificial-development-delay": "yes", }; +// When STACK_TEST_SDK_FALLBACK is set, omit explicit baseUrl so the SDK resolves +// from NEXT_PUBLIC_STACK_API_URL and exercises its fallback logic +const sdkBaseUrl = process.env.STACK_TEST_SDK_FALLBACK ? undefined : STACK_BACKEND_BASE_URL; + export async function scaffoldProject(body?: Omit & { displayName?: string }) { const internalApp = new StackAdminApp({ projectId: 'internal', - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, publishableClientKey: STACK_INTERNAL_PROJECT_CLIENT_KEY, secretServerKey: STACK_INTERNAL_PROJECT_SERVER_KEY, superSecretAdminKey: STACK_INTERNAL_PROJECT_ADMIN_KEY, @@ -54,7 +58,7 @@ export async function createApp( const { project, adminUser } = await scaffoldProject(body); const adminApp = new StackAdminApp({ projectId: project.id, - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectOwnerSession: adminUser._internalSession, tokenStore: "memory", redirectMethod: "none", @@ -74,7 +78,7 @@ export async function createApp( const secretServerKey = apiKey.secretServerKey; const serverApp = new StackServerApp({ - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectId: project.id, publishableClientKey: apiKey.publishableClientKey, secretServerKey, @@ -85,7 +89,7 @@ export async function createApp( }); const clientApp = new StackClientApp({ - baseUrl: STACK_BACKEND_BASE_URL, + baseUrl: sdkBaseUrl, projectId: project.id, publishableClientKey: apiKey.publishableClientKey, tokenStore: "memory", diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile new file mode 100644 index 0000000000..fa53a1422b --- /dev/null +++ b/docker/backend/Dockerfile @@ -0,0 +1,86 @@ +# Backend for Cloud Run / self-hosted deployment (fallback backend server). +# Connects to the same AWS services (RDS, S3, KMS) as the Vercel deployment. +# +# Build: docker build -f docker/backend/Dockerfile -t stack-backend . +# Run: docker run -p 8102:8102 --env-file .env stack-backend + +ARG NODE_VERSION=22.21.1 + +# Base +FROM node:${NODE_VERSION} AS base + +WORKDIR /app + +RUN apt-get update && \ + apt-get upgrade -y && \ + rm -rf /var/lib/apt/lists/* + +ENV PNPM_HOME=/pnpm +ENV PATH=$PNPM_HOME:$PATH + +RUN corepack enable +RUN corepack prepare pnpm@10.23.0 --activate +RUN pnpm add -g turbo +RUN pnpm add -g tsx + + +# Prune stage +FROM base AS pruner + +COPY . . + +RUN tsx ./scripts/generate-sdks.ts + +# Only prune backend (no dashboard) +RUN turbo prune --scope=@stackframe/backend --docker + + +# Build stage +FROM base AS builder + +COPY --from=pruner /app/out/json/ . +COPY --from=pruner /app/out/pnpm-lock.yaml . +COPY .gitignore . +COPY pnpm-workspace.yaml . +COPY turbo.json . +COPY configs ./configs +RUN STACK_SKIP_TEMPLATE_GENERATION=true pnpm install --frozen-lockfile + +COPY --from=pruner /app/out/full/ . + +# Docs are required for the NextJS backend build +COPY docs ./docs + +ENV NEXT_CONFIG_OUTPUT=standalone + +# Build backend only +RUN pnpm turbo run docker-build --filter=@stackframe/backend... + + +# Final image +FROM node:${NODE_VERSION}-slim + +WORKDIR /app + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends openssl && \ + rm -rf /var/lib/apt/lists/* + +# Copy Next.js standalone output — this includes a traced, minimal copy of +# node_modules/ and packages/ (only the files the server actually imports). +COPY --from=builder --chown=node:node /app/apps/backend/.next/standalone ./ +COPY --from=builder --chown=node:node /app/apps/backend/.next/static ./apps/backend/.next/static + +# Prisma schema (needed at runtime by Prisma client) +COPY --from=builder --chown=node:node /app/apps/backend/prisma ./apps/backend/prisma + +ENV NODE_ENV=production +ENV PORT=8102 +ENV HOSTNAME=0.0.0.0 + +USER node + +EXPOSE 8102 + +CMD ["node", "apps/backend/server.js"] diff --git a/examples/demo/src/app/fallback-test/client.tsx b/examples/demo/src/app/fallback-test/client.tsx new file mode 100644 index 0000000000..4245e15ab5 --- /dev/null +++ b/examples/demo/src/app/fallback-test/client.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { useStackApp, useUser } from "@stackframe/stack"; +import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { useCallback, useEffect, useRef, useState } from "react"; + +type LogEntry = { + time: number; + msg: string; + ok: boolean; +}; + +export function FallbackTestClient() { + const app = useStackApp(); + const user = useUser(); + const pathname = usePathname(); + const [log, setLog] = useState([]); + const [running, setRunning] = useState(false); + const renderCount = useRef(0); + renderCount.current++; + + const addLog = useCallback((msg: string, ok: boolean) => { + setLog(prev => [...prev, { time: Date.now(), msg, ok }]); + }, []); + + const runTests = useCallback(async () => { + setLog([]); + setRunning(true); + + // Test 1: getProject + { + const start = Date.now(); + try { + const project = await app.getProject(); + addLog(`getProject: ${project.id} (${Date.now() - start}ms)`, true); + } catch (e: any) { + addLog(`getProject FAILED: ${e.message?.slice(0, 80)} (${Date.now() - start}ms)`, false); + } + } + + // Test 2: useUser + addLog(`useUser: ${user ? user.primaryEmail ?? user.id : "(not signed in)"}`, true); + + // Test 3: 5x getProject to show sticky latency + { + const times: number[] = []; + for (let i = 0; i < 5; i++) { + const start = Date.now(); + try { + await app.getProject(); + times.push(Date.now() - start); + } catch { + times.push(-1); + } + } + const avg = times.filter(t => t >= 0).reduce((a, b) => a + b, 0) / times.filter(t => t >= 0).length; + addLog(`getProject x5: [${times.map(t => t >= 0 ? `${t}ms` : "FAIL").join(", ")}] avg=${Math.round(avg)}ms`, times.every(t => t >= 0)); + } + + setRunning(false); + }, [app, user, addLog]); + + useEffect(() => { + runAsynchronously(runTests()); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return ( +
+
+

Client-side

+ +
+ Debug: renders={renderCount.current} | pathname={pathname} +
+ +
+ {log.map((entry, i) => ( +
+ {entry.ok ? "OK" : "ERR"} {entry.msg} +
+ ))} + {log.length === 0 &&
Running...
} +
+ +
+ +
+
+ +
+

SPA Navigation Test

+

+ Click these links (client-side navigation) and come back. + If sticky fallback persists, requests after navigating back should still be fast. +

+
+ Home + This page (reload) + Settings +
+
+
+ ); +} diff --git a/examples/demo/src/app/fallback-test/page.tsx b/examples/demo/src/app/fallback-test/page.tsx new file mode 100644 index 0000000000..4dd3336589 --- /dev/null +++ b/examples/demo/src/app/fallback-test/page.tsx @@ -0,0 +1,22 @@ +import { stackServerApp } from "src/stack"; +import { FallbackTestClient } from "./client"; + +export default async function FallbackTestPage() { + const serverStart = Date.now(); + const user = await stackServerApp.getUser(); + const project = await stackServerApp.getProject(); + const serverDuration = Date.now() - serverStart; + + return ( +
+

SDK Fallback Test

+ +
+

Server-side (RSC)

+
{JSON.stringify({ projectId: project.id, projectName: project.displayName, user: user?.primaryEmail ?? user?.id ?? null, duration: `${serverDuration}ms` }, null, 2)}
+
+ + +
+ ); +} diff --git a/packages/stack-shared/package.json b/packages/stack-shared/package.json index c49704a3ef..3d6fb26cdd 100644 --- a/packages/stack-shared/package.json +++ b/packages/stack-shared/package.json @@ -62,6 +62,7 @@ }, "dependencies": { "@aws-sdk/client-kms": "^3.876.0", + "@aws-sdk/credential-provider-web-identity": "^3.972.27", "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", diff --git a/packages/stack-shared/src/helpers/vault/server-side.ts b/packages/stack-shared/src/helpers/vault/server-side.ts index a1f0b32217..37ab2bc0e0 100644 --- a/packages/stack-shared/src/helpers/vault/server-side.ts +++ b/packages/stack-shared/src/helpers/vault/server-side.ts @@ -6,30 +6,81 @@ import { GenerateDataKeyCommand, KMSClient } from "@aws-sdk/client-kms"; -import { awsCredentialsProvider } from '@vercel/functions/oidc'; import { decodeBase64, encodeBase64 } from "../../utils/bytes"; import { decrypt, encrypt } from "../../utils/crypto"; import { getEnvVariable } from "../../utils/env"; import { Result } from "../../utils/results"; -function getKmsClient() { - const roleArn = getEnvVariable("STACK_AWS_VERCEL_OIDC_ROLE_ARN", ""); - return new KMSClient({ - region: getEnvVariable("STACK_AWS_REGION"), - endpoint: getEnvVariable("STACK_AWS_KMS_ENDPOINT"), - credentials: roleArn ? awsCredentialsProvider({ - roleArn, - }) : { - accessKeyId: getEnvVariable("STACK_AWS_ACCESS_KEY_ID"), - secretAccessKey: getEnvVariable("STACK_AWS_SECRET_ACCESS_KEY"), - }, +async function getAwsCredentials() { + // 1. Vercel OIDC: Vercel injects an OIDC token that can be exchanged for AWS credentials + const vercelRoleArn = getEnvVariable("STACK_AWS_VERCEL_OIDC_ROLE_ARN", ""); + if (vercelRoleArn) { + const { awsCredentialsProvider } = await import("@vercel/functions/oidc"); + return awsCredentialsProvider({ roleArn: vercelRoleArn }); + } + + // 2. GCP Workload Identity Federation: Cloud Run gets a GCP ID token from the metadata server, + // then exchanges it for temporary AWS credentials via STS AssumeRoleWithWebIdentity. + // Requires: + // - An OIDC identity provider in AWS IAM (issuer: https://accounts.google.com) + // - An IAM role with a trust policy allowing the GCP service account + // - STACK_AWS_GCP_WIF_ROLE_ARN set to that role's ARN + // - STACK_AWS_GCP_WIF_AUDIENCE set to the audience configured in the AWS OIDC provider + const gcpWifRoleArn = getEnvVariable("STACK_AWS_GCP_WIF_ROLE_ARN", ""); + if (gcpWifRoleArn) { + const { fromWebToken } = await import("@aws-sdk/credential-provider-web-identity"); + const audience = getEnvVariable("STACK_AWS_GCP_WIF_AUDIENCE", "sts.amazonaws.com"); + // Return a provider that fetches a fresh GCP ID token on each invocation. + // GCP metadata tokens expire after ~1h, so we can't bake a single token into the closure. + return async () => { + return await fromWebToken({ + roleArn: gcpWifRoleArn, + roleSessionName: "stack-backend-cloudrun", + webIdentityToken: await fetchGcpIdToken(audience), + })(); + }; + } + + // 3. Static credentials: fallback for self-hosted / local development + return { + accessKeyId: getEnvVariable("STACK_AWS_ACCESS_KEY_ID"), + secretAccessKey: getEnvVariable("STACK_AWS_SECRET_ACCESS_KEY"), + }; +} + +/** + * Fetches a GCP ID token from the metadata server (available on Cloud Run, GCE, GKE). + * The token is a Google-signed JWT with the specified audience, suitable for + * AWS STS AssumeRoleWithWebIdentity. + */ +async function fetchGcpIdToken(audience: string): Promise { + const metadataUrl = `http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=${encodeURIComponent(audience)}`; + const response = await fetch(metadataUrl, { + headers: { "Metadata-Flavor": "Google" }, }); + if (!response.ok) { + throw new Error(`Failed to fetch GCP ID token: ${response.status} ${await response.text()}`); + } + return await response.text(); +} + +let kmsClientCache: KMSClient | undefined; + +async function getKmsClient() { + if (!kmsClientCache) { + kmsClientCache = new KMSClient({ + region: getEnvVariable("STACK_AWS_REGION"), + endpoint: getEnvVariable("STACK_AWS_KMS_ENDPOINT"), + credentials: await getAwsCredentials(), + }); + } + return kmsClientCache; } async function getOrCreateKekId(): Promise { const id = "alias/stack-data-vault-server-side-kek"; - const kms = getKmsClient(); + const kms = await getKmsClient(); try { const describeResult = await kms.send(new DescribeKeyCommand({ KeyId: id })); if (describeResult.KeyMetadata?.KeyId) return describeResult.KeyMetadata.KeyId; @@ -48,7 +99,7 @@ async function getOrCreateKekId(): Promise { async function genDEK() { const kekId = await getOrCreateKekId(); - const kms = getKmsClient(); + const kms = await getKmsClient(); const out = await kms.send(new GenerateDataKeyCommand({ KeyId: kekId, KeySpec: "AES_256" })); if (!out.Plaintext || !out.CiphertextBlob) throw new Error("GenerateDataKey failed"); return { @@ -59,7 +110,7 @@ async function genDEK() { async function unwrapDEK(edk_b64: string) { const edkBytes = decodeBase64(edk_b64); - const kms = getKmsClient(); + const kms = await getKmsClient(); const out = await kms.send(new DecryptCommand({ CiphertextBlob: edkBytes })); if (!out.Plaintext) throw new Error("KMS Decrypt failed"); return { diff --git a/packages/stack-shared/src/interface/client-interface.test.ts b/packages/stack-shared/src/interface/client-interface.test.ts index 84e5be3ae8..de5442c1c6 100644 --- a/packages/stack-shared/src/interface/client-interface.test.ts +++ b/packages/stack-shared/src/interface/client-interface.test.ts @@ -4,10 +4,17 @@ import { InternalSession } from "../sessions"; import { Result } from "../utils/results"; import { StackClientInterface } from "./client-interface"; -function createClientInterface() { +function createClientInterface(options?: { + baseUrl?: string, + apiUrls?: string[], + probeRate?: number, +}) { + const apiUrls = options?.apiUrls ?? [options?.baseUrl ?? "https://api.example.com"]; return new StackClientInterface({ clientVersion: "test", - getBaseUrl: () => "https://api.example.com", + getBaseUrl: () => apiUrls[0], + getApiUrls: () => apiUrls, + probeRate: options?.probeRate, extraRequestHeaders: {}, projectId: "project-id", publishableClientKey: "publishable-client-key", @@ -339,3 +346,269 @@ describe("StackClientInterface bot challenge compatibility", () => { }); }); }); + +describe("_withFallback", () => { + // --------------------------------------------------------------------------- + // Helpers — reduce boilerplate across tests + // --------------------------------------------------------------------------- + + /** Builds a list of N URL bases: ["https://url-0.test", "https://url-1.test", ...] */ + function urlList(n: number): string[] { + return Array.from({ length: n }, (_, i) => `https://url-${i}.test`); + } + + /** Returns the index of the URL base that `fullUrl` starts with, or -1. */ + function urlIndex(urls: string[], fullUrl: string): number { + return urls.findIndex(base => fullUrl.startsWith(base)); + } + + /** Records every fetch URL and calls `handler` to decide the outcome. */ + function mockFetch(handler: (url: string) => "ok" | "fail") { + const log: string[] = []; + vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => { + const url = input.toString(); + log.push(url); + if (handler(url) === "fail") throw new TypeError("Failed to fetch"); + return createJsonResponse({ display_name: "test" }); + })); + return log; + } + + function sendRequest(iface: StackClientInterface) { + const session = iface.createSession({ refreshToken: null, accessToken: null }); + return iface.sendClientRequest("/users/me", { method: "GET" }, session); + } + + // --------------------------------------------------------------------------- + // Single URL — no fallback + // --------------------------------------------------------------------------- + + it("single URL uses standard 5-retry behavior", async () => { + let attempts = 0; + vi.stubGlobal("fetch", vi.fn(async () => { + attempts++; + if (attempts < 3) throw new TypeError("Failed to fetch"); + return createJsonResponse({ display_name: "test" }); + })); + + const iface = createClientInterface({ apiUrls: urlList(1) }); + await sendRequest(iface); + expect(attempts).toBe(3); + }); + + // --------------------------------------------------------------------------- + // Normal mode — iterating through URLs in order + // --------------------------------------------------------------------------- + + it("uses primary when it is healthy", async () => { + const urls = urlList(3); + const log = mockFetch(() => "ok"); + + const iface = createClientInterface({ apiUrls: urls }); + await sendRequest(iface); + + expect(log.every(u => urlIndex(urls, u) === 0)).toBe(true); + }); + + it("tries URLs in order and succeeds on first working one", async () => { + const urls = urlList(4); + // url-0 and url-1 are down, url-2 is up + const log = mockFetch((u) => urlIndex(urls, u) < 2 ? "fail" : "ok"); + + const iface = createClientInterface({ apiUrls: urls }); + await sendRequest(iface); + + expect(urlIndex(urls, log[0])).toBe(0); + expect(urlIndex(urls, log[1])).toBe(1); + expect(urlIndex(urls, log[2])).toBe(2); + expect(log.length).toBe(3); + }); + + it("does not fall back on KnownError", async () => { + const urls = urlList(3); + const log: string[] = []; + vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => { + log.push(input.toString()); + return createKnownErrorResponse(new KnownErrors.UserNotFound()); + })); + + const iface = createClientInterface({ apiUrls: urls }); + await expect(sendRequest(iface)).rejects.toThrow(); + expect(log.every(u => urlIndex(urls, u) === 0)).toBe(true); + }); + + it("makes 2 passes × N URLs attempts before throwing", async () => { + for (const n of [2, 3, 5]) { + const urls = urlList(n); + const log = mockFetch(() => "fail"); + + const iface = createClientInterface({ apiUrls: urls }); + await expect(sendRequest(iface)).rejects.toThrow(); + + expect(log.length).toBe(2 * n); + for (let i = 0; i < n; i++) { + expect(log.filter(u => urlIndex(urls, u) === i).length).toBe(2); + } + } + }); + + it("bypasses fallback when apiUrlOverride is provided", async () => { + const log: string[] = []; + vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => { + log.push(input.toString()); + return createJsonResponse({ display_name: "test" }); + })); + + const iface = createClientInterface({ apiUrls: urlList(3) }); + const session = iface.createSession({ refreshToken: null, accessToken: null }); + await iface.sendClientRequest("/users/me", { method: "GET" }, session, "client", "https://override.test/api/v1"); + + expect(log.every(u => u.startsWith("https://override.test"))).toBe(true); + }); + + // --------------------------------------------------------------------------- + // Sticky mode — remembering the working fallback + // --------------------------------------------------------------------------- + + it("enters sticky mode: subsequent requests skip straight to the working fallback", async () => { + const urls = urlList(4); + const iface = createClientInterface({ apiUrls: urls, probeRate: 0 }); + + // url-0,1,2 down → sticky on url-3 + mockFetch((u) => urlIndex(urls, u) === 3 ? "ok" : "fail"); + await sendRequest(iface); + + // Next request goes directly to url-3 (probeRate=0 means no probe) + const log = mockFetch(() => "ok"); + await sendRequest(iface); + + expect(log.length).toBe(1); + expect(urlIndex(urls, log[0])).toBe(3); + }); + + it("probes primary and exits sticky mode when probe succeeds", async () => { + const urls = urlList(3); + const iface = createClientInterface({ apiUrls: urls, probeRate: 1 }); + + // url-0 down → sticky on url-1 + mockFetch((u) => urlIndex(urls, u) === 0 ? "fail" : "ok"); + await sendRequest(iface); + + // url-0 recovers. probeRate=1 → always probes → probe succeeds → exits sticky + const log = mockFetch(() => "ok"); + await sendRequest(iface); + expect(urlIndex(urls, log[0])).toBe(0); + + // Next request should go to url-0 directly (no longer sticky) + const log2 = mockFetch(() => "ok"); + await sendRequest(iface); + expect(log2.length).toBe(1); + expect(urlIndex(urls, log2[0])).toBe(0); + }); + + it("halves probe rate on each failed probe", async () => { + const urls = urlList(2); + const iface = createClientInterface({ apiUrls: urls, probeRate: 1 }); + + // Enter sticky on url-1, url-0 stays permanently down + mockFetch((u) => urlIndex(urls, u) === 0 ? "fail" : "ok"); + await sendRequest(iface); + + // probeRate=1 → probes url-0, fails → rate becomes 0.5 + mockFetch((u) => urlIndex(urls, u) === 0 ? "fail" : "ok"); + await sendRequest(iface); + + // probeRate=0.5 → probes (random < 0.5), fails → rate becomes 0.25 + const randomMock = vi.spyOn(Math, "random").mockReturnValue(0.4); + mockFetch((u) => urlIndex(urls, u) === 0 ? "fail" : "ok"); + await sendRequest(iface); + + // rate=0.25, random=0.3 ≥ 0.25 → should NOT probe primary + randomMock.mockReturnValue(0.3); + const log = mockFetch(() => "ok"); + await sendRequest(iface); + + expect(log.length).toBe(1); + expect(urlIndex(urls, log[0])).toBe(1); + + randomMock.mockRestore(); + }); + + // --------------------------------------------------------------------------- + // Sticky URL goes down — falls through to full iteration + // --------------------------------------------------------------------------- + + it("falls through to full iteration when sticky URL also goes down", async () => { + const urls = urlList(3); + const iface = createClientInterface({ apiUrls: urls, probeRate: 0 }); + + // url-0,1 down → sticky on url-2 + mockFetch((u) => urlIndex(urls, u) === 2 ? "ok" : "fail"); + await sendRequest(iface); + + // Now url-2 also down, url-1 recovers + const log = mockFetch((u) => urlIndex(urls, u) === 1 ? "ok" : "fail"); + await sendRequest(iface); + + // Should have tried sticky url-2 (failed), then iterated 0→1 (found url-1) + expect(log.some(u => urlIndex(urls, u) === 2)).toBe(true); + expect(log.some(u => urlIndex(urls, u) === 1)).toBe(true); + }); + + it("re-enters sticky on the new working URL after fallthrough", async () => { + const urls = urlList(4); + const iface = createClientInterface({ apiUrls: urls, probeRate: 0 }); + + // sticky on url-3 + mockFetch((u) => urlIndex(urls, u) === 3 ? "ok" : "fail"); + await sendRequest(iface); + + // url-3 dies, url-2 recovers → should re-sticky on url-2 + mockFetch((u) => urlIndex(urls, u) === 2 ? "ok" : "fail"); + await sendRequest(iface); + + // Next request goes directly to url-2 + const log = mockFetch(() => "ok"); + await sendRequest(iface); + + expect(log.length).toBe(1); + expect(urlIndex(urls, log[0])).toBe(2); + }); + + it("throws when sticky URL fails and all URLs fail in iteration", async () => { + const urls = urlList(3); + const iface = createClientInterface({ apiUrls: urls, probeRate: 0 }); + + // sticky on url-1 + mockFetch((u) => urlIndex(urls, u) === 1 ? "ok" : "fail"); + await sendRequest(iface); + + // Everything is now down + const log = mockFetch(() => "fail"); + await expect(sendRequest(iface)).rejects.toThrow(); + + // sticky attempt (1) + 2 passes × 3 URLs (6) = 7 + expect(log.length).toBe(7); + }); + + it("does not probe primary when sticky URL fails (probe only before sticky attempt)", async () => { + const urls = urlList(3); + const iface = createClientInterface({ apiUrls: urls, probeRate: 1 }); + + // sticky on url-2, url-0 stays down + mockFetch((u) => urlIndex(urls, u) === 2 ? "ok" : "fail"); + await sendRequest(iface); + + // url-0 still down, url-2 also dies, url-1 is the only one up + // probeRate=1 → probes url-0 first (fails), then tries sticky url-2 (fails), + // then full iteration finds url-1 + const log = mockFetch((u) => urlIndex(urls, u) === 1 ? "ok" : "fail"); + await sendRequest(iface); + + const hitOrder = log.map(u => urlIndex(urls, u)); + // probe url-0, sticky url-2, then iteration: 0, 1 (succeeds) + expect(hitOrder[0]).toBe(0); // probe + expect(hitOrder[1]).toBe(2); // sticky attempt + expect(hitOrder).toContain(1); // found during iteration + }); +}); diff --git a/packages/stack-shared/src/interface/client-interface.ts b/packages/stack-shared/src/interface/client-interface.ts index a0d69deb71..b7fb192dd0 100644 --- a/packages/stack-shared/src/interface/client-interface.ts +++ b/packages/stack-shared/src/interface/client-interface.ts @@ -38,6 +38,18 @@ export type ClientInterfaceOptions = { // This is a function instead of a string because it might be different based on the environment (for example client vs server) getBaseUrl: () => string, getAnalyticsBaseUrl?: () => string, + /** + * Ordered list of base URLs for request routing with fallback. + * Index 0 = primary, index 1..N = fallbacks in priority order. + * A single-element array means no fallback occurs. + */ + getApiUrls: () => string[], + /** + * When a fallback succeeds and becomes the active server, this is the initial probability + * (0–1) that any given request will probe the primary to check if it's back. + * Halves on each failed probe, resets on success. Default: 0.3 (30%). + */ + probeRate?: number, extraRequestHeaders: Record, projectId: string, prepareRequest?: () => Promise, @@ -111,8 +123,15 @@ function getBotChallengeRequestFields(botChallenge: BotChallengeInput | undefine export class StackClientInterface { private pendingNetworkDiagnostics?: ReturnType; + /** + * Fallback state. When null, we're in normal mode (primary first). + * When set, we skip straight to `stickyIndex` and only probe primary occasionally. + */ + private _sticky: { index: number, probeRate: number } | null = null; + private readonly _initialProbeRate: number; + constructor(public readonly options: ClientInterfaceOptions) { - // nothing here + this._initialProbeRate = options.probeRate ?? 0.3; } get projectId() { @@ -123,6 +142,123 @@ export class StackClientInterface { return this.options.getBaseUrl() + "/api/v1"; } + getApiUrls(): string[] { + return this.options.getApiUrls().map(u => u + "/api/v1"); + } + + /** + * Returns the best-known-good API URL: the sticky fallback if we're in + * fallback mode, otherwise the primary. Use for browser-navigated URLs + * (e.g. OAuth authorize) where _withFallback can't help. + */ + getBestApiUrl(): string { + const apiUrls = this.getApiUrls(); + if (this._sticky && apiUrls[this._sticky.index]) { + return apiUrls[this._sticky.index]; + } + return apiUrls[0]; + } + + /** + * Routes a request through an ordered URL list with automatic failover. + * + * The URL list is [primary, ...fallbacks]. The logic has two modes: + * + * **Normal mode** (`_sticky` is null) — try each URL in order. If a + * non-primary URL succeeds, enter sticky mode on that index. + * + * **Sticky mode** — a previous request already failed over. We remember + * which URL worked and go there directly. Occasionally (controlled by a + * decaying probe rate) we probe the primary to see if it recovered: + * - Probe succeeds → exit sticky mode, use result. + * - Probe fails → halve probe rate, use sticky URL. + * - Sticky URL fails → exit sticky mode, do a full iteration. + * + * In both modes, a full iteration tries every URL once per pass for 2 + * passes before giving up. KnownErrors are never retried (they're + * application-level, not network-level). + * + * Single-URL lists skip all of this and use 5-retry behavior directly. + */ + protected async _withFallback(cb: (apiUrl: string, retryOptions: { maxAttempts: number, skipDiagnostics: boolean }) => Promise): Promise { + const apiUrls = this.getApiUrls(); + + // Single URL — no fallback, just retry normally. + if (apiUrls.length <= 1) { + return await cb(apiUrls[0], { maxAttempts: 5, skipDiagnostics: false }); + } + + // If we're in sticky mode, try the remembered URL (with an optional primary probe first). + if (this._sticky) { + const result = await this._tryStickyUrl(apiUrls, cb); + if (result !== undefined) return result; + // Sticky URL failed — _sticky was cleared, fall through to full iteration. + } + + // Full iteration: try every URL in order, 2 passes. + return await this._iterateUrls(apiUrls, cb); + } + + /** + * Attempts the sticky URL, optionally probing primary first. + * Returns the result on success, or `undefined` if we should fall through to full iteration. + */ + private async _tryStickyUrl( + apiUrls: string[], + cb: (apiUrl: string, retryOptions: { maxAttempts: number, skipDiagnostics: boolean }) => Promise, + ): Promise { + const sticky = this._sticky!; + + // Probabilistically probe primary to check if it's back. + if (Math.random() < sticky.probeRate) { + try { + const result = await cb(apiUrls[0], { maxAttempts: 1, skipDiagnostics: true }); + this._sticky = null; + return result; + } catch (e) { + if (e instanceof KnownError) throw e; + sticky.probeRate = Math.max(sticky.probeRate * 0.5, 0.01); + } + } + + // Try the sticky URL itself. + try { + return await cb(apiUrls[sticky.index], { maxAttempts: 1, skipDiagnostics: true }); + } catch (e) { + if (e instanceof KnownError) throw e; + this._sticky = null; + return undefined; + } + } + + /** + * Tries every URL in order for up to 2 passes. + * If a non-primary URL (index > 0) succeeds, enters sticky mode on it. + */ + private async _iterateUrls( + apiUrls: string[], + cb: (apiUrl: string, retryOptions: { maxAttempts: number, skipDiagnostics: boolean }) => Promise, + ): Promise { + let lastError: Error | undefined; + + for (let pass = 0; pass < 2; pass++) { + for (let i = 0; i < apiUrls.length; i++) { + try { + const result = await cb(apiUrls[i], { maxAttempts: 1, skipDiagnostics: true }); + if (i > 0) { + this._sticky = { index: i, probeRate: this._initialProbeRate }; + } + return result; + } catch (e) { + if (e instanceof KnownError) throw e; + lastError = e instanceof Error ? e : new Error(String(e)); + } + } + } + + throw lastError!; + } + getAnalyticsApiUrl() { return (this.options.getAnalyticsBaseUrl ?? this.options.getBaseUrl)() + "/api/v1"; } @@ -194,10 +330,15 @@ export class StackClientInterface { `, { cause: cause }); } - protected async _networkRetry(cb: () => Promise>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise { + protected async _networkRetry( + cb: () => Promise>, + session?: InternalSession | null, + requestType?: "client" | "server" | "admin", + options?: { maxAttempts?: number, skipDiagnostics?: boolean }, + ): Promise { const retriedResult = await Result.retry( cb, - 5, + options?.maxAttempts ?? 5, { exponentialDelayBase: 1000 }, ); @@ -206,13 +347,21 @@ export class StackClientInterface { if (globalVar.navigator && globalVar.navigator.onLine === false) { throw new Error("You are offline. Please check your internet connection and try again. (window.navigator.onLine is false)", { cause: retriedResult.error }); } + if (options?.skipDiagnostics) { + throw retriedResult.error; + } throw await this._createNetworkError(retriedResult.error, session, requestType); } return retriedResult.data; } - protected async _networkRetryException(cb: () => Promise, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise { - return await this._networkRetry(async () => await Result.fromThrowingAsync(cb), session, requestType); + protected async _networkRetryException( + cb: () => Promise, + session?: InternalSession | null, + requestType?: "client" | "server" | "admin", + options?: { maxAttempts?: number, skipDiagnostics?: boolean }, + ): Promise { + return await this._networkRetry(async () => await Result.fromThrowingAsync(cb), session, requestType, options); } public async fetchNewAccessToken(refreshToken: RefreshToken) { @@ -222,7 +371,13 @@ export class StackClientInterface { } const clientSecret = this.options.publishableClientKey ?? publishableClientKeyNotNecessarySentinel; - const tokenEndpoint = this.getApiUrl() + '/auth/oauth/token'; + return await this._withFallback(async (apiUrl, retryOptions) => { + return await this._fetchNewAccessTokenInner(refreshToken, clientSecret, apiUrl, retryOptions); + }); + } + + private async _fetchNewAccessTokenInner(refreshToken: RefreshToken, clientSecret: string, apiUrl: string, retryOptions?: { maxAttempts?: number, skipDiagnostics?: boolean }) { + const tokenEndpoint = apiUrl + '/auth/oauth/token'; const as = { issuer: this.options.getBaseUrl(), algorithm: 'oauth2', @@ -262,7 +417,7 @@ export class StackClientInterface { } return response.data; - }); + }, undefined, undefined, retryOptions); if (!response) return null; let result: oauth.TokenEndpointResponse; @@ -284,7 +439,6 @@ export class StackClientInterface { } return AccessToken.createIfValid(result.access_token) ?? throwErr("Access token in fetchNewAccessToken is invalid, looks like the backend is returning an invalid token!", { result }); - } public async sendClientRequest( @@ -298,11 +452,22 @@ export class StackClientInterface { refreshToken: null, }); - return await this._networkRetry( - () => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrlOverride), - session, - requestType, - ); + if (apiUrlOverride) { + return await this._networkRetry( + () => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrlOverride), + session, + requestType, + ); + } + + return await this._withFallback(async (apiUrl, retryOptions) => { + return await this._networkRetry( + () => this.sendClientRequestInner(path, requestOptions, session!, requestType, apiUrl), + session, + requestType, + retryOptions, + ); + }); } public createSession(options: Omit[0], "refreshAccessTokenCallback">): InternalSession { @@ -1131,7 +1296,7 @@ export class StackClientInterface { throw new Error("Admin session token is currently not supported for OAuth"); } const clientSecret = this.options.publishableClientKey ?? publishableClientKeyNotNecessarySentinel; - const url = new URL(this.getApiUrl() + "/auth/oauth/authorize/" + options.provider.toLowerCase()); + const url = new URL(this.getBestApiUrl() + "/auth/oauth/authorize/" + options.provider.toLowerCase()); url.searchParams.set("client_id", this.projectId); url.searchParams.set("client_secret", clientSecret); url.searchParams.set("redirect_uri", updatedRedirectUrl.toString()); @@ -1232,8 +1397,20 @@ export class StackClientInterface { // TODO fix throw new Error("Admin session token is currently not supported for OAuth"); } + const clientSecret = this.options.publishableClientKey ?? publishableClientKeyNotNecessarySentinel; - const tokenEndpoint = this.getApiUrl() + '/auth/oauth/token'; + return await this._withFallback(async (apiUrl) => { + return await this._callOAuthCallbackInner(options, clientSecret, apiUrl); + }); + } + + private async _callOAuthCallbackInner(options: { + oauthParams: URLSearchParams, + redirectUri: string, + codeVerifier: string, + state: string, + }, clientSecret: string, apiUrl: string): Promise<{ newUser: boolean, afterCallbackRedirectUrl?: string, accessToken: string, refreshToken: string }> { + const tokenEndpoint = apiUrl + '/auth/oauth/token'; const as = { issuer: this.options.getBaseUrl(), algorithm: 'oauth2', diff --git a/packages/stack-shared/src/utils/urls.tsx b/packages/stack-shared/src/utils/urls.tsx index cb19763faa..d4c21caead 100644 --- a/packages/stack-shared/src/utils/urls.tsx +++ b/packages/stack-shared/src/utils/urls.tsx @@ -196,6 +196,24 @@ import.meta.vitest?.test("matchHostnamePattern", ({ expect }) => { expect(matchHostnamePattern("*.*.org", "example.org")).toBe(false); }); +export function getHardcodedFallbackUrls(primaryBaseUrl: string): string[] { + if (primaryBaseUrl === "https://api.stack-auth.com") { + return ["https://api1.stack-auth.com", "https://api2.stack-auth.com"]; + } + if (primaryBaseUrl === "https://api.dev.stack-auth.com") { + return ["https://api1.dev.stack-auth.com", "https://api2.dev.stack-auth.com"]; + } + const localhostMatch = primaryBaseUrl.match(/^http:\/\/localhost:(\d+)02$/); + if (localhostMatch) { + return [`http://localhost:${localhostMatch[1]}10`]; + } + return []; +} + +export function getDefaultApiUrls(primaryBaseUrl: string): string[] { + return [primaryBaseUrl, ...getHardcodedFallbackUrls(primaryBaseUrl)]; +} + export function isLocalhost(urlOrString: string | URL) { const url = createUrlIfValid(urlOrString); if (!url) return false; diff --git a/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts b/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts index 9ea25231db..2755c1a972 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts @@ -22,7 +22,7 @@ import { AdminProjectPermission, AdminProjectPermissionDefinition, AdminProjectP import { AdminOwnedProject, AdminProject, AdminProjectUpdateOptions, PushConfigOptions, adminProjectUpdateOptionsToCrud } from "../../projects"; import type { AdminSessionReplay, AdminSessionReplayChunk, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, ListSessionReplaysOptions, ListSessionReplaysResult, SessionReplayAllEventsResult } from "../../session-replays"; import { ManagedEmailProviderListItem, ManagedEmailProviderSetupResult, ManagedEmailProviderStatus, EmailOutboxUpdateOptions, StackAdminApp, StackAdminAppConstructorOptions } from "../interfaces/admin-app"; -import { clientVersion, createCache, getBaseUrl, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, getDefaultSuperSecretAdminKey, resolveConstructorOptions } from "./common"; +import { clientVersion, createCache, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, getDefaultSuperSecretAdminKey, resolveApiUrls, resolveConstructorOptions } from "./common"; import { _StackServerAppImplIncomplete } from "./server-app-impl"; import { CompleteConfig, EnvironmentConfigOverrideOverride } from "@stackframe/stack-shared/dist/config/schema"; @@ -130,19 +130,23 @@ export class _StackAdminAppImplIncomplete getBaseUrl(resolvedOptions.baseUrl), - projectId: resolvedOptions.projectId ?? getDefaultProjectId(), - extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(), - clientVersion, - ...resolvedOptions.projectOwnerSession ? { - projectOwnerSession: resolvedOptions.projectOwnerSession, - } : { - ...(publishableClientKey ? { publishableClientKey } : {}), - secretServerKey: resolvedOptions.secretServerKey ?? getDefaultSecretServerKey(), - superSecretAdminKey: resolvedOptions.superSecretAdminKey ?? getDefaultSuperSecretAdminKey(), - }, - }), + interface: extraOptions?.interface ?? (() => { + const apiUrls = resolveApiUrls(resolvedOptions.baseUrl); + return new StackAdminInterface({ + getBaseUrl: () => apiUrls()[0], + getApiUrls: apiUrls, + projectId: resolvedOptions.projectId ?? getDefaultProjectId(), + extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(), + clientVersion, + ...resolvedOptions.projectOwnerSession ? { + projectOwnerSession: resolvedOptions.projectOwnerSession, + } : { + ...(publishableClientKey ? { publishableClientKey } : {}), + secretServerKey: resolvedOptions.secretServerKey ?? getDefaultSecretServerKey(), + superSecretAdminKey: resolvedOptions.superSecretAdminKey ?? getDefaultSuperSecretAdminKey(), + }, + }); + })(), }); } diff --git a/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts b/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts index e2eb3b8c86..12386ae464 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts @@ -56,7 +56,7 @@ import { isHostedHandlerUrlForProject, resolveHandlerUrls } from "../../url-targ import { ActiveSession, Auth, BaseUser, CurrentUser, InternalUserExtra, OAuthProvider, ProjectCurrentUser, SyncedPartialUser, TokenPartialUser, UserExtra, UserUpdateOptions, userUpdateOptionsToCrud, withUserDestructureGuard } from "../../users"; import { StackClientApp, StackClientAppConstructorOptions, StackClientAppJson } from "../interfaces/client-app"; import { _StackAdminAppImplIncomplete } from "./admin-app-impl"; -import { TokenObject, clientVersion, createCache, createCacheBySession, createEmptyTokenStore, getAnalyticsBaseUrl, getBaseUrl, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getUrls, resolveConstructorOptions } from "./common"; +import { TokenObject, clientVersion, createCache, createCacheBySession, createEmptyTokenStore, getAnalyticsBaseUrl, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getUrls, resolveApiUrls, resolveConstructorOptions } from "./common"; import { EventTracker } from "./event-tracker"; import { crossDomainAuthQueryParams, getCrossDomainHandoffParamsFromCurrentUrl, planRedirectToHandler } from "./redirect-page-urls"; import type { CrossDomainHandoffParams } from "./redirect-page-urls"; @@ -516,9 +516,11 @@ export class _StackClientAppImplIncomplete getBaseUrl(resolvedOptions.baseUrl), - getAnalyticsBaseUrl: () => getAnalyticsBaseUrl(getBaseUrl(resolvedOptions.baseUrl)), + getBaseUrl: () => apiUrls()[0], + getAnalyticsBaseUrl: () => getAnalyticsBaseUrl(apiUrls()[0]), + getApiUrls: apiUrls, extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(), projectId, clientVersion, diff --git a/packages/template/src/lib/stack-app/apps/implementations/common.ts b/packages/template/src/lib/stack-app/apps/implementations/common.ts index 0f83c1bd2a..960264e586 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/common.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/common.ts @@ -1,13 +1,15 @@ import { InternalSession } from "@stackframe/stack-shared/dist/sessions"; import { AsyncCache } from "@stackframe/stack-shared/dist/utils/caches"; import { isBrowserLike } from "@stackframe/stack-shared/dist/utils/env"; -import { StackAssertionError, concatStacktraces, throwErr } from "@stackframe/stack-shared/dist/utils/errors"; -import { getGlobal } from "@stackframe/stack-shared/dist/utils/globals"; +import { StackAssertionError, captureError, concatStacktraces, throwErr } from "@stackframe/stack-shared/dist/utils/errors"; +import { createGlobal, getGlobal } from "@stackframe/stack-shared/dist/utils/globals"; +import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; import { filterUndefined, omit } from "@stackframe/stack-shared/dist/utils/objects"; import { ReactPromise } from "@stackframe/stack-shared/dist/utils/promises"; import { suspendIfSsr, use } from "@stackframe/stack-shared/dist/utils/react"; import { Result } from "@stackframe/stack-shared/dist/utils/results"; import { Store } from "@stackframe/stack-shared/dist/utils/stores"; +import { getDefaultApiUrls } from "@stackframe/stack-shared/dist/utils/urls"; import React, { useCallback } from "react"; // THIS_LINE_PLATFORM react-like import { envVars } from "../../../env"; import { HandlerUrlOptions, ResolvedHandlerUrls, stackAppInternalsSymbol } from "../../common"; @@ -129,6 +131,42 @@ export function getAnalyticsBaseUrl(regularBaseUrl: string): string { return regularBaseUrl === defaultBaseUrl ? defaultAnalyticsBaseUrl : regularBaseUrl; } + +function fetchBackendUrlsInBackground(primaryBaseUrl: string): void { + createGlobal('__stack-fetch-backend-urls-started', () => { + runAsynchronously(async () => { + try { + const res = await fetch(`${primaryBaseUrl}/api/v1/internal/backend-urls`); + if (!res.ok) { + return; + } + const data = await res.json(); + if (!Array.isArray(data.urls) || !data.urls.every((u: unknown) => typeof u === 'string')) { + return; + } + createGlobal('__stack-fetched-backend-urls', () => data.urls as string[]); + } catch (e) { + captureError('fetch-backend-urls-in-background', e); + } + }); + return true; + }); +} + +export function resolveApiUrls(userExplicitBaseUrl: string | { browser: string, server: string } | undefined): () => string[] { + return () => { + if (userExplicitBaseUrl != null) { + return [getBaseUrl(userExplicitBaseUrl)]; + } + const primary = getBaseUrl(undefined); + // Always try to fetch server-configured URLs (supports custom domains via + // STACK_BACKEND_URLS_CONFIG). Hardcoded fallbacks are used as a default + // until the background fetch completes. + fetchBackendUrlsInBackground(primary); + return getGlobal('__stack-fetched-backend-urls') ?? getDefaultApiUrls(primary); + }; +} + export type TokenObject = { accessToken: string | null, refreshToken: string | null, diff --git a/packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts b/packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts index b2ebc35554..184190d34d 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts @@ -35,7 +35,7 @@ import { EditableTeamMemberProfile, ReceivedTeamInvitation, SentTeamInvitation, import { ProjectCurrentServerUser, ServerOAuthProvider, ServerUser, ServerUserCreateOptions, ServerUserUpdateOptions, serverUserCreateOptionsToCrud, serverUserUpdateOptionsToCrud, withUserDestructureGuard } from "../../users"; import { StackServerAppConstructorOptions } from "../interfaces/server-app"; import { _StackClientAppImplIncomplete } from "./client-app-impl"; -import { clientVersion, createCache, createCacheBySession, getBaseUrl, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, resolveConstructorOptions } from "./common"; +import { clientVersion, createCache, createCacheBySession, getDefaultExtraRequestHeaders, getDefaultProjectId, getDefaultPublishableClientKey, getDefaultSecretServerKey, resolveApiUrls, resolveConstructorOptions } from "./common"; import { useAsyncCache } from "./common"; // THIS_LINE_PLATFORM react-like @@ -421,14 +421,18 @@ export class _StackServerAppImplIncomplete getBaseUrl(resolvedOptions.baseUrl), - projectId: resolvedOptions.projectId ?? getDefaultProjectId(), - extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(), - clientVersion, - ...(publishableClientKey != null ? { publishableClientKey } : {}), - secretServerKey: resolvedOptions.secretServerKey ?? getDefaultSecretServerKey(), - }), + interface: extraOptions?.interface ?? (() => { + const apiUrls = resolveApiUrls(resolvedOptions.baseUrl); + return new StackServerInterface({ + getBaseUrl: () => apiUrls()[0], + getApiUrls: apiUrls, + projectId: resolvedOptions.projectId ?? getDefaultProjectId(), + extraRequestHeaders: resolvedOptions.extraRequestHeaders ?? getDefaultExtraRequestHeaders(), + clientVersion, + ...(publishableClientKey != null ? { publishableClientKey } : {}), + secretServerKey: resolvedOptions.secretServerKey ?? getDefaultSecretServerKey(), + }); + })(), }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a7cebe56e..b3adb2d53c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,10 +203,10 @@ importers: version: 2.8.2 '@vercel/functions': specifier: ^2.0.0 - version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.876.0) + version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.972.27) '@vercel/otel': specifier: ^1.10.4 - version: 1.10.4(@opentelemetry/api-logs@0.53.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0)) + version: 1.10.4(@opentelemetry/api-logs@0.53.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0)) '@vercel/sandbox': specifier: ^1.2.0 version: 1.2.0 @@ -474,7 +474,7 @@ importers: version: 2.0.2(react@19.2.3) '@sentry/nextjs': specifier: ^10.11.0 - version: 10.11.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2)) + version: 10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2)) '@stackframe/dashboard-ui-components': specifier: workspace:* version: link:../../packages/dashboard-ui-components @@ -2050,6 +2050,9 @@ importers: '@aws-sdk/client-kms': specifier: ^3.876.0 version: 3.876.0 + '@aws-sdk/credential-provider-web-identity': + specifier: ^3.972.27 + version: 3.972.27 '@babel/core': specifier: ^7.28.5 version: 7.28.5 @@ -2079,7 +2082,7 @@ importers: version: 18.3.1 '@vercel/functions': specifier: ^2.0.0 - version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.876.0) + version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.972.27) async-mutex: specifier: ^0.5.0 version: 0.5.0 @@ -2116,7 +2119,7 @@ importers: devDependencies: '@sentry/nextjs': specifier: ^10.11.0 - version: 10.11.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(webpack@5.92.0(esbuild@0.24.2)) + version: 10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(webpack@5.92.0(esbuild@0.24.2)) '@simplewebauthn/types': specifier: ^11.0.0 version: 11.0.0 @@ -2638,6 +2641,10 @@ packages: resolution: {integrity: sha512-sVFBFkdoPOPyY13NaXO1E/R9O5J6ixzHnnRbqrbXYM2QQgLNPTKIiRtmVEuVoFV9YULg+/aKm7caix8m468y9w==} engines: {node: '>=18.0.0'} + '@aws-sdk/core@3.973.26': + resolution: {integrity: sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.864.0': resolution: {integrity: sha512-StJPOI2Rt8UE6lYjXUpg6tqSZaM72xg46ljPg8kIevtBAAfdtq9K20qT/kSliWGIBocMFAv0g2mC0hAa+ECyvg==} engines: {node: '>=18.0.0'} @@ -2694,6 +2701,10 @@ packages: resolution: {integrity: sha512-q/XSCP1uae5aB9veM8zcm6Gqu6A4ckX9ZbhHgCzURXVJDwp+nINW1hM9vppMjGw3ND9Ibx/adR+KfTI0TDMzqw==} engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.27': + resolution: {integrity: sha512-CUY4hQIFswdQNEsRGEzGBUKGMK5KpqmNDdu2ROMgI+45PLFS8H0y3Tm7kvM16uvvw3n1pVxk85tnRVUTgtaa1w==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.862.0': resolution: {integrity: sha512-Wcsc7VPLjImQw+CP1/YkwyofMs9Ab6dVq96iS8p0zv0C6YTaMjvillkau4zFfrrrTshdzFWKptIFhKK8Zsei1g==} engines: {node: '>=18.0.0'} @@ -2714,6 +2725,10 @@ packages: resolution: {integrity: sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-location-constraint@3.862.0': resolution: {integrity: sha512-MnwLxCw7Cc9OngEH3SHFhrLlDI9WVxaBkp3oTsdY9JE7v8OE38wQ9vtjaRsynjwu0WRtrctSHbpd7h/QVvtjyA==} engines: {node: '>=18.0.0'} @@ -2726,6 +2741,10 @@ packages: resolution: {integrity: sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-recursion-detection@3.862.0': resolution: {integrity: sha512-KVoo3IOzEkTq97YKM4uxZcYFSNnMkhW/qj22csofLegZi5fk90ztUnnaeKfaEJHfHp/tm1Y3uSoOXH45s++kKQ==} engines: {node: '>=18.0.0'} @@ -2734,6 +2753,10 @@ packages: resolution: {integrity: sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-sdk-s3@3.864.0': resolution: {integrity: sha512-GjYPZ6Xnqo17NnC8NIQyvvdzzO7dm+Ks7gpxD/HsbXPmV2aEfuFveJXneGW9e1BheSKFff6FPDWu8Gaj2Iu1yg==} engines: {node: '>=18.0.0'} @@ -2750,6 +2773,10 @@ packages: resolution: {integrity: sha512-FR+8INfnbNv32QDQ5szxkWX6mB/QgezfNyx8LnAh1ErISZMmEFBxXXir+ZOfuV8vsmal1a6cy9qmnMNDaNnaNQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-user-agent@3.972.27': + resolution: {integrity: sha512-TIRLO5UR2+FVUGmhYoAwVkKhcVzywEDX/5LzR9tjy1h8FQAXOtFg2IqgmwvxU7y933rkTn9rl6AdgcAUgQ1/Kg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/nested-clients@3.864.0': resolution: {integrity: sha512-H1C+NjSmz2y8Tbgh7Yy89J20yD/hVyk15hNoZDbCYkXg0M358KS7KVIEYs8E2aPOCr1sK3HBE819D/yvdMgokA==} engines: {node: '>=18.0.0'} @@ -2758,6 +2785,10 @@ packages: resolution: {integrity: sha512-R4TZrkM2gUElTsotk8mt3y7iLG8TNi1LL1wgVdEEWSLOYTaFyglGdoNBMtEeP7lmXilaTy00AbYF6BakJvSTHg==} engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.996.17': + resolution: {integrity: sha512-7B0HIX0tEFmOSJuWzdHZj1WhMXSryM+h66h96ZkqSncoY7J6wq61KOu4Kr57b/YnJP3J/EeQYVFulgR281h+7A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.862.0': resolution: {integrity: sha512-VisR+/HuVFICrBPY+q9novEiE4b3mvDofWqyvmxHcWM7HumTz9ZQSuEtnlB/92GVM3KDUrR9EmBHNRrfXYZkcQ==} engines: {node: '>=18.0.0'} @@ -2766,6 +2797,10 @@ packages: resolution: {integrity: sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==} engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/signature-v4-multi-region@3.864.0': resolution: {integrity: sha512-w2HIn/WIcUyv1bmyCpRUKHXB5KdFGzyxPkp/YK5g+/FuGdnFFYWGfcO8O+How4jwrZTarBYsAHW9ggoKvwr37w==} engines: {node: '>=18.0.0'} @@ -2782,6 +2817,10 @@ packages: resolution: {integrity: sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==} engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-arn-parser@3.804.0': resolution: {integrity: sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==} engines: {node: '>=18.0.0'} @@ -2794,6 +2833,10 @@ packages: resolution: {integrity: sha512-YByHrhjxYdjKRf/RQygRK1uh0As1FIi9+jXTcIEX/rBgN8mUByczr2u4QXBzw7ZdbdcOBMOkPnLRjNOWW1MkFg==} engines: {node: '>=18.0.0'} + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-locate-window@3.804.0': resolution: {integrity: sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==} engines: {node: '>=18.0.0'} @@ -2804,6 +2847,9 @@ packages: '@aws-sdk/util-user-agent-browser@3.873.0': resolution: {integrity: sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==} + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} + '@aws-sdk/util-user-agent-node@3.864.0': resolution: {integrity: sha512-d+FjUm2eJEpP+FRpVR3z6KzMdx1qwxEYDz8jzNKwxYLBBquaBaP/wfoMtMQKAcbrR7aT9FZVZF7zDgzNxUvQlQ==} engines: {node: '>=18.0.0'} @@ -2822,6 +2868,15 @@ packages: aws-crt: optional: true + '@aws-sdk/util-user-agent-node@3.973.13': + resolution: {integrity: sha512-s1dCJ0J9WU9UPkT3FFqhKTSquYTkqWXGRaapHFyWwwJH86ZussewhNST5R5TwXVL1VSHq4aJVl9fWK+svaRVCQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + '@aws-sdk/xml-builder@3.862.0': resolution: {integrity: sha512-6Ed0kmC1NMbuFTEgNmamAUU1h5gShgxL1hBVLbEzUa3trX5aJBz1vU4bXaBTvOYUAnOHtiy1Ml4AMStd6hJnFA==} engines: {node: '>=18.0.0'} @@ -2830,6 +2885,14 @@ packages: resolution: {integrity: sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==} engines: {node: '>=18.0.0'} + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -5613,14 +5676,14 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/context-async-hooks@2.1.0': - resolution: {integrity: sha512-zOyetmZppnwTyPrt4S7jMfXiSX9yyfF0hxlA8B5oo2TtKl+/RGCy7fi4DrBfIf3lCPrkKsRBWZZD7RFojK7FDg==} + '@opentelemetry/context-async-hooks@2.6.0': + resolution: {integrity: sha512-L8UyDwqpTcbkIK5cgwDRDYDoEhQoj8wp8BwsO19w3LB1Z41yEQm2VJyNfAi9DrLP/YTqXqWpKHyZfR9/tFYo1Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/context-async-hooks@2.6.0': - resolution: {integrity: sha512-L8UyDwqpTcbkIK5cgwDRDYDoEhQoj8wp8BwsO19w3LB1Z41yEQm2VJyNfAi9DrLP/YTqXqWpKHyZfR9/tFYo1Q==} + '@opentelemetry/context-async-hooks@2.6.1': + resolution: {integrity: sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -5637,12 +5700,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.1.0': - resolution: {integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.2.0': resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -5655,6 +5712,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@2.6.1': + resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/exporter-logs-otlp-grpc@0.213.0': resolution: {integrity: sha512-QiRZzvayEOFnenSXi85Eorgy5WTqyNQ+E7gjl6P6r+W3IUIwAIH8A9/BgMWfP056LwmdrBL6+qvnwaIEmug6Yg==} engines: {node: ^18.19.0 || >=20.6.0} @@ -6254,12 +6317,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/resources@2.1.0': - resolution: {integrity: sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.2.0': resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} engines: {node: ^18.19.0 || >=20.6.0} @@ -6272,6 +6329,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/resources@2.6.1': + resolution: {integrity: sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-logs@0.208.0': resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -6308,6 +6371,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' + '@opentelemetry/sdk-metrics@2.6.1': + resolution: {integrity: sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + '@opentelemetry/sdk-node@0.213.0': resolution: {integrity: sha512-8s7SQtY8DIAjraXFrUf0+I90SBAUQbsMWMtUGKmusswRHWXtKJx42aJQMoxEtC82Csqj+IlBH6FoP8XmmUDSrQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -6320,12 +6389,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.1.0': - resolution: {integrity: sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.2.0': resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -6338,6 +6401,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-base@2.6.1': + resolution: {integrity: sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-node@1.26.0': resolution: {integrity: sha512-Fj5IVKrj0yeUwlewCRwzOVcr5avTuNnMHWf7GPc1t6WaT78J6CJyF3saZ/0RkZfdeNO8IcBl/bNcWMVZBMRW8Q==} engines: {node: '>=14'} @@ -8988,6 +9057,14 @@ packages: resolution: {integrity: sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==} engines: {node: '>=18.0.0'} + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.13': + resolution: {integrity: sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==} + engines: {node: '>=18.0.0'} + '@smithy/core@3.8.0': resolution: {integrity: sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ==} engines: {node: '>=18.0.0'} @@ -8996,6 +9073,10 @@ packages: resolution: {integrity: sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==} engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} + engines: {node: '>=18.0.0'} + '@smithy/eventstream-codec@4.0.5': resolution: {integrity: sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==} engines: {node: '>=18.0.0'} @@ -9020,6 +9101,10 @@ packages: resolution: {integrity: sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} + engines: {node: '>=18.0.0'} + '@smithy/hash-blob-browser@4.0.5': resolution: {integrity: sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==} engines: {node: '>=18.0.0'} @@ -9028,6 +9113,10 @@ packages: resolution: {integrity: sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==} engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} + engines: {node: '>=18.0.0'} + '@smithy/hash-stream-node@4.0.5': resolution: {integrity: sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==} engines: {node: '>=18.0.0'} @@ -9036,6 +9125,10 @@ packages: resolution: {integrity: sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==} engines: {node: '>=18.0.0'} + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} + engines: {node: '>=18.0.0'} + '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} @@ -9044,6 +9137,10 @@ packages: resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} engines: {node: '>=18.0.0'} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + '@smithy/md5-js@4.0.5': resolution: {integrity: sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==} engines: {node: '>=18.0.0'} @@ -9052,62 +9149,126 @@ packages: resolution: {integrity: sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.1.18': resolution: {integrity: sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.4.28': + resolution: {integrity: sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.1.19': resolution: {integrity: sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.4.45': + resolution: {integrity: sha512-td1PxpwDIaw5/oP/xIRxBGxJKoF1L4DBAwbZ8wjMuXBYOP/r2ZE/Ocou+mBHx/yk9knFEtDBwhSrYVn+Mz4pHw==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.0.9': resolution: {integrity: sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==} engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.16': + resolution: {integrity: sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.0.5': resolution: {integrity: sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} + engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.1.4': resolution: {integrity: sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==} engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} + engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.1.1': resolution: {integrity: sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.5.1': + resolution: {integrity: sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.0.5': resolution: {integrity: sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==} engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} + engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.1.3': resolution: {integrity: sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==} engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.0.5': resolution: {integrity: sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==} engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.0.5': resolution: {integrity: sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==} engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} + engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@4.0.7': resolution: {integrity: sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==} engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} + engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.0.5': resolution: {integrity: sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==} engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} + engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.1.3': resolution: {integrity: sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==} engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.8': + resolution: {integrity: sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.4.10': resolution: {integrity: sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ==} engines: {node: '>=18.0.0'} + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} + engines: {node: '>=18.0.0'} + '@smithy/types@4.3.2': resolution: {integrity: sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==} engines: {node: '>=18.0.0'} @@ -9116,18 +9277,34 @@ packages: resolution: {integrity: sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==} engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} + engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.0.0': resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.0.0': resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.0.0': resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} @@ -9136,42 +9313,82 @@ packages: resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} engines: {node: '>=18.0.0'} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.0.0': resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.0.26': resolution: {integrity: sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.44': + resolution: {integrity: sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.0.26': resolution: {integrity: sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.48': + resolution: {integrity: sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==} + engines: {node: '>=18.0.0'} + '@smithy/util-endpoints@3.0.7': resolution: {integrity: sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==} engines: {node: '>=18.0.0'} + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} + engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.0.0': resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.0.5': resolution: {integrity: sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==} engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-retry@4.0.7': resolution: {integrity: sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==} engines: {node: '>=18.0.0'} + '@smithy/util-retry@4.2.12': + resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.2.4': resolution: {integrity: sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==} engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.21': + resolution: {integrity: sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==} + engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.0.0': resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} @@ -9180,10 +9397,18 @@ packages: resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} engines: {node: '>=18.0.0'} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + '@smithy/util-waiter@4.0.7': resolution: {integrity: sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==} engines: {node: '>=18.0.0'} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -12714,6 +12939,9 @@ packages: fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + fast-xml-parser@4.5.3: resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} hasBin: true @@ -12722,6 +12950,10 @@ packages: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} + hasBin: true + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -15479,6 +15711,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.2.0: + resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -17217,6 +17453,9 @@ packages: strnum@2.1.1: resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + strnum@2.2.2: + resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} + style-to-js@1.1.16: resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} @@ -19289,6 +19528,22 @@ snapshots: fast-xml-parser: 5.2.5 tslib: 2.8.1 + '@aws-sdk/core@3.973.26': + 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 + tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.864.0': dependencies: '@aws-sdk/core': 3.864.0 @@ -19467,6 +19722,18 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-web-identity@3.972.27': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.17 + '@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 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/middleware-bucket-endpoint@3.862.0': dependencies: '@aws-sdk/types': 3.862.0 @@ -19514,6 +19781,13 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/middleware-host-header@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@aws-sdk/middleware-location-constraint@3.862.0': dependencies: '@aws-sdk/types': 3.862.0 @@ -19532,6 +19806,12 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/middleware-logger@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@aws-sdk/middleware-recursion-detection@3.862.0': dependencies: '@aws-sdk/types': 3.862.0 @@ -19546,6 +19826,14 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/middleware-recursion-detection@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@aws-sdk/middleware-sdk-s3@3.864.0': dependencies: '@aws-sdk/core': 3.864.0 @@ -19589,6 +19877,17 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/middleware-user-agent@3.972.27': + 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.12 + tslib: 2.8.1 + '@aws-sdk/nested-clients@3.864.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -19675,6 +19974,49 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/nested-clients@3.996.17': + 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.27 + '@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.13 + '@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.45 + '@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.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/region-config-resolver@3.862.0': dependencies: '@aws-sdk/types': 3.862.0 @@ -19693,6 +20035,14 @@ snapshots: '@smithy/util-middleware': 4.0.5 tslib: 2.8.1 + '@aws-sdk/region-config-resolver@3.972.10': + 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 + tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.864.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.864.0 @@ -19731,6 +20081,11 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/types@3.973.6': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@aws-sdk/util-arn-parser@3.804.0': dependencies: tslib: 2.8.1 @@ -19751,6 +20106,14 @@ snapshots: '@smithy/util-endpoints': 3.0.7 tslib: 2.8.1 + '@aws-sdk/util-endpoints@3.996.5': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 + tslib: 2.8.1 + '@aws-sdk/util-locate-window@3.804.0': dependencies: tslib: 2.8.1 @@ -19769,6 +20132,13 @@ snapshots: bowser: 2.11.0 tslib: 2.8.1 + '@aws-sdk/util-user-agent-browser@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + bowser: 2.11.0 + tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.864.0': dependencies: '@aws-sdk/middleware-user-agent': 3.864.0 @@ -19785,6 +20155,15 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.973.13': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.27 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + '@aws-sdk/xml-builder@3.862.0': dependencies: '@smithy/types': 4.3.2 @@ -19795,6 +20174,14 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.16': + dependencies: + '@smithy/types': 4.13.1 + fast-xml-parser: 5.5.8 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -22736,11 +23123,11 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -22752,22 +23139,22 @@ snapshots: '@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/exporter-logs-otlp-grpc@0.213.0(@opentelemetry/api@1.9.0)': dependencies: @@ -22845,7 +23232,7 @@ snapshots: '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/exporter-trace-otlp-grpc@0.213.0(@opentelemetry/api@1.9.0)': dependencies: @@ -22891,14 +23278,14 @@ snapshots: '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/instrumentation-amqplib@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22949,9 +23336,9 @@ snapshots: '@opentelemetry/instrumentation-connect@0.47.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color @@ -22998,9 +23385,9 @@ snapshots: '@opentelemetry/instrumentation-express@0.52.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23025,7 +23412,7 @@ snapshots: '@opentelemetry/instrumentation-fs@0.23.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -23077,9 +23464,9 @@ snapshots: '@opentelemetry/instrumentation-hapi@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23097,7 +23484,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color @@ -23117,7 +23504,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23134,7 +23521,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23150,7 +23537,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23165,9 +23552,9 @@ snapshots: '@opentelemetry/instrumentation-koa@0.51.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23207,7 +23594,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23222,9 +23609,9 @@ snapshots: '@opentelemetry/instrumentation-mongoose@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23241,7 +23628,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -23259,7 +23646,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color @@ -23310,9 +23697,9 @@ snapshots: '@opentelemetry/instrumentation-pg@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.4 '@types/pg-pool': 2.0.6 @@ -23345,7 +23732,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -23393,7 +23780,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color @@ -23410,7 +23797,7 @@ snapshots: '@opentelemetry/instrumentation-undici@0.14.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -23611,12 +23998,6 @@ snapshots: '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 - '@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 - '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -23627,7 +24008,13 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': dependencies: @@ -23642,7 +24029,7 @@ snapshots: '@opentelemetry/api-logs': 0.213.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-logs@0.53.0(@opentelemetry/api@1.9.0)': dependencies: @@ -23669,6 +24056,12 @@ snapshots: '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node@0.213.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -23706,26 +24099,26 @@ snapshots: '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 - '@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 - '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-trace-node@1.26.0(@opentelemetry/api@1.9.0)': dependencies: @@ -23753,7 +24146,7 @@ snapshots: '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@orama/orama@3.1.16': {} @@ -27186,7 +27579,7 @@ snapshots: '@sentry/core@10.45.0': {} - '@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(webpack@5.92.0(esbuild@0.24.2))': + '@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(webpack@5.92.0(esbuild@0.24.2))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.37.0 @@ -27195,7 +27588,7 @@ snapshots: '@sentry/bundler-plugin-core': 4.3.0(encoding@0.1.13) '@sentry/core': 10.11.0 '@sentry/node': 10.11.0 - '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) + '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) '@sentry/react': 10.11.0(react@19.2.1) '@sentry/vercel-edge': 10.11.0 '@sentry/webpack-plugin': 4.3.0(encoding@0.1.13)(webpack@5.92.0(esbuild@0.24.2)) @@ -27213,7 +27606,7 @@ snapshots: - supports-color - webpack - '@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))': + '@sentry/nextjs@10.11.0(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.5(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.92.0(esbuild@0.24.2))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.37.0 @@ -27222,7 +27615,7 @@ snapshots: '@sentry/bundler-plugin-core': 4.3.0(encoding@0.1.13) '@sentry/core': 10.11.0 '@sentry/node': 10.11.0 - '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) + '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) '@sentry/react': 10.11.0(react@19.2.3) '@sentry/vercel-edge': 10.11.0 '@sentry/webpack-plugin': 4.3.0(encoding@0.1.13)(webpack@5.92.0(esbuild@0.24.2)) @@ -27265,17 +27658,17 @@ snapshots: - supports-color - webpack - '@sentry/node-core@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': + '@sentry/node-core@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@sentry/core': 10.11.0 - '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) + '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 1.14.2 '@sentry/node-core@10.45.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.213.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': @@ -27295,8 +27688,8 @@ snapshots: '@sentry/node@10.11.0': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-amqplib': 0.50.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-connect': 0.47.0(@opentelemetry/api@1.9.0) @@ -27320,13 +27713,13 @@ snapshots: '@opentelemetry/instrumentation-redis': 0.51.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-tedious': 0.22.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-undici': 0.14.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@prisma/instrumentation': 6.14.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.11.0 - '@sentry/node-core': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) - '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) + '@sentry/node-core': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/opentelemetry': 10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 1.14.2 minimatch: 9.0.5 transitivePeerDependencies: @@ -27372,21 +27765,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': + '@sentry/opentelemetry@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@sentry/core': 10.11.0 - '@sentry/opentelemetry@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': + '@sentry/opentelemetry@10.11.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/context-async-hooks': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.37.0 '@sentry/core': 10.11.0 @@ -27431,7 +27824,7 @@ snapshots: '@sentry/vercel-edge@10.11.0': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.11.0 '@sentry/vercel-edge@10.45.0': @@ -27620,6 +28013,28 @@ snapshots: '@smithy/util-middleware': 4.0.5 tslib: 2.8.1 + '@smithy/config-resolver@4.4.13': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + + '@smithy/core@3.23.13': + 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 + tslib: 2.8.1 + '@smithy/core@3.8.0': dependencies: '@smithy/middleware-serde': 4.0.9 @@ -27642,6 +28057,14 @@ snapshots: '@smithy/url-parser': 4.0.5 tslib: 2.8.1 + '@smithy/credential-provider-imds@4.2.12': + 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 + tslib: 2.8.1 + '@smithy/eventstream-codec@4.0.5': dependencies: '@aws-crypto/crc32': 5.2.0 @@ -27680,6 +28103,14 @@ snapshots: '@smithy/util-base64': 4.0.0 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.3.15': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + '@smithy/hash-blob-browser@4.0.5': dependencies: '@smithy/chunked-blob-reader': 5.0.0 @@ -27694,6 +28125,13 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/hash-node@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/hash-stream-node@4.0.5': dependencies: '@smithy/types': 4.3.2 @@ -27705,6 +28143,11 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/invalid-dependency@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 @@ -27713,6 +28156,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/md5-js@4.0.5': dependencies: '@smithy/types': 4.3.2 @@ -27725,6 +28172,12 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/middleware-content-length@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/middleware-endpoint@4.1.18': dependencies: '@smithy/core': 3.8.0 @@ -27736,6 +28189,17 @@ snapshots: '@smithy/util-middleware': 4.0.5 tslib: 2.8.1 + '@smithy/middleware-endpoint@4.4.28': + 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 + tslib: 2.8.1 + '@smithy/middleware-retry@4.1.19': dependencies: '@smithy/node-config-provider': 4.1.4 @@ -27749,17 +28213,41 @@ snapshots: tslib: 2.8.1 uuid: 9.0.1 + '@smithy/middleware-retry@4.4.45': + 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.12 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + '@smithy/middleware-serde@4.0.9': dependencies: '@smithy/protocol-http': 5.1.3 '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/middleware-serde@4.2.16': + dependencies: + '@smithy/core': 3.23.13 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/middleware-stack@4.0.5': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/middleware-stack@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/node-config-provider@4.1.4': dependencies: '@smithy/property-provider': 4.0.5 @@ -27767,6 +28255,13 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/node-config-provider@4.3.12': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/node-http-handler@4.1.1': dependencies: '@smithy/abort-controller': 4.0.5 @@ -27775,36 +28270,73 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/node-http-handler@4.5.1': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/property-provider@4.0.5': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/property-provider@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/protocol-http@5.1.3': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/protocol-http@5.3.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/querystring-builder@4.0.5': dependencies: '@smithy/types': 4.3.2 '@smithy/util-uri-escape': 4.0.0 tslib: 2.8.1 + '@smithy/querystring-builder@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + '@smithy/querystring-parser@4.0.5': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/querystring-parser@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/service-error-classification@4.0.7': dependencies: '@smithy/types': 4.3.2 + '@smithy/service-error-classification@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/shared-ini-file-loader@4.0.5': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/shared-ini-file-loader@4.4.7': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/signature-v4@5.1.3': dependencies: '@smithy/is-array-buffer': 4.0.0 @@ -27816,6 +28348,27 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/signature-v4@5.3.12': + 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 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.8': + 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 + tslib: 2.8.1 + '@smithy/smithy-client@4.4.10': dependencies: '@smithy/core': 3.8.0 @@ -27826,6 +28379,10 @@ snapshots: '@smithy/util-stream': 4.2.4 tslib: 2.8.1 + '@smithy/types@4.13.1': + dependencies: + tslib: 2.8.1 + '@smithy/types@4.3.2': dependencies: tslib: 2.8.1 @@ -27836,20 +28393,40 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/url-parser@4.2.12': + dependencies: + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/util-base64@4.0.0': dependencies: '@smithy/util-buffer-from': 4.0.0 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/util-body-length-browser@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-body-length-node@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 @@ -27860,10 +28437,19 @@ snapshots: '@smithy/is-array-buffer': 4.0.0 tslib: 2.8.1 + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + '@smithy/util-config-provider@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.0.26': dependencies: '@smithy/property-provider': 4.0.5 @@ -27872,6 +28458,13 @@ snapshots: bowser: 2.11.0 tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.44': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.0.26': dependencies: '@smithy/config-resolver': 4.1.5 @@ -27882,27 +28475,58 @@ snapshots: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.2.48': + 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 + tslib: 2.8.1 + '@smithy/util-endpoints@3.0.7': dependencies: '@smithy/node-config-provider': 4.1.4 '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/util-endpoints@3.3.3': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/util-hex-encoding@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-middleware@4.0.5': dependencies: '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/util-middleware@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/util-retry@4.0.7': dependencies: '@smithy/service-error-classification': 4.0.7 '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/util-retry@4.2.12': + dependencies: + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + '@smithy/util-stream@4.2.4': dependencies: '@smithy/fetch-http-handler': 5.1.1 @@ -27914,10 +28538,25 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 + '@smithy/util-stream@4.5.21': + dependencies: + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.1 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/util-uri-escape@4.0.0': dependencies: tslib: 2.8.1 + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 @@ -27928,12 +28567,21 @@ snapshots: '@smithy/util-buffer-from': 4.0.0 tslib: 2.8.1 + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + '@smithy/util-waiter@4.0.7': dependencies: '@smithy/abort-controller': 4.0.5 '@smithy/types': 4.3.2 tslib: 2.8.1 + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + '@socket.io/component-emitter@3.1.2': {} '@stablelib/base64@1.0.1': {} @@ -29264,9 +29912,9 @@ snapshots: next: 16.1.5(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 - '@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.876.0)': + '@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.972.27)': optionalDependencies: - '@aws-sdk/credential-provider-web-identity': 3.876.0 + '@aws-sdk/credential-provider-web-identity': 3.972.27 '@vercel/mcp-adapter@1.0.0(@modelcontextprotocol/sdk@1.17.2)(next@15.5.10(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: @@ -29277,14 +29925,14 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vercel/otel@1.10.4(@opentelemetry/api-logs@0.53.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))': + '@vercel/otel@1.10.4(@opentelemetry/api-logs@0.53.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.53.0 '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.6.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) '@vercel/sandbox@1.2.0': @@ -31910,7 +32558,7 @@ snapshots: '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.2(eslint@8.57.1) @@ -31966,6 +32614,25 @@ snapshots: - eslint-import-resolver-webpack - supports-color + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + enhanced-resolve: 5.17.1 + eslint: 8.57.1 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + fast-glob: 3.3.3 + get-tsconfig: 4.8.1 + is-bun-module: 1.2.1 + is-glob: 4.0.3 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 @@ -32656,6 +33323,10 @@ snapshots: fast-uri@3.0.6: {} + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.2.0 + fast-xml-parser@4.5.3: dependencies: strnum: 1.1.2 @@ -32664,6 +33335,12 @@ snapshots: dependencies: strnum: 2.1.1 + fast-xml-parser@5.5.8: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.0 + strnum: 2.2.2 + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -36377,6 +37054,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.2.0: {} + path-is-absolute@1.0.1: {} path-is-inside@1.0.2: {} @@ -38737,6 +39416,8 @@ snapshots: strnum@2.1.1: {} + strnum@2.2.2: {} + style-to-js@1.1.16: dependencies: style-to-object: 1.0.8