Next.js 15 Update (#26)

* Update Next.js and React versions in all packages
* Replace onRedirect function with next/link in BillingSessionStatus, since it's no longer cached by default
* Remove unused revalidatePath import in billing return page, since it's no longer cached by default
* Add Turbopack module aliases to improve development server speed
* Converted new Dynamic APIs to be Promise-based
* Adjust mobile layout
* Use ENABLE_REACT_COMPILER to enable the React Compiler in Next.js 15
* Report Errors using the new onRequestError hook
This commit is contained in:
Giancarlo Buomprisco
2024-10-22 08:39:21 +02:00
committed by GitHub
parent 93cb011260
commit 5b9285a575
109 changed files with 5143 additions and 5545 deletions

View File

@@ -17,7 +17,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^22.5.5"
"@types/node": "^22.7.8"
},
"eslintConfig": {
"root": true,

View File

@@ -27,14 +27,14 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@supabase/supabase-js": "^2.45.4",
"@types/react": "^18.3.10",
"@supabase/supabase-js": "^2.45.6",
"@types/react": "^18.3.11",
"date-fns": "^4.1.0",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"react": "18.3.1",
"react-hook-form": "^7.53.0",
"react-i18next": "^15.0.2",
"lucide-react": "^0.453.0",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -1,3 +1,5 @@
import Link from 'next/link';
import { Check, ChevronRight } from 'lucide-react';
import { Button } from '@kit/ui/button';
@@ -11,10 +13,10 @@ import { Trans } from '@kit/ui/trans';
**/
export function BillingSessionStatus({
customerEmail,
onRedirect,
redirectPath,
}: React.PropsWithChildren<{
customerEmail: string;
onRedirect: () => void;
redirectPath: string;
}>) {
return (
<section
@@ -53,18 +55,17 @@ export function BillingSessionStatus({
</p>
</div>
<form>
<Button
data-test={'checkout-success-back-link'}
formAction={onRedirect}
>
<span>
<Trans i18nKey={'billing:checkoutSuccessBackButton'} />
</span>
<div>
<Button data-test={'checkout-success-back-link'} asChild>
<Link href={redirectPath}>
<span>
<Trans i18nKey={'billing:checkoutSuccessBackButton'} />
</span>
<ChevronRight className={'h-4'} />
<ChevronRight className={'h-4'} />
</Link>
</Button>
</form>
</div>
</div>
</section>
);

View File

@@ -14,7 +14,7 @@ import { Trans } from '@kit/ui/trans';
import { CurrentPlanBadge } from './current-plan-badge';
import { LineItemDetails } from './line-item-details';
type Order = Tables<'orders'>
type Order = Tables<'orders'>;
type LineItem = Tables<'order_items'>;
interface Props {

View File

@@ -4,7 +4,7 @@ import { Trans } from '@kit/ui/trans';
export function CurrentPlanAlert(
props: React.PropsWithoutRef<{
status: Enums<'subscription_status'>
status: Enums<'subscription_status'>;
}>,
) {
let variant: 'success' | 'warning' | 'destructive';

View File

@@ -397,12 +397,12 @@ function PlanIntervalSwitcher(
const selected = plan === props.interval;
const className = cn(
'focus:!ring-0 !outline-none animate-in transition-all fade-in',
'animate-in fade-in !outline-none transition-all focus:!ring-0',
{
'rounded-r-none border-r-transparent': index === 0,
'rounded-l-none': index === props.intervals.length - 1,
['hover:text-primary border text-muted-foreground']: !selected,
['font-semibold cursor-default hover:text-initial hover:bg-background']:
['hover:text-primary text-muted-foreground border']: !selected,
['hover:text-initial hover:bg-background cursor-default font-semibold']:
selected,
},
);

View File

@@ -4,7 +4,7 @@ import { Tables } from '@kit/supabase/database';
import { createBillingGatewayService } from '../billing-gateway/billing-gateway.service';
type Subscription = Tables<'subscriptions'>
type Subscription = Tables<'subscriptions'>;
export function createBillingWebhooksService() {
return new BillingWebhooksService();

View File

@@ -25,9 +25,9 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/react": "^18.3.10",
"next": "14.2.13",
"react": "18.3.1",
"@types/react": "^18.3.11",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -15,9 +15,9 @@
"./components": "./src/components/index.ts"
},
"dependencies": {
"@stripe/react-stripe-js": "^2.8.0",
"@stripe/stripe-js": "^4.6.0",
"stripe": "^16.12.0"
"@stripe/react-stripe-js": "^2.8.1",
"@stripe/stripe-js": "^4.9.0",
"stripe": "^17.2.1"
},
"devDependencies": {
"@kit/billing": "workspace:^",
@@ -28,10 +28,10 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/react": "^18.3.10",
"@types/react": "^18.3.11",
"date-fns": "^4.1.0",
"next": "14.2.13",
"react": "18.3.1",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -34,13 +34,16 @@ export async function createStripeCheckout(
const isSubscription = mode === 'subscription';
const trialSettings = params.plan.trialDays && enableTrialWithoutCreditCard ? {
trial_settings: {
end_behavior: {
missing_payment_method: 'cancel' as const,
},
},
} : {};
const trialSettings =
params.plan.trialDays && enableTrialWithoutCreditCard
? {
trial_settings: {
end_behavior: {
missing_payment_method: 'cancel' as const,
},
},
}
: {};
// this should only be set if the mode is 'subscription'
const subscriptionData:
@@ -96,11 +99,12 @@ export async function createStripeCheckout(
};
});
const paymentCollectionMethod = enableTrialWithoutCreditCard && params.plan.trialDays
? {
payment_method_collection: 'if_required' as const,
}
: {};
const paymentCollectionMethod =
enableTrialWithoutCreditCard && params.plan.trialDays
? {
payment_method_collection: 'if_required' as const,
}
: {};
return stripe.checkout.sessions.create({
mode,

View File

@@ -2,7 +2,7 @@ import 'server-only';
import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
const STRIPE_API_VERSION = '2024-06-20';
const STRIPE_API_VERSION = '2024-09-30.acacia';
/**
* @description returns a Stripe instance

View File

@@ -38,8 +38,7 @@ export class StripeWebhookHandlerService
constructor(private readonly config: BillingConfig) {}
private readonly provider: BillingProvider =
'stripe';
private readonly provider: BillingProvider = 'stripe';
private readonly namespace = 'billing.stripe';

View File

@@ -19,7 +19,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/wordpress": "workspace:^",
"@types/node": "^22.5.5"
"@types/node": "^22.7.8"
},
"eslintConfig": {
"root": true,

View File

@@ -16,7 +16,7 @@
"./route-handler": "./src/keystatic-route-handler.ts"
},
"dependencies": {
"@keystatic/core": "0.5.33",
"@keystatic/core": "0.5.38",
"@keystatic/next": "^5.0.1",
"@markdoc/markdoc": "^0.4.0"
},
@@ -26,9 +26,9 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/node": "^22.5.5",
"@types/react": "^18.3.10",
"react": "18.3.1",
"@types/node": "^22.7.8",
"@types/react": "^18.3.11",
"react": "19.0.0-rc-69d4b800-20241021",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -1 +1 @@
export * from './create-keystatic-cms';
export * from './create-keystatic-cms';

View File

@@ -11,19 +11,21 @@ type ZodOutputFor<T> = z.ZodType<T, z.ZodTypeDef, unknown>;
* The previous environment variable `KEYSTATIC_STORAGE_KIND` is deprecated - as Keystatic may need this to be available in the client-side.
*
*/
const STORAGE_KIND = process.env.NEXT_PUBLIC_KEYSTATIC_STORAGE_KIND ??
/* @deprecated */
process.env.KEYSTATIC_STORAGE_KIND ??
'local';
const STORAGE_KIND =
process.env.NEXT_PUBLIC_KEYSTATIC_STORAGE_KIND ??
/* @deprecated */
process.env.KEYSTATIC_STORAGE_KIND ??
'local';
/**
* @name REPO
* @description The repository to use for the GitHub storage.
* This can be provided through the `NEXT_PUBLIC_KEYSTATIC_STORAGE_REPO` environment variable. The previous environment variable `KEYSTATIC_STORAGE_REPO` is deprecated.
*/
const REPO = process.env.NEXT_PUBLIC_KEYSTATIC_STORAGE_REPO ??
/* @deprecated */
process.env.KEYSTATIC_STORAGE_REPO;
const REPO =
process.env.NEXT_PUBLIC_KEYSTATIC_STORAGE_REPO ??
/* @deprecated */
process.env.KEYSTATIC_STORAGE_REPO;
const BRANCH_PREFIX = process.env.KEYSTATIC_STORAGE_BRANCH_PREFIX;
const PATH_PREFIX = process.env.KEYSTATIC_PATH_PREFIX;
@@ -43,9 +45,11 @@ const local = z.object({
*/
const cloud = z.object({
kind: z.literal('cloud'),
project: z.string({
description: `The Keystatic Cloud project. Please provide the value through the "KEYSTATIC_STORAGE_PROJECT" environment variable.`,
}).min(1),
project: z
.string({
description: `The Keystatic Cloud project. Please provide the value through the "KEYSTATIC_STORAGE_PROJECT" environment variable.`,
})
.min(1),
branchPrefix: z.string().optional(),
pathPrefix: z.string().optional(),
}) satisfies ZodOutputFor<CloudConfig['storage']>;
@@ -72,4 +76,4 @@ export const KeystaticStorage = z.union([local, cloud, github]).parse({
repo: REPO,
branchPrefix: BRANCH_PREFIX,
pathPrefix: PATH_PREFIX,
});
});

View File

@@ -1,2 +1,2 @@
export * from './cms-client';
export * from './cms.type';
export * from './cms.type';

View File

@@ -20,8 +20,8 @@
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/node": "^22.5.5",
"@types/react": "^18.3.10",
"@types/node": "^22.7.8",
"@types/react": "^18.3.11",
"wp-types": "^4.66.1"
},
"eslintConfig": {

View File

@@ -1 +1 @@
export * from './wp-client';
export * from './wp-client';

View File

@@ -23,7 +23,7 @@
"@kit/tailwind-config": "workspace:*",
"@kit/team-accounts": "workspace:^",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "^2.45.4",
"@supabase/supabase-js": "^2.45.6",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -34,17 +34,17 @@
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@radix-ui/react-icons": "^1.3.0",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"lucide-react": "^0.453.0",
"next": "15.0.0",
"next-themes": "0.3.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.53.0",
"react-i18next": "^15.0.2",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"sonner": "^1.5.0",
"zod": "^3.23.8"
},

View File

@@ -40,6 +40,7 @@ interface AccountSelectorProps {
selectedAccount?: string;
collapsed?: boolean;
className?: string;
collisionPadding?: number;
onAccountChange: (value: string | undefined) => void;
}
@@ -56,6 +57,7 @@ export function AccountSelector({
enableTeamCreation: true,
},
collapsed = false,
collisionPadding = 20,
}: React.PropsWithChildren<AccountSelectorProps>) {
const [open, setOpen] = useState<boolean>(false);
const [isCreatingAccount, setIsCreatingAccount] = useState<boolean>(false);
@@ -154,7 +156,7 @@ export function AccountSelector({
<PopoverContent
data-test={'account-selector-content'}
className="w-full p-0"
collisionPadding={20}
collisionPadding={collisionPadding}
>
<Command>
<CommandInput placeholder={t('searchAccount')} className="h-9" />

View File

@@ -6,12 +6,11 @@ import { useSupabase } from '@kit/supabase/hooks/use-supabase';
export function usePersonalAccountData(
userId: string,
partialAccount?:
| {
id: string | null;
name: string | null;
picture_url: string | null;
}
partialAccount?: {
id: string | null;
name: string | null;
picture_url: string | null;
},
) {
const client = useSupabase();
const queryKey = ['account:data', userId];

View File

@@ -5,7 +5,7 @@
"scripts": {
"clean": "git clean -xdf .turbo node_modules",
"format": "prettier --check \"**/*.{ts,tsx}\"",
"lint": "eslint ",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"prettier": "@kit/prettier-config",
@@ -21,15 +21,15 @@
"@kit/ui": "workspace:^",
"@makerkit/data-loader-supabase-core": "^0.0.8",
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@tanstack/react-table": "^8.20.5",
"@types/react": "^18.3.10",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.53.0",
"@types/react": "^18.3.11",
"lucide-react": "^0.453.0",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react-hook-form": "^7.53.1",
"zod": "^3.23.8"
},
"exports": {

View File

@@ -21,7 +21,6 @@ import {
} from '@kit/ui/dropdown-menu';
import { DataTable } from '@kit/ui/enhanced-data-table';
import { Form, FormControl, FormField, FormItem } from '@kit/ui/form';
import { Heading } from '@kit/ui/heading';
import { If } from '@kit/ui/if';
import { Input } from '@kit/ui/input';
import {

View File

@@ -28,13 +28,13 @@
"@kit/ui": "workspace:^",
"@marsidev/react-turnstile": "^1.0.2",
"@radix-ui/react-icons": "^1.3.0",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@types/react": "^18.3.10",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"react-hook-form": "^7.53.0",
"react-i18next": "^15.0.2",
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@types/react": "^18.3.11",
"lucide-react": "^0.453.0",
"next": "15.0.0",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"sonner": "^1.5.0",
"zod": "^3.23.8"
},

View File

@@ -20,13 +20,13 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@types/react": "^18.3.10",
"lucide-react": "^0.446.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "^15.0.2"
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@types/react": "^18.3.11",
"lucide-react": "^0.453.0",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react-i18next": "^15.1.0"
},
"prettier": "@kit/prettier-config",
"eslintConfig": {

View File

@@ -32,19 +32,19 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@tanstack/react-table": "^8.20.5",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"class-variance-authority": "^0.7.0",
"date-fns": "^4.1.0",
"lucide-react": "^0.446.0",
"next": "14.2.13",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.53.0",
"react-i18next": "^15.0.2",
"lucide-react": "^0.453.0",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"sonner": "^1.5.0",
"zod": "^3.23.8"
},

View File

@@ -21,13 +21,16 @@
"@kit/shared": "workspace:^",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@tanstack/react-query": "5.56.2",
"react-i18next": "^15.0.2"
"@tanstack/react-query": "5.59.15",
"react-i18next": "^15.1.0"
},
"dependencies": {
"i18next": "^23.15.2",
"i18next": "^23.16.2",
"i18next-browser-languagedetector": "8.0.0",
"i18next-resources-to-backend": "^1.2.1"
"i18next-resources-to-backend": "^1.2.1",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"react-dom": "19.0.0-rc-69d4b800-20241021"
},
"eslintConfig": {
"root": true,

View File

@@ -19,7 +19,7 @@
"@kit/resend": "workspace:^",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^22.5.5",
"@types/node": "^22.7.8",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -34,4 +34,4 @@ async function getNodemailer() {
'Nodemailer is not available on the edge runtime. Please use another mailer.',
);
}
}
}

View File

@@ -18,7 +18,7 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/node": "^22.5.5",
"@types/node": "^22.7.8",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -1,3 +1,3 @@
export * from './schema/mailer.schema';
export * from './schema/smtp-config.schema';
export * from './mailer';
export * from './mailer';

View File

@@ -24,8 +24,8 @@
"@kit/sentry": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/react": "^18.3.10",
"react": "18.3.1"
"@types/react": "^18.3.11",
"react": "19.0.0-rc-69d4b800-20241021"
},
"eslintConfig": {
"root": true,

View File

@@ -25,8 +25,8 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/react": "^18.3.10",
"react": "18.3.1",
"@types/react": "^18.3.11",
"react": "19.0.0-rc-69d4b800-20241021",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -18,8 +18,8 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/react": "^18.3.10",
"react": "18.3.1"
"@types/react": "^18.3.11",
"react": "19.0.0-rc-69d4b800-20241021"
},
"eslintConfig": {
"root": true,

View File

@@ -16,7 +16,7 @@
"./config/server": "./src/sentry.client.server.ts"
},
"dependencies": {
"@sentry/nextjs": "^8.32.0"
"@sentry/nextjs": "^8.35.0"
},
"devDependencies": {
"@kit/eslint-config": "workspace:*",
@@ -24,8 +24,8 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/react": "^18.3.10",
"react": "18.3.1"
"@types/react": "^18.3.11",
"react": "19.0.0-rc-69d4b800-20241021"
},
"eslintConfig": {
"root": true,

View File

@@ -21,8 +21,8 @@
"@kit/supabase": "workspace:^",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "^2.45.4",
"next": "14.2.13",
"@supabase/supabase-js": "^2.45.6",
"next": "15.0.0",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { isRedirectError } from 'next/dist/client/components/redirect';
import { redirect } from 'next/navigation';
import type { User } from '@supabase/supabase-js';
@@ -11,7 +10,7 @@ import { verifyCaptchaToken } from '@kit/auth/captcha/server';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { captureException, zodParseFactory } from '../utils';
import { zodParseFactory } from '../utils';
/**
* @name enhanceAction
@@ -23,7 +22,6 @@ export function enhanceAction<
Config extends {
auth?: boolean;
captcha?: boolean;
captureException?: boolean;
schema?: z.ZodType<
Config['captcha'] extends true ? Args & { captchaToken: string } : Args,
z.ZodTypeDef
@@ -73,28 +71,6 @@ export function enhanceAction<
user = auth.data as UserParam;
}
// capture exceptions if required
const shouldCaptureException = config.captureException ?? true;
// if the action should capture exceptions, wrap the action in a try/catch block
if (shouldCaptureException) {
try {
// pass the data to the action
return await fn(data, user);
} catch (error) {
if (isRedirectError(error)) {
throw error;
}
// capture the exception
await captureException(error);
// re-throw the error
throw error;
}
} else {
// no need to capture exceptions, just pass the data to the action
return fn(data, user);
}
return fn(data, user);
};
}

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { isRedirectError } from 'next/dist/client/components/redirect';
import { redirect } from 'next/navigation';
import { NextRequest, NextResponse } from 'next/server';
@@ -12,12 +11,11 @@ import { verifyCaptchaToken } from '@kit/auth/captcha/server';
import { requireUser } from '@kit/supabase/require-user';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { captureException, zodParseFactory } from '../utils';
import { zodParseFactory } from '../utils';
interface Config<Schema> {
auth?: boolean;
captcha?: boolean;
captureException?: boolean;
schema?: Schema;
}
@@ -124,35 +122,12 @@ export const enhanceRouteHandler = <
body = zodParseFactory(params.schema)(json);
}
const shouldCaptureException = params?.captureException ?? true;
if (shouldCaptureException) {
try {
return await handler({
request,
body,
user,
params: routeParams.params,
});
} catch (error) {
if (isRedirectError(error)) {
throw error;
}
// capture the exception
await captureException(error);
throw error;
}
} else {
// all good, call the handler with the request, body and user
return handler({
request,
body,
user,
params: routeParams.params,
});
}
return handler({
request,
body,
user,
params: routeParams.params,
});
};
};

View File

@@ -12,16 +12,3 @@ export const zodParseFactory =
throw new Error(`Invalid data: ${err as string}`);
}
};
export async function captureException(exception: unknown) {
const { getServerMonitoringService } = await import('@kit/monitoring/server');
const service = await getServerMonitoringService();
await service.ready();
const error =
exception instanceof Error ? exception : new Error(exception as string);
return service.captureException(error);
}

View File

@@ -20,10 +20,10 @@
"@kit/prettier-config": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@types/react": "^18.3.10"
"@types/react": "^18.3.11"
},
"dependencies": {
"pino": "^9.4.0"
"pino": "^9.5.0"
},
"eslintConfig": {
"root": true,

View File

@@ -29,11 +29,11 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/ssr": "^0.5.1",
"@supabase/supabase-js": "^2.45.4",
"@tanstack/react-query": "5.56.2",
"@types/react": "^18.3.10",
"next": "14.2.13",
"react": "18.3.1",
"@supabase/supabase-js": "^2.45.6",
"@tanstack/react-query": "5.59.15",
"@types/react": "^18.3.11",
"next": "15.0.0",
"react": "19.0.0-rc-69d4b800-20241021",
"server-only": "^0.0.1",
"zod": "^3.23.8"
},

View File

@@ -48,7 +48,8 @@ class AuthCallbackService {
const token_hash = searchParams.get('token_hash');
const type = searchParams.get('type') as EmailOtpType | null;
const callbackParam = searchParams.get('next') ?? searchParams.get('callback');
const callbackParam =
searchParams.get('next') ?? searchParams.get('callback');
let nextPath: string | null = null;
const callbackUrl = callbackParam ? new URL(callbackParam) : null;

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { unstable_noStore as noStore } from 'next/cache';
import { cookies } from 'next/headers';
import { createClient } from '@supabase/supabase-js';
@@ -28,9 +27,6 @@ export function getSupabaseRouteHandlerClient<GenericSchema = Database>(
admin: false,
},
) {
// prevent any caching (to be removed in Next v15)
noStore();
if (params.admin) {
warnServiceRoleKeyUsage();
@@ -49,16 +45,20 @@ export function getSupabaseRouteHandlerClient<GenericSchema = Database>(
}
function getCookiesStrategy() {
const cookieStore = cookies();
return {
set: (name: string, value: string, options: CookieOptions) => {
set: async (name: string, value: string, options: CookieOptions) => {
const cookieStore = await cookies();
cookieStore.set({ name, value, ...options });
},
get: (name: string) => {
get: async (name: string) => {
const cookieStore = await cookies();
return cookieStore.get(name)?.value;
},
remove: (name: string, options: CookieOptions) => {
remove: async (name: string, options: CookieOptions) => {
const cookieStore = await cookies();
cookieStore.set({ name, value: '', ...options });
},
};

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { unstable_noStore as noStore } from 'next/cache';
import { cookies } from 'next/headers';
import { createClient } from '@supabase/supabase-js';
@@ -33,9 +32,6 @@ function createServerSupabaseClient<
export function getSupabaseServerActionClient<
GenericSchema extends Database = Database,
>(params?: { admin: boolean }) {
// prevent any caching (to be removed in Next v15)
noStore();
const keys = getSupabaseClientKeys();
const admin = params?.admin ?? false;
@@ -55,16 +51,21 @@ export function getSupabaseServerActionClient<
}
function getCookiesStrategy() {
const cookieStore = cookies();
return {
get: (name: string) => {
return cookieStore.get(name)?.value;
get: async (name: string) => {
const cookieStore = await cookies();
const cookie = cookieStore.get(name);
return cookie?.value;
},
set: (name: string, value: string, options: object) => {
set: async (name: string, value: string, options: object) => {
const cookieStore = await cookies();
cookieStore.set({ name, value, ...options });
},
remove: (name: string, options: object) => {
remove: async (name: string, options: object) => {
const cookieStore = await cookies();
cookieStore.set({
name,
value: '',

View File

@@ -1,7 +1,5 @@
import 'server-only';
import { unstable_noStore as noStore } from 'next/cache';
import { createClient } from '@supabase/supabase-js';
import { Database } from '../database.types';
@@ -16,7 +14,6 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
* @description Get a Supabase client for use in the Server with admin access to the database.
*/
export function getSupabaseServerAdminClient<GenericSchema = Database>() {
noStore();
warnServiceRoleKeyUsage();
const url = getSupabaseClientKeys().url;

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { unstable_noStore as noStore } from 'next/cache';
import { cookies } from 'next/headers';
import { createServerClient } from '@supabase/ssr';
@@ -13,17 +12,18 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
* @description Creates a Supabase client for use in the Server.
*/
export function getSupabaseServerClient<GenericSchema = Database>() {
noStore();
const cookieStore = cookies();
const keys = getSupabaseClientKeys();
return createServerClient<GenericSchema>(keys.url, keys.anonKey, {
cookies: {
getAll() {
async getAll() {
const cookieStore = await cookies();
return cookieStore.getAll();
},
setAll(cookiesToSet) {
async setAll(cookiesToSet) {
const cookieStore = await cookies();
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options),

View File

@@ -1,6 +1,5 @@
import 'server-only';
import { unstable_noStore as noStore } from 'next/cache';
import { cookies } from 'next/headers';
import { createClient } from '@supabase/supabase-js';
@@ -26,9 +25,6 @@ export function getSupabaseServerComponentClient<GenericSchema = Database>(
admin: false,
},
) {
// prevent any caching (to be removed in Next v15)
noStore();
if (params.admin) {
warnServiceRoleKeyUsage();
@@ -47,10 +43,10 @@ export function getSupabaseServerComponentClient<GenericSchema = Database>(
}
function getCookiesStrategy() {
const cookieStore = cookies();
return {
get: (name: string) => {
get: async (name: string) => {
const cookieStore = await cookies();
return cookieStore.get(name)?.value;
},
};

View File

@@ -10,31 +10,31 @@
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-accordion": "1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-accordion": "1.2.1",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.1",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "1.1.2",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "1.1.3",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"input-otp": "1.2.4",
"lucide-react": "^0.446.0",
"lucide-react": "^0.453.0",
"react-top-loading-bar": "2.3.1",
"recharts": "^2.12.7",
"tailwind-merge": "^2.5.2"
"recharts": "2.13.0",
"tailwind-merge": "^2.5.4"
},
"devDependencies": {
"@kit/eslint-config": "workspace:*",
@@ -42,23 +42,23 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@radix-ui/react-icons": "^1.3.0",
"@tanstack/react-query": "5.56.2",
"@tanstack/react-query": "5.59.15",
"@tanstack/react-table": "^8.20.5",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"class-variance-authority": "^0.7.0",
"date-fns": "^4.1.0",
"eslint": "^8.57.0",
"next": "14.2.13",
"next": "15.0.0",
"next-themes": "0.3.0",
"prettier": "^3.3.3",
"react-day-picker": "^8.10.1",
"react-hook-form": "^7.53.0",
"react-i18next": "^15.0.2",
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"sonner": "^1.5.0",
"tailwindcss": "3.4.13",
"tailwindcss": "3.4.14",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.6.2",
"typescript": "^5.6.3",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -34,7 +34,7 @@ export const ImageUploadInput = forwardRef<React.ElementRef<'input'>, Props>(
},
forwardedRef,
) {
const localRef = useRef<HTMLInputElement>();
const localRef = useRef<HTMLInputElement>(null);
const [state, setState] = useState({
image,

View File

@@ -30,21 +30,24 @@ function PageWithSidebar(props: PageProps) {
return (
<div
className={cn('flex bg-gray-50/95 dark:bg-background/85', props.className)}
className={cn(
'flex bg-gray-50/95 dark:bg-background/85',
props.className,
)}
>
{Navigation}
<div
className={
props.contentContainerClassName ??
'mx-auto flex h-screen w-full flex-col overflow-y-auto px-4 lg:px-0 bg-inherit'
'mx-auto flex h-screen w-full flex-col overflow-y-auto bg-inherit'
}
>
{MobileNavigation}
<div
className={
'flex flex-1 flex-col overflow-y-auto bg-background lg:m-1.5 lg:ml-0 lg:rounded-lg lg:border'
'flex flex-1 flex-col overflow-y-auto bg-background px-4 lg:m-1.5 lg:ml-0 lg:rounded-lg lg:border lg:px-0'
}
>
{Children}
@@ -62,7 +65,7 @@ export function PageMobileNavigation(
return (
<div
className={cn(
'flex w-full items-center border-b py-2 lg:hidden',
'flex w-full items-center border-b px-4 py-2 lg:hidden lg:px-0',
props.className,
)}
>
@@ -115,7 +118,9 @@ export function PageBody(
}
export function PageNavigation(props: React.PropsWithChildren) {
return <div className={'hidden flex-1 lg:flex bg-inherit'}>{props.children}</div>;
return (
<div className={'hidden flex-1 bg-inherit lg:flex'}>{props.children}</div>
);
}
export function PageDescription(props: React.PropsWithChildren) {

View File

@@ -17,7 +17,7 @@ type ProfileAvatarProps = (SessionProps | TextProps) & {
export function ProfileAvatar(props: ProfileAvatarProps) {
const avatarClassName = cn(
props.className,
'mx-auto w-9 h-9 group-focus:ring-2',
'mx-auto h-9 w-9 group-focus:ring-2',
);
if ('text' in props) {

View File

@@ -42,7 +42,7 @@ export function Stepper(props: {
const isDotsVariant = variant === 'dots';
const labelClassName = cn({
['text-xs px-1.5 py-2']: !isNumberVariant,
['px-1.5 py-2 text-xs']: !isNumberVariant,
['hidden']: isDotsVariant,
});
@@ -182,15 +182,15 @@ function StepDivider({
selected: boolean;
complete: boolean;
}>) {
const spanClassName = cn('font-medium text-sm min-w-max', {
['text-muted-foreground hidden sm:flex']: !selected,
const spanClassName = cn('min-w-max text-sm font-medium', {
['hidden text-muted-foreground sm:flex']: !selected,
['text-secondary-foreground']: selected || complete,
['font-medium']: selected,
});
const className = cn(
'flex flex-1 last:flex-[0_0_0] items-center h-9 justify-center' +
' items-center w-full group px-3 flex space-x-3',
'flex h-9 flex-1 items-center justify-center last:flex-[0_0_0]' +
' group flex w-full items-center space-x-3 px-3',
);
return (