Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion packages/tanstackstart-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
"./package.json": "./package.json",
".": {
"types": "./build/types/index.types.d.ts",
"workerd": {
"import": "./build/esm/cloudflare/index.js",
"require": "./build/cjs/cloudflare/index.js"
},
"worker": {
"import": "./build/esm/cloudflare/index.js",
"require": "./build/cjs/cloudflare/index.js"
},
"browser": {
"import": "./build/esm/index.client.js",
"require": "./build/cjs/index.client.js"
Expand All @@ -28,6 +36,12 @@
"require": "./build/cjs/index.server.js"
}
},
"./cloudflare": {
"types": "./build/types/cloudflare/index.d.ts",
"import": "./build/esm/cloudflare/index.js",
"require": "./build/cjs/cloudflare/index.js",
"default": "./build/esm/cloudflare/index.js"
},
"./import": {
"import": {
"default": "./build/import-hook.mjs"
Expand Down Expand Up @@ -72,7 +86,7 @@
"build:dev:watch": "yarn build:watch",
"build:transpile:watch": "nodemon --ext ts --watch src scripts/buildRollup.ts",
"build:tarball": "npm pack",
"circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts",
"circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts && madge --circular src/cloudflare/index.ts",
"clean": "rimraf build coverage sentry-tanstackstart-react-*.tgz",
"fix": "eslint . --format stylish --fix",
"lint": "eslint . --format stylish",
Expand Down
8 changes: 7 additions & 1 deletion packages/tanstackstart-react/rollup.npm.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sent
export default [
...makeNPMConfigVariants(
makeBaseNPMConfig({
entrypoints: ['src/index.server.ts', 'src/index.client.ts', 'src/client/index.ts', 'src/server/index.ts'],
entrypoints: [
'src/index.server.ts',
'src/index.client.ts',
'src/client/index.ts',
'src/server/index.ts',
'src/cloudflare/index.ts',
],
}),
),
...makeOtelLoaders('./build', 'sentry-node'),
Expand Down
36 changes: 36 additions & 0 deletions packages/tanstackstart-react/src/cloudflare/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// import/export got a false positive, and affects most of our index barrel files
// can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703
/* eslint-disable import/export */
export * from '../client';
export * from '../common';

export { wrapFetchWithSentry } from '../server/wrapFetchWithSentry';
export { wrapMiddlewaresWithSentry } from '../server/middleware';
export { sentryGlobalRequestMiddleware, sentryGlobalFunctionMiddleware } from '../server/globalMiddleware';

/**
* A passthrough error boundary for the server that doesn't depend on any react. Error boundaries don't catch SSR errors
* so they should simply be a passthrough.
*/
export const ErrorBoundary = (props: React.PropsWithChildren<unknown>): React.ReactNode => {
if (!props.children) {
return null;
}

if (typeof props.children === 'function') {
return (props.children as () => React.ReactNode)();
}

return props.children;
};

/**
* A passthrough error boundary wrapper for the server that doesn't depend on any react. Error boundaries don't catch
* SSR errors so they should simply be a passthrough.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function withErrorBoundary<P extends Record<string, any>>(
WrappedComponent: React.ComponentType<P>,
): React.FC<P> {
return WrappedComponent as React.FC<P>;
}
5 changes: 2 additions & 3 deletions packages/tanstackstart-react/src/server/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { addNonEnumerableProperty } from '@sentry/core';
import type { Span } from '@sentry/node';
import { getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/node';
import type { Span } from '@sentry/core';
import { addNonEnumerableProperty, getActiveSpan, startSpanManual, withActiveSpan } from '@sentry/core';
import type { MiddlewareWrapperOptions, TanStackMiddlewareBase } from '../common/types';
import { getMiddlewareSpanOptions } from './utils';

Expand Down
2 changes: 1 addition & 1 deletion packages/tanstackstart-react/src/server/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { StartSpanOptions } from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';

/**
* Extracts the SHA-256 hash from a server function pathname.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/node';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';
import { extractServerFunctionSha256 } from './utils';

export type ServerEntry = {
Expand Down
60 changes: 60 additions & 0 deletions packages/tanstackstart-react/test/cloudflare/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, it } from 'vitest';
import * as CloudflareExports from '../../src/cloudflare/index';

describe('Cloudflare entrypoint', () => {
describe('exports', () => {
it('exports wrapMiddlewaresWithSentry as a function (server implementation)', () => {
expect(typeof CloudflareExports.wrapMiddlewaresWithSentry).toBe('function');

// The server implementation wraps middlewares with Sentry instrumentation.
// Verify it handles an empty object correctly (server impl returns an empty array).
const result = CloudflareExports.wrapMiddlewaresWithSentry({});
expect(result).toEqual([]);
});

it('exports sentryGlobalRequestMiddleware as a middleware object with server handler', () => {
const middleware = CloudflareExports.sentryGlobalRequestMiddleware;
expect(middleware).toBeDefined();
// The server implementation has a server handler function, unlike the client no-op stub.
expect(typeof middleware.options.server).toBe('function');
});

it('exports sentryGlobalFunctionMiddleware as a middleware object with server handler', () => {
const middleware = CloudflareExports.sentryGlobalFunctionMiddleware;
expect(middleware).toBeDefined();
// The server implementation has a server handler function, unlike the client no-op stub.
expect(typeof middleware.options.server).toBe('function');
});

it('exports wrapFetchWithSentry', () => {
expect(typeof CloudflareExports.wrapFetchWithSentry).toBe('function');
});
});

describe('ErrorBoundary', () => {
it('is a passthrough that returns children directly', () => {
const children = 'test child';
const result = CloudflareExports.ErrorBoundary({ children });
expect(result).toBe('test child');
});

it('returns null when no children are provided', () => {
const result = CloudflareExports.ErrorBoundary({});
expect(result).toBeNull();
});

it('calls children when children is a function', () => {
const children = () => 'function result';
const result = CloudflareExports.ErrorBoundary({ children });
expect(result).toBe('function result');
});
});

describe('withErrorBoundary', () => {
it('is a passthrough that returns the original component', () => {
const MockComponent = () => null;
const result = CloudflareExports.withErrorBoundary(MockComponent);
expect(result).toBe(MockComponent);
});
});
});