diff --git a/.craft.yml b/.craft.yml index d69334cd3f94..dd50cb686c79 100644 --- a/.craft.yml +++ b/.craft.yml @@ -108,6 +108,9 @@ targets: - name: npm id: '@sentry/sveltekit' includeNames: /^sentry-sveltekit-\d.*\.tgz$/ + - name: npm + id: '@sentry/tanstackstart' + includeNames: /^sentry-tanstackstart-\d.*\.tgz$/ - name: npm id: '@sentry/gatsby' includeNames: /^sentry-gatsby-\d.*\.tgz$/ diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml index cbb73201eebf..af7d62521c59 100644 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml @@ -170,6 +170,12 @@ packages: unpublish: $all # proxy: npmjs # Don't proxy for E2E tests! + '@sentry/tanstackstart': + access: $all + publish: $all + unpublish: $all + # proxy: npmjs # Don't proxy for E2E tests! + '@sentry/types': access: $all publish: $all diff --git a/package.json b/package.json index 6127b9c5c461..460d2c42abb1 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "packages/solidstart", "packages/svelte", "packages/sveltekit", + "packages/tanstackstart", "packages/types", "packages/typescript", "packages/vercel-edge", diff --git a/packages/tanstackstart/.eslintrc.js b/packages/tanstackstart/.eslintrc.js new file mode 100644 index 000000000000..54e8382b22a8 --- /dev/null +++ b/packages/tanstackstart/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + env: { + browser: true, + node: true, + }, + parserOptions: { + jsx: true, + }, + extends: ['../../.eslintrc.js'], +}; diff --git a/packages/tanstackstart/LICENSE b/packages/tanstackstart/LICENSE new file mode 100644 index 000000000000..0da96cd2f885 --- /dev/null +++ b/packages/tanstackstart/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Functional Software, Inc. dba Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/tanstackstart/README.md b/packages/tanstackstart/README.md new file mode 100644 index 000000000000..35e369cf25e9 --- /dev/null +++ b/packages/tanstackstart/README.md @@ -0,0 +1,52 @@ +

+ + Sentry + +

+ +# Official Sentry SDK for TanStack Start (Alpha) + +[![npm version](https://img.shields.io/npm/v/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) +[![npm dm](https://img.shields.io/npm/dm/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) +[![npm dt](https://img.shields.io/npm/dt/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) + +> NOTICE: This package is in alpha state and may be subject to breaking changes. + +## Getting Started + +This SDK does not have docs yet. Stay tuned. + +## Compatibility + +The minimum supported version of TanStack Start is `1.111.12`. + +## Custom Usage + +To set context information or to send manual events, you can use `@sentry/tanstackstart` as follows: + +```ts +import * as Sentry from '@sentry/tanstackstart'; + +// Set user information, as well as tags and further extras +Sentry.setTag('user_mode', 'admin'); +Sentry.setUser({ id: '4711' }); +Sentry.setContext('application_area', { location: 'checkout' }); + +// Add a breadcrumb for future events +Sentry.addBreadcrumb({ + message: '"Add to cart" clicked', + // ... +}); + +// Capture exceptions or messages +Sentry.captureException(new Error('Oh no.')); +Sentry.captureMessage('Hello, world!'); +``` + +## Links + + + +- [Sentry.io](https://sentry.io/?utm_source=github&utm_medium=npm_tanstackstart) +- [Sentry Discord Server](https://discord.gg/Ww9hbqr) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/sentry) diff --git a/packages/tanstackstart/package.json b/packages/tanstackstart/package.json new file mode 100644 index 000000000000..63a7916ff3dd --- /dev/null +++ b/packages/tanstackstart/package.json @@ -0,0 +1,84 @@ +{ + "name": "@sentry/tanstackstart", + "version": "9.2.0", + "description": "Official Sentry SDK for TanStack Start", + "repository": "git://github.com/getsentry/sentry-javascript.git", + "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tanstackstart", + "author": "Sentry", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "main": "build/cjs/index.server.js", + "module": "build/esm/index.server.js", + "types": "build/types/index.types.d.ts", + "files": [ + "/build" + ], + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./build/types/index.types.d.ts", + "browser": { + "import": "./build/esm/index.client.js", + "require": "./build/cjs/index.client.js" + }, + "node": "./build/cjs/index.server.js", + "import": "./build/esm/index.server.js" + }, + "./import": { + "import": { + "default": "./build/import-hook.mjs" + } + }, + "./loader": { + "import": { + "default": "./build/loader-hook.mjs" + } + } + }, + "typesVersions": { + "<5.0": { + "build/npm/types/index.d.ts": [ + "build/npm/types-ts3.8/index.d.ts" + ] + } + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.30.0", + "@sentry-internal/browser-utils": "9.2.0", + "@sentry/core": "9.2.0", + "@sentry/node": "9.2.0", + "@sentry/opentelemetry": "9.2.0", + "@sentry/react": "9.2.0" + }, + "scripts": { + "build": "run-p build:transpile build:types", + "build:dev": "yarn build", + "build:transpile": "rollup -c rollup.npm.config.mjs", + "build:types": "run-s build:types:core build:types:downlevel", + "build:types:core": "tsc -p tsconfig.types.json", + "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8", + "build:watch": "run-p build:transpile:watch build:types:watch", + "build:dev:watch": "yarn build:watch", + "build:transpile:watch": "nodemon --ext ts --watch src scripts/buildRollup.ts", + "build:types:watch": "tsc -p tsconfig.types.json --watch", + "build:tarball": "npm pack", + "circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts", + "clean": "rimraf build coverage sentry-tanstackstart-*.tgz", + "fix": "eslint . --format stylish --fix", + "lint": "eslint . --format stylish", + "test": "yarn test:unit", + "test:unit": "vitest run", + "test:watch": "vitest --watch", + "yalc:publish": "yalc publish --push --sig" + }, + "volta": { + "extends": "../../package.json" + }, + "sideEffects": false +} diff --git a/packages/tanstackstart/rollup.npm.config.mjs b/packages/tanstackstart/rollup.npm.config.mjs new file mode 100644 index 000000000000..9b334bdbae41 --- /dev/null +++ b/packages/tanstackstart/rollup.npm.config.mjs @@ -0,0 +1,16 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sentry-internal/rollup-utils'; + +export default [ + ...makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: [ + 'src/index.server.ts', + 'src/index.client.ts', + 'src/client/index.ts', + 'src/server/index.ts', + 'src/config/index.ts', + ], + }), + ), + ...makeOtelLoaders('./build', 'sentry-node'), +]; diff --git a/packages/tanstackstart/src/client/index.ts b/packages/tanstackstart/src/client/index.ts new file mode 100644 index 000000000000..c45aad673ad0 --- /dev/null +++ b/packages/tanstackstart/src/client/index.ts @@ -0,0 +1 @@ +export * from '@sentry/react'; diff --git a/packages/tanstackstart/src/config/index.ts b/packages/tanstackstart/src/config/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/tanstackstart/src/config/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/tanstackstart/src/index.client.ts b/packages/tanstackstart/src/index.client.ts new file mode 100644 index 000000000000..4f1cce44fa36 --- /dev/null +++ b/packages/tanstackstart/src/index.client.ts @@ -0,0 +1 @@ +export * from './client'; diff --git a/packages/tanstackstart/src/index.server.ts b/packages/tanstackstart/src/index.server.ts new file mode 100644 index 000000000000..d08940e2ac5d --- /dev/null +++ b/packages/tanstackstart/src/index.server.ts @@ -0,0 +1,43 @@ +export * from './config'; +export * from './server'; + +/** + * 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): React.ReactNode => { + if (!props.children) { + return null; + } + + if (typeof props.children === 'function') { + return (props.children as () => React.ReactNode)(); + } + + return props.children; +}; + +/** + * A passthrough redux enhancer for the server that doesn't depend on anything from the `@sentry/react` package. + */ +export function createReduxEnhancer() { + return (createStore: unknown) => createStore; +} + +/** + * 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

