mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(analytics): upload web source maps to Sentry + tie release to build (#369)
* feat(analytics): upload web source maps to Sentry + tie release to build Web crash reports were unusable: the bundle ships minified with no source maps uploaded, and every build reported as the frozen APP_VERSION, so a Sentry error showed an unreadable stack under a single release. - Add @sentry/vite-plugin: emit hidden source maps and upload them by debug id when SENTRY_AUTH_TOKEN is present (published Docker build only), then delete the maps so they never ship. No-op for dev and the source archive. - Set the Sentry release from SENTRY_RELEASE / VITE_SENTRY_RELEASE (the Docker build passes the release version), falling back to APP_VERSION. - Relax beforeSend so app bundle frames keep a host-stripped path (Sentry needs it to match the uploaded map) while the instance hostname, error message, and PII stay stripped. Filesystem paths still collapse to the basename. - Wire the Dockerfile (sentry_auth_token build secret + SENTRY_RELEASE arg/env) and the release docker job. * fix(analytics): point source map upload at the snapotter org (project node)
This commit is contained in:
@@ -241,6 +241,9 @@ jobs:
|
||||
SNAPOTTER_ANALYTICS=on
|
||||
SNAPOTTER_POSTHOG_KEY=${{ secrets.SNAPOTTER_POSTHOG_KEY }}
|
||||
SNAPOTTER_SENTRY_DSN=${{ secrets.SNAPOTTER_SENTRY_DSN }}
|
||||
SENTRY_RELEASE=${{ needs.release.outputs.new_version }}
|
||||
secrets: |
|
||||
sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,"name=snapotter/snapotter,ghcr.io/snapotter-hq/snapotter",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=registry,ref=ghcr.io/snapotter-hq/snapotter:cache-${{ env.PLATFORM_PAIR }}
|
||||
|
||||
@@ -17,10 +17,13 @@ if (ANALYTICS_BAKED.sentryDsn) {
|
||||
try {
|
||||
const Sentry = await import("@sentry/node");
|
||||
const { APP_VERSION } = await import("@snapotter/shared");
|
||||
// The Docker build sets SENTRY_RELEASE to the release version so errors
|
||||
// attribute to a build; falls back to APP_VERSION for non-image runs.
|
||||
const release = process.env.SENTRY_RELEASE || APP_VERSION;
|
||||
|
||||
Sentry.init({
|
||||
dsn: ANALYTICS_BAKED.sentryDsn,
|
||||
release: APP_VERSION,
|
||||
release,
|
||||
environment: process.env.NODE_ENV || "production",
|
||||
tracesSampleRate: ANALYTICS_BAKED.sampleRate,
|
||||
sendDefaultPii: false,
|
||||
@@ -59,7 +62,7 @@ if (ANALYTICS_BAKED.sentryDsn) {
|
||||
},
|
||||
});
|
||||
|
||||
console.log("[sentry] initialized, release:", APP_VERSION);
|
||||
console.log("[sentry] initialized, release:", release);
|
||||
} catch {
|
||||
// @sentry/node not available
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/vite-plugin": "^5.3.0",
|
||||
"@tailwindcss/vite": "^4.3.1",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/react": "^19.2.17",
|
||||
|
||||
@@ -11,6 +11,16 @@ function basename(p: string): string {
|
||||
return i >= 0 ? p.slice(i + 1) : p;
|
||||
}
|
||||
|
||||
// App bundle frames are http(s) URLs. Keep the host-less pathname so Sentry can
|
||||
// match the frame to its uploaded source map, but drop the instance hostname
|
||||
// (not anonymous). Filesystem-style paths collapse to the basename so a local
|
||||
// path or username can never leave the browser.
|
||||
function scrubFramePath(p: string): string {
|
||||
const url = p.match(/^https?:\/\/[^/]+(\/[^?#]*)?/i);
|
||||
if (url) return url[1] ?? "/";
|
||||
return basename(p);
|
||||
}
|
||||
|
||||
// Only these keys may leave the browser per event, and only as primitives.
|
||||
const ALLOWED: Record<string, ReadonlySet<string>> = {
|
||||
tool_opened: new Set(["tool_id", "category", "modality"]),
|
||||
@@ -66,7 +76,8 @@ export async function initAnalytics(config: AnalyticsConfig): Promise<void> {
|
||||
const Sentry = await import("@sentry/react");
|
||||
Sentry.init({
|
||||
dsn: config.sentryDsn,
|
||||
release: (await import("@snapotter/shared")).APP_VERSION,
|
||||
release:
|
||||
import.meta.env.VITE_SENTRY_RELEASE || (await import("@snapotter/shared")).APP_VERSION,
|
||||
environment: "production",
|
||||
tracesSampleRate: config.sampleRate,
|
||||
sendDefaultPii: false,
|
||||
@@ -85,13 +96,22 @@ export async function initAnalytics(config: AnalyticsConfig): Promise<void> {
|
||||
ex.value = ex.type;
|
||||
if (ex.stacktrace?.frames) {
|
||||
for (const frame of ex.stacktrace.frames) {
|
||||
if (frame.filename) frame.filename = basename(frame.filename);
|
||||
frame.abs_path = undefined;
|
||||
if (frame.filename) frame.filename = scrubFramePath(frame.filename);
|
||||
if (frame.abs_path) frame.abs_path = scrubFramePath(frame.abs_path);
|
||||
frame.vars = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep debug_meta image paths consistent with the scrubbed frames so
|
||||
// debug-id source-map matching still resolves, minus the hostname.
|
||||
if (event.debug_meta?.images) {
|
||||
for (const img of event.debug_meta.images) {
|
||||
if ("code_file" in img && img.code_file) {
|
||||
img.code_file = scrubFramePath(img.code_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return event;
|
||||
},
|
||||
beforeBreadcrumb() {
|
||||
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
// Sentry release, injected at build time by the Docker web build. Unset in
|
||||
// dev and source-archive builds, where the SDK falls back to APP_VERSION.
|
||||
readonly VITE_SENTRY_RELEASE?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
+28
-1
@@ -1,10 +1,34 @@
|
||||
import path from "node:path";
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
// Source maps are emitted and uploaded to Sentry only when the build supplies
|
||||
// SENTRY_AUTH_TOKEN (the published Docker image does; dev and the source-archive
|
||||
// build do not). Debug IDs tie each build's bundle to its own maps, so upload
|
||||
// works regardless of release. Maps are deleted after upload so they never ship.
|
||||
const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN;
|
||||
const sentryRelease = process.env.SENTRY_RELEASE;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
// Must come after the other plugins so it sees the final bundle.
|
||||
sentryVitePlugin({
|
||||
org: "snapotter",
|
||||
project: "node",
|
||||
authToken: sentryAuthToken,
|
||||
telemetry: false,
|
||||
disable: !sentryAuthToken,
|
||||
applicationKey: "snapotter-web",
|
||||
// Don't inject the release into the bundle; the SDK reads it explicitly
|
||||
// from VITE_SENTRY_RELEASE so there is a single source of truth.
|
||||
release: { name: sentryRelease, inject: false },
|
||||
sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
@@ -36,6 +60,9 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
build: {
|
||||
// Hidden source maps: generated for upload but not referenced from the
|
||||
// shipped JS, so browsers never fetch them. Off entirely when not uploading.
|
||||
sourcemap: sentryAuthToken ? "hidden" : false,
|
||||
rollupOptions: {},
|
||||
},
|
||||
});
|
||||
|
||||
+14
-1
@@ -73,8 +73,16 @@ RUN SNAPOTTER_POSTHOG_KEY="${SNAPOTTER_POSTHOG_KEY}" \
|
||||
SNAPOTTER_SENTRY_DSN="${SNAPOTTER_SENTRY_DSN}" \
|
||||
node scripts/bake-analytics.mjs ${SNAPOTTER_ANALYTICS}
|
||||
|
||||
# Build only the web frontend (API runs from TS source via tsx)
|
||||
# Build only the web frontend (API runs from TS source via tsx). When a
|
||||
# SENTRY_AUTH_TOKEN build secret is supplied (the published image build does),
|
||||
# the Sentry Vite plugin uploads source maps for SENTRY_RELEASE; without it the
|
||||
# plugin is a no-op and no maps are emitted.
|
||||
ARG SENTRY_RELEASE=
|
||||
RUN --mount=type=cache,id=turbo-cache,target=/app/.turbo \
|
||||
--mount=type=secret,id=sentry_auth_token,required=false \
|
||||
SENTRY_AUTH_TOKEN="$(cat /run/secrets/sentry_auth_token 2>/dev/null || true)" \
|
||||
SENTRY_RELEASE="${SENTRY_RELEASE}" \
|
||||
VITE_SENTRY_RELEASE="${SENTRY_RELEASE}" \
|
||||
pnpm --filter @snapotter/web build
|
||||
|
||||
# ============================================
|
||||
@@ -420,6 +428,11 @@ ENV PORT=1349 \
|
||||
OIDC_ENABLED=false \
|
||||
EXTERNAL_URL=
|
||||
|
||||
# Sentry release for the API runtime, matching the source maps the web build
|
||||
# uploaded. Empty for non-image builds, where the API falls back to APP_VERSION.
|
||||
ARG SENTRY_RELEASE=
|
||||
ENV SENTRY_RELEASE=${SENTRY_RELEASE}
|
||||
|
||||
# COOKIE_SECRET is intentionally not baked in: the app auto-generates and persists one
|
||||
# on first boot if unset (see apps/api/src/index.ts). Override via runtime env to pin it.
|
||||
|
||||
|
||||
Generated
+248
@@ -533,6 +533,9 @@ importers:
|
||||
specifier: ^5.0.14
|
||||
version: 5.0.14(@types/react@19.2.17)(react@19.2.7)
|
||||
devDependencies:
|
||||
'@sentry/vite-plugin':
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.1(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.47.1)(tsx@4.22.4)(yaml@2.9.0))
|
||||
@@ -3137,6 +3140,10 @@ packages:
|
||||
peerDependencies:
|
||||
semantic-release: '>=20.1.0'
|
||||
|
||||
'@sentry/babel-plugin-component-annotate@5.3.0':
|
||||
resolution: {integrity: sha512-p4q8gn8wcFqZGP/s2MnJCAAd8fTikaU6A0mM97RDHQgStcrYiaS0Sc5zUNfb1V+UOLPuvdEdL6MwyxfzjYJQTA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@sentry/browser-utils@10.59.0':
|
||||
resolution: {integrity: sha512-DpJIrNi0Hsj/YONTZ8km1wv7ue2NzrTmFKJ3lRW8Q2nS/mrMeP3LCvjreLtKheTouB4go58NxM68AEFbXk4rPg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3145,6 +3152,62 @@ packages:
|
||||
resolution: {integrity: sha512-d0o0oc78KNWCZ6yq9gREf9BPXWza/Wj9W+nCm0CvPD5k2IbouD/eJsm2Mb+d53YfEm12L+SePfgOx01KbbVx4A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@sentry/bundler-plugin-core@5.3.0':
|
||||
resolution: {integrity: sha512-L5T60sWdAI3qWwdg3Ptwek/0TY59PERrxyqp4XMUkroayQvGd9r5dIW9Q1kSeXX9iJ442nXbFZKAOyCKV4Z13Q==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@sentry/cli-darwin@2.58.6':
|
||||
resolution: {integrity: sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA==}
|
||||
engines: {node: '>=10'}
|
||||
os: [darwin]
|
||||
|
||||
'@sentry/cli-linux-arm64@2.58.6':
|
||||
resolution: {integrity: sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux, freebsd, android]
|
||||
|
||||
'@sentry/cli-linux-arm@2.58.6':
|
||||
resolution: {integrity: sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
os: [linux, freebsd, android]
|
||||
|
||||
'@sentry/cli-linux-i686@2.58.6':
|
||||
resolution: {integrity: sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x86, ia32]
|
||||
os: [linux, freebsd, android]
|
||||
|
||||
'@sentry/cli-linux-x64@2.58.6':
|
||||
resolution: {integrity: sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux, freebsd, android]
|
||||
|
||||
'@sentry/cli-win32-arm64@2.58.6':
|
||||
resolution: {integrity: sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@sentry/cli-win32-i686@2.58.6':
|
||||
resolution: {integrity: sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x86, ia32]
|
||||
os: [win32]
|
||||
|
||||
'@sentry/cli-win32-x64@2.58.6':
|
||||
resolution: {integrity: sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@sentry/cli@2.58.6':
|
||||
resolution: {integrity: sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@sentry/conventions@0.12.0':
|
||||
resolution: {integrity: sha512-z1JQrl/1SLY+8wpzvork6vl+fpsg/oCCxM7HWWhUnI/R+OGNyoIzieQuggX3uUMY7NBtp8UWCQx6FeFazzOF9g==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -3204,6 +3267,15 @@ packages:
|
||||
resolution: {integrity: sha512-fpHL25JErsSfTyusuyZ3Q5JmRZeWYWynJO3PNiQH6A/58EqaCp8U5y8LCJ+CSPaeuBMka3S3Qn6U4eXcvkDMRA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@sentry/rollup-plugin@5.3.0':
|
||||
resolution: {integrity: sha512-hgPGPYdQJ/G1cGYOxAb7d4z3V+/k/E5/P/5TFPEEBLuIbFFk+JG0CISUDJdzXJjO382Lb99PBJuXGbueBmO79w==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
rollup: '>=3.2.0'
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@sentry/server-utils@10.59.0':
|
||||
resolution: {integrity: sha512-mR3fWaU7uGxIstRba6YO+/6V3qIa7432F7/U8EWHry+dY4C9DWAVG90E2GCzeD2MwLSP0tB25i8p1TWTGiQgVg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3213,6 +3285,10 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@sentry/vite-plugin@5.3.0':
|
||||
resolution: {integrity: sha512-qcoSzo4n2MulVQ70UUPLq6dTleb2a2HwL2wuwvAgWhPChrYTuk6A6mDg6aQb9fairPAwFPiU9PzOANpoDJcz1A==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@shikijs/core@2.5.0':
|
||||
resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==}
|
||||
|
||||
@@ -3906,6 +3982,10 @@ packages:
|
||||
resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==}
|
||||
engines: {node: '>=12.0'}
|
||||
|
||||
agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
agent-base@9.0.0:
|
||||
resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==}
|
||||
engines: {node: '>= 20'}
|
||||
@@ -5158,6 +5238,10 @@ packages:
|
||||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
find-up@5.0.0:
|
||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
find-versions@6.0.0:
|
||||
resolution: {integrity: sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -5418,6 +5502,10 @@ packages:
|
||||
resolution: {integrity: sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==}
|
||||
engines: {node: '>= 20'}
|
||||
|
||||
https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
https-proxy-agent@9.0.0:
|
||||
resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==}
|
||||
engines: {node: '>= 20'}
|
||||
@@ -5842,6 +5930,10 @@ packages:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
lodash-es@4.18.1:
|
||||
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
|
||||
|
||||
@@ -6309,6 +6401,15 @@ packages:
|
||||
node-fetch-native@1.6.7:
|
||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
|
||||
hasBin: true
|
||||
@@ -6503,6 +6604,10 @@ packages:
|
||||
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-limit@3.1.0:
|
||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
p-limit@7.3.0:
|
||||
resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==}
|
||||
engines: {node: '>=20'}
|
||||
@@ -6515,6 +6620,10 @@ packages:
|
||||
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
p-locate@5.0.0:
|
||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
p-map@7.0.4:
|
||||
resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -6882,6 +6991,9 @@ packages:
|
||||
resolution: {integrity: sha512-/+XMv9JalknuncEJSwsyEVlwcxVLKx2iaoSUXFZA86MJkdqyOdfrlB1sB7S6aKyUk9tl20YY+SgQe5J2sJHTcg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pump@3.0.4:
|
||||
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||
|
||||
@@ -7722,6 +7834,9 @@ packages:
|
||||
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
tr46@6.0.0:
|
||||
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
|
||||
engines: {node: '>=20'}
|
||||
@@ -8258,6 +8373,9 @@ packages:
|
||||
web-worker@1.5.0:
|
||||
resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
webidl-conversions@8.0.1:
|
||||
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||
engines: {node: '>=20'}
|
||||
@@ -8270,6 +8388,9 @@ packages:
|
||||
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
which-module@2.0.1:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
|
||||
@@ -8441,6 +8562,10 @@ packages:
|
||||
resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yocto-queue@0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yocto-queue@1.2.2:
|
||||
resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
|
||||
engines: {node: '>=12.20'}
|
||||
@@ -11386,6 +11511,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@sentry/babel-plugin-component-annotate@5.3.0': {}
|
||||
|
||||
'@sentry/browser-utils@10.59.0':
|
||||
dependencies:
|
||||
'@sentry/core': 10.59.0
|
||||
@@ -11398,6 +11525,63 @@ snapshots:
|
||||
'@sentry/replay': 10.59.0
|
||||
'@sentry/replay-canvas': 10.59.0
|
||||
|
||||
'@sentry/bundler-plugin-core@5.3.0':
|
||||
dependencies:
|
||||
'@babel/core': 7.29.7
|
||||
'@sentry/babel-plugin-component-annotate': 5.3.0
|
||||
'@sentry/cli': 2.58.6
|
||||
dotenv: 16.6.1
|
||||
find-up: 5.0.0
|
||||
glob: 13.0.6
|
||||
magic-string: 0.30.21
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@sentry/cli-darwin@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm64@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-arm@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-i686@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-linux-x64@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-arm64@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-i686@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli-win32-x64@2.58.6':
|
||||
optional: true
|
||||
|
||||
'@sentry/cli@2.58.6':
|
||||
dependencies:
|
||||
https-proxy-agent: 5.0.1
|
||||
node-fetch: 2.7.0
|
||||
progress: 2.0.3
|
||||
proxy-from-env: 1.1.0
|
||||
which: 2.0.2
|
||||
optionalDependencies:
|
||||
'@sentry/cli-darwin': 2.58.6
|
||||
'@sentry/cli-linux-arm': 2.58.6
|
||||
'@sentry/cli-linux-arm64': 2.58.6
|
||||
'@sentry/cli-linux-i686': 2.58.6
|
||||
'@sentry/cli-linux-x64': 2.58.6
|
||||
'@sentry/cli-win32-arm64': 2.58.6
|
||||
'@sentry/cli-win32-i686': 2.58.6
|
||||
'@sentry/cli-win32-x64': 2.58.6
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@sentry/conventions@0.12.0': {}
|
||||
|
||||
'@sentry/core@10.59.0': {}
|
||||
@@ -11460,6 +11644,14 @@ snapshots:
|
||||
'@sentry/browser-utils': 10.59.0
|
||||
'@sentry/core': 10.59.0
|
||||
|
||||
'@sentry/rollup-plugin@5.3.0':
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 5.3.0
|
||||
magic-string: 0.30.21
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@sentry/server-utils@10.59.0(vite@8.0.16(@types/node@22.20.0)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.47.1)(tsx@4.22.4)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
'@apm-js-collab/code-transformer': 0.15.0
|
||||
@@ -11473,6 +11665,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@sentry/vite-plugin@5.3.0':
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 5.3.0
|
||||
'@sentry/rollup-plugin': 5.3.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@shikijs/core@2.5.0':
|
||||
dependencies:
|
||||
'@shikijs/engine-javascript': 2.5.0
|
||||
@@ -12318,6 +12519,12 @@ snapshots:
|
||||
|
||||
adm-zip@0.5.17: {}
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
agent-base@9.0.0: {}
|
||||
|
||||
aggregate-error@3.1.0:
|
||||
@@ -13599,6 +13806,11 @@ snapshots:
|
||||
locate-path: 5.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
find-up@5.0.0:
|
||||
dependencies:
|
||||
locate-path: 6.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
find-versions@6.0.0:
|
||||
dependencies:
|
||||
semver-regex: 4.0.5
|
||||
@@ -13937,6 +14149,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
https-proxy-agent@5.0.1:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
https-proxy-agent@9.0.0:
|
||||
dependencies:
|
||||
agent-base: 9.0.0
|
||||
@@ -14358,6 +14577,10 @@ snapshots:
|
||||
dependencies:
|
||||
p-locate: 4.1.0
|
||||
|
||||
locate-path@6.0.0:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
lodash-es@4.18.1: {}
|
||||
|
||||
lodash.camelcase@4.3.0: {}
|
||||
@@ -14967,6 +15190,10 @@ snapshots:
|
||||
|
||||
node-fetch-native@1.6.7: {}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-gyp-build-optional-packages@5.2.2:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
@@ -15090,6 +15317,10 @@ snapshots:
|
||||
dependencies:
|
||||
p-try: 2.2.0
|
||||
|
||||
p-limit@3.1.0:
|
||||
dependencies:
|
||||
yocto-queue: 0.1.0
|
||||
|
||||
p-limit@7.3.0:
|
||||
dependencies:
|
||||
yocto-queue: 1.2.2
|
||||
@@ -15102,6 +15333,10 @@ snapshots:
|
||||
dependencies:
|
||||
p-limit: 2.3.0
|
||||
|
||||
p-locate@5.0.0:
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
|
||||
p-map@7.0.4: {}
|
||||
|
||||
p-queue@9.3.0:
|
||||
@@ -15461,6 +15696,8 @@ snapshots:
|
||||
dependencies:
|
||||
long: 5.3.2
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pump@3.0.4:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.5
|
||||
@@ -16521,6 +16758,8 @@ snapshots:
|
||||
dependencies:
|
||||
tldts: 7.0.30
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tr46@6.0.0:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
@@ -17099,6 +17338,8 @@ snapshots:
|
||||
|
||||
web-worker@1.5.0: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
webidl-conversions@8.0.1: {}
|
||||
|
||||
whatwg-mimetype@5.0.0: {}
|
||||
@@ -17111,6 +17352,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
|
||||
which-module@2.0.1: {}
|
||||
|
||||
which-pm-runs@1.1.0: {}
|
||||
@@ -17302,6 +17548,8 @@ snapshots:
|
||||
dependencies:
|
||||
pend: 1.2.0
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
yocto-queue@1.2.2: {}
|
||||
|
||||
yoctocolors@2.1.2: {}
|
||||
|
||||
@@ -154,10 +154,59 @@ describe("Analytics No-Leak Invariant (baked model)", () => {
|
||||
// Free-text message is replaced by the bare error type; the path cannot leak.
|
||||
expect(result.exception.values[0].value).toBe("Error");
|
||||
expect(result.exception.values[0].value).not.toContain("photo.jpg");
|
||||
// Stack frame keeps only the basename.
|
||||
// Filesystem-style paths still collapse to the basename (no dir/username leak).
|
||||
expect(result.exception.values[0].stacktrace.frames[0].filename).toBe("handler.png");
|
||||
});
|
||||
|
||||
it("Sentry keeps a host-stripped path for app bundle frames so maps symbolicate", async () => {
|
||||
await mod.initAnalytics(enabledConfig);
|
||||
const sentryCall = mockSentryInit.mock.calls.find((call: unknown[]) => call[0]?.beforeSend);
|
||||
const beforeSend = sentryCall?.[0].beforeSend;
|
||||
if (!beforeSend) return;
|
||||
|
||||
const event = {
|
||||
exception: {
|
||||
values: [
|
||||
{
|
||||
type: "TypeError",
|
||||
value: "boom",
|
||||
stacktrace: {
|
||||
frames: [
|
||||
{
|
||||
filename: "https://files.acme.example/assets/index-abc123.js",
|
||||
abs_path: "https://files.acme.example/assets/index-abc123.js",
|
||||
lineno: 5,
|
||||
colno: 12,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
debug_meta: {
|
||||
images: [
|
||||
{
|
||||
type: "sourcemap",
|
||||
code_file: "https://files.acme.example/assets/index-abc123.js",
|
||||
debug_id: "11111111-2222-3333-4444-555555555555",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const result = beforeSend(event);
|
||||
const frame = result.exception.values[0].stacktrace.frames[0];
|
||||
// Host-less path is kept (Sentry needs it to match the uploaded map); the
|
||||
// instance hostname is dropped. Line/column survive for symbolication.
|
||||
expect(frame.filename).toBe("/assets/index-abc123.js");
|
||||
expect(frame.abs_path).toBe("/assets/index-abc123.js");
|
||||
expect(frame.lineno).toBe(5);
|
||||
// debug_meta stays consistent with the frame and keeps the debug id.
|
||||
expect(result.debug_meta.images[0].code_file).toBe("/assets/index-abc123.js");
|
||||
expect(result.debug_meta.images[0].debug_id).toBe("11111111-2222-3333-4444-555555555555");
|
||||
// The instance hostname must not appear anywhere in the serialized event.
|
||||
expect(JSON.stringify(result)).not.toContain("acme.example");
|
||||
});
|
||||
|
||||
it("Sentry blocks ui.click breadcrumbs", async () => {
|
||||
await mod.initAnalytics(enabledConfig);
|
||||
const sentryCall = mockSentryInit.mock.calls.find(
|
||||
|
||||
@@ -220,9 +220,10 @@ describe("analytics lib (baked model)", () => {
|
||||
expect(result.breadcrumbs).toBeUndefined();
|
||||
// The exception message is replaced by its type so no free text leaves.
|
||||
expect(result.exception.values[0].value).toBe("TypeError");
|
||||
// Stack frames keep only the basename; absolute paths are dropped.
|
||||
// Filesystem paths collapse to the basename (directory and any username
|
||||
// stripped); abs_path is scrubbed the same way rather than dropped.
|
||||
expect(result.exception.values[0].stacktrace.frames[0].filename).toBe("file.png");
|
||||
expect(result.exception.values[0].stacktrace.frames[0].abs_path).toBeUndefined();
|
||||
expect(result.exception.values[0].stacktrace.frames[0].abs_path).toBe("photo.jpeg");
|
||||
});
|
||||
|
||||
it("handles event without user or exception fields", async () => {
|
||||
|
||||
Reference in New Issue
Block a user