GenerAI / worldmonitor /src /bootstrap /sentry-init.ts
amogaddy's picture
Integra World Monitor (AGPL-3.0, self-hosted) nello Space: pagina, menu, e arricchimento notizie per la AI (part 6)
fa9c65f verified
Raw
History Blame Contribute Delete
58.1 kB
/**
* Deferred Sentry SDK init and filtering policy.
*
* This module is dynamically imported by `sentry-defer.ts` so the large
* `beforeSend` policy and Sentry SDK import stay out of the eager dashboard
* entry chunk. Keep pre-init queuing in `sentry-defer.ts`; keep SDK setup here.
*/
import { isDebugBearRumScriptFrame } from './debugbear-rum';
import { getSentryBuildMetadata } from './sentry-build-metadata';
type SentryNs = typeof import('@sentry/browser');
// Known third-party hosts fetched by MapLibre (tiles, styles, glyphs, sprites).
// Hosts whose `Failed to fetch (<host>)` errors are suppressed in beforeSend.
// Originally maplibre-only (transient tile/style failures), expanded to cover
// first-party callers that hit the same hosts directly (e.g.
// `MapContainer.fetchAndApplyRadar` β†’ `api.rainviewer.com`). The set IS the
// safety: only known third-party hosts are suppressed; first-party fetches
// to `api.worldmonitor.app` and the self-hosted R2 PMTiles bucket are NOT
// in the set, so genuine basemap / API regressions still surface.
const THIRD_PARTY_FETCH_HOST_ALLOWLIST = new Set([
'tilecache.rainviewer.com',
'api.rainviewer.com', // weather radar API used by MapContainer.fetchAndApplyRadar β€” WORLDMONITOR-QG
'basemaps.cartocdn.com',
'tiles.openfreemap.org',
'protomaps.github.io',
// Clerk Frontend API (CNAME β†’ Clerk's auth infra). The bundled Clerk SDK
// fetches it for session/token refresh and retries transient failures
// itself (`retryImmediately`); a `Failed to fetch (clerk.worldmonitor.app)`
// that leaks to onunhandledrejection is a Clerk-SDK-internal network blip,
// not our code β€” same disposition as the existing `/ClerkJS: Network error/`
// ignoreError. NOT our `api.worldmonitor.app`, which stays off the list so
// genuine API regressions still surface (WORLDMONITOR-SA/SB).
'clerk.worldmonitor.app',
// DebugBear RUM beacon collector. We embed the DebugBear RUM script
// (`src/bootstrap/debugbear-rum.ts` β†’ cdn.debugbear.com) whose collector
// POSTs field metrics to `data.debugbear.com`; a leaked
// `NetworkError ... (data.debugbear.com)` / `Failed to fetch (data.debugbear.com)`
// is a dropped monitoring beacon (adblock / network blip) β€” invisible to the
// user and unactionable, same disposition as the Clerk-SDK-internal fetch
// above. NOT `api.worldmonitor.app` (stays off so real API regressions
// surface). WORLDMONITOR-RP.
'data.debugbear.com',
// Self-hosted Umami analytics collector (`src/services/analytics.ts` loads
// `abacus.worldmonitor.app/script.js`, whose tracker POSTs events to
// `/api/send`). Same disposition as the DebugBear beacon above: a dropped
// analytics beacon is invisible to the user and unactionable β€” typically an
// ad-blocker or a fetch-wrapping extension killing the POST. It reaches
// Sentry despite the extension gate because the leaked rejection carries our
// Vite `window.fetch` trampolines, which make hasFirstParty true. Serves no
// product data, so an abacus outage belongs to uptime monitoring, not a
// per-user Sentry error. NOT `api.worldmonitor.app` (stays off so real API
// regressions surface). WORLDMONITOR-WH/WJ.
'abacus.worldmonitor.app',
]);
function buildSentryInitOptions(): Parameters<SentryNs['init']>[0] {
const sentryDsn = import.meta.env.VITE_SENTRY_DSN?.trim();
return {
dsn: sentryDsn || undefined,
...getSentryBuildMetadata(__APP_VERSION__, __BUILD_HASH__),
environment: (location.hostname === 'worldmonitor.app' || location.hostname.endsWith('.worldmonitor.app')) ? 'production'
: location.hostname.includes('vercel.app') ? 'preview'
: 'development',
enabled: Boolean(sentryDsn) && !location.hostname.startsWith('localhost') && !('__TAURI_INTERNALS__' in window),
allowUrls: [
/https?:\/\/(www\.|tech\.|finance\.|commodity\.|happy\.)?worldmonitor\.app/,
/https?:\/\/.*\.vercel\.app/,
],
sendDefaultPii: true,
tracesSampleRate: 0.1,
ignoreErrors: [
'Invalid WebGL2RenderingContext',
'WebGL context lost',
/imageManager/,
/ResizeObserver loop/,
/NotAllowedError/,
/InvalidAccessError/,
/importScripts/,
/^TypeError: Load failed( \(.*\))?$/,
/^TypeError: (?:cancelled|avbruten)$/,
/runtime\.sendMessage\(\)/,
/Java object is gone/,
/^Object captured as promise rejection with keys:/,
/Unable to load image/,
/Non-Error promise rejection captured with value:/,
/Connection to Indexed Database server lost/,
// Library-thrown (Convex client / Clerk persistent cache) when the user's
// browser has IndexedDB disabled (Safari Private Browsing, hardened
// Firefox, some WebView contexts). Our code only initializes the
// library; the throw is environmental and unavoidable from our side.
// Same disposition as the existing "Connection to Indexed Database
// server lost" entry above. WORLDMONITOR-RC.
/^IndexedDBUnavailableError|IndexedDB is not available in this environment/,
/webkit\.messageHandlers/,
/(?:unsafe-eval.*Content Security Policy|Content Security Policy.*unsafe-eval)/,
/Fullscreen request denied/,
/requestFullscreen/,
/webkitEnterFullscreen/,
/vc_text_indicators_context/,
/Program failed to link/,
/too much recursion/,
/zaloJSV2/,
/Java bridge method invocation error/,
/Could not compile fragment shader/,
/can't redefine non-configurable property/,
/Can.t find variable: (CONFIG|currentInset|NP|webkit|EmptyRanges|logMutedMessage|UTItemActionController|DarkReader|Readability|onPageLoaded|Game|frappe|getPercent|ucConfig|\$a)/,
/invalid origin/,
/\.data\.split is not a function/,
/signal is aborted without reason/,
/contentWindow\.postMessage/,
/Could not compile vertex shader/,
/objectStoreNames/,
/Unexpected identifier 'https'/,
/Can't find variable: _0x/,
/Can't find variable: video/,
/hackLocationFailed is not defined/,
/userScripts is not defined/,
/NS_ERROR_ABORT/,
/NS_ERROR_OUT_OF_MEMORY/,
/NS_ERROR_UNEXPECTED/, // Firefox XPCOM: Worker init failure on privacy-hardened Firefox/Ubuntu β€” WORLDMONITOR-N6/N7/N8/N9
/NS_ERROR_FILE_NO_DEVICE_SPACE/, // Firefox XPCOM: disk-full on IndexedDB/cache/SW write β€” WORLDMONITOR-Q0
/DataCloneError.*could not be cloned/,
/cannot decode message/,
/WKWebView was deallocated/,
// WKWebView host-app JS bridge timeout β€” Apple WebKit emits this exact phrase
// when a JS-to-native `postMessage` (e.g. WKScriptMessageHandler) gets no
// reply within the host's expected window. Common in in-app browsers like
// DuckDuckGo / Yelp / Reddit-mobile / Instagram. We never postMessage to a
// WKScriptMessageHandler ourselves; this is browser-native and unactionable
// (WORLDMONITOR-KJ β€” 15 events / 14 users in DuckDuckGo 26.3 on macOS).
/WKWebView API client did not respond to this postMessage/,
/Unexpected end of(?: JSON)? input/,
/window\.android\.\w+ is not a function/,
/Attempted to assign to readonly property/,
/Cannot assign to read only property/,
/FetchEvent\.respondWith/,
/QuotaExceededError/,
/^TypeError: ε·²ε–ζΆˆ$/,
/^fetchError: Network request failed$/,
/window\.ethereum/,
/setting 'luma'/,
/ML request .* timed out/,
/(?:AbortError: )?The operation was aborted\.?\s*$/,
// Bare `Uncaught Error: AbortError` (no message body) from Convex
// server-side action timeouts auto-captured by Convex's Sentry
// integration. Zero-frame, environment 'prod', no actionable context
// β€” the action retries cleanly. WORLDMONITOR-QH.
/^Uncaught Error: AbortError$/,
/Unexpected end of script/,
/Style is not done loading/,
/Event `CustomEvent`.*captured as promise rejection/,
/Event `ProgressEvent`.*captured as promise rejection/, // resource/XHR `error` ProgressEvent leaking via onunhandledrejection (img/script/audio/EventSource load failure). Our IDB/worker/FileReader onerror handlers all reject with wrapped Errors (never a raw ProgressEvent); the only XHR caller is fire-and-forget + Tauri-desktop-only where Sentry is disabled β€” so a raw ProgressEvent rejection can never originate from our bundle. Sibling of the CustomEvent entry above β€” WORLDMONITOR-SQ
/getProgramInfoLog/,
/__firefox__/,
/ifameElement\.contentDocument/,
/Invalid video id/,
/Fetch is aborted/,
/Stylesheet append timeout/,
/Worker is not a constructor/,
/_pcmBridgeCallbackHandler/,
/UCShellJava/,
/Cannot define multiple custom elements/,
/maxTextureDimension2D/,
/Container app not found/,
/this\.St\.unref/,
/evaluating 'elemFound\.value'/,
/[Cc]an(?:'t|not) access (?:'\w+'|lexical declaration '\w+') before initialization/,
/^Uint8Array$/,
/createObjectStore/,
/The database connection is closing/,
/shortcut icon/,
/Attempting to change value of a readonly property/,
/reading 'nodeType'/,
/The node to be removed is not a child of this node/,
/The object can not be found here/, // Safari variant of above (Clerk SDK removeChild on detached DOM)
/feature named .\w+. was not found/,
/a2z\.onStatusUpdate/,
/Attempting to run\(\), but is already running/,
/this\.player\.destroy is not a function/,
/isReCreate is not defined/,
/reading 'style'.*HTMLImageElement/,
/can't access property "write", \w+ is undefined/,
/(?:AbortError: )?The user aborted a request/,
/\w+ is not a function.*\/uv\/service\//,
/__isInQueue__/,
/^(?:LIDNotify(?:Id)?|onWebViewAppeared|onGetWiFiBSSID|onHide|onShow|onReady|tapAt|removeHighlight|UTItemActionController) is not defined$/,
/Se requiere plan premium/,
/hybridExecute is not defined/,
/reading 'postMessage'/,
/appendChild.*Unexpected token/,
/\bmag is not defined\b/,
/evaluating '[^']*\.luma/,
/translateNotifyError/,
/GM_getValue/,
/gm_menus/, // WORLDMONITOR-TJ β€” Greasemonkey/Violentmonkey internal (GUID-keyed window['<uuid>'].gm_menus userscript-menu registry); never in our bundle, sibling of GM_getValue
/^InvalidStateError:|The object is in an invalid state/,
/Could not establish connection\. Receiving end does not exist/,
/webkitCurrentPlaybackTargetIsWireless/,
/webkit(?:Supports)?PresentationMode/,
/Cannot redefine property: webdriver/,
/null is not an object \(evaluating '\w+\.theme'\)/,
/this\.player\.\w+ is not a function/,
/videoTrack\.configuration/,
/evaluating 'v\.setProps'/,
/button\[aria-label/,
/The fetching process for the media resource was aborted/,
/Invalid regular expression: missing/,
/WeixinJSBridge/,
/evaluating '\w+\.type'/,
/Policy with name .* already exists/,
/[sx]wbrowser is not defined/,
/browser\.storage\.local/,
/The play\(\) request was interrupted/,
/MutationEvent is not defined/,
/Cannot redefine property: userAgent/,
/st_framedeep|ucbrowser_script/,
/iabjs_unified_bridge/,
/DarkReader/,
/window\.receiveMessage/,
/Cross-origin script load denied/,
/orgSetInterval is not a function/,
/Blocked a frame with origin.*accessing a cross-origin frame/,
/SnapTube/,
/sortedTrackListForMenu/,
/isWhiteToBlack/,
/window\.videoSniffer/,
/closeTabMediaModal/,
/missing \) after argument list/,
/Error invoking postMessage: Java exception/,
/IndexSizeError/,
/Failed to construct 'Worker'.*cannot be accessed from origin/,
/undefined is not an object \(evaluating '(?:this\.)?media(?:Controller)?\.(?:duration|videoTracks|readyState|audioTracks|media)/,
/\$ is not defined/,
/Qt\([^)]*\) is not a function/,
/shaderSource must be an instance of WebGLShader/,
/WebGL2RenderingContext\.shaderSource: Argument 1 is not an object/,
// Chrome wording for the same condition (gl.createShader returned null,
// typically after WebGL context loss or on degraded GPU drivers). WORLDMONITOR-RM.
/Failed to execute 'shaderSource' on 'WebGL2?RenderingContext': parameter 1 is not of type 'WebGLShader'/,
/Failed to initialize WebGL/,
/opacityVertexArray\.length/,
/Length of new data is \d+, which doesn't match current length of/,
/^AJAXError:.*(?:Load failed|Unauthorized|\(401\))/,
/^NetworkError: Load failed$/,
/^A network error occurred\.?$/,
/nmhCrx is not defined/,
/\bcrusoe is not defined\b/, // WORLDMONITOR-R3 β€” injected userscript reference, anonymous-frames-only stack
/\bvc_request_action is not defined\b/, // WORLDMONITOR-RB β€” Samsung Internet / Tizen smart-view-cast global injection
/\bmainWorldSdk is not defined\b/, // WORLDMONITOR-TG β€” browser extension SDK injected into the page main world references its global before define; not in our bundle (Edge 148/Windows, anonymous-frames-only stack)
/navigationPerformanceLoggerJavascriptInterface/,
/jQuery is not defined/,
/illegal UTF-16 sequence/,
/detectIncognito/,
/Cannot read properties of null \(reading '__uv'\)/,
/Can't find variable: p\d+/,
/^timeout$/,
/Can't find variable: caches/,
/crypto\.randomUUID is not a function/,
/ucapi is not defined/,
/Identifier '(?:script|reportPage|element|Shop|change_ua|originalPrompt)' has already been declared/, // change_ua: User-Agent-changer browser extension injecting same script twice β€” WORLDMONITOR-2D (88 events / 26 users). originalPrompt: extension hooking window.prompt double-injected β€” WORLDMONITOR-TE (not in our bundle; build would fail on a duplicate top-level const)
/getAttribute is not a function.*getAttribute\("role"\)/,
/SCDynimacBridge/,
/errTimes is not defined/,
/Failed to get ServiceWorkerRegistration/,
/^ReferenceError: Cannot access uninitialized variable\.?$/,
/Failed writing data to the file system/,
/Error invoking initializeCallbackHandler/,
/releasePointerCapture.*Invalid pointer/,
/Array buffer allocation failed/,
/Client can't handle this message/,
/Invalid LngLat object/,
/autoReset/,
/webkitExitFullScreen/,
/downProgCallback/,
/syncDownloadState/,
/^ReferenceError: HTMLOUT is not defined$/,
/^ReferenceError: xbrowser is not defined$/,
/LibraryDetectorTests_detect/,
/contentBoxSize\[0\] is undefined/,
/Attempting to run\(\), but is already running/,
/Out of range source coordinates for DEM data/,
/Invalid character: '\\0'/,
/Failed to execute 'unobserve' on 'IntersectionObserver'/,
/WKErrorDomain/,
/Content-Length header of network response exceeds response Body/,
/^Uncaught \[object ErrorEvent\]$/,
/^\[object Event\]$/,
/trsMethod\w+ is not defined/,
/checkLogin is not a function/,
/VConsole is not defined/,
/exitFullscreen.*Document not active/,
/Force close delete origin/,
/zp_token is not defined/,
/literal not terminated before end of script/,
/'' is not a valid selector/,
/frappe is not defined/,
/Unexpected identifier 'does'/,
/Failed reading data from the file system/,
/^UnavailableError(:.*)?$/,
/null is not an object \(evaluating '\w{1,3}\.indexOf'\)/,
/export declarations may only appear at top level/,
/ucConfig is not defined/,
/getShaderPrecisionFormat/,
/Cannot read properties of null \(reading 'touches'\)/,
/Failed to execute 'querySelectorAll' on '[^']*': ':[a-z]+\(/,
/args\.site\.enabledFeatures/,
/can't access property "\w+", FONTS\[/,
/null is not an object \(evaluating '\w+\.magnitude\.toFixed'\)/,
/start offset of Int16Array should be a multiple of 2/,
/Cannot read properties of undefined \(reading 'then'\)/,
/^(?:Error: )?uncaught exception: undefined$/,
/ss_bootstrap_config/, // Surfly proxy β€” "Can't find variable: ss_bootstrap_config" (Safari) or "ss_bootstrap_config is not defined" (Chrome)
/undefined is not an object \(evaluating '[a-z]\.includes'\)/,
/^"use strict" is not a function$/,
/Can only call Window\.setTimeout on instances of Window/, // iOS Safari cross-frame setTimeout from 3rd-party injected script
/^Can't find variable: _G$/, // browser extension/userscript injecting _G global
/onAppPageCallback is not defined/, // Android Chrome WebView injection (Huawei/Samsung browsers)
/\.at is not a function/, // Instagram/older Android in-app browsers missing Array.at()
/Response cannot have a body with the given status/, // Safari: Response constructor with 204/304 + body
/ClerkJS: Network error/, // Clerk SDK transient network failures on user devices
/^ClerkJS: Response: needs_(?:first|second)_factor\b/, // Clerk SDK auth-flow branch not yet supported; SDK-internal limitation, not our code β€” WORLDMONITOR-Q1. Narrow to the observed `needs_*_factor` family so future actionable `ClerkJS: Response: <something>` errors (e.g. misconfigured redirect URI) still surface.
/\[clerk\] failed to load/, // Clerk SDK failed to load its own UI chunk from clerk.worldmonitor.app β€” SDK-internal load failure, not our code (WORLDMONITOR-??: Yandex Browser 26.4).
/doesn't provide an export named/, // stale cached chunk after deploy references removed export
/Possible side-effect in debug-evaluate/, // Chrome DevTools internal EvalError
/ConvexError: CONFLICT/, // Expected OCC rejection on concurrent preference saves
/ConvexError: API_ACCESS_REQUIRED/, // Expected business error: free user opens API Keys tab; client handles gracefully (UnifiedSettings.ts:731-738) β€” WORLDMONITOR-NA
/\[CONVEX [AQM]\(.+?\)\] Connection lost while action was in flight/, // Convex SDK transient WS disconnect
/^Invalid start version: \d+:\d+:\d+, transitioning from \d+:\d+:\d+$/, // Convex SDK internal sync protocol error from `remote_query_set.js` (server republished query mid-transition or WS reconnect race) β€” WORLDMONITOR-Q5
/Response did not contain `success` or `data`/, // DuckDuckGo browser internal tracker/content-block response β€” never emitted by our code
/Cannot set properties of undefined \(setting 'bodyTouched'\)/, // Quark browser (Alibaba mobile) touch-tracking script injection (WORLDMONITOR-N1)
/Cannot read properties of \w+ \(reading '[^']*[^\x00-\x7F][^']*'\)/, // Non-ASCII property name in message = mojibake/corrupted identifier from injected extension; our bundle emits ASCII-only identifiers (WORLDMONITOR-NS)
/Octal literals are not allowed in strict mode/, // Runtime SyntaxError from injected extension script; our TS bundle never emits octal literals and doesn't eval (WORLDMONITOR-NV)
/Unexpected identifier 'm'/, // Foreign script injection on Opera; pre-compiled bundle can't parse-fail at runtime (WORLDMONITOR-NT)
/PlayerControlsInterface\.\w+ is not a function/, // Android Chrome WebView native bridge injection (Bilibili/UC/QQ-style host) β€” never emitted by our code (WORLDMONITOR-P2)
/github\.com\/styled-components\/styled-components\/blob/, // styled-components runtime error (errors.md#N URL); we don't depend on styled-components, so it can only be a browser extension (Grammarly et al.) injecting its own bundle β€” WORLDMONITOR-SE
],
beforeSend(event) {
const msg = event.exception?.values?.[0]?.value ?? '';
if (msg.length <= 3 && /^[a-zA-Z_$]+$/.test(msg)) return null;
const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];
const vendorChunk = /\/(maplibre|deck-stack|d3|topojson|i18n|sentry|transformers|onnxruntime)-[A-Za-z0-9_-]+\.js/;
const firstPartyFile = (filename: string) => {
if (/\.(ts|tsx)$/.test(filename) || /^src\//.test(filename)) return true;
if (/\/assets\/[A-Za-z0-9_-]+(-[A-Za-z0-9_-]+)*\.js/.test(filename)) return !vendorChunk.test(filename);
return false;
};
const nonInfraFrames = frames.filter(f => f.filename && f.filename !== '<anonymous>' && f.filename !== '[native code]' && !/\/sentry-[A-Za-z0-9_-]+\.js/.test(f.filename));
const hasFirstParty = nonInfraFrames.some(f => firstPartyFile(f.filename ?? ''));
const hasAnyStack = nonInfraFrames.length > 0;
// Suppress maplibre internal null-access crashes (light, placement) only when stack is in map chunk
if (/this\.style\._layers|reading '_layers'|this\.(light|sky) is null|can't access property "(id|type|setFilter|bind)"[,] ?[\w.]+ is (null|undefined)|can't access property "(id|type)" of null|Cannot read properties of null \(reading '(id|type|setFilter|_layers)'\)|null is not an object \(evaluating '\w{1,3}\.(id|style)|^\w{1,2} is null$/.test(msg)) {
if (frames.some(f => /\/(map|maplibre|deck-stack)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
}
// Suppress any TypeError / RangeError that happens entirely within maplibre or deck.gl internals.
// RangeError: "Invalid array length" during deck.gl bindVertexArray / _updateCache on large
// GL layer updates (vertex-buffer allocation failure in vendor code β€” WORLDMONITOR-N4).
// EXCEPTION: `Failed to fetch (<host>)` is routed through the host-allowlist block below
// so a self-hosted R2 PMTiles / first-party basemap regression isn't silently dropped just
// because its stack happens to be all-vendor frames (WORLDMONITOR-NE/NF follow-up).
const excType = event.exception?.values?.[0]?.type ?? '';
// Host-suffixed fetch-failure shapes β€” Chrome/Edge `Failed to fetch (<host>)`
// (maplibre's AJAX wrapper AND first-party fetch callers) and Firefox
// `NetworkError when attempting to fetch resource. (<host>)` (the
// engine-equivalent phrasing, e.g. an embedded SDK's beacon fetch β€”
// WORLDMONITOR-RP). Both route through the host allowlist below, which is
// the load-bearing safety; this match is just the shape detector.
const isHostScopedFetchFailure = excType === 'TypeError'
&& /^(?:Failed to fetch|NetworkError when attempting to fetch resource\.) \([^)]+\)$/.test(msg);
if (!isHostScopedFetchFailure
&& (excType === 'TypeError' || excType === 'RangeError' || /^(?:TypeError|RangeError):/.test(msg))
&& frames.length > 0) {
if (nonInfraFrames.length > 0 && nonInfraFrames.every(f => /\/(map|maplibre|deck-stack)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
}
// Suppress `Failed to fetch (<host>)` for known third-party hosts. Originally
// scoped to maplibre's tile/style/glyph fetches (which wrap transient network
// errors and rethrow in a Generator-backed Promise that leaks to
// onunhandledrejection even though DeckGLMap's map-error handler already
// logs the warning). Expanded (WORLDMONITOR-QG) to also cover first-party
// call sites that fetch the same allowlisted hosts directly β€” e.g.
// `MapContainer.fetchAndApplyRadar` hitting `api.rainviewer.com`. The
// host-allowlist set is the load-bearing safety: only known third-party
// hosts get suppressed; first-party fetch failures (self-hosted R2 PMTiles
// bucket, `api.worldmonitor.app`) are intentionally NOT in the set so a
// real basemap / API regression is never silently dropped
// (WORLDMONITOR-NE/NF, WORLDMONITOR-QG).
if (isHostScopedFetchFailure) {
const hostMatch = msg.match(/^(?:Failed to fetch|NetworkError when attempting to fetch resource\.) \(([^)]+)\)$/);
const host = hostMatch?.[1];
if (host && THIRD_PARTY_FETCH_HOST_ALLOWLIST.has(host)) return null;
}
// Suppress Three.js/globe.gl TypeError crashes in main bundle (reading 'type'/'pathType'/'count'/'__globeObjType' on undefined during WebGL traversal/raycast).
// __globeObjType is exclusively set by three-globe on its own objects and we have no user onClick/onHover handler, so it is always globe.gl internal even when the stack shows the bundled main chunk (WORLDMONITOR-ME).
if (/reading '__globeObjType'|__globeObjType/.test(msg)) return null;
if (/reading '(?:type|pathType|count)'|can't access property "(?:type|pathType|count|__globeObjType)",? \w+ is (?:undefined|null)|undefined is not an object \(evaluating '\w+\.(?:pathType|count)'\)/.test(msg)) {
if (!hasFirstParty) return null;
}
// deck.gl/maplibre internal null-access on Layer.isHidden during render (Safari 26.4 beta,
// empty stacks, preceded by DeckGLMap map-error breadcrumbs). Our first-party `isHidden`
// lives on SmartPollContext in runtime.ts β€” any access there would produce frames, so gate
// on !hasFirstParty to preserve signal on a real poller regression (WORLDMONITOR-NR).
if (/undefined is not an object \(evaluating '\w{1,3}\.isHidden'\)|Cannot read properties of undefined \(reading 'isHidden'\)/.test(msg)) {
if (!hasFirstParty) return null;
}
// Short minified ReferenceError from Safari ("Can't find variable: ss"). With an empty stack
// and no first-party frames, this is userscript/extension injection. Our own minified bundle
// would keep frames via the source-mapped assets/*.js chunks; if the SDK strips them, the
// stack is non-empty. Bound var length to 1–2 to avoid masking a real "foo is not defined"
// that happens to hit the unhandledrejection path (WORLDMONITOR-NQ).
if (!hasFirstParty && frames.length === 0 && /^Can't find variable: \w{1,2}$/.test(msg)) return null;
// Suppress minified Three.js/globe.gl crashes (e.g. "l is undefined" in raycast, "b is undefined" in update/initGlobe)
if (/^\w{1,2} is (?:undefined|not an object)$/.test(msg) && frames.length > 0) {
if (frames.some(f => /\/(main|index)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? '') && /(raycast|update|initGlobe|traverse|render)/.test(f.function ?? ''))) return null;
}
// Suppress Three.js OrbitControls touch crashes (finger lifted during pinch-zoom).
// OrbitControls is bundled into the main chunk, so hasFirstParty is true.
// Match by function name pattern (_handleTouch*Dolly*) or suppress when no first-party frames.
//
// Symbolicated case: function name regex hits (_handleTouchDolly*, OrbitControls).
// Unsymbolicated case (Sentry WORLDMONITOR-P7): single minified frame in the main
// bundle (e.g. `Yge`) on iOS/iPadOS Safari. iOS is the only platform where a
// touch-driven `t.x` crash is plausible AND the production build can lose source
// maps for OrbitControls' touch handlers. Gate on:
// - exactly one main-bundle frame in the trace (no other first-party functions)
// - device.family/os indicates iOS/iPadOS
// so a real `t.x` regression elsewhere on desktop still surfaces.
if (/undefined is not an object \(evaluating 't\.x'\)|Cannot read properties of undefined \(reading 'x'\)/.test(msg)) {
if (!hasFirstParty || frames.some(f => /\b_handleTouch\w*Dolly|OrbitControls/.test(f.function ?? ''))) return null;
const osName = ((event.contexts as any)?.os?.name as string) ?? '';
const isTouchOs = /^(iOS|iPadOS)$/.test(osName);
const mainBundleFrames = nonInfraFrames.filter(f => /\/(main|index)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''));
if (isTouchOs && mainBundleFrames.length === 1 && nonInfraFrames.length === mainBundleFrames.length) return null;
}
// Suppress Three.js OrbitControls pointer-capture race: pointerdown handler calls
// setPointerCapture but the browser has already released the pointer (focus change,
// rapid re-tap). OrbitControls is bundled into main-*.js, so hasFirstParty=true and
// production stacks are often unsymbolicated β€” require a positive three.js signature
// in the frame context (the literal `this._pointers … setPointerCapture` code slice)
// so an unrelated first-party setPointerCapture regression still surfaces (WORLDMONITOR-NC).
if (excType === 'NotFoundError' && /setPointerCapture.*No active pointer with the given id/.test(msg)) {
// Sentry wire format includes `context: [[lineno, text], ...]` per frame, but the
// SDK's StackFrame type omits it β€” cast to any to read it.
const hasOrbitControlsContext = frames.some(f => {
const ctx = (f as any).context;
if (!Array.isArray(ctx)) return false;
return ctx.some(row =>
Array.isArray(row) && typeof row[1] === 'string'
&& /_pointers[^\n]*setPointerCapture|setPointerCapture[^\n]*_pointers/.test(row[1]),
);
});
if (hasOrbitControlsContext) return null;
}
// Suppress deck.gl/maplibre null-access crashes with no usable stack trace (requestAnimationFrame wrapping)
if (/null is not an object \(evaluating '\w{1,3}\.(id|type|style)'\)/.test(msg) && frames.length === 0) return null;
// Suppress Safari sortedTrackListForMenu native crash (value is generic "Type error", function name in stack)
if (excType === 'TypeError' && frames.some(f => /sortedTrackListForMenu/.test(f.function ?? ''))) return null;
// Suppress TypeErrors from anonymous/injected scripts (no real source files or only inline page URL)
if ((excType === 'TypeError' || /^TypeError:/.test(msg)) && frames.length > 0 && frames.every(f => !f.filename || f.filename === '<anonymous>' || /^blob:/.test(f.filename) || /^https?:\/\/[^/]+\/?$/.test(f.filename))) return null;
// Suppress errors thrown by an injected browser-automation harness driving
// the page (e.g. Floot's agent). Its selector-resolution helpers throw
// `Element not found: <sel>`, `No element found: <sel>`, and
// `$pressKey(...) was called with no selector` from an injected
// `<anonymous>` script (helperGetStyle et al.), and reference Floot's own
// `data-floot-id` attribute. These are generic `Error` (not TypeError, so
// the anonymous-script gate above misses them) whose only frames are
// `<anonymous>` β†’ hasFirstParty=false. Our bundle never emits these
// phrasings (grep-verified across src/ + api/). Gated on !hasFirstParty so
// a same-worded first-party error would still surface. The `... found:`
// matches REQUIRE the trailing colon+selector, leaving the colon-less
// ambiguous `Element not found` (handled by the !hasFirstParty ambiguous
// gate below, which additionally demands a confirmed third-party stack)
// untouched. WORLDMONITOR-VR/VV/VW/VX/VY/VS/VT/VZ (2026-07-09 Floot agent).
// NB: the `called with no selector` regex deliberately omits the leading
// "was " β€” the beforeSend unit-test harness strips TypeScript `as <T>`
// assertions with a crude `/as\s+\w+/` that also mangles the English
// "was called". Matching from "called" keeps the test's eval'd copy intact.
if (!hasFirstParty && (
/^(?:Element not found|No element found):/.test(msg)
|| /\bcalled with no selector\b/.test(msg)
|| /data-floot-id/.test(msg)
)) return null;
// Suppress parentNode.insertBefore from injected/inline scripts (iOS WKWebView, Apple Mail)
// Also covers [native code] frames (no filename) produced by WKWebView's forEach wrapper
if (/parentNode\.insertBefore/.test(msg) && frames.every(f => !f.filename || f.filename === '<anonymous>' || f.filename === '[native code]' || /^blob:/.test(f.filename) || /^https?:\/\/[^/]+\/?$/.test(f.filename))) return null;
// Suppress TypeErrors whose ONLY source frames are non-script URLs β€” the
// page document URL itself (a relative path like `/dashboard` or the full
// `https://<host>/dashboard`) or any external resource served without a
// recognized script extension. Scripts injected into the page's MAIN world
// β€” WKUserScript content scripts on Firefox iOS / other in-app WebViews,
// bookmarklets β€” are attributed by WebKit to the document URL (line 1,
// minified fns `o`/`s`/`Or`, with native `insertBefore`/`forEach` frames),
// not to a distinct `.js` script URL. Our own shipped code never runs from
// such a URL: the app entry is a hashed `/assets/*.js` module chunk (flagged
// first-party by firstPartyFile), so a TypeError with zero first-party
// frames whose non-infra frames are all non-script URLs cannot originate in
// our bundle. The `frames.every(...)` injected-script gate above misses this
// because the `[native code]` insertBefore/forEach frames break its
// predicate and its page-URL matcher only accepts bare origins, not document
// paths (WORLDMONITOR-V8: Firefox iOS 152, `undefined is not an object
// (evaluating 's[e]')`, insertBefore in a promiseReactionJob β€” 6 events / 1
// user).
//
// `isNonScriptUrlFrame` is intentionally broader than "page document": the
// `https?://` branch also matches extensionless third-party script URLs
// (e.g. `https://js.stripe.com/v3/`). That is still correct to suppress here
// β€” the `!hasFirstParty` guard already proves zero first-party involvement,
// so a same-shaped error from a third-party host is equally unactionable.
const isNonScriptUrlFrame = (filename: string) =>
!/\.(?:m|c)?[jt]sx?(?:[?#]|$)/.test(filename)
&& (/^\/(?!\/)/.test(filename) || /^https?:\/\//.test(filename));
if ((excType === 'TypeError' || /^TypeError:/.test(msg))
&& !hasFirstParty
&& nonInfraFrames.length > 0
&& nonInfraFrames.every(f => isNonScriptUrlFrame(f.filename ?? ''))) return null;
// Suppress NotFoundError: insertBefore with no usable stack (Chrome 146+ extension DOM interference β€” stack shows minified bundle but no line/function)
if (excType === 'NotFoundError' && /insertBefore/.test(msg) && frames.every(f => !f.lineno && !f.function)) return null;
// Suppress Sentry breadcrumb DOM-measuring crashes (element.offsetWidth on detached DOM)
if (/evaluating '(?:element|e)\.offset(?:Width|Height)'/.test(msg) && frames.some(f => /\/sentry-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
// Suppress errors originating entirely from blob: URLs (browser extensions)
if (frames.length > 0 && frames.every(f => /^blob:/.test(f.filename ?? ''))) return null;
// Suppress errors where any frame is a chrome/moz/safari extension, ONLY when stack has no first-party frames.
// A first-party frame elsewhere in the stack means the error likely originated in our code; surface it even if
// an extension wrapped the call.
if (!hasFirstParty && frames.some(f => /^(?:chrome|moz|safari(?:-web)?)-extension:\/\//.test(f.filename ?? ''))) return null;
// Bare `Failed to fetch` leaking via onunhandledrejection when a browser
// extension has monkeypatched `window.fetch` (e.g. Adjust SDK's
// injectScriptAdjust.js, page-inspector extensions) and chained an uncaught
// `.then()` on the result. A transient network blip rejects the underlying
// fetch and the extension's orphan promise surfaces as an unhandled rejection.
// Our first-party frames (the runtime fetch interceptor + country-geometry
// loader) appear ONLY because our wrapper sits in the call chain β€” our own
// fetch callers already wrap rejections in try/catch (country-geometry's
// ensureLoaded logs a warning and resolves), so this is NOT a first-party
// leak. Unlike the generic `!hasFirstParty` `Failed to fetch` gate below,
// this fires WITH first-party frames present, but only when an extension has
// a monkeypatched-`window.fetch` frame on the stack β€” a genuine API outage
// (host-suffixed `Failed to fetch (<host>)`, handled above) and any
// non-extension user are unaffected. The function match is anchored to
// exactly `window.fetch` / `fetch` or the `Function.prototype.apply`
// trampoline (`Object.apply` / `apply`) an extension's hook.js uses to
// re-invoke the original fetch β€” NOT a loose `/fetch/` β€” so an extension
// frame named `fetchContent` / `prefetch` does NOT swallow a real bare
// `Failed to fetch` from our own code (WORLDMONITOR-SG). The `apply`
// trampoline variant is WORLDMONITOR-TZ: a wallet extension's
// `injected/hook.js` wraps `window.fetch` and the leaked rejection frame
// surfaces as `Object.apply`, not `window.fetch`.
if (/^(?:TypeError: )?Failed to fetch$/.test(msg)
&& frames.some(f => /^(?:chrome|moz|safari(?:-web)?)-extension:\/\//.test(f.filename ?? '') && /^(?:(?:.*\.)?window\.|(?:window|Object)\.)?(?:fetch|apply)$/i.test(f.function ?? ''))) {
return null;
}
// Bare `Failed to fetch` surfacing through the DebugBear RUM collector's
// window.fetch monkeypatch. DebugBear (src/bootstrap/debugbear-rum.ts β†’
// cdn.debugbear.com/<id>.js; Sentry attributes its frames to the script
// configured script path) wraps window.fetch to time it, so a
// transient network blip on ANY app fetch rejects and its wrapper
// re-surfaces the rejection as an unhandled rejection, injecting its own
// frames. Without DebugBear the identical failure is zero-frame and already
// suppressed above β€” the collector's frames are the ONLY reason it reaches
// here. The `/assets/*.js` frames it carries are `window.fetch` TRAMPOLINES
// (Vite code-split chunk names, e.g. panel-storage/widget-store, which do
// not themselves fetch β€” grep-verified), NOT real callers. Suppress only
// when a DebugBear collector frame is present AND every non-infra frame is
// either that collector or the observed caller-free `window.fetch`/`fetch`
// trampolines from panel-storage/widget-store. Other first-party fetch
// wrappers (notably runtime.ts) must surface. Mirrors the SG
// extension-wrapper gate above; collector identity comes from
// DEBUGBEAR_RUM_SCRIPT_SRC via the shared predicate.
// WORLDMONITOR-VC (93ev/69u, 2026-07-04+).
// The optional `\w{1,3}.` receiver prefix is WORLDMONITOR-VQ: a later Vite
// build emits the same trampoline as `Rt.window.fetch` rather than a bare
// `window.fetch`, and the anchored match rejected it, so the identical
// wrapper class re-surfaced as a new issue. The prefix is bounded to a
// minified identifier (≀3 chars) so a real named receiver β€” e.g.
// `apiClient.fetch` β€” is still read as a genuine caller and surfaces.
if (/^(?:TypeError: )?Failed to fetch$/.test(msg)
&& frames.some(f => isDebugBearRumScriptFrame(f.filename ?? ''))
&& nonInfraFrames.every(f =>
isDebugBearRumScriptFrame(f.filename ?? '')
|| (/\/assets\/(?:panel-storage|widget-store)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? '')
&& /^(?:\w{1,3}\.)?(?:window\.)?fetch$/.test(f.function ?? '')))) {
return null;
}
// Suppress Sentry SDK DOM breadcrumb null-access on document.activeElement/contains.
// Gated on !hasFirstParty because Sentry wraps first-party handlers, so a genuine app `el.contains(...)` bug
// can produce a stack containing both main-*.js and sentry-*.js frames.
if (!hasFirstParty && /Cannot read properties of null \(reading 'contains'\)|null is not an object \(evaluating '\w+\.contains'\)/.test(msg) && frames.some(f => /\/sentry-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
// Suppress Convex WS onmessage JSON.parse truncation (intermittent WS frame splits on Ping/Updated control messages)
if (excType === 'SyntaxError' && /is not valid JSON/.test(msg) && !hasFirstParty && frames.some(f => /onmessage/.test(f.function ?? ''))) return null;
// Suppress errors originating from UV proxy (Ultraviolet service worker)
if (frames.some(f => /\/uv\/service\//.test(f.filename ?? '') || /uv\.handler/.test(f.filename ?? ''))) return null;
// Suppress Greasemonkey/Tampermonkey userscript errors (x-plugin-script, stay-userscript.html)
if (frames.length > 0 && frames.every(f => !f.filename || /\/x-plugin-script\/|\/stay-userscript\.html$/.test(f.filename))) return null;
// Suppress YouTube IFrame widget API internal errors
if (frames.some(f => /www-widgetapi\.js/.test(f.filename ?? ''))) return null;
// Suppress Sentry beacon XHR transport errors (readyState on aborted XHR β€” not our code)
if (frames.some(f => /beacon\.min\.js/.test(f.filename ?? ''))) return null;
// Suppress Fireglass (Symantec/Broadcom CloudSOC) console-hook recursion.
// Fireglass wraps console.log and recurses on its own debug output, producing
// "Maximum call stack size exceeded". Stack frames are <anonymous> so the
// generic hasFirstParty gate below can't see it β€” match by function name.
// Gated on excType === 'RangeError' (mirrors the sortedTrackListForMenu
// pattern above) so an unrelated exception with a FireglassUtils frame
// isn't silently dropped (WORLDMONITOR-MK).
if (excType === 'RangeError' && frames.some(f => /FireglassUtils/.test(f.function ?? ''))) return null;
// Suppress Chrome Mobile WebView 105+ Request constructor quirk ONLY when
// the Dodo checkout lazy chunk is in the stack (WORLDMONITOR-MH). The
// exact message is unique to the Fetch Β§ Request() duplex requirement, but
// src/services/runtime.ts (runtime fetch patch) also constructs `new
// Request(init)` at lines 861/869/902 β€” without this provenance guard the
// same filter would hide a real first-party streaming-fetch regression.
// Guard on the vendored chunk name (checkout-*.js = Dodo SDK, lazy-loaded
// only when startCheckout runs) so a runtime.ts failure still surfaces.
if (/Failed to construct 'Request': The `duplex` member must be specified/.test(msg)
&& frames.some(f => /\/assets\/checkout-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
// Suppress "options is not defined" from browser extension overriding Navigator getter (WORLDMONITOR-JN).
// Only suppress when stack has no first-party frames (filename=<anonymous> is the extension getter).
if (/^options is not defined$/.test(msg) && frames.every(f => !f.filename || f.filename === '<anonymous>' || f.filename === '[native code]')) return null;
// Suppress TransactionInactiveError only when no first-party frames are present
// (Safari kills open IDB transactions in background tabs β€” not actionable noise)
// First-party paths in storage.ts / persistent-cache.ts / vector-db.ts must still surface.
if ((/TransactionInactiveError/.test(msg) || excType === 'TransactionInactiveError') && !hasFirstParty) return null;
// Suppress ambiguous runtime errors ONLY when stack positively identifies third-party
// origin. Empty stacks are NOT suppressed because we cannot confirm the error didn't
// come from our own code (OOM, stack overflow, network failures all commonly arrive
// without frames even when our code triggered them).
// iOS Safari WKWebView throws `UnknownError: Cannot inject key into script value`
// at the native bridge when a non-structurally-cloneable value is passed to a
// bridge API (history.pushState, IndexedDB, etc.). The throw is native; a first-
// party caller is always on the stack, so the generic `!hasFirstParty` gate below
// misses it. Scope to excType==='UnknownError' β€” that type name is WebKit-only and
// cannot originate from our TypeScript (WORLDMONITOR-NM).
if (excType === 'UnknownError' && /Cannot inject key into script value/.test(msg)) return null;
// Convex SDK re-auth race: during a WebSocket reconnect, `BaseConvexClient.
// tryToReauthenticate` can read `this.authState.config.fetchToken` while
// authState is transitioning out of `authenticated` state. Known Convex
// internal; we use the SDK as-is. Gate by the exact function name so we
// don't mask a genuine first-party `fetchToken` regression
// (WORLDMONITOR-NJ).
if (/Cannot read properties of undefined \(reading 'fetchToken'\)/.test(msg)
&& frames.some(f => /tryToReauthenticate/.test(f.function ?? ''))) return null;
// Dynamic-import chunk-load failures whose browser-emitted message names one of
// our own hashed `/assets/*.js` chunks. These FETCH-failure phrasings (Chrome
// `Failed to fetch dynamically imported module: <url>`, Firefox `error loading
// dynamically imported module: <url>`) are deploy-skew (a stale hashed filename
// 404s after a deploy) or a transient network blip β€” never a first-party logic
// bug: our compiled code can't synthesize the string, the URL is one of our
// owned hashed chunks, and the load itself failed (a chunk that fetches
// then throws during evaluation rejects with the underlying error, not
// this wrapper). Unlike the
// zero-frame variant below, the `import()` call site here is first-party
// (MapContainer.initDeck, lazy panel/video loaders), so the rejection rides a
// first-party frame and the `!hasFirstParty` gate misses it (WORLDMONITOR-TN: Map
// chunk, WORLDMONITOR-S1: hls chunk). Match the owned, hashed asset URL in
// the message instead of the stack.
const dynamicImportAssetUrlMatch = msg.match(
/(?:https?:\/\/[^\s'")]+)?\/assets\/[A-Za-z0-9_-]+-[A-Za-z0-9_-]+\.js/i,
);
let isOwnedDynamicImportAssetUrl = false;
if (dynamicImportAssetUrlMatch) {
const assetUrl = dynamicImportAssetUrlMatch[0];
if (assetUrl.startsWith('/')) {
isOwnedDynamicImportAssetUrl = true;
} else {
try {
const host = new URL(assetUrl).hostname;
const currentHost = typeof location !== 'undefined' ? location.hostname : '';
isOwnedDynamicImportAssetUrl = host === 'worldmonitor.app'
|| host.endsWith('.worldmonitor.app')
|| (currentHost.endsWith('.vercel.app') && host === currentHost);
} catch {
isOwnedDynamicImportAssetUrl = false;
}
}
}
if (/(?:Failed to fetch|error loading) dynamically imported module/i.test(msg)
&& isOwnedDynamicImportAssetUrl) return null;
// Stale-chunk-after-deploy: modulepreload / dynamic import failures arrive with no
// stack trace because the browser fires them as synthetic TypeErrors at fetch time,
// not at any first-party call site. The chunk-reload guard auto-reloads the page,
// so the user is unaffected β€” but the Sentry event is still captured. Drop these
// even when frames.length === 0 (WORLDMONITOR-Q / WORLDMONITOR-15). The phrases
// are runtime-emitted only β€” our shipped code cannot synthesize them. Browser
// variants: Chrome/Edge `Failed to fetch dynamically imported module` (no URL /
// modulepreload), Safari `Importing a module script failed.`, Firefox `error
// loading dynamically imported module`. `Importing binding name '<x>' is not
// found.` (Safari) is the module-LINK counterpart: a chunk imports a named export
// a sibling chunk no longer provides after a deploy β€” a built bundle always links
// consistently, so at runtime this is version skew, never a code defect, and it
// throws at link time with zero first-party frames (WORLDMONITOR-TM).
if (
!hasFirstParty
&& /(?:Failed to fetch|error loading) dynamically imported module|Importing a module script failed|Importing binding name '[^']*' is not found/i.test(msg)
) return null;
// Zero-frame async-rejection patterns: AbortSignal.timeout() rejections
// and DOMException(NotSupportedError) bubble up via
// onunhandledrejection without any first-party frames captured (the
// browser fires them from internal infra at the timer boundary). Both
// phrases are runtime-emitted only β€” our shipped code cannot synthesize
// the literal "signal timed out" or DOMException name. Same `!hasFirstParty`
// safety as the dynamic-import block (WORLDMONITOR-66 / WORLDMONITOR-62).
//
// Extensions to the same gate:
// β€’ `out of memory` β€” Firefox via setInterval mechanism, zero frames
// (WORLDMONITOR-KE). Browser-engine signal, not synthesizable by
// our code.
// β€’ `\.(toLowerCase|trim|indexOf|findIndex) is not a function` β€”
// Apple Mail privacy proxy walks DOM with forEach and assumes
// `el.className` is a string, but on SVG elements it's a
// `SVGAnimatedString` (WORLDMONITOR-P2). Frame stack is
// [sentry-chunk, [native code]] which gets fully filtered out of
// `nonInfraFrames` β†’ hasAnyStack=false. The literal " is not a
// function" suffix anchored to those four mutator names is
// unambiguously a third-party prototype-mismatch (our code never
// calls those methods on objects of unknown shape).
// β€’ `Request timeout: /...` β€” third-party Electron wrappers
// (WORLDMONITOR-PW: Electron 39.2.7 polling /api/setIsSelect, an
// endpoint we don't serve). Our own `Request timeout` strings
// don't include a colon-and-path suffix; the format is unique to
// wrapper-injected code.
if (
!hasFirstParty
&& (
/signal timed out/.test(msg)
|| /NotSupportedError/.test(msg)
|| /out of memory/i.test(msg)
|| /\.(?:toLowerCase|trim|indexOf|findIndex) is not a function/.test(msg)
|| /^(?:Error: )?Request timeout: \//.test(msg)
// `^Failed to fetch$` (no host suffix) with zero captured frames =
// background fetch from a service worker / browser extension /
// in-app webview / stale pre-deploy bundle. A first-party fetch
// failing in our shipped code surfaces with at least one
// source-mapped .ts frame on the rejection (the awaiting site).
// The hostname-suffixed variant `Failed to fetch (<host>)` is
// handled above by `isHostScopedFetchFailure` which does its own
// first-party-host allowlist (WORLDMONITOR-KM).
|| /^(?:TypeError: )?Failed to fetch$/.test(msg)
// Safari module-loader abort / streaming-fetch interruption: iOS
// Safari emits `SyntaxError: Unexpected EOF` with zero captured
// frames via `onunhandledrejection` when a dynamic `import()` or
// service-worker-mediated fetch is truncated mid-stream (PWA
// lifecycle transitions, background-tab termination, network blip
// during app boot). Our own `JSON.parse` calls produce
// engine-specific phrasings β€” V8: `Unexpected end of JSON input`;
// Safari: `JSON Parse error: Unexpected EOF` (with prefix) β€” so
// bare `Unexpected EOF` is engine-emitted only. Same `!hasFirstParty`
// safety as the `Failed to fetch` / `signal timed out` blocks above
// (WORLDMONITOR-RF).
|| /^(?:SyntaxError: )?Unexpected EOF$/.test(msg)
// `Unexpected token '<'` with zero captured frames = HTML served
// where JS was expected: a stale hashed chunk after a deploy (the
// SPA index.html fallback starts with `<!DOCTYPE html>`), or a
// captive-portal / proxy / ISP HTML interstitial intercepting a
// `<script>` / dynamic-import fetch. The `<` is dispositive β€” our
// already-parsed, build-time-validated first-party bundle cannot
// emit a parse error on its own source, and a genuine first-party
// SyntaxError carries a source-mapped .ts frame (hasFirstParty β†’
// preserved). The `hasAnyStack`-gated `Unexpected token/keyword`
// gate below misses this zero-frame variant. `(?:SyntaxError: )?`
// mirrors the EOF gate above: some engines embed the type in the
// `value` field (WORLDMONITOR-TY).
|| /^(?:SyntaxError: )?Unexpected token '<'/.test(msg)
// Bare `Unexpected token '<keyword>'` with zero captured frames on ancient
// Android WebView (Chrome 98) β€” injected bridge/extension script or a
// browser-internal parse failure, not our already-parsed bundle. A genuine
// first-party SyntaxError carries a source-mapped .ts frame or an owned
// hashed-chunk URL in the message (handled above). The `hasAnyStack`-gated
// token gate below misses this zero-frame variant (WORLDMONITOR-??:
// Unexpected token 'else' / 'for', 2026-07-18).
|| /^(?:SyntaxError: )?Unexpected token '(?:else|for)'$/.test(msg)
// Firefox's wording for a failed `fetch()` β€” the engine-emitted
// equivalent of Chrome's bare `Failed to fetch` (above) and Safari's
// `Load failed`. Surfaces via `onunhandledrejection` with zero captured
// frames. Same provenance reasoning as the `Failed to fetch` gate
// (WORLDMONITOR-KM): a genuine first-party fetch failure keeps a
// source-mapped .ts frame on the awaiting site (hasFirstParty β†’ NOT
// suppressed, preserved by the first-party-stack test), so a zero-frame
// rejection is a background / service-worker / extension / stale-pre-
// deploy-bundle fetch. The literal phrase is engine-emitted only β€” our
// shipped code never synthesizes it. This aligns the Firefox phrasing
// with the bare `Failed to fetch` handling; the earlier blanket
// "let NetworkError through" caution predated the KM provenance
// refinement (WORLDMONITOR-RK).
|| /^(?:TypeError: )?NetworkError when attempting to fetch resource\.?$/.test(msg)
// `.postMessage` on null with no first-party frame = an in-app webview
// JS bridge / injected extension script posting to a null message
// target (observed on ancient Mobile Safari 13 in-app browsers β€”
// WORLDMONITOR-TE/TF). A genuine first-party `worker.postMessage` /
// iframe-bridge bug keeps a source-mapped .ts frame (hasFirstParty β†’
// preserved), so a no-first-party occurrence is bridge/extension noise.
// This is the WebKit phrasing; the V8 `reading 'postMessage'` variant is
// already suppressed via the ignoreErrors entry above.
|| /null is not an object \(evaluating '[^']*\.postMessage'\)/.test(msg)
)
) return null;
if (hasAnyStack && !hasFirstParty && (
/Maximum call stack size exceeded/.test(msg)
|| /^\w{1,2} is not a (?:function|constructor)/.test(msg)
|| /Cannot add property \w+, object is not extensible/.test(msg)
|| /^TypeError: Internal error$/.test(msg)
|| /^Key not found$/.test(msg)
|| /^Element not found$/.test(msg)
|| /^TypeError: NetworkError/.test(msg)
|| /Could not connect to the server/.test(msg)
|| (excType === 'SyntaxError' && /^Unexpected (?:token|keyword)/.test(msg))
|| /^SyntaxError: Unexpected (?:token|keyword)/.test(msg)
|| /Invalid or unexpected token/.test(msg)
|| /^Operation timed out/.test(msg)
|| /Cannot inject key into script value/.test(msg)
|| /Connection lost while action was in flight/.test(msg)
|| /WEBGLRenderPipeline.*Link error/.test(msg)
)) return null;
// `SyntaxError: Invalid or unexpected token` (and the Unexpected token/keyword/EOF
// family) surfacing THROUGH the deck.gl/maplibre WebGL init path. Our compiled,
// already-parsed bundle cannot emit a JS parse error at the first-party
// `MapContainer.initDeck` call site β€” a runtime SyntaxError here means deck.gl /
// maplibre parsed external content (a Worker script, a `new Function` shader
// builder, or a stale/corrupt lazily-loaded chunk after a deploy). The
// `!hasFirstParty` token-parse gate above misses this because `initDeck` rides the
// stack as the CALLER, not the source. Gate on the presence of a deck-stack /
// maplibre vendor frame so a genuine first-party SyntaxError elsewhere still
// surfaces (WORLDMONITOR-SP).
// `(?:SyntaxError: )?` mirrors the EOF/token gates above (lines 588, 601):
// some engines embed the exception type in the `value` field, so `msg` can be
// either `Invalid or unexpected token` or `SyntaxError: Invalid or unexpected
// token`. Anchoring without the optional prefix would let the prefixed variant
// slip through here despite the first-party `MapContainer` frame (Greptile P2).
if (excType === 'SyntaxError'
&& /^(?:SyntaxError: )?(?:Invalid or unexpected token|Unexpected (?:token|keyword|identifier|EOF|end of script))/.test(msg)
&& frames.some(f => /\/(?:maplibre|deck-stack)-[A-Za-z0-9_-]+\.js/.test(f.filename ?? ''))) return null;
return event;
},
};
}
export async function loadAndInitSentry(): Promise<SentryNs> {
const ns = await import('@sentry/browser');
ns.init(buildSentryInitOptions());
return ns;
}