>( + WrappedComponent: React.ComponentType

, +): React.FC

{ + return WrappedComponent as React.FC

; +} + +/** + * Just a passthrough since we're on the server and showing the report dialog on the server doesn't make any sense. + */ +export function showReportDialog(): void { + return; +} diff --git a/packages/tanstackstart/src/index.types.ts b/packages/tanstackstart/src/index.types.ts new file mode 100644 index 000000000000..ede1b27f0b3b --- /dev/null +++ b/packages/tanstackstart/src/index.types.ts @@ -0,0 +1,26 @@ +// We export everything from both the client part of the SDK and from the server part. Some of the exports collide, +// which is not allowed, unless we redefine the colliding exports in this file - which we do below. +export * from './config'; +export * from './client'; +export * from './server'; + +import type { Client, Integration, Options, StackParser } from '@sentry/core'; + +import type * as clientSdk from './client'; +import type * as serverSdk from './server'; + +/** Initializes Sentry TanStack Start SDK */ +export declare function init(options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions): Client | undefined; + +export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; +export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; + +export declare const getDefaultIntegrations: (options: Options) => Integration[]; +export declare const defaultStackParser: StackParser; + +export declare function getSentryRelease(fallback?: string): string | undefined; + +export declare const ErrorBoundary: typeof clientSdk.ErrorBoundary; +export declare const createReduxEnhancer: typeof clientSdk.createReduxEnhancer; +export declare const showReportDialog: typeof clientSdk.showReportDialog; +export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary; diff --git a/packages/tanstackstart/src/server/index.ts b/packages/tanstackstart/src/server/index.ts new file mode 100644 index 000000000000..d61c75b7bfb4 --- /dev/null +++ b/packages/tanstackstart/src/server/index.ts @@ -0,0 +1 @@ +export * from '@sentry/node'; diff --git a/packages/tanstackstart/test/temp.test.ts b/packages/tanstackstart/test/temp.test.ts new file mode 100644 index 000000000000..28874b0d1c84 --- /dev/null +++ b/packages/tanstackstart/test/temp.test.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest'; + +describe('Basic test suite', () => { + it('should pass', () => { + expect(true).toBe(true); + }); +}); diff --git a/packages/tanstackstart/test/tsconfig.json b/packages/tanstackstart/test/tsconfig.json new file mode 100644 index 000000000000..38ca0b13bcdd --- /dev/null +++ b/packages/tanstackstart/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.test.json" +} diff --git a/packages/tanstackstart/tsconfig.json b/packages/tanstackstart/tsconfig.json new file mode 100644 index 000000000000..20cf507e5203 --- /dev/null +++ b/packages/tanstackstart/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "lib": ["es2018", "es2020.string"], + "module": "Node16" + } +} diff --git a/packages/tanstackstart/tsconfig.test.json b/packages/tanstackstart/tsconfig.test.json new file mode 100644 index 000000000000..bbbebba51d18 --- /dev/null +++ b/packages/tanstackstart/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["test/**/*", "vite.config.ts"], + "compilerOptions": { + "types": ["node"], + "lib": ["DOM", "ESNext"] + } +} diff --git a/packages/tanstackstart/tsconfig.types.json b/packages/tanstackstart/tsconfig.types.json new file mode 100644 index 000000000000..b1a51db073c2 --- /dev/null +++ b/packages/tanstackstart/tsconfig.types.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/types" + } +} diff --git a/packages/tanstackstart/vite.config.ts b/packages/tanstackstart/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/tanstackstart/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +};