diff --git a/apps/web/.env.production b/apps/web/.env.production index 6c50e0284..41b5ebb3d 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -44,4 +44,8 @@ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= NEXT_PUBLIC_ENABLE_ACCOUNT_DELETION=true NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_BILLING=true NEXT_PUBLIC_ENABLE_ORGANIZATION_DELETION=true -NEXT_PUBLIC_ENABLE_ORGANIZATION_BILLING=true \ No newline at end of file +NEXT_PUBLIC_ENABLE_ORGANIZATION_BILLING=true + +# MONITORING +MONITORING_PROVIDER= +MONITORING_INSTRUMENTATION_ENABLED=false diff --git a/apps/web/app/(dashboard)/home/[account]/billing/error.tsx b/apps/web/app/(dashboard)/home/[account]/billing/error.tsx index 509bbdb22..5eb160524 100644 --- a/apps/web/app/(dashboard)/home/[account]/billing/error.tsx +++ b/apps/web/app/(dashboard)/home/[account]/billing/error.tsx @@ -1,16 +1,21 @@ 'use client'; -import { useRouter } from 'next/navigation'; - import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; +import { useCaptureException } from '@kit/monitoring/hooks'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { Button } from '@kit/ui/button'; import { PageBody, PageHeader } from '@kit/ui/page'; import { Trans } from '@kit/ui/trans'; -export default function BillingErrorPage() { - const router = useRouter(); +export default function BillingErrorPage({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useCaptureException(error); return ( <> @@ -34,7 +39,7 @@ export default function BillingErrorPage() {
-
diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index a3e84ac15..57ea2a3d5 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -4,13 +4,22 @@ import Link from 'next/link'; import { ArrowLeft } from 'lucide-react'; +import { useCaptureException } from '@kit/monitoring/hooks'; import { Button } from '@kit/ui/button'; import { Heading } from '@kit/ui/heading'; import { Trans } from '@kit/ui/trans'; import { SiteHeader } from '~/(marketing)/_components/site-header'; -const ErrorPage = () => { +const ErrorPage = ({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) => { + useCaptureException(error); + return (
@@ -39,13 +48,11 @@ const ErrorPage = () => {
- - - + +
diff --git a/apps/web/instrumentation.ts b/apps/web/instrumentation.ts index cc4b7aeec..9c4237a0e 100644 --- a/apps/web/instrumentation.ts +++ b/apps/web/instrumentation.ts @@ -2,11 +2,17 @@ * This file is used to register monitoring instrumentation * for your Next.js application. */ + +const RUNTIME = process.env.NEXT_RUNTIME; + +const ENABLE_INSTRUMENTATION = + process.env.MONITORING_INSTRUMENTATION_ENABLED === 'true'; + export async function register() { // only run in nodejs runtime - if (process.env.NEXT_RUNTIME === 'nodejs') { + if (RUNTIME === 'nodejs' && ENABLE_INSTRUMENTATION) { const { registerMonitoringInstrumentation } = await import( - '@kit/monitoring' + '@kit/monitoring/instrumentation' ); // Register monitoring instrumentation based on the diff --git a/apps/web/public/locales/en/common.json b/apps/web/public/locales/en/common.json index d0c75538f..bcf564bb9 100644 --- a/apps/web/public/locales/en/common.json +++ b/apps/web/public/locales/en/common.json @@ -21,6 +21,7 @@ "clear": "Clear", "notFound": "Not Found", "backToHomePage": "Back to Home Page", + "goBack": "Go Back", "genericServerError": "Sorry, something went wrong.", "genericServerErrorHeading": "Sorry, something went wrong while processing your request. Please contact us if the issue persists.", "pageNotFound": "Sorry, this page does not exist.", diff --git a/packages/features/accounts/package.json b/packages/features/accounts/package.json index 298b5e8be..f79eb62fa 100644 --- a/packages/features/accounts/package.json +++ b/packages/features/accounts/package.json @@ -23,6 +23,7 @@ "@kit/email-templates": "workspace:^", "@kit/eslint-config": "workspace:*", "@kit/mailers": "workspace:^", + "@kit/monitoring": "workspace:^", "@kit/prettier-config": "workspace:*", "@kit/shared": "workspace:^", "@kit/supabase": "workspace:^", diff --git a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx index f8a0192d8..3c5177a4e 100644 --- a/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx +++ b/packages/features/accounts/src/components/personal-account-settings/account-danger-zone.tsx @@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; import { useForm } from 'react-hook-form'; +import { ErrorBoundary } from '@kit/monitoring/components'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { AlertDialog, @@ -17,7 +18,6 @@ import { AlertDialogTrigger, } from '@kit/ui/alert-dialog'; import { Button } from '@kit/ui/button'; -import { ErrorBoundary } from '@kit/ui/error-boundary'; import { Form, FormControl, FormItem, FormLabel } from '@kit/ui/form'; import { Input } from '@kit/ui/input'; import { Trans } from '@kit/ui/trans'; diff --git a/packages/features/team-accounts/package.json b/packages/features/team-accounts/package.json index 53b7ea95f..c8118b0ca 100644 --- a/packages/features/team-accounts/package.json +++ b/packages/features/team-accounts/package.json @@ -22,6 +22,7 @@ "@kit/email-templates": "workspace:^", "@kit/eslint-config": "workspace:*", "@kit/mailers": "workspace:^", + "@kit/monitoring": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/shared": "workspace:^", "@kit/supabase": "workspace:^", diff --git a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx index d036c9f33..f4c8fcee4 100644 --- a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx +++ b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx @@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { ErrorBoundary } from '@kit/monitoring/components'; import { useUser } from '@kit/supabase/hooks/use-user'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { @@ -19,7 +20,6 @@ import { AlertDialogTrigger, } from '@kit/ui/alert-dialog'; import { Button } from '@kit/ui/button'; -import { ErrorBoundary } from '@kit/ui/error-boundary'; import { Form, FormControl, diff --git a/packages/monitoring/baselime/package.json b/packages/monitoring/baselime/package.json index c581fa704..06ce9abc3 100644 --- a/packages/monitoring/baselime/package.json +++ b/packages/monitoring/baselime/package.json @@ -10,10 +10,13 @@ }, "prettier": "@kit/prettier-config", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", + "./provider": "./src/components/provider.tsx" }, "dependencies": { - "@baselime/node-opentelemetry": "^0.5.8" + "@baselime/node-opentelemetry": "^0.5.8", + "@baselime/react-rum": "^0.2.9" }, "devDependencies": { "@kit/eslint-config": "workspace:*", diff --git a/packages/monitoring/baselime/src/capture-exception.ts b/packages/monitoring/baselime/src/capture-exception.ts new file mode 100644 index 000000000..c9e1114dc --- /dev/null +++ b/packages/monitoring/baselime/src/capture-exception.ts @@ -0,0 +1,4 @@ +export function captureException(error: Error) { + console.info(`No yet defined...`); + return console.error(`Caught exception: ${JSON.stringify(error)}`); +} diff --git a/packages/monitoring/baselime/src/components/provider.tsx b/packages/monitoring/baselime/src/components/provider.tsx new file mode 100644 index 000000000..a78ff5d39 --- /dev/null +++ b/packages/monitoring/baselime/src/components/provider.tsx @@ -0,0 +1,22 @@ +import { BaselimeRum } from '@baselime/react-rum'; + +export function BaselineProvider({ + children, + apiKey, + enableWebVitals, + ErrorPage, +}: React.PropsWithChildren<{ + apiKey: string; + enableWebVitals?: boolean; + ErrorPage?: React.ReactElement; +}>) { + return ( + + {children} + + ); +} diff --git a/packages/monitoring/baselime/src/hooks/use-capture-exception.ts b/packages/monitoring/baselime/src/hooks/use-capture-exception.ts new file mode 100644 index 000000000..29bacd09d --- /dev/null +++ b/packages/monitoring/baselime/src/hooks/use-capture-exception.ts @@ -0,0 +1,12 @@ +import { useCallback } from 'react'; + +import { useBaselimeRum } from '@baselime/react-rum'; + +export function useCaptureException() { + const { captureException } = useBaselimeRum(); + + return useCallback( + (error: Error) => captureException(error), + [captureException], + ); +} diff --git a/packages/monitoring/baselime/src/index.ts b/packages/monitoring/baselime/src/index.ts index 590437486..c258b27a8 100644 --- a/packages/monitoring/baselime/src/index.ts +++ b/packages/monitoring/baselime/src/index.ts @@ -1,31 +1 @@ -const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; - -if (!INSTRUMENTATION_SERVICE_NAME) { - throw new Error(` - You have set the Baselime instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. - `); -} - -/** - * @name registerBaselimeInstrumentation - * @description This file is used to register Baselime instrumentation for your Next.js application. - * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'baselime' to register Baselime instrumentation. - */ -export async function registerBaselimeInstrumentation() { - const { BaselimeSDK, BetterHttpInstrumentation, VercelPlugin } = await import( - '@baselime/node-opentelemetry' - ); - - const sdk = new BaselimeSDK({ - serverless: true, - service: INSTRUMENTATION_SERVICE_NAME, - instrumentations: [ - new BetterHttpInstrumentation({ - plugins: [new VercelPlugin()], - }), - ], - }); - - sdk.start(); -} +export * from './capture-exception'; diff --git a/packages/monitoring/baselime/src/instrumentation.ts b/packages/monitoring/baselime/src/instrumentation.ts new file mode 100644 index 000000000..590437486 --- /dev/null +++ b/packages/monitoring/baselime/src/instrumentation.ts @@ -0,0 +1,31 @@ +const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; + +if (!INSTRUMENTATION_SERVICE_NAME) { + throw new Error(` + You have set the Baselime instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. + `); +} + +/** + * @name registerBaselimeInstrumentation + * @description This file is used to register Baselime instrumentation for your Next.js application. + * + * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'baselime' to register Baselime instrumentation. + */ +export async function registerBaselimeInstrumentation() { + const { BaselimeSDK, BetterHttpInstrumentation, VercelPlugin } = await import( + '@baselime/node-opentelemetry' + ); + + const sdk = new BaselimeSDK({ + serverless: true, + service: INSTRUMENTATION_SERVICE_NAME, + instrumentations: [ + new BetterHttpInstrumentation({ + plugins: [new VercelPlugin()], + }), + ], + }); + + sdk.start(); +} diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index f94331b89..987655f66 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -11,7 +11,10 @@ }, "prettier": "@kit/prettier-config", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", + "./hooks": "./src/hooks/index.ts", + "./components": "./src/components/index.ts" }, "devDependencies": { "@kit/baselime": "workspace:*", @@ -19,7 +22,12 @@ "@kit/prettier-config": "workspace:*", "@kit/sentry": "workspace:*", "@kit/tailwind-config": "workspace:*", - "@kit/tsconfig": "workspace:*" + "@kit/tsconfig": "workspace:*", + "@types/react": "^18.2.77", + "react": "18.2.0" + }, + "peerDependencies": { + "react": "^18.2.0" }, "eslintConfig": { "root": true, diff --git a/packages/monitoring/sentry/package.json b/packages/monitoring/sentry/package.json index cb61b722b..04ee4ac9e 100644 --- a/packages/monitoring/sentry/package.json +++ b/packages/monitoring/sentry/package.json @@ -11,6 +11,7 @@ "prettier": "@kit/prettier-config", "exports": { ".": "./src/index.ts", + "./instrumentation": "./src/instrumentation.ts", "./config/client": "./src/config/sentry.client.config.ts", "./config/server": "./src/config/sentry.server.config.ts", "./config/edge": "./src/config/sentry.server.edge.ts" diff --git a/packages/monitoring/sentry/src/capture-exception.ts b/packages/monitoring/sentry/src/capture-exception.ts new file mode 100644 index 000000000..5342fa459 --- /dev/null +++ b/packages/monitoring/sentry/src/capture-exception.ts @@ -0,0 +1,5 @@ +import * as Sentry from '@sentry/nextjs'; + +export function captureException(error: Error & { digest?: string }) { + return Sentry.captureException(error); +} diff --git a/packages/monitoring/sentry/src/index.ts b/packages/monitoring/sentry/src/index.ts index d2b87772c..7000d7f04 100644 --- a/packages/monitoring/sentry/src/index.ts +++ b/packages/monitoring/sentry/src/index.ts @@ -1,36 +1 @@ -const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; - -if (!INSTRUMENTATION_SERVICE_NAME) { - throw new Error(` - You have set the Sentry instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. - `); -} - -/** - * @name registerSentryInstrumentation - * @description This file is used to register Sentry instrumentation for your Next.js application. - * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'sentry' to register Sentry instrumentation. - */ -export async function registerSentryInstrumentation() { - const { Resource } = await import('@opentelemetry/resources'); - const { NodeSDK } = await import('@opentelemetry/sdk-node'); - - const { SEMRESATTRS_SERVICE_NAME } = await import( - '@opentelemetry/semantic-conventions' - ); - - const { SentrySpanProcessor, SentryPropagator } = await import( - '@sentry/opentelemetry-node' - ); - - const sdk = new NodeSDK({ - resource: new Resource({ - [SEMRESATTRS_SERVICE_NAME]: INSTRUMENTATION_SERVICE_NAME, - }), - spanProcessor: new SentrySpanProcessor(), - textMapPropagator: new SentryPropagator(), - }); - - sdk.start(); -} +export { captureException } from './capture-exception'; diff --git a/packages/monitoring/sentry/src/instrumentation.ts b/packages/monitoring/sentry/src/instrumentation.ts new file mode 100644 index 000000000..d2b87772c --- /dev/null +++ b/packages/monitoring/sentry/src/instrumentation.ts @@ -0,0 +1,36 @@ +const INSTRUMENTATION_SERVICE_NAME = process.env.INSTRUMENTATION_SERVICE_NAME; + +if (!INSTRUMENTATION_SERVICE_NAME) { + throw new Error(` + You have set the Sentry instrumentation provider, but have not set the INSTRUMENTATION_SERVICE_NAME environment variable. Please set the INSTRUMENTATION_SERVICE_NAME environment variable. + `); +} + +/** + * @name registerSentryInstrumentation + * @description This file is used to register Sentry instrumentation for your Next.js application. + * + * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to 'sentry' to register Sentry instrumentation. + */ +export async function registerSentryInstrumentation() { + const { Resource } = await import('@opentelemetry/resources'); + const { NodeSDK } = await import('@opentelemetry/sdk-node'); + + const { SEMRESATTRS_SERVICE_NAME } = await import( + '@opentelemetry/semantic-conventions' + ); + + const { SentrySpanProcessor, SentryPropagator } = await import( + '@sentry/opentelemetry-node' + ); + + const sdk = new NodeSDK({ + resource: new Resource({ + [SEMRESATTRS_SERVICE_NAME]: INSTRUMENTATION_SERVICE_NAME, + }), + spanProcessor: new SentrySpanProcessor(), + textMapPropagator: new SentryPropagator(), + }); + + sdk.start(); +} diff --git a/packages/monitoring/src/capture-exception.ts b/packages/monitoring/src/capture-exception.ts new file mode 100644 index 000000000..f99873bfa --- /dev/null +++ b/packages/monitoring/src/capture-exception.ts @@ -0,0 +1,44 @@ +import { InstrumentationProvider } from './monitoring-providers.enum'; + +/** + * @name MONITORING_PROVIDER + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. + */ +const MONITORING_PROVIDER = process.env.MONITORING_PROVIDER as + | InstrumentationProvider + | undefined; + +/** + * @name captureException + * @description Capture an exception and send it to the monitoring provider defined. + * @param error + */ +export async function captureException(error: Error) { + if (!MONITORING_PROVIDER) { + console.info( + `No instrumentation provider specified. Logging to console...`, + ); + + return console.error(`Caught exception: ${JSON.stringify(error)}`); + } + + switch (MONITORING_PROVIDER) { + case InstrumentationProvider.Baselime: { + const { captureException } = await import('@kit/baselime'); + + return captureException(error); + } + + case InstrumentationProvider.Sentry: { + const { captureException } = await import('@kit/sentry'); + + return captureException(error); + } + + default: { + throw new Error( + `Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider.`, + ); + } + } +} diff --git a/packages/monitoring/src/components/error-boundary.tsx b/packages/monitoring/src/components/error-boundary.tsx new file mode 100644 index 000000000..6a5b4f265 --- /dev/null +++ b/packages/monitoring/src/components/error-boundary.tsx @@ -0,0 +1,39 @@ +import type { ErrorInfo, ReactNode } from 'react'; +import { Component } from 'react'; + +import { captureException } from '../capture-exception'; + +interface Props { + onError?: (error: Error, info: ErrorInfo) => void; + fallback: ReactNode; + children: ReactNode; +} + +export class ErrorBoundary extends Component { + readonly state = { hasError: false, error: null }; + + constructor(props: Props) { + super(props); + } + + static getDerivedStateFromError(error: unknown) { + return { + hasError: true, + error, + }; + } + + async componentDidCatch(error: Error, info: ErrorInfo) { + this.props.onError?.(error, info); + + await captureException(error); + } + + render() { + if (this.state.hasError) { + return this.props.fallback; + } + + return this.props.children; + } +} diff --git a/packages/monitoring/src/components/index.ts b/packages/monitoring/src/components/index.ts new file mode 100644 index 000000000..39d945d40 --- /dev/null +++ b/packages/monitoring/src/components/index.ts @@ -0,0 +1 @@ +export * from './error-boundary'; diff --git a/packages/monitoring/src/hooks/index.ts b/packages/monitoring/src/hooks/index.ts new file mode 100644 index 000000000..bb2203ca8 --- /dev/null +++ b/packages/monitoring/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './use-capture-exception'; diff --git a/packages/monitoring/src/hooks/use-capture-exception.ts b/packages/monitoring/src/hooks/use-capture-exception.ts new file mode 100644 index 000000000..68e780c7b --- /dev/null +++ b/packages/monitoring/src/hooks/use-capture-exception.ts @@ -0,0 +1,11 @@ +import { useEffect } from 'react'; + +import { captureException } from '../capture-exception'; + +export function useCaptureException(error: Error) { + useEffect(() => { + void captureException(error); + }, [error]); + + return null; +} diff --git a/packages/monitoring/src/index.ts b/packages/monitoring/src/index.ts index c75c68adc..c258b27a8 100644 --- a/packages/monitoring/src/index.ts +++ b/packages/monitoring/src/index.ts @@ -1 +1 @@ -export * from './instrumentation'; +export * from './capture-exception'; diff --git a/packages/monitoring/src/instrumentation.ts b/packages/monitoring/src/instrumentation.ts index c8a722d9e..de4c9b5e4 100644 --- a/packages/monitoring/src/instrumentation.ts +++ b/packages/monitoring/src/instrumentation.ts @@ -1,44 +1,46 @@ -enum InstrumentationProvider { - Baselime = 'baselime', - Sentry = 'sentry', -} +import { InstrumentationProvider } from './monitoring-providers.enum'; /** - * @name DEFAULT_INSTRUMENTATION_PROVIDER - * @description Register monitoring instrumentation based on the MONITORING_INSTRUMENTATION_PROVIDER environment variable. + * @name MONITORING_PROVIDER + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. */ -const DEFAULT_INSTRUMENTATION_PROVIDER = process.env - .MONITORING_INSTRUMENTATION_PROVIDER as InstrumentationProvider | undefined; +const MONITORING_PROVIDER = process.env.MONITORING_PROVIDER as + | InstrumentationProvider + | undefined; /** * @name registerMonitoringInstrumentation - * @description Register monitoring instrumentation based on the MONITORING_INSTRUMENTATION_PROVIDER environment variable. + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. * - * Please set the MONITORING_INSTRUMENTATION_PROVIDER environment variable to register the monitoring instrumentation provider. + * Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider. */ export async function registerMonitoringInstrumentation() { - if (!DEFAULT_INSTRUMENTATION_PROVIDER) { + if (!MONITORING_PROVIDER) { console.info(`No instrumentation provider specified. Skipping...`); return; } - switch (DEFAULT_INSTRUMENTATION_PROVIDER) { + switch (MONITORING_PROVIDER) { case InstrumentationProvider.Baselime: { - const { registerBaselimeInstrumentation } = await import('@kit/baselime'); + const { registerBaselimeInstrumentation } = await import( + '@kit/baselime/instrumentation' + ); return registerBaselimeInstrumentation(); } case InstrumentationProvider.Sentry: { - const { registerSentryInstrumentation } = await import('@kit/sentry'); + const { registerSentryInstrumentation } = await import( + '@kit/sentry/instrumentation' + ); return registerSentryInstrumentation(); } default: throw new Error( - `Unknown instrumentation provider: ${DEFAULT_INSTRUMENTATION_PROVIDER as string}`, + `Unknown instrumentation provider: ${MONITORING_PROVIDER as string}`, ); } } diff --git a/packages/monitoring/src/monitoring-providers.enum.ts b/packages/monitoring/src/monitoring-providers.enum.ts new file mode 100644 index 000000000..030610031 --- /dev/null +++ b/packages/monitoring/src/monitoring-providers.enum.ts @@ -0,0 +1,4 @@ +export enum InstrumentationProvider { + Baselime = 'baselime', + Sentry = 'sentry', +} diff --git a/packages/ui/package.json b/packages/ui/package.json index e21558de9..482dc4a27 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -119,7 +119,6 @@ "./page": "./src/makerkit/page.tsx", "./image-uploader": "./src/makerkit/image-uploader.tsx", "./global-loader": "./src/makerkit/global-loader.tsx", - "./error-boundary": "./src/makerkit/error-boundary.tsx", "./auth-change-listener": "./src/makerkit/auth-change-listener.tsx", "./loading-overlay": "./src/makerkit/loading-overlay.tsx", "./profile-avatar": "./src/makerkit/profile-avatar.tsx", diff --git a/packages/ui/src/makerkit/error-boundary.tsx b/packages/ui/src/makerkit/error-boundary.tsx deleted file mode 100644 index 04aeaa64e..000000000 --- a/packages/ui/src/makerkit/error-boundary.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client'; - -import type { ReactNode } from 'react'; -import { Component } from 'react'; - -export class ErrorBoundary extends Component<{ - fallback: ReactNode; - children: ReactNode; -}> { - readonly state = { hasError: false, error: null }; - - static getDerivedStateFromError(error: unknown) { - return { - hasError: true, - error, - }; - } - - render() { - if (this.state.hasError) { - return this.props.fallback; - } - - return this.props.children; - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c6483b16..503390801 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -554,6 +554,9 @@ importers: '@kit/mailers': specifier: workspace:^ version: link:../../mailers + '@kit/monitoring': + specifier: workspace:^ + version: link:../../monitoring '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier @@ -759,6 +762,9 @@ importers: '@kit/mailers': specifier: workspace:^ version: link:../../mailers + '@kit/monitoring': + specifier: workspace:* + version: link:../../monitoring '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier @@ -901,12 +907,21 @@ importers: '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript + '@types/react': + specifier: ^18.2.77 + version: 18.2.78 + react: + specifier: 18.2.0 + version: 18.2.0 packages/monitoring/baselime: dependencies: '@baselime/node-opentelemetry': specifier: ^0.5.8 version: 0.5.8(@trpc/server@10.45.2) + '@baselime/react-rum': + specifier: ^0.2.9 + version: 0.2.9(react@18.2.0) devDependencies: '@kit/eslint-config': specifier: workspace:* @@ -1584,6 +1599,17 @@ packages: - supports-color dev: false + /@baselime/react-rum@0.2.9(react@18.2.0): + resolution: {integrity: sha512-x5+eYsNCsasHD6jbfH02HmwA7i8A+3Ac3PS2tKwgFgoqi1EmIgktjAvdYFosTZe73VIQMal19peRXAu2/lGxAA==} + peerDependencies: + react: ^18.2.0 + dependencies: + js-cookie: 3.0.5 + react: 18.2.0 + react-error-boundary: 4.0.13(react@18.2.0) + web-vitals: 3.5.2 + dev: false + /@braintree/sanitize-url@6.0.4: resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} dev: false @@ -11690,6 +11716,15 @@ packages: - webpack-cli dev: false + /react-error-boundary@4.0.13(react@18.2.0): + resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.24.4 + react: 18.2.0 + dev: false + /react-hook-form@7.51.3(react@18.2.0): resolution: {integrity: sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ==} engines: {node: '>=12.22.0'} @@ -13296,6 +13331,10 @@ packages: engines: {node: '>= 8'} dev: true + /web-vitals@3.5.2: + resolution: {integrity: sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==} + dev: false + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}