@sentry/remix
Version:
Official Sentry SDK for Remix
157 lines (153 loc) • 6.06 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const diagnosticsChannel = require('node:diagnostics_channel');
const core = require('@sentry/core');
const config = require('@sentry/server-utils/orchestrion/config');
const debugId = require('./debugId.js');
const orchestrionLoader = require('./orchestrionLoader.js');
const SOURCE_MAPPING_URL_REGEX = /\n\/\/# sourceMappingURL=\S+\s*$/;
const MAX_STAMPED_BODIES = 2e3;
const SDK_PACKAGE = "@sentry/remix";
let instrumented = false;
function instrumentAssetServer() {
if (instrumented) {
return;
}
instrumented = true;
diagnosticsChannel.tracingChannel(config.remixV3Channels.REMIX_V3_CREATE_ASSET_SERVER).subscribe({
start(data) {
try {
const context = data;
const options = context.arguments[0];
context._sentryHideSourceMaps = options?.sourceMaps === void 0;
context.arguments[0] = withDebugIdOptions(options);
} catch {
}
},
end(data) {
try {
const { result, _sentryHideSourceMaps } = data;
if (result) {
stampServedAssets(result, { hideSourceMaps: Boolean(_sentryHideSourceMaps) });
}
} catch {
}
},
asyncStart() {
},
asyncEnd() {
},
error() {
}
});
}
const debugIdLoader = (url, context, nextLoad) => {
const result = nextLoad(url, context);
if (result.format !== "module" || typeof result.source !== "string") {
return result;
}
const debugId$1 = debugId.getDebugId(`${context.moduleUrl ?? url}
${result.source}`);
return { ...result, source: debugId.injectDebugIdSnippet(result.source, debugId$1) };
};
function withDebugIdOptions(options) {
if (!options) {
return options;
}
if (options.sourceMaps === false) {
core.consoleSandbox(() => {
console.warn(
"[Sentry] Source maps are disabled in your asset server (`sourceMaps: false`). Sentry will not override this, so client stack traces stay minified."
);
});
}
const servesPackages = (options.allowPackages?.length ?? 0) > 0;
const allowPackages = servesPackages && !options.allowPackages?.includes(SDK_PACKAGE) ? [...options.allowPackages ?? [], SDK_PACKAGE] : options.allowPackages;
const shimUrl = servesPackages ? orchestrionLoader.getShimUrl(options.basePath ?? "/", options.rootDir ?? process.cwd(), options.mounts, orchestrionLoader.resolveShimPath()) : void 0;
if (servesPackages && shimUrl === void 0) {
core.consoleSandbox(() => {
console.warn(
"[Sentry] The browser channel shim is outside the asset server's node_modules mount, so browser modules are served without instrumentation. Component render errors need `captureRuntimeErrors(app)`."
);
});
}
const loaders = (options.scripts?.loaders ?? []).filter(
(loader) => loader !== debugIdLoader && !orchestrionLoader.isOrchestrionLoader(loader)
);
const external = (options.scripts?.external ?? []).filter((entry) => entry !== shimUrl);
return {
...options,
...allowPackages !== options.allowPackages && { allowPackages },
// Hidden unless the app chose otherwise, see `stampServedAssets`.
sourceMaps: options.sourceMaps ?? "external",
scripts: {
...options.scripts,
// The transform first, the debug ID last, so the ID covers the transformed module.
loaders: shimUrl === void 0 ? [...loaders, debugIdLoader] : [...loaders, orchestrionLoader.orchestrionLoader(shimUrl), debugIdLoader],
// Kept as a URL by the compiler. A bare specifier would not resolve from inside an instrumented
// package under pnpm.
external: shimUrl === void 0 ? external : [shimUrl, ...external]
}
};
}
const UNSTAMPED_FETCH = /* @__PURE__ */ Symbol.for("sentry.remix.unstampedFetch");
function stampServedAssets(server, { hideSourceMaps }) {
const fetchAsset = server.fetch;
server[UNSTAMPED_FETCH] = fetchAsset;
const stamped = /* @__PURE__ */ new Map();
server.fetch = async (request) => {
const response = await fetchAsset(request);
if (response?.status !== 200 || request.method !== "GET") {
return response;
}
const url = new URL(request.url);
const contentType = response.headers.get("content-type") ?? "";
const etag = response.headers.get("etag");
if (contentType.includes("javascript")) {
return withBody(response, await remember(etag, async () => stampModule(await response.text())));
}
if (url.pathname.endsWith(".map")) {
if (hideSourceMaps) {
return null;
}
const body = await remember(etag, async () => {
url.pathname = url.pathname.slice(0, -".map".length);
const moduleResponse = await fetchAsset(new Request(url));
const debugId$1 = moduleResponse?.ok ? debugId.findDebugId(await moduleResponse.text()) : void 0;
const map = await response.text();
return debugId$1 ? debugId.addDebugIdToSourceMap(map, debugId$1) : map;
});
return withBody(response, body);
}
return response;
};
function stampModule(served) {
const code = hideSourceMaps ? served.replace(SOURCE_MAPPING_URL_REGEX, "") : served;
const debugId$1 = debugId.findDebugId(code);
return debugId$1 ? `${code}
//# debugId=${debugId$1}` : code;
}
async function remember(etag, compute) {
if (etag === null) {
return compute();
}
const cached = stamped.get(etag);
if (cached !== void 0) {
return cached;
}
const body = await compute();
if (stamped.size >= MAX_STAMPED_BODIES) {
stamped.delete(stamped.keys().next().value);
}
stamped.set(etag, body);
return body;
}
}
function withBody(response, body) {
const headers = new Headers(response.headers);
headers.delete("content-length");
return new Response(body, { status: response.status, statusText: response.statusText, headers });
}
exports.UNSTAMPED_FETCH = UNSTAMPED_FETCH;
exports.debugIdLoader = debugIdLoader;
exports.instrumentAssetServer = instrumentAssetServer;
//# sourceMappingURL=assetServer.js.map