Skip to content
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ module.exports = [
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: false,
brotli: false,
limit: '500 KiB',
limit: '502 KiB',
disablePlugins: ['@size-limit/webpack'],
webpack: false,
modifyEsbuildConfig: function (config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ sentryTest(
expect(spans).toEqual([
{
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: {
type: 'string',
value: 'test-child',
Expand Down Expand Up @@ -124,6 +125,7 @@ sentryTest(
},
{
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: {
type: 'string',
value: 'manual',
Expand Down Expand Up @@ -168,6 +170,7 @@ sentryTest(
},
{
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: {
type: 'string',
value: 'manual',
Expand Down Expand Up @@ -216,6 +219,7 @@ sentryTest(
},
{
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'culture.calendar': {
type: 'string',
value: expect.any(String),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow

expect(navigationSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: {
type: 'string',
value: 'stream',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ sentryTest(

expect(pageloadSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'culture.calendar': {
type: 'string',
value: expect.any(String),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser

expect(interactionSegmentSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: {
type: 'string',
value: 'stream',
Expand Down Expand Up @@ -127,6 +128,7 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser
const interactionSpan = interactionSpanTree.find(span => getSpanOp(span) === 'ui.interaction.click');
expect(interactionSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: {
type: 'string',
value: 'stream',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 1,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Sentry.startSpan({ name: 'child-span' }, () => {
// noop
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import fs from 'fs';
import path from 'path';
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { shouldSkipTracingTest } from '../../../utils/helpers';
import { waitForStreamedSpans } from '../../../utils/spanUtils';

sentryTest('is false when the page is not served from localhost', async ({ getLocalTestUrl, page }) => {
sentryTest.skip(shouldSkipTracingTest());

const url = await getLocalTestUrl({ testDir: __dirname });

const spansPromise = waitForStreamedSpans(page, spans => spans.some(span => span.name === 'child-span'));

await page.goto(url);

const spans = await spansPromise;

expect(spans.length).toBeGreaterThan(1);
for (const span of spans) {
expect(span.attributes['sentry.is_localhost']).toEqual({ type: 'boolean', value: false });
}
});

sentryTest('is true when the page is served from localhost', async ({ getLocalTestUrl, page }) => {
sentryTest.skip(shouldSkipTracingTest());

// `getLocalTestUrl` serves from `http://sentry-test.io`, so the routes are wired up by hand here
// to serve the very same page from `localhost` instead.
const tmpDir = await getLocalTestUrl({ testDir: __dirname, skipRouteHandler: true });

await page.route(/^https:\/\/dsn\.ingest\.sentry\.io\//, route =>
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'test-id' }) }),
);

await page.route('http://localhost/*.*', route => {
const file = route.request().url().split('/').pop();
const filePath = path.resolve(tmpDir, `./${file}`);

return fs.existsSync(filePath) ? route.fulfill({ path: filePath }) : route.continue();
});

const spansPromise = waitForStreamedSpans(page, spans => spans.some(span => span.name === 'child-span'));

await page.goto('http://localhost/index.html');

const spans = await spansPromise;

expect(spans.length).toBeGreaterThan(1);
for (const span of spans) {
expect(span.attributes['sentry.is_localhost']).toEqual({ type: 'boolean', value: true });
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down Expand Up @@ -159,6 +160,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down Expand Up @@ -144,6 +145,7 @@ sentryTest(
is_segment: false,
status: 'ok',
attributes: {
'sentry.is_localhost': { value: false, type: 'boolean' },
'sentry.origin': { value: 'auto.http.browser.inp', type: 'string' },
'sentry.op': { value: 'ui.interaction.click', type: 'string' },
'sentry.exclusive_time': { value: inpValue, type: expect.stringMatching(/^(integer)|(double)$/) },
Expand Down
18 changes: 18 additions & 0 deletions dev-packages/bun-integration-tests/suites/is-localhost/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Sentry from '@sentry/bun';

Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});

const server = Bun.serve({
port: 0,
fetch() {
Sentry.startSpan({ name: 'child-span' }, () => {
// noop
});
return new Response('Hello from Bun!');
},
});

process.send?.(JSON.stringify({ event: 'READY', port: server.port }));
27 changes: 27 additions & 0 deletions dev-packages/bun-integration-tests/suites/is-localhost/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Envelope, SerializedStreamedSpan, SerializedStreamedSpanContainer } from '@sentry/core';
import { expect, it } from 'vitest';
import { createRunner } from '../../runner';

function getSpans(envelope: Envelope): SerializedStreamedSpan[] {
return (envelope[1][0][1] as SerializedStreamedSpanContainer).items;
}

// The runner always requests `http://localhost:<port>`, so only the `true` case is reachable here.
// The `false` case is covered by the unit tests for `isLocalhostRequest`.
it('sets sentry.is_localhost on every streamed span', async ({ signal }) => {
const runner = createRunner(__dirname)
.expect(envelope => {
const spans = getSpans(envelope);

expect(spans.some(span => span.is_segment)).toBe(true);
expect(spans.some(span => span.name === 'child-span')).toBe(true);

for (const span of spans) {
expect(span.attributes['sentry.is_localhost']).toEqual({ value: true, type: 'boolean' });
}
})
.start(signal);

await runner.makeRequest('get', '/');
await runner.completed();
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa
expect(childSpan).toBeDefined();
expect(childSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' },
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: {
type: 'string',
Expand Down Expand Up @@ -107,6 +108,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa
expect(inactiveSpan).toBeDefined();
expect(inactiveSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' },
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'manual' },
[SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK },
Expand Down Expand Up @@ -143,6 +145,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa
expect(manualSpan).toBeDefined();
expect(manualSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' },
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: { type: 'string', value: 'manual' },
[SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK },
Expand All @@ -164,6 +167,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa

expect(parentTestSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' },
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: { type: 'string', value: 'test' },
[SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK },
Expand All @@ -186,6 +190,7 @@ it('sends a streamed span envelope with correct spans for a manually started spa

expect(segmentSpan).toEqual({
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
[SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream' },
[SENTRY_SDK_NAME]: { type: 'string', value: CLOUDFLARE_SDK },
[SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ describe('with span streaming enabled', () => {
{
name: 'CREATE TABLE users',
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
...commonAttributes(segmentSpan),
'db.operation.name': { type: 'string', value: 'exec' },
'db.query.text': {
Expand All @@ -187,6 +188,7 @@ describe('with span streaming enabled', () => {
{
name: 'INSERT users',
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
...commonAttributes(segmentSpan),
'db.operation.name': { type: 'string', value: 'run' },
'db.query.text': { type: 'string', value: 'INSERT INTO users (name) VALUES (?)' },
Expand All @@ -208,6 +210,7 @@ describe('with span streaming enabled', () => {
{
name: 'SELECT users',
attributes: {
'sentry.is_localhost': { value: true, type: 'boolean' },
...commonAttributes(segmentSpan),
'db.operation.name': { type: 'string', value: 'first' },
'db.query.text': { type: 'string', value: 'SELECT * FROM users WHERE name = ?' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as Sentry from '@sentry/cloudflare';

interface Env {
SENTRY_DSN: string;
}

export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1,
}),
{
async fetch(_request, _env, _ctx) {
Sentry.startSpan({ name: 'child-span' }, () => {
// noop
});
return Response.json({ status: 'ok' });
},
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, it } from 'vitest';
import { createRunner } from '../../../runner';

it('sets sentry.is_localhost on every streamed span', async ({ signal }) => {
const runner = createRunner(__dirname)
.expect(envelope => {
const items = envelope[1].filter(item => item[0].type === 'span').flatMap(item => (item[1] as any).items);

expect(items.length).toBeGreaterThan(0);
expect(items.some((s: any) => s.is_segment)).toBe(true);
expect(items.some((s: any) => s.name === 'child-span')).toBe(true);

for (const span of items) {
expect(span.attributes['sentry.is_localhost']).toEqual({ type: 'boolean', value: true });
}
})
.start(signal);

await runner.makeRequest('get', '/');
await runner.completed();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "is-localhost",
"compatibility_date": "2025-06-17",
"main": "index.ts",
"compatibility_flags": ["nodejs_compat"],
}
Loading
Loading