diff --git a/.env.example b/.env.example index 9ea92ae21..a640168b1 100644 --- a/.env.example +++ b/.env.example @@ -107,3 +107,18 @@ NODE_OPTIONS="--max_old_space_size=4096" # ───────────────────────────────────────────── # Log verbosity: error | warn | info | debug LOG_LEVEL=debug + +# ───────────────────────────────────────────── +# Error tracking (Sentry) +# ───────────────────────────────────────────── +# Leave SENTRY_DSN empty to disable Sentry entirely (no-op). +# Get a DSN from sentry.io → Project Settings → Client Keys (DSN). +SENTRY_DSN= +# Logical environment shown in Sentry (defaults to NODE_ENV). +SENTRY_ENVIRONMENT= +# Release/commit identifier so events are grouped by deploy. Set to the git SHA in CI. +SENTRY_RELEASE= +# Performance tracing sample rate, 0–1. Default 0 (errors only) to protect quota. +SENTRY_TRACES_SAMPLE_RATE=0 +# Identifies which worker process emitted an event (e.g. "worker-chat"). +WORKER_NAME= diff --git a/apps/worker/docker/rootfs/usr/local/bin/docker-entrypoint.sh b/apps/worker/docker/rootfs/usr/local/bin/docker-entrypoint.sh index 6857cac6b..5bd889650 100755 --- a/apps/worker/docker/rootfs/usr/local/bin/docker-entrypoint.sh +++ b/apps/worker/docker/rootfs/usr/local/bin/docker-entrypoint.sh @@ -4,6 +4,14 @@ set -eu WORKER_DIST_DIR="/app/apps/worker/dist" NODE_BIN="/usr/local/bin/node" +INSTRUMENT_SCRIPT="${WORKER_DIST_DIR}/instrument.mjs" +# Debug + observability flags applied to every worker process: +# --enable-source-maps : remap stack traces from bundled .mjs back to .ts +# --import instrument : initialize Sentry before any worker module loads +# Passed directly (not via NODE_OPTIONS) so a runtime NODE_OPTIONS +# (e.g. --max_old_space_size) cannot clobber them. Unquoted on use so it +# word-splits into separate flags. +NODE_FLAGS="--enable-source-maps --import ${INSTRUMENT_SCRIPT}" # ALL_WORKERS="chat integration ai-agent default webhook trigger analytics schedule sequence-scheduler sequence-producer sequence-consumer" ALL_WORKERS="chat integration ai-agent default webhook trigger analytics schedule sequence-scheduler" @@ -76,7 +84,9 @@ run_all_workers() { trap 'handle_shutdown' INT TERM - # Validate all worker entrypoints first so we never start partially. + # Validate the Sentry instrument and all worker entrypoints first so we + # never start partially. + require_script "$INSTRUMENT_SCRIPT" for worker in $ALL_WORKERS; do script="$(resolve_worker_script "$worker")" require_script "$script" @@ -85,7 +95,8 @@ run_all_workers() { for worker in $ALL_WORKERS; do script="$(resolve_worker_script "$worker")" echo "Starting worker: $worker ($script)" - "$NODE_BIN" "$script" & + # shellcheck disable=SC2086 # NODE_FLAGS must word-split into separate flags + "$NODE_BIN" $NODE_FLAGS "$script" & pids="$pids $!" done @@ -128,8 +139,10 @@ case "${1:-}" in print_usage exit 3 } + require_script "$INSTRUMENT_SCRIPT" require_script "$script" - exec "$NODE_BIN" "$script" + # shellcheck disable=SC2086 # NODE_FLAGS must word-split into separate flags + exec "$NODE_BIN" $NODE_FLAGS "$script" ;; "/bin/sh" | "sh" | "/bin/bash" | "bash") exec "$@" diff --git a/apps/worker/package.json b/apps/worker/package.json index f10ee81db..3682ae919 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -63,6 +63,7 @@ "@faker-js/faker": "^10.4.0", "@mozilla/readability": "^0.6.0", "@platformatic/kafka": "^1.34.0", + "@sentry/node": "^10.56.0", "@t3-oss/env-core": "^0.13.11", "ai": "^6.0.170", "bullmq": "^5.76.4", diff --git a/apps/worker/src/chat/worker.ts b/apps/worker/src/chat/worker.ts index a02fd83b2..99a731f6f 100644 --- a/apps/worker/src/chat/worker.ts +++ b/apps/worker/src/chat/worker.ts @@ -11,6 +11,7 @@ import { import { type Job, Worker } from "bullmq" import { ensureBootstrapped } from "../lib/bootstrap" import { logger } from "../lib/logger" +import { captureException, flushSentry, reportJobFailure } from "../lib/sentry" import { sendChatMessage, sendFlowStep } from "./handlers/send-flow-step" import { sendMessageToChannel, @@ -25,6 +26,8 @@ async function startChatWorker() { logger.info("Chat worker bootstrapped successfully") } catch (err) { logger.error(err, "Failed to bootstrap chat worker") + captureException(err, { worker: "chat", phase: "bootstrap" }) + await flushSentry() process.exit(1) } @@ -75,6 +78,7 @@ async function startChatWorker() { worker.on("failed", (job, err) => { if (job) { logger.error(err, `Job ${job.id} has failed`) + reportJobFailure(job, err, { worker: "chat" }) } }) } diff --git a/apps/worker/src/env.ts b/apps/worker/src/env.ts index 002742a07..b00d830a4 100644 --- a/apps/worker/src/env.ts +++ b/apps/worker/src/env.ts @@ -9,6 +9,13 @@ export const env = createEnv({ server: { NEXT_PUBLIC_EDITION: editionRule, QUOTA_SYNC_INTERVAL_SECONDS: z.coerce.number().int().min(10).default(60), + // Observability — all optional; omitting SENTRY_DSN disables Sentry. + // Read directly from process.env in instrument.ts (runs before this module). + SENTRY_DSN: z.url().optional(), + SENTRY_ENVIRONMENT: z.string().optional(), + SENTRY_RELEASE: z.string().optional(), + SENTRY_TRACES_SAMPLE_RATE: z.coerce.number().min(0).max(1).optional(), + WORKER_NAME: z.string().optional(), }, runtimeEnv: process.env, skipValidation: process.env.SKIP_ENV_CHECK === "true", diff --git a/apps/worker/src/instrument.ts b/apps/worker/src/instrument.ts new file mode 100644 index 000000000..bbd85cff5 --- /dev/null +++ b/apps/worker/src/instrument.ts @@ -0,0 +1,33 @@ +/** + * Sentry initialization for the worker. + * + * This file MUST be loaded before any other module so Sentry can hook into + * the runtime early. In production it is wired via node's `--import` flag in + * `docker/rootfs/usr/local/bin/docker-entrypoint.sh`, so it runs ahead of every + * worker entrypoint. + * + * It reads configuration straight from `process.env` (not the validated `env` + * module) on purpose: this runs before anything else, and a missing DSN simply + * disables Sentry rather than throwing. + */ +import { init } from "@sentry/node" + +const dsn = process.env.SENTRY_DSN + +if (dsn) { + const tracesSampleRate = process.env.SENTRY_TRACES_SAMPLE_RATE + ? Number(process.env.SENTRY_TRACES_SAMPLE_RATE) + : 0 + + init({ + dsn, + environment: + process.env.SENTRY_ENVIRONMENT ?? process.env.NODE_ENV ?? "development", + release: process.env.SENTRY_RELEASE, + // Errors are the priority. Performance tracing is sampled low (default off) + // to protect the Sentry quota — raise SENTRY_TRACES_SAMPLE_RATE when needed. + tracesSampleRate: Number.isFinite(tracesSampleRate) ? tracesSampleRate : 0, + // Identify which worker process emitted the event (set per container). + serverName: process.env.WORKER_NAME, + }) +} diff --git a/apps/worker/src/lib/sentry.ts b/apps/worker/src/lib/sentry.ts new file mode 100644 index 000000000..248169165 --- /dev/null +++ b/apps/worker/src/lib/sentry.ts @@ -0,0 +1,85 @@ +import { + flush, + getClient, + captureException as sentryCaptureException, +} from "@sentry/node" +import type { Job } from "bullmq" + +/** + * Whether Sentry was initialized (i.e. a DSN was provided). When false, every + * helper below is a cheap no-op, so call sites don't need their own guards. + */ +export function isSentryEnabled(): boolean { + return Boolean(getClient()) +} + +/** + * Capture an arbitrary exception with optional structured context. + * Safe to call even when Sentry is disabled — it just returns. + */ +export function captureException( + err: unknown, + context?: Record, +): void { + if (!isSentryEnabled()) { + return + } + + sentryCaptureException(err, context ? { extra: context } : undefined) +} + +/** + * Report a BullMQ job failure. Only the *final* attempt is sent: a job that + * still has retries left would otherwise emit a duplicate event on every + * attempt and burn through the Sentry quota during a retry storm. + * + * Job payloads can contain contact PII, so only identifiers and retry metadata + * are attached — never the raw `job.data`. + */ +export function reportJobFailure( + job: Job | undefined, + err: unknown, + context: { worker: string }, +): void { + if (!(job && isSentryEnabled())) { + return + } + + const attemptsMade = job.attemptsMade ?? 0 + const maxAttempts = job.opts?.attempts ?? 1 + + // Still has retries left — wait for the final attempt before reporting. + if (attemptsMade < maxAttempts) { + return + } + + sentryCaptureException(err, (scope) => { + scope.setTag("worker", context.worker) + scope.setTag("queue", job.queueName) + scope.setTag("job.name", job.name) + scope.setContext("job", { + id: job.id, + name: job.name, + queue: job.queueName, + attemptsMade, + maxAttempts, + }) + return scope + }) +} + +/** + * Flush queued Sentry events before the process exits. Best-effort: never block + * shutdown if Sentry is slow or unreachable. + */ +export async function flushSentry(timeoutMs = 2000): Promise { + if (!isSentryEnabled()) { + return + } + + try { + await flush(timeoutMs) + } catch { + // ignore — shutdown must not hang on Sentry + } +} diff --git a/apps/worker/tsdown.config.ts b/apps/worker/tsdown.config.ts index d51e2df6b..57bb846c0 100644 --- a/apps/worker/tsdown.config.ts +++ b/apps/worker/tsdown.config.ts @@ -3,6 +3,8 @@ import { defineConfig } from "tsdown" export default defineConfig({ format: ["esm"], entry: [ + // Loaded first via node --import; initializes Sentry before any worker runs. + "src/instrument.ts", "src/chat/worker.ts", "src/integration/worker.ts", "src/ai-agent/worker.ts", @@ -29,6 +31,8 @@ export default defineConfig({ minify: false, unbundle: false, // splitting: false, - sourcemap: false, + // Emit source maps so node --enable-source-maps and Sentry map stack traces + // in the bundled .mjs output back to the original TypeScript sources. + sourcemap: true, treeshake: true, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b645fee93..d0619bd37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -655,6 +655,9 @@ importers: '@platformatic/kafka': specifier: ^1.34.0 version: 1.34.0 + '@sentry/node': + specifier: ^10.56.0 + version: 10.56.0 '@t3-oss/env-core': specifier: ^0.13.11 version: 0.13.11(typescript@5.9.3)(zod@4.4.3) @@ -4371,10 +4374,38 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@opentelemetry/api-logs@0.214.0': + resolution: {integrity: sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} + '@opentelemetry/core@2.7.1': + resolution: {integrity: sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/instrumentation@0.214.0': + resolution: {integrity: sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.7.1': + resolution: {integrity: sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.7.1': + resolution: {integrity: sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/semantic-conventions@1.41.1': resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} engines: {node: '>=14'} @@ -5852,6 +5883,51 @@ packages: peerDependencies: selderee: ~0.12.0 + '@sentry-internal/server-utils@10.56.0': + resolution: {integrity: sha512-6kuZI/vAjyVKMm1cTzc2pdUmVR4Px4etMG6wnCPyFnwEaGbUKQnTynUBFpTuo/q6Js6QBQvhLNoAnO4YsOfW4w==} + engines: {node: '>=18'} + + '@sentry/core@10.56.0': + resolution: {integrity: sha512-L+u1dIz5SANrmST5jhIwETtt4apILgKrylv12X4hKJU0PvZl+NorjeV/ty3MwzpKQPg6b6q6qMOSLc1rLpy3iQ==} + engines: {node: '>=18'} + + '@sentry/node-core@10.56.0': + resolution: {integrity: sha512-61lD2Wjtv5Lw2F3lJarcD0ORjR4GlVxrEd6w6Of/uF3DH73dD6K3n/3wXEeCIRfV/kgiCFIrCIq76nz0LVgE5g==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/exporter-trace-otlp-http': '>=0.57.0 <1' + '@opentelemetry/instrumentation': '>=0.57.1 <1' + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/core': + optional: true + '@opentelemetry/exporter-trace-otlp-http': + optional: true + '@opentelemetry/instrumentation': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/semantic-conventions': + optional: true + + '@sentry/node@10.56.0': + resolution: {integrity: sha512-qvgtXHkcR4CH3fh0VEVyw4Ysc6MMiAnm727NdTTm0yU5e53erCeo2521+yfJkqmRTGiOSgwA7B5Bs+ot9j0vFQ==} + engines: {node: '>=18'} + + '@sentry/opentelemetry@10.56.0': + resolution: {integrity: sha512-PtMudApHMHvttjos3b7JZ2gJ+nstHAOYE3vKPYB5o0WQO95ldiaYnpLKMCRIGZWF3Dk7ynrqqnBpn8LZLt+Mrg==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + '@smithy/core@3.24.6': resolution: {integrity: sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==} engines: {node: '>=18.0.0'} @@ -6642,6 +6718,11 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx-walk@2.0.0: resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==} @@ -6979,6 +7060,9 @@ packages: citty@0.2.2: resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -8208,6 +8292,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-in-the-middle@3.0.2: + resolution: {integrity: sha512-LGLYRl0A2gtyUJb2WDliBHmk6TtlHwdDjxonacZ8QrEs/ZW+YDgNv2QAfjRQWpS8HqvNcq6GGnN6jrOa5FysDQ==} + engines: {node: '>=18'} + import-without-cache@0.3.3: resolution: {integrity: sha512-bDxwDdF04gm550DfZHgffvlX+9kUlcz32UD0AeBTmVPFiWkrexF2XVmiuFFbDhiFuP8fQkrkvI2KdSNPYWAXkQ==} engines: {node: '>=20.19.0'} @@ -8918,6 +9006,9 @@ packages: mnemonist@0.40.4: resolution: {integrity: sha512-ZAv+KNavneRVzu4tUeOgzkScI3W5BGwZ3rkxIpKtzzVgfTtWQFN1CgX0U72cyvyh3iTuHL3SiSmrQxTlryEIcw==} + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + motion-dom@12.40.0: resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} @@ -9908,6 +9999,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} @@ -12676,8 +12771,39 @@ snapshots: '@open-draft/until@2.1.0': {} + '@opentelemetry/api-logs@0.214.0': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api@1.9.1': {} + '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.214.0 + import-in-the-middle: 3.0.2 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + '@opentelemetry/semantic-conventions@1.41.1': {} '@orpc/client@1.14.4(@opentelemetry/api@1.9.1)': @@ -14044,6 +14170,48 @@ snapshots: domhandler: 5.0.3 selderee: 0.12.0 + '@sentry-internal/server-utils@10.56.0': + dependencies: + '@sentry/core': 10.56.0 + + '@sentry/core@10.56.0': {} + + '@sentry/node-core@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + dependencies: + '@sentry/core': 10.56.0 + '@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@sentry/node@10.56.0': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + '@sentry-internal/server-utils': 10.56.0 + '@sentry/core': 10.56.0 + '@sentry/node-core': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + import-in-the-middle: 3.0.2 + transitivePeerDependencies: + - '@opentelemetry/exporter-trace-otlp-http' + - supports-color + + '@sentry/opentelemetry@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + '@sentry/core': 10.56.0 + '@smithy/core@3.24.6': dependencies: '@aws-crypto/crc32': 5.2.0 @@ -14843,6 +15011,10 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 + acorn-import-attributes@1.9.5(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx-walk@2.0.0: {} acorn-jsx@5.3.2(acorn@8.16.0): @@ -15152,6 +15324,8 @@ snapshots: citty@0.2.2: {} + cjs-module-lexer@2.2.0: {} + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 @@ -16388,6 +16562,13 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-in-the-middle@3.0.2: + dependencies: + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 2.2.0 + module-details-from-path: 1.0.4 + import-without-cache@0.3.3: {} index-to-position@1.2.0: {} @@ -17429,6 +17610,8 @@ snapshots: dependencies: obliterator: 2.0.5 + module-details-from-path@1.0.4: {} + motion-dom@12.40.0: dependencies: motion-utils: 12.39.0 @@ -18527,6 +18710,13 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3(supports-color@10.2.2) + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + reselect@5.1.1: {} resize-observer-polyfill@1.5.1: {}